]> git.ipfire.org Git - thirdparty/squid.git/blob - src/store.cc
sigh, undo StoreEntry->flags changes to make it a structure with single-bit ints
[thirdparty/squid.git] / src / store.cc
1
2 /*
3 * $Id: store.cc,v 1.462 1998/09/19 17:06:12 wessels Exp $
4 *
5 * DEBUG: section 20 Storage Manager
6 * AUTHOR: Harvest Derived
7 *
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * Duane Wessels and the University of California San Diego. Please
16 * see the COPYRIGHT file for full details. Squid incorporates
17 * software developed and/or copyrighted by other sources. Please see
18 * the CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36 #include "squid.h"
37
38 #define REBUILD_TIMESTAMP_DELTA_MAX 2
39
40 #define STORE_IN_MEM_BUCKETS (229)
41
42 const char *memStatusStr[] =
43 {
44 "NOT_IN_MEMORY",
45 "IN_MEMORY"
46 };
47
48 const char *pingStatusStr[] =
49 {
50 "PING_NONE",
51 "PING_WAITING",
52 "PING_TIMEOUT",
53 "PING_DONE"
54 };
55
56 const char *storeStatusStr[] =
57 {
58 "STORE_OK",
59 "STORE_PENDING",
60 "STORE_ABORTED"
61 };
62
63 const char *swapStatusStr[] =
64 {
65 "SWAPOUT_NONE",
66 "SWAPOUT_OPENING",
67 "SWAPOUT_WRITING",
68 "SWAPOUT_DONE"
69 };
70
71 typedef struct lock_ctrl_t {
72 SIH *callback;
73 void *callback_data;
74 StoreEntry *e;
75 } lock_ctrl_t;
76
77 /*
78 * local function prototypes
79 */
80 static int storeCheckExpired(const StoreEntry *);
81 static int storeEntryLocked(const StoreEntry *);
82 static int storeEntryValidLength(const StoreEntry *);
83 static void storeGetMemSpace(int);
84 static void storeHashDelete(StoreEntry *);
85 static MemObject *new_MemObject(const char *, const char *);
86 static void destroy_MemObject(StoreEntry *);
87 static FREE destroy_StoreEntry;
88 static void storePurgeMem(StoreEntry *);
89 static int getKeyCounter(void);
90 static int storeKeepInMemory(const StoreEntry *);
91 static OBJH storeCheckCachableStats;
92
93 /*
94 * local variables
95 */
96 static dlink_list inmem_list;
97 static int store_pages_high = 0;
98 static int store_pages_low = 0;
99 static int store_swap_high = 0;
100 static int store_swap_low = 0;
101 static int store_swap_mid = 0;
102 static int store_maintain_rate;
103
104 static MemObject *
105 new_MemObject(const char *url, const char *log_url)
106 {
107 MemObject *mem = memAllocate(MEM_MEMOBJECT);
108 mem->reply = httpReplyCreate();
109 mem->url = xstrdup(url);
110 mem->log_url = xstrdup(log_url);
111 mem->swapout.fd = -1;
112 mem->object_sz = -1;
113 mem->fd = -1;
114 /* XXX account log_url */
115 debug(20, 3) ("new_MemObject: returning %p\n", mem);
116 return mem;
117 }
118
119 StoreEntry *
120 new_StoreEntry(int mem_obj_flag, const char *url, const char *log_url)
121 {
122 StoreEntry *e = NULL;
123 e = memAllocate(MEM_STOREENTRY);
124 if (mem_obj_flag)
125 e->mem_obj = new_MemObject(url, log_url);
126 debug(20, 3) ("new_StoreEntry: returning %p\n", e);
127 e->expires = e->lastmod = e->lastref = e->timestamp = -1;
128 return e;
129 }
130
131 static void
132 destroy_MemObject(StoreEntry * e)
133 {
134 MemObject *mem = e->mem_obj;
135 const Ctx ctx = ctx_enter(mem->url);
136 debug(20, 3) ("destroy_MemObject: destroying %p\n", mem);
137 e->mem_obj = NULL;
138 if (!shutting_down)
139 assert(mem->swapout.fd == -1);
140 stmemFree(&mem->data_hdr);
141 mem->inmem_hi = 0;
142 /* XXX account log_url */
143 #if USE_ASYNC_IO
144 while (mem->clients != NULL)
145 storeUnregister(e, mem->clients->callback_data);
146 #endif
147 /*
148 * There is no way to abort FD-less clients, so they might
149 * still have mem->clients set if mem->fd == -1
150 */
151 assert(mem->fd == -1 || mem->clients == NULL);
152 httpReplyDestroy(mem->reply);
153 requestUnlink(mem->request);
154 mem->request = NULL;
155 ctx_exit(ctx); /* must exit before we free mem->url */
156 safe_free(mem->url);
157 safe_free(mem->log_url);
158 memFree(MEM_MEMOBJECT, mem);
159 }
160
161 static void
162 destroy_StoreEntry(void *data)
163 {
164 StoreEntry *e = data;
165 debug(20, 3) ("destroy_StoreEntry: destroying %p\n", e);
166 assert(e != NULL);
167 if (e->mem_obj)
168 destroy_MemObject(e);
169 storeHashDelete(e);
170 assert(e->key == NULL);
171 memFree(MEM_STOREENTRY, e);
172 }
173
174 /* ----- INTERFACE BETWEEN STORAGE MANAGER AND HASH TABLE FUNCTIONS --------- */
175
176 void
177 storeHashInsert(StoreEntry * e, const cache_key * key)
178 {
179 debug(20, 3) ("storeHashInsert: Inserting Entry %p key '%s'\n",
180 e, storeKeyText(key));
181 e->key = storeKeyDup(key);
182 hash_join(store_table, (hash_link *) e);
183 dlinkAdd(e, &e->lru, &store_list);
184 }
185
186 static void
187 storeHashDelete(StoreEntry * e)
188 {
189 hash_remove_link(store_table, (hash_link *) e);
190 dlinkDelete(&e->lru, &store_list);
191 storeKeyFree(e->key);
192 e->key = NULL;
193 }
194
195 /* -------------------------------------------------------------------------- */
196
197
198 /* get rid of memory copy of the object */
199 /* Only call this if storeCheckPurgeMem(e) returns 1 */
200 static void
201 storePurgeMem(StoreEntry * e)
202 {
203 if (e->mem_obj == NULL)
204 return;
205 debug(20, 3) ("storePurgeMem: Freeing memory-copy of %s\n",
206 storeKeyText(e->key));
207 storeSetMemStatus(e, NOT_IN_MEMORY);
208 destroy_MemObject(e);
209 if (e->swap_status != SWAPOUT_DONE)
210 storeRelease(e);
211 }
212
213 void
214 storeLockObject(StoreEntry * e)
215 {
216 if (e->lock_count++ == 0) {
217 dlinkDelete(&e->lru, &store_list);
218 dlinkAdd(e, &e->lru, &store_list);
219 }
220 debug(20, 3) ("storeLockObject: key '%s' count=%d\n",
221 storeKeyText(e->key), (int) e->lock_count);
222 e->lastref = squid_curtime;
223 }
224
225 void
226 storeReleaseRequest(StoreEntry * e)
227 {
228 if (EBIT_TEST(e->flags, RELEASE_REQUEST))
229 return;
230 assert(storeEntryLocked(e));
231 debug(20, 3) ("storeReleaseRequest: '%s'\n", storeKeyText(e->key));
232 EBIT_SET(e->flags, RELEASE_REQUEST);
233 /*
234 * Clear cachable flag here because we might get called before
235 * anyone else even looks at the cachability flag. Also, this
236 * prevents httpMakePublic from really setting a public key.
237 */
238 EBIT_CLR(e->flags, ENTRY_CACHABLE);
239 storeSetPrivateKey(e);
240 }
241
242 /* unlock object, return -1 if object get released after unlock
243 * otherwise lock_count */
244 int
245 storeUnlockObject(StoreEntry * e)
246 {
247 e->lock_count--;
248 debug(20, 3) ("storeUnlockObject: key '%s' count=%d\n",
249 storeKeyText(e->key), e->lock_count);
250 if (e->lock_count)
251 return (int) e->lock_count;
252 if (e->store_status == STORE_PENDING) {
253 assert(!EBIT_TEST(e->flags, ENTRY_DISPATCHED));
254 EBIT_SET(e->flags, RELEASE_REQUEST);
255 }
256 assert(storePendingNClients(e) == 0);
257 if (EBIT_TEST(e->flags, RELEASE_REQUEST))
258 storeRelease(e);
259 else if (storeKeepInMemory(e)) {
260 storeSetMemStatus(e, IN_MEMORY);
261 requestUnlink(e->mem_obj->request);
262 e->mem_obj->request = NULL;
263 } else {
264 storePurgeMem(e);
265 if (EBIT_TEST(e->flags, KEY_PRIVATE)) {
266 dlinkDelete(&e->lru, &store_list);
267 dlinkAddTail(e, &e->lru, &store_list);
268 }
269 }
270 return 0;
271 }
272
273 /* Lookup an object in the cache.
274 * return just a reference to object, don't start swapping in yet. */
275 StoreEntry *
276 storeGet(const cache_key * key)
277 {
278 debug(20, 3) ("storeGet: looking up %s\n", storeKeyText(key));
279 return (StoreEntry *) hash_lookup(store_table, key);
280 }
281
282 static int
283 getKeyCounter(void)
284 {
285 static int key_counter = 0;
286 if (++key_counter < 0)
287 key_counter = 1;
288 return key_counter;
289 }
290
291 void
292 storeSetPrivateKey(StoreEntry * e)
293 {
294 const cache_key *newkey;
295 MemObject *mem = e->mem_obj;
296 if (e->key && EBIT_TEST(e->flags, KEY_PRIVATE))
297 return; /* is already private */
298 if (e->key) {
299 if (e->swap_file_number > -1)
300 storeDirSwapLog(e, SWAP_LOG_DEL);
301 storeHashDelete(e);
302 }
303 if (mem != NULL) {
304 mem->id = getKeyCounter();
305 newkey = storeKeyPrivate(mem->url, mem->method, mem->id);
306 } else {
307 newkey = storeKeyPrivate("JUNK", METHOD_NONE, getKeyCounter());
308 }
309 assert(hash_lookup(store_table, newkey) == NULL);
310 EBIT_SET(e->flags, KEY_PRIVATE);
311 storeHashInsert(e, newkey);
312 }
313
314 void
315 storeSetPublicKey(StoreEntry * e)
316 {
317 StoreEntry *e2 = NULL;
318 const cache_key *newkey;
319 MemObject *mem = e->mem_obj;
320 if (e->key && !EBIT_TEST(e->flags, KEY_PRIVATE))
321 return; /* is already public */
322 assert(mem);
323 /*
324 * We can't make RELEASE_REQUEST objects public. Depending on
325 * when RELEASE_REQUEST gets set, we might not be swapping out
326 * the object. If we're not swapping out, then subsequent
327 * store clients won't be able to access object data which has
328 * been freed from memory.
329 */
330 assert(!EBIT_TEST(e->flags, RELEASE_REQUEST));
331 newkey = storeKeyPublic(mem->url, mem->method);
332 if ((e2 = (StoreEntry *) hash_lookup(store_table, newkey))) {
333 debug(20, 3) ("storeSetPublicKey: Making old '%s' private.\n", mem->url);
334 storeSetPrivateKey(e2);
335 storeRelease(e2);
336 newkey = storeKeyPublic(mem->url, mem->method);
337 }
338 if (e->key)
339 storeHashDelete(e);
340 EBIT_CLR(e->flags, KEY_PRIVATE);
341 storeHashInsert(e, newkey);
342 if (e->swap_file_number > -1)
343 storeDirSwapLog(e, SWAP_LOG_ADD);
344 }
345
346 StoreEntry *
347 storeCreateEntry(const char *url, const char *log_url, request_flags flags, method_t method)
348 {
349 StoreEntry *e = NULL;
350 MemObject *mem = NULL;
351 debug(20, 3) ("storeCreateEntry: '%s'\n", url);
352
353 e = new_StoreEntry(STORE_ENTRY_WITH_MEMOBJ, url, log_url);
354 e->lock_count = 1; /* Note lock here w/o calling storeLock() */
355 mem = e->mem_obj;
356 mem->method = method;
357 if (neighbors_do_private_keys || !flags.hierarchical)
358 storeSetPrivateKey(e);
359 else
360 storeSetPublicKey(e);
361 if (flags.cachable) {
362 EBIT_SET(e->flags, ENTRY_CACHABLE);
363 EBIT_CLR(e->flags, RELEASE_REQUEST);
364 } else {
365 EBIT_CLR(e->flags, ENTRY_CACHABLE);
366 storeReleaseRequest(e);
367 }
368 e->store_status = STORE_PENDING;
369 storeSetMemStatus(e, NOT_IN_MEMORY);
370 e->swap_status = SWAPOUT_NONE;
371 e->swap_file_number = -1;
372 e->refcount = 0;
373 e->lastref = squid_curtime;
374 e->timestamp = 0; /* set in storeTimestampsSet() */
375 e->ping_status = PING_NONE;
376 EBIT_SET(e->flags, ENTRY_VALIDATED);
377 #ifdef PPNR_WIP
378 EBIT_SET(e->flags, ENTRY_FWD_HDR_WAIT);
379 #endif /* PPNR_WIP */
380 return e;
381 }
382
383 /* Mark object as expired */
384 void
385 storeExpireNow(StoreEntry * e)
386 {
387 debug(20, 3) ("storeExpireNow: '%s'\n", storeKeyText(e->key));
388 e->expires = squid_curtime;
389 }
390
391 /* Append incoming data from a primary server to an entry. */
392 void
393 storeAppend(StoreEntry * e, const char *buf, int len)
394 {
395 MemObject *mem = e->mem_obj;
396 assert(mem != NULL);
397 assert(len >= 0);
398 if (len) {
399 debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n",
400 len,
401 storeKeyText(e->key));
402 storeGetMemSpace(len);
403 stmemAppend(&mem->data_hdr, buf, len);
404 mem->inmem_hi += len;
405 }
406 if (EBIT_TEST(e->flags, DELAY_SENDING))
407 return;
408 #ifdef OPTIMISTIC_IO
409 storeLockObject(e);
410 #endif
411 InvokeHandlers(e);
412 storeCheckSwapOut(e);
413 #ifdef OPTIMISTIC_IO
414 storeUnlockObject(e);
415 #endif
416 }
417
418 #ifdef __STDC__
419 void
420 storeAppendPrintf(StoreEntry * e, const char *fmt,...)
421 {
422 va_list args;
423 va_start(args, fmt);
424 #else
425 void
426 storeAppendPrintf(va_alist)
427 va_dcl
428 {
429 va_list args;
430 StoreEntry *e = NULL;
431 const char *fmt = NULL;
432 va_start(args);
433 e = va_arg(args, StoreEntry *);
434 fmt = va_arg(args, char *);
435 #endif
436 storeAppendVPrintf(e, fmt, args);
437 va_end(args);
438 }
439
440 /* used be storeAppendPrintf and Packer */
441 void
442 storeAppendVPrintf(StoreEntry * e, const char *fmt, va_list vargs)
443 {
444 LOCAL_ARRAY(char, buf, 4096);
445 buf[0] = '\0';
446 vsnprintf(buf, 4096, fmt, vargs);
447 storeAppend(e, buf, strlen(buf));
448 }
449
450 struct _store_check_cachable_hist {
451 struct {
452 int non_get;
453 int not_entry_cachable;
454 int release_request;
455 int wrong_content_length;
456 int negative_cached;
457 int too_big;
458 int private_key;
459 int too_many_open_files;
460 int lru_age_too_low;
461 } no;
462 struct {
463 int Default;
464 } yes;
465 } store_check_cachable_hist;
466
467 int
468 storeTooManyDiskFilesOpen(void)
469 {
470 if (Config.max_open_disk_fds == 0)
471 return 0;
472 if (open_disk_fd > Config.max_open_disk_fds)
473 return 1;
474 return 0;
475 }
476
477 int
478 storeCheckCachable(StoreEntry * e)
479 {
480 #if CACHE_ALL_METHODS
481 if (e->mem_obj->method != METHOD_GET) {
482 debug(20, 2) ("storeCheckCachable: NO: non-GET method\n");
483 store_check_cachable_hist.no.non_get++;
484 } else
485 #endif
486 if (!EBIT_TEST(e->flags, ENTRY_CACHABLE)) {
487 debug(20, 2) ("storeCheckCachable: NO: not cachable\n");
488 store_check_cachable_hist.no.not_entry_cachable++;
489 } else if (EBIT_TEST(e->flags, RELEASE_REQUEST)) {
490 debug(20, 2) ("storeCheckCachable: NO: release requested\n");
491 store_check_cachable_hist.no.release_request++;
492 } else if (e->store_status == STORE_OK && EBIT_TEST(e->flags, ENTRY_BAD_LENGTH)) {
493 debug(20, 2) ("storeCheckCachable: NO: wrong content-length\n");
494 store_check_cachable_hist.no.wrong_content_length++;
495 } else if (EBIT_TEST(e->flags, ENTRY_NEGCACHED)) {
496 debug(20, 3) ("storeCheckCachable: NO: negative cached\n");
497 store_check_cachable_hist.no.negative_cached++;
498 return 0; /* avoid release call below */
499 } else if (e->mem_obj->inmem_hi > Config.Store.maxObjectSize) {
500 debug(20, 2) ("storeCheckCachable: NO: too big\n");
501 store_check_cachable_hist.no.too_big++;
502 } else if (EBIT_TEST(e->flags, KEY_PRIVATE)) {
503 debug(20, 3) ("storeCheckCachable: NO: private key\n");
504 store_check_cachable_hist.no.private_key++;
505 } else if (storeTooManyDiskFilesOpen()) {
506 debug(20, 2) ("storeCheckCachable: NO: too many disk files open\n");
507 store_check_cachable_hist.no.too_many_open_files++;
508 } else if (storeExpiredReferenceAge() < 300) {
509 debug(20, 2) ("storeCheckCachable: NO: LRU Age = %d\n",
510 storeExpiredReferenceAge());
511 store_check_cachable_hist.no.lru_age_too_low++;
512 } else {
513 store_check_cachable_hist.yes.Default++;
514 return 1;
515 }
516 storeReleaseRequest(e);
517 EBIT_CLR(e->flags, ENTRY_CACHABLE);
518 return 0;
519 }
520
521 static void
522 storeCheckCachableStats(StoreEntry * sentry)
523 {
524 storeAppendPrintf(sentry, "no.non_get\t%d\n",
525 store_check_cachable_hist.no.non_get);
526 storeAppendPrintf(sentry, "no.not_entry_cachable\t%d\n",
527 store_check_cachable_hist.no.not_entry_cachable);
528 storeAppendPrintf(sentry, "no.release_request\t%d\n",
529 store_check_cachable_hist.no.release_request);
530 storeAppendPrintf(sentry, "no.wrong_content_length\t%d\n",
531 store_check_cachable_hist.no.wrong_content_length);
532 storeAppendPrintf(sentry, "no.negative_cached\t%d\n",
533 store_check_cachable_hist.no.negative_cached);
534 storeAppendPrintf(sentry, "no.too_big\t%d\n",
535 store_check_cachable_hist.no.too_big);
536 storeAppendPrintf(sentry, "no.private_key\t%d\n",
537 store_check_cachable_hist.no.private_key);
538 storeAppendPrintf(sentry, "no.too_many_open_files\t%d\n",
539 store_check_cachable_hist.no.too_many_open_files);
540 storeAppendPrintf(sentry, "no.lru_age_too_low\t%d\n",
541 store_check_cachable_hist.no.lru_age_too_low);
542 storeAppendPrintf(sentry, "yes.default\t%d\n",
543 store_check_cachable_hist.yes.Default);
544 }
545
546 /* Complete transfer into the local cache. */
547 void
548 storeComplete(StoreEntry * e)
549 {
550 debug(20, 3) ("storeComplete: '%s'\n", storeKeyText(e->key));
551 assert(e->store_status == STORE_PENDING);
552 e->mem_obj->object_sz = e->mem_obj->inmem_hi;
553 e->store_status = STORE_OK;
554 assert(e->mem_status == NOT_IN_MEMORY);
555 if (!storeEntryValidLength(e))
556 EBIT_SET(e->flags, ENTRY_BAD_LENGTH);
557 #if USE_CACHE_DIGESTS
558 if (e->mem_obj->request)
559 e->mem_obj->request->hier.store_complete_stop = current_time;
560 #endif
561 InvokeHandlers(e);
562 storeCheckSwapOut(e);
563 }
564
565 #ifdef PPNR_WIP
566 void
567 storePPNR(StoreEntry * e)
568 {
569 assert(EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT));
570 EBIT_CLR(e->flags, ENTRY_FWD_HDR_WAIT);
571 }
572
573 #endif /* PPNR_WIP */
574
575 /*
576 * Someone wants to abort this transfer. Set the reason in the
577 * request structure, call the server-side callback and mark the
578 * entry for releasing
579 */
580 void
581 storeAbort(StoreEntry * e, int cbflag)
582 {
583 MemObject *mem = e->mem_obj;
584 STABH *callback;
585 void *data;
586 assert(e->store_status == STORE_PENDING);
587 assert(mem != NULL);
588 debug(20, 6) ("storeAbort: %s\n", storeKeyText(e->key));
589 storeLockObject(e); /* lock while aborting */
590 storeNegativeCache(e);
591 storeReleaseRequest(e);
592 e->store_status = STORE_ABORTED;
593 storeSetMemStatus(e, NOT_IN_MEMORY);
594 /* No DISK swap for negative cached object */
595 e->swap_status = SWAPOUT_NONE;
596 /*
597 * We assign an object length here. The only other place we assign
598 * the object length is in storeComplete()
599 */
600 mem->object_sz = mem->inmem_hi;
601 /* Notify the server side */
602 if (cbflag && mem->abort.callback) {
603 callback = mem->abort.callback;
604 data = mem->abort.data;
605 mem->abort.callback = NULL;
606 mem->abort.data = NULL;
607 callback(data);
608 }
609 /* Notify the client side */
610 InvokeHandlers(e);
611 /* Do we need to close the swapout file? */
612 /* Not if we never started swapping out */
613 /* But we may need to cancel an open/stat in progress if using ASYNC */
614 #if USE_ASYNC_IO
615 aioCancel(-1, e);
616 #endif
617 if (e->swap_file_number > -1) {
618 #if USE_ASYNC_IO
619 /* Need to cancel any pending ASYNC writes right now */
620 if (mem->swapout.fd >= 0)
621 aioCancel(mem->swapout.fd, NULL);
622 #endif
623 /* we have to close the disk file if there is no write pending */
624 if (!storeSwapOutWriteQueued(mem))
625 storeSwapOutFileClose(e);
626 }
627 storeUnlockObject(e); /* unlock */
628 }
629
630 /* Clear Memory storage to accommodate the given object len */
631 static void
632 storeGetMemSpace(int size)
633 {
634 StoreEntry *e = NULL;
635 int released = 0;
636 static time_t last_check = 0;
637 int pages_needed;
638 dlink_node *m;
639 dlink_node *head;
640 dlink_node *prev = NULL;
641 if (squid_curtime == last_check)
642 return;
643 last_check = squid_curtime;
644 pages_needed = (size / SM_PAGE_SIZE) + 1;
645 if (memInUse(MEM_STMEM_BUF) + pages_needed < store_pages_high)
646 return;
647 if (store_rebuilding)
648 return;
649 debug(20, 2) ("storeGetMemSpace: Starting, need %d pages\n", pages_needed);
650 head = inmem_list.head;
651 for (m = inmem_list.tail; m; m = prev) {
652 if (m == head)
653 break;
654 prev = m->prev;
655 e = m->data;
656 if (storeEntryLocked(e)) {
657 dlinkDelete(m, &inmem_list);
658 dlinkAdd(e, m, &inmem_list);
659 continue;
660 }
661 released++;
662 storePurgeMem(e);
663 if (memInUse(MEM_STMEM_BUF) + pages_needed < store_pages_high)
664 break;
665 }
666 debug(20, 3) ("storeGetMemSpace stats:\n");
667 debug(20, 3) (" %6d HOT objects\n", hot_obj_count);
668 debug(20, 3) (" %6d were released\n", released);
669 }
670
671 /* The maximum objects to scan for maintain storage space */
672 #define MAINTAIN_MAX_SCAN 1024
673 #define MAINTAIN_MAX_REMOVE 64
674
675 /*
676 * This routine is to be called by main loop in main.c.
677 * It removes expired objects on only one bucket for each time called.
678 * returns the number of objects removed
679 *
680 * This should get called 1/s from main().
681 */
682 void
683 storeMaintainSwapSpace(void *datanotused)
684 {
685 dlink_node *m;
686 dlink_node *prev = NULL;
687 StoreEntry *e = NULL;
688 int scanned = 0;
689 int locked = 0;
690 int expired = 0;
691 int max_scan;
692 int max_remove;
693 static time_t last_warn_time = 0;
694 /* We can't delete objects while rebuilding swap */
695 if (store_rebuilding) {
696 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0, 1);
697 return;
698 } else if (store_swap_size < store_swap_mid) {
699 max_scan = 100;
700 max_remove = 8;
701 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0, 1);
702 } else if (store_swap_size < store_swap_high) {
703 max_scan = 200;
704 max_remove = 8;
705 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 0.1, 1);
706 } else {
707 max_scan = 500;
708 max_remove = 32;
709 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 0.0, 1);
710 }
711 debug(20, 3) ("storeMaintainSwapSpace\n");
712 for (m = store_list.tail; m; m = prev) {
713 prev = m->prev;
714 e = m->data;
715 scanned++;
716 if (storeEntryLocked(e)) {
717 /*
718 * If there is a locked entry at the tail of the LRU list,
719 * move it to the beginning to get it out of the way.
720 * Theoretically, we might have all locked objects at the
721 * tail, and then we'll never remove anything here and the
722 * LRU age will go to zero.
723 */
724 if (memInUse(MEM_STOREENTRY) > max_scan) {
725 dlinkDelete(&e->lru, &store_list);
726 dlinkAdd(e, &e->lru, &store_list);
727 }
728 locked++;
729 } else if (storeCheckExpired(e)) {
730 expired++;
731 storeRelease(e);
732 }
733 if (expired >= max_remove)
734 break;
735 if (scanned >= max_scan)
736 break;
737 }
738 debug(20, 3) ("storeMaintainSwapSpace stats:\n");
739 debug(20, 3) (" %6d objects\n", memInUse(MEM_STOREENTRY));
740 debug(20, 3) (" %6d were scanned\n", scanned);
741 debug(20, 3) (" %6d were locked\n", locked);
742 debug(20, 3) (" %6d were expired\n", expired);
743 if (store_swap_size < Config.Swap.maxSize)
744 return;
745 if (squid_curtime - last_warn_time < 10)
746 return;
747 debug(20, 0) ("WARNING: Disk space over limit: %d KB > %d KB\n",
748 store_swap_size, Config.Swap.maxSize);
749 last_warn_time = squid_curtime;
750 }
751
752
753 /* release an object from a cache */
754 /* return number of objects released. */
755 void
756 storeRelease(StoreEntry * e)
757 {
758 debug(20, 3) ("storeRelease: Releasing: '%s'\n", storeKeyText(e->key));
759 /* If, for any reason we can't discard this object because of an
760 * outstanding request, mark it for pending release */
761 if (storeEntryLocked(e)) {
762 storeExpireNow(e);
763 debug(20, 3) ("storeRelease: Only setting RELEASE_REQUEST bit\n");
764 storeReleaseRequest(e);
765 return;
766 }
767 if (store_rebuilding) {
768 debug(20, 2) ("storeRelease: Delaying release until store is rebuilt: '%s'\n",
769 storeUrl(e));
770 storeExpireNow(e);
771 storeSetPrivateKey(e);
772 EBIT_SET(e->flags, RELEASE_REQUEST);
773 return;
774 }
775 #if USE_ASYNC_IO
776 /*
777 * Make sure all forgotten async ops are cancelled
778 */
779 aioCancel(-1, e);
780 #endif
781 storeLog(STORE_LOG_RELEASE, e);
782 if (e->swap_file_number > -1) {
783 storeUnlinkFileno(e->swap_file_number);
784 storeDirMapBitReset(e->swap_file_number);
785 if (e->swap_status == SWAPOUT_DONE)
786 if (EBIT_TEST(e->flags, ENTRY_VALIDATED))
787 storeDirUpdateSwapSize(e->swap_file_number, e->swap_file_sz, -1);
788 if (!EBIT_TEST(e->flags, KEY_PRIVATE))
789 storeDirSwapLog(e, SWAP_LOG_DEL);
790 }
791 storeSetMemStatus(e, NOT_IN_MEMORY);
792 destroy_StoreEntry(e);
793 }
794
795 /* return 1 if a store entry is locked */
796 static int
797 storeEntryLocked(const StoreEntry * e)
798 {
799 if (e->lock_count)
800 return 1;
801 if (e->swap_status == SWAPOUT_OPENING)
802 return 1;
803 if (e->swap_status == SWAPOUT_WRITING)
804 return 1;
805 if (e->store_status == STORE_PENDING)
806 return 1;
807 if (EBIT_TEST(e->flags, ENTRY_SPECIAL))
808 return 1;
809 return 0;
810 }
811
812 static int
813 storeEntryValidLength(const StoreEntry * e)
814 {
815 int diff;
816 const HttpReply *reply;
817 assert(e->mem_obj != NULL);
818 reply = e->mem_obj->reply;
819 debug(20, 3) ("storeEntryValidLength: Checking '%s'\n", storeKeyText(e->key));
820 debug(20, 5) ("storeEntryValidLength: object_len = %d\n",
821 objectLen(e));
822 debug(20, 5) ("storeEntryValidLength: hdr_sz = %d\n",
823 reply->hdr_sz);
824 debug(20, 5) ("storeEntryValidLength: content_length = %d\n",
825 reply->content_length);
826 if (reply->content_length < 0) {
827 debug(20, 5) ("storeEntryValidLength: Unspecified content length: %s\n",
828 storeKeyText(e->key));
829 return 1;
830 }
831 if (reply->hdr_sz == 0) {
832 debug(20, 5) ("storeEntryValidLength: Zero header size: %s\n",
833 storeKeyText(e->key));
834 return 1;
835 }
836 if (e->mem_obj->method == METHOD_HEAD) {
837 debug(20, 5) ("storeEntryValidLength: HEAD request: %s\n",
838 storeKeyText(e->key));
839 return 1;
840 }
841 if (reply->sline.status == HTTP_NOT_MODIFIED)
842 return 1;
843 if (reply->sline.status == HTTP_NO_CONTENT)
844 return 1;
845 diff = reply->hdr_sz + reply->content_length - objectLen(e);
846 if (diff == 0)
847 return 1;
848 debug(20, 3) ("storeEntryValidLength: %d bytes too %s; '%s'\n",
849 diff < 0 ? -diff : diff,
850 diff < 0 ? "small" : "big",
851 storeKeyText(e->key));
852 return 0;
853 }
854
855 static void
856 storeInitHashValues(void)
857 {
858 int i;
859 /* Calculate size of hash table (maximum currently 64k buckets). */
860 i = Config.Swap.maxSize / Config.Store.avgObjectSize;
861 debug(20, 1) ("Swap maxSize %d KB, estimated %d objects\n",
862 Config.Swap.maxSize, i);
863 i /= Config.Store.objectsPerBucket;
864 debug(20, 1) ("Target number of buckets: %d\n", i);
865 /* ideally the full scan period should be configurable, for the
866 * moment it remains at approximately 24 hours. */
867 store_hash_buckets = storeKeyHashBuckets(i);
868 store_maintain_rate = 86400 / store_hash_buckets;
869 assert(store_maintain_rate > 0);
870 debug(20, 1) ("Using %d Store buckets, replacement runs every %d second%s\n",
871 store_hash_buckets,
872 store_maintain_rate,
873 store_maintain_rate == 1 ? null_string : "s");
874 debug(20, 1) ("Max Mem size: %d KB\n", Config.Mem.maxSize >> 10);
875 debug(20, 1) ("Max Swap size: %d KB\n", Config.Swap.maxSize);
876 }
877
878 void
879 storeInit(void)
880 {
881 storeKeyInit();
882 storeInitHashValues();
883 store_table = hash_create(storeKeyHashCmp,
884 store_hash_buckets, storeKeyHashHash);
885 storeDigestInit();
886 storeLogOpen();
887 if (storeVerifyCacheDirs() < 0) {
888 xstrncpy(tmp_error_buf,
889 "\tFailed to verify one of the swap directories, Check cache.log\n"
890 "\tfor details. Run 'squid -z' to create swap directories\n"
891 "\tif needed, or if running Squid for the first time.",
892 ERROR_BUF_SZ);
893 fatal(tmp_error_buf);
894 }
895 storeDirOpenSwapLogs();
896 store_list.head = store_list.tail = NULL;
897 inmem_list.head = inmem_list.tail = NULL;
898 storeRebuildStart();
899 cachemgrRegister("storedir",
900 "Store Directory Stats",
901 storeDirStats, 0, 1);
902 cachemgrRegister("store_check_cachable_stats",
903 "storeCheckCachable() Stats",
904 storeCheckCachableStats, 0, 1);
905 }
906
907 void
908 storeConfigure(void)
909 {
910 int store_mem_high = 0;
911 int store_mem_low = 0;
912 store_mem_high = (long) (Config.Mem.maxSize / 100) *
913 Config.Mem.highWaterMark;
914 store_mem_low = (long) (Config.Mem.maxSize / 100) *
915 Config.Mem.lowWaterMark;
916
917 store_swap_high = (long) (((float) Config.Swap.maxSize *
918 (float) Config.Swap.highWaterMark) / (float) 100);
919 store_swap_low = (long) (((float) Config.Swap.maxSize *
920 (float) Config.Swap.lowWaterMark) / (float) 100);
921 store_swap_mid = (store_swap_high >> 1) + (store_swap_low >> 1);
922
923 store_pages_high = store_mem_high / SM_PAGE_SIZE;
924 store_pages_low = store_mem_low / SM_PAGE_SIZE;
925 }
926
927 static int
928 storeKeepInMemory(const StoreEntry * e)
929 {
930 MemObject *mem = e->mem_obj;
931 if (mem == NULL)
932 return 0;
933 if (mem->data_hdr.head == NULL)
934 return 0;
935 return mem->inmem_lo == 0;
936 }
937
938 static int
939 storeCheckExpired(const StoreEntry * e)
940 {
941 if (storeEntryLocked(e))
942 return 0;
943 if (EBIT_TEST(e->flags, RELEASE_REQUEST))
944 return 1;
945 if (EBIT_TEST(e->flags, ENTRY_NEGCACHED) && squid_curtime >= e->expires)
946 return 1;
947 if (squid_curtime - e->lastref > storeExpiredReferenceAge())
948 return 1;
949 return 0;
950 }
951
952 /*
953 * storeExpiredReferenceAge
954 *
955 * The LRU age is scaled exponentially between 1 minute and
956 * Config.referenceAge , when store_swap_low < store_swap_size <
957 * store_swap_high. This keeps store_swap_size within the low and high
958 * water marks. If the cache is very busy then store_swap_size stays
959 * closer to the low water mark, if it is not busy, then it will stay
960 * near the high water mark. The LRU age value can be examined on the
961 * cachemgr 'info' page.
962 */
963 time_t
964 storeExpiredReferenceAge(void)
965 {
966 double x;
967 double z;
968 time_t age;
969 x = (double) (store_swap_high - store_swap_size) / (store_swap_high - store_swap_low);
970 x = x < 0.0 ? 0.0 : x > 1.0 ? 1.0 : x;
971 z = pow((double) (Config.referenceAge / 60), x);
972 age = (time_t) (z * 60.0);
973 if (age < 60)
974 age = 60;
975 else if (age > 31536000)
976 age = 31536000;
977 return age;
978 }
979
980 void
981 storeNegativeCache(StoreEntry * e)
982 {
983 e->expires = squid_curtime + Config.negativeTtl;
984 EBIT_SET(e->flags, ENTRY_NEGCACHED);
985 }
986
987 void
988 storeFreeMemory(void)
989 {
990 hashFreeItems(store_table, destroy_StoreEntry);
991 hashFreeMemory(store_table);
992 store_table = NULL;
993 if (store_digest)
994 cacheDigestDestroy(store_digest);
995 store_digest = NULL;
996 }
997
998 int
999 expiresMoreThan(time_t expires, time_t when)
1000 {
1001 if (expires < 0) /* No Expires given */
1002 return 1;
1003 return (expires > (squid_curtime + when));
1004 }
1005
1006 int
1007 storeEntryValidToSend(StoreEntry * e)
1008 {
1009 if (EBIT_TEST(e->flags, RELEASE_REQUEST))
1010 return 0;
1011 if (EBIT_TEST(e->flags, ENTRY_NEGCACHED))
1012 if (e->expires <= squid_curtime)
1013 return 0;
1014 if (e->store_status == STORE_ABORTED)
1015 return 0;
1016 return 1;
1017 }
1018
1019 void
1020 storeTimestampsSet(StoreEntry * entry)
1021 {
1022 time_t served_date = -1;
1023 const HttpReply *reply = entry->mem_obj->reply;
1024 served_date = reply->date;
1025 if (served_date < 0)
1026 served_date = squid_curtime;
1027 entry->expires = reply->expires;
1028 entry->lastmod = reply->last_modified;
1029 entry->timestamp = served_date;
1030 }
1031
1032 void
1033 storeRegisterAbort(StoreEntry * e, STABH * cb, void *data)
1034 {
1035 MemObject *mem = e->mem_obj;
1036 assert(mem);
1037 assert(mem->abort.callback == NULL);
1038 mem->abort.callback = cb;
1039 mem->abort.data = data;
1040 }
1041
1042 void
1043 storeUnregisterAbort(StoreEntry * e)
1044 {
1045 MemObject *mem = e->mem_obj;
1046 assert(mem);
1047 mem->abort.callback = NULL;
1048 }
1049
1050 void
1051 storeMemObjectDump(MemObject * mem)
1052 {
1053 debug(20, 1) ("MemObject->data.head: %p\n",
1054 mem->data_hdr.head);
1055 debug(20, 1) ("MemObject->data.tail: %p\n",
1056 mem->data_hdr.tail);
1057 debug(20, 1) ("MemObject->data.origin_offset: %d\n",
1058 mem->data_hdr.origin_offset);
1059 debug(20, 1) ("MemObject->start_ping: %d.%06d\n",
1060 (int) mem->start_ping.tv_sec,
1061 (int) mem->start_ping.tv_usec);
1062 debug(20, 1) ("MemObject->inmem_hi: %d\n",
1063 (int) mem->inmem_hi);
1064 debug(20, 1) ("MemObject->inmem_lo: %d\n",
1065 (int) mem->inmem_lo);
1066 debug(20, 1) ("MemObject->clients: %p\n",
1067 mem->clients);
1068 debug(20, 1) ("MemObject->nclients: %d\n",
1069 mem->nclients);
1070 debug(20, 1) ("MemObject->swapout.fd: %d\n",
1071 mem->swapout.fd);
1072 debug(20, 1) ("MemObject->reply: %p\n",
1073 mem->reply);
1074 debug(20, 1) ("MemObject->request: %p\n",
1075 mem->request);
1076 debug(20, 1) ("MemObject->log_url: %p %s\n",
1077 mem->log_url,
1078 checkNullString(mem->log_url));
1079 }
1080
1081 void
1082 storeEntryDump(StoreEntry * e, int l)
1083 {
1084 debug(20, l) ("StoreEntry->key: %s\n", storeKeyText(e->key));
1085 debug(20, l) ("StoreEntry->next: %p\n", e->next);
1086 debug(20, l) ("StoreEntry->mem_obj: %p\n", e->mem_obj);
1087 debug(20, l) ("StoreEntry->timestamp: %d\n", (int) e->timestamp);
1088 debug(20, l) ("StoreEntry->lastref: %d\n", (int) e->lastref);
1089 debug(20, l) ("StoreEntry->expires: %d\n", (int) e->expires);
1090 debug(20, l) ("StoreEntry->lastmod: %d\n", (int) e->lastmod);
1091 debug(20, l) ("StoreEntry->swap_file_sz: %d\n", (int) e->swap_file_sz);
1092 debug(20, l) ("StoreEntry->refcount: %d\n", e->refcount);
1093 debug(20, l) ("StoreEntry->flags: %s\n", storeEntryFlags(e));
1094 debug(20, l) ("StoreEntry->swap_file_number: %d\n", (int) e->swap_file_number);
1095 debug(20, l) ("StoreEntry->lock_count: %d\n", (int) e->lock_count);
1096 debug(20, l) ("StoreEntry->mem_status: %d\n", (int) e->mem_status);
1097 debug(20, l) ("StoreEntry->ping_status: %d\n", (int) e->ping_status);
1098 debug(20, l) ("StoreEntry->store_status: %d\n", (int) e->store_status);
1099 debug(20, l) ("StoreEntry->swap_status: %d\n", (int) e->swap_status);
1100 }
1101
1102 /* NOTE, this function assumes only two mem states */
1103 void
1104 storeSetMemStatus(StoreEntry * e, int new_status)
1105 {
1106 MemObject *mem = e->mem_obj;
1107 if (new_status == e->mem_status)
1108 return;
1109 assert(mem != NULL);
1110 if (new_status == IN_MEMORY) {
1111 assert(mem->inmem_lo == 0);
1112 dlinkAdd(e, &mem->lru, &inmem_list);
1113 hot_obj_count++;
1114 } else {
1115 dlinkDelete(&mem->lru, &inmem_list);
1116 hot_obj_count--;
1117 }
1118 e->mem_status = new_status;
1119 }
1120
1121 const char *
1122 storeUrl(const StoreEntry * e)
1123 {
1124 if (e == NULL)
1125 return "[null_entry]";
1126 else if (e->mem_obj == NULL)
1127 return "[null_mem_obj]";
1128 else
1129 return e->mem_obj->url;
1130 }
1131
1132 void
1133 storeCreateMemObject(StoreEntry * e, const char *url, const char *log_url)
1134 {
1135 if (e->mem_obj)
1136 return;
1137 e->mem_obj = new_MemObject(url, log_url);
1138 }
1139
1140 /* this just sets DELAY_SENDING */
1141 void
1142 storeBuffer(StoreEntry * e)
1143 {
1144 EBIT_SET(e->flags, DELAY_SENDING);
1145 }
1146
1147 /* this just clears DELAY_SENDING and Invokes the handlers */
1148 void
1149 storeBufferFlush(StoreEntry * e)
1150 {
1151 EBIT_CLR(e->flags, DELAY_SENDING);
1152 InvokeHandlers(e);
1153 storeCheckSwapOut(e);
1154 }
1155
1156 void
1157 storeUnlinkFileno(int fileno)
1158 {
1159 debug(20, 5) ("storeUnlinkFileno: %08X\n", fileno);
1160 #if USE_ASYNC_IO
1161 safeunlink(storeSwapFullPath(fileno, NULL), 1);
1162 #else
1163 unlinkdUnlink(storeSwapFullPath(fileno, NULL));
1164 #endif
1165 }
1166
1167 int
1168 objectLen(const StoreEntry * e)
1169 {
1170 assert(e->mem_obj != NULL);
1171 return e->mem_obj->object_sz;
1172 }
1173
1174 int
1175 contentLen(const StoreEntry * e)
1176 {
1177 assert(e->mem_obj != NULL);
1178 assert(e->mem_obj->reply != NULL);
1179 return e->mem_obj->object_sz - e->mem_obj->reply->hdr_sz;
1180 }
1181
1182 HttpReply *
1183 storeEntryReply(StoreEntry * e)
1184 {
1185 if (NULL == e)
1186 return NULL;
1187 if (NULL == e->mem_obj)
1188 return NULL;
1189 return e->mem_obj->reply;
1190 }