/* called with cm_scacheLock and scp write-locked; recycles an existing scp. */
long cm_RecycleSCache(cm_scache_t *scp, afs_int32 flags)
{
+ cm_fid_t fid;
+ afs_uint32 fileType;
+ int callback;
+
lock_AssertWrite(&cm_scacheLock);
lock_AssertWrite(&scp->rw);
return -1;
}
+
if (scp->flags & CM_SCACHEFLAG_SMB_FID) {
osi_Log1(afsd_logp,"cm_RecycleSCache CM_SCACHEFLAG_SMB_FID detected scp 0x%p", scp);
#ifdef DEBUG
return -1;
}
+ fid = scp->fid;
+ fileType = scp->fileType;
+ callback = scp->cbExpires ? 1 : 0;
+
cm_RemoveSCacheFromHashTable(scp);
/* invalidate so next merge works fine;
scp->cbServerp = NULL;
}
scp->cbExpires = 0;
+ scp->cbIssued = 0;
scp->volumeCreationDate = 0;
scp->fid.vnode = 0;
cm_FreeAllACLEnts(scp);
cm_ResetSCacheDirectory(scp, 0);
+
+ if (RDR_Initialized && callback) {
+ /*
+ * We drop the cm_scacheLock because it may be required to
+ * satisfy an ioctl request from the redirector. It should
+ * be safe to hold the scp->rw lock here because at this
+ * point (a) the object has just been recycled so the fid
+ * is nul and there are no requests that could possibly
+ * be issued by the redirector that would depend upon it.
+ */
+ lock_ReleaseWrite(&cm_scacheLock);
+ RDR_InvalidateObject( fid.cell, fid.volume, fid.vnode,
+ fid.unique, fid.hash,
+ fileType, AFS_INVALIDATE_EXPIRED);
+ lock_ObtainWrite(&cm_scacheLock);
+ }
+
return 0;
}
cm_GetNewSCache(afs_uint32 locked)
{
cm_scache_t *scp = NULL;
- int retry = 0;
+ cm_scache_t *scp_prev = NULL;
+ cm_scache_t *scp_next = NULL;
+ int attempt = 0;
if (locked)
lock_AssertWrite(&cm_scacheLock);
/* There were no deleted scache objects that we could use. Try to find
* one that simply hasn't been used in a while.
*/
- for (retry = 0 ; retry < 2; retry++) {
+ for (attempt = 0 ; attempt < 128; attempt++) {
+ afs_uint32 count = 0;
+
for ( scp = cm_data.scacheLRULastp;
scp;
scp = (cm_scache_t *) osi_QPrev(&scp->q))
{
+ /*
+ * We save the prev and next pointers in the
+ * LRU because we are going to drop the cm_scacheLock and
+ * the order of the list could change out from beneath us.
+ * If both changed, it means that this entry has been moved
+ * within the LRU and it should no longer be recycled.
+ */
+ scp_prev = (cm_scache_t *) osi_QPrev(&scp->q);
+ scp_next = (cm_scache_t *) osi_QNext(&scp->q);
+ count++;
+
/* It is possible for the refCount to be zero and for there still
* to be outstanding dirty buffers. If there are dirty buffers,
* we must not recycle the scp.
buf_dirty = buf_DirtyBuffersExist(&scp->fid);
if (!buf_dirty)
buf_rdr = buf_RDRBuffersExist(&scp->fid);
- lock_ObtainWrite(&cm_scacheLock);
if (!buf_dirty && !buf_rdr) {
cm_fid_t fid;
afs_uint32 fileType;
- if (!lock_TryWrite(&scp->rw))
- continue;
+ if (!lock_TryWrite(&scp->rw)) {
+ lock_ObtainWrite(&cm_scacheLock);
+ if (scp_prev != (cm_scache_t *) osi_QPrev(&scp->q) &&
+ scp_next != (cm_scache_t *) osi_QNext(&scp->q))
+ break;
+ else
+ continue;
+ }
+
+ lock_ObtainWrite(&cm_scacheLock);
+ if (scp_prev != (cm_scache_t *) osi_QPrev(&scp->q) &&
+ scp_next != (cm_scache_t *) osi_QNext(&scp->q))
+ {
+ lock_ReleaseWrite(&scp->rw);
+ break;
+ }
/* Found a likely candidate. Save type and fid in case we succeed */
fid = scp->fid;
*/
cm_AdjustScacheLRU(scp);
- if (RDR_Initialized) {
- /*
- * We drop the cm_scacheLock because it may be required to
- * satisfy an ioctl request from the redirector. It should
- * be safe to hold the scp->rw lock here because at this
- * point (a) the object has just been recycled so the fid
- * is nul and there are no requests that could possibly
- * be issued by the redirector that would depend upon it.
- */
- lock_ReleaseWrite(&cm_scacheLock);
- RDR_InvalidateObject( fid.cell, fid.volume, fid.vnode,
- fid.unique, fid.hash,
- fileType, AFS_INVALIDATE_EXPIRED);
- lock_ObtainWrite(&cm_scacheLock);
- }
-
/* and we're done */
osi_assertx(!(scp->flags & CM_SCACHEFLAG_INHASH), "CM_SCACHEFLAG_INHASH set");
goto done;
}
lock_ReleaseWrite(&scp->rw);
+ } else if (!buf_rdr) {
+ osi_Log1(afsd_logp, "GetNewSCache dirty buffers scp 0x%p", scp);
+ lock_ObtainWrite(&cm_scacheLock);
+ if (scp_prev != (cm_scache_t *) osi_QPrev(&scp->q) &&
+ scp_next != (cm_scache_t *) osi_QNext(&scp->q))
+ break;
} else {
- osi_Log1(afsd_logp,"GetNewSCache dirty buffers exist scp 0x%p", scp);
+ osi_Log1(afsd_logp,"GetNewSCache redirector is holding extents scp 0x%p", scp);
+ lock_ObtainWrite(&cm_scacheLock);
+ if (scp_prev != (cm_scache_t *) osi_QPrev(&scp->q) &&
+ scp_next != (cm_scache_t *) osi_QNext(&scp->q))
+ break;
}
}
- }
- osi_Log1(afsd_logp, "GetNewSCache all scache entries in use (retry = %d)", retry);
+ } /* for */
+
+ osi_Log2(afsd_logp, "GetNewSCache all scache entries in use (attempt = %d, count = %u)", attempt, count);
}
goto done;
}
#endif
lock_InitializeMutex(&scp->redirMx, "cm_scache_t redirMx", LOCK_HIERARCHY_SCACHE_REDIRMX);
scp->serverLock = -1;
+ scp->dataVersion = CM_SCACHE_VERSION_BAD;
+ scp->bufDataVersionLow = CM_SCACHE_VERSION_BAD;
+ scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
/* and put it in the LRU queue */
osi_QAddH((osi_queue_t **) &cm_data.scacheLRUFirstp, (osi_queue_t **)&cm_data.scacheLRULastp, &scp->q);
cm_data.fakeSCache.magic = CM_SCACHE_MAGIC;
cm_data.fakeSCache.cbServerp = (struct cm_server *)(-1);
cm_data.fakeSCache.cbExpires = (time_t)-1;
+ cm_data.fakeSCache.cbExpires = time(NULL);
/* can leave clientModTime at 0 */
cm_data.fakeSCache.fileType = CM_SCACHETYPE_FILE;
cm_data.fakeSCache.unixModeBits = 0777;
scp->cbServerp = NULL;
}
scp->cbExpires = 0;
+ scp->cbIssued = 0;
_InterlockedAnd(&scp->flags, ~CM_SCACHEFLAG_CALLBACK);
lock_ReleaseWrite(&scp->rw);
#endif
scp->cbServerp = NULL;
scp->cbExpires = 0;
+ scp->cbIssued = 0;
scp->volumeCreationDate = 0;
scp->fileLocksH = NULL;
scp->fileLocksT = NULL;
}
#ifdef DEBUG_REFCOUNT
-long cm_GetSCacheDbg(cm_fid_t *fidp, cm_scache_t **outScpp, cm_user_t *userp,
+long cm_GetSCacheDbg(cm_fid_t *fidp, cm_fid_t *parentFidp, cm_scache_t **outScpp, cm_user_t *userp,
cm_req_t *reqp, char * file, long line)
#else
-long cm_GetSCache(cm_fid_t *fidp, cm_scache_t **outScpp, cm_user_t *userp,
+long cm_GetSCache(cm_fid_t *fidp, cm_fid_t *parentFidp, cm_scache_t **outScpp, cm_user_t *userp,
cm_req_t *reqp)
#endif
{
cm_data.fakeDirVersion != scp->dataVersion)
break;
#endif
+ if (parentFidp && scp->parentVnode == 0) {
+ scp->parentVnode = parentFidp->vnode;
+ scp->parentUnique = parentFidp->unique;
+ }
cm_HoldSCacheNoLock(scp);
*outScpp = scp;
lock_ConvertRToW(&cm_scacheLock);
lock_ObtainWrite(&scp->rw);
}
scp->fid = *fidp;
- scp->dotdotFid.cell=AFS_FAKE_ROOT_CELL_ID;
- scp->dotdotFid.volume=AFS_FAKE_ROOT_VOL_ID;
- scp->dotdotFid.unique=1;
- scp->dotdotFid.vnode=1;
+ cm_SetFid(&scp->dotdotFid,AFS_FAKE_ROOT_CELL_ID,AFS_FAKE_ROOT_VOL_ID,1,1);
+ if (parentFidp) {
+ scp->parentVnode = parentFidp->vnode;
+ scp->parentUnique = parentFidp->unique;
+ }
_InterlockedOr(&scp->flags, (CM_SCACHEFLAG_PURERO | CM_SCACHEFLAG_RO));
lock_ObtainWrite(&cm_scacheLock);
if (!(scp->flags & CM_SCACHEFLAG_INHASH)) {
afsi_log("%s:%d cm_GetSCache (3) scp 0x%p ref %d", file, line, scp, scp->refCount);
osi_Log1(afsd_logp,"cm_GetSCache (3) scp 0x%p", scp);
#endif
+ if (parentFidp && scp->parentVnode == 0) {
+ scp->parentVnode = parentFidp->vnode;
+ scp->parentUnique = parentFidp->unique;
+ }
if (volp)
cm_PutVolume(volp);
cm_HoldSCacheNoLock(scp);
scp->fid = *fidp;
if (!cm_freelanceEnabled || !isRoot) {
/* if this scache entry represents a volume root then we need
- * to copy the dotdotFipd from the volume structure where the
+ * to copy the dotdotFid from the volume structure where the
* "master" copy is stored (defect 11489)
*/
if (volp->vol[ROVOL].ID == fidp->volume) {
scp->dotdotFid = cm_VolumeStateByType(volp, RWVOL)->dotdotFid;
}
}
+ if (parentFidp) {
+ scp->parentVnode = parentFidp->vnode;
+ scp->parentUnique = parentFidp->unique;
+ }
if (volp)
cm_PutVolume(volp);
cm_fid_t parent_fid;
cm_scache_t * pscp = NULL;
+ if (scp->parentVnode == 0)
+ return NULL;
+
lock_ObtainWrite(&cm_scacheLock);
cm_SetFid(&parent_fid, scp->fid.cell, scp->fid.volume, scp->parentVnode, scp->parentUnique);
}
}
+static afs_uint32
+dv_diff(afs_uint64 dv1, afs_uint64 dv2)
+{
+ if ( dv1 - dv2 > 0x7FFFFFFF )
+ return (afs_uint32)(dv2 - dv1);
+ else
+ return (afs_uint32)(dv1 - dv2);
+}
+
/* merge in a response from an RPC. The scp must be locked, and the callback
* is optional.
*
scp->bufDataVersionLow = CM_SCACHE_VERSION_BAD;
scp->fsLockCount = 0;
- if (dscp) {
+ if (dscp && dscp != scp) {
scp->parentVnode = dscp->fid.vnode;
scp->parentUnique = dscp->fid.unique;
} else {
scp->parentVnode = 0;
scp->parentUnique = 0;
}
- goto done;
+
+ if (RDR_Initialized)
+ rdr_invalidate = 1;
} else {
_InterlockedAnd(&scp->flags, ~CM_SCACHEFLAG_EACCESS);
}
scp->cbServerp->addr.sin_addr.s_addr,
volp ? volp->namep : "(unknown)");
}
+
osi_Log3(afsd_logp, "Bad merge, scp 0x%p, scp dv %d, RPC dv %d",
scp, scp->dataVersion, dataVersion);
/* we have a number of data fetch/store operations running
cm_AddACLCache(scp, userp, statusp->CallerAccess);
}
- if (scp->dataVersion != 0 &&
- (!(flags & (CM_MERGEFLAG_DIROP|CM_MERGEFLAG_STOREDATA)) && dataVersion != scp->dataVersion ||
- (flags & (CM_MERGEFLAG_DIROP|CM_MERGEFLAG_STOREDATA)) && dataVersion - scp->dataVersion > activeRPCs)) {
+ if (dataVersion != 0 && scp->dataVersion != CM_SCACHE_VERSION_BAD &&
+ (!(flags & (CM_MERGEFLAG_DIROP|CM_MERGEFLAG_STOREDATA)) && (dataVersion != scp->dataVersion) ||
+ (flags & (CM_MERGEFLAG_DIROP|CM_MERGEFLAG_STOREDATA)) &&
+ (dv_diff(dataVersion, scp->dataVersion) > activeRPCs))) {
/*
* We now know that all of the data buffers that we have associated
* with this scp are invalid. Subsequent operations will go faster
* object during an uncontested storeData operation. As a result this
* merge status no longer has performance characteristics derived from
* the size of the file.
+ *
+ * For directory buffers, only current dataVersion values are up to date.
*/
- if (((flags & CM_MERGEFLAG_STOREDATA) && dataVersion - scp->dataVersion > activeRPCs) ||
- (!(flags & CM_MERGEFLAG_STOREDATA) && scp->dataVersion != dataVersion) ||
- scp->bufDataVersionLow == 0)
+ if (((flags & (CM_MERGEFLAG_STOREDATA|CM_MERGEFLAG_DIROP)) && (dv_diff(dataVersion, scp->dataVersion) > activeRPCs)) ||
+ (!(flags & (CM_MERGEFLAG_STOREDATA|CM_MERGEFLAG_DIROP)) && (scp->dataVersion != dataVersion)) ||
+ scp->bufDataVersionLow == CM_SCACHE_VERSION_BAD ||
+ scp->fileType == CM_SCACHETYPE_DIRECTORY)
scp->bufDataVersionLow = dataVersion;
- if (RDR_Initialized && scp->dataVersion != CM_SCACHE_VERSION_BAD) {
- if ( ( !(reqp->flags & CM_REQ_SOURCE_REDIR) || !(flags & (CM_MERGEFLAG_DIROP|CM_MERGEFLAG_STOREDATA))) &&
- scp->dataVersion != dataVersion && (dataVersion - scp->dataVersion > activeRPCs - 1)) {
+ if (RDR_Initialized) {
+ /*
+ * The redirector maintains its own cached status information which
+ * must be updated when a DV change occurs that is not the result
+ * of a redirector initiated data change.
+ *
+ * If the current old DV is BAD, send a DV change notification.
+ *
+ * If the DV has changed and request was not initiated by the
+ * redirector, send a DV change notification.
+ *
+ * If the request was initiated by the redirector, send a notification
+ * for store and directory operations that result in a DV change greater
+ * than the number of active RPCs or any other operation that results
+ * in an unexpected DV change such as FetchStatus.
+ */
+
+ if (scp->dataVersion == CM_SCACHE_VERSION_BAD && dataVersion != 0) {
rdr_invalidate = 1;
- } else if ( (reqp->flags & CM_REQ_SOURCE_REDIR) && (flags & (CM_MERGEFLAG_DIROP|CM_MERGEFLAG_STOREDATA)) &&
- dataVersion - scp->dataVersion > activeRPCs) {
+ } else if (!(reqp->flags & CM_REQ_SOURCE_REDIR) && scp->dataVersion != dataVersion) {
rdr_invalidate = 1;
+ } else if (reqp->flags & CM_REQ_SOURCE_REDIR) {
+ if (!(flags & (CM_MERGEFLAG_DIROP|CM_MERGEFLAG_STOREDATA)) &&
+ (dv_diff(dataVersion, scp->dataVersion) > activeRPCs - 1)) {
+ rdr_invalidate = 1;
+ } else if ((flags & (CM_MERGEFLAG_DIROP|CM_MERGEFLAG_STOREDATA)) &&
+ dv_diff(dataVersion, scp->dataVersion) > activeRPCs) {
+ rdr_invalidate = 1;
+ }
}
}
scp->dataVersion = dataVersion;
scp->cbServerp = NULL;
}
scp->cbExpires = 0;
+ scp->cbIssued = 0;
scp->volumeCreationDate = 0;
_InterlockedAnd(&scp->flags, ~(CM_SCACHEFLAG_CALLBACK | CM_SCACHEFLAG_LOCAL | CM_SCACHEFLAG_RDR_IN_USE));
cm_dnlcPurgedp(scp);