From dc0b96f5b7ee3f5f95639dfd5afb98ecb4ff6885 Mon Sep 17 00:00:00 2001 From: Simon Wilkinson Date: Mon, 19 Jan 2009 19:42:32 +0000 Subject: [PATCH] disconnected-extend-truncation-20090119 LICENSE IPL10 FIXES 124094 implement extending truncation so disconnected works --- src/afs/VNOPS/afs_vnop_attrs.c | 10 +++- src/afs/VNOPS/afs_vnop_write.c | 120 ++--------------------------------------- src/afs/afs_dcache.c | 84 +++++++++++++++++++++++++++++ src/afs/afs_disconnected.c | 2 +- src/afs/afs_prototypes.h | 8 +++ src/afs/afs_segments.c | 62 +++++++++++++++++++++ 6 files changed, 168 insertions(+), 118 deletions(-) diff --git a/src/afs/VNOPS/afs_vnop_attrs.c b/src/afs/VNOPS/afs_vnop_attrs.c index 50ddd8b..46b9886 100644 --- a/src/afs/VNOPS/afs_vnop_attrs.c +++ b/src/afs/VNOPS/afs_vnop_attrs.c @@ -545,12 +545,18 @@ afs_setattr(OSI_VC_DECL(avc), register struct vattr *attrs, ObtainWriteLock(&avc->lock, 128); avc->states |= CDirty; - code = afs_TruncateAllSegments(avc, tsize, &treq, acred); + if (AFS_IS_DISCONNECTED && tsize >=avc->m.Length) { + /* If we're growing the file, and we're disconnected, we need + * to make the relevant dcache chunks appear ourselves. */ + code = afs_ExtendSegments(avc, tsize, &treq); + } else { + code = afs_TruncateAllSegments(avc, tsize, &treq, acred); + } #ifdef AFS_LINUX26_ENV /* We must update the Linux kernel's idea of file size as soon as * possible, to avoid racing with delayed writepages delivered by * pdflush */ - if (code == 0) + if (code == 0) i_size_write(AFSTOV(avc), tsize); #endif /* if date not explicitly set by this call, set it ourselves, since we diff --git a/src/afs/VNOPS/afs_vnop_write.c b/src/afs/VNOPS/afs_vnop_write.c index f7c1b01..5b56a7e 100644 --- a/src/afs/VNOPS/afs_vnop_write.c +++ b/src/afs/VNOPS/afs_vnop_write.c @@ -110,8 +110,6 @@ afs_StoreOnLastReference(register struct vcache *avc, return code; } - - int afs_MemWrite(register struct vcache *avc, struct uio *auio, int aio, struct AFS_UCRED *acred, int noLock) @@ -217,66 +215,13 @@ afs_MemWrite(register struct vcache *avc, struct uio *auio, int aio, tvec = (struct iovec *)osi_AllocSmallSpace(sizeof(struct iovec)); #endif while (totalLength > 0) { - /* - * The following line is necessary because afs_GetDCache with - * flag == 4 expects the length field to be filled. It decides - * from this whether it's necessary to fetch data into the chunk - * before writing or not (when the whole chunk is overwritten!). - */ - len = totalLength; /* write this amount by default */ - if (noLock) { - tdc = afs_FindDCache(avc, filePos); - if (tdc) - ObtainWriteLock(&tdc->lock, 653); - } else if (afs_blocksUsed > - PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks)) { - tdc = afs_FindDCache(avc, filePos); - if (tdc) { - ObtainWriteLock(&tdc->lock, 654); - if (!hsame(tdc->f.versionNo, avc->m.DataVersion) - || (tdc->dflags & DFFetching)) { - ReleaseWriteLock(&tdc->lock); - afs_PutDCache(tdc); - tdc = NULL; - } - } - if (!tdc) { - afs_MaybeWakeupTruncateDaemon(); - while (afs_blocksUsed > - PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks)) { - ReleaseWriteLock(&avc->lock); - if (afs_blocksUsed - afs_blocksDiscarded > - PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks)) { - afs_WaitForCacheDrain = 1; - afs_osi_Sleep(&afs_WaitForCacheDrain); - } - afs_MaybeFreeDiscardedDCache(); - afs_MaybeWakeupTruncateDaemon(); - ObtainWriteLock(&avc->lock, 506); - } - avc->states |= CDirty; - tdc = afs_GetDCache(avc, filePos, &treq, &offset, &len, 4); - if (tdc) - ObtainWriteLock(&tdc->lock, 655); - } - } else { - tdc = afs_GetDCache(avc, filePos, &treq, &offset, &len, 4); - if (tdc) - ObtainWriteLock(&tdc->lock, 656); - } + tdc = afs_ObtainDCacheForWriting(avc, filePos, totalLength, &treq, + noLock); if (!tdc) { error = EIO; break; } - if (!(afs_indexFlags[tdc->index] & IFDataMod)) { - afs_stats_cmperf.cacheCurrDirtyChunks++; - afs_indexFlags[tdc->index] |= IFDataMod; /* so it doesn't disappear */ - } - if (!(tdc->f.states & DWriting)) { - /* don't mark entry as mod if we don't have to */ - tdc->f.states |= DWriting; - tdc->dflags |= DFEntryMod; - } + len = totalLength; /* write this amount by default */ offset = filePos - AFS_CHUNKTOBASE(tdc->f.chunk); max = AFS_CHUNKTOSIZE(tdc->f.chunk); /* max size of this chunk */ @@ -483,67 +428,12 @@ afs_UFSWrite(register struct vcache *avc, struct uio *auio, int aio, tvec = (struct iovec *)osi_AllocSmallSpace(sizeof(struct iovec)); #endif while (totalLength > 0) { - /* - * The following line is necessary because afs_GetDCache with - * flag == 4 expects the length field to be filled. It decides - * from this whether it's necessary to fetch data into the chunk - * before writing or not (when the whole chunk is overwritten!). - */ - len = totalLength; /* write this amount by default */ - /* read the cached info */ - if (noLock) { - tdc = afs_FindDCache(avc, filePos); - if (tdc) - ObtainWriteLock(&tdc->lock, 657); - } else if (afs_blocksUsed > - PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks)) { - tdc = afs_FindDCache(avc, filePos); - if (tdc) { - ObtainWriteLock(&tdc->lock, 658); - if (!hsame(tdc->f.versionNo, avc->m.DataVersion) - || (tdc->dflags & DFFetching)) { - ReleaseWriteLock(&tdc->lock); - afs_PutDCache(tdc); - tdc = NULL; - } - } - if (!tdc) { - afs_MaybeWakeupTruncateDaemon(); - while (afs_blocksUsed > - PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks)) { - ReleaseWriteLock(&avc->lock); - if (afs_blocksUsed - afs_blocksDiscarded > - PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks)) { - afs_WaitForCacheDrain = 1; - afs_osi_Sleep(&afs_WaitForCacheDrain); - } - afs_MaybeFreeDiscardedDCache(); - afs_MaybeWakeupTruncateDaemon(); - ObtainWriteLock(&avc->lock, 509); - } - avc->states |= CDirty; - tdc = afs_GetDCache(avc, filePos, &treq, &offset, &len, 4); - if (tdc) - ObtainWriteLock(&tdc->lock, 659); - } - } else { - tdc = afs_GetDCache(avc, filePos, &treq, &offset, &len, 4); - if (tdc) - ObtainWriteLock(&tdc->lock, 660); - } + tdc = afs_ObtainDCacheForWriting(avc, filePos, totalLength, &treq, + noLock); if (!tdc) { error = EIO; break; } - if (!(afs_indexFlags[tdc->index] & IFDataMod)) { - afs_stats_cmperf.cacheCurrDirtyChunks++; - afs_indexFlags[tdc->index] |= IFDataMod; /* so it doesn't disappear */ - } - if (!(tdc->f.states & DWriting)) { - /* don't mark entry as mod if we don't have to */ - tdc->f.states |= DWriting; - tdc->dflags |= DFEntryMod; - } #if defined(LINUX_USE_FH) tfile = (struct osi_file *)osi_UFSOpen_fh(&tdc->f.fh, tdc->f.fh_type); #else diff --git a/src/afs/afs_dcache.c b/src/afs/afs_dcache.c index 0ecb739..52c3d30 100644 --- a/src/afs/afs_dcache.c +++ b/src/afs/afs_dcache.c @@ -3642,6 +3642,90 @@ shutdown_dcache(void) } +/*! + * Get a dcache ready for writing, respecting the current cache size limits + * + * len is required because afs_GetDCache with flag == 4 expects the length + * field to be filled. It decides from this whether it's necessary to fetch + * data into the chunk before writing or not (when the whole chunk is + * overwritten!). + * + * \param avc The vcache to fetch a dcache for + * \param filePos The start of the section to be written + * \param len The length of the section to be written + * \param areq + * \param noLock + * + * \return If successful, a reference counted dcache with tdc->lock held. Lock + * must be released and afs_PutDCache() called to free dcache. + * NULL on failure + * + * \note avc->lock must be held on entry. Function may release and reobtain + * avc->lock and GLOCK. + */ + +struct dcache * +afs_ObtainDCacheForWriting(struct vcache *avc, afs_size_t filePos, + afs_size_t len, struct vrequest *areq, + int noLock) { + struct dcache *tdc = NULL; + afs_size_t offset; + + /* read the cached info */ + if (noLock) { + tdc = afs_FindDCache(avc, filePos); + if (tdc) + ObtainWriteLock(&tdc->lock, 657); + } else if (afs_blocksUsed > + PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks)) { + tdc = afs_FindDCache(avc, filePos); + if (tdc) { + ObtainWriteLock(&tdc->lock, 658); + if (!hsame(tdc->f.versionNo, avc->m.DataVersion) + || (tdc->dflags & DFFetching)) { + ReleaseWriteLock(&tdc->lock); + afs_PutDCache(tdc); + tdc = NULL; + } + } + if (!tdc) { + afs_MaybeWakeupTruncateDaemon(); + while (afs_blocksUsed > + PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks)) { + ReleaseWriteLock(&avc->lock); + if (afs_blocksUsed - afs_blocksDiscarded > + PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks)) { + afs_WaitForCacheDrain = 1; + afs_osi_Sleep(&afs_WaitForCacheDrain); + } + afs_MaybeFreeDiscardedDCache(); + afs_MaybeWakeupTruncateDaemon(); + ObtainWriteLock(&avc->lock, 509); + } + avc->states |= CDirty; + tdc = afs_GetDCache(avc, filePos, areq, &offset, &len, 4); + if (tdc) + ObtainWriteLock(&tdc->lock, 659); + } + } else { + tdc = afs_GetDCache(avc, filePos, areq, &offset, &len, 4); + if (tdc) + ObtainWriteLock(&tdc->lock, 660); + } + if (tdc) { + if (!(afs_indexFlags[tdc->index] & IFDataMod)) { + afs_stats_cmperf.cacheCurrDirtyChunks++; + afs_indexFlags[tdc->index] |= IFDataMod; /* so it doesn't disappear */ + } + if (!(tdc->f.states & DWriting)) { + /* don't mark entry as mod if we don't have to */ + tdc->f.states |= DWriting; + tdc->dflags |= DFEntryMod; + } + } + return tdc; +} + #if defined(AFS_DISCON_ENV) /*! diff --git a/src/afs/afs_disconnected.c b/src/afs/afs_disconnected.c index e52c3f6..e520cdc 100644 --- a/src/afs/afs_disconnected.c +++ b/src/afs/afs_disconnected.c @@ -605,7 +605,7 @@ int afs_ProcessOpCreate(struct vcache *avc, tname = afs_osi_Alloc(AFSNAMEMAX); if (!tname) { - printf("afs_ProcessOpCreate: Couldn't find file name\n"); + printf("afs_ProcessOpCreate: Couldn't alloc space for file name\n"); return ENOMEM; } diff --git a/src/afs/afs_prototypes.h b/src/afs/afs_prototypes.h index 828f84b..6339eff 100644 --- a/src/afs/afs_prototypes.h +++ b/src/afs/afs_prototypes.h @@ -280,6 +280,12 @@ extern int afs_WriteDCache(register struct dcache *adc, int atime); extern int afs_wakeup(register struct vcache *avc); extern int afs_InitCacheFile(char *afile, ino_t ainode); extern int afs_DCacheMissingChunks(struct vcache *avc); +extern struct dcache *afs_ObtainDCacheForWriting(struct vcache *avc, + afs_size_t filePos, + afs_size_t len, + struct vrequest *areq, + int noLock); + /* afs_disconnected.c */ @@ -780,6 +786,8 @@ extern int afs_StoreMini(register struct vcache *avc, struct vrequest *areq); extern int afs_StoreAllSegments(register struct vcache *avc, struct vrequest *areq, int sync); extern int afs_InvalidateAllSegments(struct vcache *avc); +extern int afs_ExtendSegments(struct vcache *avc, + afs_size_t alen, struct vrequest *areq); extern int afs_TruncateAllSegments(register struct vcache *avc, afs_size_t alen, struct vrequest *areq, struct AFS_UCRED *acred); diff --git a/src/afs/afs_segments.c b/src/afs/afs_segments.c index 35235fe..97674c3 100644 --- a/src/afs/afs_segments.c +++ b/src/afs/afs_segments.c @@ -920,6 +920,68 @@ afs_InvalidateAllSegments(struct vcache *avc) return 0; } +/*! + * + * Extend a cache file + * + * \param avc pointer to vcache to extend data for + * \param alen Length to extend file to + * \param areq + * + * \note avc must be write locked. May release and reobtain avc and GLOCK + */ +int +afs_ExtendSegments(struct vcache *avc, afs_size_t alen, struct vrequest *areq) { + afs_size_t offset, toAdd; + struct osi_file *tfile; + afs_int32 code = 0; + struct dcache *tdc; + void *zeros; + + zeros = (void *) afs_osi_Alloc(AFS_PAGESIZE); + if (zeros == NULL) + return ENOMEM; + memset(zeros, 0, AFS_PAGESIZE); + + while (avc->m.Length < alen) { + tdc = afs_ObtainDCacheForWriting(avc, avc->m.Length, alen - avc->m.Length, areq, 0); + if (!tdc) { + code = EIO; + break; + } + + toAdd = alen - avc->m.Length; + + offset = avc->m.Length - AFS_CHUNKTOBASE(tdc->f.chunk); + if (offset + toAdd > AFS_CHUNKTOSIZE(tdc->f.chunk)) { + toAdd = AFS_CHUNKTOSIZE(tdc->f.chunk) - offset; + } +#if defined(LINUX_USE_FH) + tfile = afs_CFileOpen(&tdc->f.fh, tdc->f.fh_type); +#else + tfile = afs_CFileOpen(tdc->f.inode); +#endif + while(tdc->validPos < avc->m.Length + toAdd) { + afs_size_t towrite; + + towrite = (avc->m.Length + toAdd) - tdc->validPos; + if (towrite > AFS_PAGESIZE) towrite = AFS_PAGESIZE; + + code = afs_CFileWrite(tfile, + tdc->validPos - AFS_CHUNKTOBASE(tdc->f.chunk), + zeros, towrite); + tdc->validPos += towrite; + } + afs_CFileClose(tfile); + afs_AdjustSize(tdc, offset + toAdd ); + avc->m.Length += toAdd; + ReleaseWriteLock(&tdc->lock); + afs_PutDCache(tdc); + } + + afs_osi_Free(zeros, AFS_PAGESIZE); + return code; +} /* * afs_TruncateAllSegments -- 1.9.4