Resolve error return issues in writepage
[openafs.git] / src / afs / LINUX / osi_vnodeops.c
index bd2745d..ae26fa2 100644 (file)
@@ -49,8 +49,6 @@
 #define MAX_ERRNO 1000L
 #endif
 
-#define LockPage(pp) lock_page(pp)
-#define UnlockPage(pp) unlock_page(pp)
 extern struct backing_dev_info afs_backing_dev_info;
 
 extern struct vcache *afs_globalVp;
@@ -472,7 +470,7 @@ afs_linux_lock(struct file *fp, int cmd, struct file_lock *flp)
     struct AFS_FLOCK flock;
     
     /* Convert to a lock format afs_lockctl understands. */
-    memset((char *)&flock, 0, sizeof(flock));
+    memset(&flock, 0, sizeof(flock));
     flock.l_type = flp->fl_type;
     flock.l_pid = flp->fl_pid;
     flock.l_whence = 0;
@@ -535,7 +533,7 @@ afs_linux_flock(struct file *fp, int cmd, struct file_lock *flp) {
     cred_t *credp = crref();
     struct AFS_FLOCK flock;
     /* Convert to a lock format afs_lockctl understands. */
-    memset((char *)&flock, 0, sizeof(flock));
+    memset(&flock, 0, sizeof(flock));
     flock.l_type = flp->fl_type;
     flock.l_pid = flp->fl_pid;
     flock.l_whence = 0;
@@ -1447,7 +1445,7 @@ afs_linux_read_cache(struct file *cachefp, struct page *page,
            SetPageUptodate(page);
 
            if (task)
-               UnlockPage(page);
+               unlock_page(page);
         } else if (task) {
            afs_pagecopy_queue_page(task, cachepage, page);
        } else {
@@ -1456,7 +1454,7 @@ afs_linux_read_cache(struct file *cachefp, struct page *page,
     }
 
     if (code && task) {
-        UnlockPage(page);
+        unlock_page(page);
     }
 
 out:
@@ -1727,7 +1725,7 @@ afs_linux_bypass_readpages(struct file *fp, struct address_space *mapping,
        isize = (i_size_read(fp->f_mapping->host) - 1) >> PAGE_CACHE_SHIFT;
        if(pp->index > isize) {
            if(PageLocked(pp))
-               UnlockPage(pp);
+               unlock_page(pp);
            continue;
        }
 
@@ -1740,7 +1738,7 @@ afs_linux_bypass_readpages(struct file *fp, struct address_space *mapping,
         code = add_to_page_cache(pp, mapping, pp->index, GFP_KERNEL);
         if(base_index != pp->index) {
             if(PageLocked(pp))
-                                UnlockPage(pp);
+                unlock_page(pp);
             page_cache_release(pp);
            iovecp[page_ix].iov_base = (void *) 0;
            base_index++;
@@ -1749,13 +1747,13 @@ afs_linux_bypass_readpages(struct file *fp, struct address_space *mapping,
         base_index++;
         if(code) {
            if(PageLocked(pp))
-               UnlockPage(pp);
+               unlock_page(pp);
            page_cache_release(pp);
            iovecp[page_ix].iov_base = (void *) 0;
        } else {
            page_count++;
            if(!PageLocked(pp)) {
-               LockPage(pp);
+               lock_page(pp);
            }
 
            /* save the page for background map */
@@ -1884,7 +1882,7 @@ afs_linux_readpage(struct file *fp, struct page *pp)
        code = afs_linux_fillpage(fp, pp);
        if (!code)
            code = afs_linux_prefetch(fp, pp);
-       UnlockPage(pp);
+       unlock_page(pp);
     }
 
     return code;
@@ -1985,15 +1983,43 @@ afs_linux_readpages(struct file *fp, struct address_space *mapping,
     return 0;
 }
 
+/* Prepare an AFS vcache for writeback. Should be called with the vcache
+ * locked */
+static inline int
+afs_linux_prepare_writeback(struct vcache *avc) {
+    if (avc->f.states & CPageWrite) {
+       return AOP_WRITEPAGE_ACTIVATE;
+    }
+    avc->f.states |= CPageWrite;
+    return 0;
+}
+
+static inline int
+afs_linux_dopartialwrite(struct vcache *avc, cred_t *credp) {
+    struct vrequest treq;
+    int code = 0;
+
+    if (!afs_InitReq(&treq, credp))
+       code = afs_DoPartialWrite(avc, &treq);
+
+    return afs_convert_code(code);
+}
+
+static inline void
+afs_linux_complete_writeback(struct vcache *avc) {
+    avc->f.states &= ~CPageWrite;
+}
+
+/* Writeback a given page syncronously. Called with no AFS locks held */
 static int
-afs_linux_writepage_sync(struct inode *ip, struct page *pp,
-                        unsigned long offset, unsigned int count)
+afs_linux_page_writeback(struct inode *ip, struct page *pp,
+                        unsigned long offset, unsigned int count,
+                        cred_t *credp)
 {
     struct vcache *vcp = VTOAFS(ip);
     char *buffer;
     afs_offs_t base;
     int code = 0;
-    cred_t *credp;
     uio_t tuio;
     struct iovec iovec;
     int f_flags = 0;
@@ -2001,25 +2027,12 @@ afs_linux_writepage_sync(struct inode *ip, struct page *pp,
     buffer = kmap(pp) + offset;
     base = page_offset(pp) + offset;
 
-    credp = crref();
     afs_maybe_lock_kernel();
     AFS_GLOCK();
     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);
 
-    ObtainWriteLock(&vcp->lock, 532);
-    if (vcp->f.states & CPageWrite) {
-       ReleaseWriteLock(&vcp->lock);
-       AFS_GUNLOCK();
-       afs_maybe_unlock_kernel();
-       crfree(credp);
-       kunmap(pp);
-       return AOP_WRITEPAGE_ACTIVATE;
-    }
-    vcp->f.states |= CPageWrite;
-    ReleaseWriteLock(&vcp->lock);
-
     setup_uio(&tuio, &iovec, buffer, base, count, UIO_WRITE, AFS_UIOSYS);
 
     code = afs_write(vcp, &tuio, f_flags, credp, 0);
@@ -2027,30 +2040,61 @@ afs_linux_writepage_sync(struct inode *ip, struct page *pp,
     i_size_write(ip, vcp->f.m.Length);
     ip->i_blocks = ((vcp->f.m.Length + 1023) >> 10) << 1;
 
-    ObtainWriteLock(&vcp->lock, 533);
-    if (!code) {
-       struct vrequest treq;
-
-       if (!afs_InitReq(&treq, credp))
-           code = afs_DoPartialWrite(vcp, &treq);
-    }
     code = code ? afs_convert_code(code) : count - tuio.uio_resid;
 
-    vcp->f.states &= ~CPageWrite;
-    ReleaseWriteLock(&vcp->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, code);
 
     AFS_GUNLOCK();
     afs_maybe_unlock_kernel();
-    crfree(credp);
     kunmap(pp);
 
     return code;
 }
 
+static int
+afs_linux_writepage_sync(struct inode *ip, struct page *pp,
+                        unsigned long offset, unsigned int count)
+{
+    int code;
+    int code1 = 0;
+    struct vcache *vcp = VTOAFS(ip);
+    cred_t *credp;
+
+    /* Catch recursive writeback. This occurs if the kernel decides
+     * writeback is required whilst we are writing to the cache, or
+     * flushing to the server. */
+    AFS_GLOCK();
+    ObtainWriteLock(&vcp->lock, 532);
+    code = afs_linux_prepare_writeback(vcp);
+    if (code) {
+       ReleaseWriteLock(&vcp->lock);
+       AFS_GUNLOCK();
+       return code;
+    }
+    ReleaseWriteLock(&vcp->lock);
+    AFS_GUNLOCK();
+
+    credp = crref();
+    code = afs_linux_page_writeback(ip, pp, offset, count, credp);
+
+    afs_maybe_lock_kernel();
+    AFS_GLOCK();
+    ObtainWriteLock(&vcp->lock, 533);
+    if (code > 0)
+       code1 = afs_linux_dopartialwrite(vcp, credp);
+    afs_linux_complete_writeback(vcp);
+    ReleaseWriteLock(&vcp->lock);
+    AFS_GUNLOCK();
+    afs_maybe_unlock_kernel();
+    crfree(credp);
+
+    if (code1)
+       return code1;
+
+    return code;
+}
 
 static int
 #ifdef AOP_WRITEPAGE_TAKES_WRITEBACK_CONTROL
@@ -2090,7 +2134,7 @@ done:
     SetPageUptodate(pp);
     if ( status != AOP_WRITEPAGE_ACTIVATE ) {
        /* XXX - do we need to redirty the page here? */
-       UnlockPage(pp);
+       unlock_page(pp);
     }
 
     page_cache_release(pp);
@@ -2241,7 +2285,7 @@ afs_symlink_filler(struct file *file, struct page *page)
 
     SetPageUptodate(page);
     kunmap(page);
-    UnlockPage(page);
+    unlock_page(page);
     return 0;
 
   fail:
@@ -2249,7 +2293,7 @@ afs_symlink_filler(struct file *file, struct page *page)
 
     SetPageError(page);
     kunmap(page);
-    UnlockPage(page);
+    unlock_page(page);
     return code;
 }