disconnected-extend-truncation-20090119
authorSimon Wilkinson <sxw@inf.ed.ac.uk>
Mon, 19 Jan 2009 19:42:32 +0000 (19:42 +0000)
committerDerrick Brashear <shadow@dementia.org>
Mon, 19 Jan 2009 19:42:32 +0000 (19:42 +0000)
LICENSE IPL10
FIXES 124094

implement extending truncation so disconnected works

src/afs/VNOPS/afs_vnop_attrs.c
src/afs/VNOPS/afs_vnop_write.c
src/afs/afs_dcache.c
src/afs/afs_disconnected.c
src/afs/afs_prototypes.h
src/afs/afs_segments.c

index 50ddd8b..46b9886 100644 (file)
@@ -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
index f7c1b01..5b56a7e 100644 (file)
@@ -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
index 0ecb739..52c3d30 100644 (file)
@@ -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)
 
 /*!
index e52c3f6..e520cdc 100644 (file)
@@ -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;
     }
 
index 828f84b..6339eff 100644 (file)
@@ -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);
index 35235fe..97674c3 100644 (file)
@@ -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