This assertion could be triggered by various swapout failures for
ufs/aufs/diskd cache_dir entries.
The bug was caused by
4310f8b change related to storeSwapOutFileClosed()
method. Before that change, swapout failures resulted in
StoreEntry::swap_status set to SWAPOUT_NONE, preventing
another/asserting iteration of StoreEntry::swapOut().
This fix adds SWAPOUT_FAILED swap status for marking swapout failures
(instead of reviving and abusing SWAPOUT_NONE), making the code more
reliable.
Also removed storeSwapOutFileNotify() implementation. We should not
waste time on maintaining an unused method that now contains conflicting
assertions: swappingOut() and !hasDisk().
bool swappingOut() const { return swap_status == SWAPOUT_WRITING; }
/// whether the entire entry is now on disk (possibly marked for deletion)
bool swappedOut() const { return swap_status == SWAPOUT_DONE; }
+ /// whether we failed to write this entry to disk
+ bool swapoutFailed() const { return swap_status == SWAPOUT_FAILED; }
void swapOutFileClose(int how);
const char *url() const;
/// Satisfies cachability requirements shared among disk and RAM caches.
SWAPOUT_WRITING,
/// StoreEntry is associated with a complete (i.e., fully swapped out) disk store entry.
/// Guarantees the disk store entry existence.
- SWAPOUT_DONE
+ SWAPOUT_DONE,
+ /// StoreEntry is associated with an unusable disk store entry.
+ /// Swapout attempt has failed. The entry should be marked for eventual deletion.
+ /// Guarantees the disk store entry existence.
+ SWAPOUT_FAILED
} swap_status_t;
typedef enum {
if (!e.hasDisk())
return; // see evictIfFound()
+ // Since these fields grow only after swap out ends successfully,
+ // do not decrement them for e.swappingOut() and e.swapoutFailed().
if (e.swappedOut()) {
cur_size -= fs.blksize * sizeInBlocks(e.swap_file_sz);
--n_disk_objects;
Fs::Ufs::UFSSwapDir::finalizeSwapoutFailure(StoreEntry &entry)
{
debugs(47, 5, entry);
- // rely on the expected subsequent StoreEntry::release(), evictCached(), or
+ // rely on the expected eventual StoreEntry::release(), evictCached(), or
// a similar call to call unlink(), detachFromDisk(), etc. for the entry.
}
const char *swapStatusStr[] = {
"SWAPOUT_NONE",
"SWAPOUT_WRITING",
- "SWAPOUT_DONE"
+ "SWAPOUT_DONE",
+ "SWAPOUT_FAILED"
};
/*
// XXX: Type names mislead. STORE_DISK_CLIENT actually means that we should
// open swapin file, aggressively trim memory, and ignore read-ahead gap.
// It does not mean we will read from disk exclusively (or at all!).
+// STORE_MEM_CLIENT covers all other cases, including in-memory entries,
+// newly created entries, and entries not backed by disk or memory cache.
// XXX: May create STORE_DISK_CLIENT with no disk caching configured.
// XXX: Collapsed clients cannot predict their type.
store_client_t
return STORE_MEM_CLIENT;
}
+ if (swapoutFailed())
+ return STORE_MEM_CLIENT;
+
if (store_status == STORE_OK) {
/* the object has completed. */
void
StoreEntry::checkDisk() const
{
- const bool ok = (swap_dirn < 0) == (swap_filen < 0) &&
- (swap_dirn < 0) == (swap_status == SWAPOUT_NONE) &&
- (swap_dirn < 0 || swap_dirn < Config.cacheSwap.n_configured);
-
- if (!ok) {
- debugs(88, DBG_IMPORTANT, "ERROR: inconsistent disk entry state " << *this);
- throw std::runtime_error("inconsistent disk entry state ");
+ try {
+ if (swap_dirn < 0) {
+ Must(swap_filen < 0);
+ Must(swap_status == SWAPOUT_NONE);
+ } else {
+ Must(swap_filen >= 0);
+ Must(swap_dirn < Config.cacheSwap.n_configured);
+ if (swapoutFailed()) {
+ Must(EBIT_TEST(flags, RELEASE_REQUEST));
+ } else {
+ Must(swappingOut() || swappedOut());
+ }
+ }
+ } catch (...) {
+ debugs(88, DBG_IMPORTANT, "ERROR: inconsistent disk entry state " <<
+ *this << "; problem: " << CurrentException);
+ throw;
}
}
if (getType() == STORE_DISK_CLIENT) {
/* assert we'll be able to get the data we want */
/* maybe we should open swapin_sio here */
- assert(entry->hasDisk() || entry->swappingOut());
+ assert(entry->hasDisk() && !entry->swapoutFailed());
}
}
dlinkDelete(&sc->node, &mem->clients);
-- mem->nclients;
- if (e->store_status == STORE_OK && !e->swappedOut())
+ const auto swapoutFinished = e->swappedOut() || e->swapoutFailed();
+ if (e->store_status == STORE_OK && !swapoutFinished)
e->swapOut();
if (sc->swapin_sio != NULL) {
return;
}
+ if (e->swapoutFailed()) {
+ debugs(20, DBG_IMPORTANT, "BUG: Attempt to swap in a failed-to-store entry " << *e << ". Salvaged.");
+ return;
+ }
+
assert(e->mem_obj != NULL);
sc->swapin_sio = storeOpen(e, storeSwapInFileNotify, storeSwapInFileClosed, sc);
}
/// XXX: unused, see a related StoreIOState::file_callback
static void
-storeSwapOutFileNotify(void *data, int errflag, StoreIOState::Pointer self)
+storeSwapOutFileNotify(void *, int, StoreIOState::Pointer)
{
- StoreEntry *e;
- static_cast<generic_cbdata *>(data)->unwrap(&e);
-
- MemObject *mem = e->mem_obj;
- assert(e->swappingOut());
- assert(mem);
- assert(mem->swapout.sio == self);
- assert(errflag == 0);
- assert(!e->hasDisk()); // if this fails, call SwapDir::disconnect(e)
- e->swap_filen = mem->swapout.sio->swap_filen;
- e->swap_dirn = mem->swapout.sio->swap_dirn;
+ assert(false);
}
static bool
storeConfigure();
}
+ // mark the locked entry for deletion
+ // TODO: Keep the memory entry (if any)
+ e->releaseRequest();
+ e->swap_status = SWAPOUT_FAILED;
e->disk().finalizeSwapoutFailure(*e);
- e->releaseRequest(); // TODO: Keep the memory entry (if any)
} else {
/* swapping complete */
debugs(20, 3, "storeSwapOutFileClosed: SwapOut complete: '" << e->url() << "' to " <<