vinyl-cache/bin/vinyld/cache/cache_pool.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2011 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
 * SUCH DAMAGE.
29
 *
30
 * We maintain a number of worker thread pools, to spread lock contention.
31
 *
32
 * Pools can be added on the fly, as a means to mitigate lock contention,
33
 * but can only be removed again by a restart. (XXX: we could fix that)
34
 *
35
 */
36
37
#include "config.h"
38
39
#include <stdlib.h>
40
41
#include "cache_int.h"
42
#include "cache_pool.h"
43
44
#include "vtim.h"
45
46
static pthread_t                thr_pool_herder;
47
48
static struct lock              wstat_mtx;
49
struct lock                     pool_mtx;
50
static VTAILQ_HEAD(,pool)       pools = VTAILQ_HEAD_INITIALIZER(pools);
51
static pthread_cond_t           cond = PTHREAD_COND_INITIALIZER;
52
53
/*--------------------------------------------------------------------
54
 * Summing of stats into global stats counters
55
 */
56
57
void
58 534218
Pool_Sumstat(const struct worker *wrk)
59
{
60
61 534218
        Lck_Lock(&wstat_mtx);
62 534218
        VSC_main_Summ_wrk(VSC_C_main, wrk->stats);
63 534218
        Lck_Unlock(&wstat_mtx);
64 534218
        memset(wrk->stats, 0, sizeof *wrk->stats);
65 534218
}
66
67
int
68 0
Pool_TrySumstat(const struct worker *wrk)
69
{
70 0
        if (Lck_Trylock(&wstat_mtx))
71 0
                return (0);
72 0
        VSC_main_Summ_wrk(VSC_C_main, wrk->stats);
73 0
        Lck_Unlock(&wstat_mtx);
74 0
        memset(wrk->stats, 0, sizeof *wrk->stats);
75 0
        return (1);
76 0
}
77
78
/*--------------------------------------------------------------------
79
 * Facility for scheduling a task on any convenient pool.
80
 */
81
82
int
83 6062
Pool_Task_Any(struct pool_task *task, enum task_prio prio)
84
{
85
        struct pool *pp;
86
87 6062
        Lck_Lock(&pool_mtx);
88 6062
        pp = VTAILQ_FIRST(&pools);
89 6062
        if (pp != NULL) {
90 4578
                VTAILQ_REMOVE(&pools, pp, list);
91 4578
                VTAILQ_INSERT_TAIL(&pools, pp, list);
92 4578
                AZ(pp->die);
93 4578
        }
94 6062
        Lck_Unlock(&pool_mtx);
95 6062
        if (pp == NULL)
96 1484
                return (-1);
97
98
        // We never see a dying pool here, but it might die right after the
99
        // Unlock, in which case Pool_Task races for a worker, but because of
100
        // the destruction delay it is highly unlikely that all threads are gone
101
102 4578
        return (Pool_Task(pp, task, prio));
103 6062
}
104
105
/*--------------------------------------------------------------------
106
 * Helper function to update stats for purges under lock
107
 */
108
109
void
110 294
Pool_PurgeStat(unsigned nobj)
111
{
112 294
        Lck_Lock(&wstat_mtx);
113 294
        VSC_C_main->n_purges++;
114 294
        VSC_C_main->n_obj_purged += nobj;
115 294
        Lck_Unlock(&wstat_mtx);
116 294
}
117
118
/*--------------------------------------------------------------------
119
 * Special function to summ stats
120
 */
121
122
void v_matchproto_(task_func_t)
123 158332
pool_stat_summ(struct worker *wrk, void *priv)
124
{
125
        struct VSC_main_wrk *src;
126
        struct pool *pp;
127
128 158332
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
129 158332
        pp = wrk->pool;
130 158332
        CHECK_OBJ_NOTNULL(pp, POOL_MAGIC);
131 158332
        AN(priv);
132 158332
        src = priv;
133
134 158332
        Lck_Lock(&wstat_mtx);
135 158332
        VSC_main_Summ_wrk(VSC_C_main, src);
136
137 158332
        Lck_Lock(&pp->mtx);
138 158332
        VSC_main_Summ_pool(VSC_C_main, pp->stats);
139 158332
        Lck_Unlock(&pp->mtx);
140 158332
        memset(pp->stats, 0, sizeof pp->stats);
141
142 158332
        Lck_Unlock(&wstat_mtx);
143 158332
        memset(src, 0, sizeof *src);
144
145 158332
        AZ(pp->b_stat);
146 158332
        pp->b_stat = src;
147 158332
}
148
149
/*--------------------------------------------------------------------
150
 * Add a thread pool
151
 */
152
153
static struct pool *
154 41480
pool_mkpool(unsigned pool_no)
155
{
156
        struct pool *pp;
157
        int i;
158
159 41480
        ALLOC_OBJ(pp, POOL_MAGIC);
160 41480
        if (pp == NULL)
161 0
                return (NULL);
162 41480
        pp->a_stat = calloc(1, sizeof *pp->a_stat);
163 41480
        AN(pp->a_stat);
164 41480
        pp->b_stat = calloc(1, sizeof *pp->b_stat);
165 41480
        AN(pp->b_stat);
166 41480
        Lck_New(&pp->mtx, lck_perpool);
167
168 41480
        VTAILQ_INIT(&pp->idle_queue);
169 41480
        VTAILQ_INIT(&pp->poolsocks);
170 248880
        for (i = 0; i < TASK_QUEUE_RESERVE; i++)
171 207400
                VTAILQ_INIT(&pp->queues[i]);
172 41480
        PTOK(pthread_cond_init(&pp->herder_cond, NULL));
173 41480
        PTOK(pthread_create(&pp->herder_thr, NULL, pool_herder, pp));
174
175 82960
        while (VTAILQ_EMPTY(&pp->idle_queue))
176 41480
                VTIM_sleep(0.01);
177
178 41480
        SES_NewPool(pp, pool_no);
179 41480
        VCA_NewPool(pp);
180
181 41480
        return (pp);
182 41480
}
183
184
static void
185 40950
pool_destroy(struct pool *ppx)
186
{
187
        void *rvp;
188
189 40950
        CHECK_OBJ_NOTNULL(ppx, POOL_MAGIC);
190 40950
        PTOK(pthread_join(ppx->herder_thr, &rvp));
191 40950
        PTOK(pthread_cond_destroy(&ppx->herder_cond));
192 40950
        assert(VTAILQ_EMPTY(&ppx->poolsocks));
193 40950
        free(ppx->a_stat);
194 40950
        free(ppx->b_stat);
195 40950
        SES_DestroyPool(ppx);
196 40950
        Lck_Delete(&ppx->mtx);
197 40950
        FREE_OBJ(ppx);
198 40950
        VSC_C_main->pools--;
199 40950
}
200
201
/*--------------------------------------------------------------------
202
 * This thread adjusts the number of pools to match the parameter.
203
 *
204
 * NB: This is quite silly.  The master should tell the child through
205
 * NB: CLI when parameters change and an appropriate call-out table
206
 * NB: be maintained for params which require action.
207
 */
208
209
static void * v_matchproto_()
210 20887
pool_poolherder(void *priv)
211
{
212 20887
        VTAILQ_HEAD(,pool) deadpools = VTAILQ_HEAD_INITIALIZER(deadpools);
213
        unsigned nwq, poolno;
214
        struct pool *pp, *next;
215
        uint64_t u;
216
217 20887
        THR_SetName("pool_poolherder");
218 20887
        THR_Init();
219 20887
        (void)priv;
220
221 20887
        nwq = poolno = 0;
222 288333
        while (cache_param->wthread_pools > 0 ||
223 102678
            VTAILQ_FIRST(&pools) ||
224 61959
            VTAILQ_FIRST(&deadpools)) {
225 164768
                if (nwq < cache_param->wthread_pools) {
226
                        // die before we would wrap
227 41479
                        assert(poolno < UINT_MAX);
228 41479
                        pp = pool_mkpool(poolno);
229 41479
                        if (pp != NULL) {
230 41479
                                Lck_Lock(&pool_mtx);
231 41479
                                VTAILQ_INSERT_TAIL(&pools, pp, list);
232 41479
                                Lck_Unlock(&pool_mtx);
233 41479
                                VSC_C_main->pools++;
234 41479
                                poolno++;
235 41479
                                nwq++;
236 41479
                                continue;
237
                        }
238 123289
                } else if (nwq > cache_param->wthread_pools) {
239 40950
                        Lck_Lock(&pool_mtx);
240 40950
                        pp = VTAILQ_FIRST(&pools);
241 40950
                        CHECK_OBJ_NOTNULL(pp, POOL_MAGIC);
242 40950
                        VTAILQ_REMOVE(&pools, pp, list);
243 40950
                        AZ(pp->die);
244 40950
                        Lck_Unlock(&pool_mtx);
245
246 40950
                        VTAILQ_INSERT_TAIL(&deadpools, pp, list);
247 40950
                        AN(nwq);
248 40950
                        nwq--;
249 40950
                        VSL(SLT_Debug, NO_VXID, "Kill Pool %p", pp);
250 40950
                        pp->die = 1;
251 40950
                        VCA_DestroyPool(pp);
252 40950
                        PTOK(pthread_cond_signal(&pp->herder_cond));
253 40950
                        continue;
254
                }
255
256 164364
                VTAILQ_FOREACH_SAFE(pp, &deadpools, list, next) {
257 82025
                        CHECK_OBJ_NOTNULL(pp, POOL_MAGIC);
258
                        int active;
259
260 82025
                        AN(pp->die);
261 82025
                        if (pp->nthr > 0 || pp->wrk_dying > 0)
262 41075
                                continue;
263
264 40950
                        Lck_Lock(&pp->mtx);
265 40950
                        active = (pp->nthr > 0 || pp->wrk_dying > 0) ? 1 : 0;
266 40950
                        Lck_Unlock(&pp->mtx);
267
268 40950
                        if (active)
269 0
                                continue;
270
271 40950
                        VTAILQ_REMOVE(&deadpools, pp, list);
272 40950
                        pool_destroy(pp);
273 40950
                }
274
275 82339
                u = 0;
276 82339
                Lck_Lock(&pool_mtx);
277 161863
                VTAILQ_FOREACH(pp, &pools, list) {
278 79524
                        CHECK_OBJ_NOTNULL(pp, POOL_MAGIC);
279
280 79524
                        u += pp->lqueue;
281 79524
                }
282 82339
                VSC_C_main->thread_queue_len = u;
283 82339
                if (nwq > 0 || cache_param->wthread_pools > 0 ||
284 41337
                    VTAILQ_FIRST(&deadpools) != NULL)
285 61717
                        (void)Lck_CondWaitTimeout(&cond, &pool_mtx, 1.0);
286 82339
                Lck_Unlock(&pool_mtx);
287
        }
288 20887
        return (NULL);
289
}
290
291
/*--------------------------------------------------------------------*/
292
void
293 266
pan_pool(struct vsb *vsb)
294
{
295
        struct pool *pp;
296
297 266
        VSB_cat(vsb, "pools = {\n");
298 266
        VSB_indent(vsb, 2);
299 795
        VTAILQ_FOREACH(pp, &pools, list) {
300 529
                if (PAN_dump_struct(vsb, pp, POOL_MAGIC, "pool"))
301 0
                        continue;
302 529
                VSB_printf(vsb, "nidle = %u,\n", pp->nidle);
303 529
                VSB_printf(vsb, "nthr = %u,\n", pp->nthr);
304 529
                VSB_printf(vsb, "lqueue = %u\n", pp->lqueue);
305 529
                VSB_indent(vsb, -2);
306 529
                VSB_cat(vsb, "},\n");
307 529
        }
308 266
        VSB_indent(vsb, -2);
309 266
        VSB_cat(vsb, "},\n");
310 266
}
311
312
/*--------------------------------------------------------------------*/
313
314
void
315 20887
Pool_Init(void)
316
{
317
318 20887
        Lck_New(&wstat_mtx, lck_wstat);
319 20887
        Lck_New(&pool_mtx, lck_wq);
320 20887
        PTOK(pthread_create(&thr_pool_herder, NULL, pool_poolherder, NULL));
321 62661
        while (!VSC_C_main->pools)
322 41774
                VTIM_sleep(0.01);
323 20887
}
324
325
void
326 20622
Pool_Stop(void)
327
{
328 20622
        Lck_Lock(&pool_mtx);
329 20622
        cache_param->wthread_pools = 0;
330 20622
        PTOK(pthread_cond_signal(&cond));
331 20622
        Lck_Unlock(&pool_mtx);
332 20622
}
333
334
void
335 20622
Pool_Fini(void)
336
{
337 20622
        AZ(pthread_join(thr_pool_herder, NULL));
338 20622
}