/* * DEBUG: section 47 Store COSS Directory Routines * AUTHOR: Eric Stern * * SQUID Web Proxy Cache http://www.squid-cache.org/ * ---------------------------------------------------------- * * Squid is the result of efforts by numerous individuals from * the Internet community; see the CONTRIBUTORS file for full * details. Many organizations have provided support for Squid's * development; see the SPONSORS file for full details. Squid is * Copyrighted (C) 2001 by the Regents of the University of * California; see the COPYRIGHT file for full details. Squid * incorporates software developed and/or copyrighted by other * sources; see the CREDITS file for full details. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #include "squid.h" #include "CossSwapDir.h" #include "cache_cf.h" #include "Store.h" #include "store_coss.h" #include "disk.h" #include "event.h" #include "fde.h" #include "SwapDir.h" #include "StoreSwapLogData.h" #include "DiskIO/DiskIOModule.h" #include "DiskIO/DiskIOStrategy.h" #include "DiskIO/ReadRequest.h" #include "ConfigOption.h" #include "StoreFScoss.h" #include "Parsing.h" #include "store_key_md5.h" #include "swap_log_op.h" #include "store_rebuild.h" #include "SquidConfig.h" #include "SquidMath.h" #if HAVE_ERRNO_H #include #endif #define STORE_META_BUFSZ 4096 int n_coss_dirs = 0; /* static int last_coss_pick_index = -1; */ MemAllocator *coss_index_pool = NULL; typedef struct _RebuildState RebuildState; struct _RebuildState { CossSwapDir *sd; int n_read; FILE *log; int speed; struct { unsigned int clean:1; } flags; StoreRebuildData counts; }; static char *storeCossDirSwapLogFile(SwapDir *, const char *); static EVH storeCossRebuildFromSwapLog; static void storeCossDirRebuild(CossSwapDir * sd); static void storeCossDirCloseTmpSwapLog(CossSwapDir * sd); static FILE *storeCossDirOpenTmpSwapLog(CossSwapDir *, int *, int *); static char * storeCossDirSwapLogFile(SwapDir * sd, const char *ext) { LOCAL_ARRAY(char, path, MAXPATHLEN); LOCAL_ARRAY(char, pathtmp, MAXPATHLEN); LOCAL_ARRAY(char, digit, 32); char *pathtmp2; if (Config.Log.swap) { xstrncpy(pathtmp, sd->path, MAXPATHLEN - 64); pathtmp2 = pathtmp; while ((pathtmp2 = strchr(pathtmp2, '/')) != NULL) *pathtmp2 = '.'; while (strlen(pathtmp) && pathtmp[strlen(pathtmp) - 1] == '.') pathtmp[strlen(pathtmp) - 1] = '\0'; for (pathtmp2 = pathtmp; *pathtmp2 == '.'; ++pathtmp2); snprintf(path, MAXPATHLEN - 64, Config.Log.swap, pathtmp2); if (strncmp(path, Config.Log.swap, MAXPATHLEN - 64) == 0) { strcat(path, "."); snprintf(digit, 32, "%02d", sd->index); strncat(path, digit, 3); } } else { xstrncpy(path, sd->path, MAXPATHLEN - 64); strcat(path, "/swap.state"); } if (ext) strncat(path, ext, 16); return path; } void CossSwapDir::openLog() { char *logPath; logPath = storeCossDirSwapLogFile(this, NULL); swaplog_fd = file_open(logPath, O_WRONLY | O_CREAT | O_BINARY); if (swaplog_fd < 0) { debugs(47, DBG_IMPORTANT, "" << logPath << ": " << xstrerror()); fatal("storeCossDirOpenSwapLog: Failed to open swap log."); } debugs(47, 3, "Cache COSS Dir #" << index << " log opened on FD " << swaplog_fd); } void CossSwapDir::closeLog() { if (swaplog_fd < 0) /* not open */ return; file_close(swaplog_fd); debugs(47, 3, "Cache COSS Dir #" << index << " log closed on FD " << swaplog_fd); swaplog_fd = -1; } void CossSwapDir::ioCompletedNotification() { if (theFile->error()) { debugs(47, DBG_IMPORTANT, "" << path << ": " << xstrerror()); fatal("storeCossDirInit: Failed to open a COSS file."); } } void CossSwapDir::closeCompleted() { theFile = NULL; } void CossSwapDir::readCompleted(const char *buf, int len, int errflag, RefCount aRequest) { CossRead* cossRead= dynamic_cast(aRequest.getRaw()); assert (cossRead); StoreIOState::Pointer sio = cossRead->sio; void *cbdata; StoreIOState::STRCB *callback = sio->read.callback; char *p; CossState *cstate = dynamic_cast(sio.getRaw()); ssize_t rlen; debugs(79, 3, "storeCossReadDone: fileno " << sio->swap_filen << ", len " << len); cstate->flags.reading = false; if (errflag) { ++ StoreFScoss::GetInstance().stats.read.fail; if (errflag > 0) { errno = errflag; debugs(79, DBG_IMPORTANT, "storeCossReadDone: error: " << xstrerror()); } else { debugs(79, DBG_IMPORTANT, "storeCossReadDone: got failure (" << errflag << ")"); } rlen = -1; } else { ++ StoreFScoss::GetInstance().stats.read.success; if (cstate->readbuffer == NULL) { cstate->readbuffer = (char *)xmalloc(cstate->st_size); p = storeCossMemPointerFromDiskOffset(storeCossFilenoToDiskOffset(sio->swap_filen), NULL); memcpy(cstate->readbuffer, p, cstate->st_size); } sio->offset_ += len; memcpy(cstate->requestbuf, &cstate->readbuffer[cstate->requestoffset], cstate->requestlen); rlen = (size_t) cstate->requestlen; } assert(callback); sio->read.callback = NULL; if (cbdataReferenceValidDone(sio->read.callback_data, &cbdata)) callback(cbdata, cstate->requestbuf, rlen, sio); } void CossSwapDir::writeCompleted(int errflag, size_t len, RefCount writeRequest) { CossWrite* cossWrite= dynamic_cast(writeRequest.getRaw()); assert (cossWrite); debugs(79, 3, "storeCossWriteMemBufDone: buf " << cossWrite->membuf << ", len " << len); if (errflag) { ++ StoreFScoss::GetInstance().stats.stripe_write.fail; debugs(79, DBG_IMPORTANT, "storeCossWriteMemBufDone: got failure (" << errflag << ")"); debugs(79, DBG_IMPORTANT, "size=" << cossWrite->membuf->diskend - cossWrite->membuf->diskstart); } else { ++ StoreFScoss::GetInstance().stats.stripe_write.success; } dlinkDelete(&cossWrite->membuf->node, &membufs); cbdataFree(cossWrite->membuf); -- StoreFScoss::GetInstance().stats.stripes; } void CossSwapDir::changeIO(DiskIOModule *module) { DiskIOStrategy *anIO = module->createStrategy(); safe_free(ioModule); ioModule = xstrdup(module->type()); delete io; io = anIO; /* Change the IO Options */ if (currentIOOptions == NULL) currentIOOptions = new ConfigOptionVector(); if (currentIOOptions->options.size() > 3) delete currentIOOptions->options.pop_back(); /* TODO: factor out these 4 lines */ ConfigOption *ioOptions = NULL; if (io) ioOptions = io->getOptionTree(); if (ioOptions) currentIOOptions->options.push_back(ioOptions); } bool CossSwapDir::optionIOParse(char const *option, const char *value, int reconfiguring) { if (strcmp(option, "IOEngine") != 0) return false; if (reconfiguring) /* silently ignore this */ return true; if (!value) self_destruct(); DiskIOModule *module = DiskIOModule::Find(value); if (!module) self_destruct(); changeIO(module); return true; } void CossSwapDir::optionIODump(StoreEntry * e) const { storeAppendPrintf(e, " IOEngine=%s", ioModule); } ConfigOption * CossSwapDir::getOptionTree() const { ConfigOption *parentResult = SwapDir::getOptionTree(); if (currentIOOptions == NULL) currentIOOptions = new ConfigOptionVector(); currentIOOptions->options.push_back(parentResult); currentIOOptions->options.push_back(new ConfigOptionAdapter(*const_cast(this), &CossSwapDir::optionIOParse, &CossSwapDir::optionIODump)); currentIOOptions->options.push_back( new ConfigOptionAdapter(*const_cast(this), &CossSwapDir::optionBlockSizeParse, &CossSwapDir::optionBlockSizeDump)); ConfigOption *ioOptions = NULL; if (io) ioOptions = io->getOptionTree(); if (ioOptions) currentIOOptions->options.push_back(ioOptions); ConfigOption* result = currentIOOptions; currentIOOptions = NULL; return result; } void CossSwapDir::init() { /* FIXME: SwapDirs aren't refcounted. We call IORequestor calls, which * are refcounted. SO, we up our count once to avoid implicit delete's. */ RefCountReference(); io->init(); openLog(); storeCossDirRebuild(this); theFile = io->newFile(stripePath()); theFile->open(O_RDWR | O_CREAT, 0644, this); ++n_coss_dirs; /* * fs.blksize is normally determined by calling statvfs() etc, * but we just set it here. It is used in accounting the * total store size, and is reported in cachemgr 'storedir' * page. */ fs.blksize = 1 << blksz_bits; } void storeCossRemove(CossSwapDir * sd, StoreEntry * e) { CossIndexNode *coss_node = (CossIndexNode *)e->repl.data; e->repl.data = NULL; dlinkDelete(&coss_node->node, &sd->cossindex); coss_index_pool->freeOne(coss_node); sd->count -= 1; } void storeCossAdd(CossSwapDir * sd, StoreEntry * e) { CossIndexNode *coss_node = (CossIndexNode *)coss_index_pool->alloc(); assert(!e->repl.data); e->repl.data = coss_node; dlinkAdd(e, &coss_node->node, &sd->cossindex); sd->count += 1; } static void storeCossRebuildComplete(void *data) { RebuildState *rb = (RebuildState *)data; CossSwapDir *sd = rb->sd; sd->startMembuf(); -- StoreController::store_dirs_rebuilding; storeCossDirCloseTmpSwapLog(rb->sd); storeRebuildComplete(&rb->counts); cbdataFree(rb); } static void storeCossRebuildFromSwapLog(void *data) { RebuildState *rb = (RebuildState *)data; StoreEntry *e = NULL; StoreSwapLogData s; size_t ss = sizeof(StoreSwapLogData); double x; assert(rb != NULL); /* load a number of objects per invocation */ for (int aCount = 0; aCount < rb->speed; ++aCount) { if (fread(&s, ss, 1, rb->log) != 1) { debugs(47, DBG_IMPORTANT, "Done reading " << rb->sd->path << " swaplog (" << rb->n_read << " entries)"); fclose(rb->log); rb->log = NULL; storeCossRebuildComplete(rb); return; } ++ rb->n_read; if (s.op <= SWAP_LOG_NOP) continue; if (s.op >= SWAP_LOG_MAX) continue; debugs(47, 3, "storeCossRebuildFromSwapLog: " << swap_log_op_str[(int) s.op] << " " << storeKeyText(s.key) << " "<< std::setfill('0') << std::hex << std::uppercase << std::setw(8) << s.swap_filen); if (s.op == SWAP_LOG_ADD) { (void) 0; } else if (s.op == SWAP_LOG_DEL) { /* Delete unless we already have a newer copy */ if ((e = rb->sd->get (s.key)) != NULL && s.lastref > e->lastref) { /* * Make sure we don't unlink the file, it might be * in use by a subsequent entry. Also note that * we don't have to subtract from cur_size because * adding to cur_size happens in the cleanup procedure. */ e->expireNow(); e->releaseRequest(); if (e->swap_filen > -1) { e->swap_filen = -1; } e->release(); /* Fake an unlink here, this is a bad hack :( */ storeCossRemove(rb->sd, e); -- rb->counts.objcount; ++ rb->counts.cancelcount; } continue; } else { x = log(static_cast(++rb->counts.bad_log_op)) / log(10.0); if (0.0 == x - (double) (int) x) debugs(47, DBG_IMPORTANT, "WARNING: " << rb->counts.bad_log_op << " invalid swap log entries found"); ++ rb->counts.invalid; continue; } if ((++rb->counts.scancount & 0xFFF) == 0) { struct stat sb; if (0 == fstat(fileno(rb->log), &sb)) storeRebuildProgress(rb->sd->index, (int) sb.st_size / ss, rb->n_read); } if (EBIT_TEST(s.flags, KEY_PRIVATE)) { ++ rb->counts.badflags; continue; } e = rb->sd->get (s.key); if (e) { /* key already exists, current entry is newer */ /* keep old, ignore new */ ++ rb->counts.dupcount; continue; } ++ rb->counts.objcount; e = rb->sd->addDiskRestore(s.key, s.swap_filen, s.swap_file_sz, s.expires, s.timestamp, s.lastref, s.lastmod, s.refcount, s.flags, (int) rb->flags.clean); storeDirSwapLog(e, SWAP_LOG_ADD); } eventAdd("storeCossRebuild", storeCossRebuildFromSwapLog, rb, 0.0, 1); } /* Add a new object to the cache with empty memory copy and pointer to disk * use to rebuild store from disk. */ StoreEntry * CossSwapDir::addDiskRestore(const cache_key *const key, int file_number, uint64_t swap_file_sz, time_t expires, time_t timestamp, time_t lastref, time_t lastmod, uint32_t refcount, uint16_t flags, int clean) { StoreEntry *e = NULL; debugs(47, 5, "storeCossAddDiskRestore: " << storeKeyText(key) << ", fileno="<< std::setfill('0') << std::hex << std::uppercase << std::setw(8) << file_number); /* if you call this you'd better be sure file_number is not * already in use! */ e = new StoreEntry(); e->store_status = STORE_OK; e->swap_dirn = index; e->setMemStatus(NOT_IN_MEMORY); e->swap_status = SWAPOUT_DONE; e->swap_filen = file_number; e->swap_file_sz = swap_file_sz; e->lock_count = 0; e->lastref = lastref; e->timestamp = timestamp; e->expires = expires; e->lastmod = lastmod; e->refcount = refcount; e->flags = flags; EBIT_SET(e->flags, ENTRY_CACHABLE); EBIT_CLR(e->flags, RELEASE_REQUEST); EBIT_CLR(e->flags, KEY_PRIVATE); e->ping_status = PING_NONE; EBIT_CLR(e->flags, ENTRY_VALIDATED); cur_size += fs.blksize * sizeInBlocks(e->swap_file_sz); ++n_disk_objects; e->hashInsert(key); /* do it after we clear KEY_PRIVATE */ storeCossAdd(this, e); assert(e->swap_filen >= 0); return e; } CBDATA_TYPE(RebuildState); static void storeCossDirRebuild(CossSwapDir * sd) { RebuildState *rb; int clean = 0; int zero = 0; FILE *fp; CBDATA_INIT_TYPE(RebuildState); rb = cbdataAlloc(RebuildState); rb->sd = sd; rb->speed = opt_foreground_rebuild ? 1 << 30 : 50; rb->flags.clean = (unsigned int) clean; /* * If the swap.state file exists in the cache_dir, then * we'll use storeCossRebuildFromSwapLog(). */ fp = storeCossDirOpenTmpSwapLog(sd, &clean, &zero); debugs(47, DBG_IMPORTANT, "Rebuilding COSS storage in " << sd->path << " (" << (clean ? "CLEAN" : "DIRTY") << ")"); rb->log = fp; ++ StoreController::store_dirs_rebuilding; if (!clean || fp == NULL) { /* COSS cannot yet rebuild from a dirty state. If the log * is dirty then the COSS contents is thrown away. * Why? I guess it is because some contents will be lost, * and COSS cannot verify this.. */ if (fp != NULL) fclose(fp); /* * XXX Make sure we don't trigger an assertion if this is the first * storedir, since if we are, this call will cause storeRebuildComplete * to prematurely complete the rebuild process, and then some other * storedir will try to rebuild and eventually die. */ eventAdd("storeCossRebuildComplete", storeCossRebuildComplete, rb, 0.0, 0); return; } eventAdd("storeCossRebuild", storeCossRebuildFromSwapLog, rb, 0.0, 1); } static void storeCossDirCloseTmpSwapLog(CossSwapDir * sd) { char *swaplog_path = xstrdup(storeCossDirSwapLogFile(sd, NULL)); char *new_path = xstrdup(storeCossDirSwapLogFile(sd, ".new")); int anfd; file_close(sd->swaplog_fd); if (xrename(new_path, swaplog_path) < 0) { fatal("storeCossDirCloseTmpSwapLog: rename failed"); } anfd = file_open(swaplog_path, O_WRONLY | O_CREAT | O_BINARY); if (anfd < 0) { debugs(50, DBG_IMPORTANT, "" << swaplog_path << ": " << xstrerror()); fatal("storeCossDirCloseTmpSwapLog: Failed to open swap log."); } safe_free(swaplog_path); safe_free(new_path); sd->swaplog_fd = anfd; debugs(47, 3, "Cache COSS Dir #" << sd->index << " log opened on FD " << anfd); } static FILE * storeCossDirOpenTmpSwapLog(CossSwapDir * sd, int *clean_flag, int *zero_flag) { char *swaplog_path = xstrdup(storeCossDirSwapLogFile(sd, NULL)); char *clean_path = xstrdup(storeCossDirSwapLogFile(sd, ".last-clean")); char *new_path = xstrdup(storeCossDirSwapLogFile(sd, ".new")); struct stat log_sb; struct stat clean_sb; FILE *fp; int anfd; if (::stat(swaplog_path, &log_sb) < 0) { debugs(50, DBG_IMPORTANT, "Cache COSS Dir #" << sd->index << ": No log file"); safe_free(swaplog_path); safe_free(clean_path); safe_free(new_path); return NULL; } *zero_flag = log_sb.st_size == 0 ? 1 : 0; /* close the existing write-only FD */ if (sd->swaplog_fd >= 0) file_close(sd->swaplog_fd); /* open a write-only FD for the new log */ anfd = file_open(new_path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY); if (anfd < 0) { debugs(50, DBG_IMPORTANT, "" << new_path << ": " << xstrerror()); fatal("storeDirOpenTmpSwapLog: Failed to open swap log."); } sd->swaplog_fd = anfd; /* open a read-only stream of the old log */ fp = fopen(swaplog_path, "rb"); if (fp == NULL) { debugs(50, DBG_CRITICAL, "" << swaplog_path << ": " << xstrerror()); fatal("Failed to open swap log for reading"); } memset(&clean_sb, '\0', sizeof(struct stat)); if (::stat(clean_path, &clean_sb) < 0) *clean_flag = 0; else if (clean_sb.st_mtime < log_sb.st_mtime) *clean_flag = 0; else *clean_flag = 1; safeunlink(clean_path, 1); safe_free(swaplog_path); safe_free(clean_path); safe_free(new_path); return fp; } class CossCleanLog : public SwapDir::CleanLog { public: CossCleanLog(CossSwapDir *); virtual const StoreEntry *nextEntry(); virtual void write(StoreEntry const &); char *cur; char *newLog; char *cln; char *outbuf; off_t outbuf_offset; int fd; dlink_node *current; CossSwapDir *sd; }; #define CLEAN_BUF_SZ 16384 CossCleanLog::CossCleanLog(CossSwapDir *aSwapDir) : cur(NULL),newLog(NULL),cln(NULL),outbuf(NULL), outbuf_offset(0), fd(-1),current(NULL), sd(aSwapDir) {} /* * Begin the process to write clean cache state. For COSS this means * opening some log files and allocating write buffers. Return 0 if * we succeed, and assign the 'func' and 'data' return pointers. */ int CossSwapDir::writeCleanStart() { CossCleanLog *state = new CossCleanLog(this); #if HAVE_FCHMOD struct stat sb; #endif state->newLog = xstrdup(storeCossDirSwapLogFile(this, ".clean")); state->fd = file_open(state->newLog, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY); cleanLog = NULL; if (state->fd < 0) { xfree(state->newLog); delete state; return -1; } state->cur = xstrdup(storeCossDirSwapLogFile(this, NULL)); state->cln = xstrdup(storeCossDirSwapLogFile(this, ".last-clean")); state->outbuf = (char *)xcalloc(CLEAN_BUF_SZ, 1); state->outbuf_offset = 0; ::unlink(state->cln); state->current = cossindex.tail; debugs(50, 3, "storeCOssDirWriteCleanLogs: opened " << state->newLog << ", FD " << state->fd); #if HAVE_FCHMOD if (::stat(state->cur, &sb) == 0) fchmod(state->fd, sb.st_mode); #endif cleanLog = state; return 0; } /* RBC 20050101 - I think there is a race condition here, * *current can be freed as its not ref counted, if/when * the store overruns the log writer */ const StoreEntry * CossCleanLog::nextEntry() { const StoreEntry *entry; if (!current) return NULL; entry = (const StoreEntry *) current->data; current = current->prev; return entry; } /* * "write" an entry to the clean log file. */ void CossCleanLog::write(StoreEntry const &e) { CossCleanLog *state = this; StoreSwapLogData s; static size_t ss = sizeof(StoreSwapLogData); s.op = (char) SWAP_LOG_ADD; s.swap_filen = e.swap_filen; s.timestamp = e.timestamp; s.lastref = e.lastref; s.expires = e.expires; s.lastmod = e.lastmod; s.swap_file_sz = e.swap_file_sz; s.refcount = e.refcount; s.flags = e.flags; memcpy(&s.key, e.key, SQUID_MD5_DIGEST_LENGTH); memcpy(outbuf + outbuf_offset, &s, ss); outbuf_offset += ss; /* buffered write */ if (outbuf_offset + ss > CLEAN_BUF_SZ) { if (FD_WRITE_METHOD(fd, outbuf, outbuf_offset) < 0) { debugs(50, DBG_CRITICAL, "storeCossDirWriteCleanLogs: " << newLog << ": write: " << xstrerror()); debugs(50, DBG_CRITICAL, "storeCossDirWriteCleanLogs: Current swap logfile not replaced."); file_close(fd); fd = -1; unlink(newLog); sd->cleanLog = NULL; delete state; return; } outbuf_offset = 0; } } void CossSwapDir::writeCleanDone() { CossCleanLog *state = (CossCleanLog *)cleanLog; if (NULL == state) return; if (state->fd < 0) return; if (FD_WRITE_METHOD(state->fd, state->outbuf, state->outbuf_offset) < 0) { debugs(50, DBG_CRITICAL, "storeCossDirWriteCleanLogs: " << state->newLog << ": write: " << xstrerror()); debugs(50, DBG_CRITICAL, "storeCossDirWriteCleanLogs: Current swap logfile not replaced."); file_close(state->fd); state->fd = -1; ::unlink(state->newLog); } safe_free(state->outbuf); /* * You can't rename open files on Microsoft "operating systems" * so we have to close before renaming. */ closeLog(); /* save the fd value for a later test */ int anfd = state->fd; /* rename */ if (state->fd >= 0) { #if _SQUID_OS2_ || _SQUID_WINDOWS_ file_close(state->fd); state->fd = -1; #endif xrename(state->newLog, state->cur); } /* touch a timestamp file if we're not still validating */ if (StoreController::store_dirs_rebuilding) (void) 0; else if (anfd < 0) (void) 0; else file_close(file_open(state->cln, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY)); /* close */ safe_free(state->cur); safe_free(state->newLog); safe_free(state->cln); if (state->fd >= 0) file_close(state->fd); state->fd = -1; delete state; cleanLog = NULL; } static void FreeObject(void *address) { StoreSwapLogData *anObject = static_cast (address); delete anObject; } void CossSwapDir::logEntry(const StoreEntry & e, int op) const { StoreSwapLogData *s = new StoreSwapLogData; s->op = (char) op; s->swap_filen = e.swap_filen; s->timestamp = e.timestamp; s->lastref = e.lastref; s->expires = e.expires; s->lastmod = e.lastmod; s->swap_file_sz = e.swap_file_sz; s->refcount = e.refcount; s->flags = e.flags; memcpy(s->key, e.key, SQUID_MD5_DIGEST_LENGTH); file_write(swaplog_fd, -1, s, sizeof(StoreSwapLogData), NULL, NULL, &FreeObject); } void CossSwapDir::create() { debugs (47, 3, "Creating swap space in " << path); struct stat swap_sb; int swap; if (::stat(path, &swap_sb) < 0) { debugs (47, 2, "COSS swap space space being allocated."); mkdir(path, 0700); } /* should check here for directories instead of files, and for file size * TODO - if nothing changes, there is nothing to do */ swap = open(stripePath(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600); /* TODO just set the file size */ char block[1024]; Must(maxSize() % sizeof(block) == 0); memset(block, '\0', sizeof(block)); for (uint64_t offset = 0; offset < maxSize(); offset += sizeof(block)) { if (write (swap, block, sizeof(block)) != sizeof(block)) { debugs (47, 0, "Failed to create COSS swap space in " << path); } } close (swap); } /* we are shutting down, flush all membufs to disk */ CossSwapDir::~CossSwapDir() { io->sync(); if (theFile != NULL) theFile->close(); delete io; closeLog(); --n_coss_dirs; safe_free(ioModule); safe_free(stripe_path); } bool CossSwapDir::canStore(const StoreEntry &e, int64_t diskSpaceNeeded, int &load) const { if (!SwapDir::canStore(e, diskSpaceNeeded, load)) return false; load = io->load(); return true; } /* * storeCossDirCallback - do the IO completions */ int CossSwapDir::callback() { return io->callback(); } /* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */ void CossSwapDir::statfs(StoreEntry & sentry) const { storeAppendPrintf(&sentry, "\n"); storeAppendPrintf(&sentry, "Maximum Size: %" PRIu64 " KB\n", maxSize() >> 10); storeAppendPrintf(&sentry, "Current Size: %.2f KB\n", currentSize() / 1024.0); storeAppendPrintf(&sentry, "Percent Used: %0.2f%%\n", Math::doublePercent(currentSize(), maxSize()) ); storeAppendPrintf(&sentry, "Number of object collisions: %d\n", (int) numcollisions); #if 0 /* is this applicable? I Hope not .. */ storeAppendPrintf(sentry, "Filemap bits in use: %d of %d (%d%%)\n", SD->map->numFilesInMap(), SD->map->capacity(), Math::intPercent(SD->map->numFilesInMap(), SD->map->capacity())); #endif // storeAppendPrintf(&sentry, "Pending operations: %d out of %d\n", io->aq.aq_numpending, MAX_ASYNCOP); storeAppendPrintf(&sentry, "Flags:"); if (flags.selected) storeAppendPrintf(&sentry, " SELECTED"); if (flags.read_only) storeAppendPrintf(&sentry, " READ-ONLY"); storeAppendPrintf(&sentry, "\n"); } void CossSwapDir::parse(int anIndex, char *aPath) { const int i = GetInteger(); if (i <= 0) fatal("storeCossDirParse: invalid size value"); index = anIndex; path = xstrdup(aPath); max_size = static_cast(i) << 20; // MBytes to Bytes parseOptions(0); if (NULL == io) changeIO(DiskIOModule::FindDefault()); /* Enforce maxobjsize being set to something */ if (max_objsize == -1) fatal("COSS requires max-size to be set to something other than -1!\n"); if (max_objsize > COSS_MEMBUF_SZ) fatalf("COSS max-size option must be less than COSS_MEMBUF_SZ (%d)\n", COSS_MEMBUF_SZ); // check that we won't overflow sfileno later. const uint64_t max_offset = (uint64_t)SwapFilenMax << blksz_bits; if (maxSize() > max_offset) { debugs(47, DBG_CRITICAL, "COSS block-size = " << (1<(i) << 20; // MBytes to Bytes if (size == maxSize()) debugs(3, DBG_IMPORTANT, "Cache COSS dir '" << path << "' size remains unchanged at " << i << " MB"); else { debugs(3, DBG_IMPORTANT, "Cache COSS dir '" << path << "' size changed to " << i << " MB"); max_size = size; } /* Enforce maxobjsize being set to something */ if (max_objsize == -1) fatal("COSS requires max-size to be set to something other than -1!\n"); } void CossSwapDir::swappedOut(const StoreEntry &e) { cur_size += fs.blksize * sizeInBlocks(e.swap_file_sz); ++n_disk_objects; } void CossSwapDir::dump(StoreEntry &entry)const { storeAppendPrintf(&entry, " %" PRIu64, maxSize() >> 20); dumpOptions(&entry); } 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), cur_size(0), n_disk_objects(0) { membufs.head = NULL; membufs.tail = NULL; cossindex.head = NULL; cossindex.tail = NULL; blksz_mask = (1 << blksz_bits) - 1; repl = NULL; } bool CossSwapDir::optionBlockSizeParse(const char *option, const char *value, int reconfiguring) { assert(option); if (strcmp(option, "block-size") != 0) return false; if (!value) self_destruct(); int blksz = atoi(value); if (blksz == (1 << blksz_bits)) /* no change */ return true; if (reconfiguring) { debugs(47, DBG_CRITICAL, "WARNING: cannot change COSS block-size while Squid is running"); return false; } int nbits = 0; int check = blksz; while (check > 1) { ++nbits; check >>= 1; } check = 1 << nbits; if (check != blksz) fatal("COSS block-size must be a power of 2\n"); if (nbits > 13) fatal("COSS block-size must be 8192 or smaller\n"); blksz_bits = nbits; blksz_mask = (1 << blksz_bits) - 1; return true; } void CossSwapDir::optionBlockSizeDump(StoreEntry * e) const { storeAppendPrintf(e, " block-size=%d", 1 << blksz_bits); } StoreSearch * CossSwapDir::search(String const url, HttpRequest *) { if (url.size()) fatal ("Cannot search by url yet\n"); return new StoreSearchCoss (this); } char const * CossSwapDir::stripePath() const { if (!stripe_path) { String result = path; result.append("/stripe"); const_cast(this)->stripe_path = xstrdup(result.termedBuf()); } return stripe_path; } CBDATA_CLASS_INIT(StoreSearchCoss); StoreSearchCoss::StoreSearchCoss(RefCount aSwapDir) : sd(aSwapDir), callback (NULL), cbdata(NULL), _done (false), current(NULL), next_(sd->cossindex.tail) { /* TODO: this races with the store as does the cleanlog stuff. * FIXME by making coss_nodes ref counted */ } /* do not link StoreSearchCoss::StoreSearchCoss(StoreSearchCoss const &); */ StoreSearchCoss::~StoreSearchCoss() {} void StoreSearchCoss::next(void (callback)(void *cbdata), void *cbdata) { next(); callback (cbdata); } bool StoreSearchCoss::next() { current = next_; if (next_) next_ = next_->prev; if (!current) _done = true; return current != NULL; } bool StoreSearchCoss::error() const { return false; } bool StoreSearchCoss::isDone() const { return _done; } StoreEntry * StoreSearchCoss::currentItem() { if (!current) return NULL; return static_cast( current->data ); }