]> git.ipfire.org Git - thirdparty/squid.git/blob - src/store.cc
Avoid making 'served_date' negative in storeTimestampsSet().
[thirdparty/squid.git] / src / store.cc
1
2 /*
3 * $Id: store.cc,v 1.537 2001/02/22 20:53:15 wessels Exp $
4 *
5 * DEBUG: section 20 Storage Manager
6 * AUTHOR: Harvest Derived
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see 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 extern OBJH storeIOStats;
75
76 /*
77 * local function prototypes
78 */
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 void storeEntryReferenced(StoreEntry *);
87 static void storeEntryDereferenced(StoreEntry *);
88 static int getKeyCounter(void);
89 static int storeKeepInMemory(const StoreEntry *);
90 static OBJH storeCheckCachableStats;
91 static EVH storeLateRelease;
92
93 /*
94 * local variables
95 */
96 static Stack LateReleaseStack;
97
98 #if URL_CHECKSUM_DEBUG
99 unsigned int
100 url_checksum(const char *url)
101 {
102 unsigned int ck;
103 MD5_CTX M;
104 static unsigned char digest[16];
105 MD5Init(&M);
106 MD5Update(&M, (unsigned char *) url, strlen(url));
107 MD5Final(digest, &M);
108 xmemcpy(&ck, digest, sizeof(ck));
109 return ck;
110 }
111 #endif
112
113 static MemObject *
114 new_MemObject(const char *url, const char *log_url)
115 {
116 MemObject *mem = memAllocate(MEM_MEMOBJECT);
117 mem->reply = httpReplyCreate();
118 mem->url = xstrdup(url);
119 #if URL_CHECKSUM_DEBUG
120 mem->chksum = url_checksum(mem->url);
121 #endif
122 mem->log_url = xstrdup(log_url);
123 mem->object_sz = -1;
124 mem->fd = -1;
125 /* XXX account log_url */
126 debug(20, 3) ("new_MemObject: returning %p\n", mem);
127 return mem;
128 }
129
130 StoreEntry *
131 new_StoreEntry(int mem_obj_flag, const char *url, const char *log_url)
132 {
133 StoreEntry *e = NULL;
134 e = memAllocate(MEM_STOREENTRY);
135 if (mem_obj_flag)
136 e->mem_obj = new_MemObject(url, log_url);
137 debug(20, 3) ("new_StoreEntry: returning %p\n", e);
138 e->expires = e->lastmod = e->lastref = e->timestamp = -1;
139 e->swap_filen = -1;
140 e->swap_dirn = -1;
141 return e;
142 }
143
144 static void
145 destroy_MemObject(StoreEntry * e)
146 {
147 MemObject *mem = e->mem_obj;
148 const Ctx ctx = ctx_enter(mem->url);
149 debug(20, 3) ("destroy_MemObject: destroying %p\n", mem);
150 #if URL_CHECKSUM_DEBUG
151 assert(mem->chksum == url_checksum(mem->url));
152 #endif
153 e->mem_obj = NULL;
154 if (!shutting_down)
155 assert(mem->swapout.sio == NULL);
156 stmemFree(&mem->data_hdr);
157 mem->inmem_hi = 0;
158 /*
159 * There is no way to abort FD-less clients, so they might
160 * still have mem->clients set if mem->fd == -1
161 */
162 assert(mem->fd == -1 || mem->clients.head == NULL);
163 httpReplyDestroy(mem->reply);
164 requestUnlink(mem->request);
165 mem->request = NULL;
166 ctx_exit(ctx); /* must exit before we free mem->url */
167 safe_free(mem->url);
168 safe_free(mem->log_url); /* XXX account log_url */
169 memFree(mem, MEM_MEMOBJECT);
170 }
171
172 static void
173 destroy_StoreEntry(void *data)
174 {
175 StoreEntry *e = data;
176 debug(20, 3) ("destroy_StoreEntry: destroying %p\n", e);
177 assert(e != NULL);
178 if (e->mem_obj)
179 destroy_MemObject(e);
180 storeHashDelete(e);
181 assert(e->hash.key == NULL);
182 memFree(e, MEM_STOREENTRY);
183 }
184
185 /* ----- INTERFACE BETWEEN STORAGE MANAGER AND HASH TABLE FUNCTIONS --------- */
186
187 void
188 storeHashInsert(StoreEntry * e, const cache_key * key)
189 {
190 debug(20, 3) ("storeHashInsert: Inserting Entry %p key '%s'\n",
191 e, storeKeyText(key));
192 e->hash.key = storeKeyDup(key);
193 hash_join(store_table, &e->hash);
194 }
195
196 static void
197 storeHashDelete(StoreEntry * e)
198 {
199 hash_remove_link(store_table, &e->hash);
200 storeKeyFree(e->hash.key);
201 e->hash.key = NULL;
202 }
203
204 /* -------------------------------------------------------------------------- */
205
206
207 /* get rid of memory copy of the object */
208 /* Only call this if storeCheckPurgeMem(e) returns 1 */
209 static void
210 storePurgeMem(StoreEntry * e)
211 {
212 if (e->mem_obj == NULL)
213 return;
214 debug(20, 3) ("storePurgeMem: Freeing memory-copy of %s\n",
215 storeKeyText(e->hash.key));
216 storeSetMemStatus(e, NOT_IN_MEMORY);
217 destroy_MemObject(e);
218 if (e->swap_status != SWAPOUT_DONE)
219 storeRelease(e);
220 }
221
222 static void
223 storeEntryReferenced(StoreEntry * e)
224 {
225 SwapDir *SD;
226
227 /* Notify the fs that we're referencing this object again */
228 if (e->swap_dirn > -1) {
229 SD = INDEXSD(e->swap_dirn);
230 if (SD->refobj)
231 SD->refobj(SD, e);
232 }
233 /* Notify the memory cache that we're referencing this object again */
234 if (e->mem_obj) {
235 if (mem_policy->Referenced)
236 mem_policy->Referenced(mem_policy, e, &e->mem_obj->repl);
237 }
238 }
239
240 static void
241 storeEntryDereferenced(StoreEntry * e)
242 {
243 SwapDir *SD;
244
245 /* Notify the fs that we're not referencing this object any more */
246 if (e->swap_filen > -1) {
247 SD = INDEXSD(e->swap_dirn);
248 if (SD->unrefobj != NULL)
249 SD->unrefobj(SD, e);
250 }
251 /* Notify the memory cache that we're not referencing this object any more */
252 if (e->mem_obj) {
253 if (mem_policy->Dereferenced)
254 mem_policy->Dereferenced(mem_policy, e, &e->mem_obj->repl);
255 }
256 }
257
258 void
259 storeLockObject(StoreEntry * e)
260 {
261 e->lock_count++;
262 debug(20, 3) ("storeLockObject: key '%s' count=%d\n",
263 storeKeyText(e->hash.key), (int) e->lock_count);
264 e->lastref = squid_curtime;
265 storeEntryReferenced(e);
266 }
267
268 void
269 storeReleaseRequest(StoreEntry * e)
270 {
271 if (EBIT_TEST(e->flags, RELEASE_REQUEST))
272 return;
273 debug(20, 3) ("storeReleaseRequest: '%s'\n", storeKeyText(e->hash.key));
274 EBIT_SET(e->flags, RELEASE_REQUEST);
275 /*
276 * Clear cachable flag here because we might get called before
277 * anyone else even looks at the cachability flag. Also, this
278 * prevents httpMakePublic from really setting a public key.
279 */
280 EBIT_CLR(e->flags, ENTRY_CACHABLE);
281 storeSetPrivateKey(e);
282 }
283
284 /* unlock object, return -1 if object get released after unlock
285 * otherwise lock_count */
286 int
287 storeUnlockObject(StoreEntry * e)
288 {
289 e->lock_count--;
290 debug(20, 3) ("storeUnlockObject: key '%s' count=%d\n",
291 storeKeyText(e->hash.key), e->lock_count);
292 if (e->lock_count)
293 return (int) e->lock_count;
294 if (e->store_status == STORE_PENDING)
295 EBIT_SET(e->flags, RELEASE_REQUEST);
296 assert(storePendingNClients(e) == 0);
297 if (EBIT_TEST(e->flags, RELEASE_REQUEST))
298 storeRelease(e);
299 else if (storeKeepInMemory(e)) {
300 storeEntryDereferenced(e);
301 storeSetMemStatus(e, IN_MEMORY);
302 requestUnlink(e->mem_obj->request);
303 e->mem_obj->request = NULL;
304 } else {
305 storePurgeMem(e);
306 storeEntryDereferenced(e);
307 if (EBIT_TEST(e->flags, KEY_PRIVATE))
308 debug(20, 1) ("WARNING: %s:%d: found KEY_PRIVATE\n", __FILE__, __LINE__);
309 }
310 return 0;
311 }
312
313 /* Lookup an object in the cache.
314 * return just a reference to object, don't start swapping in yet. */
315 StoreEntry *
316 storeGet(const cache_key * key)
317 {
318 debug(20, 3) ("storeGet: looking up %s\n", storeKeyText(key));
319 return (StoreEntry *) hash_lookup(store_table, key);
320 }
321
322 StoreEntry *
323 storeGetPublic(const char *uri, const method_t method)
324 {
325 return storeGet(storeKeyPublic(uri, method));
326 }
327
328 static int
329 getKeyCounter(void)
330 {
331 static int key_counter = 0;
332 if (++key_counter < 0)
333 key_counter = 1;
334 return key_counter;
335 }
336
337 void
338 storeSetPrivateKey(StoreEntry * e)
339 {
340 const cache_key *newkey;
341 MemObject *mem = e->mem_obj;
342 if (e->hash.key && EBIT_TEST(e->flags, KEY_PRIVATE))
343 return; /* is already private */
344 if (e->hash.key) {
345 if (e->swap_filen > -1)
346 storeDirSwapLog(e, SWAP_LOG_DEL);
347 storeHashDelete(e);
348 }
349 if (mem != NULL) {
350 mem->id = getKeyCounter();
351 newkey = storeKeyPrivate(mem->url, mem->method, mem->id);
352 } else {
353 newkey = storeKeyPrivate("JUNK", METHOD_NONE, getKeyCounter());
354 }
355 assert(hash_lookup(store_table, newkey) == NULL);
356 EBIT_SET(e->flags, KEY_PRIVATE);
357 storeHashInsert(e, newkey);
358 }
359
360 void
361 storeSetPublicKey(StoreEntry * e)
362 {
363 StoreEntry *e2 = NULL;
364 const cache_key *newkey;
365 MemObject *mem = e->mem_obj;
366 if (e->hash.key && !EBIT_TEST(e->flags, KEY_PRIVATE))
367 return; /* is already public */
368 assert(mem);
369 /*
370 * We can't make RELEASE_REQUEST objects public. Depending on
371 * when RELEASE_REQUEST gets set, we might not be swapping out
372 * the object. If we're not swapping out, then subsequent
373 * store clients won't be able to access object data which has
374 * been freed from memory.
375 *
376 * If RELEASE_REQUEST is set, then ENTRY_CACHABLE should not
377 * be set, and storeSetPublicKey() should not be called.
378 */
379 #if MORE_DEBUG_OUTPUT
380 if (EBIT_TEST(e->flags, RELEASE_REQUEST))
381 debug(20, 1) ("assertion failed: RELEASE key %s, url %s\n",
382 e->hash.key, mem->url);
383 #endif
384 assert(!EBIT_TEST(e->flags, RELEASE_REQUEST));
385 newkey = storeKeyPublic(mem->url, mem->method);
386 if ((e2 = (StoreEntry *) hash_lookup(store_table, newkey))) {
387 debug(20, 3) ("storeSetPublicKey: Making old '%s' private.\n", mem->url);
388 storeSetPrivateKey(e2);
389 storeRelease(e2);
390 newkey = storeKeyPublic(mem->url, mem->method);
391 }
392 if (e->hash.key)
393 storeHashDelete(e);
394 EBIT_CLR(e->flags, KEY_PRIVATE);
395 storeHashInsert(e, newkey);
396 if (e->swap_filen > -1)
397 storeDirSwapLog(e, SWAP_LOG_ADD);
398 }
399
400 StoreEntry *
401 storeCreateEntry(const char *url, const char *log_url, request_flags flags, method_t method)
402 {
403 StoreEntry *e = NULL;
404 MemObject *mem = NULL;
405 debug(20, 3) ("storeCreateEntry: '%s'\n", url);
406
407 e = new_StoreEntry(STORE_ENTRY_WITH_MEMOBJ, url, log_url);
408 e->lock_count = 1; /* Note lock here w/o calling storeLock() */
409 mem = e->mem_obj;
410 mem->method = method;
411 if (neighbors_do_private_keys || !flags.hierarchical)
412 storeSetPrivateKey(e);
413 else
414 storeSetPublicKey(e);
415 if (flags.cachable) {
416 EBIT_SET(e->flags, ENTRY_CACHABLE);
417 EBIT_CLR(e->flags, RELEASE_REQUEST);
418 } else {
419 EBIT_CLR(e->flags, ENTRY_CACHABLE);
420 storeReleaseRequest(e);
421 }
422 e->store_status = STORE_PENDING;
423 storeSetMemStatus(e, NOT_IN_MEMORY);
424 e->swap_status = SWAPOUT_NONE;
425 e->swap_filen = -1;
426 e->swap_dirn = -1;
427 e->refcount = 0;
428 e->lastref = squid_curtime;
429 e->timestamp = -1; /* set in storeTimestampsSet() */
430 e->ping_status = PING_NONE;
431 EBIT_SET(e->flags, ENTRY_VALIDATED);
432 return e;
433 }
434
435 /* Mark object as expired */
436 void
437 storeExpireNow(StoreEntry * e)
438 {
439 debug(20, 3) ("storeExpireNow: '%s'\n", storeKeyText(e->hash.key));
440 e->expires = squid_curtime;
441 }
442
443 /* Append incoming data from a primary server to an entry. */
444 void
445 storeAppend(StoreEntry * e, const char *buf, int len)
446 {
447 MemObject *mem = e->mem_obj;
448 assert(mem != NULL);
449 assert(len >= 0);
450 assert(e->store_status == STORE_PENDING);
451 if (len) {
452 debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n",
453 len,
454 storeKeyText(e->hash.key));
455 storeGetMemSpace(len);
456 stmemAppend(&mem->data_hdr, buf, len);
457 mem->inmem_hi += len;
458 }
459 if (EBIT_TEST(e->flags, DELAY_SENDING))
460 return;
461 InvokeHandlers(e);
462 storeSwapOut(e);
463 }
464
465 void
466 #if STDC_HEADERS
467 storeAppendPrintf(StoreEntry * e, const char *fmt,...)
468 #else
469 storeAppendPrintf(va_alist)
470 va_dcl
471 #endif
472 {
473 #if STDC_HEADERS
474 va_list args;
475 va_start(args, fmt);
476 #else
477 va_list args;
478 StoreEntry *e = NULL;
479 const char *fmt = NULL;
480 va_start(args);
481 e = va_arg(args, StoreEntry *);
482 fmt = va_arg(args, char *);
483 #endif
484 storeAppendVPrintf(e, fmt, args);
485 va_end(args);
486 }
487
488 /* used be storeAppendPrintf and Packer */
489 void
490 storeAppendVPrintf(StoreEntry * e, const char *fmt, va_list vargs)
491 {
492 LOCAL_ARRAY(char, buf, 4096);
493 buf[0] = '\0';
494 vsnprintf(buf, 4096, fmt, vargs);
495 storeAppend(e, buf, strlen(buf));
496 }
497
498 struct _store_check_cachable_hist {
499 struct {
500 int non_get;
501 int not_entry_cachable;
502 int release_request;
503 int wrong_content_length;
504 int negative_cached;
505 int too_big;
506 int too_small;
507 int private_key;
508 int too_many_open_files;
509 int too_many_open_fds;
510 } no;
511 struct {
512 int Default;
513 } yes;
514 } store_check_cachable_hist;
515
516 int
517 storeTooManyDiskFilesOpen(void)
518 {
519 if (Config.max_open_disk_fds == 0)
520 return 0;
521 if (store_open_disk_fd > Config.max_open_disk_fds)
522 return 1;
523 return 0;
524 }
525
526 static int
527 storeCheckTooSmall(StoreEntry * e)
528 {
529 MemObject *mem = e->mem_obj;
530 if (EBIT_TEST(e->flags, ENTRY_SPECIAL))
531 return 0;
532 if (STORE_OK == e->store_status)
533 if (mem->object_sz < Config.Store.minObjectSize)
534 return 1;
535 if (mem->reply->content_length > -1)
536 if (mem->reply->content_length < (int) Config.Store.minObjectSize)
537 return 1;
538 return 0;
539 }
540
541 int
542 storeCheckCachable(StoreEntry * e)
543 {
544 #if CACHE_ALL_METHODS
545 if (e->mem_obj->method != METHOD_GET) {
546 debug(20, 2) ("storeCheckCachable: NO: non-GET method\n");
547 store_check_cachable_hist.no.non_get++;
548 } else
549 #endif
550 if (!EBIT_TEST(e->flags, ENTRY_CACHABLE)) {
551 debug(20, 2) ("storeCheckCachable: NO: not cachable\n");
552 store_check_cachable_hist.no.not_entry_cachable++;
553 } else if (EBIT_TEST(e->flags, RELEASE_REQUEST)) {
554 debug(20, 2) ("storeCheckCachable: NO: release requested\n");
555 store_check_cachable_hist.no.release_request++;
556 } else if (e->store_status == STORE_OK && EBIT_TEST(e->flags, ENTRY_BAD_LENGTH)) {
557 debug(20, 2) ("storeCheckCachable: NO: wrong content-length\n");
558 store_check_cachable_hist.no.wrong_content_length++;
559 } else if (EBIT_TEST(e->flags, ENTRY_NEGCACHED)) {
560 debug(20, 3) ("storeCheckCachable: NO: negative cached\n");
561 store_check_cachable_hist.no.negative_cached++;
562 return 0; /* avoid release call below */
563 } else if ((e->mem_obj->reply->content_length > 0 &&
564 e->mem_obj->reply->content_length > Config.Store.maxObjectSize) ||
565 e->mem_obj->inmem_hi > Config.Store.maxObjectSize) {
566 debug(20, 2) ("storeCheckCachable: NO: too big\n");
567 store_check_cachable_hist.no.too_big++;
568 } else if (e->mem_obj->reply->content_length > (int) Config.Store.maxObjectSize) {
569 debug(20, 2) ("storeCheckCachable: NO: too big\n");
570 store_check_cachable_hist.no.too_big++;
571 } else if (storeCheckTooSmall(e)) {
572 debug(20, 2) ("storeCheckCachable: NO: too small\n");
573 store_check_cachable_hist.no.too_small++;
574 } else if (EBIT_TEST(e->flags, KEY_PRIVATE)) {
575 debug(20, 3) ("storeCheckCachable: NO: private key\n");
576 store_check_cachable_hist.no.private_key++;
577 } else if (e->swap_status != SWAPOUT_NONE) {
578 /*
579 * here we checked the swap_status because the remaining
580 * cases are only relevant only if we haven't started swapping
581 * out the object yet.
582 */
583 return 1;
584 } else if (storeTooManyDiskFilesOpen()) {
585 debug(20, 2) ("storeCheckCachable: NO: too many disk files open\n");
586 store_check_cachable_hist.no.too_many_open_files++;
587 } else if (fdNFree() < RESERVED_FD) {
588 debug(20, 2) ("storeCheckCachable: NO: too many FD's open\n");
589 store_check_cachable_hist.no.too_many_open_fds++;
590 } else {
591 store_check_cachable_hist.yes.Default++;
592 return 1;
593 }
594 storeReleaseRequest(e);
595 EBIT_CLR(e->flags, ENTRY_CACHABLE);
596 return 0;
597 }
598
599 static void
600 storeCheckCachableStats(StoreEntry * sentry)
601 {
602 storeAppendPrintf(sentry, "Category\t Count\n");
603
604 storeAppendPrintf(sentry, "no.non_get\t%d\n",
605 store_check_cachable_hist.no.non_get);
606 storeAppendPrintf(sentry, "no.not_entry_cachable\t%d\n",
607 store_check_cachable_hist.no.not_entry_cachable);
608 storeAppendPrintf(sentry, "no.release_request\t%d\n",
609 store_check_cachable_hist.no.release_request);
610 storeAppendPrintf(sentry, "no.wrong_content_length\t%d\n",
611 store_check_cachable_hist.no.wrong_content_length);
612 storeAppendPrintf(sentry, "no.negative_cached\t%d\n",
613 store_check_cachable_hist.no.negative_cached);
614 storeAppendPrintf(sentry, "no.too_big\t%d\n",
615 store_check_cachable_hist.no.too_big);
616 storeAppendPrintf(sentry, "no.too_small\t%d\n",
617 store_check_cachable_hist.no.too_small);
618 storeAppendPrintf(sentry, "no.private_key\t%d\n",
619 store_check_cachable_hist.no.private_key);
620 storeAppendPrintf(sentry, "no.too_many_open_files\t%d\n",
621 store_check_cachable_hist.no.too_many_open_files);
622 storeAppendPrintf(sentry, "no.too_many_open_fds\t%d\n",
623 store_check_cachable_hist.no.too_many_open_fds);
624 storeAppendPrintf(sentry, "yes.default\t%d\n",
625 store_check_cachable_hist.yes.Default);
626 }
627
628 /* Complete transfer into the local cache. */
629 void
630 storeComplete(StoreEntry * e)
631 {
632 debug(20, 3) ("storeComplete: '%s'\n", storeKeyText(e->hash.key));
633 if (e->store_status != STORE_PENDING) {
634 /*
635 * if we're not STORE_PENDING, then probably we got aborted
636 * and there should be NO clients on this entry
637 */
638 assert(EBIT_TEST(e->flags, ENTRY_ABORTED));
639 assert(e->mem_obj->nclients == 0);
640 return;
641 }
642 e->mem_obj->object_sz = e->mem_obj->inmem_hi;
643 e->store_status = STORE_OK;
644 assert(e->mem_status == NOT_IN_MEMORY);
645 if (!storeEntryValidLength(e)) {
646 EBIT_SET(e->flags, ENTRY_BAD_LENGTH);
647 storeReleaseRequest(e);
648 }
649 #if USE_CACHE_DIGESTS
650 if (e->mem_obj->request)
651 e->mem_obj->request->hier.store_complete_stop = current_time;
652 #endif
653 /*
654 * We used to call InvokeHandlers, then storeSwapOut. However,
655 * Madhukar Reddy <myreddy@persistence.com> reported that
656 * responses without content length would sometimes get released
657 * in client_side, thinking that the response is incomplete.
658 */
659 storeSwapOut(e);
660 InvokeHandlers(e);
661 }
662
663 /*
664 * Someone wants to abort this transfer. Set the reason in the
665 * request structure, call the server-side callback and mark the
666 * entry for releasing
667 */
668 void
669 storeAbort(StoreEntry * e)
670 {
671 MemObject *mem = e->mem_obj;
672 assert(e->store_status == STORE_PENDING);
673 assert(mem != NULL);
674 debug(20, 6) ("storeAbort: %s\n", storeKeyText(e->hash.key));
675 storeLockObject(e); /* lock while aborting */
676 storeNegativeCache(e);
677 storeReleaseRequest(e);
678 EBIT_SET(e->flags, ENTRY_ABORTED);
679 storeSetMemStatus(e, NOT_IN_MEMORY);
680 e->store_status = STORE_OK;
681 /*
682 * We assign an object length here. The only other place we assign
683 * the object length is in storeComplete()
684 */
685 mem->object_sz = mem->inmem_hi;
686 /* Notify the server side */
687 if (mem->abort.callback) {
688 eventAdd("mem->abort.callback",
689 mem->abort.callback,
690 mem->abort.data,
691 0.0,
692 0);
693 mem->abort.callback = NULL;
694 mem->abort.data = NULL;
695 }
696 /* Notify the client side */
697 InvokeHandlers(e);
698 /* Close any swapout file */
699 storeSwapOutFileClose(e);
700 storeUnlockObject(e); /* unlock */
701 }
702
703 /* Clear Memory storage to accommodate the given object len */
704 static void
705 storeGetMemSpace(int size)
706 {
707 StoreEntry *e = NULL;
708 int released = 0;
709 static time_t last_check = 0;
710 int pages_needed;
711 RemovalPurgeWalker *walker;
712 if (squid_curtime == last_check)
713 return;
714 last_check = squid_curtime;
715 pages_needed = (size / SM_PAGE_SIZE) + 1;
716 if (memInUse(MEM_STMEM_BUF) + pages_needed < store_pages_max)
717 return;
718 debug(20, 2) ("storeGetMemSpace: Starting, need %d pages\n", pages_needed);
719 /* XXX what to set as max_scan here? */
720 walker = mem_policy->PurgeInit(mem_policy, 100000);
721 while ((e = walker->Next(walker))) {
722 storePurgeMem(e);
723 released++;
724 if (memInUse(MEM_STMEM_BUF) + pages_needed < store_pages_max)
725 break;
726 }
727 walker->Done(walker);
728 debug(20, 3) ("storeGetMemSpace stats:\n");
729 debug(20, 3) (" %6d HOT objects\n", hot_obj_count);
730 debug(20, 3) (" %6d were released\n", released);
731 }
732
733 /* The maximum objects to scan for maintain storage space */
734 #define MAINTAIN_MAX_SCAN 1024
735 #define MAINTAIN_MAX_REMOVE 64
736
737 /*
738 * This routine is to be called by main loop in main.c.
739 * It removes expired objects on only one bucket for each time called.
740 * returns the number of objects removed
741 *
742 * This should get called 1/s from main().
743 */
744 void
745 storeMaintainSwapSpace(void *datanotused)
746 {
747 int i;
748 SwapDir *SD;
749 static time_t last_warn_time = 0;
750
751 /* walk each fs */
752 for (i = 0; i < Config.cacheSwap.n_configured; i++) {
753 /* call the maintain function .. */
754 SD = INDEXSD(i);
755 /* XXX FixMe: This should be done "in parallell" on the different
756 * cache_dirs, not one at a time.
757 */
758 if (SD->maintainfs != NULL)
759 SD->maintainfs(SD);
760 }
761 if (store_swap_size > Config.Swap.maxSize) {
762 if (squid_curtime - last_warn_time > 10) {
763 debug(20, 0) ("WARNING: Disk space over limit: %d KB > %d KB\n",
764 store_swap_size, Config.Swap.maxSize);
765 last_warn_time = squid_curtime;
766 }
767 }
768 /* Reregister a maintain event .. */
769 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0, 1);
770 }
771
772
773 /* release an object from a cache */
774 void
775 storeRelease(StoreEntry * e)
776 {
777 debug(20, 3) ("storeRelease: Releasing: '%s'\n", storeKeyText(e->hash.key));
778 /* If, for any reason we can't discard this object because of an
779 * outstanding request, mark it for pending release */
780 if (storeEntryLocked(e)) {
781 storeExpireNow(e);
782 debug(20, 3) ("storeRelease: Only setting RELEASE_REQUEST bit\n");
783 storeReleaseRequest(e);
784 return;
785 }
786 if (store_dirs_rebuilding && e->swap_filen > -1) {
787 storeSetPrivateKey(e);
788 if (e->mem_obj) {
789 storeSetMemStatus(e, NOT_IN_MEMORY);
790 destroy_MemObject(e);
791 }
792 if (e->swap_filen > -1) {
793 /*
794 * Fake a call to storeLockObject(). When rebuilding is done,
795 * we'll just call storeUnlockObject() on these.
796 */
797 e->lock_count++;
798 EBIT_SET(e->flags, RELEASE_REQUEST);
799 stackPush(&LateReleaseStack, e);
800 return;
801 } else {
802 destroy_StoreEntry(e);
803 }
804 }
805 storeLog(STORE_LOG_RELEASE, e);
806 if (e->swap_filen > -1) {
807 storeUnlink(e);
808 if (e->swap_status == SWAPOUT_DONE)
809 if (EBIT_TEST(e->flags, ENTRY_VALIDATED))
810 storeDirUpdateSwapSize(&Config.cacheSwap.swapDirs[e->swap_dirn], e->swap_file_sz, -1);
811 if (!EBIT_TEST(e->flags, KEY_PRIVATE))
812 storeDirSwapLog(e, SWAP_LOG_DEL);
813 #if 0
814 /* From 2.4. I think we do this in storeUnlink? */
815 storeSwapFileNumberSet(e, -1);
816 #endif
817 }
818 storeSetMemStatus(e, NOT_IN_MEMORY);
819 destroy_StoreEntry(e);
820 }
821
822 static void
823 storeLateRelease(void *unused)
824 {
825 StoreEntry *e;
826 int i;
827 static int n = 0;
828 if (store_dirs_rebuilding) {
829 eventAdd("storeLateRelease", storeLateRelease, NULL, 1.0, 1);
830 return;
831 }
832 for (i = 0; i < 10; i++) {
833 e = stackPop(&LateReleaseStack);
834 if (e == NULL) {
835 /* done! */
836 debug(20, 1) ("storeLateRelease: released %d objects\n", n);
837 return;
838 }
839 storeUnlockObject(e);
840 n++;
841 }
842 eventAdd("storeLateRelease", storeLateRelease, NULL, 0.0, 1);
843 }
844
845 /* return 1 if a store entry is locked */
846 int
847 storeEntryLocked(const StoreEntry * e)
848 {
849 if (e->lock_count)
850 return 1;
851 if (e->swap_status == SWAPOUT_WRITING)
852 return 1;
853 if (e->store_status == STORE_PENDING)
854 return 1;
855 /*
856 * SPECIAL, PUBLIC entries should be "locked"
857 */
858 if (EBIT_TEST(e->flags, ENTRY_SPECIAL))
859 if (!EBIT_TEST(e->flags, KEY_PRIVATE))
860 return 1;
861 return 0;
862 }
863
864 static int
865 storeEntryValidLength(const StoreEntry * e)
866 {
867 int diff;
868 const HttpReply *reply;
869 assert(e->mem_obj != NULL);
870 reply = e->mem_obj->reply;
871 debug(20, 3) ("storeEntryValidLength: Checking '%s'\n", storeKeyText(e->hash.key));
872 debug(20, 5) ("storeEntryValidLength: object_len = %d\n",
873 objectLen(e));
874 debug(20, 5) ("storeEntryValidLength: hdr_sz = %d\n",
875 reply->hdr_sz);
876 debug(20, 5) ("storeEntryValidLength: content_length = %d\n",
877 reply->content_length);
878 if (reply->content_length < 0) {
879 debug(20, 5) ("storeEntryValidLength: Unspecified content length: %s\n",
880 storeKeyText(e->hash.key));
881 return 1;
882 }
883 if (reply->hdr_sz == 0) {
884 debug(20, 5) ("storeEntryValidLength: Zero header size: %s\n",
885 storeKeyText(e->hash.key));
886 return 1;
887 }
888 if (e->mem_obj->method == METHOD_HEAD) {
889 debug(20, 5) ("storeEntryValidLength: HEAD request: %s\n",
890 storeKeyText(e->hash.key));
891 return 1;
892 }
893 if (reply->sline.status == HTTP_NOT_MODIFIED)
894 return 1;
895 if (reply->sline.status == HTTP_NO_CONTENT)
896 return 1;
897 diff = reply->hdr_sz + reply->content_length - objectLen(e);
898 if (diff == 0)
899 return 1;
900 debug(20, 3) ("storeEntryValidLength: %d bytes too %s; '%s'\n",
901 diff < 0 ? -diff : diff,
902 diff < 0 ? "big" : "small",
903 storeKeyText(e->hash.key));
904 return 0;
905 }
906
907 static void
908 storeInitHashValues(void)
909 {
910 int i;
911 /* Calculate size of hash table (maximum currently 64k buckets). */
912 i = Config.Swap.maxSize / Config.Store.avgObjectSize;
913 debug(20, 1) ("Swap maxSize %d KB, estimated %d objects\n",
914 Config.Swap.maxSize, i);
915 i /= Config.Store.objectsPerBucket;
916 debug(20, 1) ("Target number of buckets: %d\n", i);
917 /* ideally the full scan period should be configurable, for the
918 * moment it remains at approximately 24 hours. */
919 store_hash_buckets = storeKeyHashBuckets(i);
920 debug(20, 1) ("Using %d Store buckets\n", store_hash_buckets);
921 debug(20, 1) ("Max Mem size: %d KB\n", Config.memMaxSize >> 10);
922 debug(20, 1) ("Max Swap size: %d KB\n", Config.Swap.maxSize);
923 }
924
925 void
926 storeInit(void)
927 {
928 storeKeyInit();
929 storeInitHashValues();
930 store_table = hash_create(storeKeyHashCmp,
931 store_hash_buckets, storeKeyHashHash);
932 mem_policy = createRemovalPolicy(Config.memPolicy);
933 storeDigestInit();
934 storeLogOpen();
935 stackInit(&LateReleaseStack);
936 eventAdd("storeLateRelease", storeLateRelease, NULL, 1.0, 1);
937 storeDirInit();
938 storeRebuildStart();
939 cachemgrRegister("storedir",
940 "Store Directory Stats",
941 storeDirStats, 0, 1);
942 cachemgrRegister("store_check_cachable_stats",
943 "storeCheckCachable() Stats",
944 storeCheckCachableStats, 0, 1);
945 cachemgrRegister("store_io",
946 "Store IO Interface Stats",
947 storeIOStats, 0, 1);
948 }
949
950 void
951 storeConfigure(void)
952 {
953 store_swap_high = (long) (((float) Config.Swap.maxSize *
954 (float) Config.Swap.highWaterMark) / (float) 100);
955 store_swap_low = (long) (((float) Config.Swap.maxSize *
956 (float) Config.Swap.lowWaterMark) / (float) 100);
957 store_pages_max = Config.memMaxSize / SM_PAGE_SIZE;
958 }
959
960 static int
961 storeKeepInMemory(const StoreEntry * e)
962 {
963 MemObject *mem = e->mem_obj;
964 if (mem == NULL)
965 return 0;
966 if (mem->data_hdr.head == NULL)
967 return 0;
968 return mem->inmem_lo == 0;
969 }
970
971 void
972 storeNegativeCache(StoreEntry * e)
973 {
974 e->expires = squid_curtime + Config.negativeTtl;
975 EBIT_SET(e->flags, ENTRY_NEGCACHED);
976 }
977
978 void
979 storeFreeMemory(void)
980 {
981 hashFreeItems(store_table, destroy_StoreEntry);
982 hashFreeMemory(store_table);
983 store_table = NULL;
984 #if USE_CACHE_DIGESTS
985 if (store_digest)
986 cacheDigestDestroy(store_digest);
987 #endif
988 store_digest = NULL;
989 }
990
991 int
992 expiresMoreThan(time_t expires, time_t when)
993 {
994 if (expires < 0) /* No Expires given */
995 return 1;
996 return (expires > (squid_curtime + when));
997 }
998
999 int
1000 storeEntryValidToSend(StoreEntry * e)
1001 {
1002 if (EBIT_TEST(e->flags, RELEASE_REQUEST))
1003 return 0;
1004 if (EBIT_TEST(e->flags, ENTRY_NEGCACHED))
1005 if (e->expires <= squid_curtime)
1006 return 0;
1007 if (EBIT_TEST(e->flags, ENTRY_ABORTED))
1008 return 0;
1009 return 1;
1010 }
1011
1012 void
1013 storeTimestampsSet(StoreEntry * entry)
1014 {
1015 const HttpReply *reply = entry->mem_obj->reply;
1016 time_t served_date = reply->date;
1017 int age = httpHeaderGetInt(&reply->header, HDR_AGE);
1018 /*
1019 * The timestamp calculations below tries to mimic the properties
1020 * of the age calculation in RFC2616 section 13.2.3. The implementaion
1021 * isn't complete, and the most notable exception from the RFC is that
1022 * this does not account for response_delay, but it probably does
1023 * not matter much as this is calculated immediately when the headers
1024 * are received, not when the whole response has been received.
1025 */
1026 /* make sure that 0 <= served_date <= squid_curtime */
1027 if (served_date < 0 || served_date > squid_curtime)
1028 served_date = squid_curtime;
1029 /*
1030 * Compensate with Age header if origin server clock is ahead
1031 * of us and there is a cache in between us and the origin
1032 * server. But DONT compensate if the age value is larger than
1033 * squid_curtime because it results in a negative served_date.
1034 */
1035 if (age > squid_curtime - served_date)
1036 if (squid_curtime < age)
1037 served_date = squid_curtime - age;
1038 entry->expires = reply->expires;
1039 entry->lastmod = reply->last_modified;
1040 entry->timestamp = served_date;
1041 }
1042
1043 void
1044 storeRegisterAbort(StoreEntry * e, STABH * cb, void *data)
1045 {
1046 MemObject *mem = e->mem_obj;
1047 assert(mem);
1048 assert(mem->abort.callback == NULL);
1049 mem->abort.callback = cb;
1050 mem->abort.data = data;
1051 }
1052
1053 void
1054 storeUnregisterAbort(StoreEntry * e)
1055 {
1056 MemObject *mem = e->mem_obj;
1057 assert(mem);
1058 mem->abort.callback = NULL;
1059 }
1060
1061 void
1062 storeMemObjectDump(MemObject * mem)
1063 {
1064 debug(20, 1) ("MemObject->data.head: %p\n",
1065 mem->data_hdr.head);
1066 debug(20, 1) ("MemObject->data.tail: %p\n",
1067 mem->data_hdr.tail);
1068 debug(20, 1) ("MemObject->data.origin_offset: %d\n",
1069 mem->data_hdr.origin_offset);
1070 debug(20, 1) ("MemObject->start_ping: %d.%06d\n",
1071 (int) mem->start_ping.tv_sec,
1072 (int) mem->start_ping.tv_usec);
1073 debug(20, 1) ("MemObject->inmem_hi: %d\n",
1074 (int) mem->inmem_hi);
1075 debug(20, 1) ("MemObject->inmem_lo: %d\n",
1076 (int) mem->inmem_lo);
1077 debug(20, 1) ("MemObject->clients: %p\n",
1078 mem->clients);
1079 debug(20, 1) ("MemObject->nclients: %d\n",
1080 mem->nclients);
1081 debug(20, 1) ("MemObject->reply: %p\n",
1082 mem->reply);
1083 debug(20, 1) ("MemObject->request: %p\n",
1084 mem->request);
1085 debug(20, 1) ("MemObject->log_url: %p %s\n",
1086 mem->log_url,
1087 checkNullString(mem->log_url));
1088 }
1089
1090 void
1091 storeEntryDump(const StoreEntry * e, int l)
1092 {
1093 debug(20, l) ("StoreEntry->key: %s\n", storeKeyText(e->hash.key));
1094 debug(20, l) ("StoreEntry->next: %p\n", e->hash.next);
1095 debug(20, l) ("StoreEntry->mem_obj: %p\n", e->mem_obj);
1096 debug(20, l) ("StoreEntry->timestamp: %d\n", (int) e->timestamp);
1097 debug(20, l) ("StoreEntry->lastref: %d\n", (int) e->lastref);
1098 debug(20, l) ("StoreEntry->expires: %d\n", (int) e->expires);
1099 debug(20, l) ("StoreEntry->lastmod: %d\n", (int) e->lastmod);
1100 debug(20, l) ("StoreEntry->swap_file_sz: %d\n", (int) e->swap_file_sz);
1101 debug(20, l) ("StoreEntry->refcount: %d\n", e->refcount);
1102 debug(20, l) ("StoreEntry->flags: %s\n", storeEntryFlags(e));
1103 debug(20, l) ("StoreEntry->swap_dirn: %d\n", (int) e->swap_dirn);
1104 debug(20, l) ("StoreEntry->swap_filen: %d\n", (int) e->swap_filen);
1105 debug(20, l) ("StoreEntry->lock_count: %d\n", (int) e->lock_count);
1106 debug(20, l) ("StoreEntry->mem_status: %d\n", (int) e->mem_status);
1107 debug(20, l) ("StoreEntry->ping_status: %d\n", (int) e->ping_status);
1108 debug(20, l) ("StoreEntry->store_status: %d\n", (int) e->store_status);
1109 debug(20, l) ("StoreEntry->swap_status: %d\n", (int) e->swap_status);
1110 }
1111
1112 /*
1113 * NOTE, this function assumes only two mem states
1114 */
1115 void
1116 storeSetMemStatus(StoreEntry * e, int new_status)
1117 {
1118 MemObject *mem = e->mem_obj;
1119 if (new_status == e->mem_status)
1120 return;
1121 assert(mem != NULL);
1122 if (new_status == IN_MEMORY) {
1123 assert(mem->inmem_lo == 0);
1124 if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
1125 debug(20, 4) ("storeSetMemStatus: not inserting special %s into policy\n",
1126 mem->url);
1127 } else {
1128 mem_policy->Add(mem_policy, e, &mem->repl);
1129 debug(20, 4) ("storeSetMemStatus: inserted mem node %s\n",
1130 mem->url);
1131 }
1132 hot_obj_count++;
1133 } else {
1134 if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
1135 debug(20, 4) ("storeSetMemStatus: special entry %s\n",
1136 mem->url);
1137 } else {
1138 mem_policy->Remove(mem_policy, e, &mem->repl);
1139 debug(20, 4) ("storeSetMemStatus: removed mem node %s\n",
1140 mem->url);
1141 }
1142 hot_obj_count--;
1143 }
1144 e->mem_status = new_status;
1145 }
1146
1147 const char *
1148 storeUrl(const StoreEntry * e)
1149 {
1150 if (e == NULL)
1151 return "[null_entry]";
1152 else if (e->mem_obj == NULL)
1153 return "[null_mem_obj]";
1154 else
1155 return e->mem_obj->url;
1156 }
1157
1158 void
1159 storeCreateMemObject(StoreEntry * e, const char *url, const char *log_url)
1160 {
1161 if (e->mem_obj)
1162 return;
1163 e->mem_obj = new_MemObject(url, log_url);
1164 }
1165
1166 /* this just sets DELAY_SENDING */
1167 void
1168 storeBuffer(StoreEntry * e)
1169 {
1170 EBIT_SET(e->flags, DELAY_SENDING);
1171 }
1172
1173 /* this just clears DELAY_SENDING and Invokes the handlers */
1174 void
1175 storeBufferFlush(StoreEntry * e)
1176 {
1177 EBIT_CLR(e->flags, DELAY_SENDING);
1178 InvokeHandlers(e);
1179 storeSwapOut(e);
1180 }
1181
1182 int
1183 objectLen(const StoreEntry * e)
1184 {
1185 assert(e->mem_obj != NULL);
1186 return e->mem_obj->object_sz;
1187 }
1188
1189 int
1190 contentLen(const StoreEntry * e)
1191 {
1192 assert(e->mem_obj != NULL);
1193 assert(e->mem_obj->reply != NULL);
1194 return e->mem_obj->object_sz - e->mem_obj->reply->hdr_sz;
1195 }
1196
1197 HttpReply *
1198 storeEntryReply(StoreEntry * e)
1199 {
1200 if (NULL == e)
1201 return NULL;
1202 if (NULL == e->mem_obj)
1203 return NULL;
1204 return e->mem_obj->reply;
1205 }
1206
1207 void
1208 storeEntryReset(StoreEntry * e)
1209 {
1210 MemObject *mem = e->mem_obj;
1211 debug(20, 3) ("storeEntryReset: %s\n", storeUrl(e));
1212 assert(mem->swapout.sio == NULL);
1213 stmemFree(&mem->data_hdr);
1214 mem->inmem_hi = mem->inmem_lo = 0;
1215 httpReplyDestroy(mem->reply);
1216 mem->reply = httpReplyCreate();
1217 e->expires = e->lastmod = e->timestamp = -1;
1218 }
1219
1220 /*
1221 * storeFsInit
1222 *
1223 * This routine calls the SETUP routine for each fs type.
1224 * I don't know where the best place for this is, and I'm not going to shuffle
1225 * around large chunks of code right now (that can be done once its working.)
1226 */
1227 void
1228 storeFsInit(void)
1229 {
1230 storeReplSetup();
1231 storeFsSetup();
1232 }
1233
1234
1235 /*
1236 * similar to above, but is called when a graceful shutdown is to occur
1237 * of each fs module.
1238 */
1239 void
1240 storeFsDone(void)
1241 {
1242 int i = 0;
1243
1244 while (storefs_list[i].typestr != NULL) {
1245 storefs_list[i].donefunc();
1246 i++;
1247 }
1248 }
1249
1250 /*
1251 * called to add another store fs module
1252 */
1253 void
1254 storeFsAdd(char *type, STSETUP * setup)
1255 {
1256 int i;
1257 /* find the number of currently known storefs types */
1258 for (i = 0; storefs_list && storefs_list[i].typestr; i++) {
1259 assert(strcmp(storefs_list[i].typestr, type) != 0);
1260 }
1261 /* add the new type */
1262 storefs_list = xrealloc(storefs_list, (i + 2) * sizeof(storefs_entry_t));
1263 memset(&storefs_list[i + 1], 0, sizeof(storefs_entry_t));
1264 storefs_list[i].typestr = type;
1265 /* Call the FS to set up capabilities and initialize the FS driver */
1266 setup(&storefs_list[i]);
1267 }
1268
1269 /*
1270 * called to add another store removal policy module
1271 */
1272 void
1273 storeReplAdd(char *type, REMOVALPOLICYCREATE * create)
1274 {
1275 int i;
1276 /* find the number of currently known repl types */
1277 for (i = 0; storerepl_list && storerepl_list[i].typestr; i++) {
1278 assert(strcmp(storerepl_list[i].typestr, type) != 0);
1279 }
1280 /* add the new type */
1281 storerepl_list = xrealloc(storerepl_list, (i + 2) * sizeof(storerepl_entry_t));
1282 memset(&storerepl_list[i + 1], 0, sizeof(storerepl_entry_t));
1283 storerepl_list[i].typestr = type;
1284 storerepl_list[i].create = create;
1285 }
1286
1287 /*
1288 * Create a removal policy instance
1289 */
1290 RemovalPolicy *
1291 createRemovalPolicy(RemovalPolicySettings * settings)
1292 {
1293 storerepl_entry_t *r;
1294 for (r = storerepl_list; r && r->typestr; r++) {
1295 if (strcmp(r->typestr, settings->type) == 0)
1296 return r->create(settings->args);
1297 }
1298 debug(20, 1) ("ERROR: Unknown policy %s\n", settings->type);
1299 debug(20, 1) ("ERROR: Be sure to have set cache_replacement_policy\n");
1300 debug(20, 1) ("ERROR: and memory_replacement_policy in squid.conf!\n");
1301 fatalf("ERROR: Unknown policy %s\n", settings->type);
1302 return NULL; /* NOTREACHED */
1303 }
1304
1305 #if 0
1306 void
1307 storeSwapFileNumberSet(StoreEntry * e, sfileno filn)
1308 {
1309 if (e->swap_file_number == filn)
1310 return;
1311 if (filn < 0) {
1312 assert(-1 == filn);
1313 storeDirMapBitReset(e->swap_file_number);
1314 storeDirLRUDelete(e);
1315 e->swap_file_number = -1;
1316 } else {
1317 assert(-1 == e->swap_file_number);
1318 storeDirMapBitSet(e->swap_file_number = filn);
1319 storeDirLRUAdd(e);
1320 }
1321 }
1322 #endif