/*
* 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 */
/**
/*!
* 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_GetDSlotFromList(&afs_discardDCList);
- osi_Assert(tdc);
+ if (!tdc) {
+ ReleaseWriteLock(&afs_xdcache);
+ return -1;
+ }
afs_discardDCCount--;
size = ((tdc->f.chunkBytes + afs_fsfragsize) ^ afs_fsfragsize) >> 10; /* round up */
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;
}
goto done;
}
- /* 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 (!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);
- osi_Assert(tdc);
+ 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);