]> git.ipfire.org Git - thirdparty/squid.git/blame - src/fs/coss/store_dir_coss.cc
Fix for --enable-cpu-profiling mode. Do not change PROF_start() arg.
[thirdparty/squid.git] / src / fs / coss / store_dir_coss.cc
CommitLineData
a4b8110e 1
cd748f27 2/*
d88e3c49 3 * $Id: store_dir_coss.cc,v 1.69 2007/04/20 23:53:42 wessels 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 55int n_coss_dirs = 0;
56/* static int last_coss_pick_index = -1; */
a3efa961 57MemAllocator *coss_index_pool = NULL;
cd748f27 58
59typedef struct _RebuildState RebuildState;
62e76326 60
61struct _RebuildState
62{
d3b3ab85 63 CossSwapDir *sd;
cd748f27 64 int n_read;
65 FILE *log;
66 int speed;
62e76326 67
68 struct
69 {
70
71unsigned int clean:
72 1;
73 }
74
75 flags;
76
cd748f27 77 struct _store_rebuild_data counts;
78};
79
80static char *storeCossDirSwapLogFile(SwapDir *, const char *);
81static EVH storeCossRebuildFromSwapLog;
d3b3ab85 82static 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 92static void storeCossDirRebuild(CossSwapDir * sd);
93static void storeCossDirCloseTmpSwapLog(CossSwapDir * sd);
94static FILE *storeCossDirOpenTmpSwapLog(CossSwapDir *, int *, int *);
cd748f27 95
96static char *
97storeCossDirSwapLogFile(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 135void
136CossSwapDir::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 150void
151CossSwapDir::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 164void
b9ae18aa 165CossSwapDir::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
173void
174CossSwapDir::closeCompleted()
175{
176 theFile = NULL;
177}
178
179void
180CossSwapDir::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
229void
230CossSwapDir::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
252void
253CossSwapDir::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
279bool
280CossSwapDir::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
302void
303CossSwapDir::optionIODump(StoreEntry * e) const
304{
305 storeAppendPrintf(e, " IOEngine=%s", ioModule);
306}
307
308ConfigOption *
309CossSwapDir::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
341void
342CossSwapDir::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 364void
d3b3ab85 365storeCossRemove(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
374void
d3b3ab85 375storeCossAdd(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
384static void
385storeCossRebuildComplete(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 396static void
397storeCossRebuildFromSwapLog(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 */
d88e3c49 444 e->expireNow();
445 e->releaseRequest();
62e76326 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. */
518static StoreEntry *
d3b3ab85 519storeCossAddDiskRestore(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 559CBDATA_TYPE(RebuildState);
cd748f27 560static void
d3b3ab85 561storeCossDirRebuild(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
606static void
d3b3ab85 607storeCossDirCloseTmpSwapLog(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
631static FILE *
d3b3ab85 632storeCossDirOpenTmpSwapLog(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 695class CossCleanLog : public SwapDir::CleanLog
696{
697
698public:
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 714CossCleanLog::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 723int
724CossSwapDir::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 766const StoreEntry *
767CossCleanLog::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 784void
785CossCleanLog::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 821void
822CossSwapDir::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 887static void
888FreeObject(void *address)
889{
890 StoreSwapLogData *anObject = static_cast <StoreSwapLogData *>(address);
891 delete anObject;
892}
893
d3b3ab85 894void
895CossSwapDir::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 917void
c8f4eac4 918CossSwapDir::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 959CossSwapDir::~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 */
985int
d3b3ab85 986CossSwapDir::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 1002int
1003CossSwapDir::callback()
0e23343f 1004{
b9ae18aa 1005 return io->callback();
0e23343f 1006}
1007
cd748f27 1008/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */
1009
d3b3ab85 1010void
1011CossSwapDir::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 1038void
1039CossSwapDir::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
b7717b61 1059 if (NULL == io)
1060 changeIO(DiskIOModule::FindDefault());
1061
c94af0b9 1062 /* Enforce maxobjsize being set to something */
d3b3ab85 1063 if (max_objsize == -1)
62e76326 1064 fatal("COSS requires max-size to be set to something other than -1!\n");
1a224843 1065
1066 if (max_objsize > COSS_MEMBUF_SZ)
1067 fatalf("COSS max-size option must be less than COSS_MEMBUF_SZ (%d)\n",
1068 COSS_MEMBUF_SZ);
1069
1070 /*
1071 * check that we won't overflow sfileno later. 0xFFFFFF is the
1072 * largest possible sfileno, assuming sfileno is a 25-bit
1073 * signed integer, as defined in structs.h.
1074 */
81ec98e4 1075 max_offset = (unsigned long) 0xFFFFFF << blksz_bits;
1a224843 1076
81ec98e4 1077 if ((unsigned long)max_size > (unsigned long)(max_offset>>10)) {
1a224843 1078 debug(47,0)("COSS block-size = %d bytes\n", 1<<blksz_bits);
4989878c 1079 debugs(47,0, "COSS largest file offset = " << (max_offset >> 10) << " KB");
1a224843 1080 debug(47,0)("COSS cache_dir size = %d KB\n", max_size);
1081 fatal("COSS cache_dir size exceeds largest offset\n");
1082 }
cd748f27 1083}
1084
1085
d3b3ab85 1086void
1087CossSwapDir::reconfigure(int index, char *path)
cd748f27 1088{
cd748f27 1089 unsigned int i;
1090 unsigned int size;
cd748f27 1091
1092 i = GetInteger();
1093 size = i << 10; /* Mbytes to Kbytes */
62e76326 1094
cd748f27 1095 if (size <= 0)
62e76326 1096 fatal("storeCossDirParse: invalid size value");
cd748f27 1097
d3b3ab85 1098 if (size == (size_t)max_size)
62e76326 1099 debug(3, 1) ("Cache COSS dir '%s' size remains unchanged at %d KB\n", path, size);
cd748f27 1100 else {
62e76326 1101 debug(3, 1) ("Cache COSS dir '%s' size changed to %d KB\n", path, size);
1102 max_size = size;
cd748f27 1103 }
62e76326 1104
c94af0b9 1105 /* Enforce maxobjsize being set to something */
d3b3ab85 1106 if (max_objsize == -1)
62e76326 1107 fatal("COSS requires max-size to be set to something other than -1!\n");
cd748f27 1108}
1109
1110void
d3b3ab85 1111CossSwapDir::dump(StoreEntry &entry)const
cd748f27 1112{
5c2d7edb 1113 storeAppendPrintf(&entry, " %d", max_size >> 10);
59b2d47f 1114 dumpOptions(&entry);
cd748f27 1115}
1116
c8f4eac4 1117CossSwapDir::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 1118{
1a224843 1119 membufs.head = NULL;
1120 membufs.tail = NULL;
1121 cossindex.head = NULL;
1122 cossindex.tail = NULL;
1123 blksz_mask = (1 << blksz_bits) - 1;
c8f4eac4 1124 repl = NULL;
1a224843 1125}
a4b8110e 1126
1a224843 1127bool
1128CossSwapDir::optionBlockSizeParse(const char *option, const char *value, int reconfiguring)
1129{
d9e04dc7 1130 assert(option);
1131
1132 if (strcmp(option, "block-size") != 0)
1133 return false;
1134
1135 if (!value)
1136 self_destruct();
1137
1a224843 1138 int blksz = atoi(value);
62e76326 1139
1a224843 1140 if (blksz == (1 << blksz_bits))
1141 /* no change */
1142 return true;
1143
1144 if (reconfiguring) {
1145 debug(47, 0) ("WARNING: cannot change COSS block-size while"
1146 " Squid is running\n");
1147 return false;
cd748f27 1148 }
62e76326 1149
1a224843 1150 int nbits = 0;
1151 int check = blksz;
1152
1153 while (check > 1) {
1154 nbits++;
1155 check >>= 1;
cd748f27 1156 }
62e76326 1157
1a224843 1158 check = 1 << nbits;
1159
1160 if (check != blksz)
1161 fatal("COSS block-size must be a power of 2\n");
1162
1163 if (nbits > 13)
1164 fatal("COSS block-size must be 8192 or smaller\n");
1165
1166 blksz_bits = nbits;
1167
1168 blksz_mask = (1 << blksz_bits) - 1;
1169
1170 return true;
cd748f27 1171}
62e76326 1172
1a224843 1173void
1174CossSwapDir::optionBlockSizeDump(StoreEntry * e) const
d3b3ab85 1175{
1a224843 1176 storeAppendPrintf(e, " block-size=%d", 1 << blksz_bits);
d3b3ab85 1177}
c8f4eac4 1178
1179StoreSearch *
1180CossSwapDir::search(String const url, HttpRequest *)
1181{
1182 if (url.size())
1183 fatal ("Cannot search by url yet\n");
1184
1185 return new StoreSearchCoss (this);
1186}
1187
1188char const *
1189CossSwapDir::stripePath() const
1190{
1191 if (!stripe_path) {
1192 String result = path;
1193 result.append("/stripe");
1194 const_cast<CossSwapDir *>(this)->stripe_path = xstrdup(result.buf());
1195 }
1196
1197 return stripe_path;
1198}
1199
1200CBDATA_CLASS_INIT(StoreSearchCoss);
1201StoreSearchCoss::StoreSearchCoss(RefCount<CossSwapDir> aSwapDir) : sd(aSwapDir), callback (NULL), cbdata(NULL), _done (false), current(NULL), next_(sd->cossindex.tail)
1202{
1203 /* TODO: this races with the store as does the cleanlog stuff.
1204 * FIXME by making coss_nodes ref counted */
1205}
1206
1207/* do not link
1208StoreSearchCoss::StoreSearchCoss(StoreSearchCoss const &);
1209*/
1210
1211StoreSearchCoss::~StoreSearchCoss()
1212{}
1213
1214void
1215StoreSearchCoss::next(void (callback)(void *cbdata), void *cbdata)
1216{
1217 next();
1218 callback (cbdata);
1219}
1220
1221bool
1222StoreSearchCoss::next()
1223{
1224 current = next_;
1225
1226 if (next_)
1227 next_ = next_->prev;
1228
1229 if (!current)
1230 _done = true;
1231
1232 return current != NULL;
1233}
1234
1235bool
1236StoreSearchCoss::error() const
1237{
1238 return false;
1239}
1240
1241bool
1242StoreSearchCoss::isDone() const
1243{
1244 return _done;
1245}
1246
1247StoreEntry *
1248StoreSearchCoss::currentItem()
1249{
1250 if (!current)
1251 return NULL;
1252
1253 return static_cast<StoreEntry *>( current->data );
1254}