afs: discard cached state when we are unsure of validity

in the event we got a network error, we don't know if the server
completed (or will complete) our operation. we can assume nothing.
a more complicated version of this could attempt to verify that the
state is what we expect it to be, but in extended callbacks universe
this is potentially easier to solve anyway. for now, return the
error to the caller, and mark the vcache unstat'd.

(cherry picked from commit c2fc7e0f66621fc97f5b4dc389d379260638315c)

Change-Id: Ic38cf16e47664e6f36ad614735b42d3f4e5a6ce2
Reviewed-on: http://gerrit.openafs.org/6520
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Derrick Brashear <shadow@dementix.org>
This commit is contained in:
Derrick Brashear 2012-01-04 15:04:41 -05:00
parent 6479bb29d4
commit 3d4c44b61d
2 changed files with 67 additions and 3 deletions

View File

@ -273,6 +273,61 @@ afs_BlackListOnce(struct vrequest *areq, struct VenusFid *afid,
return serversleft;
}
/*------------------------------------------------------------------------
* afs_ClearStatus
*
* Description:
* Analyze the outcome of an RPC operation, taking whatever support
* actions are necessary.
*
* Arguments:
* afid : The FID of the file involved in the action. This argument
* may be null if none was involved.
* op : which RPC we are analyzing.
* avp : A pointer to the struct volume, if we already have one.
*
* Returns:
* Non-zero value if the related RPC operation can be retried,
* zero otherwise.
*
* Environment:
* This routine is called when we got a network error,
* and discards state if the operation was a data-mutating
* operation.
*------------------------------------------------------------------------*/
static int
afs_ClearStatus(struct VenusFid *afid, int op, struct volume *avp)
{
struct volume *tvp = NULL;
/* if it's not a write op, we have nothing to veto and shouldn't clear. */
if (!AFS_STATS_FS_RPCIDXES_ISWRITE(op)) {
return 1;
}
if (avp)
tvp = avp;
else if (afid)
tvp = afs_FindVolume(afid, READ_LOCK);
/* don't assume just discarding will fix if no cached volume */
if (tvp) {
struct vcache *tvc;
ObtainReadLock(&afs_xvcache);
if ((tvc = afs_FindVCache(afid, 0, 0))) {
ReleaseReadLock(&afs_xvcache);
tvc->f.states &= ~(CStatd | CUnique);
afs_PutVCache(tvc);
} else {
ReleaseReadLock(&afs_xvcache);
}
}
if (!avp)
afs_PutVolume(tvp, READ_LOCK);
/* not retriable: we may have raced ourselves */
return 0;
}
/*------------------------------------------------------------------------
* EXPORTED afs_Analyze
@ -465,8 +520,12 @@ afs_Analyze(struct afs_conn *aconn, struct rx_connection *rxconn,
else {
if (acode == RX_MSGSIZE)
shouldRetry = 1;
else
else {
areq->networkError = 1;
/* do not promote to shouldRetry if not already */
if (afs_ClearStatus(afid, op, NULL) == 0)
shouldRetry = 0;
}
}
}
return shouldRetry;
@ -516,14 +575,17 @@ afs_Analyze(struct afs_conn *aconn, struct rx_connection *rxconn,
serversleft = afs_BlackListOnce(areq, afid, tsp);
if (afid)
tvp = afs_FindVolume(afid, READ_LOCK);
if (!afid || !tvp || (tvp->states & VRO))
areq->idleError++;
if ((serversleft == 0) && tvp &&
((tvp->states & VRO) || (tvp->states & VBackup))) {
shouldRetry = 0;
} else {
shouldRetry = 1;
}
if (!afid || !tvp || (tvp->states & VRO))
areq->idleError++;
else if (afs_ClearStatus(afid, op, tvp) == 0)
shouldRetry = 0;
if (tvp)
afs_PutVolume(tvp, READ_LOCK);
/* By doing this, we avoid ever marking a server down

View File

@ -867,6 +867,8 @@ struct afs_stats_CMPerf {
#define AFS_STATS_NUM_FS_RPC_OPS 29
#define AFS_STATS_FS_RPCIDXES_ISWRITE(X) (((X > AFS_STATS_FS_RPCIDX_FETCHSTATUS) && (X < AFS_STATS_FS_RPCIDX_GETSTATISTICS)) || (X == AFS_STATS_FS_RPCIDX_SETVOLUMESTATUS))
#define AFS_STATS_FS_XFERIDX_FETCHDATA 0
#define AFS_STATS_FS_XFERIDX_STOREDATA 1