From cc42d05324c6a3dc619192a997125ca8f3a595fd Mon Sep 17 00:00:00 2001 From: Simon Wilkinson Date: Wed, 21 Jan 2009 21:14:48 +0000 Subject: [PATCH] disconnected-replay-fixes-20090121 LICENSE IPL10 FIXES 124172 updates to fix bugs in disconnected change replays --- src/afs/DOC/afs_rwlocks | 14 +- src/afs/LINUX/osi_vnodeops.c | 18 +-- src/afs/VNOPS/afs_vnop_access.c | 5 + src/afs/VNOPS/afs_vnop_create.c | 43 ++--- src/afs/VNOPS/afs_vnop_dirops.c | 60 ++++--- src/afs/VNOPS/afs_vnop_link.c | 2 +- src/afs/VNOPS/afs_vnop_remove.c | 41 +++-- src/afs/VNOPS/afs_vnop_rename.c | 60 ++++--- src/afs/VNOPS/afs_vnop_symlink.c | 2 +- src/afs/VNOPS/afs_vnop_write.c | 34 +--- src/afs/afs.h | 14 +- src/afs/afs_buffer.c | 2 +- src/afs/afs_callback.c | 2 +- src/afs/afs_dcache.c | 76 ++++----- src/afs/afs_disconnected.c | 335 +++++++++++++++++++-------------------- src/afs/afs_init.c | 6 +- src/afs/afs_pioctl.c | 8 +- src/afs/afs_segments.c | 8 +- src/afs/afs_vcache.c | 19 +-- src/afs/discon.h | 61 +++---- 20 files changed, 374 insertions(+), 436 deletions(-) diff --git a/src/afs/DOC/afs_rwlocks b/src/afs/DOC/afs_rwlocks index d73bac7..ce28ce1 100644 --- a/src/afs/DOC/afs_rwlocks +++ b/src/afs/DOC/afs_rwlocks @@ -7,6 +7,9 @@ directory or online at http://www.openafs.org/dl/license10.html Locking order (in order of locking) -- +0.1 afs_discon_lock. Locks the current disconnected state, so it + can't be changed under active operations + 1. PVN lock in cache entry. Locks out pvn operations on vnode from our own layer. @@ -21,19 +24,24 @@ Locking order between dcache entries is in increasing offset order. However, if it turns out we never need to lock multiple dcache's, we should just say it's not allowed, and simplify things. -5. afs_xdcache. Protects the dcache hash tables and afs_index* in +5. afs_xdcache. Protects the dcache hash tables and afs_index* in afs_dcache.c. As with afs_xvcache below, a newly created dcache entries can be locked while holding afs_xdcache. Bugs: afs_xvcache locked before afs_xdcache in afs_remove, afs_symlink, etc in the file afs_vnodeops.c -6. afs_xvcache. Must be able to load new cache entries while holding +6. afs_xvcache. Must be able to load new cache entries while holding locks on others. Note this means you can't lock a cache entry while holding either of this lock, unless, as in afs_create, the cache entry is actually created while the afs_xvcache is held. -6a. afs_xvreclaim. Protects the lookaside reclaim list. Locked inside xvcache in FlushReclaimedVcaches via NewVCache or the 1 min loop. +6a. afs_disconDirtyLock. Protects the disconnected dirty and shadow +vcache queues. Must be after afs_xvcache, because we lock this whilst +hold xvcache in afs_create. + +6b. afs_xvreclaim. Protects the lookaside reclaim list. Locked inside +xvcache in FlushReclaimedVcaches via NewVCache or the 1 min loop. 7. afs_xvcb. Volume callback lock. Locked before afs_xserver in afs_RemoveVCB. diff --git a/src/afs/LINUX/osi_vnodeops.c b/src/afs/LINUX/osi_vnodeops.c index 4627d0b..4ab04e5 100644 --- a/src/afs/LINUX/osi_vnodeops.c +++ b/src/afs/LINUX/osi_vnodeops.c @@ -662,20 +662,8 @@ afs_linux_flush(struct file *fp) &treq, AFS_SYNC | AFS_LASTSTORE); } else { -#if defined(AFS_DISCON_ENV) - if (!vcp->ddirty_flags || - (vcp->ddirty_flags == VDisconShadowed)) { - - ObtainWriteLock(&afs_DDirtyVCListLock, 710); - AFS_DISCON_ADD_DIRTY(vcp, 1); - ReleaseWriteLock(&afs_DDirtyVCListLock); - } - - /* Set disconnected write flag. */ - vcp->ddirty_flags |= VDisconWriteOsiFlush; -#endif + afs_DisconAddDirty(vcp, VDisconWriteOsiFlush, 1); } - ConvertWToSLock(&vcp->lock); } code = afs_CheckCode(code, &treq, 54); @@ -1799,6 +1787,7 @@ afs_linux_readpage(struct file *fp, struct page *pp) maybe_lock_kernel(); #endif AFS_GLOCK(); + AFS_DISCON_LOCK(); afs_Trace4(afs_iclSetp, CM_TRACE_READPAGE, ICL_TYPE_POINTER, ip, ICL_TYPE_POINTER, pp, ICL_TYPE_INT32, cnt, ICL_TYPE_INT32, 99999); /* not a possible code value */ code = afs_rdwr(avc, auio, UIO_READ, 0, credp); @@ -1806,6 +1795,7 @@ afs_linux_readpage(struct file *fp, struct page *pp) afs_Trace4(afs_iclSetp, CM_TRACE_READPAGE, ICL_TYPE_POINTER, ip, ICL_TYPE_POINTER, pp, ICL_TYPE_INT32, cnt, ICL_TYPE_INT32, code); + AFS_DISCON_UNLOCK(); AFS_GUNLOCK(); #ifdef AFS_LINUX24_ENV maybe_unlock_kernel(); @@ -2012,6 +2002,7 @@ afs_linux_updatepage(struct file *fp, struct page *pp, unsigned long offset, credp = crref(); AFS_GLOCK(); + AFS_DISCON_LOCK(); afs_Trace4(afs_iclSetp, CM_TRACE_UPDATEPAGE, ICL_TYPE_POINTER, vcp, ICL_TYPE_POINTER, pp, ICL_TYPE_INT32, page_count(pp), ICL_TYPE_INT32, 99999); @@ -2039,6 +2030,7 @@ afs_linux_updatepage(struct file *fp, struct page *pp, unsigned long offset, ICL_TYPE_POINTER, pp, ICL_TYPE_INT32, page_count(pp), ICL_TYPE_INT32, code); + AFS_DISCON_UNLOCK(); AFS_GUNLOCK(); crfree(credp); diff --git a/src/afs/VNOPS/afs_vnop_access.c b/src/afs/VNOPS/afs_vnop_access.c index 5c65c65..66aa9de 100644 --- a/src/afs/VNOPS/afs_vnop_access.c +++ b/src/afs/VNOPS/afs_vnop_access.c @@ -90,6 +90,11 @@ afs_GetAccessBits(register struct vcache *avc, register afs_int32 arights, } } + if (AFS_IS_DISCONNECTED && !AFS_IN_SYNC) { + /* If we get this far, we have to ask the network. But we can't, so + * they're out of luck... */ + return 0; + } else { /* Ok, user has valid tokens, go ask the server. */ struct AFSFetchStatus OutStatus; afs_int32 code; diff --git a/src/afs/VNOPS/afs_vnop_create.c b/src/afs/VNOPS/afs_vnop_create.c index e0a8982..88e9656 100644 --- a/src/afs/VNOPS/afs_vnop_create.c +++ b/src/afs/VNOPS/afs_vnop_create.c @@ -393,6 +393,9 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, ReleaseWriteLock(&tdc->lock); afs_PutDCache(tdc); } + if (AFS_IS_DISCON_RW) + adp->m.LinkCount++; + newFid.Cell = adp->fid.Cell; newFid.Fid.Volume = adp->fid.Fid.Volume; ReleaseWriteLock(&adp->lock); @@ -442,8 +445,10 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, if (origCBs == finalCBs && origZaps == finalZaps) { tvc->states |= CStatd; /* we've fake entire thing, so don't stat */ tvc->states &= ~CBulkFetching; - tvc->cbExpires = CallBack.ExpirationTime; - afs_QueueCallback(tvc, CBHash(CallBack.ExpirationTime), volp); + if (!AFS_IS_DISCON_RW) { + tvc->cbExpires = CallBack.ExpirationTime; + afs_QueueCallback(tvc, CBHash(CallBack.ExpirationTime), volp); + } } else { afs_DequeueCallback(tvc); tvc->states &= ~(CStatd | CUnique); @@ -452,36 +457,20 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, osi_dnlc_purgedp(tvc); } ReleaseWriteLock(&afs_xcbhash); - if (!AFS_IS_DISCON_RW) - afs_ProcessFS(tvc, &OutFidStatus, &treq); + if (AFS_IS_DISCON_RW) { +#if defined(AFS_DISCON_ENV) + afs_DisconAddDirty(tvc, VDisconCreate, 0); + afs_GenDisconStatus(adp, tvc, &newFid, attrs, &treq, VREG); +#endif + } else { + afs_ProcessFS(tvc, &OutFidStatus, &treq); + } + tvc->parentVnode = adp->fid.Fid.Vnode; tvc->parentUnique = adp->fid.Fid.Unique; ReleaseWriteLock(&tvc->lock); *avcp = tvc; code = 0; - - if (AFS_IS_DISCON_RW) { -#if defined(AFS_DISCON_ENV) - /* After tvc has been created, we can do various ops on it. */ - /* Add to dirty list. */ - if (!tvc->ddirty_flags || - (tvc->ddirty_flags == VDisconShadowed)) { - /* Put it in the list only if it's fresh. */ - ObtainWriteLock(&afs_DDirtyVCListLock, 729); - AFS_DISCON_ADD_DIRTY(tvc, 0); - ReleaseWriteLock(&afs_DDirtyVCListLock); - } - - /* Set create flag. */ - ObtainWriteLock(&tvc->lock, 730); - afs_GenDisconStatus(adp, tvc, &newFid, attrs, &treq, VREG); - tvc->ddirty_flags |= VDisconCreate; - ReleaseWriteLock(&tvc->lock); - -#endif /* #ifdef AFS_DISCON_ENV */ - } /* if (AFS_IS_DISCON_RW) */ - - } else code = ENOENT; } else { diff --git a/src/afs/VNOPS/afs_vnop_dirops.c b/src/afs/VNOPS/afs_vnop_dirops.c index b3c97da..2ea01a3 100644 --- a/src/afs/VNOPS/afs_vnop_dirops.c +++ b/src/afs/VNOPS/afs_vnop_dirops.c @@ -102,7 +102,6 @@ afs_mkdir(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, ObtainWriteLock(&adp->lock, 153); if (!AFS_IS_DISCON_RW) { - do { tc = afs_Conn(&adp->fid, &treq, SHARED_LOCK); if (tc) { @@ -195,7 +194,6 @@ afs_mkdir(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, /* Generate a new vcache and fill it. */ tvc = afs_NewVCache(&newFid, NULL); if (tvc) { - code = 0; *avcp = tvc; } else { code = ENOENT; @@ -223,21 +221,12 @@ afs_mkdir(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, if (code) printf("afs_mkdir: afs_dirMakeDir code = %u\n", code); - /* Add to dirty list. */ - if (!tvc->ddirty_flags || - (tvc->ddirty_flags == VDisconShadowed)) { - - /* Put it in the list only if it's fresh. */ - ObtainWriteLock(&afs_DDirtyVCListLock, 730); - AFS_DISCON_ADD_DIRTY(tvc, 1); - ReleaseWriteLock(&afs_DDirtyVCListLock); - } + afs_PutDCache(new_dc); ObtainWriteLock(&tvc->lock, 731); + afs_DisconAddDirty(tvc, VDisconCreate, 1); /* Update length in the vcache. */ tvc->m.Length = new_dc->f.chunkBytes; - /* Set create flag. */ - tvc->ddirty_flags |= VDisconCreate; ReleaseWriteLock(&tvc->lock); #endif /* #ifdef AFS_DISCON_ENV */ } else { @@ -389,6 +378,13 @@ afs_rmdir(OSI_VC_DECL(adp), char *aname, struct AFS_UCRED *acred) #if defined(AFS_DISCON_ENV) /* Disconnected. */ + if (!tdc) { + ReleaseWriteLock(&adp->lock); + printf("afs_rmdir: No local dcache!\n"); + code = ENETDOWN; + goto done; + } + if (!tvc) { /* Find the vcache. */ struct VenusFid tfid; @@ -400,13 +396,14 @@ afs_rmdir(OSI_VC_DECL(adp), char *aname, struct AFS_UCRED *acred) ObtainSharedLock(&afs_xvcache, 764); tvc = afs_FindVCache(&tfid, 0, 1 /* do xstats */ ); ReleaseSharedLock(&afs_xvcache); - + if (!tvc) { printf("afs_rmdir: Can't find dir's vcache!\n"); ReleaseSharedLock(&tdc->lock); - afs_PutDCache(tdc); /* drop ref count */ - ReleaseWriteLock(&adp->lock); - goto done; + afs_PutDCache(tdc); /* drop ref count */ + ReleaseWriteLock(&adp->lock); + code = ENETDOWN; + goto done; } } @@ -415,6 +412,9 @@ afs_rmdir(OSI_VC_DECL(adp), char *aname, struct AFS_UCRED *acred) * deleted. */ ReleaseSharedLock(&tdc->lock); + afs_PutDCache(tdc); + afs_PutVCache(tvc); + ReleaseWriteLock(&adp->lock); code = ENOTEMPTY; goto done; } @@ -424,7 +424,7 @@ afs_rmdir(OSI_VC_DECL(adp), char *aname, struct AFS_UCRED *acred) * because a dir must be empty in order to be rmdir'ed. * If the deleted dir has no shadow, it means that it was empty. */ - if (!(adp->ddirty_flags & VDisconShadowed)) { + if (!adp->shVnode) { /* If tdc available, then it is locked. * afs_MakeShadowDir unlocks it. */ @@ -435,18 +435,6 @@ afs_rmdir(OSI_VC_DECL(adp), char *aname, struct AFS_UCRED *acred) ObtainSharedLock(&tdc->lock, 732); } - if (!tvc->ddirty_flags) { - /* Put it in the list only if it's fresh or has only been shadowed. */ - ObtainWriteLock(&afs_DDirtyVCListLock, 728); - AFS_DISCON_ADD_DIRTY(tvc, 1); - ReleaseWriteLock(&afs_DDirtyVCListLock); - } - - /* Now add the vcache to the dirty list. */ - ObtainWriteLock(&tvc->lock, 727); - tvc->ddirty_flags |= VDisconRemove; - ReleaseWriteLock(&tvc->lock); - adp->m.LinkCount--; #endif /* #ifdef AFS_DISCON_ENV */ } /* if (!AFS_IS_DISCON_RW) */ @@ -466,7 +454,6 @@ afs_rmdir(OSI_VC_DECL(adp), char *aname, struct AFS_UCRED *acred) afs_PutDCache(tdc); /* drop ref count */ } - if (tvc) osi_dnlc_purgedp(tvc); /* get rid of any entries for this directory */ else @@ -475,6 +462,17 @@ afs_rmdir(OSI_VC_DECL(adp), char *aname, struct AFS_UCRED *acred) if (tvc) { ObtainWriteLock(&tvc->lock, 155); tvc->states &= ~CUnique; /* For the dfs xlator */ +#if AFS_DISCON_ENV + if (AFS_IS_DISCON_RW) { + if (tvc->ddirty_flags & VDisconCreate) { + /* If we we were created whilst disconnected, removal doesn't + * need to get logged. Just go away gracefully */ + afs_DisconRemoveDirty(tvc); + } else { + afs_DisconAddDirty(tvc, VDisconRemove, 1); + } + } +#endif ReleaseWriteLock(&tvc->lock); afs_PutVCache(tvc); } diff --git a/src/afs/VNOPS/afs_vnop_link.c b/src/afs/VNOPS/afs_vnop_link.c index 16d6588..8da47fd 100644 --- a/src/afs/VNOPS/afs_vnop_link.c +++ b/src/afs/VNOPS/afs_vnop_link.c @@ -91,7 +91,7 @@ afs_link(struct vcache *avc, OSI_VC_DECL(adp), char *aname, goto done; } - if (AFS_IS_DISCONNECTED && !AFS_IS_LOGGING) { + if (AFS_IS_DISCONNECTED) { code = ENETDOWN; goto done; } diff --git a/src/afs/VNOPS/afs_vnop_remove.c b/src/afs/VNOPS/afs_vnop_remove.c index acb4e7e..5b63167 100644 --- a/src/afs/VNOPS/afs_vnop_remove.c +++ b/src/afs/VNOPS/afs_vnop_remove.c @@ -314,7 +314,7 @@ afs_remove(OSI_VC_DECL(adp), char *aname, struct AFS_UCRED *acred) tdc = afs_GetDCache(adp, (afs_size_t) 0, &treq, &offset, &len, 1); /* test for error below */ ObtainWriteLock(&adp->lock, 142); #if defined(AFS_DISCON_ENV) - if (AFS_IS_DISCON_RW && !(adp->ddirty_flags & VDisconShadowed)) + if (AFS_IS_DISCON_RW && !adp->shVnode) /* Make shadow copy of parent dir. */ afs_MakeShadowDir(adp); #endif @@ -364,24 +364,27 @@ afs_remove(OSI_VC_DECL(adp), char *aname, struct AFS_UCRED *acred) #if defined(AFS_DISCON_ENV) if (AFS_IS_DISCON_RW) { - /* Add removed file vcache to dirty list. */ - - if (!tvc->ddirty_flags || - (tvc->ddirty_flags == VDisconShadowed)) { - /* Add to list only if fresh. */ - ObtainWriteLock(&afs_DDirtyVCListLock, 725); - AFS_DISCON_ADD_DIRTY(tvc, 1); - ReleaseWriteLock(&afs_DDirtyVCListLock); - } + /* Can't hold a dcache lock whilst we're getting a vcache one */ + if (tdc) + ReleaseSharedLock(&tdc->lock); - ObtainWriteLock(&tvc->lock, 726); - tvc->ddirty_flags |= VDisconRemove; - ReleaseWriteLock(&tvc->lock); + /* XXX - We're holding adp->lock still, and we've got no + * guarantee about whether the ordering matches the lock hierarchy */ + ObtainWriteLock(&tvc->lock, 713); - //ObtainWriteLock(&adp->lock, 751); + /* If we were locally created, then we don't need to do very + * much beyond ensuring that we don't exist anymore */ + if (tvc->ddirty_flags & VDisconCreate) { + afs_DisconRemoveDirty(tvc); + } else { + /* Add removed file vcache to dirty list. */ + afs_DisconAddDirty(tvc, VDisconRemove, 1); + } adp->m.LinkCount--; - //ReleaseWriteLock(&adp->lock); - } + ReleaseWriteLock(&tvc->lock); + if (tdc) + ObtainSharedLock(&tdc->lock, 714); + } #endif if (tvc && osi_Active(tvc)) { @@ -448,11 +451,7 @@ afs_remove(OSI_VC_DECL(adp), char *aname, struct AFS_UCRED *acred) } if (tdc) afs_PutDCache(tdc); - /* Don't decrease refcount for this vcache if disconnected, we will - * need it during replay. - */ - if (!AFS_IS_DISCON_RW) - afs_PutVCache(tvc); + afs_PutVCache(tvc); } else { code = afsremove(adp, tdc, tvc, aname, acred, &treq); } diff --git a/src/afs/VNOPS/afs_vnop_rename.c b/src/afs/VNOPS/afs_vnop_rename.c index 1cccb92..f71dcc6 100644 --- a/src/afs/VNOPS/afs_vnop_rename.c +++ b/src/afs/VNOPS/afs_vnop_rename.c @@ -204,21 +204,16 @@ afsrename(struct vcache *aodp, char *aname1, struct vcache *andp, ReleaseSharedLock(&afs_xvcache); if (tvc) { - if (!(tvc->ddirty_flags & VDisconRename)) { - /* We only care about vnodes that haven't been - * renamed. Those that have been renamed at least once - * already have an asociated shadow dir and the flags set - * for the first old location, which is what interests us - * when reconnecting. + ObtainWriteLock(&tvc->lock, 750); + if (!(tvc->ddirty_flags & (VDisconRename|VDisconCreate))) { + /* If the vnode was created locally, then we don't care + * about recording the rename - we'll do it automatically + * on replay. If we've already renamed, we've already stored + * the required information about where we came from. */ - - if (!(aodp->ddirty_flags & VDisconShadowed) && - !(tvc->ddirty_flags & VDisconCreate)) { - /* - * Make shadow copy of parent dir only. - * Files that are created locally and renamed, - * don't need a shadow dir, so skip this step. - */ + + if (!aodp->shVnode) { + /* Make shadow copy of parent dir only. */ if (tdc1) ReleaseWriteLock(&tdc1->lock); afs_MakeShadowDir(aodp); @@ -226,27 +221,19 @@ afsrename(struct vcache *aodp, char *aname1, struct vcache *andp, ObtainWriteLock(&tdc1->lock, 753); } - if (!tvc->ddirty_flags || - (tvc->ddirty_flags == VDisconShadowed)) { - /* Add in dirty list.*/ - ObtainWriteLock(&afs_DDirtyVCListLock, 751); - AFS_DISCON_ADD_DIRTY(tvc, 1); - ReleaseWriteLock(&afs_DDirtyVCListLock); - } + afs_DisconAddDirty(tvc, + VDisconRename + | (oneDir ? VDisconRenameSameDir:0), + 1); - ObtainWriteLock(&tvc->lock, 750); /* Save old parent dir fid so it will be searchable * in the shadow dir. */ tvc->oldVnode = aodp->fid.Fid.Vnode; tvc->oldUnique = aodp->fid.Fid.Unique; - /* Set discon state flag. */ - tvc->ddirty_flags |= VDisconRename; - if (oneDir) - tvc->ddirty_flags |= VDisconRenameSameDir; - ReleaseWriteLock(&tvc->lock); - } /* if not previously renamed */ + } + ReleaseWriteLock(&tvc->lock); afs_PutVCache(tvc); } else { code = ENOENT; @@ -317,12 +304,19 @@ afsrename(struct vcache *aodp, char *aname1, struct vcache *andp, } } + /* update dir link counts */ - aodp->m.LinkCount = AFS_IS_DISCON_RW ? - (aodp->m.LinkCount - 1):OutOldDirStatus.LinkCount; - if (!oneDir) - andp->m.LinkCount = AFS_IS_DISCON_RW ? - (andp->m.LinkCount + 1):OutNewDirStatus.LinkCount; + if (AFS_IS_DISCON_RW) { + if (!oneDir) { + aodp->m.LinkCount--; + andp->m.LinkCount++; + } + /* If we're in the same directory, link count doesn't change */ + } else { + aodp->m.LinkCount = OutOldDirStatus.LinkCount; + if (!oneDir) + andp->m.LinkCount = OutNewDirStatus.LinkCount; + } } else { /* operation failed (code != 0) */ if (code < 0) { diff --git a/src/afs/VNOPS/afs_vnop_symlink.c b/src/afs/VNOPS/afs_vnop_symlink.c index a0b880b..f306715 100644 --- a/src/afs/VNOPS/afs_vnop_symlink.c +++ b/src/afs/VNOPS/afs_vnop_symlink.c @@ -107,7 +107,7 @@ afs_symlink(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, goto done; } - if (AFS_IS_DISCONNECTED && !AFS_IS_LOGGING) { + if (AFS_IS_DISCONNECTED) { code = ENETDOWN; goto done; } diff --git a/src/afs/VNOPS/afs_vnop_write.c b/src/afs/VNOPS/afs_vnop_write.c index 5b56a7e..25189f8 100644 --- a/src/afs/VNOPS/afs_vnop_write.c +++ b/src/afs/VNOPS/afs_vnop_write.c @@ -84,22 +84,9 @@ afs_StoreOnLastReference(register struct vcache *avc, * afs_{rd,wr} routines which means the vcache is a perfect candidate * for flushing! */ - -#ifdef AFS_DISCON_ENV } else if (AFS_IS_DISCON_RW) { - /* Disconnected. */ - - if (!avc->ddirty_flags || - (avc->ddirty_flags == VDisconShadowed)) { - - /* Add to disconnected dirty list. */ - AFS_DISCON_ADD_DIRTY(avc, 1); - } - - /* Set disconnected write flag. */ - avc->ddirty_flags |= VDisconWriteClose; -#endif - } /* if not disconnected */ + afs_DisconAddDirty(avc, VDisconWriteClose, 0); + } /* if not disconnected */ #if defined(AFS_SGI_ENV) osi_Assert(avc->opens > 0 && avc->execsOrWriters > 0); @@ -655,7 +642,8 @@ afs_DoPartialWrite(register struct vcache *avc, struct vrequest *areq) register afs_int32 code; if (afs_stats_cmperf.cacheCurrDirtyChunks <= - afs_stats_cmperf.cacheMaxDirtyChunks) + afs_stats_cmperf.cacheMaxDirtyChunks + || AFS_IS_DISCONNECTED) return 0; /* nothing to do */ /* otherwise, call afs_StoreDCache (later try to do this async, if possible) */ afs_Trace2(afs_iclSetp, CM_TRACE_PARTIALWRITE, ICL_TYPE_POINTER, avc, @@ -974,22 +962,10 @@ afs_fsync(OSI_VC_DECL(avc), struct AFS_UCRED *acred) #if defined(AFS_DISCON_ENV) } else { - /* Disconnected flush. */ - ObtainWriteLock(&afs_DDirtyVCListLock, 708); - - if (!avc->ddirty_flags || - (avc->ddirty_flags == VDisconShadowed)) { - - /* Add to disconnected dirty list. */ - AFS_DISCON_ADD_DIRTY(avc, 1); - } UpgradeSToWLock(&avc->lock, 711); - /* Set disconnected write flag. */ - avc->ddirty_flags |= VDisconWriteFlush; + afs_DisconAddDirty(avc, VDisconWriteFlush, 1); ConvertWToSLock(&avc->lock); - - ReleaseWriteLock(&afs_DDirtyVCListLock); #endif } /* if not disconnected */ } /* if (avc->execsOrWriters > 0) */ diff --git a/src/afs/afs.h b/src/afs/afs.h index 9a078ed..b50b7e7 100644 --- a/src/afs/afs.h +++ b/src/afs/afs.h @@ -596,9 +596,10 @@ struct SimpleLocks { #define VDisconWriteFlush 0x00000800 /* Write op on normal fsync/flush. */ #define VDisconWriteOsiFlush 0x00001000 /* Write op on osi flush. */ -#define VDisconShadowed 0x00002000 /* Shadowed dir. */ -#define VDisconRemove 0x00004000 /* Remove vnop. */ -#define VDisconCreate 0x00008000 /* Create vnop. */ +#define VDisconRemove 0x00002000 /* Remove vnop. */ +#define VDisconCreate 0x00004000 /* Create vnop. */ +#define VDisconCreated 0x00008000 /* A file that was created during + this resync operation */ #define VDisconRename 0x00010000 /* Rename vnop. */ #define VDisconRenameSameDir 0x00020000 /* Rename in same dir. */ @@ -685,10 +686,11 @@ struct vcache { #endif struct vcache *hnext; /* Hash next */ struct afs_q vhashq; /* Hashed per-volume list */ - #if defined(AFS_DISCON_ENV) - /*! Next element in afs_DDirtyVCList. Lock it with afs_DDirtyVCListLock. */ - struct vcache *ddirty_next; + /*! Queue of dirty vcaches. Lock with afs_disconDirtyLock */ + struct afs_q dirtyq; + /*! Queue of vcaches with shadow entries. Lock with afs_disconDirtyLock */ + struct afs_q shadowq; /*! Disconnected flags for this vcache element. */ uint32_t ddirty_flags; /*! Shadow vnode + unique keep the shadow dir location. */ diff --git a/src/afs/afs_buffer.c b/src/afs/afs_buffer.c index e3b2a84..b170046 100644 --- a/src/afs/afs_buffer.c +++ b/src/afs/afs_buffer.c @@ -501,7 +501,7 @@ DFlushDCache(struct dcache *adc) for (i = 0; i <= PHPAGEMASK; i++) for (tb = phTable[pHash(adc->index, i)]; tb; tb = tb->hashNext) if (tb->fid == adc->index) { - ObtainWriteLock(&tb->lock, 702); + ObtainWriteLock(&tb->lock, 701); tb->lockers++; ReleaseReadLock(&afs_bufferLock); if (tb->dirty) { diff --git a/src/afs/afs_callback.c b/src/afs/afs_callback.c index 089c2d0..ed222dc 100644 --- a/src/afs/afs_callback.c +++ b/src/afs/afs_callback.c @@ -68,7 +68,7 @@ static struct ltable { #endif #ifdef AFS_DISCON_ENV { "afs_discon_lock", (char *)&afs_discon_lock}, - { "afs_DDirtyVCListLock", (char *)&afs_DDirtyVCListLock}, + { "afs_disconDirtyLock", (char *)&afs_disconDirtyLock}, #endif }; unsigned long lastCallBack_vnode; diff --git a/src/afs/afs_dcache.c b/src/afs/afs_dcache.c index 26ad8f2..c1984cb 100644 --- a/src/afs/afs_dcache.c +++ b/src/afs/afs_dcache.c @@ -3755,24 +3755,18 @@ int afs_MakeShadowDir(struct vcache *avc) if (vType(avc) != VDIR) return ENOTDIR; + if (avc->shVnode || avc->shUnique) + return EEXIST; + /* Generate a fid for the shadow dir. */ shadow_fid.Cell = avc->fid.Cell; shadow_fid.Fid.Volume = avc->fid.Fid.Volume; afs_GenShadowFid(&shadow_fid); - /* For each dcache, do copy it into a new fresh one. */ + /* For each dcache, copy it into a new fresh one. */ + ObtainWriteLock(&afs_xdcache, 716); i = DVHash(&avc->fid); for (index = afs_dvhashTbl[i]; index != NULLIDX; index = i) { - /* Making sure that this isn't going to get locked twice. */ - if (!lock_held) { - /* XXX: Moved it from outside of the loop. - * Maybe it's not quite okay because of the use of - * dvhashTbl (once) in the for statement. - */ - ObtainWriteLock(&afs_xdcache, 716); - lock_held = 1; - } - i = afs_dvnextTbl[index]; if (afs_indexUnique[index] == avc->fid.Fid.Unique) { tdc = afs_GetDSlot(index, NULL); @@ -3780,39 +3774,33 @@ int afs_MakeShadowDir(struct vcache *avc) ReleaseReadLock(&tdc->tlock); if (!FidCmp(&tdc->f.fid, &avc->fid)) { - - /* Got a dir's dcache. */ - lock_held = 0; - /* Get a fresh dcache. */ new_dc = afs_AllocDCache(avc, 0, 0, &shadow_fid); - /* Unlock hash for now. Don't need it during operations on the - * dcache. Oh, and we can't use it because of the locking - * hierarchy... - */ - /* XXX: So much for lock ierarchy, the afs_AllocDCache doesn't - * respect it. + /* XXX - The lock ordering here is broken. We can't lock + * tdc whilst we're holding xdcache, and we can't free + * xdcache without having to start again on the hash chain + * we're currently on */ - //ReleaseWriteLock(&afs_xdcache); - ObtainReadLock(&tdc->lock); + ObtainReadLock(&tdc->mflock); + /* Set up the new fid. */ /* Copy interesting data from original dir dcache. */ - new_dc->mflags = tdc->mflags; - new_dc->dflags = tdc->dflags; - new_dc->f.modTime = tdc->f.modTime; - new_dc->f.versionNo = tdc->f.versionNo; - new_dc->f.states = tdc->f.states; - new_dc->f.chunk= tdc->f.chunk; - new_dc->f.chunkBytes = tdc->f.chunkBytes; - + new_dc->mflags = tdc->mflags; /* tdc->mflock */ + new_dc->dflags = tdc->dflags; /* tdc->lock */ + new_dc->f.modTime = tdc->f.modTime; /* tdc->lock */ + new_dc->f.versionNo = tdc->f.versionNo; /* tdc->lock */ + new_dc->f.states = tdc->f.states; /* tdc->lock */ + new_dc->f.chunk= tdc->f.chunk; /* tdc->lock */ + new_dc->f.chunkBytes = tdc->f.chunkBytes; /* tdc->lock */ + + ReleaseReadLock(&tdc->mflock); /* * Now add to the two hash chains - note that i is still set * from the above DCHash call. */ - //ObtainWriteLock(&afs_xdcache, 713); j = DCHash(&shadow_fid, 0); afs_dcnextTbl[new_dc->index] = afs_dchashTbl[j]; @@ -3887,23 +3875,25 @@ int afs_MakeShadowDir(struct vcache *avc) ReleaseReadLock(&tdc->lock); afs_PutDCache(new_dc); + ObtainWriteLock(&afs_xdcache, 720); + } /* if dcache fid match */ afs_PutDCache(tdc); } /* if unuiquifier match */ } done: - if (lock_held) - ReleaseWriteLock(&afs_xdcache); + ReleaseWriteLock(&afs_xdcache); if (!ret_code) { - if (!avc->ddirty_flags) { - ObtainWriteLock(&afs_DDirtyVCListLock, 763); - AFS_DISCON_ADD_DIRTY(avc, 1); - ReleaseWriteLock(&afs_DDirtyVCListLock); - } + ObtainWriteLock(&afs_xvcache, 763); + ObtainWriteLock(&afs_disconDirtyLock, 765); + QAdd(&afs_disconShadow, &avc->shadowq); + osi_vnhold(avc, 0); + ReleaseWriteLock(&afs_disconDirtyLock); + ReleaseWriteLock(&afs_xvcache); + avc->shVnode = shadow_fid.Fid.Vnode; avc->shUnique = shadow_fid.Fid.Unique; - avc->ddirty_flags |= VDisconShadowed; } return ret_code; @@ -3932,7 +3922,9 @@ void afs_DeleteShadowDir(struct vcache *avc) afs_DiscardDCache(tdc); afs_PutDCache(tdc); } - /* Remove shadowed dir flag. */ - avc->ddirty_flags &= ~VDisconShadowed; + avc->shVnode = avc->shUnique = 0; + ObtainWriteLock(&afs_disconDirtyLock, 708); + QRemove(&avc->shadowq); + ReleaseWriteLock(&afs_disconDirtyLock); } #endif diff --git a/src/afs/afs_disconnected.c b/src/afs/afs_disconnected.c index 149ecf8..966d729 100644 --- a/src/afs/afs_disconnected.c +++ b/src/afs/afs_disconnected.c @@ -21,16 +21,15 @@ RCSID("$Header$"); ((vc->m.DataVersion.low == fstat.DataVersion) && \ (vc->m.DataVersion.high == fstat.dataVersionHigh)) -/*! Global list of dirty vcaches. */ -/*! Last added element. */ -struct vcache *afs_DDirtyVCList = NULL; -/*! Head of list. */ -struct vcache *afs_DDirtyVCListStart = NULL; -/*! Previous element in the list. */ -struct vcache *afs_DDirtyVCListPrev = NULL; +/*! Circular queue of dirty vcaches */ +struct afs_q afs_disconDirty; -/*! Locks list of dirty vcaches. */ -afs_rwlock_t afs_DDirtyVCListLock; +/*! Circular queue of vcaches with shadow directories */ +struct afs_q afs_disconShadow; + +/*! Locks both of these lists. Must be write locked for anything other than + * list traversal */ +afs_rwlock_t afs_disconDirtyLock; extern afs_int32 *afs_dvhashTbl; /*Data cache hash table */ extern afs_int32 *afs_dchashTbl; /*Data cache hash table */ @@ -38,8 +37,7 @@ extern afs_int32 *afs_dvnextTbl; /*Dcache hash table links */ extern afs_int32 *afs_dcnextTbl; /*Dcache hash table links */ extern struct dcache **afs_indexTable; /*Pointers to dcache entries */ -/*! Vnode number. On file creation, use the current - * value and increment it. +/*! Vnode number. On file creation, use the current value and increment it. */ afs_uint32 afs_DisconVnode = 2; @@ -53,6 +51,9 @@ enum { afs_int32 afs_ConflictPolicy = SERVER_WINS; +static void afs_DisconResetVCache(struct vcache *, struct AFS_UCRED *); +static void afs_DisconDiscardAllShadows(int, struct AFS_UCRED *); + /*! * Find the first dcache of a file that has the specified fid. * Similar to afs_FindDCache, only that it takes a fid instead @@ -62,10 +63,10 @@ afs_int32 afs_ConflictPolicy = SERVER_WINS; * * \return The found dcache or NULL. */ -struct dcache *afs_FindDCacheByFid(register struct VenusFid *afid) +struct dcache *afs_FindDCacheByFid(struct VenusFid *afid) { - register afs_int32 i, index; - register struct dcache *tdc = NULL; + afs_int32 i, index; + struct dcache *tdc = NULL; i = DVHash(afid); ObtainWriteLock(&afs_xdcache, 758); @@ -150,6 +151,8 @@ int get_parent_dir_fid_hook(void *hdata, * * \param avc The file's vhash entry. * \param afid Put the fid here. + * + * \return 0 on success, -1 on failure */ int afs_GetParentDirFid(struct vcache *avc, struct VenusFid *afid) { @@ -170,6 +173,8 @@ int afs_GetParentDirFid(struct vcache *avc, struct VenusFid *afid) /* Lookup each entry for the fid. It should be the first. */ afs_dir_EnumerateDir(tdc, &get_parent_dir_fid_hook, afid); afs_PutDCache(tdc); + } else { + return -1; } } @@ -321,12 +326,12 @@ int chk_del_children_hook(void *hdata, tvc = afs_FindVCache(&tfid, 0, 1); ReleaseSharedLock(&afs_xvcache); - /* Count unfinished dirty children. VDisconShadowed can still be set, - * because we need it to remove the shadow dir. - */ + /* Count unfinished dirty children. */ if (tvc) { - if (tvc->ddirty_flags) + ObtainReadLock(&tvc->lock); + if (tvc->ddirty_flags || tvc->shVnode) v->count++; + ReleaseReadLock(&tvc->lock); afs_PutVCache(tvc); } @@ -348,7 +353,7 @@ int afs_CheckDeletedChildren(struct vcache *avc) struct DirtyChildrenCount dcc; struct VenusFid shadow_fid; - if (!(avc->ddirty_flags & VDisconShadowed)) + if (!avc->shVnode) /* Empty dir. */ return 0; @@ -476,7 +481,7 @@ void afs_DbgListDirEntries(struct VenusFid *afid) int afs_ProcessOpRename(struct vcache *avc, struct vrequest *areq) { struct VenusFid old_pdir_fid, new_pdir_fid; - char *old_name, *new_name; + char *old_name = NULL, *new_name = NULL; struct AFSFetchStatus OutOldDirStatus, OutNewDirStatus; struct AFSVolSync tsync; struct afs_conn *tc; @@ -498,8 +503,7 @@ int afs_ProcessOpRename(struct vcache *avc, struct vrequest *areq) code = afs_GetVnodeName(avc, &old_pdir_fid, old_name, 1); if (code) { printf("afs_ProcessOpRename: Couldn't find old name.\n"); - code = ENOENT; - goto end2; + goto done; } /* Alloc data first. */ @@ -507,7 +511,7 @@ int afs_ProcessOpRename(struct vcache *avc, struct vrequest *areq) if (!new_name) { printf("afs_ProcessOpRename: Couldn't alloc space for new name.\n"); code = ENOMEM; - goto end2; + goto done; } if (avc->ddirty_flags & VDisconRenameSameDir) { @@ -522,7 +526,7 @@ int afs_ProcessOpRename(struct vcache *avc, struct vrequest *areq) if (!new_pdir_fid.Fid.Unique) { printf("afs_ProcessOpRename: Couldn't find new parent dir FID.\n"); code = ENOENT; - goto end1; + goto done; } } @@ -530,8 +534,7 @@ int afs_ProcessOpRename(struct vcache *avc, struct vrequest *areq) code = afs_GetVnodeName(avc, &new_pdir_fid, new_name, 0); if (code) { printf("afs_ProcessOpRename: Couldn't find new name.\n"); - code = ENOENT; - goto end1; + goto done; } /* Send to data to server. */ @@ -563,10 +566,11 @@ int afs_ProcessOpRename(struct vcache *avc, struct vrequest *areq) if (code) printf("afs_ProcessOpRename: server code=%u\n", code); -end1: - afs_osi_Free(new_name, AFSNAMEMAX); -end2: - afs_osi_Free(old_name, AFSNAMEMAX); +done: + if (new_name) + afs_osi_Free(new_name, AFSNAMEMAX); + if (old_name) + afs_osi_Free(old_name, AFSNAMEMAX); return code; } @@ -599,7 +603,7 @@ int afs_ProcessOpCreate(struct vcache *avc, pdir_fid.Fid.Unique = 0; afs_GetParentDirFid(avc, &pdir_fid); if (!pdir_fid.Fid.Unique) { - printf("afs_ProcessOpCreate: Couldn't find parent dir'sFID.\n"); + printf("afs_ProcessOpCreate: Couldn't find parent dir's FID.\n"); return ENOENT; } @@ -613,7 +617,6 @@ int afs_ProcessOpCreate(struct vcache *avc, code = afs_GetVnodeName(avc, &pdir_fid, tname, 0); if (code) { printf("afs_ProcessOpCreate: Couldn't find file name\n"); - code = ENOENT; goto end; } @@ -629,12 +632,9 @@ int afs_ProcessOpCreate(struct vcache *avc, if (tdp->ddirty_flags & VDisconCreate) { /* If the parent dir has been created locally, defer - * this vnode for later by moving it to the end. - */ - afs_DDirtyVCList->ddirty_next = avc; - afs_DDirtyVCList = avc; - printf("afs_ProcessOpRemove: deferring this vcache\n"); - code = ENOTEMPTY; + * this vnode for later */ + printf("afs_ProcessOpCreate: deferring this vcache\n"); + code = EAGAIN; goto end; } @@ -714,7 +714,6 @@ int afs_ProcessOpCreate(struct vcache *avc, /* TODO: Handle errors. */ if (code) { printf("afs_ProcessOpCreate: error while creating vnode on server, code=%d .\n", code); - code = EIO; goto end; } @@ -772,7 +771,6 @@ int afs_ProcessOpCreate(struct vcache *avc, afs_vhashT[hash] = avc->hnext; } else { /* More elements in hash chain. */ - //for (tvc = afs_vhashT[hash]; tdp; tdp = tdp->hnext) { for (tvc = afs_vhashT[hash]; tvc; tvc = tvc->hnext) { if (tvc->hnext == avc) { tvc->hnext = avc->hnext; @@ -780,12 +778,12 @@ int afs_ProcessOpCreate(struct vcache *avc, } } } /* if (!afs_vhashT[i]->hnext) */ - QRemove(&afs_vhashTV[hash]); + QRemove(&avc->vhashq); /* Insert hash in new position. */ avc->hnext = afs_vhashT[new_hash]; afs_vhashT[new_hash] = avc; - QAdd(&afs_vhashTV[new_hash], &avc->vhashq); + QAdd(&afs_vhashTV[VCHashV(&newFid)], &avc->vhashq); ReleaseWriteLock(&afs_xvcache); @@ -813,7 +811,6 @@ int afs_ProcessOpCreate(struct vcache *avc, afs_indexUnique[tdc->index] = newFid.Fid.Unique; memcpy(&tdc->f.fid, &newFid, sizeof(struct VenusFid)); - //afs_MaybeWakeupTruncateDaemon(); } /* if fid match */ } /* if uniquifier match */ if (tdc) @@ -842,7 +839,7 @@ end: /*! * Remove a vnode on the server, be it file or directory. * Not much to do here only get the parent dir's fid and call the - * removel rpc. + * removal rpc. * * \param avc The deleted vcache * \param areq @@ -874,7 +871,7 @@ int afs_ProcessOpRemove(struct vcache *avc, struct vrequest *areq) tname = afs_osi_Alloc(AFSNAMEMAX); if (!tname) { - printf("afs_ProcessOpRemove: Couldn't find file name\n"); + printf("afs_ProcessOpRemove: Couldn't alloc space for file name\n"); return ENOMEM; } @@ -882,7 +879,6 @@ int afs_ProcessOpRemove(struct vcache *avc, struct vrequest *areq) code = afs_GetVnodeName(avc, &pdir_fid, tname, 1); if (code) { printf("afs_ProcessOpRemove: Couldn't find file name\n"); - code = ENOENT; goto end; } @@ -890,9 +886,7 @@ int afs_ProcessOpRemove(struct vcache *avc, struct vrequest *areq) /* Deleted children of this dir remain unsynchronized. * Defer this vcache. */ - afs_DDirtyVCList->ddirty_next = avc; - afs_DDirtyVCList = avc; - code = ENOTEMPTY; + code = EAGAIN; goto end; } @@ -1066,7 +1060,7 @@ int afs_SendChanges(struct vcache *avc, struct vrequest *areq) * \param acred User credentials. * * \return If all files synchronized succesfully, return 0, otherwise - * return 1. + * return error code * * \note For now, it's the request from the PDiscon pioctl. * @@ -1078,51 +1072,39 @@ int afs_ResyncDisconFiles(struct vrequest *areq, struct AFS_UCRED *acred) struct AFSFetchStatus fstat; struct AFSCallBack callback; struct AFSVolSync tsync; - struct vcache *shList, *shListStart; - int code; - int sync_failed = 0; - int ret_code = 0; - int defered = 0; + int code = 0; + int ucode; afs_int32 start = 0; XSTATS_DECLS; //AFS_STATCNT(afs_ResyncDisconFiles); - shList = shListStart = NULL; + ObtainWriteLock(&afs_disconDirtyLock, 707); - ObtainReadLock(&afs_DDirtyVCListLock); + while (!QEmpty(&afs_disconDirty)) { + tvc = QEntry(QPrev(&afs_disconDirty), struct vcache, dirtyq); - tvc = afs_DDirtyVCListStart; - while (tvc) { + /* Can't lock tvc whilst holding the discon dirty lock */ + ReleaseWriteLock(&afs_disconDirtyLock); /* Get local write lock. */ - ObtainWriteLock(&tvc->lock, 704); - sync_failed = 0; - - if ((tvc->ddirty_flags & VDisconRemove) && - (tvc->ddirty_flags & VDisconCreate)) { - /* Data created and deleted locally. The server doesn't - * need to know about this, so we'll just skip this file - * from the dirty list. - */ - goto skip_file; + ObtainWriteLock(&tvc->lock, 705); - } else if (tvc->ddirty_flags & VDisconRemove) { + if (tvc->ddirty_flags & VDisconRemove) { /* Delete the file on the server and just move on * to the next file. After all, it has been deleted * we can't replay any other operation it. */ code = afs_ProcessOpRemove(tvc, areq); - if (code == ENOTEMPTY) - defered = 1; - goto skip_file; + goto next_file; } else if (tvc->ddirty_flags & VDisconCreate) { /* For newly created files, we don't need a server lock. */ code = afs_ProcessOpCreate(tvc, areq, acred); - if (code == ENOTEMPTY) - defered = 1; if (code) - goto skip_file; + goto next_file; + + tvc->ddirty_flags &= ~VDisconCreate; + tvc->ddirty_flags |= VDisconCreated; } /* Get server write lock. */ @@ -1148,17 +1130,14 @@ int afs_ResyncDisconFiles(struct vrequest *areq, struct AFS_UCRED *acred) SHARED_LOCK, NULL)); - if (code) { - sync_failed = 1; - goto skip_file; - } + if (code) + goto next_file; - if ((tvc->ddirty_flags & VDisconRename) && - !(tvc->ddirty_flags & VDisconCreate)) { - /* Rename file only if it hasn't been created locally. */ + if (tvc->ddirty_flags & VDisconRename) { + /* If we're renaming the file, do so now */ code = afs_ProcessOpRename(tvc, areq); if (code) - goto skip_file; + goto unlock_srv_file; } /* Issue a FetchStatus to get info about DV and callbacks. */ @@ -1188,13 +1167,12 @@ int afs_ResyncDisconFiles(struct vrequest *areq, struct AFS_UCRED *acred) NULL)); if (code) { - sync_failed = 1; goto unlock_srv_file; } if ((dv_match(tvc, fstat) && (tvc->m.Date == fstat.ServerModTime)) || (afs_ConflictPolicy == CLIENT_WINS) || - (tvc->ddirty_flags & VDisconCreate)) { + (tvc->ddirty_flags & VDisconCreated)) { /* * Send changes to the server if there's data version match, or * client wins policy has been selected or file has been created @@ -1209,39 +1187,13 @@ int afs_ResyncDisconFiles(struct vrequest *areq, struct AFS_UCRED *acred) } else if (afs_ConflictPolicy == SERVER_WINS) { /* DV mismatch, apply collision resolution policy. */ - /* Dequeue whatever callback is on top (XXX: propably none). */ - ObtainWriteLock(&afs_xcbhash, 706); - afs_DequeueCallback(tvc); - tvc->callback = NULL; - tvc->states &= ~(CStatd | CDirty | CUnique); - ReleaseWriteLock(&afs_xcbhash); - - /* Save metadata. File length gets updated as well because we - * just removed CDirty from the avc. - */ - //afs_ProcessFS(tvc, &fstat, areq); - /* Discard this files chunks and remove from current dir. */ - afs_TryToSmush(tvc, acred, 1); - osi_dnlc_purgedp(tvc); - if (tvc->linkData && !(tvc->states & CCore)) { - /* Take care of symlinks. */ - afs_osi_Free(tvc->linkData, strlen(tvc->linkData) + 1); - tvc->linkData = NULL; - } - - /* Otherwise file content's won't be synchronized. */ + afs_ResetVCache(tvc, acred); tvc->truncPos = AFS_NOTRUNC; - } else { printf("afs_ResyncDisconFiles: no resolution policy selected.\n"); } /* if DV match or client wins policy */ - if (code) { - sync_failed = 1; - printf("Sync FAILED.\n"); - } - unlock_srv_file: /* Release server write lock. */ do { @@ -1249,89 +1201,123 @@ unlock_srv_file: if (tc) { XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RELEASELOCK); RX_AFS_GUNLOCK(); - code = RXAFS_ReleaseLock(tc->id, + ucode = RXAFS_ReleaseLock(tc->id, (struct AFSFid *) &tvc->fid.Fid, &tsync); RX_AFS_GLOCK(); XSTATS_END_TIME; } else - code = -1; + ucode = -1; } while (afs_Analyze(tc, - code, + ucode, &tvc->fid, areq, AFS_STATS_FS_RPCIDX_RELEASELOCK, SHARED_LOCK, NULL)); -skip_file: - /* Pop this dirty vc out. */ - tmp = tvc; - tvc = tvc->ddirty_next; - - if (!defered) { - /* Vnode not deferred. Clean it up. */ - if (!sync_failed) { - if (tmp->ddirty_flags == VDisconShadowed) { - /* Dirs that have only the shadow flag set might still - * be used so keep them in a different list, that gets - * deleted after resync is done. - */ - if (!shListStart) - shListStart = shList = tmp; - else { - shList->ddirty_next = tmp; - shList = tmp; - } - } else if (tmp->ddirty_flags & VDisconShadowed) - /* We can discard the shadow dir now. */ - afs_DeleteShadowDir(tmp); - - /* Drop the refcount on this vnode because it's not in the - * list anymore. - */ - afs_PutVCache(tmp); - - /* Only if sync was successfull, - * clear flags and dirty references. - */ - tmp->ddirty_next = NULL; - tmp->ddirty_flags = 0; - } else - ret_code = 1; +next_file: + ObtainWriteLock(&afs_disconDirtyLock, 710); + if (code == 0) { + /* Replayed successfully - pull the vcache from the + * disconnected list */ + tvc->ddirty_flags = 0; + QRemove(&tvc->dirtyq); + afs_PutVCache(tvc); } else { - tmp->ddirty_next = NULL; - defered = 0; - } /* if (!defered) */ + if (code == EAGAIN) { + /* Operation was deferred. Pull it from the current place in + * the list, and stick it at the end again */ + QRemove(&tvc->dirtyq); + QAdd(&afs_disconDirty, &tvc->dirtyq); + } else { + /* Failed - keep state as is, and let the user know we died */ + ReleaseWriteLock(&tvc->lock); + break; + } + } /* Release local write lock. */ - ReleaseWriteLock(&tmp->lock); + ReleaseWriteLock(&tvc->lock); } /* while (tvc) */ - /* Delete the rest of shadow dirs. */ - tvc = shListStart; - while (tvc) { - ObtainWriteLock(&tvc->lock, 764); + if (code) { + ReleaseWriteLock(&afs_disconDirtyLock); + return code; + } + + /* Dispose of all of the shadow directories */ + afs_DisconDiscardAllShadows(0, acred); + + ReleaseWriteLock(&afs_disconDirtyLock); + return code; +} + +/*! + * Discard all of our shadow directory copies. If squash is true, then + * we also invalidate the vcache holding the shadow directory, to ensure + * that any disconnected changes are deleted + * + * \param squash + * \param acred + * + * \note afs_disconDirtyLock must be held on entry. It will be released + * and reobtained + */ + +static void +afs_DisconDiscardAllShadows(int squash, struct AFS_UCRED *acred) { + struct vcache *tvc; + + while (!QEmpty(&afs_disconShadow)) { + tvc = QEntry(QNext(&afs_disconShadow), struct vcache, shadowq); + + /* Must release the dirty lock to be able to get a vcache lock */ + ReleaseWriteLock(&afs_disconDirtyLock); + ObtainWriteLock(&tvc->lock, 706); afs_DeleteShadowDir(tvc); tvc->shVnode = 0; tvc->shUnique = 0; - tmp = tvc; - tvc = tvc->ddirty_next; - tmp->ddirty_next = NULL; + if (squash) + afs_ResetVCache(tvc, acred); - ReleaseWriteLock(&tmp->lock); + ObtainWriteLock(&afs_disconDirtyLock, 709); + QRemove(&tvc->shadowq); + + ReleaseWriteLock(&tvc->lock); } /* while (tvc) */ +} + +/*! + * This function throws away the whole disconnected state, allowing + * the cache manager to reconnect to a server if we get into a state + * where reconiliation is impossible. + * + * \param acred + * + */ +void +afs_DisconDiscardAll(struct AFS_UCRED *acred) { + struct vcache *tvc; + + ObtainWriteLock(&afs_disconDirtyLock, 717); + while (!QEmpty(&afs_disconDirty)) { + tvc = QEntry(QPrev(&afs_disconDirty), struct vcache, dirtyq); + ReleaseWriteLock(&afs_disconDirtyLock); - if (ret_code == 0) { - /* NULLIFY dirty list only if resync complete. */ - afs_DDirtyVCListStart = NULL; - afs_DDirtyVCList = NULL; + ObtainWriteLock(&tvc->lock, 718); + afs_ResetVCache(tvc, acred); + tvc->truncPos = AFS_NOTRUNC; + ReleaseWriteLock(&tvc->lock); + afs_PutVCache(tvc); + ObtainWriteLock(&afs_disconDirtyLock, 719); } - ReleaseReadLock(&afs_DDirtyVCListLock); - return ret_code; + afs_DisconDiscardAllShadows(1, acred); + + ReleaseWriteLock(&afs_disconDirtyLock); } /*! @@ -1342,21 +1328,26 @@ skip_file: void afs_DbgDisconFiles() { struct vcache *tvc; + struct afs_q *q; int i = 0; - tvc = afs_DDirtyVCListStart; printf("List of dirty files: \n"); - while (tvc) { + + ObtainReadLock(&afs_disconDirtyLock); + for (q = QPrev(&afs_disconDirty); q != &afs_disconDirty; q = QPrev(q)) { + tvc = QEntry(q, struct vcache, dirtyq); + printf("Cell=%u Volume=%u VNode=%u Unique=%u\n", tvc->fid.Cell, tvc->fid.Fid.Volume, tvc->fid.Fid.Vnode, tvc->fid.Fid.Unique); - tvc = tvc->ddirty_next; + i++; if (i >= 30) osi_Panic("afs_DbgDisconFiles: loop in dirty list\n"); } + ReleaseReadLock(&afs_disconDirtyLock); } /*! diff --git a/src/afs/afs_init.c b/src/afs/afs_init.c index 0ca6236..ef51501 100644 --- a/src/afs/afs_init.c +++ b/src/afs/afs_init.c @@ -47,7 +47,7 @@ static struct vnode *volumeVnode; #endif #if defined(AFS_DISCON_ENV) afs_rwlock_t afs_discon_lock; -extern afs_rwlock_t afs_DDirtyVCListLock; +extern afs_rwlock_t afs_disconDirtyLock; #endif /* @@ -116,7 +116,9 @@ afs_CacheInit(afs_int32 astatSize, afs_int32 afiles, afs_int32 ablocks, AFS_RWLOCK_INIT(&afs_xaxs, "afs_xaxs"); #ifdef AFS_DISCON_ENV AFS_RWLOCK_INIT(&afs_discon_lock, "afs_discon_lock"); - AFS_RWLOCK_INIT(&afs_DDirtyVCListLock, "afs_DDirtyVCListLock"); + AFS_RWLOCK_INIT(&afs_disconDirtyLock, "afs_disconDirtyLock"); + QInit(&afs_disconDirty); + QInit(&afs_disconShadow); #endif osi_dnlc_init(); diff --git a/src/afs/afs_pioctl.c b/src/afs/afs_pioctl.c index 5497272..2e469d3 100644 --- a/src/afs/afs_pioctl.c +++ b/src/afs/afs_pioctl.c @@ -33,7 +33,6 @@ afs_int32 afs_showflags = GAGUSER | GAGCONSOLE; /* show all messages */ #ifdef AFS_DISCON_ENV afs_int32 afs_is_disconnected; -afs_int32 afs_is_logging; afs_int32 afs_is_discon_rw; /* On reconnection, turn this knob on until it finishes, * then turn it off. @@ -4690,9 +4689,12 @@ DECL_PIOCTL(PDiscon) afs_in_sync = 0; if (code && !force) { - printf("Files not synchronized properly, still in discon state. \ - Please retry or use \"force\".\n"); + printf("Files not synchronized properly, still in discon state. \n" + "Please retry or use \"force\".\n"); } else { + if (force) { + afs_DisconDiscardAll(*acred); + } afs_is_disconnected = 0; afs_is_discon_rw = 0; printf("\nSync succeeded. You are back online.\n"); diff --git a/src/afs/afs_segments.c b/src/afs/afs_segments.c index aa5fc8b..6d107bc 100644 --- a/src/afs/afs_segments.c +++ b/src/afs/afs_segments.c @@ -216,11 +216,9 @@ afs_StoreAllSegments(register struct vcache *avc, struct vrequest *areq, osi_VM_StoreAllSegments(avc); } if (AFS_IS_DISCONNECTED && !AFS_IN_SYNC) { - if (!AFS_IS_LOGGING) { - /* This will probably make someone sad ... */ - /*printf("Net down in afs_StoreSegments\n");*/ - return ENETDOWN; - } + /* This will probably make someone sad ... */ + /*printf("Net down in afs_StoreSegments\n");*/ + return ENETDOWN; } ConvertWToSLock(&avc->lock); diff --git a/src/afs/afs_vcache.c b/src/afs/afs_vcache.c index d3db0ae..511dfa6 100644 --- a/src/afs/afs_vcache.c +++ b/src/afs/afs_vcache.c @@ -935,8 +935,9 @@ restart: tvc->callback = serverp; /* to minimize chance that clear * request is lost */ #if defined(AFS_DISCON_ENV) - tvc->ddirty_next = NULL; tvc->ddirty_flags = 0; + tvc->shVnode = 0; + tvc->shUnique = 0; #endif i = VCHash(afid); @@ -1617,20 +1618,8 @@ int afs_WriteVCacheDiscon(register struct vcache *avc, flags |= VDisconTrunc; } - ObtainWriteLock(&afs_DDirtyVCListLock, 701); - - if (flags) { - /* Add to disconnected dirty list and set dirty flag.*/ - if (!avc->ddirty_flags || - (avc->ddirty_flags == VDisconShadowed)) { - /* Not in dirty list. */ - AFS_DISCON_ADD_DIRTY(avc, 1); - } - - avc->ddirty_flags |= flags; - } - - ReleaseWriteLock(&afs_DDirtyVCListLock); + if (flags) + afs_DisconAddDirty(avc, flags, 1); /* XXX: How about the rest of the fields? */ diff --git a/src/afs/discon.h b/src/afs/discon.h index ef95e27..497291b 100644 --- a/src/afs/discon.h +++ b/src/afs/discon.h @@ -3,29 +3,25 @@ #ifndef AFS_DISCON_ENV #define AFS_IS_DISCONNECTED 0 -#define AFS_IS_LOGGING 0 #define AFS_IS_DISCON_RW 0 #define AFS_IN_SYNC 0 #define AFS_DISCON_LOCK() #define AFS_DISCON_UNLOCK() -#define AFS_DISCON_ADD_DIRTY(avc, lock) +#define afs_DisconAddDirty(x, y, z) #else extern afs_int32 afs_is_disconnected; -extern afs_int32 afs_is_logging; extern afs_int32 afs_is_discon_rw; extern afs_int32 afs_in_sync; extern afs_rwlock_t afs_discon_lock; -extern struct vcache *afs_DDirtyVCList; -extern struct vcache *afs_DDirtyVCListStart; -extern struct vcache *afs_DDirtyVCListPrev; -extern afs_rwlock_t afs_DDirtyVCListLock; -extern afs_int32 afs_ConflictPolicy; +extern struct afs_q afs_disconDirty; +extern struct afs_q afs_disconShadow; +extern afs_rwlock_t afs_disconDirtyLock; +extern afs_int32 afs_ConflictPolicy; -extern void afs_RemoveAllConns(); extern afs_uint32 afs_DisconVnode; /* XXX: not protected. */ /* For afs_GenFakeFid. */ @@ -38,7 +34,7 @@ extern int afs_WriteVCacheDiscon(register struct vcache *avc, struct vattr *attrs); extern int afs_ResyncDisconFiles(struct vrequest *areq, struct AFS_UCRED *acred); -extern void afs_RemoveAllConns(); +extern void afs_RemoveAllConns(void); extern void afs_GenFakeFid(struct VenusFid *afid, afs_uint32 avtype); extern void afs_GenShadowFid(struct VenusFid *afid); extern void afs_GenDisconStatus(struct vcache *adp, @@ -47,41 +43,46 @@ extern void afs_GenDisconStatus(struct vcache *adp, struct vattr *attrs, struct vrequest *areq, int file_type); -extern int afs_HashOutDCache(struct dcache *adc, int zap); extern int afs_MakeShadowDir(struct vcache *avc); extern void afs_DeleteShadowDir(struct vcache *avc); -extern struct dcache *afs_FindDCacheByFid(register struct VenusFid *afid); +extern struct dcache *afs_FindDCacheByFid(struct VenusFid *afid); extern void afs_UpdateStatus(struct vcache *avc, struct VenusFid *afid, struct vrequest *areq, struct AFSFetchStatus *Outsp, struct AFSCallBack *acb, afs_uint32 start); -extern void afs_RemoveAllConns(); +extern void afs_DisconDiscardAll(struct AFS_UCRED *); #define AFS_IS_DISCONNECTED (afs_is_disconnected) -#define AFS_IS_LOGGING (afs_is_logging) #define AFS_IS_DISCON_RW (afs_is_discon_rw) #define AFS_IN_SYNC (afs_in_sync) #define AFS_DISCON_LOCK() ObtainReadLock(&afs_discon_lock) #define AFS_DISCON_UNLOCK() ReleaseReadLock(&afs_discon_lock) -/* Call with avc and afs_DDirtyVCListLock w locks held. */ -#define AFS_DISCON_ADD_DIRTY(avc, lock) \ -do { \ - int retry = 0; \ - if (!afs_DDirtyVCListStart) { \ - afs_DDirtyVCListStart = afs_DDirtyVCList = avc; \ - } else { \ - afs_DDirtyVCList->ddirty_next = avc; \ - afs_DDirtyVCList = avc; \ - } \ - if (lock) \ - ObtainWriteLock(&afs_xvcache, 763); \ - osi_vnhold(avc, 0); \ - if (lock) \ - ReleaseWriteLock(&afs_xvcache); \ -} while(0); +/* Call with avc lock held */ +static inline void afs_DisconAddDirty(struct vcache *avc, int operation, int lock) { + if (!avc->ddirty_flags) { + if (lock) + ObtainWriteLock(&afs_xvcache, 702); + ObtainWriteLock(&afs_disconDirtyLock, 703); + QAdd(&afs_disconDirty, &avc->dirtyq); + osi_vnhold(avc, 0); + ReleaseWriteLock(&afs_disconDirtyLock); + if (lock) + ReleaseWriteLock(&afs_xvcache); + } + avc->ddirty_flags |= operation; +} + +/* Call with avc lock held */ +static inline void afs_DisconRemoveDirty(struct vcache *avc) { + ObtainWriteLock(&afs_disconDirtyLock, 704); + QRemove(&avc->dirtyq); + ReleaseWriteLock(&afs_disconDirtyLock); + avc->ddirty_flags = 0; + afs_PutVCache(avc); +} #endif /* AFS_DISCON_ENV */ #endif /* _DISCON_H */ -- 1.9.4