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
static void
54 14008
pool_sumstat(const struct VSC_main *src)
55
{
56
57 14008
        Lck_AssertHeld(&wstat_mtx);
58 14008
        VSC_main_Summ(VSC_C_main, src);
59 14008
}
60
61
void
62 6999
Pool_Sumstat(const struct worker *wrk)
63
{
64
65 6999
        Lck_Lock(&wstat_mtx);
66 6999
        pool_sumstat(wrk->stats);
67 6999
        Lck_Unlock(&wstat_mtx);
68 6999
        memset(wrk->stats, 0, sizeof *wrk->stats);
69 6999
}
70
71
int
72 0
Pool_TrySumstat(const struct worker *wrk)
73
{
74 0
        if (Lck_Trylock(&wstat_mtx))
75 0
                return (0);
76 0
        pool_sumstat(wrk->stats);
77 0
        Lck_Unlock(&wstat_mtx);
78 0
        memset(wrk->stats, 0, sizeof *wrk->stats);
79 0
        return (1);
80
}
81
82
/*--------------------------------------------------------------------
83
 * Facility for scheduling a task on any convenient pool.
84
 */
85
86
int
87 486
Pool_Task_Any(struct pool_task *task, enum task_prio prio)
88
{
89
        struct pool *pp;
90
91 486
        Lck_Lock(&pool_mtx);
92 486
        pp = VTAILQ_FIRST(&pools);
93 486
        if (pp != NULL) {
94 486
                VTAILQ_REMOVE(&pools, pp, list);
95 486
                VTAILQ_INSERT_TAIL(&pools, pp, list);
96
        }
97 486
        Lck_Unlock(&pool_mtx);
98 486
        if (pp == NULL)
99 0
                return (-1);
100
        // NB: When we remove pools, is there a race here ?
101 486
        return (Pool_Task(pp, task, prio));
102
}
103
104
/*--------------------------------------------------------------------
105
 * Helper function to update stats for purges under lock
106
 */
107
108
void
109 26
Pool_PurgeStat(unsigned nobj)
110
{
111 26
        Lck_Lock(&wstat_mtx);
112 26
        VSC_C_main->n_purges++;
113 26
        VSC_C_main->n_obj_purged += nobj;
114 26
        Lck_Unlock(&wstat_mtx);
115 26
}
116
117
/*--------------------------------------------------------------------
118
 * Special function to summ stats
119
 */
120
121
void v_matchproto_(task_func_t)
122 7009
pool_stat_summ(struct worker *wrk, void *priv)
123
{
124
        struct VSC_main *src;
125
126 7009
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
127 7009
        CHECK_OBJ_NOTNULL(wrk->pool, POOL_MAGIC);
128 7009
        AN(priv);
129 7009
        src = priv;
130 7009
        Lck_Lock(&wstat_mtx);
131 7009
        pool_sumstat(src);
132 7009
        Lck_Unlock(&wstat_mtx);
133 7009
        memset(src, 0, sizeof *src);
134 7009
        AZ(wrk->pool->b_stat);
135 7009
        wrk->pool->b_stat = src;
136 7009
}
137
138
/*--------------------------------------------------------------------
139
 * Add a thread pool
140
 */
141
142
static struct pool *
143 2720
pool_mkpool(unsigned pool_no)
144
{
145
        struct pool *pp;
146
        int i;
147
148 2720
        ALLOC_OBJ(pp, POOL_MAGIC);
149 2720
        if (pp == NULL)
150 0
                return (NULL);
151 2720
        pp->a_stat = calloc(1, sizeof *pp->a_stat);
152 2720
        AN(pp->a_stat);
153 2720
        pp->b_stat = calloc(1, sizeof *pp->b_stat);
154 2720
        AN(pp->b_stat);
155 2720
        Lck_New(&pp->mtx, lck_wq);
156
157 2720
        VTAILQ_INIT(&pp->idle_queue);
158 2720
        VTAILQ_INIT(&pp->poolsocks);
159 13600
        for (i = 0; i < TASK_QUEUE_END; i++)
160 10880
                VTAILQ_INIT(&pp->queues[i]);
161 2720
        AZ(pthread_cond_init(&pp->herder_cond, NULL));
162 2720
        AZ(pthread_create(&pp->herder_thr, NULL, pool_herder, pp));
163
164 8160
        while (VTAILQ_EMPTY(&pp->idle_queue))
165 2720
                (void)usleep(10000);
166
167 2720
        SES_NewPool(pp, pool_no);
168 2720
        VCA_NewPool(pp);
169
170 2720
        return (pp);
171
}
172
173
/*--------------------------------------------------------------------
174
 * This thread adjusts the number of pools to match the parameter.
175
 *
176
 * NB: This is quite silly.  The master should tell the child through
177
 * NB: CLI when parameters change and an appropriate call-out table
178
 * NB: be maintained for params which require action.
179
 */
180
181
static void * v_matchproto_()
182 1368
pool_poolherder(void *priv)
183
{
184
        unsigned nwq;
185
        struct pool *pp, *ppx;
186
        uint64_t u;
187
        void *rvp;
188
189 1368
        THR_SetName("pool_poolherder");
190 1368
        THR_Init();
191
        (void)priv;
192
193 1368
        nwq = 0;
194
        while (1) {
195 11998
                if (nwq < cache_param->wthread_pools) {
196 2720
                        pp = pool_mkpool(nwq);
197 2720
                        if (pp != NULL) {
198 2720
                                Lck_Lock(&pool_mtx);
199 2720
                                VTAILQ_INSERT_TAIL(&pools, pp, list);
200 2720
                                Lck_Unlock(&pool_mtx);
201 2720
                                VSC_C_main->pools++;
202 2720
                                nwq++;
203 2720
                                continue;
204
                        }
205 3967
                } else if (nwq > cache_param->wthread_pools &&
206 4
                                DO_DEBUG(DBG_DROP_POOLS)) {
207 4
                        Lck_Lock(&pool_mtx);
208 4
                        pp = VTAILQ_FIRST(&pools);
209 4
                        AN(pp);
210 4
                        VTAILQ_REMOVE(&pools, pp, list);
211 4
                        VTAILQ_INSERT_TAIL(&pools, pp, list);
212 4
                        if (!pp->die)
213 4
                                nwq--;
214 4
                        Lck_Unlock(&pool_mtx);
215 4
                        if (!pp->die) {
216 4
                                VSL(SLT_Debug, 0, "XXX Kill Pool %p", pp);
217 4
                                pp->die = 1;
218 4
                                VCA_DestroyPool(pp);
219 4
                                AZ(pthread_cond_signal(&pp->herder_cond));
220
                        }
221
                }
222 3963
                (void)sleep(1);
223 2595
                u = 0;
224 2595
                ppx = NULL;
225 2595
                Lck_Lock(&pool_mtx);
226 7647
                VTAILQ_FOREACH(pp, &pools, list) {
227 5052
                        if (pp->die && pp->nthr == 0)
228 4
                                ppx = pp;
229 5052
                        u += pp->lqueue;
230
                }
231 2595
                if (ppx != NULL) {
232 4
                        VTAILQ_REMOVE(&pools, ppx, list);
233 4
                        AZ(pthread_join(ppx->herder_thr, &rvp));
234 4
                        AZ(pthread_cond_destroy(&ppx->herder_cond));
235 4
                        free(ppx->a_stat);
236 4
                        free(ppx->b_stat);
237 4
                        SES_DestroyPool(ppx);
238 4
                        FREE_OBJ(ppx);
239 4
                        VSC_C_main->pools--;
240
                }
241 2595
                Lck_Unlock(&pool_mtx);
242 2595
                VSC_C_main->thread_queue_len = u;
243
        }
244
        NEEDLESS(return NULL);
245
}
246
247
/*--------------------------------------------------------------------*/
248
249
void
250 1368
Pool_Init(void)
251
{
252
253 1368
        Lck_New(&wstat_mtx, lck_wstat);
254 1368
        Lck_New(&pool_mtx, lck_wq);
255 1368
        AZ(pthread_create(&thr_pool_herder, NULL, pool_poolherder, NULL));
256 5484
        while (!VSC_C_main->pools)
257 2748
                (void)usleep(10000);
258 1368
}