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