varnish-cache/bin/varnishd/cache/cache_pool.c
1
/*-
2
 * Copyright (c) 2006 Verdens Gang AS
3
 * Copyright (c) 2006-2011 Varnish Software AS
4
 * All rights reserved.
5
 *
6
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 *
29
 * We maintain a number of worker thread pools, to spread lock contention.
30
 *
31
 * Pools can be added on the fly, as a means to mitigate lock contention,
32
 * but can only be removed again by a restart. (XXX: we could fix that)
33
 *
34
 */
35
36
#include "config.h"
37
38
#include <stdlib.h>
39
40
#include "cache_varnishd.h"
41
#include "cache_pool.h"
42
43
static pthread_t                thr_pool_herder;
44
45
static struct lock              wstat_mtx;
46
struct lock                     pool_mtx;
47
static VTAILQ_HEAD(,pool)       pools = VTAILQ_HEAD_INITIALIZER(pools);
48
49
/*--------------------------------------------------------------------
50
 * Summing of stats into global stats counters
51
 */
52
53
void
54 3567
Pool_Sumstat(const struct worker *wrk)
55
{
56
57 3567
        Lck_Lock(&wstat_mtx);
58 3567
        VSC_main_Summ_wrk(VSC_C_main, wrk->stats);
59 3567
        Lck_Unlock(&wstat_mtx);
60 3567
        memset(wrk->stats, 0, sizeof *wrk->stats);
61 3567
}
62
63
int
64 0
Pool_TrySumstat(const struct worker *wrk)
65
{
66 0
        if (Lck_Trylock(&wstat_mtx))
67 0
                return (0);
68 0
        VSC_main_Summ_wrk(VSC_C_main, wrk->stats);
69 0
        Lck_Unlock(&wstat_mtx);
70 0
        memset(wrk->stats, 0, sizeof *wrk->stats);
71 0
        return (1);
72
}
73
74
/*--------------------------------------------------------------------
75
 * Facility for scheduling a task on any convenient pool.
76
 */
77
78
int
79 245
Pool_Task_Any(struct pool_task *task, enum task_prio prio)
80
{
81
        struct pool *pp;
82
83 245
        Lck_Lock(&pool_mtx);
84 245
        pp = VTAILQ_FIRST(&pools);
85 245
        if (pp != NULL) {
86 245
                VTAILQ_REMOVE(&pools, pp, list);
87 245
                VTAILQ_INSERT_TAIL(&pools, pp, list);
88
        }
89 245
        Lck_Unlock(&pool_mtx);
90 245
        if (pp == NULL)
91 0
                return (-1);
92
        // NB: When we remove pools, is there a race here ?
93 245
        return (Pool_Task(pp, task, prio));
94
}
95
96
/*--------------------------------------------------------------------
97
 * Helper function to update stats for purges under lock
98
 */
99
100
void
101 13
Pool_PurgeStat(unsigned nobj)
102
{
103 13
        Lck_Lock(&wstat_mtx);
104 13
        VSC_C_main->n_purges++;
105 13
        VSC_C_main->n_obj_purged += nobj;
106 13
        Lck_Unlock(&wstat_mtx);
107 13
}
108
109
/*--------------------------------------------------------------------
110
 * Special function to summ stats
111
 */
112
113
void v_matchproto_(task_func_t)
114 3478
pool_stat_summ(struct worker *wrk, void *priv)
115
{
116
        struct VSC_main_wrk *src;
117
118 3478
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
119 3478
        CHECK_OBJ_NOTNULL(wrk->pool, POOL_MAGIC);
120 3478
        AN(priv);
121 3478
        src = priv;
122 3478
        Lck_Lock(&wstat_mtx);
123 3478
        VSC_main_Summ_wrk(VSC_C_main, src);
124 3478
        Lck_Unlock(&wstat_mtx);
125 3478
        memset(src, 0, sizeof *src);
126 3478
        AZ(wrk->pool->b_stat);
127 3478
        wrk->pool->b_stat = src;
128 3478
}
129
130
/*--------------------------------------------------------------------
131
 * Add a thread pool
132
 */
133
134
static struct pool *
135 1367
pool_mkpool(unsigned pool_no)
136
{
137
        struct pool *pp;
138
        int i;
139
140 1367
        ALLOC_OBJ(pp, POOL_MAGIC);
141 1367
        if (pp == NULL)
142 0
                return (NULL);
143 1367
        pp->a_stat = calloc(1, sizeof *pp->a_stat);
144 1367
        AN(pp->a_stat);
145 1367
        pp->b_stat = calloc(1, sizeof *pp->b_stat);
146 1367
        AN(pp->b_stat);
147 1367
        Lck_New(&pp->mtx, lck_wq);
148
149 1367
        VTAILQ_INIT(&pp->idle_queue);
150 1367
        VTAILQ_INIT(&pp->poolsocks);
151 8202
        for (i = 0; i < TASK_QUEUE_END; i++)
152 6835
                VTAILQ_INIT(&pp->queues[i]);
153 1367
        AZ(pthread_cond_init(&pp->herder_cond, NULL));
154 1367
        AZ(pthread_create(&pp->herder_thr, NULL, pool_herder, pp));
155
156 4101
        while (VTAILQ_EMPTY(&pp->idle_queue))
157 1367
                (void)usleep(10000);
158
159 1367
        SES_NewPool(pp, pool_no);
160 1367
        VCA_NewPool(pp);
161
162 1367
        return (pp);
163
}
164
165
/*--------------------------------------------------------------------
166
 * This thread adjusts the number of pools to match the parameter.
167
 *
168
 * NB: This is quite silly.  The master should tell the child through
169
 * NB: CLI when parameters change and an appropriate call-out table
170
 * NB: be maintained for params which require action.
171
 */
172
173
static void * v_matchproto_()
174 688
pool_poolherder(void *priv)
175
{
176
        unsigned nwq;
177
        struct pool *pp, *ppx;
178
        uint64_t u;
179
        void *rvp;
180
181 688
        THR_SetName("pool_poolherder");
182 688
        THR_Init();
183
        (void)priv;
184
185 688
        nwq = 0;
186
        while (1) {
187 6234
                if (nwq < cache_param->wthread_pools) {
188 1367
                        pp = pool_mkpool(nwq);
189 1367
                        if (pp != NULL) {
190 1367
                                Lck_Lock(&pool_mtx);
191 1367
                                VTAILQ_INSERT_TAIL(&pools, pp, list);
192 1367
                                Lck_Unlock(&pool_mtx);
193 1367
                                VSC_C_main->pools++;
194 1367
                                nwq++;
195 1367
                                continue;
196
                        }
197 2096
                } else if (nwq > cache_param->wthread_pools &&
198 2
                                DO_DEBUG(DBG_DROP_POOLS)) {
199 2
                        Lck_Lock(&pool_mtx);
200 2
                        pp = VTAILQ_FIRST(&pools);
201 2
                        AN(pp);
202 2
                        VTAILQ_REMOVE(&pools, pp, list);
203 2
                        VTAILQ_INSERT_TAIL(&pools, pp, list);
204 2
                        if (!pp->die)
205 2
                                nwq--;
206 2
                        Lck_Unlock(&pool_mtx);
207 2
                        if (!pp->die) {
208 2
                                VSL(SLT_Debug, 0, "XXX Kill Pool %p", pp);
209 2
                                pp->die = 1;
210 2
                                VCA_DestroyPool(pp);
211 2
                                AZ(pthread_cond_signal(&pp->herder_cond));
212
                        }
213
                }
214 2094
                (void)sleep(1);
215 1406
                u = 0;
216 1406
                ppx = NULL;
217 1406
                Lck_Lock(&pool_mtx);
218 4143
                VTAILQ_FOREACH(pp, &pools, list) {
219 2737
                        if (pp->die && pp->nthr == 0)
220 2
                                ppx = pp;
221 2737
                        u += pp->lqueue;
222
                }
223 1406
                if (ppx != NULL) {
224 2
                        VTAILQ_REMOVE(&pools, ppx, list);
225 2
                        AZ(pthread_join(ppx->herder_thr, &rvp));
226 2
                        AZ(pthread_cond_destroy(&ppx->herder_cond));
227 2
                        free(ppx->a_stat);
228 2
                        free(ppx->b_stat);
229 2
                        SES_DestroyPool(ppx);
230 2
                        FREE_OBJ(ppx);
231 2
                        VSC_C_main->pools--;
232
                }
233 1406
                Lck_Unlock(&pool_mtx);
234 1406
                VSC_C_main->thread_queue_len = u;
235
        }
236
        NEEDLESS(return NULL);
237
}
238
239
/*--------------------------------------------------------------------*/
240
241
void
242 688
Pool_Init(void)
243
{
244
245 688
        Lck_New(&wstat_mtx, lck_wstat);
246 688
        Lck_New(&pool_mtx, lck_wq);
247 688
        AZ(pthread_create(&thr_pool_herder, NULL, pool_poolherder, NULL));
248 2758
        while (!VSC_C_main->pools)
249 1382
                (void)usleep(10000);
250 688
}