Commit 1c3c07e9 authored by Trond Myklebust's avatar Trond Myklebust

NFS: Add a new ACCESS rpc call cache to the linux nfs client

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: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent 3eeab61a
...@@ -1638,35 +1638,134 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1638,35 +1638,134 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
return error; return error;
} }
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 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);
/* 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_zap_cache(struct inode *inode)
{
spin_lock(&inode->i_lock);
/* This will release the spinlock */
__nfs_access_zap_cache(inode);
}
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;
}
return NULL;
}
int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, struct nfs_access_entry *res) 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_inode *nfsi = NFS_I(inode);
struct nfs_access_entry *cache = &nfsi->cache_access; struct nfs_access_entry *cache;
int err = -ENOENT;
if (cache->cred != cred spin_lock(&inode->i_lock);
|| time_after(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode)) if (nfsi->cache_validity & NFS_INO_INVALID_ACCESS)
|| (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; return -ENOENT;
memcpy(res, cache, sizeof(*res));
return 0;
} }
void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set) static void nfs_access_add_rbtree(struct inode *inode, struct nfs_access_entry *set)
{ {
struct nfs_inode *nfsi = NFS_I(inode); struct rb_root *root_node = &NFS_I(inode)->access_cache;
struct nfs_access_entry *cache = &nfsi->cache_access; struct rb_node **p = &root_node->rb_node;
struct rb_node *parent = NULL;
struct nfs_access_entry *entry;
if (cache->cred != set->cred) {
if (cache->cred)
put_rpccred(cache->cred);
cache->cred = get_rpccred(set->cred);
}
/* FIXME: replace current access_cache BKL reliance with inode->i_lock */
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
nfsi->cache_validity &= ~NFS_INO_INVALID_ACCESS; 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); 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->jiffies = set->jiffies;
cache->cred = get_rpccred(set->cred);
cache->mask = set->mask; cache->mask = set->mask;
nfs_access_add_rbtree(inode, cache);
} }
static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask) static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask)
......
...@@ -76,19 +76,14 @@ int nfs_write_inode(struct inode *inode, int sync) ...@@ -76,19 +76,14 @@ int nfs_write_inode(struct inode *inode, int sync)
void nfs_clear_inode(struct inode *inode) void nfs_clear_inode(struct inode *inode)
{ {
struct nfs_inode *nfsi = NFS_I(inode);
struct rpc_cred *cred;
/* /*
* The following should never happen... * The following should never happen...
*/ */
BUG_ON(nfs_have_writebacks(inode)); 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); nfs_zap_acl_cache(inode);
cred = nfsi->cache_access.cred; nfs_access_zap_cache(inode);
if (cred)
put_rpccred(cred);
BUG_ON(atomic_read(&nfsi->data_updates) != 0);
} }
/** /**
...@@ -290,7 +285,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) ...@@ -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 = NFS_MINATTRTIMEO(inode);
nfsi->attrtimeo_timestamp = jiffies; nfsi->attrtimeo_timestamp = jiffies;
memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));
nfsi->cache_access.cred = NULL; nfsi->access_cache = RB_ROOT;
unlock_new_inode(inode); unlock_new_inode(inode);
} else } else
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#include <linux/in.h> #include <linux/in.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/rbtree.h>
#include <linux/rwsem.h> #include <linux/rwsem.h>
#include <linux/wait.h> #include <linux/wait.h>
...@@ -69,6 +70,7 @@ ...@@ -69,6 +70,7 @@
* NFSv3/v4 Access mode cache entry * NFSv3/v4 Access mode cache entry
*/ */
struct nfs_access_entry { struct nfs_access_entry {
struct rb_node rb_node;
unsigned long jiffies; unsigned long jiffies;
struct rpc_cred * cred; struct rpc_cred * cred;
int mask; int mask;
...@@ -145,7 +147,7 @@ struct nfs_inode { ...@@ -145,7 +147,7 @@ struct nfs_inode {
*/ */
atomic_t data_updates; atomic_t data_updates;
struct nfs_access_entry cache_access; struct rb_root access_cache;
#ifdef CONFIG_NFS_V3_ACL #ifdef CONFIG_NFS_V3_ACL
struct posix_acl *acl_access; struct posix_acl *acl_access;
struct posix_acl *acl_default; struct posix_acl *acl_default;
...@@ -297,6 +299,7 @@ extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *); ...@@ -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_permission(struct inode *, int, struct nameidata *);
extern int nfs_access_get_cached(struct inode *, struct rpc_cred *, struct nfs_access_entry *); 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_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_open(struct inode *, struct file *);
extern int nfs_release(struct inode *, struct file *); extern int nfs_release(struct inode *, struct file *);
extern int nfs_attribute_timeout(struct inode *inode); extern int nfs_attribute_timeout(struct inode *inode);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment