From: Simon Wilkinson Date: Sat, 24 Oct 2009 09:54:32 +0000 (+0100) Subject: Use user credentials for Linux writepage() X-Git-Tag: openafs-devel-1_5_67~114 X-Git-Url: http://git.openafs.org/?p=openafs.git;a=commitdiff_plain;h=70c8deab1694e2cd200f4ff7a81f5f3f028a4c19 Use user credentials for Linux writepage() 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 Tested-by: Marc Dionne Tested-by: Derrick Brashear Reviewed-by: Derrick Brashear --- diff --git a/src/afs/LINUX/osi_vnodeops.c b/src/afs/LINUX/osi_vnodeops.c index ae26fa2..c851adf 100644 --- a/src/afs/LINUX/osi_vnodeops.c +++ b/src/afs/LINUX/osi_vnodeops.c @@ -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 diff --git a/src/afs/VNOPS/afs_vnop_open.c b/src/afs/VNOPS/afs_vnop_open.c index d850f6e..1d5dd63 100644 --- a/src/afs/VNOPS/afs_vnop_open.c +++ b/src/afs/VNOPS/afs_vnop_open.c @@ -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; diff --git a/src/afs/afs.h b/src/afs/afs.h index 0e1842c..69021d7 100644 --- a/src/afs/afs.h +++ b/src/afs/afs.h @@ -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; diff --git a/src/afs/afs_vcache.c b/src/afs/afs_vcache.c index be467b3..6457918 100644 --- a/src/afs/afs_vcache.c +++ b/src/afs/afs_vcache.c @@ -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