#include "afs/afs_cbqueue.h"
#include "afs/afs_osidnlc.h"
+#include <opr/ffs.h>
+
/* Forward declarations. */
static void afs_GetDownD(int anumber, int *aneedSpace, afs_int32 buckethint);
static int afs_FreeDiscardedDCache(void);
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.
AFS_STATCNT(afs_AdjustSize);
+ if (newSize > afs_OtherCSize && !(adc->f.fid.Fid.Vnode & 1)) {
+ /* No non-dir cache files should be larger than the chunk size.
+ * (Directory blobs are fetched in a single chunk file, so directories
+ * can be larger.) If someone is requesting that a chunk is larger than
+ * the chunk size, something strange is happening. Log a message about
+ * it, to give a hint to subsequent strange behavior, if any occurs. */
+ static int warned;
+ if (!warned) {
+ warned = 1;
+ afs_warn("afs: Warning: dcache %d is very large (%d > %d). This "
+ "should not happen, but trying to continue regardless. If "
+ "AFS starts hanging or behaving strangely, this might be "
+ "why.\n",
+ adc->index, newSize, afs_OtherCSize);
+ }
+ }
+
adc->dflags |= DFEntryMod;
oldSize = ((adc->f.chunkBytes + afs_fsfragsize) ^ afs_fsfragsize) >> 10; /* round up */
adc->f.chunkBytes = newSize;
{
struct dcache *tdc;
- for ( ; *indexp != NULLIDX; indexp = &afs_dvnextTbl[*indexp]) {
+ if (*indexp != NULLIDX) {
tdc = afs_GetUnusedDSlot(*indexp);
if (tdc) {
osi_Assert(tdc->refCount == 1);
* Truncate the element to reclaim its space
*/
tfile = afs_CFileOpen(&tdc->f.inode);
+ osi_Assert(tfile);
afs_CFileTruncate(tfile, 0);
afs_CFileClose(tfile);
afs_AdjustSize(tdc, 0);
tdc = afs_GetValidDSlot(index);
if (!tdc) {
/* afs_TryToSmush is best-effort; we may not actually discard
- * everything, so failure to discard a dcache due to an i/o
+ * everything, so failure to discard dcaches due to an i/o
* error is okay. */
- continue;
+ break;
}
if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {
if (sync) {
i = afs_dvnextTbl[index];
if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
tdc = afs_GetValidDSlot(index);
- if (tdc) {
- if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {
- totalChunks--;
- }
- ReleaseReadLock(&tdc->tlock);
- afs_PutDCache(tdc);
- }
+ if (!tdc) {
+ break;
+ }
+ if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {
+ totalChunks--;
+ }
+ ReleaseReadLock(&tdc->tlock);
+ afs_PutDCache(tdc);
}
}
ReleaseWriteLock(&afs_xdcache);
/* 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;
+ index = NULLIDX;
+ break;
}
ReleaseReadLock(&tdc->tlock);
if (!FidCmp(&tdc->f.fid, &avc->f.fid) && chunk == tdc->f.chunk) {
if ((lock & 2)) {
/* Truncate the chunk so zeroes get filled properly */
file = afs_CFileOpen(&tdc->f.inode);
+ osi_Assert(file);
afs_CFileTruncate(file, 0);
afs_CFileClose(file);
afs_AdjustSize(tdc, 0);
*
* \return The new dcache.
*/
-struct dcache *
+static struct dcache *
afs_AllocDCache(struct vcache *avc, afs_int32 chunk, afs_int32 lock,
struct VenusFid *ashFid)
{
if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
tdc = afs_GetValidDSlot(index);
if (!tdc) {
- /* 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. */
+ /* we got an i/o error when trying to get the given dslot.
+ * it's possible the dslot we're looking for is elsewhere,
+ * but most likely the disk cache is currently unusable, so
+ * all afs_GetValidDSlot calls will fail, so just bail out. */
dslot_error = 1;
- continue;
+ index = NULLIDX;
+ break;
}
ReleaseReadLock(&tdc->tlock);
/*
}
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;
+ if (afs_discardDCList == NULLIDX && afs_freeDCList == NULLIDX) {
+ /* It looks like afs_AllocDCache failed because we don't
+ * have any free dslots to use. Maybe if we wait a little
+ * while, we'll be able to free up some slots, so try for 5
+ * minutes, then bail out. */
+ if (++downDCount > 300) {
+ afs_warn("afs: Unable to get free cache space for file "
+ "%u:%u.%u.%u for 5 minutes; failing with an i/o error\n",
+ avc->f.fid.Cell,
+ avc->f.fid.Fid.Volume,
+ avc->f.fid.Fid.Vnode,
+ avc->f.fid.Fid.Unique);
+ goto done;
+ }
+ afs_osi_Wait(1000, 0, 0);
+ goto RetryLookup;
+ }
+
+ /* afs_AllocDCache failed, but not because we're out of free
+ * dslots. Something must be screwy with the cache, so bail out
+ * immediately without waiting. */
+ afs_warn("afs: Error while alloc'ing cache slot for file "
+ "%u:%u.%u.%u; failing with an i/o error\n",
+ avc->f.fid.Cell,
+ avc->f.fid.Fid.Volume,
+ avc->f.fid.Fid.Vnode,
+ avc->f.fid.Fid.Unique);
+ goto done;
}
/*
/* no data in file to read at this position */
UpgradeSToWLock(&tdc->lock, 607);
file = afs_CFileOpen(&tdc->f.inode);
+ osi_Assert(file);
afs_CFileTruncate(file, 0);
afs_CFileClose(file);
afs_AdjustSize(tdc, 0);
maxGoodLength = avc->f.truncPos;
size = AFS_CHUNKSIZE(abyte); /* expected max size */
- if (Position + size > maxGoodLength)
+ if (Position > maxGoodLength) { /* If we're beyond EOF */
+ size = 0;
+ } else if (Position + size > maxGoodLength) {
size = maxGoodLength - Position;
- if (size < 0)
- size = 0; /* Handle random races */
+ }
+ osi_Assert(size >= 0);
+
if (size > tdc->f.chunkBytes) {
/* pre-reserve estimated space for file */
afs_AdjustSize(tdc, size); /* changes chunkBytes */
* avc->f.truncPos to reappear, instead of extending the file
* with NUL bytes. */
size = AFS_CHUNKSIZE(abyte);
- if (Position + size > avc->f.truncPos) {
+ if (Position > avc->f.truncPos) {
+ size = 0;
+ } else if (Position + size > avc->f.truncPos) {
size = avc->f.truncPos - Position;
}
- if (size < 0) {
- size = 0;
- }
+ osi_Assert(size >= 0);
}
}
if (afs_mariner && !tdc->f.chunk)
*/
DZap(tdc); /* pages in cache may be old */
file = afs_CFileOpen(&tdc->f.inode);
+ if (!file) {
+ /* We can't access the file in the disk cache backing this dcache;
+ * bail out. */
+ ReleaseWriteLock(&tdc->lock);
+ afs_PutDCache(tdc);
+ tdc = NULL;
+ goto done;
+ }
afs_RemoveVCB(&avc->f.fid);
tdc->f.states |= DWriting;
tdc->dflags |= DFFetching;
afs_CFileTruncate(file, size); /* prune it */
} else {
if (!setLocks || slowPass) {
- ObtainWriteLock(&afs_xcbhash, 453);
- afs_DequeueCallback(avc);
- avc->f.states &= ~(CStatd | CUnique);
- avc->callback = NULL;
- ReleaseWriteLock(&afs_xcbhash);
- if (avc->f.fid.Fid.Vnode & 1 || (vType(avc) == VDIR))
- osi_dnlc_purgedp(avc);
+ afs_StaleVCacheFlags(avc, AFS_STALEVC_CLEARCB, CUnique);
} else {
/* Something lost. Forget about performance, and go
* back with a vcache write lock.
ReleaseWriteLock(&tdc->lock);
afs_PutDCache(tdc);
if (!afs_IsDynroot(avc)) {
- ObtainWriteLock(&afs_xcbhash, 454);
- afs_DequeueCallback(avc);
- avc->f.states &= ~(CStatd | CUnique);
- ReleaseWriteLock(&afs_xcbhash);
- if (avc->f.fid.Fid.Vnode & 1 || (vType(avc) == VDIR))
- osi_dnlc_purgedp(avc);
+ afs_StaleVCacheFlags(avc, 0, CUnique);
/*
* Locks held:
* avc->lock(W); assert(!setLocks || slowPass)
* Environment:
* The afs_xdcache is write-locked through this whole affair.
*/
-void
+int
afs_WriteThroughDSlots(void)
{
struct dcache *tdc;
afs_int32 i, touchedit = 0;
+ int code = 0;
struct afs_q DirtyQ, *tq;
#define DQTODC(q) ((struct dcache *)(((char *) (q)) - sizeof(struct afs_q)))
- for (tq = DirtyQ.prev; tq != &DirtyQ; tq = QPrev(tq)) {
+ for (tq = DirtyQ.prev; tq != &DirtyQ && code == 0; tq = QPrev(tq)) {
tdc = DQTODC(tq);
if (tdc->dflags & DFEntryMod) {
int wrLock;
if (wrLock && (tdc->dflags & DFEntryMod)) {
tdc->dflags &= ~DFEntryMod;
ObtainWriteLock(&afs_xdcache, 620);
- osi_Assert(afs_WriteDCache(tdc, 1) == 0);
+ code = afs_WriteDCache(tdc, 1);
ReleaseWriteLock(&afs_xdcache);
- touchedit = 1;
+ if (code) {
+ /* We didn't successfully write out the dslot; make sure we
+ * try again later */
+ tdc->dflags |= DFEntryMod;
+ } else {
+ touchedit = 1;
+ }
}
if (wrLock)
ReleaseWriteLock(&tdc->lock);
afs_PutDCache(tdc);
}
+ if (code) {
+ return code;
+ }
+
ObtainWriteLock(&afs_xdcache, 617);
if (!touchedit && (cacheDiskType != AFS_FCACHE_TYPE_MEM)) {
/* Touch the file to make sure that the mtime on the file is kept
afs_osi_Write(afs_cacheInodep, 0, &theader, sizeof(theader));
}
ReleaseWriteLock(&afs_xdcache);
+ return 0;
}
/*
*
* 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 (indexvalid && !datavalid) {
- /* we know that the given dslot does exist, but the data in it is not
- * valid. this only occurs when we pull a dslot from the free or
- * discard list, so be sure not to re-use the data; force invalidation.
+ 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;
}
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;
struct dcache *tdp;
int i;
int code;
+ int afs_dhashbits;
afs_freeDCList = NULLIDX;
afs_discardDCList = NULLIDX;
if (!aDentries)
aDentries = DDSIZE;
+ /* afs_dhashsize defaults to 1024 */
if (aDentries > 512)
afs_dhashsize = 2048;
+ /* Try to keep the average chain length around two unless the table
+ * would be ridiculously big. */
+ if (aDentries > 4096) {
+ afs_dhashbits = opr_fls(aDentries) - 3;
+ /* Cap the hash tables to 32k entries. */
+ if (afs_dhashbits > 15)
+ afs_dhashbits = 15;
+ afs_dhashsize = opr_jhash_size(afs_dhashbits);
+ }
/* initialize hash tables */
afs_dvhashTbl = afs_osi_Alloc(afs_dhashsize * sizeof(afs_int32));
osi_Assert(afs_dvhashTbl != NULL);
/* Open the files. */
tfile_src = afs_CFileOpen(&adc->f.inode);
tfile_dst = afs_CFileOpen(&new_dc->f.inode);
+ osi_Assert(tfile_src);
+ osi_Assert(tfile_dst);
/* And now copy dir dcache data into this dcache,
* 4k at a time.