]>
Commit | Line | Data |
---|---|---|
a4b8110e | 1 | |
cd748f27 | 2 | /* |
ddd1cefa | 3 | * $Id: store_dir_coss.cc,v 1.61 2005/07/03 20:41:46 serassio Exp $ |
c8f4eac4 | 4 | * vim: set et : |
cd748f27 | 5 | * |
17bb3486 | 6 | * DEBUG: section 47 Store COSS Directory Routines |
cd748f27 | 7 | * AUTHOR: Eric Stern |
8 | * | |
2b6662ba | 9 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
cd748f27 | 10 | * ---------------------------------------------------------- |
11 | * | |
2b6662ba | 12 | * Squid is the result of efforts by numerous individuals from |
13 | * the Internet community; see the CONTRIBUTORS file for full | |
14 | * details. Many organizations have provided support for Squid's | |
15 | * development; see the SPONSORS file for full details. Squid is | |
16 | * Copyrighted (C) 2001 by the Regents of the University of | |
17 | * California; see the COPYRIGHT file for full details. Squid | |
18 | * incorporates software developed and/or copyrighted by other | |
19 | * sources; see the CREDITS file for full details. | |
cd748f27 | 20 | * |
21 | * This program is free software; you can redistribute it and/or modify | |
22 | * it under the terms of the GNU General Public License as published by | |
23 | * the Free Software Foundation; either version 2 of the License, or | |
24 | * (at your option) any later version. | |
25 | * | |
26 | * This program is distributed in the hope that it will be useful, | |
27 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
28 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
29 | * GNU General Public License for more details. | |
30 | * | |
31 | * You should have received a copy of the GNU General Public License | |
32 | * along with this program; if not, write to the Free Software | |
33 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
34 | * | |
35 | */ | |
36 | ||
37 | #include "squid.h" | |
b9ae18aa | 38 | #include "CossSwapDir.h" |
e6ccf245 | 39 | #include "Store.h" |
cd748f27 | 40 | |
41 | #include "store_coss.h" | |
528b2c61 | 42 | #include "fde.h" |
d3b3ab85 | 43 | #include "SwapDir.h" |
51ee7c82 | 44 | #include "StoreSwapLogData.h" |
b9ae18aa | 45 | #include "DiskIO/DiskIOModule.h" |
46 | #include "DiskIO/DiskIOStrategy.h" | |
47 | #include "DiskIO/ReadRequest.h" | |
48 | #include "ConfigOption.h" | |
49 | #include "StoreFScoss.h" | |
c8f4eac4 | 50 | #include "Parsing.h" |
cd748f27 | 51 | |
52 | #define STORE_META_BUFSZ 4096 | |
53 | ||
cd748f27 | 54 | int n_coss_dirs = 0; |
55 | /* static int last_coss_pick_index = -1; */ | |
b001e822 | 56 | MemAllocatorProxy *coss_index_pool = NULL; |
cd748f27 | 57 | |
58 | typedef struct _RebuildState RebuildState; | |
62e76326 | 59 | |
60 | struct _RebuildState | |
61 | { | |
d3b3ab85 | 62 | CossSwapDir *sd; |
cd748f27 | 63 | int n_read; |
64 | FILE *log; | |
65 | int speed; | |
62e76326 | 66 | |
67 | struct | |
68 | { | |
69 | ||
70 | unsigned int clean: | |
71 | 1; | |
72 | } | |
73 | ||
74 | flags; | |
75 | ||
cd748f27 | 76 | struct _store_rebuild_data counts; |
77 | }; | |
78 | ||
79 | static char *storeCossDirSwapLogFile(SwapDir *, const char *); | |
80 | static EVH storeCossRebuildFromSwapLog; | |
d3b3ab85 | 81 | static StoreEntry *storeCossAddDiskRestore(CossSwapDir * SD, const cache_key * key, |
62e76326 | 82 | int file_number, |
83 | size_t swap_file_sz, | |
84 | time_t expires, | |
85 | time_t timestamp, | |
86 | time_t lastref, | |
87 | time_t lastmod, | |
88 | u_int32_t refcount, | |
89 | u_int16_t flags, | |
90 | int clean); | |
d3b3ab85 | 91 | static void storeCossDirRebuild(CossSwapDir * sd); |
92 | static void storeCossDirCloseTmpSwapLog(CossSwapDir * sd); | |
93 | static FILE *storeCossDirOpenTmpSwapLog(CossSwapDir *, int *, int *); | |
cd748f27 | 94 | |
95 | static char * | |
96 | storeCossDirSwapLogFile(SwapDir * sd, const char *ext) | |
97 | { | |
98 | LOCAL_ARRAY(char, path, SQUID_MAXPATHLEN); | |
99 | LOCAL_ARRAY(char, pathtmp, SQUID_MAXPATHLEN); | |
100 | LOCAL_ARRAY(char, digit, 32); | |
101 | char *pathtmp2; | |
62e76326 | 102 | |
cd748f27 | 103 | if (Config.Log.swap) { |
62e76326 | 104 | xstrncpy(pathtmp, sd->path, SQUID_MAXPATHLEN - 64); |
105 | pathtmp2 = pathtmp; | |
106 | ||
107 | while ((pathtmp2 = strchr(pathtmp2, '/')) != NULL) | |
108 | *pathtmp2 = '.'; | |
109 | ||
110 | while (strlen(pathtmp) && pathtmp[strlen(pathtmp) - 1] == '.') | |
111 | pathtmp[strlen(pathtmp) - 1] = '\0'; | |
112 | ||
113 | for (pathtmp2 = pathtmp; *pathtmp2 == '.'; pathtmp2++) | |
114 | ||
115 | ; | |
116 | snprintf(path, SQUID_MAXPATHLEN - 64, Config.Log.swap, pathtmp2); | |
117 | ||
118 | if (strncmp(path, Config.Log.swap, SQUID_MAXPATHLEN - 64) == 0) { | |
119 | strcat(path, "."); | |
120 | snprintf(digit, 32, "%02d", sd->index); | |
121 | strncat(path, digit, 3); | |
122 | } | |
cd748f27 | 123 | } else { |
62e76326 | 124 | xstrncpy(path, sd->path, SQUID_MAXPATHLEN - 64); |
125 | strcat(path, "/swap.state"); | |
cd748f27 | 126 | } |
62e76326 | 127 | |
cd748f27 | 128 | if (ext) |
62e76326 | 129 | strncat(path, ext, 16); |
130 | ||
cd748f27 | 131 | return path; |
132 | } | |
133 | ||
d3b3ab85 | 134 | void |
135 | CossSwapDir::openLog() | |
cd748f27 | 136 | { |
d3b3ab85 | 137 | char *logPath; |
138 | logPath = storeCossDirSwapLogFile(this, NULL); | |
139 | swaplog_fd = file_open(logPath, O_WRONLY | O_CREAT | O_BINARY); | |
62e76326 | 140 | |
d3b3ab85 | 141 | if (swaplog_fd < 0) { |
62e76326 | 142 | debug(47, 1) ("%s: %s\n", logPath, xstrerror()); |
143 | fatal("storeCossDirOpenSwapLog: Failed to open swap log."); | |
cd748f27 | 144 | } |
62e76326 | 145 | |
d3b3ab85 | 146 | debug(47, 3) ("Cache COSS Dir #%d log opened on FD %d\n", index, swaplog_fd); |
cd748f27 | 147 | } |
148 | ||
d3b3ab85 | 149 | void |
150 | CossSwapDir::closeLog() | |
cd748f27 | 151 | { |
d3b3ab85 | 152 | if (swaplog_fd < 0) /* not open */ |
62e76326 | 153 | return; |
154 | ||
d3b3ab85 | 155 | file_close(swaplog_fd); |
62e76326 | 156 | |
99cce4cb | 157 | debug(47, 3) ("Cache COSS Dir #%d log closed on FD %d\n", |
62e76326 | 158 | index, swaplog_fd); |
159 | ||
d3b3ab85 | 160 | swaplog_fd = -1; |
cd748f27 | 161 | } |
162 | ||
d3b3ab85 | 163 | void |
b9ae18aa | 164 | CossSwapDir::ioCompletedNotification() |
d3b3ab85 | 165 | { |
b9ae18aa | 166 | if (theFile->error()) { |
62e76326 | 167 | debug(47, 1) ("%s: %s\n", path, xstrerror()); |
1a224843 | 168 | fatal("storeCossDirInit: Failed to open a COSS file."); |
697c29aa | 169 | } |
b9ae18aa | 170 | } |
171 | ||
172 | void | |
173 | CossSwapDir::closeCompleted() | |
174 | { | |
175 | theFile = NULL; | |
176 | } | |
177 | ||
178 | void | |
179 | CossSwapDir::readCompleted(const char *buf, int len, int errflag, RefCount<ReadRequest> aRequest) | |
180 | { | |
181 | CossRead* cossRead= dynamic_cast<CossRead *>(aRequest.getRaw()); | |
182 | assert (cossRead); | |
183 | StoreIOState::Pointer sio = cossRead->sio; | |
184 | void *cbdata; | |
185 | STRCB *callback = sio->read.callback; | |
186 | char *p; | |
187 | CossState *cstate = dynamic_cast<CossState *>(sio.getRaw()); | |
188 | ssize_t rlen; | |
189 | ||
190 | debug(79, 3) ("storeCossReadDone: fileno %d, len %d\n", | |
191 | sio->swap_filen, len); | |
192 | cstate->flags.reading = 0; | |
193 | ||
194 | if (errflag) { | |
195 | StoreFScoss::GetInstance().stats.read.fail++; | |
196 | ||
197 | if (errflag > 0) { | |
198 | errno = errflag; | |
199 | debug(79, 1) ("storeCossReadDone: error: %s\n", xstrerror()); | |
200 | } else { | |
201 | debug(79, 1) ("storeCossReadDone: got failure (%d)\n", errflag); | |
202 | } | |
203 | ||
204 | rlen = -1; | |
205 | } else { | |
206 | StoreFScoss::GetInstance().stats.read.success++; | |
207 | ||
208 | if (cstate->readbuffer == NULL) { | |
209 | cstate->readbuffer = (char *)xmalloc(cstate->st_size); | |
210 | p = storeCossMemPointerFromDiskOffset(storeCossFilenoToDiskOffset(sio->swap_filen), | |
211 | NULL); | |
212 | xmemcpy(cstate->readbuffer, p, cstate->st_size); | |
213 | } | |
214 | ||
215 | sio->offset_ += len; | |
216 | xmemcpy(cstate->requestbuf, &cstate->readbuffer[cstate->requestoffset], | |
217 | cstate->requestlen); | |
218 | rlen = (size_t) cstate->requestlen; | |
219 | } | |
220 | ||
221 | assert(callback); | |
222 | sio->read.callback = NULL; | |
223 | ||
224 | if (cbdataReferenceValidDone(sio->read.callback_data, &cbdata)) | |
225 | callback(cbdata, cstate->requestbuf, rlen); | |
226 | } | |
227 | ||
228 | void | |
229 | CossSwapDir::writeCompleted(int errflag, size_t len, RefCount<WriteRequest> writeRequest) | |
230 | { | |
231 | CossWrite* cossWrite= dynamic_cast<CossWrite *>(writeRequest.getRaw()); | |
232 | assert (cossWrite); | |
233 | ||
234 | debug(79, 3) ("storeCossWriteMemBufDone: buf %p, len %ld\n", cossWrite->membuf, (long int) len); | |
235 | ||
236 | ||
237 | if (errflag) { | |
238 | StoreFScoss::GetInstance().stats.stripe_write.fail++; | |
239 | debug(79, 1) ("storeCossWriteMemBufDone: got failure (%d)\n", errflag); | |
ddd1cefa | 240 | debugs(79, 1, "size=" << cossWrite->membuf->diskend - cossWrite->membuf->diskstart); |
b9ae18aa | 241 | } else { |
242 | StoreFScoss::GetInstance().stats.stripe_write.success++; | |
243 | } | |
244 | ||
245 | ||
246 | dlinkDelete(&cossWrite->membuf->node, &membufs); | |
247 | cbdataFree(cossWrite->membuf); | |
248 | StoreFScoss::GetInstance().stats.stripes--; | |
249 | } | |
250 | ||
251 | void | |
252 | CossSwapDir::changeIO(DiskIOModule *module) | |
253 | { | |
254 | DiskIOStrategy *anIO = module->createStrategy(); | |
255 | safe_free(ioModule); | |
256 | ioModule = xstrdup(module->type()); | |
257 | ||
258 | delete io; | |
259 | io = anIO; | |
260 | /* Change the IO Options */ | |
261 | ||
e2a8733e | 262 | if (currentIOOptions == NULL) |
263 | currentIOOptions = new ConfigOptionVector(); | |
264 | ||
b9ae18aa | 265 | if (currentIOOptions->options.size() > 3) |
266 | delete currentIOOptions->options.pop_back(); | |
267 | ||
268 | /* TODO: factor out these 4 lines */ | |
269 | ConfigOption *ioOptions = NULL; | |
270 | ||
271 | if (io) | |
272 | ioOptions = io->getOptionTree(); | |
273 | ||
274 | if (ioOptions) | |
275 | currentIOOptions->options.push_back(ioOptions); | |
276 | } | |
277 | ||
278 | bool | |
279 | CossSwapDir::optionIOParse(char const *option, const char *value, int reconfiguring) | |
280 | { | |
281 | if (strcmp(option, "IOEngine") != 0) | |
282 | return false; | |
283 | ||
284 | if (reconfiguring) | |
285 | /* silently ignore this */ | |
286 | return true; | |
287 | ||
288 | if (!value) | |
289 | self_destruct(); | |
290 | ||
291 | DiskIOModule *module = DiskIOModule::Find(value); | |
292 | ||
293 | if (!module) | |
294 | self_destruct(); | |
295 | ||
296 | changeIO(module); | |
297 | ||
298 | return true; | |
299 | } | |
300 | ||
301 | void | |
302 | CossSwapDir::optionIODump(StoreEntry * e) const | |
303 | { | |
304 | storeAppendPrintf(e, " IOEngine=%s", ioModule); | |
305 | } | |
306 | ||
307 | ConfigOption * | |
308 | CossSwapDir::getOptionTree() const | |
309 | { | |
310 | ConfigOption *parentResult = SwapDir::getOptionTree(); | |
311 | ||
e2a8733e | 312 | if (currentIOOptions == NULL) |
313 | currentIOOptions = new ConfigOptionVector(); | |
314 | ||
b9ae18aa | 315 | currentIOOptions->options.push_back(parentResult); |
e2a8733e | 316 | |
b9ae18aa | 317 | currentIOOptions->options.push_back(new ConfigOptionAdapter<CossSwapDir>(*const_cast<CossSwapDir *>(this), &CossSwapDir::optionIOParse, &CossSwapDir::optionIODump)); |
e2a8733e | 318 | |
b9ae18aa | 319 | currentIOOptions->options.push_back( |
320 | new ConfigOptionAdapter<CossSwapDir>(*const_cast<CossSwapDir *>(this), | |
321 | &CossSwapDir::optionBlockSizeParse, | |
322 | &CossSwapDir::optionBlockSizeDump)); | |
323 | ||
324 | ||
325 | ConfigOption *ioOptions = NULL; | |
326 | ||
327 | if (io) | |
328 | ioOptions = io->getOptionTree(); | |
329 | ||
330 | if (ioOptions) | |
331 | currentIOOptions->options.push_back(ioOptions); | |
332 | ||
e2a8733e | 333 | ConfigOption* result = currentIOOptions; |
334 | ||
335 | currentIOOptions = NULL; | |
336 | ||
337 | return result; | |
b9ae18aa | 338 | } |
339 | ||
340 | void | |
341 | CossSwapDir::init() | |
342 | { | |
343 | /* FIXME: SwapDirs aren't refcounted. We call IORequestor calls, which | |
344 | * are refcounted. SO, we up our count once to avoid implicit delete's. | |
345 | */ | |
346 | RefCountReference(); | |
347 | io->init(); | |
348 | openLog(); | |
349 | storeCossDirRebuild(this); | |
c8f4eac4 | 350 | theFile = io->newFile(stripePath()); |
b9ae18aa | 351 | theFile->open(O_RDWR | O_CREAT, 0644, this); |
62e76326 | 352 | |
b9ae18aa | 353 | ++n_coss_dirs; |
1a224843 | 354 | /* |
355 | * fs.blksize is normally determined by calling statvfs() etc, | |
356 | * but we just set it here. It is used in accounting the | |
357 | * total store size, and is reported in cachemgr 'storedir' | |
358 | * page. | |
359 | */ | |
360 | fs.blksize = 1 << blksz_bits; | |
cd748f27 | 361 | } |
362 | ||
6a566b9c | 363 | void |
d3b3ab85 | 364 | storeCossRemove(CossSwapDir * sd, StoreEntry * e) |
6a566b9c | 365 | { |
e6ccf245 | 366 | CossIndexNode *coss_node = (CossIndexNode *)e->repl.data; |
6a566b9c | 367 | e->repl.data = NULL; |
d3b3ab85 | 368 | dlinkDelete(&coss_node->node, &sd->cossindex); |
b001e822 | 369 | coss_index_pool->free(coss_node); |
d3b3ab85 | 370 | sd->count -= 1; |
6a566b9c | 371 | } |
372 | ||
373 | void | |
d3b3ab85 | 374 | storeCossAdd(CossSwapDir * sd, StoreEntry * e) |
6a566b9c | 375 | { |
b001e822 | 376 | CossIndexNode *coss_node = (CossIndexNode *)coss_index_pool->alloc(); |
6a566b9c | 377 | assert(!e->repl.data); |
378 | e->repl.data = coss_node; | |
d3b3ab85 | 379 | dlinkAdd(e, &coss_node->node, &sd->cossindex); |
380 | sd->count += 1; | |
6a566b9c | 381 | } |
382 | ||
383 | static void | |
384 | storeCossRebuildComplete(void *data) | |
385 | { | |
e6ccf245 | 386 | RebuildState *rb = (RebuildState *)data; |
d3b3ab85 | 387 | CossSwapDir *sd = rb->sd; |
b9ae18aa | 388 | sd->startMembuf(); |
6a566b9c | 389 | store_dirs_rebuilding--; |
390 | storeCossDirCloseTmpSwapLog(rb->sd); | |
391 | storeRebuildComplete(&rb->counts); | |
392 | cbdataFree(rb); | |
393 | } | |
394 | ||
cd748f27 | 395 | static void |
396 | storeCossRebuildFromSwapLog(void *data) | |
397 | { | |
e6ccf245 | 398 | RebuildState *rb = (RebuildState *)data; |
cd748f27 | 399 | StoreEntry *e = NULL; |
51ee7c82 | 400 | StoreSwapLogData s; |
401 | size_t ss = sizeof(StoreSwapLogData); | |
cd748f27 | 402 | double x; |
403 | assert(rb != NULL); | |
404 | /* load a number of objects per invocation */ | |
62e76326 | 405 | |
d3b3ab85 | 406 | for (int aCount = 0; aCount < rb->speed; aCount++) { |
62e76326 | 407 | if (fread(&s, ss, 1, rb->log) != 1) { |
408 | debug(47, 1) ("Done reading %s swaplog (%d entries)\n", | |
409 | rb->sd->path, rb->n_read); | |
410 | fclose(rb->log); | |
411 | rb->log = NULL; | |
412 | storeCossRebuildComplete(rb); | |
413 | return; | |
414 | } | |
415 | ||
416 | rb->n_read++; | |
417 | ||
418 | if (s.op <= SWAP_LOG_NOP) | |
419 | continue; | |
420 | ||
421 | if (s.op >= SWAP_LOG_MAX) | |
422 | continue; | |
423 | ||
424 | debug(47, 3) ("storeCossRebuildFromSwapLog: %s %s %08X\n", | |
425 | swap_log_op_str[(int) s.op], | |
426 | storeKeyText(s.key), | |
427 | s.swap_filen); | |
428 | ||
429 | if (s.op == SWAP_LOG_ADD) { | |
430 | (void) 0; | |
431 | } else if (s.op == SWAP_LOG_DEL) { | |
432 | /* Delete unless we already have a newer copy */ | |
433 | ||
c8f4eac4 | 434 | if ((e = rb->sd->get |
435 | (s.key)) != NULL && s.lastref > e->lastref) { | |
62e76326 | 436 | /* |
437 | * Make sure we don't unlink the file, it might be | |
438 | * in use by a subsequent entry. Also note that | |
439 | * we don't have to subtract from store_swap_size | |
440 | * because adding to store_swap_size happens in | |
441 | * the cleanup procedure. | |
442 | */ | |
443 | storeExpireNow(e); | |
444 | storeReleaseRequest(e); | |
445 | ||
446 | if (e->swap_filen > -1) { | |
447 | e->swap_filen = -1; | |
448 | } | |
449 | ||
450 | storeRelease(e); | |
451 | /* Fake an unlink here, this is a bad hack :( */ | |
452 | storeCossRemove(rb->sd, e); | |
453 | rb->counts.objcount--; | |
454 | rb->counts.cancelcount++; | |
455 | } | |
456 | ||
457 | continue; | |
458 | } else { | |
411c6ea3 | 459 | x = log(static_cast<double>(++rb->counts.bad_log_op)) / log(10.0); |
62e76326 | 460 | |
411c6ea3 | 461 | if (0.0 == x - (double) |
462 | (int) x) | |
62e76326 | 463 | debug(47, 1) ("WARNING: %d invalid swap log entries found\n", |
464 | rb->counts.bad_log_op); | |
465 | ||
466 | rb->counts.invalid++; | |
467 | ||
468 | continue; | |
469 | } | |
470 | ||
471 | if ((++rb->counts.scancount & 0xFFF) == 0) { | |
472 | ||
473 | struct stat sb; | |
474 | ||
475 | if (0 == fstat(fileno(rb->log), &sb)) | |
476 | storeRebuildProgress(rb->sd->index, | |
477 | (int) sb.st_size / ss, rb->n_read); | |
478 | } | |
479 | ||
480 | if (EBIT_TEST(s.flags, KEY_PRIVATE)) { | |
481 | rb->counts.badflags++; | |
482 | continue; | |
483 | } | |
484 | ||
c8f4eac4 | 485 | e = rb->sd->get |
486 | (s.key); | |
62e76326 | 487 | |
488 | if (e) { | |
489 | /* key already exists, current entry is newer */ | |
490 | /* keep old, ignore new */ | |
491 | rb->counts.dupcount++; | |
492 | continue; | |
493 | } | |
494 | ||
495 | /* update store_swap_size */ | |
496 | rb->counts.objcount++; | |
497 | ||
498 | e = storeCossAddDiskRestore(rb->sd, s.key, | |
499 | s.swap_filen, | |
500 | s.swap_file_sz, | |
501 | s.expires, | |
502 | s.timestamp, | |
503 | s.lastref, | |
504 | s.lastmod, | |
505 | s.refcount, | |
506 | s.flags, | |
507 | (int) rb->flags.clean); | |
508 | ||
509 | storeDirSwapLog(e, SWAP_LOG_ADD); | |
cd748f27 | 510 | } |
62e76326 | 511 | |
cd748f27 | 512 | eventAdd("storeCossRebuild", storeCossRebuildFromSwapLog, rb, 0.0, 1); |
513 | } | |
514 | ||
515 | /* Add a new object to the cache with empty memory copy and pointer to disk | |
516 | * use to rebuild store from disk. */ | |
517 | static StoreEntry * | |
d3b3ab85 | 518 | storeCossAddDiskRestore(CossSwapDir * SD, const cache_key * key, |
62e76326 | 519 | int file_number, |
520 | size_t swap_file_sz, | |
521 | time_t expires, | |
522 | time_t timestamp, | |
523 | time_t lastref, | |
524 | time_t lastmod, | |
525 | u_int32_t refcount, | |
526 | u_int16_t flags, | |
527 | int clean) | |
cd748f27 | 528 | { |
529 | StoreEntry *e = NULL; | |
99cce4cb | 530 | debug(47, 5) ("storeCossAddDiskRestore: %s, fileno=%08X\n", storeKeyText(key), file_number); |
62e76326 | 531 | /* if you call this you'd better be sure file_number is not |
cd748f27 | 532 | * already in use! */ |
c8f4eac4 | 533 | e = new StoreEntry(); |
cd748f27 | 534 | e->store_status = STORE_OK; |
535 | e->swap_dirn = SD->index; | |
536 | storeSetMemStatus(e, NOT_IN_MEMORY); | |
537 | e->swap_status = SWAPOUT_DONE; | |
538 | e->swap_filen = file_number; | |
539 | e->swap_file_sz = swap_file_sz; | |
540 | e->lock_count = 0; | |
cd748f27 | 541 | e->lastref = lastref; |
542 | e->timestamp = timestamp; | |
543 | e->expires = expires; | |
544 | e->lastmod = lastmod; | |
545 | e->refcount = refcount; | |
546 | e->flags = flags; | |
547 | EBIT_SET(e->flags, ENTRY_CACHABLE); | |
548 | EBIT_CLR(e->flags, RELEASE_REQUEST); | |
549 | EBIT_CLR(e->flags, KEY_PRIVATE); | |
550 | e->ping_status = PING_NONE; | |
551 | EBIT_CLR(e->flags, ENTRY_VALIDATED); | |
552 | storeHashInsert(e, key); /* do it after we clear KEY_PRIVATE */ | |
6a566b9c | 553 | storeCossAdd(SD, e); |
1a224843 | 554 | assert(e->swap_filen >= 0); |
cd748f27 | 555 | return e; |
556 | } | |
557 | ||
28c60158 | 558 | CBDATA_TYPE(RebuildState); |
cd748f27 | 559 | static void |
d3b3ab85 | 560 | storeCossDirRebuild(CossSwapDir * sd) |
cd748f27 | 561 | { |
28c60158 | 562 | RebuildState *rb; |
cd748f27 | 563 | int clean = 0; |
564 | int zero = 0; | |
565 | FILE *fp; | |
28c60158 | 566 | CBDATA_INIT_TYPE(RebuildState); |
72711e31 | 567 | rb = cbdataAlloc(RebuildState); |
cd748f27 | 568 | rb->sd = sd; |
569 | rb->speed = opt_foreground_rebuild ? 1 << 30 : 50; | |
6a566b9c | 570 | rb->flags.clean = (unsigned int) clean; |
cd748f27 | 571 | /* |
572 | * If the swap.state file exists in the cache_dir, then | |
573 | * we'll use storeCossRebuildFromSwapLog(). | |
574 | */ | |
575 | fp = storeCossDirOpenTmpSwapLog(sd, &clean, &zero); | |
99cce4cb | 576 | debug(47, 1) ("Rebuilding COSS storage in %s (%s)\n", |
62e76326 | 577 | sd->path, clean ? "CLEAN" : "DIRTY"); |
6a566b9c | 578 | rb->log = fp; |
579 | store_dirs_rebuilding++; | |
62e76326 | 580 | |
cd748f27 | 581 | if (!clean || fp == NULL) { |
62e76326 | 582 | /* COSS cannot yet rebuild from a dirty state. If the log |
583 | * is dirty then the COSS contents is thrown away. | |
584 | * Why? I guess it is because some contents will be lost, | |
585 | * and COSS cannot verify this.. | |
586 | */ | |
587 | ||
588 | if (fp != NULL) | |
589 | fclose(fp); | |
590 | ||
591 | /* | |
592 | * XXX Make sure we don't trigger an assertion if this is the first | |
593 | * storedir, since if we are, this call will cause storeRebuildComplete | |
594 | * to prematurely complete the rebuild process, and then some other | |
595 | * storedir will try to rebuild and eventually die. | |
596 | */ | |
597 | eventAdd("storeCossRebuildComplete", storeCossRebuildComplete, rb, 0.0, 0); | |
598 | ||
599 | return; | |
cd748f27 | 600 | } |
62e76326 | 601 | |
c8f4eac4 | 602 | eventAdd("storeCossRebuild", storeCossRebuildFromSwapLog, rb, 0.0, 1); |
cd748f27 | 603 | } |
604 | ||
605 | static void | |
d3b3ab85 | 606 | storeCossDirCloseTmpSwapLog(CossSwapDir * sd) |
cd748f27 | 607 | { |
cd748f27 | 608 | char *swaplog_path = xstrdup(storeCossDirSwapLogFile(sd, NULL)); |
609 | char *new_path = xstrdup(storeCossDirSwapLogFile(sd, ".new")); | |
d3b3ab85 | 610 | int anfd; |
611 | file_close(sd->swaplog_fd); | |
62e76326 | 612 | |
cd748f27 | 613 | if (xrename(new_path, swaplog_path) < 0) { |
62e76326 | 614 | fatal("storeCossDirCloseTmpSwapLog: rename failed"); |
cd748f27 | 615 | } |
62e76326 | 616 | |
d3b3ab85 | 617 | anfd = file_open(swaplog_path, O_WRONLY | O_CREAT | O_BINARY); |
62e76326 | 618 | |
d3b3ab85 | 619 | if (anfd < 0) { |
62e76326 | 620 | debug(50, 1) ("%s: %s\n", swaplog_path, xstrerror()); |
621 | fatal("storeCossDirCloseTmpSwapLog: Failed to open swap log."); | |
cd748f27 | 622 | } |
62e76326 | 623 | |
cd748f27 | 624 | safe_free(swaplog_path); |
625 | safe_free(new_path); | |
d3b3ab85 | 626 | sd->swaplog_fd = anfd; |
627 | debug(47, 3) ("Cache COSS Dir #%d log opened on FD %d\n", sd->index, anfd); | |
cd748f27 | 628 | } |
629 | ||
630 | static FILE * | |
d3b3ab85 | 631 | storeCossDirOpenTmpSwapLog(CossSwapDir * sd, int *clean_flag, int *zero_flag) |
cd748f27 | 632 | { |
cd748f27 | 633 | char *swaplog_path = xstrdup(storeCossDirSwapLogFile(sd, NULL)); |
634 | char *clean_path = xstrdup(storeCossDirSwapLogFile(sd, ".last-clean")); | |
635 | char *new_path = xstrdup(storeCossDirSwapLogFile(sd, ".new")); | |
62e76326 | 636 | |
cd748f27 | 637 | struct stat log_sb; |
62e76326 | 638 | |
cd748f27 | 639 | struct stat clean_sb; |
640 | FILE *fp; | |
d3b3ab85 | 641 | int anfd; |
62e76326 | 642 | |
c8f4eac4 | 643 | if (::stat(swaplog_path, &log_sb) < 0) { |
62e76326 | 644 | debug(50, 1) ("Cache COSS Dir #%d: No log file\n", sd->index); |
645 | safe_free(swaplog_path); | |
646 | safe_free(clean_path); | |
647 | safe_free(new_path); | |
648 | return NULL; | |
cd748f27 | 649 | } |
62e76326 | 650 | |
cd748f27 | 651 | *zero_flag = log_sb.st_size == 0 ? 1 : 0; |
652 | /* close the existing write-only FD */ | |
62e76326 | 653 | |
d3b3ab85 | 654 | if (sd->swaplog_fd >= 0) |
62e76326 | 655 | file_close(sd->swaplog_fd); |
656 | ||
cd748f27 | 657 | /* open a write-only FD for the new log */ |
d3b3ab85 | 658 | anfd = file_open(new_path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY); |
62e76326 | 659 | |
d3b3ab85 | 660 | if (anfd < 0) { |
62e76326 | 661 | debug(50, 1) ("%s: %s\n", new_path, xstrerror()); |
662 | fatal("storeDirOpenTmpSwapLog: Failed to open swap log."); | |
cd748f27 | 663 | } |
62e76326 | 664 | |
d3b3ab85 | 665 | sd->swaplog_fd = anfd; |
cd748f27 | 666 | /* open a read-only stream of the old log */ |
3d37fe17 | 667 | fp = fopen(swaplog_path, "rb"); |
62e76326 | 668 | |
cd748f27 | 669 | if (fp == NULL) { |
62e76326 | 670 | debug(50, 0) ("%s: %s\n", swaplog_path, xstrerror()); |
671 | fatal("Failed to open swap log for reading"); | |
cd748f27 | 672 | } |
62e76326 | 673 | |
cd748f27 | 674 | memset(&clean_sb, '\0', sizeof(struct stat)); |
62e76326 | 675 | |
c8f4eac4 | 676 | if (::stat(clean_path, &clean_sb) < 0) |
62e76326 | 677 | *clean_flag = 0; |
cd748f27 | 678 | else if (clean_sb.st_mtime < log_sb.st_mtime) |
62e76326 | 679 | *clean_flag = 0; |
cd748f27 | 680 | else |
62e76326 | 681 | *clean_flag = 1; |
682 | ||
cd748f27 | 683 | safeunlink(clean_path, 1); |
62e76326 | 684 | |
cd748f27 | 685 | safe_free(swaplog_path); |
62e76326 | 686 | |
cd748f27 | 687 | safe_free(clean_path); |
62e76326 | 688 | |
cd748f27 | 689 | safe_free(new_path); |
62e76326 | 690 | |
cd748f27 | 691 | return fp; |
692 | } | |
693 | ||
62e76326 | 694 | class CossCleanLog : public SwapDir::CleanLog |
695 | { | |
696 | ||
697 | public: | |
b9ae18aa | 698 | CossCleanLog(CossSwapDir *); |
d3b3ab85 | 699 | virtual const StoreEntry *nextEntry(); |
700 | virtual void write(StoreEntry const &); | |
cd748f27 | 701 | char *cur; |
e6ccf245 | 702 | char *newLog; |
cd748f27 | 703 | char *cln; |
704 | char *outbuf; | |
705 | off_t outbuf_offset; | |
706 | int fd; | |
6a566b9c | 707 | dlink_node *current; |
b9ae18aa | 708 | CossSwapDir *sd; |
cd748f27 | 709 | }; |
710 | ||
711 | #define CLEAN_BUF_SZ 16384 | |
d3b3ab85 | 712 | |
b9ae18aa | 713 | CossCleanLog::CossCleanLog(CossSwapDir *aSwapDir) : cur(NULL),newLog(NULL),cln(NULL),outbuf(NULL), |
62e76326 | 714 | outbuf_offset(0), fd(-1),current(NULL), sd(aSwapDir) |
715 | {} | |
d3b3ab85 | 716 | |
cd748f27 | 717 | /* |
718 | * Begin the process to write clean cache state. For COSS this means | |
719 | * opening some log files and allocating write buffers. Return 0 if | |
720 | * we succeed, and assign the 'func' and 'data' return pointers. | |
721 | */ | |
d3b3ab85 | 722 | int |
723 | CossSwapDir::writeCleanStart() | |
cd748f27 | 724 | { |
d3b3ab85 | 725 | CossCleanLog *state = new CossCleanLog(this); |
1f56cfef | 726 | #if HAVE_FCHMOD |
62e76326 | 727 | |
cd748f27 | 728 | struct stat sb; |
1f56cfef | 729 | #endif |
62e76326 | 730 | |
d3b3ab85 | 731 | state->newLog = xstrdup(storeCossDirSwapLogFile(this, ".clean")); |
e6ccf245 | 732 | state->fd = file_open(state->newLog, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY); |
d3b3ab85 | 733 | cleanLog = NULL; |
62e76326 | 734 | |
81928dfe | 735 | if (state->fd < 0) { |
62e76326 | 736 | xfree(state->newLog); |
737 | delete state; | |
738 | return -1; | |
81928dfe | 739 | } |
62e76326 | 740 | |
d3b3ab85 | 741 | state->cur = xstrdup(storeCossDirSwapLogFile(this, NULL)); |
742 | state->cln = xstrdup(storeCossDirSwapLogFile(this, ".last-clean")); | |
e6ccf245 | 743 | state->outbuf = (char *)xcalloc(CLEAN_BUF_SZ, 1); |
cd748f27 | 744 | state->outbuf_offset = 0; |
d3b3ab85 | 745 | ::unlink(state->cln); |
746 | state->current = cossindex.tail; | |
99cce4cb | 747 | debug(50, 3) ("storeCOssDirWriteCleanLogs: opened %s, FD %d\n", |
62e76326 | 748 | state->newLog, state->fd); |
cd748f27 | 749 | #if HAVE_FCHMOD |
62e76326 | 750 | |
c8f4eac4 | 751 | if (::stat(state->cur, &sb) == 0) |
62e76326 | 752 | fchmod(state->fd, sb.st_mode); |
753 | ||
cd748f27 | 754 | #endif |
62e76326 | 755 | |
d3b3ab85 | 756 | cleanLog = state; |
6a566b9c | 757 | |
cd748f27 | 758 | return 0; |
759 | } | |
760 | ||
c8f4eac4 | 761 | /* RBC 20050101 - I think there is a race condition here, |
762 | * *current can be freed as its not ref counted, if/when | |
763 | * the store overruns the log writer | |
764 | */ | |
d3b3ab85 | 765 | const StoreEntry * |
766 | CossCleanLog::nextEntry() | |
6a566b9c | 767 | { |
6a566b9c | 768 | const StoreEntry *entry; |
62e76326 | 769 | |
d3b3ab85 | 770 | if (!current) |
62e76326 | 771 | return NULL; |
772 | ||
d3b3ab85 | 773 | entry = (const StoreEntry *) current->data; |
62e76326 | 774 | |
d3b3ab85 | 775 | current = current->prev; |
62e76326 | 776 | |
6a566b9c | 777 | return entry; |
778 | } | |
779 | ||
cd748f27 | 780 | /* |
781 | * "write" an entry to the clean log file. | |
782 | */ | |
d3b3ab85 | 783 | void |
784 | CossCleanLog::write(StoreEntry const &e) | |
cd748f27 | 785 | { |
d3b3ab85 | 786 | CossCleanLog *state = this; |
51ee7c82 | 787 | StoreSwapLogData s; |
788 | static size_t ss = sizeof(StoreSwapLogData); | |
cd748f27 | 789 | s.op = (char) SWAP_LOG_ADD; |
d3b3ab85 | 790 | s.swap_filen = e.swap_filen; |
791 | s.timestamp = e.timestamp; | |
792 | s.lastref = e.lastref; | |
793 | s.expires = e.expires; | |
794 | s.lastmod = e.lastmod; | |
795 | s.swap_file_sz = e.swap_file_sz; | |
796 | s.refcount = e.refcount; | |
797 | s.flags = e.flags; | |
798 | xmemcpy(&s.key, e.key, MD5_DIGEST_CHARS); | |
b9ae18aa | 799 | xmemcpy(outbuf + outbuf_offset, &s, ss); |
800 | outbuf_offset += ss; | |
cd748f27 | 801 | /* buffered write */ |
62e76326 | 802 | |
b9ae18aa | 803 | if (outbuf_offset + ss > CLEAN_BUF_SZ) { |
804 | if (FD_WRITE_METHOD(fd, outbuf, outbuf_offset) < 0) { | |
62e76326 | 805 | debug(50, 0) ("storeCossDirWriteCleanLogs: %s: write: %s\n", |
b9ae18aa | 806 | newLog, xstrerror()); |
62e76326 | 807 | debug(50, 0) ("storeCossDirWriteCleanLogs: Current swap logfile not replaced.\n"); |
b9ae18aa | 808 | file_close(fd); |
809 | fd = -1; | |
810 | unlink(newLog); | |
62e76326 | 811 | sd->cleanLog = NULL; |
b9ae18aa | 812 | delete state; |
62e76326 | 813 | return; |
814 | } | |
815 | ||
b9ae18aa | 816 | outbuf_offset = 0; |
cd748f27 | 817 | } |
818 | } | |
819 | ||
d3b3ab85 | 820 | void |
821 | CossSwapDir::writeCleanDone() | |
cd748f27 | 822 | { |
d3b3ab85 | 823 | CossCleanLog *state = (CossCleanLog *)cleanLog; |
62e76326 | 824 | |
a7d542ee | 825 | if (NULL == state) |
62e76326 | 826 | return; |
827 | ||
cd748f27 | 828 | if (state->fd < 0) |
62e76326 | 829 | return; |
830 | ||
d0ef8ea8 | 831 | if (FD_WRITE_METHOD(state->fd, state->outbuf, state->outbuf_offset) < 0) { |
62e76326 | 832 | debug(50, 0) ("storeCossDirWriteCleanLogs: %s: write: %s\n", |
833 | state->newLog, xstrerror()); | |
834 | debug(50, 0) ("storeCossDirWriteCleanLogs: Current swap logfile " | |
835 | "not replaced.\n"); | |
836 | file_close(state->fd); | |
837 | state->fd = -1; | |
838 | ::unlink(state->newLog); | |
cd748f27 | 839 | } |
62e76326 | 840 | |
cd748f27 | 841 | safe_free(state->outbuf); |
842 | /* | |
843 | * You can't rename open files on Microsoft "operating systems" | |
844 | * so we have to close before renaming. | |
845 | */ | |
d3b3ab85 | 846 | closeLog(); |
3d37fe17 | 847 | /* save the fd value for a later test */ |
d3b3ab85 | 848 | int anfd = state->fd; |
cd748f27 | 849 | /* rename */ |
62e76326 | 850 | |
cd748f27 | 851 | if (state->fd >= 0) { |
966f432f | 852 | #if defined(_SQUID_OS2_) || defined(_SQUID_WIN32_) |
62e76326 | 853 | file_close(state->fd); |
854 | state->fd = -1; | |
855 | ||
cd748f27 | 856 | #endif |
62e76326 | 857 | |
858 | xrename(state->newLog, state->cur); | |
cd748f27 | 859 | } |
62e76326 | 860 | |
cd748f27 | 861 | /* touch a timestamp file if we're not still validating */ |
862 | if (store_dirs_rebuilding) | |
62e76326 | 863 | (void) 0; |
d3b3ab85 | 864 | else if (anfd < 0) |
62e76326 | 865 | (void) 0; |
cd748f27 | 866 | else |
62e76326 | 867 | file_close(file_open(state->cln, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY)); |
868 | ||
cd748f27 | 869 | /* close */ |
870 | safe_free(state->cur); | |
62e76326 | 871 | |
e6ccf245 | 872 | safe_free(state->newLog); |
62e76326 | 873 | |
cd748f27 | 874 | safe_free(state->cln); |
62e76326 | 875 | |
cd748f27 | 876 | if (state->fd >= 0) |
62e76326 | 877 | file_close(state->fd); |
878 | ||
cd748f27 | 879 | state->fd = -1; |
62e76326 | 880 | |
d3b3ab85 | 881 | delete state; |
62e76326 | 882 | |
d3b3ab85 | 883 | cleanLog = NULL; |
cd748f27 | 884 | } |
885 | ||
061c7171 | 886 | static void |
887 | FreeObject(void *address) | |
888 | { | |
889 | StoreSwapLogData *anObject = static_cast <StoreSwapLogData *>(address); | |
890 | delete anObject; | |
891 | } | |
892 | ||
d3b3ab85 | 893 | void |
894 | CossSwapDir::logEntry(const StoreEntry & e, int op) const | |
cd748f27 | 895 | { |
51ee7c82 | 896 | StoreSwapLogData *s = new StoreSwapLogData; |
cd748f27 | 897 | s->op = (char) op; |
d3b3ab85 | 898 | s->swap_filen = e.swap_filen; |
899 | s->timestamp = e.timestamp; | |
900 | s->lastref = e.lastref; | |
901 | s->expires = e.expires; | |
902 | s->lastmod = e.lastmod; | |
903 | s->swap_file_sz = e.swap_file_sz; | |
904 | s->refcount = e.refcount; | |
905 | s->flags = e.flags; | |
906 | xmemcpy(s->key, e.key, MD5_DIGEST_CHARS); | |
907 | file_write(swaplog_fd, | |
62e76326 | 908 | -1, |
909 | s, | |
51ee7c82 | 910 | sizeof(StoreSwapLogData), |
62e76326 | 911 | NULL, |
912 | NULL, | |
061c7171 | 913 | &FreeObject); |
cd748f27 | 914 | } |
915 | ||
d3b3ab85 | 916 | void |
c8f4eac4 | 917 | CossSwapDir::create() |
cd748f27 | 918 | { |
c8f4eac4 | 919 | debugs (47, 3, "Creating swap space in " << path); |
920 | ||
921 | struct stat swap_sb; | |
922 | int swap; | |
923 | ||
924 | if (::stat(path, &swap_sb) < 0) { | |
925 | debugs (47, 2, "COSS swap space space being allocated."); | |
7be3caf3 | 926 | #ifdef _SQUID_MSWIN_ |
927 | ||
928 | mkdir(path); | |
929 | #else | |
930 | ||
c8f4eac4 | 931 | mkdir(path, 0700); |
7be3caf3 | 932 | #endif |
933 | ||
c8f4eac4 | 934 | } |
935 | ||
936 | /* should check here for directories instead of files, and for file size | |
937 | * TODO - if nothing changes, there is nothing to do | |
938 | */ | |
939 | swap = open(stripePath(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600); | |
940 | ||
941 | /* TODO just set the file size */ | |
942 | /* swap size is in K */ | |
943 | char *block[1024]; | |
944 | ||
945 | memset(&block, '\0', 1024); | |
946 | ||
947 | for (off_t offset = 0; offset < max_size; ++offset) { | |
948 | if (write (swap, block, 1024) < 1024) { | |
949 | debugs (47, 0, "Failed to create COSS swap space in " << path); | |
950 | } | |
951 | } | |
952 | ||
953 | close (swap); | |
954 | ||
cd748f27 | 955 | } |
956 | ||
a4b8110e | 957 | /* we are shutting down, flush all membufs to disk */ |
d3b3ab85 | 958 | CossSwapDir::~CossSwapDir() |
cd748f27 | 959 | { |
b9ae18aa | 960 | io->sync(); |
c8f4eac4 | 961 | |
962 | if (theFile != NULL) | |
963 | theFile->close(); | |
964 | ||
b9ae18aa | 965 | delete io; |
cd748f27 | 966 | |
d3b3ab85 | 967 | closeLog(); |
c8f4eac4 | 968 | |
cd748f27 | 969 | n_coss_dirs--; |
c8f4eac4 | 970 | |
b9ae18aa | 971 | safe_free(ioModule); |
c8f4eac4 | 972 | |
973 | safe_free(stripe_path); | |
cd748f27 | 974 | } |
975 | ||
976 | /* | |
977 | * storeCossDirCheckObj | |
978 | * | |
979 | * This routine is called by storeDirSelectSwapDir to see if the given | |
980 | * object is able to be stored on this filesystem. COSS filesystems will | |
981 | * not store everything. We don't check for maxobjsize here since its | |
982 | * done by the upper layers. | |
983 | */ | |
984 | int | |
d3b3ab85 | 985 | CossSwapDir::canStore(StoreEntry const &e)const |
cd748f27 | 986 | { |
0dbabb56 | 987 | |
a4b8110e | 988 | /* Check if the object is a special object, we can't cache these */ |
62e76326 | 989 | |
d3b3ab85 | 990 | if (EBIT_TEST(e.flags, ENTRY_SPECIAL)) |
62e76326 | 991 | return -1; |
cd748f27 | 992 | |
a4b8110e | 993 | /* Otherwise, we're ok */ |
0dbabb56 | 994 | /* Return load, cs->aq.aq_numpending out of MAX_ASYNCOP */ |
b9ae18aa | 995 | return io->load(); |
cd748f27 | 996 | } |
a4b8110e | 997 | |
0e23343f | 998 | /* |
999 | * storeCossDirCallback - do the IO completions | |
1000 | */ | |
d3b3ab85 | 1001 | int |
1002 | CossSwapDir::callback() | |
0e23343f | 1003 | { |
b9ae18aa | 1004 | return io->callback(); |
0e23343f | 1005 | } |
1006 | ||
cd748f27 | 1007 | /* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */ |
1008 | ||
d3b3ab85 | 1009 | void |
1010 | CossSwapDir::statfs(StoreEntry & sentry) const | |
cd748f27 | 1011 | { |
d3b3ab85 | 1012 | storeAppendPrintf(&sentry, "\n"); |
1013 | storeAppendPrintf(&sentry, "Maximum Size: %d KB\n", max_size); | |
1014 | storeAppendPrintf(&sentry, "Current Size: %d KB\n", cur_size); | |
1015 | storeAppendPrintf(&sentry, "Percent Used: %0.2f%%\n", | |
62e76326 | 1016 | 100.0 * cur_size / max_size); |
d3b3ab85 | 1017 | storeAppendPrintf(&sentry, "Number of object collisions: %d\n", (int) numcollisions); |
cd748f27 | 1018 | #if 0 |
1019 | /* is this applicable? I Hope not .. */ | |
1020 | storeAppendPrintf(sentry, "Filemap bits in use: %d of %d (%d%%)\n", | |
62e76326 | 1021 | SD->map->n_files_in_map, SD->map->max_n_files, |
1022 | percent(SD->map->n_files_in_map, SD->map->max_n_files)); | |
cd748f27 | 1023 | #endif |
62e76326 | 1024 | |
b9ae18aa | 1025 | // storeAppendPrintf(&sentry, "Pending operations: %d out of %d\n", io->aq.aq_numpending, MAX_ASYNCOP); |
d3b3ab85 | 1026 | storeAppendPrintf(&sentry, "Flags:"); |
62e76326 | 1027 | |
d3b3ab85 | 1028 | if (flags.selected) |
62e76326 | 1029 | storeAppendPrintf(&sentry, " SELECTED"); |
1030 | ||
d3b3ab85 | 1031 | if (flags.read_only) |
62e76326 | 1032 | storeAppendPrintf(&sentry, " READ-ONLY"); |
1033 | ||
d3b3ab85 | 1034 | storeAppendPrintf(&sentry, "\n"); |
cd748f27 | 1035 | } |
1036 | ||
d3b3ab85 | 1037 | void |
1038 | CossSwapDir::parse(int anIndex, char *aPath) | |
cd748f27 | 1039 | { |
cd748f27 | 1040 | unsigned int i; |
1041 | unsigned int size; | |
81ec98e4 | 1042 | unsigned long max_offset; |
cd748f27 | 1043 | |
1044 | i = GetInteger(); | |
1045 | size = i << 10; /* Mbytes to Kbytes */ | |
62e76326 | 1046 | |
cd748f27 | 1047 | if (size <= 0) |
62e76326 | 1048 | fatal("storeCossDirParse: invalid size value"); |
cd748f27 | 1049 | |
d3b3ab85 | 1050 | index = anIndex; |
62e76326 | 1051 | |
d3b3ab85 | 1052 | path = xstrdup(aPath); |
62e76326 | 1053 | |
d3b3ab85 | 1054 | max_size = size; |
1055 | ||
450e0c10 | 1056 | parseOptions(0); |
1057 | ||
c94af0b9 | 1058 | /* Enforce maxobjsize being set to something */ |
d3b3ab85 | 1059 | if (max_objsize == -1) |
62e76326 | 1060 | fatal("COSS requires max-size to be set to something other than -1!\n"); |
1a224843 | 1061 | |
1062 | if (max_objsize > COSS_MEMBUF_SZ) | |
1063 | fatalf("COSS max-size option must be less than COSS_MEMBUF_SZ (%d)\n", | |
1064 | COSS_MEMBUF_SZ); | |
1065 | ||
1066 | /* | |
1067 | * check that we won't overflow sfileno later. 0xFFFFFF is the | |
1068 | * largest possible sfileno, assuming sfileno is a 25-bit | |
1069 | * signed integer, as defined in structs.h. | |
1070 | */ | |
81ec98e4 | 1071 | max_offset = (unsigned long) 0xFFFFFF << blksz_bits; |
1a224843 | 1072 | |
81ec98e4 | 1073 | if ((unsigned long)max_size > (unsigned long)(max_offset>>10)) { |
1a224843 | 1074 | debug(47,0)("COSS block-size = %d bytes\n", 1<<blksz_bits); |
4989878c | 1075 | debugs(47,0, "COSS largest file offset = " << (max_offset >> 10) << " KB"); |
1a224843 | 1076 | debug(47,0)("COSS cache_dir size = %d KB\n", max_size); |
1077 | fatal("COSS cache_dir size exceeds largest offset\n"); | |
1078 | } | |
cd748f27 | 1079 | } |
1080 | ||
1081 | ||
d3b3ab85 | 1082 | void |
1083 | CossSwapDir::reconfigure(int index, char *path) | |
cd748f27 | 1084 | { |
cd748f27 | 1085 | unsigned int i; |
1086 | unsigned int size; | |
cd748f27 | 1087 | |
1088 | i = GetInteger(); | |
1089 | size = i << 10; /* Mbytes to Kbytes */ | |
62e76326 | 1090 | |
cd748f27 | 1091 | if (size <= 0) |
62e76326 | 1092 | fatal("storeCossDirParse: invalid size value"); |
cd748f27 | 1093 | |
d3b3ab85 | 1094 | if (size == (size_t)max_size) |
62e76326 | 1095 | debug(3, 1) ("Cache COSS dir '%s' size remains unchanged at %d KB\n", path, size); |
cd748f27 | 1096 | else { |
62e76326 | 1097 | debug(3, 1) ("Cache COSS dir '%s' size changed to %d KB\n", path, size); |
1098 | max_size = size; | |
cd748f27 | 1099 | } |
62e76326 | 1100 | |
c94af0b9 | 1101 | /* Enforce maxobjsize being set to something */ |
d3b3ab85 | 1102 | if (max_objsize == -1) |
62e76326 | 1103 | fatal("COSS requires max-size to be set to something other than -1!\n"); |
cd748f27 | 1104 | } |
1105 | ||
1106 | void | |
d3b3ab85 | 1107 | CossSwapDir::dump(StoreEntry &entry)const |
cd748f27 | 1108 | { |
59b2d47f | 1109 | storeAppendPrintf(&entry, " %d", max_size >> 20); |
1110 | dumpOptions(&entry); | |
cd748f27 | 1111 | } |
1112 | ||
c8f4eac4 | 1113 | CossSwapDir::CossSwapDir() : SwapDir ("coss"), swaplog_fd(-1), count(0), current_membuf (NULL), current_offset(0), numcollisions(0), blksz_bits(0), io (NULL), ioModule(NULL), currentIOOptions(new ConfigOptionVector()), stripe_path(NULL) |
cd748f27 | 1114 | { |
1a224843 | 1115 | membufs.head = NULL; |
1116 | membufs.tail = NULL; | |
1117 | cossindex.head = NULL; | |
1118 | cossindex.tail = NULL; | |
1119 | blksz_mask = (1 << blksz_bits) - 1; | |
c8f4eac4 | 1120 | repl = NULL; |
1a224843 | 1121 | } |
a4b8110e | 1122 | |
1a224843 | 1123 | bool |
1124 | CossSwapDir::optionBlockSizeParse(const char *option, const char *value, int reconfiguring) | |
1125 | { | |
d9e04dc7 | 1126 | assert(option); |
1127 | ||
1128 | if (strcmp(option, "block-size") != 0) | |
1129 | return false; | |
1130 | ||
1131 | if (!value) | |
1132 | self_destruct(); | |
1133 | ||
1a224843 | 1134 | int blksz = atoi(value); |
62e76326 | 1135 | |
1a224843 | 1136 | if (blksz == (1 << blksz_bits)) |
1137 | /* no change */ | |
1138 | return true; | |
1139 | ||
1140 | if (reconfiguring) { | |
1141 | debug(47, 0) ("WARNING: cannot change COSS block-size while" | |
1142 | " Squid is running\n"); | |
1143 | return false; | |
cd748f27 | 1144 | } |
62e76326 | 1145 | |
1a224843 | 1146 | int nbits = 0; |
1147 | int check = blksz; | |
1148 | ||
1149 | while (check > 1) { | |
1150 | nbits++; | |
1151 | check >>= 1; | |
cd748f27 | 1152 | } |
62e76326 | 1153 | |
1a224843 | 1154 | check = 1 << nbits; |
1155 | ||
1156 | if (check != blksz) | |
1157 | fatal("COSS block-size must be a power of 2\n"); | |
1158 | ||
1159 | if (nbits > 13) | |
1160 | fatal("COSS block-size must be 8192 or smaller\n"); | |
1161 | ||
1162 | blksz_bits = nbits; | |
1163 | ||
1164 | blksz_mask = (1 << blksz_bits) - 1; | |
1165 | ||
1166 | return true; | |
cd748f27 | 1167 | } |
62e76326 | 1168 | |
1a224843 | 1169 | void |
1170 | CossSwapDir::optionBlockSizeDump(StoreEntry * e) const | |
d3b3ab85 | 1171 | { |
1a224843 | 1172 | storeAppendPrintf(e, " block-size=%d", 1 << blksz_bits); |
d3b3ab85 | 1173 | } |
c8f4eac4 | 1174 | |
1175 | StoreSearch * | |
1176 | CossSwapDir::search(String const url, HttpRequest *) | |
1177 | { | |
1178 | if (url.size()) | |
1179 | fatal ("Cannot search by url yet\n"); | |
1180 | ||
1181 | return new StoreSearchCoss (this); | |
1182 | } | |
1183 | ||
1184 | char const * | |
1185 | CossSwapDir::stripePath() const | |
1186 | { | |
1187 | if (!stripe_path) { | |
1188 | String result = path; | |
1189 | result.append("/stripe"); | |
1190 | const_cast<CossSwapDir *>(this)->stripe_path = xstrdup(result.buf()); | |
1191 | } | |
1192 | ||
1193 | return stripe_path; | |
1194 | } | |
1195 | ||
1196 | CBDATA_CLASS_INIT(StoreSearchCoss); | |
1197 | StoreSearchCoss::StoreSearchCoss(RefCount<CossSwapDir> aSwapDir) : sd(aSwapDir), callback (NULL), cbdata(NULL), _done (false), current(NULL), next_(sd->cossindex.tail) | |
1198 | { | |
1199 | /* TODO: this races with the store as does the cleanlog stuff. | |
1200 | * FIXME by making coss_nodes ref counted */ | |
1201 | } | |
1202 | ||
1203 | /* do not link | |
1204 | StoreSearchCoss::StoreSearchCoss(StoreSearchCoss const &); | |
1205 | */ | |
1206 | ||
1207 | StoreSearchCoss::~StoreSearchCoss() | |
1208 | {} | |
1209 | ||
1210 | void | |
1211 | StoreSearchCoss::next(void (callback)(void *cbdata), void *cbdata) | |
1212 | { | |
1213 | next(); | |
1214 | callback (cbdata); | |
1215 | } | |
1216 | ||
1217 | bool | |
1218 | StoreSearchCoss::next() | |
1219 | { | |
1220 | current = next_; | |
1221 | ||
1222 | if (next_) | |
1223 | next_ = next_->prev; | |
1224 | ||
1225 | if (!current) | |
1226 | _done = true; | |
1227 | ||
1228 | return current != NULL; | |
1229 | } | |
1230 | ||
1231 | bool | |
1232 | StoreSearchCoss::error() const | |
1233 | { | |
1234 | return false; | |
1235 | } | |
1236 | ||
1237 | bool | |
1238 | StoreSearchCoss::isDone() const | |
1239 | { | |
1240 | return _done; | |
1241 | } | |
1242 | ||
1243 | StoreEntry * | |
1244 | StoreSearchCoss::currentItem() | |
1245 | { | |
1246 | if (!current) | |
1247 | return NULL; | |
1248 | ||
1249 | return static_cast<StoreEntry *>( current->data ); | |
1250 | } |