vinyl-cache/bin/vinyld/cache/cache_ban_lurker.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2015 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
 */
31
32
#include "config.h"
33
34
#include "cache_int.h"
35
36
#include "cache_ban.h"
37
#include "cache_objhead.h"
38
39
#include "vtim.h"
40
41
static struct objcore oc_mark_cnt = { .magic = OBJCORE_MAGIC, };
42
static struct objcore oc_mark_end = { .magic = OBJCORE_MAGIC, };
43
static unsigned ban_batch;
44
static unsigned ban_generation;
45
46
pthread_cond_t  ban_lurker_cond;
47
48
void
49 45328
ban_kick_lurker(void)
50
{
51
52 45328
        Lck_AssertHeld(&ban_mtx);
53 45328
        ban_generation++;
54 45328
        PTOK(pthread_cond_signal(&ban_lurker_cond));
55 45328
}
56
57
/*
58
 * ban_cleantail: clean the tail of the ban list up to the first ban which is
59
 * still referenced. For already completed bans, we update statistics
60
 * accordingly, but otherwise just skip the completion step and remove directly
61
 *
62
 * if an obans list is passed, we clean its tail as well
63
 */
64
65
static void
66 45609
ban_cleantail(struct banhead_s *obans)
67
{
68
        struct ban *b, *bt;
69 45609
        struct banhead_s freelist = VTAILQ_HEAD_INITIALIZER(freelist);
70
71
        /* handle the zero-length tail unprotected */
72 45609
        if (VTAILQ_LAST(&ban_head, banhead_s) == VTAILQ_FIRST(&ban_head))
73 41346
                return;
74
75 4263
        Lck_Lock(&ban_mtx);
76 4263
        do {
77 6197
                b = VTAILQ_LAST(&ban_head, banhead_s);
78 6197
                if (b != VTAILQ_FIRST(&ban_head) && b->refcount == 0) {
79 1934
                        assert(VTAILQ_EMPTY(&b->objcore));
80 1934
                        if (b->flags & BANS_FLAG_COMPLETED)
81 1304
                                VSC_C_main->bans_completed--;
82 1934
                        if (b->flags & BANS_FLAG_OBJ)
83 987
                                VSC_C_main->bans_obj--;
84 1934
                        if (b->flags & BANS_FLAG_REQ)
85 441
                                VSC_C_main->bans_req--;
86 1934
                        VSC_C_main->bans--;
87 1934
                        VSC_C_main->bans_deleted++;
88 1934
                        VTAILQ_REMOVE(&ban_head, b, list);
89 1934
                        VTAILQ_INSERT_TAIL(&freelist, b, list);
90 1934
                        bans_persisted_fragmentation +=
91 1934
                            ban_len(b->spec);
92 1934
                        VSC_C_main->bans_persisted_fragmentation =
93 1934
                            bans_persisted_fragmentation;
94 1934
                        ban_info_drop(b->spec, ban_len(b->spec));
95 1934
                } else {
96 4263
                        b = NULL;
97
                }
98 6197
        } while (b != NULL);
99
100 4263
        Lck_Unlock(&ban_mtx);
101
102
        /* oban order is head to tail, freelist tail to head */
103 4263
        if (obans != NULL)
104 3528
                bt = VTAILQ_LAST(obans, banhead_s);
105
        else
106 735
                bt = NULL;
107
108 4263
        if (bt != NULL) {
109 693
                AN(obans);
110 1449
                VTAILQ_FOREACH(b, &freelist, list) {
111 756
                        if (b != bt)
112 525
                                continue;
113 231
                        VTAILQ_REMOVE(obans, b, l_list);
114 231
                        bt = VTAILQ_LAST(obans, banhead_s);
115 231
                        if (bt == NULL)
116 0
                                break;
117 231
                }
118 693
        }
119
120 6197
        VTAILQ_FOREACH_SAFE(b, &freelist, list, bt)
121 1934
                BAN_Free(b);
122
123 4263
        return;
124 45609
}
125
126
/*--------------------------------------------------------------------
127
 * Our task here is somewhat tricky:  The canonical locking order is
128
 * objhead->mtx first, then ban_mtx, because that is the order which
129
 * makes most sense in HSH_Lookup(), but we come the other way.
130
 * We optimistically try to get them the other way, and get out of
131
 * the way if that fails, and retry again later.
132
 *
133
 * To avoid hammering on contested ocs, we first move those behind a marker
134
 * once. When we only have contested ocs left, we stop moving them around and
135
 * re-try them in order.
136
 */
137
138
static struct objcore *
139 2058
ban_lurker_getfirst(struct vsl_log *vsl, struct ban *bt)
140
{
141
        struct objhead *oh;
142
        struct objcore *oc, *noc;
143 2058
        int move_oc = 1;
144
145 2058
        Lck_Lock(&ban_mtx);
146
147 2058
        oc = VTAILQ_FIRST(&bt->objcore);
148 2058
        while (1) {
149 2058
                CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
150
151 2058
                if (oc == &oc_mark_cnt) {
152 861
                        if (VTAILQ_NEXT(oc, ban_list) == &oc_mark_end) {
153
                                /* done with this ban's oc list */
154 861
                                VTAILQ_REMOVE(&bt->objcore, &oc_mark_cnt,
155
                                    ban_list);
156 861
                                VTAILQ_REMOVE(&bt->objcore, &oc_mark_end,
157
                                    ban_list);
158 861
                                oc = NULL;
159 861
                                break;
160
                        }
161 0
                        oc = VTAILQ_NEXT(oc, ban_list);
162 0
                        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
163 0
                        move_oc = 0;
164 1197
                } else if (oc == &oc_mark_end) {
165 0
                        assert(move_oc == 0);
166
167
                        /* hold off to give lookup a chance and reiterate */
168 0
                        VSC_C_main->bans_lurker_contention++;
169 0
                        Lck_Unlock(&ban_mtx);
170 0
                        VSL_Flush(vsl, 0);
171 0
                        VTIM_sleep(cache_param->ban_lurker_holdoff);
172 0
                        Lck_Lock(&ban_mtx);
173
174 0
                        oc = VTAILQ_FIRST(&bt->objcore);
175 0
                        assert(oc == &oc_mark_cnt);
176 0
                        continue;
177
                }
178
179 1197
                assert(oc != &oc_mark_cnt);
180 1197
                assert(oc != &oc_mark_end);
181
182 1197
                oh = oc->objhead;
183 1197
                CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
184 1197
                if (!Lck_Trylock(&oh->mtx)) {
185 1197
                        if (oc->flags & OC_F_BUSY) {
186 0
                                Lck_Unlock(&oh->mtx);
187 1197
                        } else if (oc->refcnt == 0 ||
188 1197
                            oc->flags & (OC_F_DYING | OC_F_FAILED)) {
189
                                /*
190
                                 * We seize the opportunity to remove
191
                                 * the object completely off the ban
192
                                 * list, now that we have both the oh
193
                                 * and ban mutexes.
194
                                 */
195 0
                                noc = VTAILQ_NEXT(oc, ban_list);
196 0
                                VTAILQ_REMOVE(&bt->objcore, oc, ban_list);
197 0
                                oc->ban = NULL;
198 0
                                bt->refcount--;
199 0
                                Lck_Unlock(&oh->mtx);
200 0
                                oc = noc;
201 0
                                continue;
202
                        } else {
203
                                /*
204
                                 * We got the lock, and the oc is not being
205
                                 * dismantled under our feet - grab a ref
206
                                 */
207 1197
                                AZ(oc->flags & OC_F_BUSY);
208 1197
                                oc->refcnt += 1;
209 1197
                                VTAILQ_REMOVE(&bt->objcore, oc, ban_list);
210 1197
                                VTAILQ_INSERT_TAIL(&bt->objcore, oc, ban_list);
211 1197
                                Lck_Unlock(&oh->mtx);
212 1197
                                break;
213
                        }
214 0
                }
215
216 0
                noc = VTAILQ_NEXT(oc, ban_list);
217
218 0
                if (move_oc) {
219
                        /* contested ocs go between the two markers */
220 0
                        VTAILQ_REMOVE(&bt->objcore, oc, ban_list);
221 0
                        VTAILQ_INSERT_BEFORE(&oc_mark_end, oc, ban_list);
222 0
                }
223
224 0
                oc = noc;
225
        }
226 2058
        Lck_Unlock(&ban_mtx);
227 2058
        return (oc);
228
}
229
230
static void
231 1428
ban_lurker_test_ban(struct worker *wrk, struct ban *bt,
232
    struct banhead_s *obans, struct ban *bd, int kill)
233
{
234
        struct ban *bl, *bln;
235
        struct objcore *oc;
236
        unsigned tests;
237
        int i;
238 1428
        uint64_t tested = 0, tested_tests = 0, lok = 0, lokc = 0;
239
240 1428
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
241
242
        /*
243
         * First see if there is anything to do, and if so, insert markers
244
         */
245 1428
        Lck_Lock(&ban_mtx);
246 1428
        oc = VTAILQ_FIRST(&bt->objcore);
247 1428
        if (oc != NULL) {
248 861
                VTAILQ_INSERT_TAIL(&bt->objcore, &oc_mark_cnt, ban_list);
249 861
                VTAILQ_INSERT_TAIL(&bt->objcore, &oc_mark_end, ban_list);
250 861
        }
251 1428
        Lck_Unlock(&ban_mtx);
252 1428
        if (oc == NULL)
253 567
                return;
254
255 2058
        while (1) {
256 2058
                if (++ban_batch > cache_param->ban_lurker_batch) {
257 0
                        (void)Pool_TrySumstat(wrk);
258 0
                        VTIM_sleep(cache_param->ban_lurker_sleep);
259 0
                        ban_batch = 0;
260 0
                }
261 2058
                oc = ban_lurker_getfirst(wrk->vsl, bt);
262 2058
                if (oc == NULL) {
263 861
                        if (tested == 0 && lokc == 0) {
264 399
                                AZ(tested_tests);
265 399
                                AZ(lok);
266 399
                                return;
267
                        }
268 462
                        Lck_Lock(&ban_mtx);
269 462
                        VSC_C_main->bans_lurker_tested += tested;
270 462
                        VSC_C_main->bans_lurker_tests_tested += tested_tests;
271 462
                        VSC_C_main->bans_lurker_obj_killed += lok;
272 462
                        VSC_C_main->bans_lurker_obj_killed_cutoff += lokc;
273 462
                        Lck_Unlock(&ban_mtx);
274 462
                        return;
275
                }
276 1197
                i = 0;
277 1932
                VTAILQ_FOREACH_REVERSE_SAFE(bl, obans, banhead_s, l_list, bln) {
278 1491
                        if (oc->ban != bt) {
279
                                /*
280
                                 * HSH_Lookup() grabbed this oc, killed
281
                                 * it or tested it to top.  We're done.
282
                                 */
283 0
                                break;
284
                        }
285 1491
                        if (bl->flags & BANS_FLAG_COMPLETED) {
286
                                /* Ban was overtaken by new (dup) ban */
287 0
                                VTAILQ_REMOVE(obans, bl, l_list);
288 0
                                continue;
289
                        }
290 1491
                        if (kill == 1)
291 63
                                i = 1;
292
                        else {
293 1428
                                AZ(bl->flags & BANS_FLAG_REQ);
294 1428
                                tests = 0;
295 1428
                                i = ban_evaluate(wrk, bl->spec, oc, NULL,
296
                                    &tests);
297 1428
                                tested++;
298 1428
                                tested_tests += tests;
299
                        }
300 1491
                        if (i) {
301 756
                                if (kill) {
302 126
                                        VSLb(wrk->vsl, SLT_ExpBan,
303
                                            "%ju killed for lurker cutoff",
304 63
                                            VXID(ObjGetXID(wrk, oc)));
305 63
                                        lokc++;
306 63
                                } else {
307 1386
                                        VSLb(wrk->vsl, SLT_ExpBan,
308
                                            "%ju banned by lurker",
309 693
                                            VXID(ObjGetXID(wrk, oc)));
310 693
                                        lok++;
311
                                }
312 756
                                HSH_Kill(oc);
313 756
                                break;
314
                        }
315 735
                }
316 1197
                if (i == 0 && oc->ban == bt) {
317 441
                        Lck_Lock(&ban_mtx);
318 441
                        VSC_C_main->bans_lurker_tested += tested;
319 441
                        VSC_C_main->bans_lurker_tests_tested += tested_tests;
320 441
                        VSC_C_main->bans_lurker_obj_killed += lok;
321 441
                        VSC_C_main->bans_lurker_obj_killed_cutoff += lokc;
322 441
                        tested = tested_tests = lok = lokc = 0;
323 441
                        if (oc->ban == bt && bt != bd) {
324 399
                                bt->refcount--;
325 399
                                VTAILQ_REMOVE(&bt->objcore, oc, ban_list);
326 399
                                oc->ban = bd;
327 399
                                bd->refcount++;
328 399
                                VTAILQ_INSERT_TAIL(&bd->objcore, oc, ban_list);
329 399
                                i = 1;
330 399
                        }
331 441
                        Lck_Unlock(&ban_mtx);
332 441
                        if (i)
333 399
                                ObjSendEvent(wrk, oc, OEV_BANCHG);
334 441
                }
335 1197
                (void)HSH_DerefObjCore(wrk, &oc);
336
        }
337 1428
}
338
339
/*--------------------------------------------------------------------
340
 * Ban lurker thread:
341
 *
342
 * try to move ocs as far up the ban list as possible (to bd)
343
 *
344
 * BANS_FLAG_REQ bans act as barriers, for bans further down, ocs get moved to
345
 * them. But still all bans up to the initial bd get checked and marked
346
 * completed.
347
 */
348
349
static vtim_dur
350 45609
ban_lurker_work(struct worker *wrk)
351
{
352
        struct ban *b, *bd;
353
        struct banhead_s obans;
354
        vtim_real d;
355
        vtim_dur dt, n;
356 45609
        unsigned count = 0, cutoff = UINT_MAX;
357
358 45609
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
359
360 45609
        dt = 49.62;             // Random, non-magic
361 45609
        if (cache_param->ban_lurker_sleep == 0) {
362 1050
                ban_cleantail(NULL);
363 1050
                return (dt);
364
        }
365 44559
        if (cache_param->ban_cutoff > 0)
366 294
                cutoff = cache_param->ban_cutoff;
367
368 44559
        Lck_Lock(&ban_mtx);
369 44559
        b = ban_start;
370 44559
        Lck_Unlock(&ban_mtx);
371 44559
        d = VTIM_real() - cache_param->ban_lurker_age;
372 44559
        bd = NULL;
373 44559
        VTAILQ_INIT(&obans);
374 100708
        for (; b != NULL; b = VTAILQ_NEXT(b, list), count++) {
375 56149
                if (bd != NULL)
376 2856
                        ban_lurker_test_ban(wrk, b, &obans, bd,
377 1428
                            count > cutoff ? 1 : 0);
378 56149
                if (b->flags & BANS_FLAG_COMPLETED)
379 44851
                        continue;
380 11298
                if (b->flags & BANS_FLAG_REQ && count <= cutoff) {
381 1932
                        if (bd != NULL)
382 105
                                bd = VTAILQ_NEXT(b, list);
383 1932
                        continue;
384
                }
385 9366
                n = ban_time(b->spec) - d;
386 9366
                if (n < 0) {
387 1092
                        VTAILQ_INSERT_TAIL(&obans, b, l_list);
388 1092
                        if (bd == NULL)
389 714
                                bd = b;
390 9366
                } else if (n < dt) {
391 525
                        dt = n;
392 525
                }
393 9366
        }
394
395
        /*
396
         * conceptually, all obans are now completed. Remove the tail.
397
         * If any bans to be completed remain after the tail is cut,
398
         * mark them completed
399
         */
400 44559
        ban_cleantail(&obans);
401
402 44559
        if (VTAILQ_FIRST(&obans) == NULL)
403 43845
                return (dt);
404
405 714
        Lck_Lock(&ban_mtx);
406 1575
        VTAILQ_FOREACH(b, &obans, l_list)
407 861
                ban_mark_completed(b);
408 714
        Lck_Unlock(&ban_mtx);
409 714
        return (dt);
410 45609
}
411
412
void * v_matchproto_(bgthread_t)
413 20841
ban_lurker(struct worker *wrk, void *priv)
414
{
415
        struct vsl_log vsl;
416
        vtim_dur dt;
417 20841
        unsigned gen = ban_generation + 1;
418
419 20841
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
420 20841
        AZ(priv);
421
422 20841
        VSL_Alloc(&vsl);
423 20841
        AZ(wrk->vsl);
424 20841
        wrk->vsl = &vsl;
425
426 66231
        while (!ban_shutdown) {
427 45390
                dt = ban_lurker_work(wrk);
428 45390
                if (DO_DEBUG(DBG_LURKER))
429 609
                        VSLb(&vsl, SLT_Debug, "lurker: sleep = %lf", dt);
430 45390
                Lck_Lock(&ban_mtx);
431 45390
                if (gen == ban_generation) {
432 24768
                        Pool_Sumstat(wrk);
433 24768
                        (void)Lck_CondWaitTimeout(
434 24768
                            &ban_lurker_cond, &ban_mtx, dt);
435 24768
                        ban_batch = 0;
436 24768
                }
437 45390
                gen = ban_generation;
438 45390
                Lck_Unlock(&ban_mtx);
439
        }
440 20841
        wrk->vsl = NULL;
441 20841
        VSL_Free(&vsl);
442 20841
        pthread_exit(0);
443
        NEEDLESS(return (NULL));
444
}