]> git.ipfire.org Git - thirdparty/squid.git/blob - src/store.cc
merge changes from SQUID_2_3 branch
[thirdparty/squid.git] / src / store.cc
1
2 /*
3 * $Id: store.cc,v 1.510 1999/12/30 17:36:53 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_DONE"
53 };
54
55 const char *storeStatusStr[] =
56 {
57 "STORE_OK",
58 "STORE_PENDING"
59 };
60
61 const char *swapStatusStr[] =
62 {
63 "SWAPOUT_NONE",
64 "SWAPOUT_WRITING",
65 "SWAPOUT_DONE"
66 };
67
68 typedef struct lock_ctrl_t {
69 SIH *callback;
70 void *callback_data;
71 StoreEntry *e;
72 } lock_ctrl_t;
73
74 /*
75 * local function prototypes
76 */
77 static int storeCheckExpired(const StoreEntry *);
78 static int storeEntryLocked(const StoreEntry *);
79 static int storeEntryValidLength(const StoreEntry *);
80 static void storeGetMemSpace(int);
81 static void storeHashDelete(StoreEntry *);
82 static MemObject *new_MemObject(const char *, const char *);
83 static void destroy_MemObject(StoreEntry *);
84 static FREE destroy_StoreEntry;
85 static void storePurgeMem(StoreEntry *);
86 static int getKeyCounter(void);
87 static int storeKeepInMemory(const StoreEntry *);
88 static OBJH storeCheckCachableStats;
89 static EVH storeLateRelease;
90 #if HEAP_REPLACEMENT
91 static heap_key_func HeapKeyGen_StoreEntry_LFUDA;
92 static heap_key_func HeapKeyGen_StoreEntry_GDSF;
93 static heap_key_func HeapKeyGen_StoreEntry_LRU;
94 #endif
95
96 /*
97 * local variables
98 */
99 #if HEAP_REPLACEMENT
100 /*
101 * The heap equivalent of inmem_list, inmem_heap, is in globals.c so other
102 * modules can access it when updating object metadata (e.g., refcount)
103 */
104 #else
105 static dlink_list inmem_list;
106 #endif
107 static int store_pages_max = 0;
108 static int store_swap_high = 0;
109 static int store_swap_low = 0;
110 static Stack LateReleaseStack;
111
112 #if URL_CHECKSUM_DEBUG
113 unsigned int
114 url_checksum(const char *url)
115 {
116 unsigned int ck;
117 MD5_CTX M;
118 static unsigned char digest[16];
119 MD5Init(&M);
120 MD5Update(&M, (unsigned char *) url, strlen(url));
121 MD5Final(digest, &M);
122 xmemcpy(&ck, digest, sizeof(ck));
123 return ck;
124 }
125 #endif
126
127 static MemObject *
128 new_MemObject(const char *url, const char *log_url)
129 {
130 MemObject *mem = memAllocate(MEM_MEMOBJECT);
131 mem->reply = httpReplyCreate();
132 mem->url = xstrdup(url);
133 #if URL_CHECKSUM_DEBUG
134 mem->chksum = url_checksum(mem->url);
135 #endif
136 mem->log_url = xstrdup(log_url);
137 mem->object_sz = -1;
138 mem->fd = -1;
139 /* XXX account log_url */
140 debug(20, 3) ("new_MemObject: returning %p\n", mem);
141 return mem;
142 }
143
144 StoreEntry *
145 new_StoreEntry(int mem_obj_flag, const char *url, const char *log_url)
146 {
147 StoreEntry *e = NULL;
148 e = memAllocate(MEM_STOREENTRY);
149 if (mem_obj_flag)
150 e->mem_obj = new_MemObject(url, log_url);
151 debug(20, 3) ("new_StoreEntry: returning %p\n", e);
152 e->expires = e->lastmod = e->lastref = e->timestamp = -1;
153 return e;
154 }
155
156 static void
157 destroy_MemObject(StoreEntry * e)
158 {
159 MemObject *mem = e->mem_obj;
160 const Ctx ctx = ctx_enter(mem->url);
161 debug(20, 3) ("destroy_MemObject: destroying %p\n", mem);
162 #if URL_CHECKSUM_DEBUG
163 assert(mem->chksum == url_checksum(mem->url));
164 #endif
165 e->mem_obj = NULL;
166 if (!shutting_down)
167 assert(mem->swapout.sio == NULL);
168 stmemFree(&mem->data_hdr);
169 mem->inmem_hi = 0;
170 /*
171 * There is no way to abort FD-less clients, so they might
172 * still have mem->clients set if mem->fd == -1
173 */
174 assert(mem->fd == -1 || mem->clients == NULL);
175 httpReplyDestroy(mem->reply);
176 requestUnlink(mem->request);
177 mem->request = NULL;
178 ctx_exit(ctx); /* must exit before we free mem->url */
179 safe_free(mem->url);
180 safe_free(mem->log_url); /* XXX account log_url */
181 memFree(mem, MEM_MEMOBJECT);
182 }
183
184 static void
185 destroy_StoreEntry(void *data)
186 {
187 StoreEntry *e = data;
188 debug(20, 3) ("destroy_StoreEntry: destroying %p\n", e);
189 assert(e != NULL);
190 if (e->mem_obj)
191 destroy_MemObject(e);
192 storeHashDelete(e);
193 assert(e->key == NULL);
194 memFree(e, MEM_STOREENTRY);
195 }
196
197 /* ----- INTERFACE BETWEEN STORAGE MANAGER AND HASH TABLE FUNCTIONS --------- */
198
199 void
200 storeHashInsert(StoreEntry * e, const cache_key * key)
201 {
202 debug(20, 3) ("storeHashInsert: Inserting Entry %p key '%s'\n",
203 e, storeKeyText(key));
204 e->key = storeKeyDup(key);
205 hash_join(store_table, (hash_link *) e);
206 #if HEAP_REPLACEMENT
207 if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
208 (void) 0;
209 } else {
210 e->node = heap_insert(store_heap, e);
211 debug(20, 4) ("storeHashInsert: inserted node %p\n", e->node);
212 }
213 #else
214 dlinkAdd(e, &e->lru, &store_list);
215 #endif
216 }
217
218 static void
219 storeHashDelete(StoreEntry * e)
220 {
221 hash_remove_link(store_table, (hash_link *) e);
222 #if HEAP_REPLACEMENT
223 if (e->node) {
224 debug(20, 4) ("storeHashDelete: deleting node %p\n", e->node);
225 heap_delete(store_heap, e->node);
226 e->node = NULL;
227 }
228 #else
229 dlinkDelete(&e->lru, &store_list);
230 #endif
231 storeKeyFree(e->key);
232 e->key = NULL;
233 }
234
235 /* -------------------------------------------------------------------------- */
236
237
238 /* get rid of memory copy of the object */
239 /* Only call this if storeCheckPurgeMem(e) returns 1 */
240 static void
241 storePurgeMem(StoreEntry * e)
242 {
243 if (e->mem_obj == NULL)
244 return;
245 debug(20, 3) ("storePurgeMem: Freeing memory-copy of %s\n",
246 storeKeyText(e->key));
247 storeSetMemStatus(e, NOT_IN_MEMORY);
248 destroy_MemObject(e);
249 if (e->swap_status != SWAPOUT_DONE)
250 storeRelease(e);
251 }
252
253 void
254 storeLockObject(StoreEntry * e)
255 {
256 if (e->lock_count++ == 0) {
257 #if HEAP_REPLACEMENT
258 /*
259 * There is no reason to take any action here. Squid by
260 * default is moving locked objects to the end of the LRU
261 * list to keep them from getting bumped into by the
262 * replacement algorithm. We can't do that so we will just
263 * have to handle them.
264 */
265 debug(20, 4) ("storeLockObject: just locked node %p\n", e->node);
266 #else
267 dlinkDelete(&e->lru, &store_list);
268 dlinkAdd(e, &e->lru, &store_list);
269 #endif
270 }
271 debug(20, 3) ("storeLockObject: key '%s' count=%d\n",
272 storeKeyText(e->key), (int) e->lock_count);
273 e->lastref = squid_curtime;
274 }
275
276 void
277 storeReleaseRequest(StoreEntry * e)
278 {
279 if (EBIT_TEST(e->flags, RELEASE_REQUEST))
280 return;
281 debug(20, 3) ("storeReleaseRequest: '%s'\n", storeKeyText(e->key));
282 EBIT_SET(e->flags, RELEASE_REQUEST);
283 /*
284 * Clear cachable flag here because we might get called before
285 * anyone else even looks at the cachability flag. Also, this
286 * prevents httpMakePublic from really setting a public key.
287 */
288 EBIT_CLR(e->flags, ENTRY_CACHABLE);
289 storeSetPrivateKey(e);
290 }
291
292 /* unlock object, return -1 if object get released after unlock
293 * otherwise lock_count */
294 int
295 storeUnlockObject(StoreEntry * e)
296 {
297 e->lock_count--;
298 debug(20, 3) ("storeUnlockObject: key '%s' count=%d\n",
299 storeKeyText(e->key), e->lock_count);
300 if (e->lock_count)
301 return (int) e->lock_count;
302 if (e->store_status == STORE_PENDING)
303 EBIT_SET(e->flags, RELEASE_REQUEST);
304 assert(storePendingNClients(e) == 0);
305 #if HEAP_REPLACEMENT
306 storeHeapPositionUpdate(e);
307 #endif
308 if (EBIT_TEST(e->flags, RELEASE_REQUEST))
309 storeRelease(e);
310 else if (storeKeepInMemory(e)) {
311 storeSetMemStatus(e, IN_MEMORY);
312 requestUnlink(e->mem_obj->request);
313 e->mem_obj->request = NULL;
314 } else {
315 storePurgeMem(e);
316 if (EBIT_TEST(e->flags, KEY_PRIVATE)) {
317 #if HEAP_REPLACEMENT
318 /*
319 * Squid/LRU is moving things around in the linked list in order
320 * to keep from bumping into them when purging from the LRU list.
321 */
322 debug(20, 4) ("storeUnlockObject: purged private node %p\n",
323 e->node);
324 #else
325 dlinkDelete(&e->lru, &store_list);
326 dlinkAddTail(e, &e->lru, &store_list);
327 #endif
328 }
329 }
330 return 0;
331 }
332
333 /* Lookup an object in the cache.
334 * return just a reference to object, don't start swapping in yet. */
335 StoreEntry *
336 storeGet(const cache_key * key)
337 {
338 debug(20, 3) ("storeGet: looking up %s\n", storeKeyText(key));
339 return (StoreEntry *) hash_lookup(store_table, key);
340 }
341
342 StoreEntry *
343 storeGetPublic(const char *uri, const method_t method)
344 {
345 return storeGet(storeKeyPublic(uri, method));
346 }
347
348 static int
349 getKeyCounter(void)
350 {
351 static int key_counter = 0;
352 if (++key_counter < 0)
353 key_counter = 1;
354 return key_counter;
355 }
356
357 void
358 storeSetPrivateKey(StoreEntry * e)
359 {
360 const cache_key *newkey;
361 MemObject *mem = e->mem_obj;
362 if (e->key && EBIT_TEST(e->flags, KEY_PRIVATE))
363 return; /* is already private */
364 if (e->key) {
365 if (e->swap_file_number > -1)
366 storeDirSwapLog(e, SWAP_LOG_DEL);
367 storeHashDelete(e);
368 }
369 if (mem != NULL) {
370 mem->id = getKeyCounter();
371 newkey = storeKeyPrivate(mem->url, mem->method, mem->id);
372 } else {
373 newkey = storeKeyPrivate("JUNK", METHOD_NONE, getKeyCounter());
374 }
375 assert(hash_lookup(store_table, newkey) == NULL);
376 EBIT_SET(e->flags, KEY_PRIVATE);
377 storeHashInsert(e, newkey);
378 }
379
380 void
381 storeSetPublicKey(StoreEntry * e)
382 {
383 StoreEntry *e2 = NULL;
384 const cache_key *newkey;
385 MemObject *mem = e->mem_obj;
386 if (e->key && !EBIT_TEST(e->flags, KEY_PRIVATE))
387 return; /* is already public */
388 assert(mem);
389 /*
390 * We can't make RELEASE_REQUEST objects public. Depending on
391 * when RELEASE_REQUEST gets set, we might not be swapping out
392 * the object. If we're not swapping out, then subsequent
393 * store clients won't be able to access object data which has
394 * been freed from memory.
395 *
396 * If RELEASE_REQUEST is set, then ENTRY_CACHABLE should not
397 * be set, and storeSetPublicKey() should not be called.
398 */
399 #if HEAP_REPLACEMENT
400 if (EBIT_TEST(e->flags, RELEASE_REQUEST))
401 debug(20, 1) ("assertion failed: RELEASE key %s, url %s\n",
402 e->key, mem->url);
403 #endif
404 assert(!EBIT_TEST(e->flags, RELEASE_REQUEST));
405 newkey = storeKeyPublic(mem->url, mem->method);
406 if ((e2 = (StoreEntry *) hash_lookup(store_table, newkey))) {
407 debug(20, 3) ("storeSetPublicKey: Making old '%s' private.\n", mem->url);
408 storeSetPrivateKey(e2);
409 storeRelease(e2);
410 newkey = storeKeyPublic(mem->url, mem->method);
411 }
412 if (e->key)
413 storeHashDelete(e);
414 EBIT_CLR(e->flags, KEY_PRIVATE);
415 storeHashInsert(e, newkey);
416 if (e->swap_file_number > -1)
417 storeDirSwapLog(e, SWAP_LOG_ADD);
418 }
419
420 StoreEntry *
421 storeCreateEntry(const char *url, const char *log_url, request_flags flags, method_t method)
422 {
423 StoreEntry *e = NULL;
424 MemObject *mem = NULL;
425 debug(20, 3) ("storeCreateEntry: '%s'\n", url);
426
427 e = new_StoreEntry(STORE_ENTRY_WITH_MEMOBJ, url, log_url);
428 e->lock_count = 1; /* Note lock here w/o calling storeLock() */
429 mem = e->mem_obj;
430 mem->method = method;
431 if (neighbors_do_private_keys || !flags.hierarchical)
432 storeSetPrivateKey(e);
433 else
434 storeSetPublicKey(e);
435 if (flags.cachable) {
436 EBIT_SET(e->flags, ENTRY_CACHABLE);
437 EBIT_CLR(e->flags, RELEASE_REQUEST);
438 } else {
439 EBIT_CLR(e->flags, ENTRY_CACHABLE);
440 storeReleaseRequest(e);
441 }
442 e->store_status = STORE_PENDING;
443 storeSetMemStatus(e, NOT_IN_MEMORY);
444 e->swap_status = SWAPOUT_NONE;
445 e->swap_file_number = -1;
446 e->refcount = 0;
447 e->lastref = squid_curtime;
448 e->timestamp = 0; /* set in storeTimestampsSet() */
449 e->ping_status = PING_NONE;
450 EBIT_SET(e->flags, ENTRY_VALIDATED);
451 return e;
452 }
453
454 /* Mark object as expired */
455 void
456 storeExpireNow(StoreEntry * e)
457 {
458 debug(20, 3) ("storeExpireNow: '%s'\n", storeKeyText(e->key));
459 e->expires = squid_curtime;
460 }
461
462 /* Append incoming data from a primary server to an entry. */
463 void
464 storeAppend(StoreEntry * e, const char *buf, int len)
465 {
466 MemObject *mem = e->mem_obj;
467 assert(mem != NULL);
468 assert(len >= 0);
469 assert(e->store_status == STORE_PENDING);
470 if (len) {
471 debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n",
472 len,
473 storeKeyText(e->key));
474 storeGetMemSpace(len);
475 stmemAppend(&mem->data_hdr, buf, len);
476 mem->inmem_hi += len;
477 }
478 if (EBIT_TEST(e->flags, DELAY_SENDING))
479 return;
480 InvokeHandlers(e);
481 storeSwapOut(e);
482 }
483
484 void
485 #if STDC_HEADERS
486 storeAppendPrintf(StoreEntry * e, const char *fmt,...)
487 #else
488 storeAppendPrintf(va_alist)
489 va_dcl
490 #endif
491 {
492 #if STDC_HEADERS
493 va_list args;
494 va_start(args, fmt);
495 #else
496 va_list args;
497 StoreEntry *e = NULL;
498 const char *fmt = NULL;
499 va_start(args);
500 e = va_arg(args, StoreEntry *);
501 fmt = va_arg(args, char *);
502 #endif
503 storeAppendVPrintf(e, fmt, args);
504 va_end(args);
505 }
506
507 /* used be storeAppendPrintf and Packer */
508 void
509 storeAppendVPrintf(StoreEntry * e, const char *fmt, va_list vargs)
510 {
511 LOCAL_ARRAY(char, buf, 4096);
512 buf[0] = '\0';
513 vsnprintf(buf, 4096, fmt, vargs);
514 storeAppend(e, buf, strlen(buf));
515 }
516
517 struct _store_check_cachable_hist {
518 struct {
519 int non_get;
520 int not_entry_cachable;
521 int release_request;
522 int wrong_content_length;
523 int negative_cached;
524 int too_big;
525 int private_key;
526 int too_many_open_files;
527 int too_many_open_fds;
528 int lru_age_too_low;
529 } no;
530 struct {
531 int Default;
532 } yes;
533 } store_check_cachable_hist;
534
535 int
536 storeTooManyDiskFilesOpen(void)
537 {
538 if (Config.max_open_disk_fds == 0)
539 return 0;
540 if (store_open_disk_fd > Config.max_open_disk_fds)
541 return 1;
542 return 0;
543 }
544
545 int
546 storeCheckCachable(StoreEntry * e)
547 {
548 #if CACHE_ALL_METHODS
549 if (e->mem_obj->method != METHOD_GET) {
550 debug(20, 2) ("storeCheckCachable: NO: non-GET method\n");
551 store_check_cachable_hist.no.non_get++;
552 } else
553 #endif
554 if (!EBIT_TEST(e->flags, ENTRY_CACHABLE)) {
555 debug(20, 2) ("storeCheckCachable: NO: not cachable\n");
556 store_check_cachable_hist.no.not_entry_cachable++;
557 } else if (EBIT_TEST(e->flags, RELEASE_REQUEST)) {
558 debug(20, 2) ("storeCheckCachable: NO: release requested\n");
559 store_check_cachable_hist.no.release_request++;
560 } else if (e->store_status == STORE_OK && EBIT_TEST(e->flags, ENTRY_BAD_LENGTH)) {
561 debug(20, 2) ("storeCheckCachable: NO: wrong content-length\n");
562 store_check_cachable_hist.no.wrong_content_length++;
563 } else if (EBIT_TEST(e->flags, ENTRY_NEGCACHED)) {
564 debug(20, 3) ("storeCheckCachable: NO: negative cached\n");
565 store_check_cachable_hist.no.negative_cached++;
566 return 0; /* avoid release call below */
567 } else if (e->mem_obj->inmem_hi > Config.Store.maxObjectSize) {
568 debug(20, 2) ("storeCheckCachable: NO: too big\n");
569 store_check_cachable_hist.no.too_big++;
570 } else if (e->mem_obj->reply->content_length > (int) Config.Store.maxObjectSize) {
571 debug(20, 2) ("storeCheckCachable: NO: too big\n");
572 store_check_cachable_hist.no.too_big++;
573 } else if (EBIT_TEST(e->flags, KEY_PRIVATE)) {
574 debug(20, 3) ("storeCheckCachable: NO: private key\n");
575 store_check_cachable_hist.no.private_key++;
576 } else if (e->swap_status != SWAPOUT_NONE) {
577 /*
578 * here we checked the swap_status because the remaining
579 * cases are only relevant only if we haven't started swapping
580 * out the object yet.
581 */
582 return 1;
583 } else if (storeTooManyDiskFilesOpen()) {
584 debug(20, 2) ("storeCheckCachable: NO: too many disk files open\n");
585 store_check_cachable_hist.no.too_many_open_files++;
586 } else if (fdNFree() < RESERVED_FD) {
587 debug(20, 2) ("storeCheckCachable: NO: too many FD's open\n");
588 store_check_cachable_hist.no.too_many_open_fds++;
589 #if HEAP_REPLACEMENT
590 /*
591 * With the HEAP-based replacement policies a low reference
592 * age should not prevent cacheability of an object. We
593 * do not use LRU age at all.
594 */
595 #else
596 } else if (storeExpiredReferenceAge() < 300) {
597 debug(20, 2) ("storeCheckCachable: NO: LRU Age = %d\n",
598 storeExpiredReferenceAge());
599 store_check_cachable_hist.no.lru_age_too_low++;
600 #endif
601 } else {
602 store_check_cachable_hist.yes.Default++;
603 return 1;
604 }
605 storeReleaseRequest(e);
606 EBIT_CLR(e->flags, ENTRY_CACHABLE);
607 return 0;
608 }
609
610 static void
611 storeCheckCachableStats(StoreEntry * sentry)
612 {
613 storeAppendPrintf(sentry, "Category\t Count\n");
614
615 storeAppendPrintf(sentry, "no.non_get\t%d\n",
616 store_check_cachable_hist.no.non_get);
617 storeAppendPrintf(sentry, "no.not_entry_cachable\t%d\n",
618 store_check_cachable_hist.no.not_entry_cachable);
619 storeAppendPrintf(sentry, "no.release_request\t%d\n",
620 store_check_cachable_hist.no.release_request);
621 storeAppendPrintf(sentry, "no.wrong_content_length\t%d\n",
622 store_check_cachable_hist.no.wrong_content_length);
623 storeAppendPrintf(sentry, "no.negative_cached\t%d\n",
624 store_check_cachable_hist.no.negative_cached);
625 storeAppendPrintf(sentry, "no.too_big\t%d\n",
626 store_check_cachable_hist.no.too_big);
627 storeAppendPrintf(sentry, "no.private_key\t%d\n",
628 store_check_cachable_hist.no.private_key);
629 storeAppendPrintf(sentry, "no.too_many_open_files\t%d\n",
630 store_check_cachable_hist.no.too_many_open_files);
631 storeAppendPrintf(sentry, "no.too_many_open_fds\t%d\n",
632 store_check_cachable_hist.no.too_many_open_fds);
633 storeAppendPrintf(sentry, "no.lru_age_too_low\t%d\n",
634 store_check_cachable_hist.no.lru_age_too_low);
635 storeAppendPrintf(sentry, "yes.default\t%d\n",
636 store_check_cachable_hist.yes.Default);
637 }
638
639 /* Complete transfer into the local cache. */
640 void
641 storeComplete(StoreEntry * e)
642 {
643 debug(20, 3) ("storeComplete: '%s'\n", storeKeyText(e->key));
644 if (e->store_status != STORE_PENDING) {
645 /*
646 * if we're not STORE_PENDING, then probably we got aborted
647 * and there should be NO clients on this entry
648 */
649 assert(EBIT_TEST(e->flags, ENTRY_ABORTED));
650 assert(e->mem_obj->nclients == 0);
651 return;
652 }
653 e->mem_obj->object_sz = e->mem_obj->inmem_hi;
654 e->store_status = STORE_OK;
655 assert(e->mem_status == NOT_IN_MEMORY);
656 if (!storeEntryValidLength(e)) {
657 EBIT_SET(e->flags, ENTRY_BAD_LENGTH);
658 storeReleaseRequest(e);
659 }
660 #if USE_CACHE_DIGESTS
661 if (e->mem_obj->request)
662 e->mem_obj->request->hier.store_complete_stop = current_time;
663 #endif
664 InvokeHandlers(e);
665 storeSwapOut(e);
666 }
667
668 /*
669 * Someone wants to abort this transfer. Set the reason in the
670 * request structure, call the server-side callback and mark the
671 * entry for releasing
672 */
673 void
674 storeAbort(StoreEntry * e)
675 {
676 MemObject *mem = e->mem_obj;
677 assert(e->store_status == STORE_PENDING);
678 assert(mem != NULL);
679 debug(20, 6) ("storeAbort: %s\n", storeKeyText(e->key));
680 storeLockObject(e); /* lock while aborting */
681 storeNegativeCache(e);
682 storeReleaseRequest(e);
683 EBIT_SET(e->flags, ENTRY_ABORTED);
684 storeSetMemStatus(e, NOT_IN_MEMORY);
685 e->store_status = STORE_OK;
686 /*
687 * We assign an object length here. The only other place we assign
688 * the object length is in storeComplete()
689 */
690 mem->object_sz = mem->inmem_hi;
691 /* Notify the server side */
692 if (mem->abort.callback) {
693 eventAdd("mem->abort.callback",
694 mem->abort.callback,
695 mem->abort.data,
696 0.0,
697 0);
698 mem->abort.callback = NULL;
699 mem->abort.data = NULL;
700 }
701 /* Notify the client side */
702 InvokeHandlers(e);
703 /* Do we need to close the swapout file? */
704 /* Not if we never started swapping out */
705 if (e->swap_file_number > -1) {
706 storeSwapOutFileClose(e);
707 }
708 storeUnlockObject(e); /* unlock */
709 }
710
711 /* Clear Memory storage to accommodate the given object len */
712 static void
713 storeGetMemSpace(int size)
714 {
715 StoreEntry *e = NULL;
716 int released = 0;
717 static time_t last_check = 0;
718 int pages_needed;
719 int locked = 0;
720 #if !HEAP_REPLACEMENT
721 dlink_node *head;
722 dlink_node *m;
723 dlink_node *prev = NULL;
724 #else
725 heap_key age;
726 heap_key min_age = 0.0;
727 link_list *locked_entries = NULL;
728 #endif
729 if (squid_curtime == last_check)
730 return;
731 last_check = squid_curtime;
732 pages_needed = (size / SM_PAGE_SIZE) + 1;
733 if (memInUse(MEM_STMEM_BUF) + pages_needed < store_pages_max)
734 return;
735 if (store_dirs_rebuilding)
736 return;
737 debug(20, 2) ("storeGetMemSpace: Starting, need %d pages\n", pages_needed);
738 #if HEAP_REPLACEMENT
739 while (heap_nodes(inmem_heap) > 0) {
740 age = heap_peepminkey(inmem_heap);
741 e = heap_extractmin(inmem_heap);
742 e->mem_obj->node = NULL; /* no longer in the heap */
743 if (storeEntryLocked(e)) {
744 locked++;
745 debug(20, 5) ("storeGetMemSpace: locked key %s\n",
746 storeKeyText(e->key));
747 linklistPush(&locked_entries, e);
748 continue;
749 }
750 released++;
751 debug(20, 3) ("Released memory object with key %f size %d refs %d url %s\n",
752 age, e->swap_file_sz, e->refcount, e->mem_obj->url);
753 min_age = age;
754 storePurgeMem(e);
755 if (memInUse(MEM_STMEM_BUF) + pages_needed < store_pages_max)
756 break;
757 }
758 /*
759 * Increase the heap age factor.
760 */
761 if (min_age > 0)
762 inmem_heap->age = min_age;
763 /*
764 * Reinsert all bumped locked entries back into heap...
765 */
766 while ((e = linklistShift(&locked_entries)))
767 e->mem_obj->node = heap_insert(inmem_heap, e);
768 #else
769 head = inmem_list.head;
770 for (m = inmem_list.tail; m; m = prev) {
771 if (m == head)
772 break;
773 prev = m->prev;
774 e = m->data;
775 if (storeEntryLocked(e)) {
776 locked++;
777 dlinkDelete(m, &inmem_list);
778 dlinkAdd(e, m, &inmem_list);
779 continue;
780 }
781 released++;
782 storePurgeMem(e);
783 if (memInUse(MEM_STMEM_BUF) + pages_needed < store_pages_max)
784 break;
785 }
786 #endif
787 debug(20, 3) ("storeGetMemSpace: released %d/%d locked %d\n",
788 released, hot_obj_count, locked);
789 debug(20, 3) ("storeGetMemSpace stats:\n");
790 debug(20, 3) (" %6d HOT objects\n", hot_obj_count);
791 debug(20, 3) (" %6d were released\n", released);
792 }
793
794 /* The maximum objects to scan for maintain storage space */
795 #define MAINTAIN_MAX_SCAN 1024
796 #define MAINTAIN_MAX_REMOVE 64
797
798 /*
799 * This routine is to be called by main loop in main.c.
800 * It removes expired objects on only one bucket for each time called.
801 * returns the number of objects removed
802 *
803 * This should get called 1/s from main().
804 */
805 void
806 storeMaintainSwapSpace(void *datanotused)
807 {
808 StoreEntry *e = NULL;
809 int scanned = 0;
810 int locked = 0;
811 int expired = 0;
812 int max_scan;
813 int max_remove;
814 double f;
815 static time_t last_warn_time = 0;
816 #if !HEAP_REPLACEMENT
817 dlink_node *m;
818 dlink_node *prev = NULL;
819 #else
820 heap_key age;
821 heap_key min_age = 0.0;
822 link_list *locked_entries = NULL;
823 #if HEAP_REPLACEMENT_DEBUG
824 if (!verify_heap_property(store_heap)) {
825 debug(20, 1) ("Heap property violated!\n");
826 }
827 #endif
828 #endif
829 /* We can't delete objects while rebuilding swap */
830 if (store_dirs_rebuilding) {
831 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0, 1);
832 return;
833 } else {
834 f = (double) (store_swap_size - store_swap_low) / (store_swap_high - store_swap_low);
835 f = f < 0.0 ? 0.0 : f > 1.0 ? 1.0 : f;
836 max_scan = (int) (f * 400.0 + 100.0);
837 max_remove = (int) (f * 70.0 + 10.0);
838 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0 - f, 1);
839 }
840 debug(20, 3) ("storeMaintainSwapSpace: f=%f, max_scan=%d, max_remove=%d\n",
841 f, max_scan, max_remove);
842 #if HEAP_REPLACEMENT
843 while (heap_nodes(store_heap) > 0) {
844 if (store_swap_size < store_swap_low)
845 break;
846 if (expired >= max_remove)
847 break;
848 if (scanned >= max_scan)
849 break;
850 age = heap_peepminkey(store_heap);
851 e = heap_extractmin(store_heap);
852 e->node = NULL; /* no longer in the heap */
853 scanned++;
854 if (storeEntryLocked(e)) {
855 /*
856 * Entry is in use ... put it in a linked list to ignore it.
857 */
858 if (!EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
859 /*
860 * If this was a "SPECIAL" do not add it back into the heap.
861 * It will always be "SPECIAL" and therefore never removed.
862 */
863 debug(20, 4) ("storeMaintainSwapSpace: locked url %s\n",
864 (e->mem_obj && e->mem_obj->url) ? e->mem_obj->url : storeKeyText(e->key));
865 linklistPush(&locked_entries, e);
866 }
867 locked++;
868 continue;
869 } else if (storeCheckExpired(e)) {
870 /*
871 * Note: This will not check the reference age ifdef
872 * HEAP_REPLACEMENT, but it does some other useful
873 * checks...
874 */
875 expired++;
876 debug(20, 3) ("Released store object age %f size %d refs %d key %s\n",
877 age, e->swap_file_sz, e->refcount, storeKeyText(e->key));
878 min_age = age;
879 storeRelease(e);
880 } else {
881 /*
882 * Did not expire the object so we need to add it back
883 * into the heap!
884 */
885 debug(20, 5) ("storeMaintainSwapSpace: non-expired %s\n",
886 storeKeyText(e->key));
887 linklistPush(&locked_entries, e);
888 continue;
889 }
890 if (store_swap_size < store_swap_low)
891 break;
892 else if (expired >= max_remove)
893 break;
894 else if (scanned >= max_scan)
895 break;
896 }
897 /*
898 * Bump the heap age factor.
899 */
900 if (min_age > 0.0)
901 store_heap->age = min_age;
902 /*
903 * Reinsert all bumped locked entries back into heap...
904 */
905 while ((e = linklistShift(&locked_entries)))
906 e->node = heap_insert(store_heap, e);
907 #else
908 for (m = store_list.tail; m; m = prev) {
909 prev = m->prev;
910 e = m->data;
911 scanned++;
912 if (storeEntryLocked(e)) {
913 /*
914 * If there is a locked entry at the tail of the LRU list,
915 * move it to the beginning to get it out of the way.
916 * Theoretically, we might have all locked objects at the
917 * tail, and then we'll never remove anything here and the
918 * LRU age will go to zero.
919 */
920 if (memInUse(MEM_STOREENTRY) > max_scan) {
921 dlinkDelete(&e->lru, &store_list);
922 dlinkAdd(e, &e->lru, &store_list);
923 }
924 locked++;
925 } else if (storeCheckExpired(e)) {
926 expired++;
927 storeRelease(e);
928 }
929 if (expired >= max_remove)
930 break;
931 if (scanned >= max_scan)
932 break;
933 }
934 #endif
935 debug(20, (expired ? 2 : 3)) ("storeMaintainSwapSpace: scanned %d/%d removed %d/%d locked %d f=%.03f\n",
936 scanned, max_scan, expired, max_remove, locked, f);
937 debug(20, 3) ("storeMaintainSwapSpace stats:\n");
938 debug(20, 3) (" %6d objects\n", memInUse(MEM_STOREENTRY));
939 debug(20, 3) (" %6d were scanned\n", scanned);
940 debug(20, 3) (" %6d were locked\n", locked);
941 debug(20, 3) (" %6d were expired\n", expired);
942 if (store_swap_size < Config.Swap.maxSize)
943 return;
944 if (squid_curtime - last_warn_time < 10)
945 return;
946 debug(20, 0) ("WARNING: Disk space over limit: %d KB > %d KB\n",
947 store_swap_size, Config.Swap.maxSize);
948 last_warn_time = squid_curtime;
949 }
950
951
952 /* release an object from a cache */
953 /* return number of objects released. */
954 void
955 storeRelease(StoreEntry * e)
956 {
957 debug(20, 3) ("storeRelease: Releasing: '%s'\n", storeKeyText(e->key));
958 /* If, for any reason we can't discard this object because of an
959 * outstanding request, mark it for pending release */
960 if (storeEntryLocked(e)) {
961 storeExpireNow(e);
962 debug(20, 3) ("storeRelease: Only setting RELEASE_REQUEST bit\n");
963 storeReleaseRequest(e);
964 return;
965 }
966 if (store_dirs_rebuilding && e->swap_file_number > -1) {
967 storeSetPrivateKey(e);
968 if (e->mem_obj) {
969 storeSetMemStatus(e, NOT_IN_MEMORY);
970 destroy_MemObject(e);
971 }
972 /*
973 * Fake a call to storeLockObject(). When rebuilding is done,
974 * we'll just call storeUnlockObject() on these.
975 */
976 e->lock_count++;
977 stackPush(&LateReleaseStack, e);
978 return;
979 }
980 storeLog(STORE_LOG_RELEASE, e);
981 if (e->swap_file_number > -1) {
982 storeUnlink(e->swap_file_number);
983 storeDirMapBitReset(e->swap_file_number);
984 if (e->swap_status == SWAPOUT_DONE)
985 if (EBIT_TEST(e->flags, ENTRY_VALIDATED))
986 storeDirUpdateSwapSize(e->swap_file_number, e->swap_file_sz, -1);
987 if (!EBIT_TEST(e->flags, KEY_PRIVATE))
988 storeDirSwapLog(e, SWAP_LOG_DEL);
989 }
990 storeSetMemStatus(e, NOT_IN_MEMORY);
991 destroy_StoreEntry(e);
992 }
993
994 static void
995 storeLateRelease(void *unused)
996 {
997 StoreEntry *e;
998 int i;
999 static int n = 0;
1000 if (store_dirs_rebuilding) {
1001 eventAdd("storeLateRelease", storeLateRelease, NULL, 1.0, 1);
1002 return;
1003 }
1004 for (i = 0; i < 10; i++) {
1005 e = stackPop(&LateReleaseStack);
1006 if (e == NULL) {
1007 /* done! */
1008 debug(20, 1) ("storeLateRelease: released %d objects\n", n);
1009 return;
1010 }
1011 storeUnlockObject(e);
1012 n++;
1013 }
1014 eventAdd("storeLateRelease", storeLateRelease, NULL, 0.0, 1);
1015 }
1016
1017 /* return 1 if a store entry is locked */
1018 static int
1019 storeEntryLocked(const StoreEntry * e)
1020 {
1021 if (e->lock_count)
1022 return 1;
1023 if (e->swap_status == SWAPOUT_WRITING)
1024 return 1;
1025 if (e->store_status == STORE_PENDING)
1026 return 1;
1027 /*
1028 * SPECIAL, PUBLIC entries should be "locked"
1029 */
1030 if (EBIT_TEST(e->flags, ENTRY_SPECIAL))
1031 if (!EBIT_TEST(e->flags, KEY_PRIVATE))
1032 return 1;
1033 return 0;
1034 }
1035
1036 static int
1037 storeEntryValidLength(const StoreEntry * e)
1038 {
1039 int diff;
1040 const HttpReply *reply;
1041 assert(e->mem_obj != NULL);
1042 reply = e->mem_obj->reply;
1043 debug(20, 3) ("storeEntryValidLength: Checking '%s'\n", storeKeyText(e->key));
1044 debug(20, 5) ("storeEntryValidLength: object_len = %d\n",
1045 objectLen(e));
1046 debug(20, 5) ("storeEntryValidLength: hdr_sz = %d\n",
1047 reply->hdr_sz);
1048 debug(20, 5) ("storeEntryValidLength: content_length = %d\n",
1049 reply->content_length);
1050 if (reply->content_length < 0) {
1051 debug(20, 5) ("storeEntryValidLength: Unspecified content length: %s\n",
1052 storeKeyText(e->key));
1053 return 1;
1054 }
1055 if (reply->hdr_sz == 0) {
1056 debug(20, 5) ("storeEntryValidLength: Zero header size: %s\n",
1057 storeKeyText(e->key));
1058 return 1;
1059 }
1060 if (e->mem_obj->method == METHOD_HEAD) {
1061 debug(20, 5) ("storeEntryValidLength: HEAD request: %s\n",
1062 storeKeyText(e->key));
1063 return 1;
1064 }
1065 if (reply->sline.status == HTTP_NOT_MODIFIED)
1066 return 1;
1067 if (reply->sline.status == HTTP_NO_CONTENT)
1068 return 1;
1069 diff = reply->hdr_sz + reply->content_length - objectLen(e);
1070 if (diff == 0)
1071 return 1;
1072 debug(20, 3) ("storeEntryValidLength: %d bytes too %s; '%s'\n",
1073 diff < 0 ? -diff : diff,
1074 diff < 0 ? "big" : "small",
1075 storeKeyText(e->key));
1076 return 0;
1077 }
1078
1079 static void
1080 storeInitHashValues(void)
1081 {
1082 int i;
1083 /* Calculate size of hash table (maximum currently 64k buckets). */
1084 i = Config.Swap.maxSize / Config.Store.avgObjectSize;
1085 debug(20, 1) ("Swap maxSize %d KB, estimated %d objects\n",
1086 Config.Swap.maxSize, i);
1087 i /= Config.Store.objectsPerBucket;
1088 debug(20, 1) ("Target number of buckets: %d\n", i);
1089 /* ideally the full scan period should be configurable, for the
1090 * moment it remains at approximately 24 hours. */
1091 store_hash_buckets = storeKeyHashBuckets(i);
1092 debug(20, 1) ("Using %d Store buckets\n", store_hash_buckets);
1093 debug(20, 1) ("Max Mem size: %d KB\n", Config.memMaxSize >> 10);
1094 debug(20, 1) ("Max Swap size: %d KB\n", Config.Swap.maxSize);
1095 }
1096
1097 #if HEAP_REPLACEMENT
1098 #include "store_heap_replacement.c"
1099 #endif
1100
1101 void
1102 storeInit(void)
1103 {
1104 storeKeyInit();
1105 storeInitHashValues();
1106 store_table = hash_create(storeKeyHashCmp,
1107 store_hash_buckets, storeKeyHashHash);
1108 storeDigestInit();
1109 storeLogOpen();
1110 #if HEAP_REPLACEMENT
1111 /*
1112 * Create new heaps with cache replacement policies attached to them.
1113 * The cache replacement policy is specified as either GDSF or LFUDA in
1114 * the squid.conf configuration file. Note that the replacement policy
1115 * applies only to the disk replacement algorithm. Memory replacement
1116 * always uses GDSF since we want to maximize object hit rate.
1117 */
1118 inmem_heap = new_heap(1000, HeapKeyGen_StoreEntry_GDSF);
1119 if (Config.replPolicy) {
1120 if (tolower(Config.replPolicy[0]) == 'g') {
1121 debug(20, 1) ("Using GDSF disk replacement policy\n");
1122 store_heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
1123 } else if (tolower(Config.replPolicy[0]) == 'l') {
1124 if (tolower(Config.replPolicy[1]) == 'f') {
1125 debug(20, 1) ("Using LFUDA disk replacement policy\n");
1126 store_heap = new_heap(10000, HeapKeyGen_StoreEntry_LFUDA);
1127 } else if (tolower(Config.replPolicy[1]) == 'r') {
1128 debug(20, 1) ("Using LRU heap disk replacement policy\n");
1129 store_heap = new_heap(10000, HeapKeyGen_StoreEntry_LRU);
1130 }
1131 } else {
1132 debug(20, 1) ("Unrecognized replacement_policy; using GDSF\n");
1133 store_heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
1134 }
1135 } else {
1136 debug(20, 1) ("Using default disk replacement policy (GDSF)\n");
1137 store_heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
1138 }
1139 #else
1140 store_list.head = store_list.tail = NULL;
1141 inmem_list.head = inmem_list.tail = NULL;
1142 #endif
1143 stackInit(&LateReleaseStack);
1144 eventAdd("storeLateRelease", storeLateRelease, NULL, 1.0, 1);
1145 storeDirInit();
1146 storeRebuildStart();
1147 cachemgrRegister("storedir",
1148 "Store Directory Stats",
1149 storeDirStats, 0, 1);
1150 cachemgrRegister("store_check_cachable_stats",
1151 "storeCheckCachable() Stats",
1152 storeCheckCachableStats, 0, 1);
1153 }
1154
1155 void
1156 storeConfigure(void)
1157 {
1158 store_swap_high = (long) (((float) Config.Swap.maxSize *
1159 (float) Config.Swap.highWaterMark) / (float) 100);
1160 store_swap_low = (long) (((float) Config.Swap.maxSize *
1161 (float) Config.Swap.lowWaterMark) / (float) 100);
1162 store_pages_max = Config.memMaxSize / SM_PAGE_SIZE;
1163 }
1164
1165 static int
1166 storeKeepInMemory(const StoreEntry * e)
1167 {
1168 MemObject *mem = e->mem_obj;
1169 if (mem == NULL)
1170 return 0;
1171 if (mem->data_hdr.head == NULL)
1172 return 0;
1173 return mem->inmem_lo == 0;
1174 }
1175
1176 static int
1177 storeCheckExpired(const StoreEntry * e)
1178 {
1179 if (storeEntryLocked(e))
1180 return 0;
1181 if (EBIT_TEST(e->flags, RELEASE_REQUEST))
1182 return 1;
1183 if (EBIT_TEST(e->flags, ENTRY_NEGCACHED) && squid_curtime >= e->expires)
1184 return 1;
1185 #if HEAP_REPLACEMENT
1186 /*
1187 * With HEAP_REPLACEMENT we are not using the LRU reference age, the heap
1188 * controls the replacement of objects.
1189 */
1190 return 1;
1191 #else
1192 if (squid_curtime - e->lastref > storeExpiredReferenceAge())
1193 return 1;
1194 return 0;
1195 #endif
1196 }
1197
1198 #if !HEAP_REPLACEMENT
1199 /*
1200 * storeExpiredReferenceAge
1201 *
1202 * The LRU age is scaled exponentially between 1 minute and
1203 * Config.referenceAge , when store_swap_low < store_swap_size <
1204 * store_swap_high. This keeps store_swap_size within the low and high
1205 * water marks. If the cache is very busy then store_swap_size stays
1206 * closer to the low water mark, if it is not busy, then it will stay
1207 * near the high water mark. The LRU age value can be examined on the
1208 * cachemgr 'info' page.
1209 */
1210 time_t
1211 storeExpiredReferenceAge(void)
1212 {
1213 double x;
1214 double z;
1215 time_t age;
1216 x = (double) (store_swap_high - store_swap_size) / (store_swap_high - store_swap_low);
1217 x = x < 0.0 ? 0.0 : x > 1.0 ? 1.0 : x;
1218 z = pow((double) (Config.referenceAge / 60), x);
1219 age = (time_t) (z * 60.0);
1220 if (age < 60)
1221 age = 60;
1222 else if (age > 31536000)
1223 age = 31536000;
1224 return age;
1225 }
1226 #endif
1227
1228 void
1229 storeNegativeCache(StoreEntry * e)
1230 {
1231 e->expires = squid_curtime + Config.negativeTtl;
1232 EBIT_SET(e->flags, ENTRY_NEGCACHED);
1233 }
1234
1235 void
1236 storeFreeMemory(void)
1237 {
1238 hashFreeItems(store_table, destroy_StoreEntry);
1239 hashFreeMemory(store_table);
1240 store_table = NULL;
1241 #if USE_CACHE_DIGESTS
1242 if (store_digest)
1243 cacheDigestDestroy(store_digest);
1244 #endif
1245 store_digest = NULL;
1246 }
1247
1248 int
1249 expiresMoreThan(time_t expires, time_t when)
1250 {
1251 if (expires < 0) /* No Expires given */
1252 return 1;
1253 return (expires > (squid_curtime + when));
1254 }
1255
1256 int
1257 storeEntryValidToSend(StoreEntry * e)
1258 {
1259 if (EBIT_TEST(e->flags, RELEASE_REQUEST))
1260 return 0;
1261 if (EBIT_TEST(e->flags, ENTRY_NEGCACHED))
1262 if (e->expires <= squid_curtime)
1263 return 0;
1264 if (EBIT_TEST(e->flags, ENTRY_ABORTED))
1265 return 0;
1266 return 1;
1267 }
1268
1269 void
1270 storeTimestampsSet(StoreEntry * entry)
1271 {
1272 const HttpReply *reply = entry->mem_obj->reply;
1273 time_t served_date = reply->date;
1274 /* make sure that 0 <= served_date <= squid_curtime */
1275 if (served_date < 0 || served_date > squid_curtime)
1276 served_date = squid_curtime;
1277 entry->expires = reply->expires;
1278 entry->lastmod = reply->last_modified;
1279 entry->timestamp = served_date;
1280 }
1281
1282 void
1283 storeRegisterAbort(StoreEntry * e, STABH * cb, void *data)
1284 {
1285 MemObject *mem = e->mem_obj;
1286 assert(mem);
1287 assert(mem->abort.callback == NULL);
1288 mem->abort.callback = cb;
1289 mem->abort.data = data;
1290 }
1291
1292 void
1293 storeUnregisterAbort(StoreEntry * e)
1294 {
1295 MemObject *mem = e->mem_obj;
1296 assert(mem);
1297 mem->abort.callback = NULL;
1298 }
1299
1300 void
1301 storeMemObjectDump(MemObject * mem)
1302 {
1303 debug(20, 1) ("MemObject->data.head: %p\n",
1304 mem->data_hdr.head);
1305 debug(20, 1) ("MemObject->data.tail: %p\n",
1306 mem->data_hdr.tail);
1307 debug(20, 1) ("MemObject->data.origin_offset: %d\n",
1308 mem->data_hdr.origin_offset);
1309 debug(20, 1) ("MemObject->start_ping: %d.%06d\n",
1310 (int) mem->start_ping.tv_sec,
1311 (int) mem->start_ping.tv_usec);
1312 debug(20, 1) ("MemObject->inmem_hi: %d\n",
1313 (int) mem->inmem_hi);
1314 debug(20, 1) ("MemObject->inmem_lo: %d\n",
1315 (int) mem->inmem_lo);
1316 debug(20, 1) ("MemObject->clients: %p\n",
1317 mem->clients);
1318 debug(20, 1) ("MemObject->nclients: %d\n",
1319 mem->nclients);
1320 debug(20, 1) ("MemObject->reply: %p\n",
1321 mem->reply);
1322 debug(20, 1) ("MemObject->request: %p\n",
1323 mem->request);
1324 debug(20, 1) ("MemObject->log_url: %p %s\n",
1325 mem->log_url,
1326 checkNullString(mem->log_url));
1327 }
1328
1329 void
1330 storeEntryDump(const StoreEntry * e, int l)
1331 {
1332 debug(20, l) ("StoreEntry->key: %s\n", storeKeyText(e->key));
1333 debug(20, l) ("StoreEntry->next: %p\n", e->next);
1334 debug(20, l) ("StoreEntry->mem_obj: %p\n", e->mem_obj);
1335 debug(20, l) ("StoreEntry->timestamp: %d\n", (int) e->timestamp);
1336 debug(20, l) ("StoreEntry->lastref: %d\n", (int) e->lastref);
1337 debug(20, l) ("StoreEntry->expires: %d\n", (int) e->expires);
1338 debug(20, l) ("StoreEntry->lastmod: %d\n", (int) e->lastmod);
1339 debug(20, l) ("StoreEntry->swap_file_sz: %d\n", (int) e->swap_file_sz);
1340 debug(20, l) ("StoreEntry->refcount: %d\n", e->refcount);
1341 debug(20, l) ("StoreEntry->flags: %s\n", storeEntryFlags(e));
1342 debug(20, l) ("StoreEntry->swap_file_number: %d\n", (int) e->swap_file_number);
1343 debug(20, l) ("StoreEntry->lock_count: %d\n", (int) e->lock_count);
1344 debug(20, l) ("StoreEntry->mem_status: %d\n", (int) e->mem_status);
1345 debug(20, l) ("StoreEntry->ping_status: %d\n", (int) e->ping_status);
1346 debug(20, l) ("StoreEntry->store_status: %d\n", (int) e->store_status);
1347 debug(20, l) ("StoreEntry->swap_status: %d\n", (int) e->swap_status);
1348 }
1349
1350 /*
1351 * NOTE, this function assumes only two mem states
1352 */
1353 void
1354 storeSetMemStatus(StoreEntry * e, int new_status)
1355 {
1356 MemObject *mem = e->mem_obj;
1357 if (new_status == e->mem_status)
1358 return;
1359 assert(mem != NULL);
1360 if (new_status == IN_MEMORY) {
1361 assert(mem->inmem_lo == 0);
1362 #if HEAP_REPLACEMENT
1363 if (mem->node == NULL) {
1364 if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
1365 debug(20, 4) ("storeSetMemStatus: not inserting special %s\n",
1366 mem->url);
1367 } else {
1368 mem->node = heap_insert(inmem_heap, e);
1369 debug(20, 4) ("storeSetMemStatus: inserted mem node %p\n",
1370 mem->node);
1371 }
1372 }
1373 #else
1374 dlinkAdd(e, &mem->lru, &inmem_list);
1375 #endif
1376 hot_obj_count++;
1377 } else {
1378 #if HEAP_REPLACEMENT
1379 /*
1380 * It's being removed from the memory heap; is it already gone?
1381 */
1382 if (mem->node) {
1383 heap_delete(inmem_heap, mem->node);
1384 debug(20, 4) ("storeSetMemStatus: deleted mem node %p\n",
1385 mem->node);
1386 mem->node = NULL;
1387 }
1388 #else
1389 dlinkDelete(&mem->lru, &inmem_list);
1390 #endif
1391 hot_obj_count--;
1392 }
1393 e->mem_status = new_status;
1394 }
1395
1396 const char *
1397 storeUrl(const StoreEntry * e)
1398 {
1399 if (e == NULL)
1400 return "[null_entry]";
1401 else if (e->mem_obj == NULL)
1402 return "[null_mem_obj]";
1403 else
1404 return e->mem_obj->url;
1405 }
1406
1407 void
1408 storeCreateMemObject(StoreEntry * e, const char *url, const char *log_url)
1409 {
1410 if (e->mem_obj)
1411 return;
1412 e->mem_obj = new_MemObject(url, log_url);
1413 }
1414
1415 /* this just sets DELAY_SENDING */
1416 void
1417 storeBuffer(StoreEntry * e)
1418 {
1419 EBIT_SET(e->flags, DELAY_SENDING);
1420 }
1421
1422 /* this just clears DELAY_SENDING and Invokes the handlers */
1423 void
1424 storeBufferFlush(StoreEntry * e)
1425 {
1426 EBIT_CLR(e->flags, DELAY_SENDING);
1427 InvokeHandlers(e);
1428 storeSwapOut(e);
1429 }
1430
1431 int
1432 objectLen(const StoreEntry * e)
1433 {
1434 assert(e->mem_obj != NULL);
1435 return e->mem_obj->object_sz;
1436 }
1437
1438 int
1439 contentLen(const StoreEntry * e)
1440 {
1441 assert(e->mem_obj != NULL);
1442 assert(e->mem_obj->reply != NULL);
1443 return e->mem_obj->object_sz - e->mem_obj->reply->hdr_sz;
1444 }
1445
1446 HttpReply *
1447 storeEntryReply(StoreEntry * e)
1448 {
1449 if (NULL == e)
1450 return NULL;
1451 if (NULL == e->mem_obj)
1452 return NULL;
1453 return e->mem_obj->reply;
1454 }
1455
1456 void
1457 storeEntryReset(StoreEntry * e)
1458 {
1459 MemObject *mem = e->mem_obj;
1460 debug(20, 3) ("storeEntryReset: %s\n", storeUrl(e));
1461 assert(mem->swapout.sio == NULL);
1462 stmemFree(&mem->data_hdr);
1463 mem->inmem_hi = mem->inmem_lo = 0;
1464 httpReplyDestroy(mem->reply);
1465 mem->reply = httpReplyCreate();
1466 e->expires = e->lastmod = e->timestamp = -1;
1467 }
1468
1469 #if HEAP_REPLACEMENT
1470 void
1471 storeHeapPositionUpdate(StoreEntry * e)
1472 {
1473 if (e->node)
1474 heap_update(store_heap, e->node, e);
1475 if (e->mem_obj && e->mem_obj->node)
1476 heap_update(inmem_heap, e->mem_obj->node, e);
1477 }
1478 #endif