]> err.no Git - linux-2.6/commitdiff
NFS: Add a new ACCESS rpc call cache to the linux nfs client
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Tue, 25 Jul 2006 15:28:18 +0000 (11:28 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Sat, 23 Sep 2006 03:24:28 +0000 (23:24 -0400)
The current access cache only allows one entry at a time to be cached for each
inode. Add a per-inode red-black tree in order to allow more than one to
be cached at a time.

Should significantly cut down the time spent in path traversal for shared
directories such as ${PATH}, /usr/share, etc.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
fs/nfs/dir.c
fs/nfs/inode.c
include/linux/nfs_fs.h

index e7ffb4deb3e5f21973a0e165f229ddc98c7ff754..094afded2b115eff67ad8167476dceebc11949cb 100644 (file)
@@ -1638,35 +1638,134 @@ out:
        return error;
 }
 
-int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, struct nfs_access_entry *res)
+static void nfs_access_free_entry(struct nfs_access_entry *entry)
+{
+       put_rpccred(entry->cred);
+       kfree(entry);
+}
+
+static void __nfs_access_zap_cache(struct inode *inode)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
-       struct nfs_access_entry *cache = &nfsi->cache_access;
+       struct rb_root *root_node = &nfsi->access_cache;
+       struct rb_node *n, *dispose = NULL;
+       struct nfs_access_entry *entry;
+
+       /* Unhook entries from the cache */
+       while ((n = rb_first(root_node)) != NULL) {
+               entry = rb_entry(n, struct nfs_access_entry, rb_node);
+               rb_erase(n, root_node);
+               n->rb_left = dispose;
+               dispose = n;
+       }
+       nfsi->cache_validity &= ~NFS_INO_INVALID_ACCESS;
+       spin_unlock(&inode->i_lock);
 
-       if (cache->cred != cred
-                       || time_after(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))
-                       || (nfsi->cache_validity & NFS_INO_INVALID_ACCESS))
-               return -ENOENT;
-       memcpy(res, cache, sizeof(*res));
-       return 0;
+       /* Now kill them all! */
+       while (dispose != NULL) {
+               n = dispose;
+               dispose = n->rb_left;
+               nfs_access_free_entry(rb_entry(n, struct nfs_access_entry, rb_node));
+       }
 }
 
-void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set)
+void nfs_access_zap_cache(struct inode *inode)
 {
-       struct nfs_inode *nfsi = NFS_I(inode);
-       struct nfs_access_entry *cache = &nfsi->cache_access;
+       spin_lock(&inode->i_lock);
+       /* This will release the spinlock */
+       __nfs_access_zap_cache(inode);
+}
 
-       if (cache->cred != set->cred) {
-               if (cache->cred)
-                       put_rpccred(cache->cred);
-               cache->cred = get_rpccred(set->cred);
+static struct nfs_access_entry *nfs_access_search_rbtree(struct inode *inode, struct rpc_cred *cred)
+{
+       struct rb_node *n = NFS_I(inode)->access_cache.rb_node;
+       struct nfs_access_entry *entry;
+
+       while (n != NULL) {
+               entry = rb_entry(n, struct nfs_access_entry, rb_node);
+
+               if (cred < entry->cred)
+                       n = n->rb_left;
+               else if (cred > entry->cred)
+                       n = n->rb_right;
+               else
+                       return entry;
        }
-       /* FIXME: replace current access_cache BKL reliance with inode->i_lock */
+       return NULL;
+}
+
+int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, struct nfs_access_entry *res)
+{
+       struct nfs_inode *nfsi = NFS_I(inode);
+       struct nfs_access_entry *cache;
+       int err = -ENOENT;
+
        spin_lock(&inode->i_lock);
-       nfsi->cache_validity &= ~NFS_INO_INVALID_ACCESS;
+       if (nfsi->cache_validity & NFS_INO_INVALID_ACCESS)
+               goto out_zap;
+       cache = nfs_access_search_rbtree(inode, cred);
+       if (cache == NULL)
+               goto out;
+       if (time_after(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode)))
+               goto out_stale;
+       res->jiffies = cache->jiffies;
+       res->cred = cache->cred;
+       res->mask = cache->mask;
+       err = 0;
+out:
+       spin_unlock(&inode->i_lock);
+       return err;
+out_stale:
+       rb_erase(&cache->rb_node, &nfsi->access_cache);
+       spin_unlock(&inode->i_lock);
+       nfs_access_free_entry(cache);
+       return -ENOENT;
+out_zap:
+       /* This will release the spinlock */
+       __nfs_access_zap_cache(inode);
+       return -ENOENT;
+}
+
+static void nfs_access_add_rbtree(struct inode *inode, struct nfs_access_entry *set)
+{
+       struct rb_root *root_node = &NFS_I(inode)->access_cache;
+       struct rb_node **p = &root_node->rb_node;
+       struct rb_node *parent = NULL;
+       struct nfs_access_entry *entry;
+
+       spin_lock(&inode->i_lock);
+       while (*p != NULL) {
+               parent = *p;
+               entry = rb_entry(parent, struct nfs_access_entry, rb_node);
+
+               if (set->cred < entry->cred)
+                       p = &parent->rb_left;
+               else if (set->cred > entry->cred)
+                       p = &parent->rb_right;
+               else
+                       goto found;
+       }
+       rb_link_node(&set->rb_node, parent, p);
+       rb_insert_color(&set->rb_node, root_node);
        spin_unlock(&inode->i_lock);
+       return;
+found:
+       rb_replace_node(parent, &set->rb_node, root_node);
+       spin_unlock(&inode->i_lock);
+       nfs_access_free_entry(entry);
+}
+
+void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set)
+{
+       struct nfs_access_entry *cache = kmalloc(sizeof(*cache), GFP_KERNEL);
+       if (cache == NULL)
+               return;
+       RB_CLEAR_NODE(&cache->rb_node);
        cache->jiffies = set->jiffies;
+       cache->cred = get_rpccred(set->cred);
        cache->mask = set->mask;
+
+       nfs_access_add_rbtree(inode, cache);
 }
 
 static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask)
index d349fb2245da4dabe1c44ecd8700e2c90112e8ae..b94ab060bb1ee01ffaba18e26fe322f2dbd6ae01 100644 (file)
@@ -76,19 +76,14 @@ int nfs_write_inode(struct inode *inode, int sync)
 
 void nfs_clear_inode(struct inode *inode)
 {
-       struct nfs_inode *nfsi = NFS_I(inode);
-       struct rpc_cred *cred;
-
        /*
         * The following should never happen...
         */
        BUG_ON(nfs_have_writebacks(inode));
-       BUG_ON (!list_empty(&nfsi->open_files));
+       BUG_ON(!list_empty(&NFS_I(inode)->open_files));
+       BUG_ON(atomic_read(&NFS_I(inode)->data_updates) != 0);
        nfs_zap_acl_cache(inode);
-       cred = nfsi->cache_access.cred;
-       if (cred)
-               put_rpccred(cred);
-       BUG_ON(atomic_read(&nfsi->data_updates) != 0);
+       nfs_access_zap_cache(inode);
 }
 
 /**
@@ -290,7 +285,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
                nfsi->attrtimeo_timestamp = jiffies;
                memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));
-               nfsi->cache_access.cred = NULL;
+               nfsi->access_cache = RB_ROOT;
 
                unlock_new_inode(inode);
        } else
index 6c2066caeaab1338538b5b2b7682497d92423bd8..cc013ed2e52efc9d789f055c7182215634180bb1 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/in.h>
 #include <linux/mm.h>
 #include <linux/pagemap.h>
+#include <linux/rbtree.h>
 #include <linux/rwsem.h>
 #include <linux/wait.h>
 
@@ -69,6 +70,7 @@
  * NFSv3/v4 Access mode cache entry
  */
 struct nfs_access_entry {
+       struct rb_node          rb_node;
        unsigned long           jiffies;
        struct rpc_cred *       cred;
        int                     mask;
@@ -145,7 +147,7 @@ struct nfs_inode {
         */
        atomic_t                data_updates;
 
-       struct nfs_access_entry cache_access;
+       struct rb_root          access_cache;
 #ifdef CONFIG_NFS_V3_ACL
        struct posix_acl        *acl_access;
        struct posix_acl        *acl_default;
@@ -297,6 +299,7 @@ extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
 extern int nfs_permission(struct inode *, int, struct nameidata *);
 extern int nfs_access_get_cached(struct inode *, struct rpc_cred *, struct nfs_access_entry *);
 extern void nfs_access_add_cache(struct inode *, struct nfs_access_entry *);
+extern void nfs_access_zap_cache(struct inode *inode);
 extern int nfs_open(struct inode *, struct file *);
 extern int nfs_release(struct inode *, struct file *);
 extern int nfs_attribute_timeout(struct inode *inode);