tlen = avc->f.truncPos;
avc->f.truncPos = AFS_NOTRUNC;
avc->f.states &= ~CExtendedFile;
+ memset(&InStatus, 0, sizeof(InStatus));
do {
tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK, &rxconn);
AFS_STATCNT(afs_StoreAllSegments);
- hset(oldDV, avc->f.m.DataVersion);
- hset(newDV, avc->f.m.DataVersion);
hash = DVHash(&avc->f.fid);
foreign = (avc->f.states & CForeign);
dcList = osi_AllocLargeSpace(AFS_LRALLOCSIZ);
/*printf("Net down in afs_StoreSegments\n");*/
return ENETDOWN;
}
+
+ /*
+ * Can't do this earlier because osi_VM_StoreAllSegments drops locks
+ * and can indirectly do some stores that increase the DV.
+ */
+ hset(oldDV, avc->f.m.DataVersion);
+ hset(newDV, avc->f.m.DataVersion);
+
ConvertWToSLock(&avc->lock);
/*
ObtainWriteLock(&afs_xdcache, 285);
for (j = 0, safety = 0, index = afs_dvhashTbl[hash];
- index != NULLIDX && safety < afs_cacheFiles + 2;) {
+ index != NULLIDX && safety < afs_cacheFiles + 2;
+ index = afs_dvnextTbl[index]) {
if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
tdc = afs_GetValidDSlot(index);
- if (!tdc) osi_Panic("afs_StoreAllSegments tdc dv");
+ if (!tdc) {
+ /* This is okay; since manipulating the dcaches at this
+ * point is best-effort. We only get a dcache here to
+ * increment the dv and turn off DWriting. If we were
+ * supposed to do that for a dcache, but could not
+ * due to an I/O error, it just means the dv won't
+ * be updated so we don't be able to use that cached
+ * chunk in the future. That's inefficient, but not
+ * an error. */
+ break;
+ }
ReleaseReadLock(&tdc->tlock);
if (!FidCmp(&tdc->f.fid, &avc->f.fid)
afs_PutDCache(tdc);
}
}
-
- index = afs_dvnextTbl[index];
}
ReleaseWriteLock(&afs_xdcache);
} /*afs_StoreAllSegments (new 03/02/94) */
-
-/*
- * afs_InvalidateAllSegments
- *
- * Description:
- * Invalidates all chunks for a given file
- *
- * Parameters:
- * avc : Pointer to vcache entry.
- *
- * Environment:
- * For example, called after an error has been detected. Called
- * with avc write-locked, and afs_xdcache unheld.
- */
-
int
-afs_InvalidateAllSegments(struct vcache *avc)
+afs_InvalidateAllSegments_once(struct vcache *avc)
{
struct dcache *tdc;
afs_int32 hash;
afs_int32 index;
- struct dcache **dcList;
- int i, dcListMax, dcListCount;
+ struct dcache **dcList = NULL;
+ int i, dcListMax, dcListCount = 0;
AFS_STATCNT(afs_InvalidateAllSegments);
afs_Trace2(afs_iclSetp, CM_TRACE_INVALL, ICL_TYPE_POINTER, avc,
hash = DVHash(&avc->f.fid);
avc->f.truncPos = AFS_NOTRUNC; /* don't truncate later */
avc->f.states &= ~CExtendedFile; /* not any more */
- ObtainWriteLock(&afs_xcbhash, 459);
- afs_DequeueCallback(avc);
- avc->f.states &= ~(CStatd | CDirty); /* mark status information as bad, too */
- ReleaseWriteLock(&afs_xcbhash);
- if (avc->f.fid.Fid.Vnode & 1 || (vType(avc) == VDIR))
- osi_dnlc_purgedp(avc);
+ afs_StaleVCacheFlags(avc, 0, CDirty);
/* Blow away pages; for now, only for Solaris */
#if (defined(AFS_SUN5_ENV))
if (WriteLocked(&avc->lock))
for (index = afs_dvhashTbl[hash]; index != NULLIDX;) {
if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
tdc = afs_GetValidDSlot(index);
- if (!tdc) osi_Panic("afs_InvalidateAllSegments tdc count");
+ if (!tdc) {
+ goto error;
+ }
ReleaseReadLock(&tdc->tlock);
if (!FidCmp(&tdc->f.fid, &avc->f.fid))
dcListMax++;
}
dcList = osi_Alloc(dcListMax * sizeof(struct dcache *));
- dcListCount = 0;
for (index = afs_dvhashTbl[hash]; index != NULLIDX;) {
if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
tdc = afs_GetValidDSlot(index);
- if (!tdc) osi_Panic("afs_InvalidateAllSegments tdc store");
+ if (!tdc) {
+ goto error;
+ }
ReleaseReadLock(&tdc->tlock);
if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {
/* same file? we'll zap it */
osi_Free(dcList, dcListMax * sizeof(struct dcache *));
return 0;
+
+ error:
+ ReleaseWriteLock(&afs_xdcache);
+
+ if (dcList) {
+ for (i = 0; i < dcListCount; i++) {
+ tdc = dcList[i];
+ if (tdc) {
+ afs_PutDCache(tdc);
+ }
+ }
+ osi_Free(dcList, dcListMax * sizeof(struct dcache *));
+ }
+ return EIO;
+}
+
+
+/*
+ * afs_InvalidateAllSegments
+ *
+ * Description:
+ * Invalidates all chunks for a given file
+ *
+ * Parameters:
+ * avc : Pointer to vcache entry.
+ *
+ * Environment:
+ * For example, called after an error has been detected. Called
+ * with avc write-locked, and afs_xdcache unheld.
+ */
+
+void
+afs_InvalidateAllSegments(struct vcache *avc)
+{
+ int code;
+ afs_uint32 last_warn;
+
+ code = afs_InvalidateAllSegments_once(avc);
+ if (code == 0) {
+ /* Success; nothing more to do. */
+ return;
+ }
+
+ /*
+ * If afs_InvalidateAllSegments_once failed, we cannot simply return an
+ * error to our caller. This function is called when we encounter a fatal
+ * error during stores, in which case we MUST invalidate all chunks for the
+ * given file. If we fail to invalidate some chunks, they will be left with
+ * the 'new' dirty/written data that was never successfully stored on the
+ * server, but the DV in the dcache is still the old DV. So, if its left
+ * alone, we may indefinitely serve data to applications that is not
+ * actually in the file on the fileserver.
+ *
+ * So to make sure we never serve userspace bad data after such a failure,
+ * we must keep trying to invalidate the dcaches for the given file. (Note
+ * that we cannot simply set a flag on the vcache to retry the invalidate
+ * later on, because the vcache may go away, but the 'bad' dcaches could
+ * remain.) We do this below, via background daemon requests because in
+ * some scenarios we can always get I/O errors on accessing the cache if we
+ * access via a user pid. (e.g. on LINUX, this can happen if the pid has a
+ * pending SIGKILL.) Doing this via background daemon ops should avoid
+ * that.
+ */
+
+ last_warn = osi_Time();
+ afs_warn("afs: Failed to invalidate cache chunks for fid %d.%d.%d.%d; our "
+ "local disk cache may be throwing errors. We must invalidate "
+ "these chunks to avoid possibly serving incorrect data, so we'll "
+ "retry until we succeed. If AFS access seems to hang, this may "
+ "be why.\n",
+ avc->f.fid.Cell, avc->f.fid.Fid.Volume, avc->f.fid.Fid.Vnode,
+ avc->f.fid.Fid.Unique);
+
+ do {
+ static const afs_uint32 warn_int = 60*60; /* warn once every hour */
+ afs_uint32 now = osi_Time();
+ struct brequest *bp;
+
+ if (now < last_warn || now - last_warn > warn_int) {
+ last_warn = now;
+ afs_warn("afs: Still trying to invalidate cache chunks for fid "
+ "%d.%d.%d.%d. We will retry until we succeed; if AFS "
+ "access seems to hang, this may be why.\n",
+ avc->f.fid.Cell, avc->f.fid.Fid.Volume,
+ avc->f.fid.Fid.Vnode, avc->f.fid.Fid.Unique);
+ }
+
+ /* Wait 10 seconds between attempts. */
+ afs_osi_Wait(1000 * 10, NULL, 0);
+
+ /*
+ * Ask a background daemon to do this request for us. Note that _we_ hold
+ * the write lock on 'avc', while the background daemon does the work. This
+ * is a little weird, but it helps avoid any issues with lock ordering
+ * or if our caller does not expect avc->lock to be dropped while
+ * running.
+ */
+ bp = afs_BQueue(BOP_INVALIDATE_SEGMENTS, avc, 0, 1, NULL, 0, 0, NULL,
+ NULL, NULL);
+ while ((bp->flags & BUVALID) == 0) {
+ bp->flags |= BUWAIT;
+ afs_osi_Sleep(bp);
+ }
+ code = bp->code_raw;
+ afs_BRelease(bp);
+ } while (code);
}
/*!
toAdd = AFS_CHUNKTOSIZE(tdc->f.chunk) - offset;
}
tfile = afs_CFileOpen(&tdc->f.inode);
+ osi_Assert(tfile);
while(tdc->validPos < avc->f.m.Length + toAdd) {
afs_size_t towrite;
afs_size_t newSize;
int dcCount, dcPos;
- struct dcache **tdcArray;
+ struct dcache **tdcArray = NULL;
AFS_STATCNT(afs_TruncateAllSegments);
avc->f.m.Date = osi_Time();
tdcArray = osi_Alloc(dcCount * sizeof(struct dcache *));
dcPos = 0;
- for (index = afs_dvhashTbl[code]; index != NULLIDX;) {
+ for (index = afs_dvhashTbl[code]; index != NULLIDX; index = afs_dvnextTbl[index]) {
if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
tdc = afs_GetValidDSlot(index);
- if (!tdc) osi_Panic("afs_TruncateAllSegments tdc");
+ if (!tdc) {
+ /* make sure we put back all of the tdcArray members before
+ * bailing out */
+ /* remember, the last valid tdc is at dcPos-1, so start at
+ * dcPos-1, not at dcPos itself. */
+ for (dcPos = dcPos - 1; dcPos >= 0; dcPos--) {
+ tdc = tdcArray[dcPos];
+ afs_PutDCache(tdc);
+ }
+ code = EIO;
+ goto done;
+ }
ReleaseReadLock(&tdc->tlock);
if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {
/* same file, and modified, we'll store it back */
afs_PutDCache(tdc);
}
}
- index = afs_dvnextTbl[index];
}
ReleaseWriteLock(&afs_xdcache);
UpgradeSToWLock(&tdc->lock, 673);
tdc->f.states |= DWriting;
tfile = afs_CFileOpen(&tdc->f.inode);
+ osi_Assert(tfile);
afs_CFileTruncate(tfile, (afs_int32)newSize);
afs_CFileClose(tfile);
afs_AdjustSize(tdc, (afs_int32)newSize);
afs_PutDCache(tdc);
}
- osi_Free(tdcArray, dcCount * sizeof(struct dcache *));
-
code = 0;
done:
+ if (tdcArray) {
+ osi_Free(tdcArray, dcCount * sizeof(struct dcache *));
+ }
#if (defined(AFS_SUN5_ENV))
ObtainWriteLock(&avc->vlock, 547);
if (--avc->activeV == 0 && (avc->vstates & VRevokeWait)) {