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