]>
Commit | Line | Data |
---|---|---|
ff5731b1 | 1 | |
30a4f2a8 | 2 | /* |
6a54c60e | 3 | * $Id: store.cc,v 1.298 1997/10/20 22:59:48 wessels Exp $ |
30a4f2a8 | 4 | * |
5 | * DEBUG: section 20 Storeage Manager | |
6 | * AUTHOR: Harvest Derived | |
7 | * | |
42c04c16 | 8 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ |
30a4f2a8 | 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 | |
14 | * the National Science Foundation. | |
15 | * | |
16 | * This program is free software; you can redistribute it and/or modify | |
17 | * it under the terms of the GNU General Public License as published by | |
18 | * the Free Software Foundation; either version 2 of the License, or | |
19 | * (at your option) any later version. | |
20 | * | |
21 | * This program is distributed in the hope that it will be useful, | |
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
24 | * GNU General Public License for more details. | |
25 | * | |
26 | * You should have received a copy of the GNU General Public License | |
27 | * along with this program; if not, write to the Free Software | |
28 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
29 | * | |
30 | */ | |
c943f331 | 31 | |
32 | /* | |
30a4f2a8 | 33 | * Copyright (c) 1994, 1995. All rights reserved. |
34 | * | |
35 | * The Harvest software was developed by the Internet Research Task | |
36 | * Force Research Group on Resource Discovery (IRTF-RD): | |
37 | * | |
38 | * Mic Bowman of Transarc Corporation. | |
39 | * Peter Danzig of the University of Southern California. | |
40 | * Darren R. Hardy of the University of Colorado at Boulder. | |
41 | * Udi Manber of the University of Arizona. | |
42 | * Michael F. Schwartz of the University of Colorado at Boulder. | |
43 | * Duane Wessels of the University of Colorado at Boulder. | |
44 | * | |
45 | * This copyright notice applies to software in the Harvest | |
46 | * ``src/'' directory only. Users should consult the individual | |
47 | * copyright notices in the ``components/'' subdirectories for | |
48 | * copyright information about other software bundled with the | |
49 | * Harvest source code distribution. | |
50 | * | |
51 | * TERMS OF USE | |
52 | * | |
53 | * The Harvest software may be used and re-distributed without | |
54 | * charge, provided that the software origin and research team are | |
55 | * cited in any use of the system. Most commonly this is | |
56 | * accomplished by including a link to the Harvest Home Page | |
57 | * (http://harvest.cs.colorado.edu/) from the query page of any | |
58 | * Broker you deploy, as well as in the query result pages. These | |
59 | * links are generated automatically by the standard Broker | |
60 | * software distribution. | |
61 | * | |
62 | * The Harvest software is provided ``as is'', without express or | |
63 | * implied warranty, and with no support nor obligation to assist | |
64 | * in its use, correction, modification or enhancement. We assume | |
65 | * no liability with respect to the infringement of copyrights, | |
66 | * trade secrets, or any patents, and are not responsible for | |
67 | * consequential damages. Proper use of the Harvest software is | |
68 | * entirely the responsibility of the user. | |
69 | * | |
70 | * DERIVATIVE WORKS | |
71 | * | |
72 | * Users may make derivative works from the Harvest software, subject | |
73 | * to the following constraints: | |
74 | * | |
75 | * - You must include the above copyright notice and these | |
76 | * accompanying paragraphs in all forms of derivative works, | |
77 | * and any documentation and other materials related to such | |
78 | * distribution and use acknowledge that the software was | |
79 | * developed at the above institutions. | |
80 | * | |
81 | * - You must notify IRTF-RD regarding your distribution of | |
82 | * the derivative work. | |
83 | * | |
84 | * - You must clearly notify users that your are distributing | |
85 | * a modified version and not the original Harvest software. | |
86 | * | |
87 | * - Any derivative product is also subject to these copyright | |
88 | * and use restrictions. | |
89 | * | |
90 | * Note that the Harvest software is NOT in the public domain. We | |
91 | * retain copyright, as specified above. | |
92 | * | |
93 | * HISTORY OF FREE SOFTWARE STATUS | |
94 | * | |
95 | * Originally we required sites to license the software in cases | |
96 | * where they were going to build commercial products/services | |
97 | * around Harvest. In June 1995 we changed this policy. We now | |
98 | * allow people to use the core Harvest software (the code found in | |
99 | * the Harvest ``src/'' directory) for free. We made this change | |
100 | * in the interest of encouraging the widest possible deployment of | |
101 | * the technology. The Harvest software is really a reference | |
102 | * implementation of a set of protocols and formats, some of which | |
103 | * we intend to standardize. We encourage commercial | |
104 | * re-implementations of code complying to this set of standards. | |
c943f331 | 105 | */ |
090089c4 | 106 | |
44a47c6e | 107 | #include "squid.h" /* goes first */ |
090089c4 | 108 | |
109 | #define REBUILD_TIMESTAMP_DELTA_MAX 2 | |
090089c4 | 110 | #define SWAP_BUF DISK_PAGE_SIZE |
8350fe9b | 111 | #define VM_WINDOW_SZ DISK_PAGE_SIZE |
090089c4 | 112 | |
227fbb74 | 113 | #define WITH_MEMOBJ 1 |
114 | #define WITHOUT_MEMOBJ 0 | |
115 | ||
fcf5283d | 116 | #define STORE_IN_MEM_BUCKETS (229) |
090089c4 | 117 | |
2285407f | 118 | #define STORE_LOG_CREATE 0 |
119 | #define STORE_LOG_SWAPIN 1 | |
120 | #define STORE_LOG_SWAPOUT 2 | |
121 | #define STORE_LOG_RELEASE 3 | |
122 | ||
8350fe9b | 123 | #define ENTRY_INMEM_SIZE(X) ((X)->inmem_hi - (X)->inmem_lo) |
38792624 | 124 | |
147d3115 | 125 | static char *storeLogTags[] = |
126 | { | |
127 | "CREATE", | |
128 | "SWAPIN", | |
129 | "SWAPOUT", | |
130 | "RELEASE" | |
2285407f | 131 | }; |
132 | ||
0ee4272b | 133 | const char *memStatusStr[] = |
e62d2dea | 134 | { |
9dfb6c1c | 135 | "NOT_IN_MEMORY", |
9dfb6c1c | 136 | "IN_MEMORY" |
137 | }; | |
138 | ||
0ee4272b | 139 | const char *pingStatusStr[] = |
e62d2dea | 140 | { |
f17936ab | 141 | "PING_NONE", |
9dfb6c1c | 142 | "PING_WAITING", |
143 | "PING_TIMEOUT", | |
f17936ab | 144 | "PING_DONE" |
9dfb6c1c | 145 | }; |
146 | ||
0ee4272b | 147 | const char *storeStatusStr[] = |
e62d2dea | 148 | { |
9dfb6c1c | 149 | "STORE_OK", |
150 | "STORE_PENDING", | |
151 | "STORE_ABORTED" | |
152 | }; | |
153 | ||
0ee4272b | 154 | const char *swapStatusStr[] = |
e62d2dea | 155 | { |
8350fe9b | 156 | "SWAPOUT_NONE", |
157 | "SWAPOUT_OPENING", | |
158 | "SWAPOUT_WRITING", | |
159 | "SWAPOUT_DONE" | |
9dfb6c1c | 160 | }; |
161 | ||
5608850b | 162 | struct storeRebuildState { |
163 | struct _rebuild_dir { | |
164 | int dirn; | |
3e347513 | 165 | FILE *log; |
5608850b | 166 | int speed; |
167 | int clean; | |
168 | struct _rebuild_dir *next; | |
3e347513 | 169 | } *rebuild_dir; |
3facc31e | 170 | int objcount; /* # objects successfully reloaded */ |
171 | int expcount; /* # objects expired */ | |
172 | int linecount; /* # lines parsed from cache logfile */ | |
173 | int clashcount; /* # swapfile clashes avoided */ | |
174 | int dupcount; /* # duplicates purged */ | |
994bbf93 | 175 | int invalid; /* # bad lines */ |
a47b9029 | 176 | int badflags; /* # bad e->flags */ |
f182d1c5 | 177 | int need_to_validate; |
5608850b | 178 | time_t start; |
179 | time_t stop; | |
180 | char *line_in; | |
181 | size_t line_in_sz; | |
3facc31e | 182 | }; |
183 | ||
d3c12084 | 184 | struct _bucketOrder { |
0c77d853 | 185 | unsigned int bucket; |
186 | int index; | |
d3c12084 | 187 | }; |
188 | ||
0a0bf5db | 189 | typedef struct storeCleanList { |
190 | char *key; | |
191 | struct storeCleanList *next; | |
192 | } storeCleanList; | |
193 | ||
35ac11a3 | 194 | typedef void (VCB) _PARAMS((void *)); |
0a0bf5db | 195 | |
196 | typedef struct valid_ctrl_t { | |
197 | struct stat *sb; | |
198 | StoreEntry *e; | |
d7b78fbb | 199 | VCB *callback; |
0a0bf5db | 200 | void *callback_data; |
201 | } valid_ctrl_t; | |
202 | ||
203 | typedef struct swapin_ctrl_t { | |
204 | StoreEntry *e; | |
205 | char *path; | |
582b6456 | 206 | SIH *callback; |
0a0bf5db | 207 | void *callback_data; |
8350fe9b | 208 | struct _store_client *sc; |
0a0bf5db | 209 | } swapin_ctrl_t; |
210 | ||
211 | typedef struct lock_ctrl_t { | |
582b6456 | 212 | SIH *callback; |
0a0bf5db | 213 | void *callback_data; |
214 | StoreEntry *e; | |
215 | } lock_ctrl_t; | |
216 | ||
217 | typedef struct swapout_ctrl_t { | |
218 | char *swapfilename; | |
219 | int oldswapstatus; | |
220 | StoreEntry *e; | |
0a0bf5db | 221 | } swapout_ctrl_t; |
222 | ||
8350fe9b | 223 | struct _mem_entry { |
224 | StoreEntry *e; | |
225 | struct _mem_entry *next; | |
226 | struct _mem_entry *prev; | |
227 | }; | |
228 | ||
30a4f2a8 | 229 | /* Static Functions */ |
76dd1215 | 230 | static void storeCreateHashTable _PARAMS((int (*)_PARAMS((const char *, const char *)))); |
24382924 | 231 | static int compareLastRef _PARAMS((StoreEntry **, StoreEntry **)); |
429fdbec | 232 | static int compareBucketOrder _PARAMS((struct _bucketOrder *, struct _bucketOrder *)); |
233 | static int storeCheckExpired _PARAMS((const StoreEntry *, int flag)); | |
8350fe9b | 234 | static struct _store_client *storeClientListSearch _PARAMS((const MemObject *, void *)); |
235 | static int storeCopy _PARAMS((const StoreEntry *, off_t, size_t, char *, size_t *)); | |
0ee4272b | 236 | static int storeEntryLocked _PARAMS((const StoreEntry *)); |
237 | static int storeEntryValidLength _PARAMS((const StoreEntry *)); | |
38792624 | 238 | static void storeGetMemSpace _PARAMS((int)); |
24382924 | 239 | static int storeHashDelete _PARAMS((StoreEntry *)); |
d7b78fbb | 240 | static VCB storeSwapInValidateComplete; |
f1dc9b30 | 241 | static mem_hdr *new_MemObjectData _PARAMS((void)); |
88738790 | 242 | static MemObject *new_MemObject _PARAMS((const char *)); |
243 | static StoreEntry *new_StoreEntry _PARAMS((int, const char *)); | |
0a0bf5db | 244 | static StoreEntry *storeAddDiskRestore _PARAMS((const char *, |
245 | int, | |
246 | int, | |
247 | time_t, | |
248 | time_t, | |
311ea387 | 249 | time_t, |
919877b6 | 250 | time_t, |
c54e9052 | 251 | u_num32, |
252 | u_num32, | |
311ea387 | 253 | int)); |
24382924 | 254 | static unsigned int storeGetBucketNum _PARAMS((void)); |
67508012 | 255 | static void destroy_MemObject _PARAMS((MemObject *)); |
256 | static void destroy_MemObjectData _PARAMS((MemObject *)); | |
257 | static void destroy_StoreEntry _PARAMS((StoreEntry *)); | |
24382924 | 258 | static void storePurgeMem _PARAMS((StoreEntry *)); |
24382924 | 259 | static void storeStartRebuildFromDisk _PARAMS((void)); |
0a0bf5db | 260 | static void storeSwapOutStart _PARAMS((StoreEntry * e)); |
d89d1fb6 | 261 | static DWCB storeSwapOutHandle; |
fe54d06d | 262 | static void storeSetPrivateKey _PARAMS((StoreEntry *)); |
d7b78fbb | 263 | static EVH storeDoRebuildFromDisk; |
264 | static EVH storeCleanup; | |
265 | static VCB storeCleanupComplete; | |
266 | static void storeValidate _PARAMS((StoreEntry *, VCB *, void *)); | |
267 | static AIOCB storeValidateComplete; | |
5608850b | 268 | static void storeRebuiltFromDisk _PARAMS((struct storeRebuildState * data)); |
2cc3f720 | 269 | static unsigned int getKeyCounter _PARAMS((void)); |
429fdbec | 270 | static void storePutUnusedFileno _PARAMS((int fileno)); |
271 | static int storeGetUnusedFileno _PARAMS((void)); | |
30a4f2a8 | 272 | |
8350fe9b | 273 | static void storeCheckSwapOut(StoreEntry * e); |
274 | static void storeSwapoutFileOpened(void *data, int fd); | |
275 | static int storeCheckCachable(StoreEntry * e); | |
276 | static int storeKeepInMemory(const StoreEntry *); | |
277 | static SIH storeClientCopyFileOpened; | |
278 | static DRCB storeClientCopyHandleRead; | |
279 | static FOCB storeSwapInFileOpened; | |
280 | static void storeClientCopyFileRead(struct _store_client *sc); | |
281 | static void storeInMemAdd(StoreEntry * e); | |
282 | static void storeInMemDelete(StoreEntry * e); | |
283 | static void storeSetMemStatus(StoreEntry * e, int); | |
284 | ||
090089c4 | 285 | /* Now, this table is inaccessible to outsider. They have to use a method |
286 | * to access a value in internal storage data structure. */ | |
35ac11a3 | 287 | static hash_table *store_table = NULL; |
8350fe9b | 288 | static struct _mem_entry *inmem_head = NULL; |
289 | static struct _mem_entry *inmem_tail = NULL; | |
090089c4 | 290 | |
13c72789 | 291 | static int store_pages_max = 0; |
e954773d | 292 | static int store_pages_high = 0; |
293 | static int store_pages_low = 0; | |
090089c4 | 294 | |
090089c4 | 295 | /* current file name, swap file, use number as a filename */ |
58104eab | 296 | static int store_swap_high = 0; |
297 | static int store_swap_low = 0; | |
2285407f | 298 | static int storelog_fd = -1; |
090089c4 | 299 | |
090089c4 | 300 | /* key temp buffer */ |
ec74cf5f | 301 | static char key_temp_buffer[MAX_URL + 100]; |
090089c4 | 302 | |
66cedb85 | 303 | /* expiration parameters and stats */ |
304 | static int store_buckets; | |
e924600d | 305 | static int store_maintain_rate; |
66cedb85 | 306 | static int store_maintain_buckets; |
e924600d | 307 | static int scan_revolutions; |
d3c12084 | 308 | static struct _bucketOrder *MaintBucketsOrder = NULL; |
66cedb85 | 309 | |
b8d8561b | 310 | static MemObject * |
88738790 | 311 | new_MemObject(const char *log_url) |
227fbb74 | 312 | { |
30a4f2a8 | 313 | MemObject *mem = get_free_mem_obj(); |
314 | mem->reply = xcalloc(1, sizeof(struct _http_reply)); | |
33b589ff | 315 | mem->reply->date = -2; |
316 | mem->reply->expires = -2; | |
317 | mem->reply->last_modified = -2; | |
88738790 | 318 | mem->log_url = xstrdup(log_url); |
8350fe9b | 319 | mem->swapout.fd = -1; |
579a45cd | 320 | meta_data.mem_obj_count++; |
30a4f2a8 | 321 | meta_data.misc += sizeof(struct _http_reply); |
88738790 | 322 | meta_data.url_strings += strlen(log_url); |
a3d5953d | 323 | debug(20, 3) ("new_MemObject: returning %p\n", mem); |
30a4f2a8 | 324 | return mem; |
227fbb74 | 325 | } |
326 | ||
b8d8561b | 327 | static StoreEntry * |
88738790 | 328 | new_StoreEntry(int mem_obj_flag, const char *log_url) |
090089c4 | 329 | { |
330 | StoreEntry *e = NULL; | |
331 | ||
30a4f2a8 | 332 | e = xcalloc(1, sizeof(StoreEntry)); |
227fbb74 | 333 | meta_data.store_entries++; |
334 | if (mem_obj_flag) | |
88738790 | 335 | e->mem_obj = new_MemObject(log_url); |
a3d5953d | 336 | debug(20, 3) ("new_StoreEntry: returning %p\n", e); |
227fbb74 | 337 | return e; |
090089c4 | 338 | } |
339 | ||
b8d8561b | 340 | static void |
341 | destroy_MemObject(MemObject * mem) | |
090089c4 | 342 | { |
a3d5953d | 343 | debug(20, 3) ("destroy_MemObject: destroying %p\n", mem); |
645afd3f | 344 | assert(mem->swapout.fd == -1); |
30a4f2a8 | 345 | destroy_MemObjectData(mem); |
88738790 | 346 | meta_data.url_strings -= strlen(mem->log_url); |
52d4522b | 347 | safe_free(mem->clients); |
30a4f2a8 | 348 | safe_free(mem->reply); |
88738790 | 349 | safe_free(mem->log_url); |
30a4f2a8 | 350 | requestUnlink(mem->request); |
351 | mem->request = NULL; | |
352 | put_free_mem_obj(mem); | |
579a45cd | 353 | meta_data.mem_obj_count--; |
30a4f2a8 | 354 | meta_data.misc -= sizeof(struct _http_reply); |
090089c4 | 355 | } |
356 | ||
b8d8561b | 357 | static void |
358 | destroy_StoreEntry(StoreEntry * e) | |
090089c4 | 359 | { |
a3d5953d | 360 | debug(20, 3) ("destroy_StoreEntry: destroying %p\n", e); |
9e975e4e | 361 | assert(e != NULL); |
227fbb74 | 362 | if (e->mem_obj) |
363 | destroy_MemObject(e->mem_obj); | |
30a4f2a8 | 364 | if (e->url) { |
365 | meta_data.url_strings -= strlen(e->url); | |
366 | safe_free(e->url); | |
367 | } else { | |
a3d5953d | 368 | debug(20, 3) ("destroy_StoreEntry: WARNING: Entry without URL string!\n"); |
30a4f2a8 | 369 | } |
370 | if (BIT_TEST(e->flag, KEY_URL)) | |
371 | e->key = NULL; | |
372 | else | |
373 | safe_free(e->key); | |
227fbb74 | 374 | xfree(e); |
375 | meta_data.store_entries--; | |
090089c4 | 376 | } |
377 | ||
f1dc9b30 | 378 | static mem_hdr * |
b8d8561b | 379 | new_MemObjectData(void) |
227fbb74 | 380 | { |
a3d5953d | 381 | debug(20, 3) ("new_MemObjectData: calling memInit()\n"); |
579a45cd | 382 | meta_data.mem_data_count++; |
227fbb74 | 383 | return memInit(); |
384 | } | |
090089c4 | 385 | |
b8d8561b | 386 | static void |
387 | destroy_MemObjectData(MemObject * mem) | |
090089c4 | 388 | { |
a3d5953d | 389 | debug(20, 3) ("destroy_MemObjectData: destroying %p, %d bytes\n", |
8350fe9b | 390 | mem->data, mem->inmem_hi); |
38792624 | 391 | store_mem_size -= ENTRY_INMEM_SIZE(mem); |
30a4f2a8 | 392 | if (mem->data) { |
d7b78fbb | 393 | memFree(mem->data); |
30a4f2a8 | 394 | mem->data = NULL; |
579a45cd | 395 | meta_data.mem_data_count--; |
090089c4 | 396 | } |
8350fe9b | 397 | mem->inmem_hi = 0; |
090089c4 | 398 | } |
399 | ||
090089c4 | 400 | /* ----- INTERFACE BETWEEN STORAGE MANAGER AND HASH TABLE FUNCTIONS --------- */ |
401 | ||
76dd1215 | 402 | static void |
35ac11a3 | 403 | storeCreateHashTable(HASHCMP * cmp_func) |
090089c4 | 404 | { |
66cedb85 | 405 | store_table = hash_create(cmp_func, store_buckets, hash4); |
b32508fb | 406 | } |
090089c4 | 407 | |
b8d8561b | 408 | static int |
409 | storeHashInsert(StoreEntry * e) | |
090089c4 | 410 | { |
a3d5953d | 411 | debug(20, 3) ("storeHashInsert: Inserting Entry %p key '%s'\n", |
227fbb74 | 412 | e, e->key); |
b32508fb | 413 | return hash_join(store_table, (hash_link *) e); |
090089c4 | 414 | } |
415 | ||
b8d8561b | 416 | static int |
417 | storeHashDelete(StoreEntry * e) | |
090089c4 | 418 | { |
b32508fb | 419 | return hash_remove_link(store_table, (hash_link *) e); |
090089c4 | 420 | } |
421 | ||
090089c4 | 422 | /* -------------------------------------------------------------------------- */ |
423 | ||
b8d8561b | 424 | static void |
fe4e214f | 425 | storeLog(int tag, const StoreEntry * e) |
2285407f | 426 | { |
0be3ec60 | 427 | LOCAL_ARRAY(char, logmsg, MAX_URL << 1); |
ca98227c | 428 | MemObject *mem = e->mem_obj; |
429 | struct _http_reply *reply; | |
2285407f | 430 | if (storelog_fd < 0) |
431 | return; | |
ca98227c | 432 | if (mem == NULL) |
433 | return; | |
88738790 | 434 | if (mem->log_url == NULL) { |
435 | debug(20, 1) ("storeLog: NULL log_url for %s\n", e->url); | |
436 | storeMemObjectDump(mem); | |
437 | mem->log_url = xstrdup(e->url); | |
438 | } | |
ca98227c | 439 | reply = mem->reply; |
8350fe9b | 440 | snprintf(logmsg, MAX_URL << 1, "%9d.%03d %-7s %08X %4d %9d %9d %9d %s %d/%d %s %s\n", |
2285407f | 441 | (int) current_time.tv_sec, |
442 | (int) current_time.tv_usec / 1000, | |
443 | storeLogTags[tag], | |
2abe4822 | 444 | e->swap_file_number, |
ca98227c | 445 | reply->code, |
446 | (int) reply->date, | |
447 | (int) reply->last_modified, | |
448 | (int) reply->expires, | |
449 | reply->content_type[0] ? reply->content_type : "unknown", | |
450 | reply->content_length, | |
711982d8 | 451 | (int) (mem->inmem_hi - mem->reply->hdr_sz), |
ca98227c | 452 | RequestMethodStr[e->method], |
88738790 | 453 | mem->log_url); |
2285407f | 454 | file_write(storelog_fd, |
455 | xstrdup(logmsg), | |
456 | strlen(logmsg), | |
2285407f | 457 | NULL, |
e47afcdb | 458 | NULL, |
459 | xfree); | |
2285407f | 460 | } |
461 | ||
090089c4 | 462 | |
463 | /* get rid of memory copy of the object */ | |
620da955 | 464 | /* Only call this if storeCheckPurgeMem(e) returns 1 */ |
24382924 | 465 | static void |
b8d8561b | 466 | storePurgeMem(StoreEntry * e) |
090089c4 | 467 | { |
2aca8433 | 468 | if (e->mem_obj == NULL) |
090089c4 | 469 | return; |
8350fe9b | 470 | debug(20, 3) ("storePurgeMem: Freeing memory-copy of %s\n", e->key); |
090089c4 | 471 | storeSetMemStatus(e, NOT_IN_MEMORY); |
227fbb74 | 472 | destroy_MemObject(e->mem_obj); |
473 | e->mem_obj = NULL; | |
090089c4 | 474 | } |
475 | ||
0a0bf5db | 476 | void |
95c4b18f | 477 | storeLockObject(StoreEntry * e) |
090089c4 | 478 | { |
090089c4 | 479 | e->lock_count++; |
a3d5953d | 480 | debug(20, 3) ("storeLockObject: key '%s' count=%d\n", |
148a2921 | 481 | e->key, (int) e->lock_count); |
b8de7ebe | 482 | e->lastref = squid_curtime; |
090089c4 | 483 | } |
484 | ||
b8d8561b | 485 | void |
486 | storeReleaseRequest(StoreEntry * e) | |
2285407f | 487 | { |
aadcfbad | 488 | if (BIT_TEST(e->flag, RELEASE_REQUEST)) |
58bcd31b | 489 | return; |
9e4ad609 | 490 | if (!storeEntryLocked(e)) |
491 | fatal_dump("storeReleaseRequest: unlocked entry"); | |
86101e40 | 492 | debug(20, 3) ("storeReleaseRequest: '%s'\n", |
493 | e->key ? e->key : e->url); | |
9e4ad609 | 494 | BIT_SET(e->flag, RELEASE_REQUEST); |
fe54d06d | 495 | storeSetPrivateKey(e); |
2285407f | 496 | } |
497 | ||
090089c4 | 498 | /* unlock object, return -1 if object get released after unlock |
499 | * otherwise lock_count */ | |
b8d8561b | 500 | int |
501 | storeUnlockObject(StoreEntry * e) | |
090089c4 | 502 | { |
6c895381 | 503 | e->lock_count--; |
a3d5953d | 504 | debug(20, 3) ("storeUnlockObject: key '%s' count=%d\n", |
6c895381 | 505 | e->key, e->lock_count); |
30a4f2a8 | 506 | if (e->lock_count) |
a1e47288 | 507 | return (int) e->lock_count; |
5b00be7a | 508 | if (e->store_status == STORE_PENDING) { |
f182d1c5 | 509 | assert(!BIT_TEST(e->flag, ENTRY_DISPATCHED)); |
510 | BIT_SET(e->flag, RELEASE_REQUEST); | |
5b00be7a | 511 | } |
9e975e4e | 512 | assert(storePendingNClients(e) == 0); |
8350fe9b | 513 | if (BIT_TEST(e->flag, RELEASE_REQUEST)) |
30a4f2a8 | 514 | storeRelease(e); |
8350fe9b | 515 | else if (storeKeepInMemory(e)) { |
516 | storeSetMemStatus(e, IN_MEMORY); | |
517 | requestUnlink(e->mem_obj->request); | |
518 | e->mem_obj->request = NULL; | |
519 | } else | |
30a4f2a8 | 520 | storePurgeMem(e); |
6c895381 | 521 | return 0; |
090089c4 | 522 | } |
523 | ||
524 | /* Lookup an object in the cache. | |
525 | * return just a reference to object, don't start swapping in yet. */ | |
b8d8561b | 526 | StoreEntry * |
0ee4272b | 527 | storeGet(const char *url) |
090089c4 | 528 | { |
a3d5953d | 529 | debug(20, 3) ("storeGet: looking up %s\n", url); |
b15fe823 | 530 | return (StoreEntry *) hash_lookup(store_table, url); |
090089c4 | 531 | } |
532 | ||
88738790 | 533 | static unsigned int |
b8d8561b | 534 | getKeyCounter(void) |
04e8dbaa | 535 | { |
2d25dc1a | 536 | static unsigned int key_counter = 0; |
7cb386d0 | 537 | if (++key_counter == (1 << 24)) |
2cc3f720 | 538 | key_counter = 1; |
2d25dc1a | 539 | return key_counter; |
04e8dbaa | 540 | } |
541 | ||
2cc3f720 | 542 | unsigned int |
543 | storeReqnum(StoreEntry * entry, method_t method) | |
544 | { | |
69951dbc | 545 | unsigned int k; |
2cc3f720 | 546 | if (BIT_TEST(entry->flag, KEY_PRIVATE)) |
69951dbc | 547 | k = atoi(entry->key); |
548 | else | |
549 | k = getKeyCounter(); | |
2cc3f720 | 550 | if (method == METHOD_GET) |
69951dbc | 551 | return k; |
552 | return (method << 24) | k; | |
2cc3f720 | 553 | } |
554 | ||
0ee4272b | 555 | const char * |
556 | storeGeneratePrivateKey(const char *url, method_t method, int num) | |
6eb42cae | 557 | { |
6eb42cae | 558 | if (num == 0) |
04e8dbaa | 559 | num = getKeyCounter(); |
2cc3f720 | 560 | else if (num & 0xFF000000) { |
561 | method = (method_t) (num >> 24); | |
562 | num &= 0x00FFFFFF; | |
563 | } | |
a3d5953d | 564 | debug(20, 3) ("storeGeneratePrivateKey: '%s'\n", url); |
6eb42cae | 565 | key_temp_buffer[0] = '\0'; |
8350fe9b | 566 | snprintf(key_temp_buffer, MAX_URL + 100, "%d/%s/%s", |
6eb42cae | 567 | num, |
73ddce89 | 568 | RequestMethodStr[method], |
6eb42cae | 569 | url); |
570 | return key_temp_buffer; | |
571 | } | |
572 | ||
0ee4272b | 573 | const char * |
574 | storeGeneratePublicKey(const char *url, method_t method) | |
090089c4 | 575 | { |
a3d5953d | 576 | debug(20, 3) ("storeGeneratePublicKey: type=%d %s\n", method, url); |
73ddce89 | 577 | switch (method) { |
6eb42cae | 578 | case METHOD_GET: |
227fbb74 | 579 | return url; |
983061ed | 580 | /* NOTREACHED */ |
227fbb74 | 581 | break; |
6eb42cae | 582 | case METHOD_POST: |
30a4f2a8 | 583 | case METHOD_PUT: |
6eb42cae | 584 | case METHOD_HEAD: |
3facc31e | 585 | case METHOD_CONNECT: |
b3b64e58 | 586 | case METHOD_TRACE: |
8350fe9b | 587 | snprintf(key_temp_buffer, MAX_URL + 100, "/%s/%s", RequestMethodStr[method], url); |
b3b64e58 | 588 | return key_temp_buffer; |
589 | /* NOTREACHED */ | |
590 | break; | |
227fbb74 | 591 | default: |
9e975e4e | 592 | fatal_dump("storeGeneratePublicKey: Unsupported request method"); |
227fbb74 | 593 | break; |
594 | } | |
595 | return NULL; | |
596 | } | |
597 | ||
fe54d06d | 598 | static void |
b8d8561b | 599 | storeSetPrivateKey(StoreEntry * e) |
227fbb74 | 600 | { |
0ee4272b | 601 | const char *newkey = NULL; |
6eb42cae | 602 | if (e->key && BIT_TEST(e->flag, KEY_PRIVATE)) |
603 | return; /* is already private */ | |
73ddce89 | 604 | newkey = storeGeneratePrivateKey(e->url, e->method, 0); |
9e975e4e | 605 | assert(hash_lookup(store_table, newkey) == NULL); |
07622ccd | 606 | if (e->key) |
edae7bc0 | 607 | storeHashDelete(e); |
07622ccd | 608 | if (e->key && !BIT_TEST(e->flag, KEY_URL)) |
609 | safe_free(e->key); | |
610 | e->key = xstrdup(newkey); | |
6eb42cae | 611 | storeHashInsert(e); |
227fbb74 | 612 | BIT_RESET(e->flag, KEY_URL); |
6eb42cae | 613 | BIT_SET(e->flag, KEY_CHANGE); |
614 | BIT_SET(e->flag, KEY_PRIVATE); | |
227fbb74 | 615 | } |
616 | ||
b8d8561b | 617 | void |
618 | storeSetPublicKey(StoreEntry * e) | |
227fbb74 | 619 | { |
6eb42cae | 620 | StoreEntry *e2 = NULL; |
621 | hash_link *table_entry = NULL; | |
0ee4272b | 622 | const char *newkey = NULL; |
234967c9 | 623 | int loop_detect = 0; |
6eb42cae | 624 | if (e->key && !BIT_TEST(e->flag, KEY_PRIVATE)) |
625 | return; /* is already public */ | |
73ddce89 | 626 | newkey = storeGeneratePublicKey(e->url, e->method); |
ec74cf5f | 627 | while ((table_entry = hash_lookup(store_table, newkey))) { |
89e079ed | 628 | debug(20, 3) ("storeSetPublicKey: Making old '%s' private.\n", newkey); |
07622ccd | 629 | e2 = (StoreEntry *) table_entry; |
630 | storeSetPrivateKey(e2); | |
30a4f2a8 | 631 | storeRelease(e2); |
9e975e4e | 632 | assert(++loop_detect < 10); |
234967c9 | 633 | newkey = storeGeneratePublicKey(e->url, e->method); |
6eb42cae | 634 | } |
07622ccd | 635 | if (e->key) |
edae7bc0 | 636 | storeHashDelete(e); |
07622ccd | 637 | if (e->key && !BIT_TEST(e->flag, KEY_URL)) |
638 | safe_free(e->key); | |
73ddce89 | 639 | if (e->method == METHOD_GET) { |
6eb42cae | 640 | e->key = e->url; |
641 | BIT_SET(e->flag, KEY_URL); | |
642 | BIT_RESET(e->flag, KEY_CHANGE); | |
643 | } else { | |
07622ccd | 644 | e->key = xstrdup(newkey); |
6eb42cae | 645 | BIT_RESET(e->flag, KEY_URL); |
646 | BIT_SET(e->flag, KEY_CHANGE); | |
227fbb74 | 647 | } |
07622ccd | 648 | BIT_RESET(e->flag, KEY_PRIVATE); |
227fbb74 | 649 | storeHashInsert(e); |
650 | } | |
651 | ||
b8d8561b | 652 | StoreEntry * |
88738790 | 653 | storeCreateEntry(const char *url, const char *log_url, int flags, method_t method) |
090089c4 | 654 | { |
090089c4 | 655 | StoreEntry *e = NULL; |
30a4f2a8 | 656 | MemObject *mem = NULL; |
a3d5953d | 657 | debug(20, 3) ("storeCreateEntry: '%s' icp flags=%x\n", url, flags); |
090089c4 | 658 | |
88738790 | 659 | e = new_StoreEntry(WITH_MEMOBJ, log_url); |
30a4f2a8 | 660 | e->lock_count = 1; /* Note lock here w/o calling storeLock() */ |
661 | mem = e->mem_obj; | |
090089c4 | 662 | e->url = xstrdup(url); |
6eb42cae | 663 | meta_data.url_strings += strlen(url); |
73ddce89 | 664 | e->method = method; |
86101e40 | 665 | if (neighbors_do_private_keys || !BIT_TEST(flags, REQ_HIERARCHICAL)) |
666 | storeSetPrivateKey(e); | |
667 | else | |
668 | storeSetPublicKey(e); | |
234967c9 | 669 | if (BIT_TEST(flags, REQ_CACHABLE)) { |
1c481e00 | 670 | BIT_SET(e->flag, ENTRY_CACHABLE); |
090089c4 | 671 | BIT_RESET(e->flag, RELEASE_REQUEST); |
672 | } else { | |
1c481e00 | 673 | BIT_RESET(e->flag, ENTRY_CACHABLE); |
2daae136 | 674 | storeReleaseRequest(e); |
090089c4 | 675 | } |
234967c9 | 676 | if (BIT_TEST(flags, REQ_HIERARCHICAL)) |
677 | BIT_SET(e->flag, HIERARCHICAL); | |
678 | else | |
679 | BIT_RESET(e->flag, HIERARCHICAL); | |
234967c9 | 680 | e->store_status = STORE_PENDING; |
090089c4 | 681 | storeSetMemStatus(e, NOT_IN_MEMORY); |
8350fe9b | 682 | e->swap_status = SWAPOUT_NONE; |
090089c4 | 683 | e->swap_file_number = -1; |
30a4f2a8 | 684 | mem->data = new_MemObjectData(); |
090089c4 | 685 | e->refcount = 0; |
b8de7ebe | 686 | e->lastref = squid_curtime; |
ca98227c | 687 | e->timestamp = 0; /* set in storeTimestampsSet() */ |
30a4f2a8 | 688 | e->ping_status = PING_NONE; |
0a0bf5db | 689 | BIT_SET(e->flag, ENTRY_VALIDATED); |
227fbb74 | 690 | |
090089c4 | 691 | /* allocate client list */ |
52d4522b | 692 | mem->nclients = MIN_CLIENT; |
d3a743b1 | 693 | mem->clients = xcalloc(mem->nclients, sizeof(struct _store_client)); |
2285407f | 694 | /* storeLog(STORE_LOG_CREATE, e); */ |
090089c4 | 695 | return e; |
696 | } | |
697 | ||
698 | /* Add a new object to the cache with empty memory copy and pointer to disk | |
699 | * use to rebuild store from disk. */ | |
24382924 | 700 | static StoreEntry * |
919877b6 | 701 | storeAddDiskRestore(const char *url, |
23ff6968 | 702 | int file_number, |
703 | int size, | |
704 | time_t expires, | |
705 | time_t timestamp, | |
706 | time_t lastref, | |
707 | time_t lastmod, | |
708 | u_num32 refcount, | |
709 | u_num32 flags, | |
710 | int clean) | |
090089c4 | 711 | { |
712 | StoreEntry *e = NULL; | |
a47b9029 | 713 | debug(20, 5) ("StoreAddDiskRestore: %s, fileno=%08X\n", url, file_number); |
3facc31e | 714 | /* if you call this you'd better be sure file_number is not |
715 | * already in use! */ | |
090089c4 | 716 | meta_data.url_strings += strlen(url); |
88738790 | 717 | e = new_StoreEntry(WITHOUT_MEMOBJ, NULL); |
090089c4 | 718 | e->url = xstrdup(url); |
73ddce89 | 719 | e->method = METHOD_GET; |
6eb42cae | 720 | storeSetPublicKey(e); |
89e079ed | 721 | assert(e->key); |
234967c9 | 722 | e->store_status = STORE_OK; |
090089c4 | 723 | storeSetMemStatus(e, NOT_IN_MEMORY); |
8350fe9b | 724 | e->swap_status = SWAPOUT_DONE; |
090089c4 | 725 | e->swap_file_number = file_number; |
090089c4 | 726 | e->object_len = size; |
727 | e->lock_count = 0; | |
090089c4 | 728 | e->refcount = 0; |
919877b6 | 729 | e->lastref = lastref; |
f5469bce | 730 | e->timestamp = timestamp; |
731 | e->expires = expires; | |
732 | e->lastmod = lastmod; | |
c54e9052 | 733 | e->refcount = refcount; |
734 | e->flag = flags; | |
89e079ed | 735 | BIT_SET(e->flag, ENTRY_CACHABLE); |
736 | BIT_RESET(e->flag, RELEASE_REQUEST); | |
737 | BIT_RESET(e->flag, KEY_PRIVATE); | |
30a4f2a8 | 738 | e->ping_status = PING_NONE; |
311ea387 | 739 | if (clean) { |
3e347513 | 740 | BIT_SET(e->flag, ENTRY_VALIDATED); |
9e4ad609 | 741 | /* Only set the file bit if we know its a valid entry */ |
742 | /* otherwise, set it in the validation procedure */ | |
743 | storeDirMapBitSet(file_number); | |
744 | } else { | |
3e347513 | 745 | BIT_RESET(e->flag, ENTRY_VALIDATED); |
9e4ad609 | 746 | } |
090089c4 | 747 | return e; |
748 | } | |
749 | ||
b8d8561b | 750 | int |
382d851a | 751 | storeUnregister(StoreEntry * e, void *data) |
090089c4 | 752 | { |
6e40f263 | 753 | MemObject *mem = e->mem_obj; |
f990cccc | 754 | struct _store_client *sc; |
38792624 | 755 | if (mem == NULL) |
756 | return 0; | |
a3d5953d | 757 | debug(20, 3) ("storeUnregister: called for '%s'\n", e->key); |
8350fe9b | 758 | sc = storeClientListSearch(mem, data); |
759 | if (sc == NULL) | |
52d4522b | 760 | return 0; |
645afd3f | 761 | if (e->store_status == STORE_OK && e->swap_status != SWAPOUT_DONE) |
6a54c60e | 762 | storeCheckSwapOut(e); |
fe96bbe6 | 763 | sc->seen_offset = 0; |
764 | sc->copy_offset = 0; | |
8350fe9b | 765 | if (sc->swapin_fd > -1) |
766 | file_close(sc->swapin_fd); | |
767 | sc->swapin_fd = -1; | |
f990cccc | 768 | sc->callback = NULL; |
769 | sc->callback_data = NULL; | |
a3d5953d | 770 | debug(20, 9) ("storeUnregister: returning 1\n"); |
52d4522b | 771 | return 1; |
090089c4 | 772 | } |
773 | ||
8350fe9b | 774 | off_t |
775 | storeLowestMemReaderOffset(const StoreEntry * entry) | |
234967c9 | 776 | { |
0ee4272b | 777 | const MemObject *mem = entry->mem_obj; |
8350fe9b | 778 | off_t lowest = mem->inmem_hi; |
234967c9 | 779 | int i; |
8350fe9b | 780 | struct _store_client *sc; |
52d4522b | 781 | for (i = 0; i < mem->nclients; i++) { |
8350fe9b | 782 | sc = &mem->clients[i]; |
783 | if (sc->callback_data == NULL) /* open slot */ | |
234967c9 | 784 | continue; |
8350fe9b | 785 | if (sc->swapin_fd > -1) /* reading from disk */ |
786 | continue; | |
787 | if (sc->copy_offset < lowest) | |
788 | lowest = sc->copy_offset; | |
234967c9 | 789 | } |
790 | return lowest; | |
791 | } | |
792 | ||
090089c4 | 793 | /* Call handlers waiting for data to be appended to E. */ |
498d87ae | 794 | void |
b8014333 | 795 | InvokeHandlers(const StoreEntry * e) |
090089c4 | 796 | { |
797 | int i; | |
9dfb6c1c | 798 | MemObject *mem = e->mem_obj; |
d89d1fb6 | 799 | STCB *callback = NULL; |
52d4522b | 800 | struct _store_client *sc; |
02be0294 | 801 | ssize_t size; |
9e975e4e | 802 | assert(mem->clients != NULL || mem->nclients == 0); |
a47b9029 | 803 | debug(20, 3) ("InvokeHandlers: %s\n", e->key); |
d89d1fb6 | 804 | /* walk the entire list looking for valid callbacks */ |
52d4522b | 805 | for (i = 0; i < mem->nclients; i++) { |
a47b9029 | 806 | debug(20, 3) ("InvokeHandlers: checking client #%d\n", i); |
52d4522b | 807 | sc = &mem->clients[i]; |
382d851a | 808 | if (sc->callback_data == NULL) |
63cc9284 | 809 | continue; |
d89d1fb6 | 810 | if ((callback = sc->callback) == NULL) |
63cc9284 | 811 | continue; |
52d4522b | 812 | sc->callback = NULL; |
382d851a | 813 | /* Don't NULL the callback_data, its used to identify the client */ |
d89d1fb6 | 814 | size = memCopy(mem->data, |
fe96bbe6 | 815 | sc->copy_offset, |
d89d1fb6 | 816 | sc->copy_buf, |
817 | sc->copy_size); | |
a47b9029 | 818 | debug(20, 3) ("InvokeHandlers: calling handler: %p\n", callback); |
d89d1fb6 | 819 | callback(sc->callback_data, sc->copy_buf, size); |
090089c4 | 820 | } |
090089c4 | 821 | } |
822 | ||
6eb42cae | 823 | /* Mark object as expired */ |
b8d8561b | 824 | void |
825 | storeExpireNow(StoreEntry * e) | |
9174e204 | 826 | { |
a3d5953d | 827 | debug(20, 3) ("storeExpireNow: '%s'\n", e->key); |
b8de7ebe | 828 | e->expires = squid_curtime; |
9174e204 | 829 | } |
830 | ||
8350fe9b | 831 | static void |
832 | storeSwapoutFileOpened(void *data, int fd) | |
090089c4 | 833 | { |
8350fe9b | 834 | swapout_ctrl_t *ctrlp = data; |
835 | int oldswapstatus = ctrlp->oldswapstatus; | |
836 | char *swapfilename = ctrlp->swapfilename; | |
837 | StoreEntry *e = ctrlp->e; | |
838 | MemObject *mem; | |
839 | xfree(ctrlp); | |
840 | assert(e->swap_status == SWAPOUT_OPENING); | |
841 | if (fd < 0) { | |
842 | debug(20, 0) ("storeSwapoutFileOpened: Unable to open swapfile: %s\n", | |
843 | swapfilename); | |
844 | storeDirMapBitReset(e->swap_file_number); | |
845 | e->swap_file_number = -1; | |
846 | e->swap_status = oldswapstatus; | |
847 | xfree(swapfilename); | |
090089c4 | 848 | return; |
8350fe9b | 849 | } |
850 | mem = e->mem_obj; | |
851 | mem->swapout.fd = (short) fd; | |
852 | e->swap_status = SWAPOUT_WRITING; | |
853 | debug(20, 5) ("storeSwapoutFileOpened: Begin SwapOut '%s' to FD %d FILE %s.\n", | |
854 | e->url, fd, swapfilename); | |
855 | xfree(swapfilename); | |
856 | debug(20, 5) ("swap_file_number=%08X\n", e->swap_file_number); | |
857 | /*mem->swap_offset = 0; */ | |
858 | mem->e_swap_buf = get_free_8k_page(); | |
859 | mem->e_swap_buf_len = 0; | |
860 | storeCheckSwapOut(e); | |
861 | } | |
862 | ||
863 | /* start swapping object to disk */ | |
864 | static void | |
865 | storeSwapOutStart(StoreEntry * e) | |
866 | { | |
867 | swapout_ctrl_t *ctrlp; | |
868 | LOCAL_ARRAY(char, swapfilename, SQUID_MAXPATHLEN); | |
869 | storeLockObject(e); | |
870 | if ((e->swap_file_number = storeGetUnusedFileno()) < 0) | |
871 | e->swap_file_number = storeDirMapAllocate(); | |
872 | storeSwapFullPath(e->swap_file_number, swapfilename); | |
873 | ctrlp = xmalloc(sizeof(swapout_ctrl_t)); | |
874 | ctrlp->swapfilename = xstrdup(swapfilename); | |
875 | ctrlp->e = e; | |
876 | ctrlp->oldswapstatus = e->swap_status; | |
877 | e->swap_status = SWAPOUT_OPENING; | |
878 | file_open(swapfilename, | |
879 | O_WRONLY | O_CREAT | O_TRUNC, | |
880 | storeSwapoutFileOpened, | |
881 | ctrlp); | |
882 | } | |
883 | ||
884 | static void | |
885 | storeSwapOutHandle(int fd, int flag, size_t len, void *data) | |
886 | { | |
887 | StoreEntry *e = data; | |
888 | MemObject *mem = e->mem_obj; | |
889 | debug(20, 3) ("storeSwapOutHandle: '%s', len=%d\n", e->key, (int) len); | |
890 | assert(mem != NULL); | |
891 | if (flag < 0) { | |
892 | debug(20, 1) ("storeSwapOutHandle: SwapOut failure (err code = %d).\n", | |
893 | flag); | |
894 | e->swap_status = SWAPOUT_NONE; | |
895 | put_free_8k_page(mem->e_swap_buf); | |
645afd3f | 896 | file_close(mem->swapout.fd); |
897 | mem->swapout.fd = -1; | |
8350fe9b | 898 | if (e->swap_file_number != -1) { |
899 | storePutUnusedFileno(e->swap_file_number); | |
900 | e->swap_file_number = -1; | |
901 | } | |
902 | storeRelease(e); | |
903 | if (flag == DISK_NO_SPACE_LEFT) { | |
904 | /* reduce the swap_size limit to the current size. */ | |
905 | Config.Swap.maxSize = store_swap_size; | |
906 | storeConfigure(); | |
907 | } | |
908 | return; | |
909 | } | |
910 | storeDirUpdateSwapSize(e->swap_file_number, len, 1); | |
8c123b71 | 911 | mem->swapout.done_offset += len; |
912 | if (e->store_status == STORE_PENDING || mem->swapout.done_offset < e->object_len) { | |
8350fe9b | 913 | storeCheckSwapOut(e); |
914 | return; | |
915 | } | |
916 | /* swapping complete */ | |
8350fe9b | 917 | file_close(mem->swapout.fd); |
e6e887e9 | 918 | put_free_8k_page(mem->e_swap_buf); |
645afd3f | 919 | mem->swapout.fd = -1; |
8350fe9b | 920 | debug(20, 5) ("storeSwapOutHandle: SwapOut complete: '%s' to %s.\n", |
921 | e->url, storeSwapFullPath(e->swap_file_number, NULL)); | |
e6e887e9 | 922 | if (!storeCheckCachable(e)) { |
923 | storeReleaseRequest(e); | |
924 | } else { | |
6a54c60e | 925 | e->swap_status = SWAPOUT_DONE; |
926 | storeLog(STORE_LOG_SWAPOUT, e); | |
927 | storeDirSwapLog(e); | |
e6e887e9 | 928 | } |
8350fe9b | 929 | HTTPCacheInfo->proto_newobject(HTTPCacheInfo, |
930 | mem->request->protocol, | |
931 | e->object_len, | |
932 | FALSE); | |
933 | storeUnlockObject(e); | |
934 | } | |
935 | ||
936 | static void | |
937 | storeCheckSwapOut(StoreEntry * e) | |
938 | { | |
939 | MemObject *mem = e->mem_obj; | |
940 | int x; | |
941 | off_t lowest_offset; | |
8c123b71 | 942 | off_t new_mem_lo; |
943 | size_t swapout_size; | |
8350fe9b | 944 | assert(mem != NULL); |
945 | /* should we swap something out to disk? */ | |
946 | debug(20, 3) ("storeCheckSwapOut: %s\n", e->url); | |
947 | debug(20, 3) ("storeCheckSwapOut: store_status = %s\n", | |
948 | storeStatusStr[e->store_status]); | |
8350fe9b | 949 | if (e->store_status == STORE_ABORTED) |
950 | return; | |
8c123b71 | 951 | |
952 | debug(20, 3) ("storeCheckSwapOut: mem->inmem_lo = %d\n", | |
953 | (int) mem->inmem_lo); | |
954 | debug(20, 3) ("storeCheckSwapOut: mem->inmem_hi = %d\n", | |
955 | (int) mem->inmem_hi); | |
956 | assert(mem->inmem_hi >= mem->swapout.queue_offset); | |
957 | swapout_size = (size_t) (mem->inmem_hi - mem->swapout.queue_offset); | |
8350fe9b | 958 | lowest_offset = storeLowestMemReaderOffset(e); |
ea0dd42b | 959 | debug(20, 3) ("storeCheckSwapOut: lowest_offset = %d\n", |
960 | (int) lowest_offset); | |
8c123b71 | 961 | assert(lowest_offset >= mem->inmem_lo); |
962 | ||
963 | new_mem_lo = lowest_offset; | |
964 | if (!BIT_TEST(e->flag, ENTRY_CACHABLE)) { | |
965 | memFreeDataUpto(mem->data, new_mem_lo); | |
966 | mem->inmem_lo = new_mem_lo; | |
8350fe9b | 967 | return; |
8c123b71 | 968 | } |
969 | if (mem->swapout.queue_offset < new_mem_lo) | |
970 | new_mem_lo = mem->swapout.queue_offset; | |
971 | memFreeDataUpto(mem->data, new_mem_lo); | |
972 | mem->inmem_lo = new_mem_lo; | |
973 | ||
974 | swapout_size = (size_t) (mem->inmem_hi - mem->swapout.queue_offset); | |
975 | debug(20, 3) ("storeCheckSwapOut: swapout_size = %d\n", | |
976 | (int) swapout_size); | |
977 | if (swapout_size == 0) | |
978 | return; | |
979 | if (e->store_status == STORE_PENDING && swapout_size < VM_WINDOW_SZ) | |
8350fe9b | 980 | return; /* wait for a full block */ |
981 | /* Ok, we have stuff to swap out. Is there a swapout.fd open? */ | |
982 | if (e->swap_status == SWAPOUT_NONE) { | |
983 | assert(mem->swapout.fd == -1); | |
8c123b71 | 984 | if (storeCheckCachable(e)) |
6a54c60e | 985 | storeSwapOutStart(e); |
8c123b71 | 986 | /* else ENTRY_CACHABLE will be cleared and we'll never get |
6a54c60e | 987 | * here again */ |
8350fe9b | 988 | return; |
989 | } | |
990 | if (e->swap_status == SWAPOUT_OPENING) | |
991 | return; | |
992 | assert(mem->swapout.fd > -1); | |
8c123b71 | 993 | if (swapout_size > SWAP_BUF) |
994 | swapout_size = SWAP_BUF; | |
995 | debug(20, 3) ("storeCheckSwapOut: swapout.queue_offset = %d\n", (int) mem->swapout.queue_offset); | |
8350fe9b | 996 | x = storeCopy(e, |
8c123b71 | 997 | mem->swapout.queue_offset, |
998 | swapout_size, | |
8350fe9b | 999 | mem->e_swap_buf, |
1000 | &mem->e_swap_buf_len); | |
1001 | if (x < 0) { | |
1002 | debug(20, 1) ("storeCopy returned %d for '%s'\n", x, e->key); | |
1003 | e->swap_file_number = -1; | |
1004 | file_close(mem->swapout.fd); | |
1005 | mem->swapout.fd = -1; | |
1006 | storeDirMapBitReset(e->swap_file_number); | |
1007 | safeunlink(storeSwapFullPath(e->swap_file_number, NULL), 1); | |
1008 | e->swap_file_number = -1; | |
1009 | e->swap_status = SWAPOUT_NONE; | |
1010 | return; | |
1011 | } | |
1012 | debug(20, 3) ("storeCheckSwapOut: e_swap_buf_len = %d\n", (int) mem->e_swap_buf_len); | |
1013 | assert(mem->e_swap_buf_len > 0); | |
1014 | debug(20, 3) ("storeCheckSwapOut: swapping out %d bytes from %d\n", | |
8c123b71 | 1015 | mem->e_swap_buf_len, mem->swapout.queue_offset); |
1016 | mem->swapout.queue_offset += mem->e_swap_buf_len; | |
8350fe9b | 1017 | x = file_write(mem->swapout.fd, |
1018 | mem->e_swap_buf, | |
1019 | mem->e_swap_buf_len, | |
1020 | storeSwapOutHandle, | |
1021 | e, | |
1022 | NULL); | |
1023 | assert(x == DISK_OK); | |
090089c4 | 1024 | } |
1025 | ||
1026 | /* Append incoming data from a primary server to an entry. */ | |
b8d8561b | 1027 | void |
b8014333 | 1028 | storeAppend(StoreEntry * e, const char *buf, int len) |
090089c4 | 1029 | { |
3a1c3e2f | 1030 | MemObject *mem = e->mem_obj; |
1031 | assert(mem != NULL); | |
1032 | assert(len >= 0); | |
090089c4 | 1033 | if (len) { |
a3d5953d | 1034 | debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n", len, e->key); |
38792624 | 1035 | storeGetMemSpace(len); |
090089c4 | 1036 | store_mem_size += len; |
3a1c3e2f | 1037 | memAppend(mem->data, buf, len); |
8350fe9b | 1038 | mem->inmem_hi += len; |
090089c4 | 1039 | } |
aadcfbad | 1040 | if (e->store_status != STORE_ABORTED && !BIT_TEST(e->flag, DELAY_SENDING)) |
090089c4 | 1041 | InvokeHandlers(e); |
8350fe9b | 1042 | storeCheckSwapOut(e); |
090089c4 | 1043 | } |
1044 | ||
24382924 | 1045 | #ifdef __STDC__ |
b8d8561b | 1046 | void |
fe4e214f | 1047 | storeAppendPrintf(StoreEntry * e, const char *fmt,...) |
c30c5a73 | 1048 | { |
15c05bb0 | 1049 | va_list args; |
95d659f0 | 1050 | LOCAL_ARRAY(char, buf, 4096); |
15c05bb0 | 1051 | va_start(args, fmt); |
c30c5a73 | 1052 | #else |
b8d8561b | 1053 | void |
1054 | storeAppendPrintf(va_alist) | |
15c05bb0 | 1055 | va_dcl |
1056 | { | |
1057 | va_list args; | |
1058 | StoreEntry *e = NULL; | |
0ee4272b | 1059 | const char *fmt = NULL; |
95d659f0 | 1060 | LOCAL_ARRAY(char, buf, 4096); |
15c05bb0 | 1061 | va_start(args); |
1062 | e = va_arg(args, StoreEntry *); | |
1063 | fmt = va_arg(args, char *); | |
c30c5a73 | 1064 | #endif |
15c05bb0 | 1065 | buf[0] = '\0'; |
8350fe9b | 1066 | vsnprintf(buf, 4096, fmt, args); |
15c05bb0 | 1067 | storeAppend(e, buf, strlen(buf)); |
1068 | va_end(args); | |
c30c5a73 | 1069 | } |
1070 | ||
090089c4 | 1071 | /* start swapping in */ |
95c4b18f | 1072 | void |
582b6456 | 1073 | storeSwapInStart(StoreEntry * e, SIH * callback, void *callback_data) |
090089c4 | 1074 | { |
0a0bf5db | 1075 | swapin_ctrl_t *ctrlp; |
8350fe9b | 1076 | assert(e->mem_status == NOT_IN_MEMORY); |
311ea387 | 1077 | if (!BIT_TEST(e->flag, ENTRY_VALIDATED)) { |
97a88399 | 1078 | if (storeDirMapBitTest(e->swap_file_number)) { |
311ea387 | 1079 | /* someone took our file while we weren't looking */ |
8350fe9b | 1080 | callback(-1, callback_data); |
311ea387 | 1081 | return; |
1082 | } | |
1083 | } | |
bbed1a99 | 1084 | assert(e->swap_status == SWAPOUT_WRITING || e->swap_status == SWAPOUT_DONE); |
95c4b18f | 1085 | assert(e->swap_file_number >= 0); |
8350fe9b | 1086 | assert(e->mem_obj != NULL); |
0a0bf5db | 1087 | ctrlp = xmalloc(sizeof(swapin_ctrl_t)); |
1088 | ctrlp->e = e; | |
1089 | ctrlp->callback = callback; | |
1090 | ctrlp->callback_data = callback_data; | |
95c4b18f | 1091 | if (BIT_TEST(e->flag, ENTRY_VALIDATED)) |
35ac11a3 | 1092 | storeSwapInValidateComplete(ctrlp); |
95c4b18f | 1093 | else |
1094 | storeValidate(e, storeSwapInValidateComplete, ctrlp); | |
0a0bf5db | 1095 | } |
090089c4 | 1096 | |
0a0bf5db | 1097 | |
1098 | static void | |
35ac11a3 | 1099 | storeSwapInValidateComplete(void *data) |
0a0bf5db | 1100 | { |
1101 | swapin_ctrl_t *ctrlp = (swapin_ctrl_t *) data; | |
0a0bf5db | 1102 | StoreEntry *e; |
1103 | e = ctrlp->e; | |
95c4b18f | 1104 | assert(e->mem_status == NOT_IN_MEMORY); |
0a0bf5db | 1105 | if (!BIT_TEST(e->flag, ENTRY_VALIDATED)) { |
fc07a0e4 | 1106 | /* Invoke a store abort that should free the memory object */ |
8350fe9b | 1107 | (ctrlp->callback) (-1, ctrlp->callback_data); |
0a0bf5db | 1108 | xfree(ctrlp); |
1109 | return; | |
090089c4 | 1110 | } |
95c4b18f | 1111 | ctrlp->path = xstrdup(storeSwapFullPath(e->swap_file_number, NULL)); |
8350fe9b | 1112 | file_open(ctrlp->path, O_RDONLY, storeSwapInFileOpened, ctrlp); |
0a0bf5db | 1113 | } |
1114 | ||
1115 | static void | |
8350fe9b | 1116 | storeSwapInFileOpened(void *data, int fd) |
0a0bf5db | 1117 | { |
0a0bf5db | 1118 | swapin_ctrl_t *ctrlp = (swapin_ctrl_t *) data; |
1119 | StoreEntry *e = ctrlp->e; | |
0379374a | 1120 | assert(e->mem_obj != NULL); |
95c4b18f | 1121 | assert(e->mem_status == NOT_IN_MEMORY); |
8350fe9b | 1122 | assert(e->swap_status == SWAPOUT_DONE); |
0a0bf5db | 1123 | if (fd < 0) { |
a3d5953d | 1124 | debug(20, 0) ("storeSwapInStartComplete: Failed for '%s'\n", e->url); |
0a0bf5db | 1125 | /* Invoke a store abort that should free the memory object */ |
8350fe9b | 1126 | (ctrlp->callback) (-1, ctrlp->callback_data); |
0a0bf5db | 1127 | xfree(ctrlp->path); |
1128 | xfree(ctrlp); | |
1129 | return; | |
1130 | } | |
a3d5953d | 1131 | debug(20, 5) ("storeSwapInStart: initialized swap file '%s' for '%s'\n", |
0a0bf5db | 1132 | ctrlp->path, e->url); |
8350fe9b | 1133 | (ctrlp->callback) (fd, ctrlp->callback_data); |
0a0bf5db | 1134 | xfree(ctrlp->path); |
1135 | xfree(ctrlp); | |
090089c4 | 1136 | } |
1137 | ||
090089c4 | 1138 | /* recreate meta data from disk image in swap directory */ |
3facc31e | 1139 | /* Add one swap file at a time from disk storage */ |
48f44632 | 1140 | static void |
1141 | storeDoRebuildFromDisk(void *data) | |
3facc31e | 1142 | { |
5608850b | 1143 | struct storeRebuildState *RB = data; |
429fdbec | 1144 | LOCAL_ARRAY(char, swapfile, MAXPATHLEN); |
f2052513 | 1145 | LOCAL_ARRAY(char, url, MAX_URL); |
090089c4 | 1146 | StoreEntry *e = NULL; |
090089c4 | 1147 | time_t expires; |
1148 | time_t timestamp; | |
919877b6 | 1149 | time_t lastref; |
f5469bce | 1150 | time_t lastmod; |
1151 | int scan1; | |
1152 | int scan2; | |
1153 | int scan3; | |
1154 | int scan4; | |
c54e9052 | 1155 | int scan5; |
1156 | int scan6; | |
919877b6 | 1157 | int scan7; |
3facc31e | 1158 | off_t size; |
090089c4 | 1159 | int sfileno = 0; |
30a4f2a8 | 1160 | int count; |
f5469bce | 1161 | int x; |
5608850b | 1162 | struct _rebuild_dir *d; |
1163 | struct _rebuild_dir **D; | |
429fdbec | 1164 | int used; /* is swapfile already in use? */ |
1165 | int newer; /* is the log entry newer than current entry? */ | |
1166 | ||
30a4f2a8 | 1167 | /* load a number of objects per invocation */ |
5608850b | 1168 | if ((d = RB->rebuild_dir) == NULL) { |
3e347513 | 1169 | storeRebuiltFromDisk(RB); |
1170 | return; | |
5608850b | 1171 | } |
1172 | for (count = 0; count < d->speed; count++) { | |
1173 | if (fgets(RB->line_in, RB->line_in_sz, d->log) == NULL) { | |
a3d5953d | 1174 | debug(20, 1) ("Done reading Cache Dir #%d swap log\n", d->dirn); |
3e347513 | 1175 | fclose(d->log); |
1176 | d->log = NULL; | |
1177 | storeDirCloseTmpSwapLog(d->dirn); | |
1178 | RB->rebuild_dir = d->next; | |
1179 | safe_free(d); | |
1180 | eventAdd("storeRebuild", storeDoRebuildFromDisk, RB, 0); | |
1181 | return; | |
48f44632 | 1182 | } |
5608850b | 1183 | if ((++RB->linecount & 0x3FFF) == 0) |
a3d5953d | 1184 | debug(20, 1) (" %7d Lines read so far.\n", RB->linecount); |
1185 | debug(20, 9) ("line_in: %s", RB->line_in); | |
5608850b | 1186 | if (RB->line_in[0] == '\0') |
1187 | continue; | |
1188 | if (RB->line_in[0] == '\n') | |
1189 | continue; | |
1190 | if (RB->line_in[0] == '#') | |
1191 | continue; | |
f5469bce | 1192 | url[0] = '\0'; |
f5469bce | 1193 | sfileno = 0; |
1194 | scan1 = 0; | |
1195 | scan2 = 0; | |
30a4f2a8 | 1196 | scan3 = 0; |
f5469bce | 1197 | scan4 = 0; |
919877b6 | 1198 | scan5 = 0; |
1199 | scan6 = 0; | |
1200 | scan7 = 0; | |
1201 | x = sscanf(RB->line_in, "%x %x %x %x %x %d %d %x %s", | |
0be3ec60 | 1202 | &sfileno, /* swap_file_number */ |
1203 | &scan1, /* timestamp */ | |
919877b6 | 1204 | &scan2, /* lastref */ |
1205 | &scan3, /* expires */ | |
1206 | &scan4, /* last modified */ | |
1207 | &scan5, /* size */ | |
1208 | &scan6, /* refcount */ | |
1209 | &scan7, /* flags */ | |
0be3ec60 | 1210 | url); /* url */ |
994bbf93 | 1211 | if (x < 1) { |
1212 | RB->invalid++; | |
30a4f2a8 | 1213 | continue; |
994bbf93 | 1214 | } |
b3264694 | 1215 | if (strncmp(url, "http://internal.squid", 21) == 0) |
1216 | continue; | |
429fdbec | 1217 | storeSwapFullPath(sfileno, swapfile); |
919877b6 | 1218 | if (x != 9) { |
994bbf93 | 1219 | RB->invalid++; |
429fdbec | 1220 | continue; |
994bbf93 | 1221 | } |
1222 | if (sfileno < 0) { | |
1223 | RB->invalid++; | |
0a0bf5db | 1224 | continue; |
994bbf93 | 1225 | } |
919877b6 | 1226 | if (BIT_TEST(scan7, KEY_PRIVATE)) { |
a47b9029 | 1227 | RB->badflags++; |
1228 | continue; | |
1229 | } | |
c932b107 | 1230 | sfileno = storeDirProperFileno(d->dirn, sfileno); |
f5469bce | 1231 | timestamp = (time_t) scan1; |
919877b6 | 1232 | lastref = (time_t) scan2; |
1233 | expires = (time_t) scan3; | |
1234 | lastmod = (time_t) scan4; | |
1235 | size = (off_t) scan5; | |
30a4f2a8 | 1236 | |
429fdbec | 1237 | e = storeGet(url); |
1238 | used = storeDirMapBitTest(sfileno); | |
1239 | /* If this URL already exists in the cache, does the swap log | |
1240 | * appear to have a newer entry? Compare 'timestamp' from the | |
1241 | * swap log to e->lastref. Note, we can't compare e->timestamp | |
1242 | * because it is the Date: header from the HTTP reply and | |
1243 | * doesn't really tell us when the object was added to the | |
1244 | * cache. */ | |
1245 | newer = e ? (timestamp > e->lastref ? 1 : 0) : 0; | |
1246 | if (used && !newer) { | |
1247 | /* log entry is old, ignore it */ | |
1248 | RB->clashcount++; | |
1249 | continue; | |
1250 | } else if (used && e && e->swap_file_number == sfileno) { | |
1251 | /* swapfile taken, same URL, newer, update meta */ | |
1252 | e->lastref = timestamp; | |
1253 | e->timestamp = timestamp; | |
1254 | e->expires = expires; | |
1255 | e->lastmod = lastmod; | |
919877b6 | 1256 | e->flag |= (u_num32) scan6; |
1257 | e->refcount += (u_num32) scan7; | |
429fdbec | 1258 | continue; |
1259 | } else if (used) { | |
1260 | /* swapfile in use, not by this URL, log entry is newer */ | |
1261 | /* This is sorta bad: the log entry should NOT be newer at this | |
1262 | * point. If the log is dirty, the filesize check should have | |
1263 | * caught this. If the log is clean, there should never be a | |
1264 | * newer entry. */ | |
a3d5953d | 1265 | debug(20, 1) ("WARNING: newer swaplog entry for fileno %08X\n", |
30a4f2a8 | 1266 | sfileno); |
429fdbec | 1267 | /* I'm tempted to remove the swapfile here just to be safe, |
1268 | * but there is a bad race condition in the NOVM version if | |
1269 | * the swapfile has recently been opened for writing, but | |
1270 | * not yet opened for reading. Because we can't map | |
1271 | * swapfiles back to StoreEntrys, we don't know the state | |
1272 | * of the entry using that file. */ | |
ccbc48f5 | 1273 | /* We'll assume the existing entry is valid, probably because |
365e5b34 | 1274 | * were in a slow rebuild and the the swap file number got taken |
1275 | * and the validation procedure hasn't run. */ | |
ccbc48f5 | 1276 | assert(RB->need_to_validate); |
5608850b | 1277 | RB->clashcount++; |
30a4f2a8 | 1278 | continue; |
429fdbec | 1279 | } else if (e) { |
1280 | /* URL already exists, this swapfile not being used */ | |
1281 | /* junk old, load new */ | |
1282 | storeRelease(e); /* release old entry */ | |
1283 | RB->dupcount++; | |
1284 | } else { | |
1285 | /* URL doesnt exist, swapfile not in use */ | |
1286 | /* load new */ | |
1287 | (void) 0; | |
30a4f2a8 | 1288 | } |
1289 | /* update store_swap_size */ | |
c932b107 | 1290 | storeDirUpdateSwapSize(sfileno, size, 1); |
5608850b | 1291 | RB->objcount++; |
f5469bce | 1292 | e = storeAddDiskRestore(url, |
30a4f2a8 | 1293 | sfileno, |
1294 | (int) size, | |
1295 | expires, | |
f5469bce | 1296 | timestamp, |
919877b6 | 1297 | lastref, |
311ea387 | 1298 | lastmod, |
919877b6 | 1299 | (u_num32) scan6, /* refcount */ |
1300 | (u_num32) scan7, /* flags */ | |
311ea387 | 1301 | d->clean); |
5608850b | 1302 | storeDirSwapLog(e); |
a528eb87 | 1303 | HTTPCacheInfo->proto_newobject(HTTPCacheInfo, |
1ac9bc38 | 1304 | urlParseProtocol(url), |
30a4f2a8 | 1305 | (int) size, |
1306 | TRUE); | |
c943f331 | 1307 | } |
5608850b | 1308 | RB->rebuild_dir = d->next; |
76fefb77 | 1309 | for (D = &RB->rebuild_dir; *D; D = &(*D)->next); |
5608850b | 1310 | *D = d; |
1311 | d->next = NULL; | |
1312 | eventAdd("storeRebuild", storeDoRebuildFromDisk, RB, 0); | |
3facc31e | 1313 | } |
1314 | ||
0a0bf5db | 1315 | static void |
1316 | storeCleanup(void *data) | |
1317 | { | |
1318 | static storeCleanList *list = NULL; | |
1319 | storeCleanList *curr; | |
1320 | static int bucketnum = -1; | |
1321 | static int validnum = 0; | |
1322 | StoreEntry *e; | |
1323 | hash_link *link_ptr = NULL; | |
1324 | if (list == NULL) { | |
1325 | if (++bucketnum >= store_buckets) { | |
a3d5953d | 1326 | debug(20, 1) (" Completed Validation Procedure\n"); |
1327 | debug(20, 1) (" Validated %d Entries\n", validnum); | |
f182d1c5 | 1328 | store_rebuilding = 0; |
0a0bf5db | 1329 | return; |
1330 | } | |
1331 | link_ptr = hash_get_bucket(store_table, bucketnum); | |
1332 | for (; link_ptr; link_ptr = link_ptr->next) { | |
1333 | e = (StoreEntry *) link_ptr; | |
35ac11a3 | 1334 | if (BIT_TEST(e->flag, ENTRY_VALIDATED)) |
1335 | continue; | |
1336 | if (BIT_TEST(e->flag, RELEASE_REQUEST)) | |
1337 | continue; | |
311ea387 | 1338 | curr = xcalloc(1, sizeof(storeCleanList)); |
0a0bf5db | 1339 | curr->key = xstrdup(e->key); |
1340 | curr->next = list; | |
1341 | list = curr; | |
1342 | } | |
1343 | } | |
1344 | if (list == NULL) { | |
1345 | eventAdd("storeCleanup", storeCleanup, NULL, 0); | |
1346 | return; | |
1347 | } | |
1348 | curr = list; | |
1349 | list = list->next; | |
1350 | e = (StoreEntry *) hash_lookup(store_table, curr->key); | |
c340827e | 1351 | if (e && !BIT_TEST(e->flag, ENTRY_VALIDATED)) { |
365e5b34 | 1352 | storeLockObject(e); |
3e347513 | 1353 | storeValidate(e, storeCleanupComplete, e); |
a3d5953d | 1354 | if ((++validnum & 0xFFF) == 0) |
1355 | debug(20, 1) (" %7d Entries Validated so far.\n", validnum); | |
35ac11a3 | 1356 | assert(validnum <= meta_data.store_entries); |
95c4b18f | 1357 | } |
0a0bf5db | 1358 | xfree(curr->key); |
1359 | xfree(curr); | |
1360 | eventAdd("storeCleanup", storeCleanup, NULL, 0); | |
1361 | } | |
1362 | ||
0a0bf5db | 1363 | static void |
35ac11a3 | 1364 | storeCleanupComplete(void *data) |
0a0bf5db | 1365 | { |
1366 | StoreEntry *e = data; | |
35ac11a3 | 1367 | storeUnlockObject(e); |
0a0bf5db | 1368 | if (!BIT_TEST(e->flag, ENTRY_VALIDATED)) |
1369 | storeRelease(e); | |
1370 | } | |
1371 | ||
1372 | static void | |
1373 | storeValidate(StoreEntry * e, VCB callback, void *callback_data) | |
1374 | { | |
1375 | valid_ctrl_t *ctrlp; | |
1376 | char *path; | |
1377 | struct stat *sb; | |
606bd3b5 | 1378 | #if !USE_ASYNC_IO |
95c4b18f | 1379 | int x; |
606bd3b5 | 1380 | #endif |
d7b78fbb | 1381 | assert(!BIT_TEST(e->flag, ENTRY_VALIDATED)); |
0a0bf5db | 1382 | if (e->swap_file_number < 0) { |
1383 | BIT_RESET(e->flag, ENTRY_VALIDATED); | |
35ac11a3 | 1384 | callback(callback_data); |
0a0bf5db | 1385 | return; |
1386 | } | |
1387 | path = storeSwapFullPath(e->swap_file_number, NULL); | |
1388 | sb = xmalloc(sizeof(struct stat)); | |
1389 | ctrlp = xmalloc(sizeof(valid_ctrl_t)); | |
1390 | ctrlp->sb = sb; | |
1391 | ctrlp->e = e; | |
1392 | ctrlp->callback = callback; | |
1393 | ctrlp->callback_data = callback_data; | |
1394 | #if USE_ASYNC_IO | |
1395 | aioStat(path, sb, storeValidateComplete, ctrlp); | |
1396 | #else | |
95c4b18f | 1397 | /* When evaluating the actual arguments in a function call, the order |
1398 | * in which the arguments and the function expression are evaluated is | |
1399 | * not specified; */ | |
1400 | x = stat(path, sb); | |
1401 | storeValidateComplete(ctrlp, x, errno); | |
0a0bf5db | 1402 | #endif |
1403 | return; | |
1404 | } | |
1405 | ||
1406 | static void | |
1407 | storeValidateComplete(void *data, int retcode, int errcode) | |
1408 | { | |
1409 | valid_ctrl_t *ctrlp = data; | |
1410 | struct stat *sb = ctrlp->sb; | |
1411 | StoreEntry *e = ctrlp->e; | |
1412 | char *path; | |
1413 | if (retcode < 0 && errcode == EWOULDBLOCK) { | |
1414 | path = storeSwapFullPath(e->swap_file_number, NULL); | |
1415 | retcode = stat(path, sb); | |
1416 | } | |
9e4ad609 | 1417 | if (retcode < 0 || sb->st_size == 0 || sb->st_size != e->object_len) { |
0a0bf5db | 1418 | BIT_RESET(e->flag, ENTRY_VALIDATED); |
9e4ad609 | 1419 | } else { |
0a0bf5db | 1420 | BIT_SET(e->flag, ENTRY_VALIDATED); |
9e4ad609 | 1421 | storeDirMapBitSet(e->swap_file_number); |
1422 | } | |
0a0bf5db | 1423 | errno = errcode; |
35ac11a3 | 1424 | ctrlp->callback(ctrlp->callback_data); |
0a0bf5db | 1425 | xfree(sb); |
1426 | xfree(ctrlp); | |
1427 | } | |
1428 | ||
3facc31e | 1429 | /* meta data recreated from disk image in swap directory */ |
b8d8561b | 1430 | static void |
5608850b | 1431 | storeRebuiltFromDisk(struct storeRebuildState *data) |
3facc31e | 1432 | { |
1433 | time_t r; | |
1434 | time_t stop; | |
97c03d3c | 1435 | stop = squid_curtime; |
3facc31e | 1436 | r = stop - data->start; |
a3d5953d | 1437 | debug(20, 1) ("Finished rebuilding storage from disk image.\n"); |
1438 | debug(20, 1) (" %7d Lines read from previous logfile.\n", data->linecount); | |
994bbf93 | 1439 | debug(20, 1) (" %7d Invalid lines.\n", data->invalid); |
a47b9029 | 1440 | debug(20, 1) (" %7d With invalid flags.\n", data->badflags); |
a3d5953d | 1441 | debug(20, 1) (" %7d Objects loaded.\n", data->objcount); |
1442 | debug(20, 1) (" %7d Objects expired.\n", data->expcount); | |
1443 | debug(20, 1) (" %7d Duplicate URLs purged.\n", data->dupcount); | |
1444 | debug(20, 1) (" %7d Swapfile clashes avoided.\n", data->clashcount); | |
1445 | debug(20, 1) (" Took %d seconds (%6.1lf objects/sec).\n", | |
3facc31e | 1446 | r > 0 ? r : 0, (double) data->objcount / (r > 0 ? r : 1)); |
a3d5953d | 1447 | debug(20, 1) (" store_swap_size = %dk\n", store_swap_size); |
76f87348 | 1448 | if (data->need_to_validate && data->linecount) { |
a3d5953d | 1449 | debug(20, 1) ("Beginning Validation Procedure\n"); |
0a0bf5db | 1450 | eventAdd("storeCleanup", storeCleanup, NULL, 0); |
f182d1c5 | 1451 | } else { |
a3d5953d | 1452 | store_rebuilding = 0; |
0a0bf5db | 1453 | } |
f182d1c5 | 1454 | safe_free(data->line_in); |
1455 | safe_free(data); | |
090089c4 | 1456 | } |
1457 | ||
24382924 | 1458 | static void |
b8d8561b | 1459 | storeStartRebuildFromDisk(void) |
3facc31e | 1460 | { |
3facc31e | 1461 | int i; |
5608850b | 1462 | struct storeRebuildState *RB; |
1463 | struct _rebuild_dir *d; | |
1464 | FILE *fp; | |
1465 | int clean; | |
1466 | RB = xcalloc(1, sizeof(struct storeRebuildState)); | |
1467 | RB->start = squid_curtime; | |
f1dc9b30 | 1468 | for (i = 0; i < Config.cacheSwap.n_configured; i++) { |
5608850b | 1469 | fp = storeDirOpenTmpSwapLog(i, &clean); |
1470 | if (fp == NULL) | |
1471 | continue; | |
1472 | d = xcalloc(1, sizeof(struct _rebuild_dir)); | |
1473 | d->dirn = i; | |
1474 | d->log = fp; | |
1475 | d->clean = clean; | |
1476 | d->speed = opt_foreground_rebuild ? 1 << 30 : 50; | |
1477 | d->next = RB->rebuild_dir; | |
1478 | RB->rebuild_dir = d; | |
1479 | if (!clean) | |
f182d1c5 | 1480 | RB->need_to_validate = 1; |
a3d5953d | 1481 | debug(20, 1) ("Rebuilding storage in Cache Dir #%d (%s)\n", |
5608850b | 1482 | i, clean ? "CLEAN" : "DIRTY"); |
1483 | } | |
1484 | RB->line_in_sz = 4096; | |
1485 | RB->line_in = xcalloc(1, RB->line_in_sz); | |
30a4f2a8 | 1486 | if (opt_foreground_rebuild) { |
5608850b | 1487 | storeDoRebuildFromDisk(RB); |
30a4f2a8 | 1488 | } else { |
5608850b | 1489 | eventAdd("storeRebuild", storeDoRebuildFromDisk, RB, 0); |
30a4f2a8 | 1490 | } |
3facc31e | 1491 | } |
090089c4 | 1492 | |
b8d8561b | 1493 | static int |
8350fe9b | 1494 | storeCheckCachable(StoreEntry * e) |
6602e70e | 1495 | { |
a7e59001 | 1496 | if (e->method != METHOD_GET) { |
8350fe9b | 1497 | debug(20, 2) ("storeCheckCachable: NO: non-GET method\n"); |
1c481e00 | 1498 | } else if (!BIT_TEST(e->flag, ENTRY_CACHABLE)) { |
8350fe9b | 1499 | debug(20, 2) ("storeCheckCachable: NO: not cachable\n"); |
6602e70e | 1500 | } else if (BIT_TEST(e->flag, RELEASE_REQUEST)) { |
8350fe9b | 1501 | debug(20, 2) ("storeCheckCachable: NO: release requested\n"); |
83d453f3 | 1502 | } else if (e->store_status == STORE_OK && !storeEntryValidLength(e)) { |
8350fe9b | 1503 | debug(20, 2) ("storeCheckCachable: NO: wrong content-length\n"); |
79b5cc5f | 1504 | } else if (BIT_TEST(e->flag, ENTRY_NEGCACHED)) { |
8350fe9b | 1505 | debug(20, 2) ("storeCheckCachable: NO: negative cached\n"); |
3e98df20 | 1506 | return 0; /* avoid release call below */ |
8350fe9b | 1507 | } else if (e->mem_obj->inmem_hi > Config.Store.maxObjectSize) { |
1508 | debug(20, 2) ("storeCheckCachable: NO: too big\n"); | |
89e079ed | 1509 | } else if (BIT_TEST(e->flag, KEY_PRIVATE)) { |
8350fe9b | 1510 | debug(20, 3) ("storeCheckCachable: NO: private key\n"); |
d4432957 | 1511 | } else { |
6602e70e | 1512 | return 1; |
d4432957 | 1513 | } |
2daae136 | 1514 | storeReleaseRequest(e); |
1c481e00 | 1515 | BIT_RESET(e->flag, ENTRY_CACHABLE); |
6602e70e | 1516 | return 0; |
1517 | } | |
1518 | ||
090089c4 | 1519 | /* Complete transfer into the local cache. */ |
b8d8561b | 1520 | void |
1521 | storeComplete(StoreEntry * e) | |
090089c4 | 1522 | { |
a3d5953d | 1523 | debug(20, 3) ("storeComplete: '%s'\n", e->key); |
8350fe9b | 1524 | e->object_len = e->mem_obj->inmem_hi; |
b8de7ebe | 1525 | e->lastref = squid_curtime; |
234967c9 | 1526 | e->store_status = STORE_OK; |
8350fe9b | 1527 | assert(e->mem_status == NOT_IN_MEMORY); |
3a1c3e2f | 1528 | InvokeHandlers(e); |
8c123b71 | 1529 | storeCheckSwapOut(e); |
090089c4 | 1530 | } |
1531 | ||
1532 | /* | |
474cac1b | 1533 | * Someone wants to abort this transfer. Set the reason in the |
1534 | * request structure, call the server-side callback and mark the | |
1535 | * entry for releasing | |
090089c4 | 1536 | */ |
b8d8561b | 1537 | void |
9b312a19 | 1538 | storeAbort(StoreEntry * e, int cbflag) |
090089c4 | 1539 | { |
3e98df20 | 1540 | MemObject *mem = e->mem_obj; |
8f39b81d | 1541 | assert(e->store_status == STORE_PENDING); |
1542 | assert(mem != NULL); | |
9b312a19 | 1543 | debug(20, 6) ("storeAbort: %s\n", e->key); |
79b5cc5f | 1544 | storeNegativeCache(e); |
474cac1b | 1545 | storeReleaseRequest(e); |
234967c9 | 1546 | e->store_status = STORE_ABORTED; |
8350fe9b | 1547 | storeSetMemStatus(e, NOT_IN_MEMORY); |
090089c4 | 1548 | /* No DISK swap for negative cached object */ |
8350fe9b | 1549 | e->swap_status = SWAPOUT_NONE; |
b8de7ebe | 1550 | e->lastref = squid_curtime; |
090089c4 | 1551 | /* Count bytes faulted through cache but not moved to disk */ |
a528eb87 | 1552 | HTTPCacheInfo->proto_touchobject(HTTPCacheInfo, |
3e98df20 | 1553 | mem->request ? mem->request->protocol : PROTO_NONE, |
8350fe9b | 1554 | mem->inmem_hi); |
090089c4 | 1555 | /* We assign an object length here--The only other place we assign the |
1556 | * object length is in storeComplete() */ | |
474cac1b | 1557 | storeLockObject(e); |
8350fe9b | 1558 | e->object_len = mem->inmem_hi; |
474cac1b | 1559 | /* Notify the server side */ |
1560 | if (cbflag && mem->abort.callback) { | |
bfcaf585 | 1561 | mem->abort.callback(mem->abort.data); |
1562 | mem->abort.callback = NULL; | |
1563 | } | |
474cac1b | 1564 | /* Notify the client side */ |
090089c4 | 1565 | InvokeHandlers(e); |
090089c4 | 1566 | storeUnlockObject(e); |
090089c4 | 1567 | } |
1568 | ||
090089c4 | 1569 | /* get the first entry in the storage */ |
b8d8561b | 1570 | StoreEntry * |
1571 | storeGetFirst(void) | |
090089c4 | 1572 | { |
c3c5b744 | 1573 | return ((StoreEntry *) hash_first(store_table)); |
090089c4 | 1574 | } |
1575 | ||
1576 | ||
1577 | /* get the next entry in the storage for a given search pointer */ | |
b8d8561b | 1578 | StoreEntry * |
1579 | storeGetNext(void) | |
090089c4 | 1580 | { |
c3c5b744 | 1581 | return ((StoreEntry *) hash_next(store_table)); |
090089c4 | 1582 | } |
1583 | ||
090089c4 | 1584 | |
090089c4 | 1585 | /* Clear Memory storage to accommodate the given object len */ |
38792624 | 1586 | static void |
d4432957 | 1587 | storeGetMemSpace(int size) |
090089c4 | 1588 | { |
b32508fb | 1589 | StoreEntry *e = NULL; |
090089c4 | 1590 | int n_expired = 0; |
66c2848f | 1591 | int n_purged = 0; |
579a45cd | 1592 | int n_released = 0; |
b32508fb | 1593 | static time_t last_check = 0; |
e954773d | 1594 | int pages_needed; |
8350fe9b | 1595 | struct _mem_entry *m; |
1596 | struct _mem_entry *prev; | |
b32508fb | 1597 | if (squid_curtime == last_check) |
38792624 | 1598 | return; |
b32508fb | 1599 | last_check = squid_curtime; |
e954773d | 1600 | pages_needed = (size / SM_PAGE_SIZE) + 1; |
61cd203b | 1601 | if (sm_stats.n_pages_in_use + pages_needed < store_pages_high) |
38792624 | 1602 | return; |
311ea387 | 1603 | if (store_rebuilding) |
38792624 | 1604 | return; |
a3d5953d | 1605 | debug(20, 2) ("storeGetMemSpace: Starting, need %d pages\n", pages_needed); |
8350fe9b | 1606 | for (m = inmem_tail; m; m = prev) { |
1607 | prev = m->prev; | |
1608 | e = m->e; | |
1609 | if (storeEntryLocked(e)) | |
1610 | continue; | |
1611 | if (storeCheckExpired(e, 0)) { | |
1612 | debug(20, 2) ("storeGetMemSpace: Expired: %s\n", e->url); | |
1613 | n_expired++; | |
1614 | storeRelease(e); | |
1615 | } else { | |
1616 | storePurgeMem(e); | |
1617 | n_purged++; | |
1618 | } | |
1619 | if (sm_stats.n_pages_in_use + pages_needed < store_pages_low) | |
1620 | break; | |
1621 | } | |
8350fe9b | 1622 | debug(20, 0) ("storeGetMemSpace stats:\n"); |
1623 | debug(20, 0) (" %6d HOT objects\n", meta_data.hot_vm); | |
1624 | debug(20, 0) (" %6d were purged\n", n_purged); | |
1625 | debug(20, 0) (" %6d were released\n", n_released); | |
090089c4 | 1626 | } |
1627 | ||
b8d8561b | 1628 | static int |
1629 | compareLastRef(StoreEntry ** e1, StoreEntry ** e2) | |
090089c4 | 1630 | { |
1631 | if (!e1 || !e2) | |
1632 | fatal_dump(NULL); | |
090089c4 | 1633 | if ((*e1)->lastref > (*e2)->lastref) |
1634 | return (1); | |
090089c4 | 1635 | if ((*e1)->lastref < (*e2)->lastref) |
1636 | return (-1); | |
090089c4 | 1637 | return (0); |
1638 | } | |
1639 | ||
cb6ae8c3 | 1640 | static int |
d3c12084 | 1641 | compareBucketOrder(struct _bucketOrder *a, struct _bucketOrder *b) |
cb6ae8c3 | 1642 | { |
d3c12084 | 1643 | return a->index - b->index; |
cb6ae8c3 | 1644 | } |
1645 | ||
090089c4 | 1646 | /* returns the bucket number to work on, |
1647 | * pointer to next bucket after each calling | |
1648 | */ | |
24382924 | 1649 | static unsigned int |
b8d8561b | 1650 | storeGetBucketNum(void) |
090089c4 | 1651 | { |
1652 | static unsigned int bucket = 0; | |
66cedb85 | 1653 | if (bucket >= store_buckets) |
090089c4 | 1654 | bucket = 0; |
1655 | return (bucket++); | |
1656 | } | |
1657 | ||
66cedb85 | 1658 | #define SWAP_MAX_HELP (store_buckets/2) |
090089c4 | 1659 | |
1660 | /* The maximum objects to scan for maintain storage space */ | |
0a0bf5db | 1661 | #define SWAP_LRUSCAN_COUNT 1024 |
1662 | #define SWAP_LRU_REMOVE_COUNT 64 | |
090089c4 | 1663 | |
1664 | /* Clear Swap storage to accommodate the given object len */ | |
b8d8561b | 1665 | int |
1666 | storeGetSwapSpace(int size) | |
090089c4 | 1667 | { |
1668 | static int fReduceSwap = 0; | |
1669 | static int swap_help = 0; | |
a1b0d7cf | 1670 | StoreEntry *e = NULL; |
090089c4 | 1671 | int scanned = 0; |
1672 | int removed = 0; | |
090089c4 | 1673 | int locked = 0; |
1674 | int locked_size = 0; | |
a1b0d7cf | 1675 | int list_count = 0; |
1676 | int scan_count = 0; | |
1677 | int max_list_count = SWAP_LRUSCAN_COUNT << 1; | |
090089c4 | 1678 | int i; |
a1b0d7cf | 1679 | StoreEntry **LRU_list; |
090089c4 | 1680 | hash_link *link_ptr = NULL, *next = NULL; |
63017fae | 1681 | int kb_size = ((size + 1023) >> 10); |
090089c4 | 1682 | |
1683 | if (store_swap_size + kb_size <= store_swap_low) | |
1684 | fReduceSwap = 0; | |
1685 | if (!fReduceSwap && (store_swap_size + kb_size <= store_swap_high)) { | |
1686 | return 0; | |
1687 | } | |
a3d5953d | 1688 | debug(20, 2) ("storeGetSwapSpace: Starting...\n"); |
090089c4 | 1689 | |
1690 | /* Set flag if swap size over high-water-mark */ | |
1691 | if (store_swap_size + kb_size > store_swap_high) | |
1692 | fReduceSwap = 1; | |
1693 | ||
a3d5953d | 1694 | debug(20, 2) ("storeGetSwapSpace: Need %d bytes...\n", size); |
090089c4 | 1695 | |
a1b0d7cf | 1696 | LRU_list = xcalloc(max_list_count, sizeof(StoreEntry *)); |
1697 | /* remove expired objects until recover enough or no expired objects */ | |
66cedb85 | 1698 | for (i = 0; i < store_buckets; i++) { |
ec74cf5f | 1699 | link_ptr = hash_get_bucket(store_table, storeGetBucketNum()); |
090089c4 | 1700 | if (link_ptr == NULL) |
1701 | continue; | |
429fdbec | 1702 | /* this for loop handles one bucket of hash table */ |
79b5cc5f | 1703 | for (; link_ptr; link_ptr = next) { |
a1b0d7cf | 1704 | if (list_count == max_list_count) |
1705 | break; | |
090089c4 | 1706 | scanned++; |
1707 | next = link_ptr->next; | |
1708 | e = (StoreEntry *) link_ptr; | |
0a0bf5db | 1709 | if (!BIT_TEST(e->flag, ENTRY_VALIDATED)) |
1710 | continue; | |
429fdbec | 1711 | if (storeCheckExpired(e, 0)) { |
a3d5953d | 1712 | debug(20, 3) ("storeGetSwapSpace: Expired '%s'\n", e->url); |
429fdbec | 1713 | storeRelease(e); |
79b5cc5f | 1714 | } else if (!storeEntryLocked(e)) { |
fedac7e5 | 1715 | *(LRU_list + list_count) = e; |
a1b0d7cf | 1716 | list_count++; |
1717 | scan_count++; | |
090089c4 | 1718 | } else { |
ff5731b1 | 1719 | locked++; |
8350fe9b | 1720 | locked_size += e->mem_obj->inmem_hi; |
090089c4 | 1721 | } |
429fdbec | 1722 | } /* for, end of one bucket of hash table */ |
a1b0d7cf | 1723 | qsort((char *) LRU_list, |
1724 | list_count, | |
6e40f263 | 1725 | sizeof(StoreEntry *), |
582b6456 | 1726 | (QS *) compareLastRef); |
85b701ed | 1727 | if (list_count > SWAP_LRU_REMOVE_COUNT) |
fedac7e5 | 1728 | list_count = SWAP_LRU_REMOVE_COUNT; /* chop list */ |
a1b0d7cf | 1729 | if (scan_count > SWAP_LRUSCAN_COUNT) |
090089c4 | 1730 | break; |
1731 | } /* for */ | |
1732 | ||
30a4f2a8 | 1733 | #ifdef LOTSA_DEBUGGING |
090089c4 | 1734 | /* end of candidate selection */ |
a3d5953d | 1735 | debug(20, 2) ("storeGetSwapSpace: Current Size: %7d kbytes\n", |
30a4f2a8 | 1736 | store_swap_size); |
a3d5953d | 1737 | debug(20, 2) ("storeGetSwapSpace: High W Mark: %7d kbytes\n", |
30a4f2a8 | 1738 | store_swap_high); |
a3d5953d | 1739 | debug(20, 2) ("storeGetSwapSpace: Low W Mark: %7d kbytes\n", |
30a4f2a8 | 1740 | store_swap_low); |
a3d5953d | 1741 | debug(20, 2) ("storeGetSwapSpace: Entry count: %7d items\n", |
30a4f2a8 | 1742 | meta_data.store_entries); |
a3d5953d | 1743 | debug(20, 2) ("storeGetSwapSpace: Visited: %7d buckets\n", |
30a4f2a8 | 1744 | i + 1); |
a3d5953d | 1745 | debug(20, 2) ("storeGetSwapSpace: Scanned: %7d items\n", |
30a4f2a8 | 1746 | scanned); |
a3d5953d | 1747 | debug(20, 2) ("storeGetSwapSpace: Expired: %7d items\n", |
30a4f2a8 | 1748 | expired); |
a3d5953d | 1749 | debug(20, 2) ("storeGetSwapSpace: Locked: %7d items\n", |
30a4f2a8 | 1750 | locked); |
a3d5953d | 1751 | debug(20, 2) ("storeGetSwapSpace: Locked Space: %7d bytes\n", |
30a4f2a8 | 1752 | locked_size); |
a3d5953d | 1753 | debug(20, 2) ("storeGetSwapSpace: Scan in array: %7d bytes\n", |
30a4f2a8 | 1754 | scan_in_objs); |
a3d5953d | 1755 | debug(20, 2) ("storeGetSwapSpace: LRU candidate: %7d items\n", |
30a4f2a8 | 1756 | LRU_list->index); |
1757 | #endif /* LOTSA_DEBUGGING */ | |
090089c4 | 1758 | |
cc61958c | 1759 | for (i = 0; i < list_count; i++) |
1760 | removed += storeRelease(*(LRU_list + i)); | |
a1b0d7cf | 1761 | if (store_swap_size + kb_size <= store_swap_low) |
fedac7e5 | 1762 | fReduceSwap = 0; |
a3d5953d | 1763 | debug(20, 2) ("storeGetSwapSpace: After Freeing Size: %7d kbytes\n", |
a1b0d7cf | 1764 | store_swap_size); |
090089c4 | 1765 | /* free the list */ |
a1b0d7cf | 1766 | safe_free(LRU_list); |
090089c4 | 1767 | |
1768 | if ((store_swap_size + kb_size > store_swap_high)) { | |
63017fae | 1769 | i = 2; |
090089c4 | 1770 | if (++swap_help > SWAP_MAX_HELP) { |
a3d5953d | 1771 | debug(20, 0) ("WARNING: Repeated failures to free up disk space!\n"); |
63017fae | 1772 | i = 0; |
090089c4 | 1773 | } |
a3d5953d | 1774 | debug(20, i) ("storeGetSwapSpace: Disk usage is over high water mark\n"); |
1775 | debug(20, i) ("--> store_swap_high = %d KB\n", store_swap_high); | |
1776 | debug(20, i) ("--> store_swap_size = %d KB\n", store_swap_size); | |
1777 | debug(20, i) ("--> asking for %d KB\n", kb_size); | |
090089c4 | 1778 | } else { |
1779 | swap_help = 0; | |
1780 | } | |
a3d5953d | 1781 | debug(20, 2) ("Removed %d objects\n", removed); |
090089c4 | 1782 | return 0; |
1783 | } | |
1784 | ||
1785 | ||
1786 | /* release an object from a cache */ | |
cc61958c | 1787 | /* return number of objects released. */ |
b8d8561b | 1788 | int |
1789 | storeRelease(StoreEntry * e) | |
090089c4 | 1790 | { |
cc61958c | 1791 | StoreEntry *hentry = NULL; |
0ee4272b | 1792 | const char *hkey; |
a3d5953d | 1793 | debug(20, 3) ("storeRelease: Releasing: '%s'\n", e->key); |
090089c4 | 1794 | /* If, for any reason we can't discard this object because of an |
1795 | * outstanding request, mark it for pending release */ | |
1796 | if (storeEntryLocked(e)) { | |
9174e204 | 1797 | storeExpireNow(e); |
a3d5953d | 1798 | debug(20, 3) ("storeRelease: Only setting RELEASE_REQUEST bit\n"); |
2daae136 | 1799 | storeReleaseRequest(e); |
cc61958c | 1800 | return 0; |
090089c4 | 1801 | } |
cc61958c | 1802 | /* check if coresponding HEAD object exists. */ |
73ddce89 | 1803 | if (e->method == METHOD_GET) { |
cc61958c | 1804 | hkey = storeGeneratePublicKey(e->url, METHOD_HEAD); |
1805 | if ((hentry = (StoreEntry *) hash_lookup(store_table, hkey))) | |
38792624 | 1806 | storeExpireNow(hentry); |
090089c4 | 1807 | } |
311ea387 | 1808 | if (store_rebuilding) { |
a3d5953d | 1809 | debug(20, 2) ("storeRelease: Delaying release until store is rebuilt: '%s'\n", |
30a4f2a8 | 1810 | e->key ? e->key : e->url ? e->url : "NO URL"); |
1811 | storeExpireNow(e); | |
1812 | storeSetPrivateKey(e); | |
35ac11a3 | 1813 | BIT_SET(e->flag, RELEASE_REQUEST); |
cc61958c | 1814 | return 0; |
30a4f2a8 | 1815 | } |
8350fe9b | 1816 | if (e->swap_status == SWAPOUT_DONE && (e->swap_file_number > -1)) { |
9e4ad609 | 1817 | if (BIT_TEST(e->flag, ENTRY_VALIDATED)) |
1818 | storePutUnusedFileno(e->swap_file_number); | |
c932b107 | 1819 | storeDirUpdateSwapSize(e->swap_file_number, e->object_len, -1); |
090089c4 | 1820 | e->swap_file_number = -1; |
a528eb87 | 1821 | HTTPCacheInfo->proto_purgeobject(HTTPCacheInfo, |
1ac9bc38 | 1822 | urlParseProtocol(e->url), |
30a4f2a8 | 1823 | e->object_len); |
090089c4 | 1824 | } |
8350fe9b | 1825 | storeSetMemStatus(e, NOT_IN_MEMORY); |
f7a5493b | 1826 | storeHashDelete(e); |
147d3115 | 1827 | storeLog(STORE_LOG_RELEASE, e); |
30a4f2a8 | 1828 | destroy_StoreEntry(e); |
cc61958c | 1829 | return 1; |
090089c4 | 1830 | } |
1831 | ||
090089c4 | 1832 | /* return 1 if a store entry is locked */ |
24382924 | 1833 | static int |
fe4e214f | 1834 | storeEntryLocked(const StoreEntry * e) |
090089c4 | 1835 | { |
30a4f2a8 | 1836 | if (e->lock_count) |
1837 | return 1; | |
8350fe9b | 1838 | if (e->swap_status == SWAPOUT_WRITING) |
30a4f2a8 | 1839 | return 1; |
a1e47288 | 1840 | if (e->store_status == STORE_PENDING) |
1841 | return 1; | |
365cb147 | 1842 | if (BIT_TEST(e->flag, ENTRY_SPECIAL)) |
1843 | return 1; | |
30a4f2a8 | 1844 | return 0; |
090089c4 | 1845 | } |
1846 | ||
b8d8561b | 1847 | static int |
8350fe9b | 1848 | storeCopy(const StoreEntry * e, off_t stateoffset, size_t maxSize, char *buf, size_t * size) |
090089c4 | 1849 | { |
429fdbec | 1850 | MemObject *mem = e->mem_obj; |
d89d1fb6 | 1851 | size_t s; |
8350fe9b | 1852 | assert(stateoffset >= mem->inmem_lo); |
d89d1fb6 | 1853 | s = memCopy(mem->data, stateoffset, buf, maxSize); |
8350fe9b | 1854 | assert(s); |
429fdbec | 1855 | return *size = s; |
090089c4 | 1856 | } |
1857 | ||
1858 | /* check if there is any client waiting for this object at all */ | |
1859 | /* return 1 if there is at least one client */ | |
b8d8561b | 1860 | int |
fe4e214f | 1861 | storeClientWaiting(const StoreEntry * e) |
090089c4 | 1862 | { |
1863 | int i; | |
6e40f263 | 1864 | MemObject *mem = e->mem_obj; |
52d4522b | 1865 | if (mem->clients) { |
1866 | for (i = 0; i < mem->nclients; i++) { | |
382d851a | 1867 | if (mem->clients[i].callback_data != NULL) |
090089c4 | 1868 | return 1; |
1869 | } | |
1870 | } | |
1871 | return 0; | |
1872 | } | |
1873 | ||
8350fe9b | 1874 | static struct _store_client * |
382d851a | 1875 | storeClientListSearch(const MemObject * mem, void *data) |
090089c4 | 1876 | { |
1877 | int i; | |
52d4522b | 1878 | if (mem->clients) { |
1879 | for (i = 0; i < mem->nclients; i++) { | |
8350fe9b | 1880 | if (mem->clients[i].callback_data == data) |
1881 | return &mem->clients[i]; | |
6e40f263 | 1882 | } |
090089c4 | 1883 | } |
8350fe9b | 1884 | return NULL; |
090089c4 | 1885 | } |
1886 | ||
1887 | /* add client with fd to client list */ | |
52d4522b | 1888 | int |
fe96bbe6 | 1889 | storeClientListAdd(StoreEntry * e, void *data) |
090089c4 | 1890 | { |
1891 | int i; | |
6e40f263 | 1892 | MemObject *mem = e->mem_obj; |
52d4522b | 1893 | struct _store_client *oldlist = NULL; |
5f0e9628 | 1894 | struct _store_client *sc; |
6e40f263 | 1895 | int oldsize; |
8350fe9b | 1896 | if (mem == NULL) |
1897 | mem = e->mem_obj = new_MemObject(urlClean(e->url)); | |
090089c4 | 1898 | /* look for empty slot */ |
52d4522b | 1899 | if (mem->clients == NULL) { |
1900 | mem->nclients = MIN_CLIENT; | |
d3a743b1 | 1901 | mem->clients = xcalloc(mem->nclients, sizeof(struct _store_client)); |
090089c4 | 1902 | } |
52d4522b | 1903 | for (i = 0; i < mem->nclients; i++) { |
382d851a | 1904 | if (mem->clients[i].callback_data == data) |
429fdbec | 1905 | return i; /* its already here */ |
382d851a | 1906 | if (mem->clients[i].callback_data == NULL) |
6e40f263 | 1907 | break; |
1908 | } | |
52d4522b | 1909 | if (i == mem->nclients) { |
a3d5953d | 1910 | debug(20, 3) ("storeClientListAdd: Growing clients for '%s'\n", e->url); |
52d4522b | 1911 | oldlist = mem->clients; |
1912 | oldsize = mem->nclients; | |
1913 | mem->nclients <<= 1; | |
d3a743b1 | 1914 | mem->clients = xcalloc(mem->nclients, sizeof(struct _store_client)); |
6e40f263 | 1915 | for (i = 0; i < oldsize; i++) |
52d4522b | 1916 | mem->clients[i] = oldlist[i]; |
6e40f263 | 1917 | safe_free(oldlist); |
52d4522b | 1918 | i = oldsize; |
6e40f263 | 1919 | } |
5f0e9628 | 1920 | sc = &mem->clients[i]; |
1921 | sc->callback_data = data; | |
1922 | sc->seen_offset = 0; | |
1923 | sc->copy_offset = 0; | |
1924 | sc->swapin_fd = -1; | |
1925 | sc->mem = mem; | |
52d4522b | 1926 | return i; |
090089c4 | 1927 | } |
1928 | ||
1929 | /* same to storeCopy but also register client fd and last requested offset | |
1930 | * for each client */ | |
d89d1fb6 | 1931 | void |
38792624 | 1932 | storeClientCopy(StoreEntry * e, |
fe96bbe6 | 1933 | off_t seen_offset, |
1934 | off_t copy_offset, | |
d89d1fb6 | 1935 | size_t size, |
38792624 | 1936 | char *buf, |
d89d1fb6 | 1937 | STCB * callback, |
382d851a | 1938 | void *data) |
38792624 | 1939 | { |
d89d1fb6 | 1940 | size_t sz; |
6e40f263 | 1941 | MemObject *mem = e->mem_obj; |
fe96bbe6 | 1942 | struct _store_client *sc; |
d4b1b930 | 1943 | static int recurse_detect = 0; |
b974bc9d | 1944 | assert(e->store_status != STORE_ABORTED); |
f182d1c5 | 1945 | assert(recurse_detect < 3); /* could == 1 for IMS not modified's */ |
8350fe9b | 1946 | debug(20, 3) ("storeClientCopy: %s, seen %d, want %d, size %d, cb %p, cbdata %p\n", |
86101e40 | 1947 | e->key, |
1948 | (int) seen_offset, | |
1949 | (int) copy_offset, | |
1950 | (int) size, | |
1951 | callback, | |
1952 | data); | |
8350fe9b | 1953 | sc = storeClientListSearch(mem, data); |
1954 | assert(sc != NULL); | |
fe96bbe6 | 1955 | sc->copy_offset = copy_offset; |
1956 | sc->seen_offset = seen_offset; | |
8350fe9b | 1957 | sc->callback = callback; |
1958 | sc->copy_buf = buf; | |
1959 | sc->copy_size = size; | |
1960 | sc->copy_offset = copy_offset; | |
b974bc9d | 1961 | assert(seen_offset <= mem->inmem_hi || e->store_status != STORE_PENDING); |
8350fe9b | 1962 | if (e->store_status == STORE_PENDING && seen_offset == mem->inmem_hi) { |
fe96bbe6 | 1963 | /* client has already seen this, wait for more */ |
8350fe9b | 1964 | debug(20, 3) ("storeClientCopy: Waiting for more\n"); |
d89d1fb6 | 1965 | return; |
090089c4 | 1966 | } |
8350fe9b | 1967 | if (copy_offset >= mem->inmem_lo && mem->inmem_lo < mem->inmem_hi) { |
1968 | /* What the client wants is in memory */ | |
1969 | debug(20, 3) ("storeClientCopy: Copying from memory\n"); | |
1970 | sz = memCopy(mem->data, copy_offset, buf, size); | |
1971 | recurse_detect++; | |
1972 | sc->callback = NULL; | |
1973 | callback(data, buf, sz); | |
1974 | } else if (sc->swapin_fd < 0) { | |
1975 | debug(20, 3) ("storeClientCopy: Need to open swap in file\n"); | |
1976 | /* gotta open the swapin file */ | |
1977 | assert(copy_offset == 0); | |
1978 | storeSwapInStart(e, storeClientCopyFileOpened, sc); | |
1979 | } else { | |
1980 | debug(20, 3) ("storeClientCopy: reading from disk FD %d\n", | |
1981 | sc->swapin_fd); | |
1982 | storeClientCopyFileRead(sc); | |
1983 | } | |
f182d1c5 | 1984 | recurse_detect--; |
090089c4 | 1985 | } |
1986 | ||
8350fe9b | 1987 | static void |
1988 | storeClientCopyFileOpened(int fd, void *data) | |
1989 | { | |
1990 | struct _store_client *sc = data; | |
1991 | if (fd < 0) { | |
1992 | debug(20, 1) ("storeClientCopyFileOpened: failed\n"); | |
1993 | sc->callback(sc->callback_data, sc->copy_buf, -1); | |
1994 | return; | |
1995 | } | |
1996 | sc->swapin_fd = fd; | |
1997 | storeClientCopyFileRead(sc); | |
1998 | } | |
1999 | ||
2000 | static void | |
2001 | storeClientCopyFileRead(struct _store_client *sc) | |
2002 | { | |
2003 | file_read(sc->swapin_fd, | |
2004 | sc->copy_buf, | |
2005 | sc->copy_size, | |
2006 | sc->copy_offset, | |
2007 | storeClientCopyHandleRead, | |
2008 | sc); | |
2009 | } | |
2010 | ||
2011 | static void | |
2012 | storeClientCopyHandleRead(int fd, const char *buf, int len, int flag, void *data) | |
2013 | { | |
2014 | struct _store_client *sc = data; | |
5f0e9628 | 2015 | MemObject *mem = sc->mem; |
2016 | debug(20, 3) ("storeClientCopyHandleRead: FD %d, len %d\n", fd, len); | |
2017 | if (len > 0 && sc->copy_offset == 0) | |
6a54c60e | 2018 | httpParseReplyHeaders(buf, mem->reply); |
8350fe9b | 2019 | sc->callback(sc->callback_data, sc->copy_buf, len); |
2020 | } | |
2021 | ||
24382924 | 2022 | static int |
fe4e214f | 2023 | storeEntryValidLength(const StoreEntry * e) |
6602e70e | 2024 | { |
ffe4a367 | 2025 | int diff; |
2285407f | 2026 | int hdr_sz; |
2027 | int content_length; | |
ffe4a367 | 2028 | |
2285407f | 2029 | if (e->mem_obj == NULL) |
ffe4a367 | 2030 | fatal_dump("storeEntryValidLength: NULL mem_obj"); |
2031 | ||
2285407f | 2032 | hdr_sz = e->mem_obj->reply->hdr_sz; |
147d3115 | 2033 | content_length = e->mem_obj->reply->content_length; |
2285407f | 2034 | |
a3d5953d | 2035 | debug(20, 3) ("storeEntryValidLength: Checking '%s'\n", e->key); |
2036 | debug(20, 5) ("storeEntryValidLength: object_len = %d\n", e->object_len); | |
2037 | debug(20, 5) ("storeEntryValidLength: hdr_sz = %d\n", hdr_sz); | |
2038 | debug(20, 5) ("storeEntryValidLength: content_length = %d\n", content_length); | |
ffe4a367 | 2039 | |
2285407f | 2040 | if (content_length == 0) { |
a3d5953d | 2041 | debug(20, 5) ("storeEntryValidLength: Zero content length; assume valid; '%s'\n", |
ffe4a367 | 2042 | e->key); |
2043 | return 1; | |
2044 | } | |
2285407f | 2045 | if (hdr_sz == 0) { |
a3d5953d | 2046 | debug(20, 5) ("storeEntryValidLength: Zero header size; assume valid; '%s'\n", |
ffe4a367 | 2047 | e->key); |
2048 | return 1; | |
2049 | } | |
2285407f | 2050 | diff = hdr_sz + content_length - e->object_len; |
ffe4a367 | 2051 | if (diff != 0) { |
a3d5953d | 2052 | debug(20, 3) ("storeEntryValidLength: %d bytes too %s; '%s'\n", |
ffe4a367 | 2053 | diff < 0 ? -diff : diff, |
2054 | diff < 0 ? "small" : "big", | |
2055 | e->key); | |
2056 | return 0; | |
2057 | } | |
2058 | return 1; | |
2059 | } | |
6602e70e | 2060 | |
d3c12084 | 2061 | #if HAVE_RANDOM |
2062 | #define squid_random random | |
2063 | #elif HAVE_LRAND48 | |
2064 | #define squid_random lrand48 | |
2065 | #else | |
2066 | #define squid_random rand | |
2067 | #endif | |
2068 | ||
63017fae | 2069 | static void |
2070 | storeRandomizeBuckets(void) | |
2071 | { | |
2072 | int i; | |
d3c12084 | 2073 | struct _bucketOrder *b; |
63017fae | 2074 | if (MaintBucketsOrder == NULL) |
d3c12084 | 2075 | MaintBucketsOrder = xcalloc(store_buckets, sizeof(struct _bucketOrder)); |
2076 | for (i = 0; i < store_buckets; i++) { | |
2077 | b = MaintBucketsOrder + i; | |
2078 | b->bucket = (unsigned int) i; | |
2079 | b->index = (int) squid_random(); | |
2080 | } | |
63017fae | 2081 | qsort((char *) MaintBucketsOrder, |
2082 | store_buckets, | |
d3c12084 | 2083 | sizeof(struct _bucketOrder), |
582b6456 | 2084 | (QS *) compareBucketOrder); |
63017fae | 2085 | } |
2086 | ||
66cedb85 | 2087 | static void |
2088 | storeInitHashValues(void) | |
2089 | { | |
2090 | int i; | |
a7e59001 | 2091 | /* Calculate size of hash table (maximum currently 64k buckets). */ |
38792624 | 2092 | i = Config.Swap.maxSize / Config.Store.avgObjectSize; |
86101e40 | 2093 | debug(20, 1) ("Swap maxSize %d KB, estimated %d objects\n", |
66cedb85 | 2094 | Config.Swap.maxSize, i); |
38792624 | 2095 | i /= Config.Store.objectsPerBucket; |
a3d5953d | 2096 | debug(20, 1) ("Target number of buckets: %d\n", i); |
66cedb85 | 2097 | /* ideally the full scan period should be configurable, for the |
2098 | * moment it remains at approximately 24 hours. */ | |
2099 | if (i < 8192) | |
2100 | store_buckets = 7951, store_maintain_rate = 10; | |
2101 | else if (i < 12288) | |
2102 | store_buckets = 12149, store_maintain_rate = 7; | |
2103 | else if (i < 16384) | |
2104 | store_buckets = 16231, store_maintain_rate = 5; | |
2105 | else if (i < 32768) | |
2106 | store_buckets = 33493, store_maintain_rate = 2; | |
2107 | else | |
2108 | store_buckets = 65357, store_maintain_rate = 1; | |
2109 | store_maintain_buckets = 1; | |
63017fae | 2110 | storeRandomizeBuckets(); |
a3d5953d | 2111 | debug(20, 1) ("Using %d Store buckets, maintain %d bucket%s every %d second%s\n", |
66cedb85 | 2112 | store_buckets, |
2113 | store_maintain_buckets, | |
2114 | store_maintain_buckets == 1 ? null_string : "s", | |
2115 | store_maintain_rate, | |
2116 | store_maintain_rate == 1 ? null_string : "s"); | |
a47b9029 | 2117 | debug(20, 1) ("Max Mem size: %d KB\n", Config.Mem.maxSize >> 10); |
2118 | debug(20, 1) ("Max Swap size: %d KB\n", Config.Swap.maxSize); | |
66cedb85 | 2119 | } |
2120 | ||
b8d8561b | 2121 | void |
2122 | storeInit(void) | |
c943f331 | 2123 | { |
234967c9 | 2124 | char *fname = NULL; |
66cedb85 | 2125 | storeInitHashValues(); |
aadcfbad | 2126 | storeCreateHashTable(urlcmp); |
b6f794d6 | 2127 | if (strcmp((fname = Config.Log.store), "none") == 0) |
234967c9 | 2128 | storelog_fd = -1; |
2129 | else | |
f17936ab | 2130 | storelog_fd = file_open(fname, O_WRONLY | O_CREAT, NULL, NULL); |
234967c9 | 2131 | if (storelog_fd < 0) |
a3d5953d | 2132 | debug(20, 1) ("Store logging disabled\n"); |
e7a22b88 | 2133 | storeVerifySwapDirs(); |
2134 | storeDirOpenSwapLogs(); | |
b6f794d6 | 2135 | if (!opt_zap_disk_store) |
3facc31e | 2136 | storeStartRebuildFromDisk(); |
30a4f2a8 | 2137 | else |
311ea387 | 2138 | store_rebuilding = 0; |
b1c0cc67 | 2139 | } |
c943f331 | 2140 | |
b8d8561b | 2141 | void |
2142 | storeConfigure(void) | |
b1c0cc67 | 2143 | { |
e954773d | 2144 | int store_mem_high = 0; |
2145 | int store_mem_low = 0; | |
b6f794d6 | 2146 | store_mem_high = (long) (Config.Mem.maxSize / 100) * |
2147 | Config.Mem.highWaterMark; | |
2148 | store_mem_low = (long) (Config.Mem.maxSize / 100) * | |
2149 | Config.Mem.lowWaterMark; | |
090089c4 | 2150 | |
b1c0cc67 | 2151 | store_swap_high = (long) (((float) Config.Swap.maxSize * |
2152 | (float) Config.Swap.highWaterMark) / (float) 100); | |
2153 | store_swap_low = (long) (((float) Config.Swap.maxSize * | |
2154 | (float) Config.Swap.lowWaterMark) / (float) 100); | |
e954773d | 2155 | |
13c72789 | 2156 | store_pages_max = Config.Mem.maxSize / SM_PAGE_SIZE; |
e954773d | 2157 | store_pages_high = store_mem_high / SM_PAGE_SIZE; |
2158 | store_pages_low = store_mem_low / SM_PAGE_SIZE; | |
090089c4 | 2159 | } |
2160 | ||
b8d8561b | 2161 | int |
0ee4272b | 2162 | urlcmp(const char *url1, const char *url2) |
090089c4 | 2163 | { |
2164 | if (!url1 || !url2) | |
6eb42cae | 2165 | fatal_dump("urlcmp: Got a NULL url pointer."); |
090089c4 | 2166 | return (strcmp(url1, url2)); |
2167 | } | |
2168 | ||
090089c4 | 2169 | /* |
2170 | * This routine is to be called by main loop in main.c. | |
2171 | * It removes expired objects on only one bucket for each time called. | |
2172 | * returns the number of objects removed | |
2173 | * | |
2174 | * This should get called 1/s from main(). | |
2175 | */ | |
48f44632 | 2176 | void |
2177 | storeMaintainSwapSpace(void *unused) | |
090089c4 | 2178 | { |
30a4f2a8 | 2179 | static time_t last_time = 0; |
cb6ae8c3 | 2180 | static int bucket_index = 0; |
090089c4 | 2181 | hash_link *link_ptr = NULL, *next = NULL; |
2182 | StoreEntry *e = NULL; | |
2183 | int rm_obj = 0; | |
58104eab | 2184 | int scan_buckets = 0; |
66cedb85 | 2185 | int scan_obj = 0; |
d3c12084 | 2186 | static struct _bucketOrder *b; |
090089c4 | 2187 | |
48f44632 | 2188 | eventAdd("storeMaintain", storeMaintainSwapSpace, NULL, 1); |
30a4f2a8 | 2189 | /* We can't delete objects while rebuilding swap */ |
311ea387 | 2190 | if (store_rebuilding) |
48f44632 | 2191 | return; |
30a4f2a8 | 2192 | |
090089c4 | 2193 | /* Purges expired objects, check one bucket on each calling */ |
66cedb85 | 2194 | if (squid_curtime - last_time >= store_maintain_rate) { |
58104eab | 2195 | for (;;) { |
2196 | if (scan_obj && scan_buckets >= store_maintain_buckets) | |
2197 | break; | |
2198 | if (++scan_buckets > 100) | |
2199 | break; | |
66cedb85 | 2200 | last_time = squid_curtime; |
cb6ae8c3 | 2201 | if (bucket_index >= store_buckets) { |
2202 | bucket_index = 0; | |
66cedb85 | 2203 | scan_revolutions++; |
a3d5953d | 2204 | debug(51, 1) ("Completed %d full expiration scans of store table\n", |
66cedb85 | 2205 | scan_revolutions); |
d25ad3e1 | 2206 | storeRandomizeBuckets(); |
66cedb85 | 2207 | } |
d3c12084 | 2208 | b = MaintBucketsOrder + bucket_index++; |
2209 | next = hash_get_bucket(store_table, b->bucket); | |
cc61958c | 2210 | while ((link_ptr = next)) { |
66cedb85 | 2211 | scan_obj++; |
2212 | next = link_ptr->next; | |
2213 | e = (StoreEntry *) link_ptr; | |
429fdbec | 2214 | if (!storeCheckExpired(e, 1)) |
66cedb85 | 2215 | continue; |
cc61958c | 2216 | rm_obj += storeRelease(e); |
66cedb85 | 2217 | } |
090089c4 | 2218 | } |
2219 | } | |
a3d5953d | 2220 | debug(51, rm_obj ? 2 : 9) ("Removed %d of %d objects from bucket %d\n", |
d3c12084 | 2221 | rm_obj, scan_obj, (int) b->bucket); |
79b5cc5f | 2222 | /* Scan row of hash table each second and free storage if we're |
2223 | * over the high-water mark */ | |
2224 | storeGetSwapSpace(0); | |
090089c4 | 2225 | } |
2226 | ||
090089c4 | 2227 | |
2228 | /* | |
5608850b | 2229 | * storeWriteCleanLogs |
090089c4 | 2230 | * |
2231 | * Writes a "clean" swap log file from in-memory metadata. | |
2232 | */ | |
b8d8561b | 2233 | int |
e102ebda | 2234 | storeWriteCleanLogs(int reopen) |
090089c4 | 2235 | { |
2236 | StoreEntry *e = NULL; | |
9e6c462c | 2237 | int *fd; |
5608850b | 2238 | char *line; |
090089c4 | 2239 | int n = 0; |
2240 | time_t start, stop, r; | |
10389b74 | 2241 | struct stat sb; |
9e6c462c | 2242 | char **cur; |
2243 | char **new; | |
2244 | char **cln; | |
5608850b | 2245 | int dirn; |
30a4f2a8 | 2246 | if (store_rebuilding) { |
a3d5953d | 2247 | debug(20, 1) ("Not currently OK to rewrite swap log.\n"); |
2248 | debug(20, 1) ("storeWriteCleanLogs: Operation aborted.\n"); | |
090089c4 | 2249 | return 0; |
2250 | } | |
a3d5953d | 2251 | debug(20, 1) ("storeWriteCleanLogs: Starting...\n"); |
97c03d3c | 2252 | start = squid_curtime; |
f1dc9b30 | 2253 | fd = xcalloc(Config.cacheSwap.n_configured, sizeof(int)); |
2254 | cur = xcalloc(Config.cacheSwap.n_configured, sizeof(char *)); | |
2255 | new = xcalloc(Config.cacheSwap.n_configured, sizeof(char *)); | |
2256 | cln = xcalloc(Config.cacheSwap.n_configured, sizeof(char *)); | |
2257 | for (dirn = 0; dirn < Config.cacheSwap.n_configured; dirn++) { | |
3e347513 | 2258 | fd[dirn] = -1; |
2259 | cur[dirn] = xstrdup(storeDirSwapLogFile(dirn, NULL)); | |
2260 | new[dirn] = xstrdup(storeDirSwapLogFile(dirn, ".clean")); | |
2261 | cln[dirn] = xstrdup(storeDirSwapLogFile(dirn, ".last-clean")); | |
2262 | safeunlink(new[dirn], 1); | |
2263 | safeunlink(cln[dirn], 1); | |
f17936ab | 2264 | fd[dirn] = file_open(new[dirn], |
9e4ad609 | 2265 | O_WRONLY | O_CREAT | O_TRUNC, |
2266 | NULL, | |
2267 | NULL); | |
3e347513 | 2268 | if (fd[dirn] < 0) { |
a3d5953d | 2269 | debug(50, 0) ("storeWriteCleanLogs: %s: %s\n", new[dirn], xstrerror()); |
5608850b | 2270 | continue; |
2271 | } | |
4ac29a5b | 2272 | #if HAVE_FCHMOD |
3e347513 | 2273 | if (stat(cur[dirn], &sb) == 0) |
5608850b | 2274 | fchmod(fd[dirn], sb.st_mode); |
4ac29a5b | 2275 | #endif |
5608850b | 2276 | } |
2277 | line = xcalloc(1, 16384); | |
090089c4 | 2278 | for (e = storeGetFirst(); e; e = storeGetNext()) { |
090089c4 | 2279 | if (e->swap_file_number < 0) |
2280 | continue; | |
8350fe9b | 2281 | if (e->swap_status != SWAPOUT_DONE) |
090089c4 | 2282 | continue; |
2283 | if (e->object_len <= 0) | |
2284 | continue; | |
3ba50d75 | 2285 | if (BIT_TEST(e->flag, RELEASE_REQUEST)) |
0a21bd84 | 2286 | continue; |
2287 | if (BIT_TEST(e->flag, KEY_PRIVATE)) | |
2288 | continue; | |
9e975e4e | 2289 | dirn = storeDirNumber(e->swap_file_number); |
2290 | assert(dirn < Config.cacheSwap.n_configured); | |
5608850b | 2291 | if (fd[dirn] < 0) |
2292 | continue; | |
042461c3 | 2293 | snprintf(line, 16384, "%08x %08x %08x %08x %08x %9d %6d %08x %s\n", |
f5469bce | 2294 | (int) e->swap_file_number, |
2295 | (int) e->timestamp, | |
919877b6 | 2296 | (int) e->lastref, |
f5469bce | 2297 | (int) e->expires, |
2298 | (int) e->lastmod, | |
2299 | e->object_len, | |
994bbf93 | 2300 | e->refcount, |
2301 | e->flag, | |
f5469bce | 2302 | e->url); |
5608850b | 2303 | if (write(fd[dirn], line, strlen(line)) < 0) { |
a3d5953d | 2304 | debug(50, 0) ("storeWriteCleanLogs: %s: %s\n", new[dirn], xstrerror()); |
2305 | debug(20, 0) ("storeWriteCleanLogs: Current swap logfile not replaced.\n"); | |
5608850b | 2306 | file_close(fd[dirn]); |
2307 | fd[dirn] = -1; | |
2308 | safeunlink(cln[dirn], 0); | |
2309 | continue; | |
30a4f2a8 | 2310 | } |
5608850b | 2311 | if ((++n & 0x3FFF) == 0) { |
a3d5953d | 2312 | debug(20, 1) (" %7d lines written so far.\n", n); |
090089c4 | 2313 | } |
2314 | } | |
5608850b | 2315 | safe_free(line); |
f1dc9b30 | 2316 | for (dirn = 0; dirn < Config.cacheSwap.n_configured; dirn++) { |
3e347513 | 2317 | file_close(fd[dirn]); |
2318 | fd[dirn] = -1; | |
2319 | if (rename(new[dirn], cur[dirn]) < 0) { | |
a3d5953d | 2320 | debug(50, 0) ("storeWriteCleanLogs: rename failed: %s\n", |
3e347513 | 2321 | xstrerror()); |
2322 | } | |
5608850b | 2323 | } |
2324 | storeDirCloseSwapLogs(); | |
e102ebda | 2325 | if (reopen) |
2326 | storeDirOpenSwapLogs(); | |
97c03d3c | 2327 | stop = squid_curtime; |
090089c4 | 2328 | r = stop - start; |
a3d5953d | 2329 | debug(20, 1) (" Finished. Wrote %d lines.\n", n); |
2330 | debug(20, 1) (" Took %d seconds (%6.1lf lines/sec).\n", | |
090089c4 | 2331 | r > 0 ? r : 0, (double) n / (r > 0 ? r : 1)); |
0a0bf5db | 2332 | /* touch a timestamp file if we're not still validating */ |
f1dc9b30 | 2333 | for (dirn = 0; dirn < Config.cacheSwap.n_configured; dirn++) { |
f182d1c5 | 2334 | if (!store_rebuilding) |
f17936ab | 2335 | file_close(file_open(cln[dirn], |
3e347513 | 2336 | O_WRONLY | O_CREAT | O_TRUNC, NULL, NULL)); |
5608850b | 2337 | safe_free(cur[dirn]); |
2338 | safe_free(new[dirn]); | |
2339 | safe_free(cln[dirn]); | |
0a0bf5db | 2340 | } |
9e6c462c | 2341 | safe_free(cur); |
2342 | safe_free(new); | |
2343 | safe_free(cln); | |
2344 | safe_free(fd); | |
090089c4 | 2345 | return n; |
2346 | } | |
2347 | ||
b8d8561b | 2348 | int |
fe4e214f | 2349 | storePendingNClients(const StoreEntry * e) |
090089c4 | 2350 | { |
2351 | int npend = 0; | |
52d4522b | 2352 | MemObject *mem = e->mem_obj; |
090089c4 | 2353 | int i; |
52d4522b | 2354 | if (mem == NULL) |
090089c4 | 2355 | return 0; |
b012353a | 2356 | for (i = 0; i < mem->nclients; i++) { |
382d851a | 2357 | if (mem->clients[i].callback_data == NULL) |
52d4522b | 2358 | continue; |
b012353a | 2359 | npend++; |
090089c4 | 2360 | } |
2361 | return npend; | |
2362 | } | |
d8b45066 | 2363 | |
b8d8561b | 2364 | void |
2365 | storeRotateLog(void) | |
d8b45066 | 2366 | { |
2367 | char *fname = NULL; | |
2368 | int i; | |
95d659f0 | 2369 | LOCAL_ARRAY(char, from, MAXPATHLEN); |
2370 | LOCAL_ARRAY(char, to, MAXPATHLEN); | |
88738790 | 2371 | #ifdef S_ISREG |
2edc2504 | 2372 | struct stat sb; |
88738790 | 2373 | #endif |
d8b45066 | 2374 | |
234967c9 | 2375 | if (storelog_fd > -1) { |
2376 | file_close(storelog_fd); | |
2377 | storelog_fd = -1; | |
2378 | } | |
b6f794d6 | 2379 | if ((fname = Config.Log.store) == NULL) |
2d25dc1a | 2380 | return; |
234967c9 | 2381 | if (strcmp(fname, "none") == 0) |
2382 | return; | |
71d95578 | 2383 | #ifdef S_ISREG |
2edc2504 | 2384 | if (stat(fname, &sb) == 0) |
2385 | if (S_ISREG(sb.st_mode) == 0) | |
2386 | return; | |
71d95578 | 2387 | #endif |
234967c9 | 2388 | |
a3d5953d | 2389 | debug(20, 1) ("storeRotateLog: Rotating.\n"); |
d8b45066 | 2390 | |
2391 | /* Rotate numbers 0 through N up one */ | |
b6f794d6 | 2392 | for (i = Config.Log.rotateNumber; i > 1;) { |
2d25dc1a | 2393 | i--; |
8350fe9b | 2394 | snprintf(from, MAXPATHLEN, "%s.%d", fname, i - 1); |
042461c3 | 2395 | snprintf(to, MAXPATHLEN, "%s.%d", fname, i); |
2d25dc1a | 2396 | rename(from, to); |
d8b45066 | 2397 | } |
2398 | /* Rotate the current log to .0 */ | |
b6f794d6 | 2399 | if (Config.Log.rotateNumber > 0) { |
8350fe9b | 2400 | snprintf(to, MAXPATHLEN, "%s.%d", fname, 0); |
2d25dc1a | 2401 | rename(fname, to); |
d8b45066 | 2402 | } |
f17936ab | 2403 | storelog_fd = file_open(fname, O_WRONLY | O_CREAT, NULL, NULL); |
234967c9 | 2404 | if (storelog_fd < 0) { |
a3d5953d | 2405 | debug(50, 0) ("storeRotateLog: %s: %s\n", fname, xstrerror()); |
2406 | debug(20, 1) ("Store logging disabled\n"); | |
234967c9 | 2407 | } |
d8b45066 | 2408 | } |
30a4f2a8 | 2409 | |
56f29785 | 2410 | static int |
8350fe9b | 2411 | storeKeepInMemory(const StoreEntry * e) |
56f29785 | 2412 | { |
8350fe9b | 2413 | MemObject *mem = e->mem_obj; |
2414 | if (mem == NULL) | |
56f29785 | 2415 | return 0; |
8350fe9b | 2416 | if (mem->data == NULL) |
2417 | return 0; | |
2418 | return mem->inmem_lo == 0; | |
56f29785 | 2419 | } |
2420 | ||
b8d8561b | 2421 | static int |
429fdbec | 2422 | storeCheckExpired(const StoreEntry * e, int check_lru_age) |
620da955 | 2423 | { |
429fdbec | 2424 | time_t max_age; |
620da955 | 2425 | if (storeEntryLocked(e)) |
2426 | return 0; | |
7cc1830f | 2427 | if (BIT_TEST(e->flag, ENTRY_NEGCACHED) && squid_curtime >= e->expires) |
2428 | return 1; | |
429fdbec | 2429 | if (!check_lru_age) |
2430 | return 0; | |
2431 | if ((max_age = storeExpiredReferenceAge()) <= 0) | |
db9ccd5a | 2432 | return 0; |
2433 | if (squid_curtime - e->lastref > max_age) | |
66cedb85 | 2434 | return 1; |
66cedb85 | 2435 | return 0; |
620da955 | 2436 | } |
2437 | ||
db9ccd5a | 2438 | /* |
2439 | * storeExpiredReferenceAge | |
2440 | * | |
429fdbec | 2441 | * The LRU age is scaled exponentially between 1 minute and |
2442 | * Config.referenceAge , when store_swap_low < store_swap_size < | |
2443 | * store_swap_high. This keeps store_swap_size within the low and high | |
2444 | * water marks. If the cache is very busy then store_swap_size stays | |
2445 | * closer to the low water mark, if it is not busy, then it will stay | |
2446 | * near the high water mark. The LRU age value can be examined on the | |
2447 | * cachemgr 'info' page. | |
fbdfb3cf | 2448 | */ |
35feb4aa | 2449 | time_t |
58104eab | 2450 | storeExpiredReferenceAge(void) |
2451 | { | |
58104eab | 2452 | double x; |
2453 | double z; | |
2454 | time_t age; | |
429fdbec | 2455 | if (Config.referenceAge == 0) |
2456 | return 0; | |
2457 | x = (double) (store_swap_high - store_swap_size) / (store_swap_high - store_swap_low); | |
2458 | x = x < 0.0 ? 0.0 : x > 1.0 ? 1.0 : x; | |
2459 | z = pow((double) Config.referenceAge, x); | |
58104eab | 2460 | age = (time_t) (z * 60.0); |
2461 | if (age < 60) | |
2462 | age = 60; | |
2463 | else if (age > 31536000) | |
2464 | age = 31536000; | |
2465 | return age; | |
0a362c34 | 2466 | } |
2edc2504 | 2467 | |
b8d8561b | 2468 | void |
2469 | storeCloseLog(void) | |
457bedbd | 2470 | { |
15be2259 | 2471 | if (storelog_fd >= 0) |
40fcf5d4 | 2472 | file_close(storelog_fd); |
457bedbd | 2473 | } |
79b5cc5f | 2474 | |
b8d8561b | 2475 | void |
2476 | storeNegativeCache(StoreEntry * e) | |
79b5cc5f | 2477 | { |
ff5731b1 | 2478 | e->expires = squid_curtime + Config.negativeTtl; |
2479 | BIT_SET(e->flag, ENTRY_NEGCACHED); | |
79b5cc5f | 2480 | } |
0a21bd84 | 2481 | |
2482 | void | |
2483 | storeFreeMemory(void) | |
2484 | { | |
2485 | StoreEntry *e; | |
df92bd2a | 2486 | StoreEntry **list; |
2487 | int i = 0; | |
ea6b05e3 | 2488 | int j; |
56e15c50 | 2489 | list = xcalloc(meta_data.store_entries, sizeof(StoreEntry *)); |
df92bd2a | 2490 | e = (StoreEntry *) hash_first(store_table); |
2491 | while (e && i < meta_data.store_entries) { | |
2492 | *(list + i) = e; | |
2493 | i++; | |
2494 | e = (StoreEntry *) hash_next(store_table); | |
2495 | } | |
ea6b05e3 | 2496 | for (j = 0; j < i; j++) |
2497 | destroy_StoreEntry(*(list + j)); | |
df92bd2a | 2498 | xfree(list); |
0a21bd84 | 2499 | hashFreeMemory(store_table); |
afe95a7e | 2500 | store_table = NULL; |
e161be2e | 2501 | safe_free(MaintBucketsOrder); |
0a21bd84 | 2502 | } |
a7e59001 | 2503 | |
2504 | int | |
2505 | expiresMoreThan(time_t expires, time_t when) | |
2506 | { | |
48f44632 | 2507 | if (expires < 0) /* No Expires given */ |
2508 | return 1; | |
2509 | return (expires > (squid_curtime + when)); | |
a7e59001 | 2510 | } |
fe54d06d | 2511 | |
2512 | int | |
2513 | storeEntryValidToSend(StoreEntry * e) | |
2514 | { | |
2515 | if (BIT_TEST(e->flag, RELEASE_REQUEST)) | |
2516 | return 0; | |
2517 | if (BIT_TEST(e->flag, ENTRY_NEGCACHED)) | |
44f78c24 | 2518 | if (e->expires <= squid_curtime) |
fe54d06d | 2519 | return 0; |
2520 | if (e->store_status == STORE_ABORTED) | |
2521 | return 0; | |
2522 | return 1; | |
2523 | } | |
62663274 | 2524 | |
ca98227c | 2525 | void |
2526 | storeTimestampsSet(StoreEntry * entry) | |
2527 | { | |
2528 | time_t served_date = -1; | |
2529 | struct _http_reply *reply = entry->mem_obj->reply; | |
2530 | served_date = reply->date > -1 ? reply->date : squid_curtime; | |
2531 | entry->expires = reply->expires; | |
2532 | if (reply->last_modified > -1) | |
2533 | entry->lastmod = reply->last_modified; | |
2534 | else | |
2535 | entry->lastmod = served_date; | |
2536 | entry->timestamp = served_date; | |
2537 | } | |
429fdbec | 2538 | |
2539 | #define FILENO_STACK_SIZE 128 | |
2540 | static int fileno_stack[FILENO_STACK_SIZE]; | |
429fdbec | 2541 | |
2542 | static int | |
2543 | storeGetUnusedFileno(void) | |
2544 | { | |
9e4ad609 | 2545 | int fn; |
429fdbec | 2546 | if (fileno_stack_count < 1) |
2547 | return -1; | |
9e4ad609 | 2548 | fn = fileno_stack[--fileno_stack_count]; |
2549 | storeDirMapBitSet(fn); | |
2550 | return fn; | |
429fdbec | 2551 | } |
2552 | ||
2553 | static void | |
2554 | storePutUnusedFileno(int fileno) | |
2555 | { | |
bf33177b | 2556 | assert(storeDirMapBitTest(fileno)); |
9e4ad609 | 2557 | storeDirMapBitReset(fileno); |
429fdbec | 2558 | if (fileno_stack_count < FILENO_STACK_SIZE) |
2559 | fileno_stack[fileno_stack_count++] = fileno; | |
2560 | else | |
2561 | unlinkdUnlink(storeSwapFullPath(fileno, NULL)); | |
2562 | } | |
bfcaf585 | 2563 | |
2564 | void | |
2565 | storeRegisterAbort(StoreEntry * e, STABH * cb, void *data) | |
2566 | { | |
2567 | MemObject *mem = e->mem_obj; | |
2568 | assert(mem); | |
2569 | assert(mem->abort.callback == NULL); | |
2570 | mem->abort.callback = cb; | |
2571 | mem->abort.data = data; | |
2572 | } | |
2573 | ||
2574 | void | |
2575 | storeUnregisterAbort(StoreEntry * e) | |
2576 | { | |
2577 | MemObject *mem = e->mem_obj; | |
2578 | assert(mem); | |
2579 | mem->abort.callback = NULL; | |
2580 | } | |
88738790 | 2581 | |
2582 | void | |
2583 | storeMemObjectDump(MemObject * mem) | |
2584 | { | |
2585 | debug(20, 1) ("MemObject->data: %p\n", | |
2586 | mem->data); | |
2587 | debug(20, 1) ("MemObject->e_swap_buf: %p %s\n", | |
2588 | mem->e_swap_buf, | |
2589 | checkNullString(mem->e_swap_buf)); | |
88738790 | 2590 | debug(20, 1) ("MemObject->start_ping: %d.%06d\n", |
2591 | mem->start_ping.tv_sec, | |
2592 | mem->start_ping.tv_usec); | |
2593 | debug(20, 1) ("MemObject->e_swap_buf_len: %d\n", | |
2594 | mem->e_swap_buf_len); | |
2595 | debug(20, 1) ("MemObject->pending_list_size: %d\n", | |
2596 | mem->pending_list_size); | |
8350fe9b | 2597 | debug(20, 1) ("MemObject->inmem_hi: %d\n", |
2598 | mem->inmem_hi); | |
2599 | debug(20, 1) ("MemObject->inmem_lo: %d\n", | |
2600 | mem->inmem_lo); | |
88738790 | 2601 | debug(20, 1) ("MemObject->clients: %p\n", |
2602 | mem->clients); | |
2603 | debug(20, 1) ("MemObject->nclients: %d\n", | |
2604 | mem->nclients); | |
8350fe9b | 2605 | debug(20, 1) ("MemObject->swapout.fd: %d\n", |
2606 | mem->swapout.fd); | |
88738790 | 2607 | debug(20, 1) ("MemObject->reply: %p\n", |
2608 | mem->reply); | |
2609 | debug(20, 1) ("MemObject->request: %p\n", | |
2610 | mem->request); | |
2611 | debug(20, 1) ("MemObject->log_url: %p %s\n", | |
2612 | mem->log_url, | |
2613 | checkNullString(mem->log_url)); | |
2614 | } | |
8350fe9b | 2615 | |
2616 | static void | |
2617 | storeInMemAdd(StoreEntry * e) | |
2618 | { | |
2619 | struct _mem_entry *m = xmalloc(sizeof(struct _mem_entry)); | |
8c123b71 | 2620 | debug(20, 3) ("storeInMemAdd: %s\n", e->url); |
88eb862c | 2621 | assert(e->mem_obj->inmem_lo == 0); |
8350fe9b | 2622 | m->e = e; |
2623 | m->prev = NULL; | |
2624 | m->next = inmem_head; | |
2625 | if (inmem_head) | |
2626 | inmem_head->prev = m; | |
2627 | inmem_head = m; | |
2628 | if (inmem_tail == NULL) | |
2629 | inmem_tail = m; | |
2630 | meta_data.hot_vm++; | |
2631 | } | |
2632 | ||
2633 | static void | |
2634 | storeInMemDelete(StoreEntry * e) | |
2635 | { | |
2636 | struct _mem_entry *m; | |
2637 | for (m = inmem_tail; m; m = m->prev) { | |
2638 | if (m->e == e) | |
2639 | break; | |
2640 | } | |
2641 | assert(m != NULL); | |
8c123b71 | 2642 | debug(20, 3) ("storeInMemDelete: %s\n", e->url); |
8350fe9b | 2643 | if (m->next) |
2644 | m->next->prev = m->prev; | |
2645 | if (m->prev) | |
2646 | m->prev->next = m->next; | |
2647 | if (m == inmem_head) | |
2648 | inmem_head = m->next; | |
2649 | if (m == inmem_tail) | |
2650 | inmem_tail = m->prev; | |
2651 | meta_data.hot_vm--; | |
2652 | } | |
2653 | ||
2654 | /* NOTE, this function assumes only two mem states */ | |
2655 | static void | |
2656 | storeSetMemStatus(StoreEntry * e, int new_status) | |
2657 | { | |
2658 | if (new_status == e->mem_status) | |
2659 | return; | |
2660 | if (new_status == IN_MEMORY) | |
2661 | storeInMemAdd(e); | |
2662 | else | |
2663 | storeInMemDelete(e); | |
2664 | e->mem_status = new_status; | |
2665 | } |