/*
* Copyright 2000, International Business Machines Corporation and others.
- *$All Rights Reserved.
+ * All Rights Reserved.
*
* This software has been released under the terms of the IBM Public
* License. For details, see the LICENSE file in the top-level source
/* Forward declarations. */
static void afs_GetDownD(int anumber, int *aneedSpace, afs_int32 buckethint);
-static void afs_FreeDiscardedDCache(void);
+static int afs_FreeDiscardedDCache(void);
static void afs_DiscardDCache(struct dcache *);
static void afs_FreeDCache(struct dcache *);
/* For split cache */
int cacheDiskType; /*Type of backing disk for cache */
struct afs_cacheOps *afs_cacheType;
+
+/*
+ * The PFlush algorithm makes use of the fact that Fid.Unique is not used in
+ * below hash algorithms. Change it if need be so that flushing algorithm
+ * doesn't move things from one hash chain to another.
+ */
+/*Vnode, Chunk -> Hash table index */
+int DCHash(struct VenusFid *fid, afs_int32 chunk)
+{
+ afs_uint32 buf[3];
+
+ buf[0] = fid->Fid.Volume;
+ buf[1] = fid->Fid.Vnode;
+ buf[2] = chunk;
+ return opr_jhash(buf, 3, 0) & (afs_dhashsize - 1);
+}
+/*Vnode -> Other hash table index */
+int DVHash(struct VenusFid *fid)
+{
+ return opr_jhash_int2(fid->Fid.Volume, fid->Fid.Vnode, 0) &
+ (afs_dhashsize - 1);
+}
+
/*!
* Where is this vcache's entry associated dcache located/
* \param avc The vcache entry.
u_int afs_min_cache = 0;
/*!
+ * If there are waiters for the cache to drain, wake them if
+ * the number of free or discarded cache blocks reaches the
+ * CM_CACHESIZEDDRAINEDPCT limit.
+ *
+ * \note Environment:
+ * This routine must be called with the afs_xdcache lock held
+ * (in write mode).
+ */
+static void
+afs_WakeCacheWaitersIfDrained(void)
+{
+ if (afs_WaitForCacheDrain) {
+ if ((afs_blocksUsed - afs_blocksDiscarded) <=
+ PERCENT(CM_CACHESIZEDRAINEDPCT, afs_cacheBlocks)) {
+ afs_WaitForCacheDrain = 0;
+ afs_osi_Wakeup(&afs_WaitForCacheDrain);
+ }
+ }
+}
+
+/*!
* Keeps the cache clean and free by truncating uneeded files, when used.
* \param
* \return
while (1) {
cb_lowat = PERCENT((CM_DCACHESPACEFREEPCT - CM_DCACHEEXTRAPCT), afs_cacheBlocks);
ObtainWriteLock(&afs_xdcache, 266);
- if (afs_CacheTooFull) {
+ if (afs_CacheTooFull || afs_WaitForCacheDrain) {
int space_needed, slots_needed;
/* if we get woken up, we should try to clean something out */
for (counter = 0; counter < 10; counter++) {
if (slots_needed || space_needed)
afs_GetDownD(slots_needed, &space_needed, 0);
if ((space_needed <= 0) && (slots_needed <= 0)) {
- afs_CacheTooFull = 0;
break;
}
if (afs_termState == AFSOP_STOP_TRUNCDAEMON)
break;
}
- if (!afs_CacheIsTooFull())
+ if (!afs_CacheIsTooFull()) {
afs_CacheTooFull = 0;
+ afs_WakeCacheWaitersIfDrained();
+ }
} /* end of cache cleanup */
ReleaseWriteLock(&afs_xdcache);
*/
while (afs_blocksDiscarded && !afs_WaitForCacheDrain
&& (afs_termState != AFSOP_STOP_TRUNCDAEMON)) {
- afs_FreeDiscardedDCache();
+ int code = afs_FreeDiscardedDCache();
+ if (code) {
+ /* If we can't free any discarded dcache entries, that's okay.
+ * We're just doing this in the background; if someone needs
+ * discarded entries freed, they will try it themselves and/or
+ * signal us that the cache is too full. In any case, we'll
+ * try doing this again the next time we run through the loop.
+ */
+ break;
+ }
}
/* See if we need to continue to run. Someone may have
} else {
afs_FreeDCache(adc);
}
-
- if (afs_WaitForCacheDrain) {
- if (afs_blocksUsed <=
- PERCENT(CM_CACHESIZEDRAINEDPCT, afs_cacheBlocks)) {
- afs_WaitForCacheDrain = 0;
- afs_osi_Wakeup(&afs_WaitForCacheDrain);
- }
- }
} /*afs_FlushDCache */
afs_indexFlags[adc->index] |= IFFree;
adc->dflags |= DFEntryMod;
- if (afs_WaitForCacheDrain) {
- if ((afs_blocksUsed - afs_blocksDiscarded) <=
- PERCENT(CM_CACHESIZEDRAINEDPCT, afs_cacheBlocks)) {
- afs_WaitForCacheDrain = 0;
- afs_osi_Wakeup(&afs_WaitForCacheDrain);
- }
- }
+ afs_WakeCacheWaitersIfDrained();
} /* afs_FreeDCache */
/*!
adc->dflags |= DFEntryMod;
afs_indexFlags[adc->index] |= IFDiscarded;
- if (afs_WaitForCacheDrain) {
- if ((afs_blocksUsed - afs_blocksDiscarded) <=
- PERCENT(CM_CACHESIZEDRAINEDPCT, afs_cacheBlocks)) {
- afs_WaitForCacheDrain = 0;
- afs_osi_Wakeup(&afs_WaitForCacheDrain);
+ afs_WakeCacheWaitersIfDrained();
+} /*afs_DiscardDCache */
+
+/**
+ * Get a dcache entry from the discard or free list
+ *
+ * @param[in] indexp A pointer to the head of the dcache free list or discard
+ * list (afs_freeDCList, or afs_discardDCList)
+ *
+ * @return A dcache from that list, or NULL if none could be retrieved.
+ *
+ * @pre afs_xdcache is write-locked
+ */
+static struct dcache *
+afs_GetDSlotFromList(afs_int32 *indexp)
+{
+ struct dcache *tdc;
+
+ for ( ; *indexp != NULLIDX; indexp = &afs_dvnextTbl[*indexp]) {
+ tdc = afs_GetUnusedDSlot(*indexp);
+ if (tdc) {
+ osi_Assert(tdc->refCount == 1);
+ ReleaseReadLock(&tdc->tlock);
+ *indexp = afs_dvnextTbl[tdc->index];
+ afs_dvnextTbl[tdc->index] = NULLIDX;
+ return tdc;
}
}
-
-} /*afs_DiscardDCache */
+ return NULL;
+}
/*!
* Free the next element on the list of discarded cache elements.
+ *
+ * Returns -1 if we encountered an error preventing us from freeing a
+ * discarded dcache, or 0 on success.
*/
-static void
+static int
afs_FreeDiscardedDCache(void)
{
struct dcache *tdc;
ObtainWriteLock(&afs_xdcache, 510);
if (!afs_blocksDiscarded) {
ReleaseWriteLock(&afs_xdcache);
- return;
+ return 0;
}
/*
* Get an entry from the list of discarded cache elements
*/
- tdc = afs_GetUnusedDSlot(afs_discardDCList);
- osi_Assert(tdc);
- osi_Assert(tdc->refCount == 1);
- ReleaseReadLock(&tdc->tlock);
+ tdc = afs_GetDSlotFromList(&afs_discardDCList);
+ if (!tdc) {
+ ReleaseWriteLock(&afs_xdcache);
+ return -1;
+ }
- afs_discardDCList = afs_dvnextTbl[tdc->index];
- afs_dvnextTbl[tdc->index] = NULLIDX;
afs_discardDCCount--;
size = ((tdc->f.chunkBytes + afs_fsfragsize) ^ afs_fsfragsize) >> 10; /* round up */
afs_blocksDiscarded -= size;
ReleaseWriteLock(&tdc->lock);
afs_PutDCache(tdc);
ReleaseWriteLock(&afs_xdcache);
+
+ return 0;
}
/*!
while (afs_blocksDiscarded
&& (afs_blocksUsed >
PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks))) {
- afs_FreeDiscardedDCache();
+ int code = afs_FreeDiscardedDCache();
+ if (code) {
+ /* Callers depend on us to get the afs_blocksDiscarded count down.
+ * If we cannot do that, the callers can spin by calling us over
+ * and over. Panic for now until we can figure out something
+ * better. */
+ osi_Panic("Error freeing discarded dcache");
+ }
}
return 0;
}
if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
int releaseTlock = 1;
tdc = afs_GetValidDSlot(index);
- if (!tdc) osi_Panic("afs_TryToSmush tdc");
+ if (!tdc) {
+ /* afs_TryToSmush is best-effort; we may not actually discard
+ * everything, so failure to discard a dcache due to an i/o
+ * error is okay. */
+ continue;
+ }
if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {
if (sync) {
if ((afs_indexFlags[index] & IFDataMod) == 0
*/
i = DCHash(&avc->f.fid, chunk);
ObtainWriteLock(&afs_xdcache, 278);
- for (index = afs_dchashTbl[i]; index != NULLIDX;) {
+ for (index = afs_dchashTbl[i]; index != NULLIDX; index = afs_dcnextTbl[index]) {
if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
tdc = afs_GetValidDSlot(index);
- if (!tdc) osi_Panic("afs_FindDCache tdc");
+ if (!tdc) {
+ /* afs_FindDCache is best-effort; we may not find the given
+ * file/offset, so if we cannot find the given dcache due to
+ * i/o errors, that is okay. */
+ continue;
+ }
ReleaseReadLock(&tdc->tlock);
if (!FidCmp(&tdc->f.fid, &avc->f.fid) && chunk == tdc->f.chunk) {
break; /* leaving refCount high for caller */
}
afs_PutDCache(tdc);
}
- index = afs_dcnextTbl[index];
}
if (index != NULLIDX) {
hset(afs_indexTimes[tdc->index], afs_indexCounter);
return NULL;
} /*afs_FindDCache */
+/* only call these from afs_AllocDCache() */
+static struct dcache *
+afs_AllocFreeDSlot(void)
+{
+ struct dcache *tdc;
+
+ tdc = afs_GetDSlotFromList(&afs_freeDCList);
+ if (!tdc) {
+ return NULL;
+ }
+ afs_indexFlags[tdc->index] &= ~IFFree;
+ ObtainWriteLock(&tdc->lock, 604);
+ afs_freeDCCount--;
+
+ return tdc;
+}
+static struct dcache *
+afs_AllocDiscardDSlot(afs_int32 lock)
+{
+ struct dcache *tdc;
+ afs_uint32 size = 0;
+ struct osi_file *file;
+
+ tdc = afs_GetDSlotFromList(&afs_discardDCList);
+ if (!tdc) {
+ return NULL;
+ }
+ afs_indexFlags[tdc->index] &= ~IFDiscarded;
+ ObtainWriteLock(&tdc->lock, 605);
+ afs_discardDCCount--;
+ size =
+ ((tdc->f.chunkBytes +
+ afs_fsfragsize) ^ afs_fsfragsize) >> 10;
+ tdc->f.states &= ~(DRO|DBackup|DRW);
+ afs_DCMoveBucket(tdc, size, 0);
+ afs_blocksDiscarded -= size;
+ afs_stats_cmperf.cacheBlocksDiscarded = afs_blocksDiscarded;
+ if ((lock & 2)) {
+ /* Truncate the chunk so zeroes get filled properly */
+ file = afs_CFileOpen(&tdc->f.inode);
+ afs_CFileTruncate(file, 0);
+ afs_CFileClose(file);
+ afs_AdjustSize(tdc, 0);
+ }
+
+ return tdc;
+}
/*!
* Get a fresh dcache from the free or discarded list.
struct VenusFid *ashFid)
{
struct dcache *tdc = NULL;
- afs_uint32 size = 0;
- struct osi_file *file;
- if (afs_discardDCList == NULLIDX
- || ((lock & 2) && afs_freeDCList != NULLIDX)) {
-
- afs_indexFlags[afs_freeDCList] &= ~IFFree;
- tdc = afs_GetUnusedDSlot(afs_freeDCList);
- osi_Assert(tdc);
- osi_Assert(tdc->refCount == 1);
- ReleaseReadLock(&tdc->tlock);
- ObtainWriteLock(&tdc->lock, 604);
- afs_freeDCList = afs_dvnextTbl[tdc->index];
- afs_freeDCCount--;
+ /* if (lock & 2), prefer 'free' dcaches; otherwise, prefer 'discard'
+ * dcaches. In either case, try both if our first choice doesn't work. */
+ if ((lock & 2)) {
+ tdc = afs_AllocFreeDSlot();
+ if (!tdc) {
+ tdc = afs_AllocDiscardDSlot(lock);
+ }
} else {
- afs_indexFlags[afs_discardDCList] &= ~IFDiscarded;
- tdc = afs_GetUnusedDSlot(afs_discardDCList);
- osi_Assert(tdc);
- osi_Assert(tdc->refCount == 1);
- ReleaseReadLock(&tdc->tlock);
- ObtainWriteLock(&tdc->lock, 605);
- afs_discardDCList = afs_dvnextTbl[tdc->index];
- afs_discardDCCount--;
- size =
- ((tdc->f.chunkBytes +
- afs_fsfragsize) ^ afs_fsfragsize) >> 10;
- tdc->f.states &= ~(DRO|DBackup|DRW);
- afs_DCMoveBucket(tdc, size, 0);
- afs_blocksDiscarded -= size;
- afs_stats_cmperf.cacheBlocksDiscarded = afs_blocksDiscarded;
- if (lock & 2) {
- /* Truncate the chunk so zeroes get filled properly */
- file = afs_CFileOpen(&tdc->f.inode);
- afs_CFileTruncate(file, 0);
- afs_CFileClose(file);
- afs_AdjustSize(tdc, 0);
+ tdc = afs_AllocDiscardDSlot(lock);
+ if (!tdc) {
+ tdc = afs_AllocFreeDSlot();
}
}
+ if (!tdc) {
+ return NULL;
+ }
/*
* Locks held:
*/
if (!tdc) { /* If the hint wasn't the right dcache entry */
+ int dslot_error = 0;
/*
* Hash on the [fid, chunk] and get the corresponding dcache index
* after write-locking the dcache.
ObtainWriteLock(&afs_xdcache, 280);
us = NULLIDX;
- for (index = afs_dchashTbl[i]; index != NULLIDX;) {
+ for (index = afs_dchashTbl[i]; index != NULLIDX; us = index, index = afs_dcnextTbl[index]) {
if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
tdc = afs_GetValidDSlot(index);
if (!tdc) {
- ReleaseWriteLock(&afs_xdcache);
- goto done;
+ /* we got an i/o error when trying to get the given dslot,
+ * but do not bail out just yet; it is possible the dcache
+ * we're looking for is elsewhere, so it doesn't matter if
+ * we can't load this one. */
+ dslot_error = 1;
+ continue;
}
ReleaseReadLock(&tdc->tlock);
/*
afs_PutDCache(tdc);
tdc = 0;
}
- us = index;
- index = afs_dcnextTbl[index];
}
/*
afs_Trace2(afs_iclSetp, CM_TRACE_GETDCACHE1, ICL_TYPE_POINTER,
avc, ICL_TYPE_INT32, chunk);
- /* Make sure there is a free dcache entry for us to use */
- if (afs_discardDCList == NULLIDX && afs_freeDCList == NULLIDX) {
- while (1) {
- if (!setLocks)
- avc->f.states |= CDCLock;
- /* just need slots */
- afs_GetDownD(5, (int *)0, afs_DCGetBucket(avc));
- if (!setLocks)
- avc->f.states &= ~CDCLock;
- if (afs_discardDCList != NULLIDX
- || afs_freeDCList != NULLIDX)
- break;
- /* If we can't get space for 5 mins we give up and panic */
- if (++downDCount > 300) {
- osi_Panic("getdcache");
- }
- ReleaseWriteLock(&afs_xdcache);
- /*
- * Locks held:
- * avc->lock(R) if setLocks
- * avc->lock(W) if !setLocks
- */
- afs_osi_Wait(1000, 0, 0);
- goto RetryLookup;
- }
+ if (dslot_error) {
+ /* We couldn't find the dcache we want, but we hit some i/o
+ * errors when trying to find it, so we're not sure if the
+ * dcache we want is in the cache or not. Error out, so we
+ * don't try to possibly create 2 separate dcaches for the
+ * same exact data. */
+ ReleaseWriteLock(&afs_xdcache);
+ goto done;
}
+ if (afs_discardDCList == NULLIDX && afs_freeDCList == NULLIDX) {
+ if (!setLocks)
+ avc->f.states |= CDCLock;
+ /* just need slots */
+ afs_GetDownD(5, (int *)0, afs_DCGetBucket(avc));
+ if (!setLocks)
+ avc->f.states &= ~CDCLock;
+ }
tdc = afs_AllocDCache(avc, chunk, aflags, NULL);
+ if (!tdc) {
+ /* If we can't get space for 5 mins we give up and panic */
+ if (++downDCount > 300)
+ osi_Panic("getdcache");
+ ReleaseWriteLock(&afs_xdcache);
+ /*
+ * Locks held:
+ * avc->lock(R) if setLocks
+ * avc->lock(W) if !setLocks
+ */
+ afs_osi_Wait(1000, 0, 0);
+ goto RetryLookup;
+ }
+
+ /*
+ * Locks held:
+ * avc->lock(R) if setLocks
+ * avc->lock(W) if !setLocks
+ * tdc->lock(W)
+ * afs_xdcache(W)
+ */
/*
* Now add to the two hash chains - note that i is still set
afs_PutDCache(tdc);
tdc = 0;
ReleaseReadLock(&avc->lock);
+
+ if (tc) {
+ /* If we have a connection, we must put it back,
+ * since afs_Analyze will not be called here. */
+ afs_PutConn(tc, rxconn, SHARED_LOCK);
+ }
+
slowPass = 1;
goto RetryGetDCache;
}
*/
struct afs_fheader theader;
- theader.magic = AFS_FHMAGIC;
- theader.firstCSize = AFS_FIRSTCSIZE;
- theader.otherCSize = AFS_OTHERCSIZE;
- theader.version = AFS_CI_VERSION;
- theader.dataSize = sizeof(struct fcache);
+ afs_InitFHeader(&theader);
afs_osi_Write(afs_cacheInodep, 0, &theader, sizeof(theader));
}
ReleaseWriteLock(&afs_xdcache);
*
* Parameters:
* aslot : Dcache slot to look at.
- * needvalid : Whether the specified slot should already exist
+ * type : What 'type' of dslot to get; see the dslot_state enum
*
* Environment:
* Must be called with afs_xdcache write-locked.
*/
struct dcache *
-afs_MemGetDSlot(afs_int32 aslot, int indexvalid, int datavalid)
+afs_MemGetDSlot(afs_int32 aslot, dslot_state type)
{
struct dcache *tdc;
int existing = 0;
return tdc;
}
- /* if 'indexvalid' is true, the slot must already exist and be populated
- * somewhere. for memcache, the only place that dcache entries exist is
- * in memory, so if we did not find it above, something is very wrong. */
- osi_Assert(!indexvalid);
+ /* if we got here, the given slot is not in memory in our list of known
+ * slots. for memcache, the only place a dslot can exist is in memory, so
+ * if the caller is expecting to get back a known dslot, and we've reached
+ * here, something is very wrong. DSLOT_NEW is the only type of dslot that
+ * may not exist; for all others, the caller assumes the given dslot
+ * already exists. so, 'type' had better be DSLOT_NEW here, or something is
+ * very wrong. */
+ osi_Assert(type == DSLOT_NEW);
if (!afs_freeDSList)
afs_GetDownDSlot(4);
*
* Parameters:
* aslot : Dcache slot to look at.
- * indexvalid : 1 if we know the slot we're giving is valid, and thus
- * reading the dcache from the disk index should succeed. 0
- * if we are initializing a new dcache, and so reading from
- * the disk index may fail.
- * datavalid : 0 if we are loading a dcache entry from the free or
- * discard list, so we know the data in the given dcache is
- * not valid. 1 if we are loading a known used dcache, so the
- * data in the dcache must be valid.
+ * type : What 'type' of dslot to get; see the dslot_state enum
*
* Environment:
* afs_xdcache lock write-locked.
*/
struct dcache *
-afs_UFSGetDSlot(afs_int32 aslot, int indexvalid, int datavalid)
+afs_UFSGetDSlot(afs_int32 aslot, dslot_state type)
{
afs_int32 code;
struct dcache *tdc;
last_error = code;
#endif
lasterrtime = osi_Time();
- if (indexvalid) {
+ if (type != DSLOT_NEW) {
+ /* If we are requesting a non-DSLOT_NEW slot, this is an error.
+ * non-DSLOT_NEW slots are supposed to already exist, so if we
+ * failed to read in the slot, something is wrong. */
struct osi_stat tstat;
if (afs_osi_Stat(afs_cacheInodep, &tstat)) {
tstat.size = -1;
}
if (!afs_CellNumValid(tdc->f.fid.Cell)) {
entryok = 0;
- if (datavalid) {
+ if (type == DSLOT_VALID) {
osi_Panic("afs: needed valid dcache but index %d off %d has "
"invalid cell num %d\n",
(int)aslot, off, (int)tdc->f.fid.Cell);
}
}
- if (datavalid && tdc->f.fid.Fid.Volume == 0) {
+ if (type == DSLOT_VALID && tdc->f.fid.Fid.Volume == 0) {
osi_Panic("afs: invalid zero-volume dcache entry at slot %d off %d",
(int)aslot, off);
}
- if (!entryok || !datavalid) {
+ if (type == DSLOT_UNUSED) {
+ /* the requested dslot is known to exist, but contain invalid data
+ * (this happens when we're using a dslot from the free or discard
+ * list). be sure not to re-use the data in it, so force invalidation.
+ */
+ entryok = 0;
+ }
+
+ if (!entryok) {
tdc->f.fid.Cell = 0;
tdc->f.fid.Fid.Volume = 0;
tdc->f.chunk = -1;
tdc->f.states &= ~(DRO|DBackup|DRW);
afs_DCMoveBucket(tdc, 0, 0);
} else {
- if (&tdc->f != 0) {
- if (tdc->f.states & DRO) {
- afs_DCMoveBucket(tdc, 0, 2);
- } else if (tdc->f.states & DBackup) {
- afs_DCMoveBucket(tdc, 0, 1);
- } else {
- afs_DCMoveBucket(tdc, 0, 1);
- }
+ if (tdc->f.states & DRO) {
+ afs_DCMoveBucket(tdc, 0, 2);
+ } else if (tdc->f.states & DBackup) {
+ afs_DCMoveBucket(tdc, 0, 1);
+ } else {
+ afs_DCMoveBucket(tdc, 0, 1);
}
}
tdc->refCount = 1;
* is already being handled by the higher-level code.
*/
if ((avc->f.states & CSafeStore) == 0) {
- tb->code = 0;
+ tb->code_raw = tb->code_checkcode = 0;
tb->flags |= BUVALID;
if (tb->flags & BUWAIT) {
tb->flags &= ~BUWAIT;
if ((tdc->f.states & DWriting) || tdc->f.fid.Fid.Volume == 0)
fileIsBad = 1;
tfile = osi_UFSOpen(&tdc->f.inode);
+ if (!tfile) {
+ ReleaseWriteLock(&afs_xdcache);
+ ReleaseWriteLock(&tdc->lock);
+ afs_PutDCache(tdc);
+ return ENOENT;
+ }
+
code = afs_osi_Stat(tfile, &tstat);
if (code)
osi_Panic("initcachefile stat");
afs_warn("afsd: memory cache too large for available memory.\n");
afs_warn("afsd: AFS files cannot be accessed.\n\n");
dcacheDisabled = 1;
- afiles = ablocks = 0;
} else
afs_warn("Memory cache: Allocating %d dcache entries...",
aDentries);
/* Get a fresh dcache. */
new_dc = afs_AllocDCache(avc, 0, 0, &shadow_fid);
+ osi_Assert(new_dc);
ObtainReadLock(&adc->mflock);