| | varnish-cache/bin/varnishd/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_varnishd.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 |
79061 |
ban_kick_lurker(void) |
50 |
|
{ |
51 |
|
|
52 |
79061 |
Lck_AssertHeld(&ban_mtx); |
53 |
79061 |
ban_generation++; |
54 |
79061 |
PTOK(pthread_cond_signal(&ban_lurker_cond)); |
55 |
79061 |
} |
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 |
79560 |
ban_cleantail(struct banhead_s *obans) |
67 |
|
{ |
68 |
|
struct ban *b, *bt; |
69 |
79560 |
struct banhead_s freelist = VTAILQ_HEAD_INITIALIZER(freelist); |
70 |
|
|
71 |
|
/* handle the zero-length tail unprotected */ |
72 |
79560 |
if (VTAILQ_LAST(&ban_head, banhead_s) == VTAILQ_FIRST(&ban_head)) |
73 |
72360 |
return; |
74 |
|
|
75 |
7200 |
Lck_Lock(&ban_mtx); |
76 |
7200 |
do { |
77 |
10000 |
b = VTAILQ_LAST(&ban_head, banhead_s); |
78 |
10000 |
if (b != VTAILQ_FIRST(&ban_head) && b->refcount == 0) { |
79 |
2800 |
assert(VTAILQ_EMPTY(&b->objcore)); |
80 |
2800 |
if (b->flags & BANS_FLAG_COMPLETED) |
81 |
1560 |
VSC_C_main->bans_completed--; |
82 |
2800 |
if (b->flags & BANS_FLAG_OBJ) |
83 |
1120 |
VSC_C_main->bans_obj--; |
84 |
2800 |
if (b->flags & BANS_FLAG_REQ) |
85 |
840 |
VSC_C_main->bans_req--; |
86 |
2800 |
VSC_C_main->bans--; |
87 |
2800 |
VSC_C_main->bans_deleted++; |
88 |
2800 |
VTAILQ_REMOVE(&ban_head, b, list); |
89 |
2800 |
VTAILQ_INSERT_TAIL(&freelist, b, list); |
90 |
2800 |
bans_persisted_fragmentation += |
91 |
2800 |
ban_len(b->spec); |
92 |
2800 |
VSC_C_main->bans_persisted_fragmentation = |
93 |
2800 |
bans_persisted_fragmentation; |
94 |
2800 |
ban_info_drop(b->spec, ban_len(b->spec)); |
95 |
2800 |
} else { |
96 |
7200 |
b = NULL; |
97 |
|
} |
98 |
10000 |
} while (b != NULL); |
99 |
|
|
100 |
7200 |
Lck_Unlock(&ban_mtx); |
101 |
|
|
102 |
|
/* oban order is head to tail, freelist tail to head */ |
103 |
7200 |
if (obans != NULL) |
104 |
5800 |
bt = VTAILQ_LAST(obans, banhead_s); |
105 |
|
else |
106 |
1400 |
bt = NULL; |
107 |
|
|
108 |
7200 |
if (bt != NULL) { |
109 |
280 |
AN(obans); |
110 |
360 |
VTAILQ_FOREACH(b, &freelist, list) { |
111 |
80 |
if (b != bt) |
112 |
40 |
continue; |
113 |
40 |
VTAILQ_REMOVE(obans, b, l_list); |
114 |
40 |
bt = VTAILQ_LAST(obans, banhead_s); |
115 |
40 |
if (bt == NULL) |
116 |
0 |
break; |
117 |
40 |
} |
118 |
280 |
} |
119 |
|
|
120 |
10000 |
VTAILQ_FOREACH_SAFE(b, &freelist, list, bt) |
121 |
2800 |
BAN_Free(b); |
122 |
|
|
123 |
7200 |
return; |
124 |
79560 |
} |
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 |
1360 |
ban_lurker_getfirst(struct vsl_log *vsl, struct ban *bt) |
140 |
|
{ |
141 |
|
struct objhead *oh; |
142 |
|
struct objcore *oc, *noc; |
143 |
1360 |
int move_oc = 1; |
144 |
|
|
145 |
1360 |
Lck_Lock(&ban_mtx); |
146 |
|
|
147 |
1360 |
oc = VTAILQ_FIRST(&bt->objcore); |
148 |
1360 |
while (1) { |
149 |
1360 |
CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC); |
150 |
|
|
151 |
1360 |
if (oc == &oc_mark_cnt) { |
152 |
600 |
if (VTAILQ_NEXT(oc, ban_list) == &oc_mark_end) { |
153 |
|
/* done with this ban's oc list */ |
154 |
600 |
VTAILQ_REMOVE(&bt->objcore, &oc_mark_cnt, |
155 |
|
ban_list); |
156 |
600 |
VTAILQ_REMOVE(&bt->objcore, &oc_mark_end, |
157 |
|
ban_list); |
158 |
600 |
oc = NULL; |
159 |
600 |
break; |
160 |
|
} |
161 |
0 |
oc = VTAILQ_NEXT(oc, ban_list); |
162 |
0 |
CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC); |
163 |
0 |
move_oc = 0; |
164 |
760 |
} 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 |
760 |
assert(oc != &oc_mark_cnt); |
180 |
760 |
assert(oc != &oc_mark_end); |
181 |
|
|
182 |
760 |
oh = oc->objhead; |
183 |
760 |
CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC); |
184 |
760 |
if (!Lck_Trylock(&oh->mtx)) { |
185 |
760 |
if (oc->flags & OC_F_BUSY) { |
186 |
0 |
Lck_Unlock(&oh->mtx); |
187 |
760 |
} else if (oc->refcnt == 0 || |
188 |
760 |
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 |
760 |
AZ(oc->flags & OC_F_BUSY); |
208 |
760 |
oc->refcnt += 1; |
209 |
760 |
VTAILQ_REMOVE(&bt->objcore, oc, ban_list); |
210 |
760 |
VTAILQ_INSERT_TAIL(&bt->objcore, oc, ban_list); |
211 |
760 |
Lck_Unlock(&oh->mtx); |
212 |
760 |
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 |
1360 |
Lck_Unlock(&ban_mtx); |
227 |
1360 |
return (oc); |
228 |
|
} |
229 |
|
|
230 |
|
static void |
231 |
840 |
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 |
840 |
uint64_t tested = 0, tested_tests = 0, lok = 0, lokc = 0; |
239 |
|
|
240 |
840 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
241 |
|
|
242 |
|
/* |
243 |
|
* First see if there is anything to do, and if so, insert markers |
244 |
|
*/ |
245 |
840 |
Lck_Lock(&ban_mtx); |
246 |
840 |
oc = VTAILQ_FIRST(&bt->objcore); |
247 |
840 |
if (oc != NULL) { |
248 |
600 |
VTAILQ_INSERT_TAIL(&bt->objcore, &oc_mark_cnt, ban_list); |
249 |
600 |
VTAILQ_INSERT_TAIL(&bt->objcore, &oc_mark_end, ban_list); |
250 |
600 |
} |
251 |
840 |
Lck_Unlock(&ban_mtx); |
252 |
840 |
if (oc == NULL) |
253 |
240 |
return; |
254 |
|
|
255 |
1360 |
while (1) { |
256 |
1360 |
if (++ban_batch > cache_param->ban_lurker_batch) { |
257 |
0 |
VTIM_sleep(cache_param->ban_lurker_sleep); |
258 |
0 |
ban_batch = 0; |
259 |
0 |
} |
260 |
1360 |
oc = ban_lurker_getfirst(wrk->vsl, bt); |
261 |
1360 |
if (oc == NULL) { |
262 |
600 |
if (tested == 0 && lokc == 0) { |
263 |
200 |
AZ(tested_tests); |
264 |
200 |
AZ(lok); |
265 |
200 |
return; |
266 |
|
} |
267 |
400 |
Lck_Lock(&ban_mtx); |
268 |
400 |
VSC_C_main->bans_lurker_tested += tested; |
269 |
400 |
VSC_C_main->bans_lurker_tests_tested += tested_tests; |
270 |
400 |
VSC_C_main->bans_lurker_obj_killed += lok; |
271 |
400 |
VSC_C_main->bans_lurker_obj_killed_cutoff += lokc; |
272 |
400 |
Lck_Unlock(&ban_mtx); |
273 |
400 |
return; |
274 |
|
} |
275 |
760 |
i = 0; |
276 |
1120 |
VTAILQ_FOREACH_REVERSE_SAFE(bl, obans, banhead_s, l_list, bln) { |
277 |
920 |
if (oc->ban != bt) { |
278 |
|
/* |
279 |
|
* HSH_Lookup() grabbed this oc, killed |
280 |
|
* it or tested it to top. We're done. |
281 |
|
*/ |
282 |
0 |
break; |
283 |
|
} |
284 |
920 |
if (bl->flags & BANS_FLAG_COMPLETED) { |
285 |
|
/* Ban was overtaken by new (dup) ban */ |
286 |
0 |
VTAILQ_REMOVE(obans, bl, l_list); |
287 |
0 |
continue; |
288 |
|
} |
289 |
920 |
if (kill == 1) |
290 |
120 |
i = 1; |
291 |
|
else { |
292 |
800 |
AZ(bl->flags & BANS_FLAG_REQ); |
293 |
800 |
tests = 0; |
294 |
800 |
i = ban_evaluate(wrk, bl->spec, oc, NULL, |
295 |
|
&tests); |
296 |
800 |
tested++; |
297 |
800 |
tested_tests += tests; |
298 |
|
} |
299 |
920 |
if (i) { |
300 |
560 |
if (kill) { |
301 |
240 |
VSLb(wrk->vsl, SLT_ExpBan, |
302 |
|
"%ju killed for lurker cutoff", |
303 |
120 |
VXID(ObjGetXID(wrk, oc))); |
304 |
120 |
lokc++; |
305 |
120 |
} else { |
306 |
880 |
VSLb(wrk->vsl, SLT_ExpBan, |
307 |
|
"%ju banned by lurker", |
308 |
440 |
VXID(ObjGetXID(wrk, oc))); |
309 |
440 |
lok++; |
310 |
|
} |
311 |
560 |
HSH_Kill(oc); |
312 |
560 |
break; |
313 |
|
} |
314 |
360 |
} |
315 |
760 |
if (i == 0 && oc->ban == bt) { |
316 |
200 |
Lck_Lock(&ban_mtx); |
317 |
200 |
VSC_C_main->bans_lurker_tested += tested; |
318 |
200 |
VSC_C_main->bans_lurker_tests_tested += tested_tests; |
319 |
200 |
VSC_C_main->bans_lurker_obj_killed += lok; |
320 |
200 |
VSC_C_main->bans_lurker_obj_killed_cutoff += lokc; |
321 |
200 |
tested = tested_tests = lok = lokc = 0; |
322 |
200 |
if (oc->ban == bt && bt != bd) { |
323 |
120 |
bt->refcount--; |
324 |
120 |
VTAILQ_REMOVE(&bt->objcore, oc, ban_list); |
325 |
120 |
oc->ban = bd; |
326 |
120 |
bd->refcount++; |
327 |
120 |
VTAILQ_INSERT_TAIL(&bd->objcore, oc, ban_list); |
328 |
120 |
i = 1; |
329 |
120 |
} |
330 |
200 |
Lck_Unlock(&ban_mtx); |
331 |
200 |
if (i) |
332 |
120 |
ObjSendEvent(wrk, oc, OEV_BANCHG); |
333 |
200 |
} |
334 |
760 |
(void)HSH_DerefObjCore(wrk, &oc, 0); |
335 |
|
} |
336 |
840 |
} |
337 |
|
|
338 |
|
/*-------------------------------------------------------------------- |
339 |
|
* Ban lurker thread: |
340 |
|
* |
341 |
|
* try to move ocs as far up the ban list as possible (to bd) |
342 |
|
* |
343 |
|
* BANS_FLAG_REQ bans act as barriers, for bans further down, ocs get moved to |
344 |
|
* them. But still all bans up to the initial bd get checked and marked |
345 |
|
* completed. |
346 |
|
*/ |
347 |
|
|
348 |
|
static vtim_dur |
349 |
79560 |
ban_lurker_work(struct worker *wrk) |
350 |
|
{ |
351 |
|
struct ban *b, *bd; |
352 |
|
struct banhead_s obans; |
353 |
|
vtim_real d; |
354 |
|
vtim_dur dt, n; |
355 |
79560 |
unsigned count = 0, cutoff = UINT_MAX; |
356 |
|
|
357 |
79560 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
358 |
|
|
359 |
79560 |
dt = 49.62; // Random, non-magic |
360 |
79560 |
if (cache_param->ban_lurker_sleep == 0) { |
361 |
2000 |
ban_cleantail(NULL); |
362 |
2000 |
return (dt); |
363 |
|
} |
364 |
77560 |
if (cache_param->ban_cutoff > 0) |
365 |
560 |
cutoff = cache_param->ban_cutoff; |
366 |
|
|
367 |
77560 |
Lck_Lock(&ban_mtx); |
368 |
77560 |
b = ban_start; |
369 |
77560 |
Lck_Unlock(&ban_mtx); |
370 |
77560 |
d = VTIM_real() - cache_param->ban_lurker_age; |
371 |
77560 |
bd = NULL; |
372 |
77560 |
VTAILQ_INIT(&obans); |
373 |
171519 |
for (; b != NULL; b = VTAILQ_NEXT(b, list), count++) { |
374 |
93959 |
if (bd != NULL) |
375 |
1680 |
ban_lurker_test_ban(wrk, b, &obans, bd, |
376 |
840 |
count > cutoff ? 1 : 0); |
377 |
93959 |
if (b->flags & BANS_FLAG_COMPLETED) |
378 |
76719 |
continue; |
379 |
17240 |
if (b->flags & BANS_FLAG_REQ && count <= cutoff) { |
380 |
3640 |
if (bd != NULL) |
381 |
200 |
bd = VTAILQ_NEXT(b, list); |
382 |
3640 |
continue; |
383 |
|
} |
384 |
13600 |
n = ban_time(b->spec) - d; |
385 |
13600 |
if (n < 0) { |
386 |
640 |
VTAILQ_INSERT_TAIL(&obans, b, l_list); |
387 |
640 |
if (bd == NULL) |
388 |
320 |
bd = b; |
389 |
13600 |
} else if (n < dt) { |
390 |
960 |
dt = n; |
391 |
960 |
} |
392 |
13600 |
} |
393 |
|
|
394 |
|
/* |
395 |
|
* conceptually, all obans are now completed. Remove the tail. |
396 |
|
* If any bans to be completed remain after the tail is cut, |
397 |
|
* mark them completed |
398 |
|
*/ |
399 |
77560 |
ban_cleantail(&obans); |
400 |
|
|
401 |
77560 |
if (VTAILQ_FIRST(&obans) == NULL) |
402 |
77240 |
return (dt); |
403 |
|
|
404 |
320 |
Lck_Lock(&ban_mtx); |
405 |
920 |
VTAILQ_FOREACH(b, &obans, l_list) |
406 |
600 |
ban_mark_completed(b); |
407 |
320 |
Lck_Unlock(&ban_mtx); |
408 |
320 |
return (dt); |
409 |
79560 |
} |
410 |
|
|
411 |
|
void * v_matchproto_(bgthread_t) |
412 |
36540 |
ban_lurker(struct worker *wrk, void *priv) |
413 |
|
{ |
414 |
|
struct vsl_log vsl; |
415 |
|
vtim_dur dt; |
416 |
36540 |
unsigned gen = ban_generation + 1; |
417 |
|
|
418 |
36540 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
419 |
36540 |
AZ(priv); |
420 |
|
|
421 |
36540 |
VSL_Setup(&vsl, NULL, 0); |
422 |
36540 |
AZ(wrk->vsl); |
423 |
36540 |
wrk->vsl = &vsl; |
424 |
|
|
425 |
115680 |
while (!ban_shutdown) { |
426 |
79140 |
dt = ban_lurker_work(wrk); |
427 |
79140 |
if (DO_DEBUG(DBG_LURKER)) |
428 |
1160 |
VSLb(&vsl, SLT_Debug, "lurker: sleep = %lf", dt); |
429 |
79140 |
Lck_Lock(&ban_mtx); |
430 |
79140 |
if (gen == ban_generation) { |
431 |
43020 |
Pool_Sumstat(wrk); |
432 |
43020 |
(void)Lck_CondWaitTimeout( |
433 |
43020 |
&ban_lurker_cond, &ban_mtx, dt); |
434 |
43020 |
ban_batch = 0; |
435 |
43020 |
} |
436 |
79140 |
gen = ban_generation; |
437 |
79140 |
Lck_Unlock(&ban_mtx); |
438 |
|
} |
439 |
36540 |
wrk->vsl = NULL; |
440 |
36540 |
pthread_exit(0); |
441 |
|
NEEDLESS(return (NULL)); |
442 |
|
} |