varnish-cache/bin/varnishd/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_varnishd.h"
42
#include "cache_pool.h"
43
44
static pthread_t                thr_pool_herder;
45
46
static struct lock              wstat_mtx;
47
struct lock                     pool_mtx;
48
static VTAILQ_HEAD(,pool)       pools = VTAILQ_HEAD_INITIALIZER(pools);
49
50
/*--------------------------------------------------------------------
51
 * Summing of stats into global stats counters
52
 */
53
54
void
55 218998
Pool_Sumstat(const struct worker *wrk)
56
{
57
58 218998
        Lck_Lock(&wstat_mtx);
59 218998
        VSC_main_Summ_wrk(VSC_C_main, wrk->stats);
60 218998
        Lck_Unlock(&wstat_mtx);
61 218998
        memset(wrk->stats, 0, sizeof *wrk->stats);
62 218998
}
63
64
int
65 0
Pool_TrySumstat(const struct worker *wrk)
66
{
67 0
        if (Lck_Trylock(&wstat_mtx))
68 0
                return (0);
69 0
        VSC_main_Summ_wrk(VSC_C_main, wrk->stats);
70 0
        Lck_Unlock(&wstat_mtx);
71 0
        memset(wrk->stats, 0, sizeof *wrk->stats);
72 0
        return (1);
73 0
}
74
75
/*--------------------------------------------------------------------
76
 * Facility for scheduling a task on any convenient pool.
77
 */
78
79
int
80 7833
Pool_Task_Any(struct pool_task *task, enum task_prio prio)
81
{
82
        struct pool *pp;
83
84 7833
        Lck_Lock(&pool_mtx);
85 7833
        pp = VTAILQ_FIRST(&pools);
86 7833
        if (pp != NULL) {
87 7833
                VTAILQ_REMOVE(&pools, pp, list);
88 7833
                VTAILQ_INSERT_TAIL(&pools, pp, list);
89 7833
        }
90 7833
        Lck_Unlock(&pool_mtx);
91 7833
        if (pp == NULL)
92 0
                return (-1);
93
        // NB: When we remove pools, is there a race here ?
94 7833
        return (Pool_Task(pp, task, prio));
95 7833
}
96
97
/*--------------------------------------------------------------------
98
 * Helper function to update stats for purges under lock
99
 */
100
101
void
102 440
Pool_PurgeStat(unsigned nobj)
103
{
104 440
        Lck_Lock(&wstat_mtx);
105 440
        VSC_C_main->n_purges++;
106 440
        VSC_C_main->n_obj_purged += nobj;
107 440
        Lck_Unlock(&wstat_mtx);
108 440
}
109
110
/*--------------------------------------------------------------------
111
 * Special function to summ stats
112
 */
113
114
void v_matchproto_(task_func_t)
115 192781
pool_stat_summ(struct worker *wrk, void *priv)
116
{
117
        struct VSC_main_wrk *src;
118
        struct pool *pp;
119
120 192781
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
121 192781
        pp = wrk->pool;
122 192781
        CHECK_OBJ_NOTNULL(pp, POOL_MAGIC);
123 192781
        AN(priv);
124 192781
        src = priv;
125
126 192781
        Lck_Lock(&wstat_mtx);
127 192781
        VSC_main_Summ_wrk(VSC_C_main, src);
128
129 192781
        Lck_Lock(&pp->mtx);
130 192781
        VSC_main_Summ_pool(VSC_C_main, pp->stats);
131 192781
        Lck_Unlock(&pp->mtx);
132 192781
        memset(pp->stats, 0, sizeof pp->stats);
133
134 192781
        Lck_Unlock(&wstat_mtx);
135 192781
        memset(src, 0, sizeof *src);
136
137 192781
        AZ(pp->b_stat);
138 192781
        pp->b_stat = src;
139 192781
}
140
141
/*--------------------------------------------------------------------
142
 * Add a thread pool
143
 */
144
145
static struct pool *
146 72552
pool_mkpool(unsigned pool_no)
147
{
148
        struct pool *pp;
149
        int i;
150
151 72552
        ALLOC_OBJ(pp, POOL_MAGIC);
152 72552
        if (pp == NULL)
153 0
                return (NULL);
154 72552
        pp->a_stat = calloc(1, sizeof *pp->a_stat);
155 72552
        AN(pp->a_stat);
156 72552
        pp->b_stat = calloc(1, sizeof *pp->b_stat);
157 72552
        AN(pp->b_stat);
158 72552
        Lck_New(&pp->mtx, lck_perpool);
159
160 72552
        VTAILQ_INIT(&pp->idle_queue);
161 72552
        VTAILQ_INIT(&pp->poolsocks);
162 435312
        for (i = 0; i < TASK_QUEUE_RESERVE; i++)
163 362760
                VTAILQ_INIT(&pp->queues[i]);
164 72552
        PTOK(pthread_cond_init(&pp->herder_cond, NULL));
165 72552
        PTOK(pthread_create(&pp->herder_thr, NULL, pool_herder, pp));
166
167 145104
        while (VTAILQ_EMPTY(&pp->idle_queue))
168 72552
                (void)usleep(10000);
169
170 72552
        SES_NewPool(pp, pool_no);
171 72552
        VCA_NewPool(pp);
172
173 72552
        return (pp);
174 72552
}
175
176
/*--------------------------------------------------------------------
177
 * This thread adjusts the number of pools to match the parameter.
178
 *
179
 * NB: This is quite silly.  The master should tell the child through
180
 * NB: CLI when parameters change and an appropriate call-out table
181
 * NB: be maintained for params which require action.
182
 */
183
184
static void * v_matchproto_()
185 0
pool_poolherder(void *priv)
186
{
187
        unsigned nwq;
188
        struct pool *pp, *ppx;
189
        uint64_t u;
190
        void *rvp;
191
192 0
        THR_SetName("pool_poolherder");
193 0
        THR_Init();
194 0
        (void)priv;
195
196 0
        nwq = 0;
197 70502
        while (1) {
198 143054
                if (nwq < cache_param->wthread_pools) {
199 72552
                        pp = pool_mkpool(nwq);
200 72552
                        if (pp != NULL) {
201 72552
                                Lck_Lock(&pool_mtx);
202 72552
                                VTAILQ_INSERT_TAIL(&pools, pp, list);
203 72552
                                Lck_Unlock(&pool_mtx);
204 72552
                                VSC_C_main->pools++;
205 72552
                                nwq++;
206 72552
                                continue;
207
                        }
208 70502
                } else if (nwq > cache_param->wthread_pools &&
209 80
                                EXPERIMENT(EXPERIMENT_DROP_POOLS)) {
210 80
                        Lck_Lock(&pool_mtx);
211 80
                        pp = VTAILQ_FIRST(&pools);
212 80
                        CHECK_OBJ_NOTNULL(pp, POOL_MAGIC);
213 80
                        VTAILQ_REMOVE(&pools, pp, list);
214 80
                        VTAILQ_INSERT_TAIL(&pools, pp, list);
215 80
                        if (!pp->die)
216 80
                                nwq--;
217 80
                        Lck_Unlock(&pool_mtx);
218 80
                        if (!pp->die) {
219 80
                                VSL(SLT_Debug, NO_VXID, "XXX Kill Pool %p", pp);
220 80
                                pp->die = 1;
221 80
                                VCA_DestroyPool(pp);
222 80
                                PTOK(pthread_cond_signal(&pp->herder_cond));
223 80
                        }
224 80
                }
225 70502
                (void)sleep(1);
226 70502
                u = 0;
227 70502
                ppx = NULL;
228 70502
                Lck_Lock(&pool_mtx);
229 135106
                VTAILQ_FOREACH(pp, &pools, list) {
230 64604
                        CHECK_OBJ_NOTNULL(pp, POOL_MAGIC);
231
232 64604
                        if (pp->die && pp->nthr == 0)
233 80
                                ppx = pp;
234 64604
                        u += pp->lqueue;
235 64604
                }
236 70502
                if (ppx != NULL) {
237 80
                        VTAILQ_REMOVE(&pools, ppx, list);
238 80
                        PTOK(pthread_join(ppx->herder_thr, &rvp));
239 80
                        PTOK(pthread_cond_destroy(&ppx->herder_cond));
240 80
                        free(ppx->a_stat);
241 80
                        free(ppx->b_stat);
242 80
                        SES_DestroyPool(ppx);
243 80
                        Lck_Delete(&ppx->mtx);
244 80
                        FREE_OBJ(ppx);
245 80
                        VSC_C_main->pools--;
246 80
                }
247 70502
                Lck_Unlock(&pool_mtx);
248 70502
                VSC_C_main->thread_queue_len = u;
249
        }
250
        NEEDLESS(return (NULL));
251
}
252
253
/*--------------------------------------------------------------------*/
254
void
255 516
pan_pool(struct vsb *vsb)
256
{
257
        struct pool *pp;
258
259 516
        VSB_cat(vsb, "pools = {\n");
260 516
        VSB_indent(vsb, 2);
261 1548
        VTAILQ_FOREACH(pp, &pools, list) {
262 1032
                if (PAN_dump_struct(vsb, pp, POOL_MAGIC, "pool"))
263 0
                        continue;
264 1032
                VSB_printf(vsb, "nidle = %u,\n", pp->nidle);
265 1032
                VSB_printf(vsb, "nthr = %u,\n", pp->nthr);
266 1032
                VSB_printf(vsb, "lqueue = %u\n", pp->lqueue);
267 1032
                VSB_indent(vsb, -2);
268 1032
                VSB_cat(vsb, "},\n");
269 1032
        }
270 516
        VSB_indent(vsb, -2);
271 516
        VSB_cat(vsb, "},\n");
272 516
}
273
274
/*--------------------------------------------------------------------*/
275
276
void
277 36676
Pool_Init(void)
278
{
279
280 36676
        Lck_New(&wstat_mtx, lck_wstat);
281 36676
        Lck_New(&pool_mtx, lck_wq);
282 36676
        PTOK(pthread_create(&thr_pool_herder, NULL, pool_poolherder, NULL));
283 110028
        while (!VSC_C_main->pools)
284 73352
                (void)usleep(10000);
285 36676
}