Lockless path through afs_linux_dentry_revalidate
[openafs.git] / src / afs / LINUX / osi_vnodeops.c
index 83a5ab0..c9d6709 100644 (file)
@@ -41,7 +41,7 @@
 #include "osi_compat.h"
 #include "osi_pagecopy.h"
 
-#ifndef HAVE_PAGEVEC_LRU_ADD_FILE
+#ifndef HAVE_LINUX_PAGEVEC_LRU_ADD_FILE
 #define __pagevec_lru_add_file __pagevec_lru_add
 #endif
 
 #define MAX_ERRNO 1000L
 #endif
 
-extern struct backing_dev_info afs_backing_dev_info;
+extern struct backing_dev_info *afs_backing_dev_info;
 
 extern struct vcache *afs_globalVp;
 extern int afs_notify_change(struct dentry *dp, struct iattr *iattrp);
-/* Some uses of BKL are perhaps not needed for bypass or memcache--
- * why don't we try it out? */
-extern struct afs_cacheOps afs_UfsCacheOps;
-
-static inline void
-afs_maybe_lock_kernel(void) {
-    if(afs_cacheType == &afs_UfsCacheOps)
-        lock_kernel();
-}
-
-static inline void
-afs_maybe_unlock_kernel(void) {
-    if(afs_cacheType == &afs_UfsCacheOps)
-       unlock_kernel();
-}
 
 /* This function converts a positive error code from AFS into a negative
  * code suitable for passing into the Linux VFS layer. It checks that the
@@ -201,7 +186,7 @@ afs_linux_readdir(struct file *fp, void *dirbuf, filldir_t filldir)
 {
     struct vcache *avc = VTOAFS(FILE_INODE(fp));
     struct vrequest treq;
-    register struct dcache *tdc;
+    struct dcache *tdc;
     int code;
     int offset;
     int dirpos;
@@ -212,7 +197,6 @@ afs_linux_readdir(struct file *fp, void *dirbuf, filldir_t filldir)
     cred_t *credp = crref();
     struct afs_fakestat_state fakestat;
 
-    afs_maybe_lock_kernel();
     AFS_GLOCK();
     AFS_STATCNT(afs_readdir);
 
@@ -364,7 +348,6 @@ out:
     afs_PutFakeStat(&fakestat);
 out1:
     AFS_GUNLOCK();
-    afs_maybe_unlock_kernel();
     return code;
 }
 
@@ -396,16 +379,18 @@ afs_linux_mmap(struct file *fp, struct vm_area_struct *vmap)
     /* get a validated vcache entry */
     code = afs_linux_VerifyVCache(vcp, NULL);
 
-    /* Linux's Flushpage implementation doesn't use credp, so optimise
-     * our code to not need to crref() it */
-    osi_FlushPages(vcp, NULL); /* ensure stale pages are gone */
+    if (code == 0) {
+        /* Linux's Flushpage implementation doesn't use credp, so optimise
+         * our code to not need to crref() it */
+        osi_FlushPages(vcp, NULL); /* ensure stale pages are gone */
+        AFS_GUNLOCK();
+        code = generic_file_mmap(fp, vmap);
+        AFS_GLOCK();
+        if (!code)
+            vcp->f.states |= CMAPPED;
+    }
     AFS_GUNLOCK();
-    code = generic_file_mmap(fp, vmap);
-    AFS_GLOCK();
-    if (!code)
-       vcp->f.states |= CMAPPED;
 
-    AFS_GUNLOCK();
     return code;
 }
 
@@ -416,11 +401,9 @@ afs_linux_open(struct inode *ip, struct file *fp)
     cred_t *credp = crref();
     int code;
 
-    afs_maybe_lock_kernel();
     AFS_GLOCK();
     code = afs_open(&vcp, fp->f_flags, credp);
     AFS_GUNLOCK();
-    afs_maybe_unlock_kernel();
 
     crfree(credp);
     return afs_convert_code(code);
@@ -433,7 +416,6 @@ afs_linux_release(struct inode *ip, struct file *fp)
     cred_t *credp = crref();
     int code = 0;
 
-    afs_maybe_lock_kernel();
     AFS_GLOCK();
     code = afs_close(vcp, fp->f_flags, credp);
     ObtainWriteLock(&vcp->lock, 807);
@@ -443,24 +425,25 @@ afs_linux_release(struct inode *ip, struct file *fp)
     }
     ReleaseWriteLock(&vcp->lock);
     AFS_GUNLOCK();
-    afs_maybe_unlock_kernel();
 
     crfree(credp);
     return afs_convert_code(code);
 }
 
 static int
+#if defined(FOP_FSYNC_TAKES_DENTRY)
 afs_linux_fsync(struct file *fp, struct dentry *dp, int datasync)
+#else
+afs_linux_fsync(struct file *fp, int datasync)
+#endif
 {
     int code;
     struct inode *ip = FILE_INODE(fp);
     cred_t *credp = crref();
 
-    afs_maybe_lock_kernel();
     AFS_GLOCK();
     code = afs_fsync(VTOAFS(ip), credp);
     AFS_GUNLOCK();
-    afs_maybe_unlock_kernel();
     crfree(credp);
     return afs_convert_code(code);
 
@@ -481,7 +464,10 @@ afs_linux_lock(struct file *fp, int cmd, struct file_lock *flp)
     flock.l_pid = flp->fl_pid;
     flock.l_whence = 0;
     flock.l_start = flp->fl_start;
-    flock.l_len = flp->fl_end - flp->fl_start + 1;
+    if (flp->fl_end == OFFSET_MAX)
+       flock.l_len = 0; /* Lock to end of file */
+    else
+       flock.l_len = flp->fl_end - flp->fl_start + 1;
 
     /* Safe because there are no large files, yet */
 #if defined(F_GETLK64) && (F_GETLK != F_GETLK64)
@@ -494,7 +480,7 @@ afs_linux_lock(struct file *fp, int cmd, struct file_lock *flp)
 #endif /* F_GETLK64 && F_GETLK != F_GETLK64 */
 
     AFS_GLOCK();
-    code = afs_lockctl(vcp, &flock, cmd, credp);
+    code = afs_convert_code(afs_lockctl(vcp, &flock, cmd, credp));
     AFS_GUNLOCK();
 
     if ((code == 0 || flp->fl_type == F_UNLCK) && 
@@ -525,10 +511,13 @@ afs_linux_lock(struct file *fp, int cmd, struct file_lock *flp)
     flp->fl_type = flock.l_type;
     flp->fl_pid = flock.l_pid;
     flp->fl_start = flock.l_start;
-    flp->fl_end = flock.l_start + flock.l_len - 1;
+    if (flock.l_len == 0)
+       flp->fl_end = OFFSET_MAX; /* Lock to end of file */
+    else
+       flp->fl_end = flock.l_start + flock.l_len - 1;
 
     crfree(credp);
-    return afs_convert_code(code);
+    return code;
 }
 
 #ifdef STRUCT_FILE_OPERATIONS_HAS_FLOCK
@@ -544,7 +533,7 @@ afs_linux_flock(struct file *fp, int cmd, struct file_lock *flp) {
     flock.l_pid = flp->fl_pid;
     flock.l_whence = 0;
     flock.l_start = 0;
-    flock.l_len = OFFSET_MAX;
+    flock.l_len = 0;
 
     /* Safe because there are no large files, yet */
 #if defined(F_GETLK64) && (F_GETLK != F_GETLK64)
@@ -557,7 +546,7 @@ afs_linux_flock(struct file *fp, int cmd, struct file_lock *flp) {
 #endif /* F_GETLK64 && F_GETLK != F_GETLK64 */
 
     AFS_GLOCK();
-    code = afs_lockctl(vcp, &flock, cmd, credp);
+    code = afs_convert_code(afs_lockctl(vcp, &flock, cmd, credp));
     AFS_GUNLOCK();
 
     if ((code == 0 || flp->fl_type == F_UNLCK) && 
@@ -578,7 +567,7 @@ afs_linux_flock(struct file *fp, int cmd, struct file_lock *flp) {
     flp->fl_pid = flock.l_pid;
 
     crfree(credp);
-    return afs_convert_code(code);
+    return code;
 }
 #endif
 
@@ -599,7 +588,7 @@ afs_linux_flush(struct file *fp)
     cred_t *credp;
     int code;
 #if defined(AFS_CACHE_BYPASS)
-    int bypasscache;
+    int bypasscache = 0;
 #endif
 
     AFS_GLOCK();
@@ -618,19 +607,20 @@ afs_linux_flush(struct file *fp)
     if (code)
        goto out;
 #if defined(AFS_CACHE_BYPASS)
-       /* If caching is bypassed for this file, or globally, just return 0 */
-       if(cache_bypass_strategy == ALWAYS_BYPASS_CACHE)
-               bypasscache = 1;
-       else {
-               ObtainReadLock(&vcp->lock);
-               if(vcp->cachingStates & FCSBypass)
-                       bypasscache = 1;
-               ReleaseReadLock(&vcp->lock);
-       }
-       if(bypasscache) {
-            /* future proof: don't rely on 0 return from afs_InitReq */
-            code = 0; goto out;
-        }
+    /* If caching is bypassed for this file, or globally, just return 0 */
+    if (cache_bypass_strategy == ALWAYS_BYPASS_CACHE)
+       bypasscache = 1;
+    else {
+       ObtainReadLock(&vcp->lock);
+       if (vcp->cachingStates & FCSBypass)
+           bypasscache = 1;
+       ReleaseReadLock(&vcp->lock);
+    }
+    if (bypasscache) {
+       /* future proof: don't rely on 0 return from afs_InitReq */
+       code = 0;
+       goto out;
+    }
 #endif
 
     ObtainSharedLock(&vcp->lock, 535);
@@ -674,7 +664,7 @@ struct file_operations afs_dir_fops = {
 struct file_operations afs_file_fops = {
   .read =      afs_linux_read,
   .write =     afs_linux_write,
-#ifdef GENERIC_FILE_AIO_READ
+#ifdef HAVE_LINUX_GENERIC_FILE_AIO_READ
   .aio_read =  generic_file_aio_read,
   .aio_write = generic_file_aio_write,
 #endif
@@ -709,13 +699,15 @@ struct file_operations afs_file_fops = {
  * AFS Linux dentry operations
  **********************************************************************/
 
-/* check_bad_parent() : Checks if this dentry's vcache is a root vcache
+/* fix_bad_parent() : called if this dentry's vcache is a root vcache
  * that has its mvid (parent dir's fid) pointer set to the wrong directory
- * due to being mounted in multiple points at once. If so, check_bad_parent()
+ * due to being mounted in multiple points at once. fix_bad_parent()
  * calls afs_lookup() to correct the vcache's mvid, as well as the volume's
  * dotdotfid and mtpoint fid members.
  * Parameters:
  *   dp - dentry to be checked.
+ *   credp - credentials
+ *   vcp, pvc - item's and parent's vcache pointer
  * Return Values:
  *   None.
  * Sideeffects:
@@ -726,27 +718,20 @@ struct file_operations afs_file_fops = {
  */
 
 static inline void
-check_bad_parent(struct dentry *dp)
+fix_bad_parent(struct dentry *dp, cred_t *credp, struct vcache *vcp, struct vcache *pvc) 
 {
-    cred_t *credp;
-    struct vcache *vcp = VTOAFS(dp->d_inode), *avc = NULL;
-    struct vcache *pvc = VTOAFS(dp->d_parent->d_inode);
-
-    if (vcp->mvid->Fid.Volume != pvc->f.fid.Fid.Volume) {      /* bad parent */
-       credp = crref();
-
-       /* force a lookup, so vcp->mvid is fixed up */
-       afs_lookup(pvc, (char *)dp->d_name.name, &avc, credp);
-       if (!avc || vcp != avc) {       /* bad, very bad.. */
-           afs_Trace4(afs_iclSetp, CM_TRACE_TMP_1S3L, ICL_TYPE_STRING,
-                      "check_bad_parent: bad pointer returned from afs_lookup origvc newvc dentry",
-                      ICL_TYPE_POINTER, vcp, ICL_TYPE_POINTER, avc,
-                      ICL_TYPE_POINTER, dp);
-       }
-       if (avc)
-           AFS_RELE(AFSTOV(avc));
-       crfree(credp);
+    struct vcache *avc = NULL;
+
+    /* force a lookup, so vcp->mvid is fixed up */
+    afs_lookup(pvc, (char *)dp->d_name.name, &avc, credp);
+    if (!avc || vcp != avc) {  /* bad, very bad.. */
+       afs_Trace4(afs_iclSetp, CM_TRACE_TMP_1S3L, ICL_TYPE_STRING,
+                  "check_bad_parent: bad pointer returned from afs_lookup origvc newvc dentry",
+                  ICL_TYPE_POINTER, vcp, ICL_TYPE_POINTER, avc,
+                  ICL_TYPE_POINTER, dp);
     }
+    if (avc)
+       AFS_RELE(AFSTOV(avc));
 
     return;
 }
@@ -765,18 +750,22 @@ afs_linux_revalidate(struct dentry *dp)
     if (afs_shuttingdown)
        return EIO;
 
-    afs_maybe_lock_kernel();
     AFS_GLOCK();
 
 #ifdef notyet
     /* Make this a fast path (no crref), since it's called so often. */
-    if (vcp->f.states & CStatd) {
-
-       if (*dp->d_name.name != '/' && vcp->mvstat == 2)        /* root vnode */
-           check_bad_parent(dp);       /* check and correct mvid */
+    if (vcp->states & CStatd) {
+       struct vcache *pvc = VTOAFS(dp->d_parent->d_inode);
 
-       AFS_GUNLOCK();
-       unlock_kernel();
+       if (*dp->d_name.name != '/' && vcp->mvstat == 2) {      /* root vnode */
+           if (vcp->mvid->Fid.Volume != pvc->fid.Fid.Volume) { /* bad parent */
+               credp = crref();
+               AFS_GLOCK();
+               fix_bad_parent(dp);     /* check and correct mvid */
+               AFS_GUNLOCK();
+               crfree(credp);
+           }
+       }
        return 0;
     }
 #endif
@@ -794,11 +783,11 @@ afs_linux_revalidate(struct dentry *dp)
        code = afs_getattr(vcp, &vattr, credp);
        crfree(credp);
     }
+
     if (!code)
         afs_fill_inode(AFSTOV(vcp), &vattr);
 
     AFS_GUNLOCK();
-    afs_maybe_unlock_kernel();
 
     return afs_convert_code(code);
 }
@@ -819,6 +808,8 @@ afs_linux_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *sta
  * we are advised to follow the entry if it is a link or to make sure that 
  * it is a directory. But since the kernel itself checks these possibilities
  * later on, we shouldn't have to do it until later. Perhaps in the future..
+ *
+ * The code here assumes that on entry the global lock is not held
  */
 static int
 #ifdef DOP_REVALIDATE_TAKES_NAMEIDATA
@@ -830,28 +821,33 @@ afs_linux_dentry_revalidate(struct dentry *dp, int flags)
     struct vattr vattr;
     cred_t *credp = NULL;
     struct vcache *vcp, *pvcp, *tvc = NULL;
+    struct dentry *parent;
     int valid;
     struct afs_fakestat_state fakestate;
+    int locked = 0;
 
-    afs_maybe_lock_kernel();
-    AFS_GLOCK();
     afs_InitFakeStat(&fakestate);
 
     if (dp->d_inode) {
-
+       parent = dget_parent(dp);
+       pvcp = VTOAFS(parent->d_inode);
        vcp = VTOAFS(dp->d_inode);
-       pvcp = VTOAFS(dp->d_parent->d_inode);           /* dget_parent()? */
 
        if (vcp == afs_globalVp)
            goto good_dentry;
 
-       if (vcp->mvstat == 1) {         /* mount point */
+       if ((vcp->mvstat == 1) || (vcp->mvstat == 2)) { /* need to lock */
+           credp = crref();
+           AFS_GLOCK();
+           locked = 1;
+       }
+
+       if (locked && vcp->mvstat == 1) {         /* mount point */
            if (vcp->mvid && (vcp->f.states & CMValid)) {
                int tryEvalOnly = 0;
                int code = 0;
                struct vrequest treq;
 
-               credp = crref();
                code = afs_InitReq(&treq, credp);
                if (
                    (strcmp(dp->d_name.name, ".directory") == 0)) {
@@ -867,8 +863,11 @@ afs_linux_dentry_revalidate(struct dentry *dp, int flags)
                }
            }
        } else
-           if (*dp->d_name.name != '/' && vcp->mvstat == 2) /* root vnode */
-               check_bad_parent(dp);   /* check and correct mvid */
+           if (locked && *dp->d_name.name != '/' && vcp->mvstat == 2) {        /* root vnode */
+               if (vcp->mvid->Fid.Volume != pvcp->f.fid.Fid.Volume) {  /* bad parent */
+                   fix_bad_parent(dp, credp, vcp, pvcp);       /* check and correct mvid */
+               }
+           }
 
 #ifdef notdef
        /* If the last looker changes, we should make sure the current
@@ -883,20 +882,29 @@ afs_linux_dentry_revalidate(struct dentry *dp, int flags)
        }
 #endif
 
+
        /* If the parent's DataVersion has changed or the vnode
         * is longer valid, we need to do a full lookup.  VerifyVCache
         * isn't enough since the vnode may have been renamed.
         */
 
-       if (hgetlo(pvcp->f.m.DataVersion) > dp->d_time || !(vcp->f.states & CStatd)) {
-
+       if ((!locked) && (hgetlo(pvcp->f.m.DataVersion) > dp->d_time || !(vcp->f.states & CStatd)) ) {
            credp = crref();
+           AFS_GLOCK();
+           locked = 1;
+       }
+
+       if (locked && (hgetlo(pvcp->f.m.DataVersion) > dp->d_time || !(vcp->f.states & CStatd))) {
            afs_lookup(pvcp, (char *)dp->d_name.name, &tvc, credp);
-           if (!tvc || tvc != vcp)
+           if (!tvc || tvc != vcp) {
+               dput(parent);
                goto bad_dentry;
+           }
 
-           if (afs_getattr(vcp, &vattr, credp))
+           if (afs_getattr(vcp, &vattr, credp)) {
+               dput(parent);
                goto bad_dentry;
+           }
 
            vattr2inode(AFSTOV(vcp), &vattr);
            dp->d_time = hgetlo(pvcp->f.m.DataVersion);
@@ -905,9 +913,13 @@ afs_linux_dentry_revalidate(struct dentry *dp, int flags)
        /* should we always update the attributes at this point? */
        /* unlikely--the vcache entry hasn't changed */
 
+       dput(parent);
     } else {
 #ifdef notyet
-       pvcp = VTOAFS(dp->d_parent->d_inode);           /* dget_parent()? */
+       /* If this code is ever enabled, we should use dget_parent to handle
+        * getting the parent, and dput() to dispose of it. See above for an
+        * example ... */
+       pvcp = VTOAFS(dp->d_parent->d_inode);
        if (hgetlo(pvcp->f.m.DataVersion) > dp->d_time)
            goto bad_dentry;
 #endif
@@ -928,8 +940,11 @@ afs_linux_dentry_revalidate(struct dentry *dp, int flags)
     /* Clean up */
     if (tvc)
        afs_PutVCache(tvc);
-    afs_PutFakeStat(&fakestate);
-    AFS_GUNLOCK();
+    afs_PutFakeStat(&fakestate);       /* from here on vcp may be no longer valid */
+    if (locked) {
+       /* we hold the global lock if we evaluated a mount point */
+       AFS_GUNLOCK();
+    }
     if (credp)
        crfree(credp);
 
@@ -937,7 +952,6 @@ afs_linux_dentry_revalidate(struct dentry *dp, int flags)
        shrink_dcache_parent(dp);
        d_drop(dp);
     }
-    afs_maybe_unlock_kernel();
     return valid;
 
   bad_dentry:
@@ -1008,7 +1022,6 @@ afs_linux_create(struct inode *dip, struct dentry *dp, int mode)
     vattr.va_mode = mode;
     vattr.va_type = mode & S_IFMT;
 
-    afs_maybe_lock_kernel();
     AFS_GLOCK();
     code = afs_create(VTOAFS(dip), (char *)name, &vattr, NONEXCL, mode,
                      &vcp, credp);
@@ -1025,7 +1038,6 @@ afs_linux_create(struct inode *dip, struct dentry *dp, int mode)
     }
     AFS_GUNLOCK();
 
-    afs_maybe_unlock_kernel();
     crfree(credp);
     return afs_convert_code(code);
 }
@@ -1046,7 +1058,6 @@ afs_linux_lookup(struct inode *dip, struct dentry *dp)
     struct dentry *newdp = NULL;
     int code;
 
-    afs_maybe_lock_kernel();
     AFS_GLOCK();
     code = afs_lookup(VTOAFS(dip), (char *)comp, &vcp, credp);
     
@@ -1074,7 +1085,6 @@ afs_linux_lookup(struct inode *dip, struct dentry *dp)
                dput(alias);
            } else {
                iput(ip);
-               afs_maybe_unlock_kernel();
                crfree(credp);
                return alias;
            }
@@ -1082,7 +1092,6 @@ afs_linux_lookup(struct inode *dip, struct dentry *dp)
     }
     newdp = d_splice_alias(ip, dp);
 
-    afs_maybe_unlock_kernel();
     crfree(credp);
 
     /* It's ok for the file to not be found. That's noted by the caller by
@@ -1186,8 +1195,6 @@ afs_linux_unlink(struct inode *dip, struct dentry *dp)
     const char *name = dp->d_name.name;
     struct vcache *tvc = VTOAFS(dp->d_inode);
 
-    afs_maybe_lock_kernel();
-
     if (VREFCOUNT(tvc) > 1 && tvc->opens > 0
                                && !(tvc->f.states & CUnlinked)) {
 
@@ -1200,7 +1207,6 @@ afs_linux_unlink(struct inode *dip, struct dentry *dp)
            d_drop(dp);
     }
 
-    afs_maybe_unlock_kernel();
     crfree(credp);
     return afs_convert_code(code);
 }
@@ -1236,7 +1242,6 @@ afs_linux_mkdir(struct inode *dip, struct dentry *dp, int mode)
     struct vattr vattr;
     const char *name = dp->d_name.name;
 
-    afs_maybe_lock_kernel();
     VATTR_NULL(&vattr);
     vattr.va_mask = ATTR_MODE;
     vattr.va_mode = mode;
@@ -1255,7 +1260,6 @@ afs_linux_mkdir(struct inode *dip, struct dentry *dp, int mode)
     }
     AFS_GUNLOCK();
 
-    afs_maybe_unlock_kernel();
     crfree(credp);
     return afs_convert_code(code);
 }
@@ -1301,7 +1305,6 @@ afs_linux_rename(struct inode *oldip, struct dentry *olddp,
     struct dentry *rehash = NULL;
 
     /* Prevent any new references during rename operation. */
-    afs_maybe_lock_kernel();
 
     if (!d_unhashed(newdp)) {
        d_drop(newdp);
@@ -1321,8 +1324,6 @@ afs_linux_rename(struct inode *oldip, struct dentry *olddp,
     if (rehash)
        d_rehash(rehash);
 
-    afs_maybe_unlock_kernel();
-
     crfree(credp);
     return afs_convert_code(code);
 }
@@ -1638,7 +1639,6 @@ afs_linux_fillpage(struct file *fp, struct page *pp)
     setup_uio(auio, iovecp, (char *)address, offset, PAGE_SIZE, UIO_READ,
               AFS_UIOSYS);
 
-    afs_maybe_lock_kernel();
     AFS_GLOCK();
     AFS_DISCON_LOCK();
     afs_Trace4(afs_iclSetp, CM_TRACE_READPAGE, ICL_TYPE_POINTER, ip,
@@ -1652,7 +1652,6 @@ afs_linux_fillpage(struct file *fp, struct page *pp)
               code);
     AFS_DISCON_UNLOCK();
     AFS_GUNLOCK();
-    afs_maybe_unlock_kernel();
     if (!code) {
        /* XXX valid for no-cache also?  Check last bits of files... :)
         * Cognate code goes in afs_NoCacheFetchProc.  */
@@ -1772,7 +1771,8 @@ afs_linux_bypass_readpages(struct file *fp, struct address_space *mapping,
             page_cache_release(pp);
            iovecp[page_ix].iov_base = (void *) 0;
            base_index++;
-            continue;
+           ancr->length -= PAGE_SIZE;
+           continue;
         }
         base_index++;
         if(code) {
@@ -1786,19 +1786,27 @@ afs_linux_bypass_readpages(struct file *fp, struct address_space *mapping,
                lock_page(pp);
            }
 
+            /* increment page refcount--our original design assumed
+             * that locking it would effectively pin it;  protect
+             * ourselves from the possiblity that this assumption is
+             * is faulty, at low cost (provided we do not fail to
+             * do the corresponding decref on the other side) */
+            get_page(pp);
+
            /* save the page for background map */
             iovecp[page_ix].iov_base = (void*) pp;
 
            /* and put it on the LRU cache */
            if (!pagevec_add(&lrupv, pp))
-               __pagevec_lru_add(&lrupv);
+               __pagevec_lru_add_file(&lrupv);
         }
     }
 
     /* If there were useful pages in the page list, make sure all pages
      * are in the LRU cache, then schedule the read */
     if(page_count) {
-        pagevec_lru_add(&lrupv);
+       if (pagevec_count(&lrupv))
+           __pagevec_lru_add_file(&lrupv);
        credp = crref();
         code = afs_ReadNoCache(avc, ancr, credp);
        crfree(credp);
@@ -1825,6 +1833,17 @@ afs_linux_bypass_readpage(struct file *fp, struct page *pp)
     struct nocache_read_request *ancr;
     int code;
 
+    /*
+     * Special case: if page is at or past end of file, just zero it and set
+     * it as up to date.
+     */
+    if (page_offset(pp) >=  i_size_read(fp->f_mapping->host)) {
+       zero_user_segment(pp, 0, PAGE_CACHE_SIZE);
+       SetPageUptodate(pp);
+       unlock_page(pp);
+       return 0;
+    }
+
     ClearPageError(pp);
 
     /* receiver frees */
@@ -1836,7 +1855,7 @@ afs_linux_bypass_readpage(struct file *fp, struct page *pp)
              PAGE_SIZE, UIO_READ, AFS_UIOSYS);
 
     /* save the page for background map */
-    /* XXX - Shouldn't we get a reference count here? */
+    get_page(pp); /* see above */
     auio->uio_iov->iov_base = (void*) pp;
     /* the background thread will free this */
     ancr = osi_Alloc(sizeof(struct nocache_read_request));
@@ -1845,9 +1864,7 @@ afs_linux_bypass_readpage(struct file *fp, struct page *pp)
     ancr->length = PAGE_SIZE;
 
     credp = crref();
-    afs_maybe_lock_kernel();
     code = afs_ReadNoCache(VTOAFS(FILE_INODE(fp)), ancr, credp);
-    afs_maybe_unlock_kernel();
     crfree(credp);
 
     return afs_convert_code(code);
@@ -1939,6 +1956,9 @@ afs_linux_readpages(struct file *fp, struct address_space *mapping,
     if (afs_linux_bypass_check(inode))
        return afs_linux_bypass_readpages(fp, mapping, page_list, num_pages);
 
+    if (cacheDiskType == AFS_FCACHE_TYPE_MEM)
+       return 0;
+
     AFS_GLOCK();
     if ((code = afs_linux_VerifyVCache(avc, NULL))) {
        AFS_GUNLOCK();
@@ -2056,7 +2076,6 @@ afs_linux_page_writeback(struct inode *ip, struct page *pp,
     buffer = kmap(pp) + offset;
     base = page_offset(pp) + offset;
 
-    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),
@@ -2076,7 +2095,6 @@ afs_linux_page_writeback(struct inode *ip, struct page *pp,
               ICL_TYPE_INT32, code);
 
     AFS_GUNLOCK();
-    afs_maybe_unlock_kernel();
     kunmap(pp);
 
     return code;
@@ -2106,7 +2124,6 @@ afs_linux_writepage_sync(struct inode *ip, struct page *pp,
     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)
@@ -2114,7 +2131,6 @@ afs_linux_writepage_sync(struct inode *ip, struct page *pp,
     afs_linux_complete_writeback(vcp);
     ReleaseWriteLock(&vcp->lock);
     AFS_GUNLOCK();
-    afs_maybe_unlock_kernel();
     crfree(credp);
 
     if (code1)
@@ -2171,7 +2187,10 @@ afs_linux_writepage(struct page *pp)
     /* 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);
+    if (credp)
+       crhold(credp);
+    else
+       credp = crref();
     ReleaseWriteLock(&vcp->lock);
     AFS_GUNLOCK();
 
@@ -2192,7 +2211,6 @@ afs_linux_writepage(struct page *pp)
 
     code = afs_linux_page_writeback(inode, pp, 0, to, credp);
 
-    afs_maybe_lock_kernel();
     AFS_GLOCK();
     ObtainWriteLock(&vcp->lock, 538);
 
@@ -2208,7 +2226,6 @@ afs_linux_writepage(struct page *pp)
     ReleaseWriteLock(&vcp->lock);
     crfree(credp);
     AFS_GUNLOCK();
-    afs_maybe_unlock_kernel();
 
 done:
     end_page_writeback(pp);
@@ -2306,7 +2323,7 @@ afs_linux_prepare_write(struct file *file, struct page *page, unsigned from,
     return 0;
 }
 
-#if defined(HAVE_WRITE_BEGIN)
+#if defined(STRUCT_ADDRESS_SPACE_OPERATIONS_HAS_WRITE_BEGIN)
 static int
 afs_linux_write_end(struct file *file, struct address_space *mapping,
                                 loff_t pos, unsigned len, unsigned copied,
@@ -2356,7 +2373,7 @@ static struct address_space_operations afs_file_aops = {
   .readpage =          afs_linux_readpage,
   .readpages =                 afs_linux_readpages,
   .writepage =         afs_linux_writepage,
-#if defined (HAVE_WRITE_BEGIN)
+#if defined (STRUCT_ADDRESS_SPACE_OPERATIONS_HAS_WRITE_BEGIN)
   .write_begin =        afs_linux_write_begin,
   .write_end =          afs_linux_write_end,
 #else
@@ -2395,7 +2412,6 @@ afs_symlink_filler(struct file *file, struct page *page)
     char *p = (char *)kmap(page);
     int code;
 
-    afs_maybe_lock_kernel();
     AFS_GLOCK();
     code = afs_linux_ireadlink(ip, p, PAGE_SIZE, AFS_UIOSYS);
     AFS_GUNLOCK();
@@ -2403,7 +2419,6 @@ afs_symlink_filler(struct file *file, struct page *page)
     if (code < 0)
        goto fail;
     p[code] = '\0';            /* null terminate? */
-    afs_maybe_unlock_kernel();
 
     SetPageUptodate(page);
     kunmap(page);
@@ -2411,8 +2426,6 @@ afs_symlink_filler(struct file *file, struct page *page)
     return 0;
 
   fail:
-    afs_maybe_unlock_kernel();
-
     SetPageError(page);
     kunmap(page);
     unlock_page(page);
@@ -2427,7 +2440,7 @@ static struct address_space_operations afs_symlink_aops = {
 static struct inode_operations afs_symlink_iops = {
 #if defined(USABLE_KERNEL_PAGE_SYMLINK_CACHE)
   .readlink =          page_readlink,
-# if defined(HAVE_KERNEL_PAGE_FOLLOW_LINK)
+# if defined(HAVE_LINUX_PAGE_FOLLOW_LINK)
   .follow_link =       page_follow_link,
 # else
   .follow_link =       page_follow_link_light,
@@ -2447,7 +2460,7 @@ afs_fill_inode(struct inode *ip, struct vattr *vattr)
     if (vattr)
        vattr2inode(ip, vattr);
 
-    ip->i_mapping->backing_dev_info = &afs_backing_dev_info;
+    ip->i_mapping->backing_dev_info = afs_backing_dev_info;
 /* Reset ops if symlink or directory. */
     if (S_ISREG(ip->i_mode)) {
        ip->i_op = &afs_file_iops;