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 5896
pool_sumstat(const struct VSC_main *src)
55
{
56
57 5896
        Lck_AssertHeld(&wstat_mtx);
58 5896
        VSC_main_Summ(VSC_C_main, src);
59 5896
}
60
61
void
62 3174
Pool_Sumstat(const struct worker *wrk)
63
{
64
65 3174
        Lck_Lock(&wstat_mtx);
66 3174
        pool_sumstat(wrk->stats);
67 3174
        Lck_Unlock(&wstat_mtx);
68 3174
        memset(wrk->stats, 0, sizeof *wrk->stats);
69 3174
}
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 155
Pool_Task_Any(struct pool_task *task, enum task_prio prio)
88
{
89
        struct pool *pp;
90
91 155
        Lck_Lock(&pool_mtx);
92 155
        pp = VTAILQ_FIRST(&pools);
93 155
        if (pp != NULL) {
94 155
                VTAILQ_REMOVE(&pools, pp, list);
95 155
                VTAILQ_INSERT_TAIL(&pools, pp, list);
96
        }
97 155
        Lck_Unlock(&pool_mtx);
98 155
        if (pp == NULL)
99 0
                return (-1);
100
        // NB: When we remove pools, is there a race here ?
101 155
        return (Pool_Task(pp, task, prio));
102
}
103
104
/*--------------------------------------------------------------------
105
 * Helper function to update stats for purges under lock
106
 */
107
108
void
109 13
Pool_PurgeStat(unsigned nobj)
110
{
111 13
        Lck_Lock(&wstat_mtx);
112 13
        VSC_C_main->n_purges++;
113 13
        VSC_C_main->n_obj_purged += nobj;
114 13
        Lck_Unlock(&wstat_mtx);
115 13
}
116
117
/*--------------------------------------------------------------------
118
 * Special function to summ stats
119
 */
120
121
void v_matchproto_(task_func_t)
122 2722
pool_stat_summ(struct worker *wrk, void *priv)
123
{
124
        struct VSC_main *src;
125
126 2722
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
127 2722
        CHECK_OBJ_NOTNULL(wrk->pool, POOL_MAGIC);
128 2722
        AN(priv);
129 2722
        src = priv;
130 2722
        Lck_Lock(&wstat_mtx);
131 2722
        pool_sumstat(src);
132 2722
        Lck_Unlock(&wstat_mtx);
133 2722
        memset(src, 0, sizeof *src);
134 2722
        AZ(wrk->pool->b_stat);
135 2722
        wrk->pool->b_stat = src;
136 2722
}
137
138
/*--------------------------------------------------------------------
139
 * Add a thread pool
140
 */
141
142
static struct pool *
143 1221
pool_mkpool(unsigned pool_no)
144
{
145
        struct pool *pp;
146
        int i;
147
148 1221
        ALLOC_OBJ(pp, POOL_MAGIC);
149 1221
        if (pp == NULL)
150 0
                return (NULL);
151 1221
        pp->a_stat = calloc(1, sizeof *pp->a_stat);
152 1221
        AN(pp->a_stat);
153 1221
        pp->b_stat = calloc(1, sizeof *pp->b_stat);
154 1221
        AN(pp->b_stat);
155 1221
        Lck_New(&pp->mtx, lck_wq);
156
157 1221
        VTAILQ_INIT(&pp->idle_queue);
158 1221
        VTAILQ_INIT(&pp->poolsocks);
159 6105
        for (i = 0; i < TASK_QUEUE_END; i++)
160 4884
                VTAILQ_INIT(&pp->queues[i]);
161 1221
        AZ(pthread_cond_init(&pp->herder_cond, NULL));
162 1221
        AZ(pthread_create(&pp->herder_thr, NULL, pool_herder, pp));
163
164 3663
        while (VTAILQ_EMPTY(&pp->idle_queue))
165 1221
                (void)usleep(10000);
166
167 1221
        SES_NewPool(pp, pool_no);
168 1221
        VCA_NewPool(pp);
169
170 1221
        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 614
pool_poolherder(void *priv)
183
{
184
        unsigned nwq;
185
        struct pool *pp, *ppx;
186
        uint64_t u;
187
        void *rvp;
188
189 614
        THR_SetName("pool_poolherder");
190 614
        THR_Init();
191
        (void)priv;
192
193 614
        nwq = 0;
194
        while (1) {
195 2941
                if (nwq < cache_param->wthread_pools) {
196 1221
                        pp = pool_mkpool(nwq);
197 1221
                        if (pp != NULL) {
198 1221
                                Lck_Lock(&pool_mtx);
199 1221
                                VTAILQ_INSERT_TAIL(&pools, pp, list);
200 1221
                                Lck_Unlock(&pool_mtx);
201 1221
                                VSC_C_main->pools++;
202 1221
                                nwq++;
203 1221
                                continue;
204
                        }
205 1722
                } else if (nwq > cache_param->wthread_pools &&
206 2
                                DO_DEBUG(DBG_DROP_POOLS)) {
207 2
                        Lck_Lock(&pool_mtx);
208 2
                        pp = VTAILQ_FIRST(&pools);
209 2
                        AN(pp);
210 2
                        VTAILQ_REMOVE(&pools, pp, list);
211 2
                        VTAILQ_INSERT_TAIL(&pools, pp, list);
212 2
                        if (!pp->die)
213 2
                                nwq--;
214 2
                        Lck_Unlock(&pool_mtx);
215 2
                        if (!pp->die) {
216 2
                                VSL(SLT_Debug, 0, "XXX Kill Pool %p", pp);
217 2
                                pp->die = 1;
218 2
                                VCA_DestroyPool(pp);
219 2
                                AZ(pthread_cond_signal(&pp->herder_cond));
220
                        }
221
                }
222 1720
                (void)sleep(1);
223 1106
                u = 0;
224 1106
                ppx = NULL;
225 1106
                Lck_Lock(&pool_mtx);
226 3253
                VTAILQ_FOREACH(pp, &pools, list) {
227 2147
                        if (pp->die && pp->nthr == 0)
228 2
                                ppx = pp;
229 2147
                        u += pp->lqueue;
230
                }
231 1106
                if (ppx != NULL) {
232 2
                        VTAILQ_REMOVE(&pools, ppx, list);
233 2
                        AZ(pthread_join(ppx->herder_thr, &rvp));
234 2
                        AZ(pthread_cond_destroy(&ppx->herder_cond));
235 2
                        free(ppx->a_stat);
236 2
                        free(ppx->b_stat);
237 2
                        SES_DestroyPool(ppx);
238 2
                        FREE_OBJ(ppx);
239 2
                        VSC_C_main->pools--;
240
                }
241 1106
                Lck_Unlock(&pool_mtx);
242 1106
                VSC_C_main->thread_queue_len = u;
243 2327
        }
244
        NEEDLESS(return NULL);
245
}
246
247
/*--------------------------------------------------------------------*/
248
249
void
250 614
Pool_Init(void)
251
{
252
253 614
        Lck_New(&wstat_mtx, lck_wstat);
254 614
        Lck_New(&pool_mtx, lck_wq);
255 614
        AZ(pthread_create(&thr_pool_herder, NULL, pool_poolherder, NULL));
256 2456
        while (!VSC_C_main->pools)
257 1228
                (void)usleep(10000);
258 614
}