} /*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;
+ }
+ }
+ return NULL;
+}
+
/*!
* Free the next element on the list of discarded cache elements.
*/
/*
* Get an entry from the list of discarded cache elements
*/
- tdc = afs_GetNewDSlot(afs_discardDCList);
- osi_Assert(tdc->refCount == 1);
- ReleaseReadLock(&tdc->tlock);
+ tdc = afs_GetDSlotFromList(&afs_discardDCList);
+ osi_Assert(tdc);
- 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;
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_GetNewDSlot(afs_freeDCList);
- 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_GetNewDSlot(afs_discardDCList);
- 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
*/
struct dcache *
-afs_MemGetDSlot(afs_int32 aslot, int needvalid)
+afs_MemGetDSlot(afs_int32 aslot, int indexvalid, int datavalid)
{
struct dcache *tdc;
int existing = 0;
return tdc;
}
- /* if 'needvalid' is true, the slot must already exist and be populated
+ /* 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(!needvalid);
+ osi_Assert(!indexvalid);
if (!afs_freeDSList)
afs_GetDownDSlot(4);
*
* Parameters:
* aslot : Dcache slot to look at.
- * needvalid : Whether the specified slot should already exist
+ * 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.
*
* Environment:
* afs_xdcache lock write-locked.
*/
struct dcache *
-afs_UFSGetDSlot(afs_int32 aslot, int needvalid)
+afs_UFSGetDSlot(afs_int32 aslot, int indexvalid, int datavalid)
{
afs_int32 code;
struct dcache *tdc;
last_error = code;
#endif
lasterrtime = osi_Time();
- if (needvalid) {
+ if (indexvalid) {
struct osi_stat tstat;
if (afs_osi_Stat(afs_cacheInodep, &tstat)) {
tstat.size = -1;
}
if (!afs_CellNumValid(tdc->f.fid.Cell)) {
entryok = 0;
- if (needvalid) {
+ if (datavalid) {
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 (needvalid && tdc->f.fid.Fid.Volume == 0) {
+ if (datavalid && tdc->f.fid.Fid.Volume == 0) {
osi_Panic("afs: invalid zero-volume dcache entry at slot %d off %d",
(int)aslot, off);
}
- if (!entryok) {
+ if (!entryok || !datavalid) {
tdc->f.fid.Cell = 0;
tdc->f.fid.Fid.Volume = 0;
tdc->f.chunk = -1;
/* Get a fresh dcache. */
new_dc = afs_AllocDCache(avc, 0, 0, &shadow_fid);
+ osi_Assert(new_dc);
ObtainReadLock(&adc->mflock);