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