Use user credentials for Linux writepage()
authorSimon Wilkinson <sxw@inf.ed.ac.uk>
Sat, 24 Oct 2009 09:54:32 +0000 (10:54 +0100)
committerDerrick Brashear <shadow|account-1000005@unknown>
Sun, 25 Oct 2009 00:18:55 +0000 (17:18 -0700)
We have no control over the context in which the kernel calls our
writepage routine. It may be from the process which original wrote the
page, from any other process on the system which is writing and goes
over the dirty page threshold, or from the flush thread (pdflush /
flush-afs). Therefore, we cannot use the credentials of the current
process to perform the writeback. This is an issue both for afs_write
(which, in our current MM model, may need to contact the fileserver
to read missing chunks), and for DoPartialWrite (which needs to be
able to store chunks when the local cache is getting full)

This patch stores the credentials of the first process to open a file in
the vcache structure. Whenever writepage() is used to writeback pages
for this file, the cached credentials are used rather than those of the
current context.

Thanks to Marc Dionne for his work in testing and refining this patch.

FIXES 125471
Change-Id: I2900f711150fd81b2b4839bbc2bf77623bde3b64
Reviewed-on: http://gerrit.openafs.org/724
Reviewed-by: Marc Dionne <marc.c.dionne@gmail.com>
Tested-by: Marc Dionne <marc.c.dionne@gmail.com>
Tested-by: Derrick Brashear <shadow@dementia.org>
Reviewed-by: Derrick Brashear <shadow@dementia.org>

src/afs/LINUX/osi_vnodeops.c
src/afs/VNOPS/afs_vnop_open.c
src/afs/afs.h
src/afs/afs_vcache.c

index ae26fa2..c851adf 100644 (file)
@@ -436,6 +436,12 @@ afs_linux_release(struct inode *ip, struct file *fp)
     afs_maybe_lock_kernel();
     AFS_GLOCK();
     code = afs_close(vcp, fp->f_flags, credp);
+    ObtainWriteLock(&vcp->lock, 807);
+    if (vcp->cred) {
+       crfree(vcp->cred);
+       vcp->cred = NULL;
+    }
+    ReleaseWriteLock(&vcp->lock);
     AFS_GUNLOCK();
     afs_maybe_unlock_kernel();
 
@@ -2105,9 +2111,12 @@ afs_linux_writepage(struct page *pp)
 {
     struct address_space *mapping = pp->mapping;
     struct inode *inode;
+    struct vcache *vcp;
+    cred_t *credp;
     unsigned int to = PAGE_CACHE_SIZE;
     loff_t isize;
-    int status = 0;
+    int code = 0;
+    int code1 = 0;
 
     if (PageReclaim(pp)) {
        return AOP_WRITEPAGE_ACTIVATE;
@@ -2116,33 +2125,70 @@ afs_linux_writepage(struct page *pp)
 
     page_cache_get(pp);
 
-    inode = (struct inode *)mapping->host;
+    inode = mapping->host;
+    vcp = VTOAFS(inode);
     isize = i_size_read(inode);
 
     /* Don't defeat an earlier truncate */
     if (page_offset(pp) > isize)
        goto done;
 
+    AFS_GLOCK();
+    ObtainWriteLock(&vcp->lock, 537);
+    code = afs_linux_prepare_writeback(vcp);
+    if (code) {
+       ReleaseWriteLock(&vcp->lock);
+       AFS_GUNLOCK();
+       return code;
+    }
+    /* Grab the creds structure currently held in the vnode, and
+     * get a reference to it, in case it goes away ... */
+    credp = vcp->cred;
+    crhold(credp);
+    ReleaseWriteLock(&vcp->lock);
+    AFS_GUNLOCK();
+
     /* If this is the final page, then just write the number of bytes that
      * are actually in it */
     if ((isize - page_offset(pp)) < to )
        to = isize - page_offset(pp);
 
-    status = afs_linux_writepage_sync(inode, pp, 0, to);
+    code = afs_linux_page_writeback(inode, pp, 0, to, credp);
+
+    afs_maybe_lock_kernel();
+    AFS_GLOCK();
+    ObtainWriteLock(&vcp->lock, 538);
+
+    /* As much as we might like to ignore a file server error here,
+     * and just try again when we close(), unfortunately StoreAllSegments
+     * will invalidate our chunks if the server returns a permanent error,
+     * so we need to at least try and get that error back to the user
+     */
+    if (code == to)
+       code1 = afs_linux_dopartialwrite(vcp, credp);
+
+    afs_linux_complete_writeback(vcp);
+    ReleaseWriteLock(&vcp->lock);
+    crfree(credp);
+    AFS_GUNLOCK();
+    afs_maybe_unlock_kernel();
 
 done:
     SetPageUptodate(pp);
-    if ( status != AOP_WRITEPAGE_ACTIVATE ) {
+    if ( code != AOP_WRITEPAGE_ACTIVATE ) {
        /* XXX - do we need to redirty the page here? */
        unlock_page(pp);
     }
 
     page_cache_release(pp);
 
-    if (status == to)
+    if (code1)
+       return code1;
+
+    if (code == to)
        return 0;
-    else
-       return status;
+
+    return code;
 }
 
 /* afs_linux_permission
index d850f6e..1d5dd63 100644 (file)
@@ -159,7 +159,7 @@ afs_open(struct vcache **avcp, afs_int32 aflags, afs_ucred_t *acred)
     if (writing)
        tvc->execsOrWriters++;
     tvc->opens++;
-#if defined(AFS_SGI_ENV)
+#if defined(AFS_SGI_ENV) || defined (AFS_LINUX26_ENV)
     if (writing && tvc->cred == NULL) {
        crhold(acred);
        tvc->cred = acred;
index 0e1842c..69021d7 100644 (file)
@@ -848,6 +848,9 @@ struct vcache {
     struct bhv_desc vc_bhv_desc;       /* vnode's behavior data. */
 #endif
 #endif                         /* AFS_SGI_ENV */
+#if defined(AFS_LINUX26_ENV)
+    cred_t *cred;              /* last writer's cred */
+#endif
     afs_int32 vc_error;                /* stash write error for this vnode. */
     int xlatordv;              /* Used by nfs xlator */
     afs_ucred_t *uncred;
index be467b3..6457918 100644 (file)
@@ -1016,6 +1016,9 @@ afs_NewVCache(struct VenusFid *afid, struct server *serverp)
     tvc->f.fid = *afid;
     tvc->asynchrony = -1;
     tvc->vc_error = 0;
+#if defined(AFS_LINUX26_ENV)
+    tvc->cred = NULL;
+#endif
 #ifdef AFS_TEXT_ENV
     tvc->flushDV.low = tvc->flushDV.high = AFS_MAXDV;
 #endif