diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-07-25 11:28:18 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-09-22 23:24:28 -0400 |
commit | 1c3c07e9f6cc50dab2aeb8051325e317d4f6c70e (patch) | |
tree | 96bbc2a304e9a0e831daf589d1662c6129303796 | |
parent | 3eeab61aa3ddd3c0bedb7449ada1599de22fdb5a (diff) |
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: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | fs/nfs/dir.c | 133 | ||||
-rw-r--r-- | fs/nfs/inode.c | 13 | ||||
-rw-r--r-- | include/linux/nfs_fs.h | 5 |
3 files changed, 124 insertions, 27 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index e7ffb4deb3e5..094afded2b11 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -1638,35 +1638,134 @@ out: | |||
1638 | return error; | 1638 | return error; |
1639 | } | 1639 | } |
1640 | 1640 | ||
1641 | int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, struct nfs_access_entry *res) | 1641 | static void nfs_access_free_entry(struct nfs_access_entry *entry) |
1642 | { | ||
1643 | put_rpccred(entry->cred); | ||
1644 | kfree(entry); | ||
1645 | } | ||
1646 | |||
1647 | static void __nfs_access_zap_cache(struct inode *inode) | ||
1642 | { | 1648 | { |
1643 | struct nfs_inode *nfsi = NFS_I(inode); | 1649 | struct nfs_inode *nfsi = NFS_I(inode); |
1644 | struct nfs_access_entry *cache = &nfsi->cache_access; | 1650 | struct rb_root *root_node = &nfsi->access_cache; |
1651 | struct rb_node *n, *dispose = NULL; | ||
1652 | struct nfs_access_entry *entry; | ||
1653 | |||
1654 | /* Unhook entries from the cache */ | ||
1655 | while ((n = rb_first(root_node)) != NULL) { | ||
1656 | entry = rb_entry(n, struct nfs_access_entry, rb_node); | ||
1657 | rb_erase(n, root_node); | ||
1658 | n->rb_left = dispose; | ||
1659 | dispose = n; | ||
1660 | } | ||
1661 | nfsi->cache_validity &= ~NFS_INO_INVALID_ACCESS; | ||
1662 | spin_unlock(&inode->i_lock); | ||
1645 | 1663 | ||
1646 | if (cache->cred != cred | 1664 | /* Now kill them all! */ |
1647 | || time_after(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode)) | 1665 | while (dispose != NULL) { |
1648 | || (nfsi->cache_validity & NFS_INO_INVALID_ACCESS)) | 1666 | n = dispose; |
1649 | return -ENOENT; | 1667 | dispose = n->rb_left; |
1650 | memcpy(res, cache, sizeof(*res)); | 1668 | nfs_access_free_entry(rb_entry(n, struct nfs_access_entry, rb_node)); |
1651 | return 0; | 1669 | } |
1652 | } | 1670 | } |
1653 | 1671 | ||
1654 | void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set) | 1672 | void nfs_access_zap_cache(struct inode *inode) |
1655 | { | 1673 | { |
1656 | struct nfs_inode *nfsi = NFS_I(inode); | 1674 | spin_lock(&inode->i_lock); |
1657 | struct nfs_access_entry *cache = &nfsi->cache_access; | 1675 | /* This will release the spinlock */ |
1676 | __nfs_access_zap_cache(inode); | ||
1677 | } | ||
1658 | 1678 | ||
1659 | if (cache->cred != set->cred) { | 1679 | static struct nfs_access_entry *nfs_access_search_rbtree(struct inode *inode, struct rpc_cred *cred) |
1660 | if (cache->cred) | 1680 | { |
1661 | put_rpccred(cache->cred); | 1681 | struct rb_node *n = NFS_I(inode)->access_cache.rb_node; |
1662 | cache->cred = get_rpccred(set->cred); | 1682 | struct nfs_access_entry *entry; |
1683 | |||
1684 | while (n != NULL) { | ||
1685 | entry = rb_entry(n, struct nfs_access_entry, rb_node); | ||
1686 | |||
1687 | if (cred < entry->cred) | ||
1688 | n = n->rb_left; | ||
1689 | else if (cred > entry->cred) | ||
1690 | n = n->rb_right; | ||
1691 | else | ||
1692 | return entry; | ||
1663 | } | 1693 | } |
1664 | /* FIXME: replace current access_cache BKL reliance with inode->i_lock */ | 1694 | return NULL; |
1695 | } | ||
1696 | |||
1697 | int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, struct nfs_access_entry *res) | ||
1698 | { | ||
1699 | struct nfs_inode *nfsi = NFS_I(inode); | ||
1700 | struct nfs_access_entry *cache; | ||
1701 | int err = -ENOENT; | ||
1702 | |||
1665 | spin_lock(&inode->i_lock); | 1703 | spin_lock(&inode->i_lock); |
1666 | nfsi->cache_validity &= ~NFS_INO_INVALID_ACCESS; | 1704 | if (nfsi->cache_validity & NFS_INO_INVALID_ACCESS) |
1705 | goto out_zap; | ||
1706 | cache = nfs_access_search_rbtree(inode, cred); | ||
1707 | if (cache == NULL) | ||
1708 | goto out; | ||
1709 | if (time_after(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))) | ||
1710 | goto out_stale; | ||
1711 | res->jiffies = cache->jiffies; | ||
1712 | res->cred = cache->cred; | ||
1713 | res->mask = cache->mask; | ||
1714 | err = 0; | ||
1715 | out: | ||
1716 | spin_unlock(&inode->i_lock); | ||
1717 | return err; | ||
1718 | out_stale: | ||
1719 | rb_erase(&cache->rb_node, &nfsi->access_cache); | ||
1720 | spin_unlock(&inode->i_lock); | ||
1721 | nfs_access_free_entry(cache); | ||
1722 | return -ENOENT; | ||
1723 | out_zap: | ||
1724 | /* This will release the spinlock */ | ||
1725 | __nfs_access_zap_cache(inode); | ||
1726 | return -ENOENT; | ||
1727 | } | ||
1728 | |||
1729 | static void nfs_access_add_rbtree(struct inode *inode, struct nfs_access_entry *set) | ||
1730 | { | ||
1731 | struct rb_root *root_node = &NFS_I(inode)->access_cache; | ||
1732 | struct rb_node **p = &root_node->rb_node; | ||
1733 | struct rb_node *parent = NULL; | ||
1734 | struct nfs_access_entry *entry; | ||
1735 | |||
1736 | spin_lock(&inode->i_lock); | ||
1737 | while (*p != NULL) { | ||
1738 | parent = *p; | ||
1739 | entry = rb_entry(parent, struct nfs_access_entry, rb_node); | ||
1740 | |||
1741 | if (set->cred < entry->cred) | ||
1742 | p = &parent->rb_left; | ||
1743 | else if (set->cred > entry->cred) | ||
1744 | p = &parent->rb_right; | ||
1745 | else | ||
1746 | goto found; | ||
1747 | } | ||
1748 | rb_link_node(&set->rb_node, parent, p); | ||
1749 | rb_insert_color(&set->rb_node, root_node); | ||
1667 | spin_unlock(&inode->i_lock); | 1750 | spin_unlock(&inode->i_lock); |
1751 | return; | ||
1752 | found: | ||
1753 | rb_replace_node(parent, &set->rb_node, root_node); | ||
1754 | spin_unlock(&inode->i_lock); | ||
1755 | nfs_access_free_entry(entry); | ||
1756 | } | ||
1757 | |||
1758 | void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set) | ||
1759 | { | ||
1760 | struct nfs_access_entry *cache = kmalloc(sizeof(*cache), GFP_KERNEL); | ||
1761 | if (cache == NULL) | ||
1762 | return; | ||
1763 | RB_CLEAR_NODE(&cache->rb_node); | ||
1668 | cache->jiffies = set->jiffies; | 1764 | cache->jiffies = set->jiffies; |
1765 | cache->cred = get_rpccred(set->cred); | ||
1669 | cache->mask = set->mask; | 1766 | cache->mask = set->mask; |
1767 | |||
1768 | nfs_access_add_rbtree(inode, cache); | ||
1670 | } | 1769 | } |
1671 | 1770 | ||
1672 | static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask) | 1771 | static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask) |
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index d349fb2245da..b94ab060bb1e 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
@@ -76,19 +76,14 @@ int nfs_write_inode(struct inode *inode, int sync) | |||
76 | 76 | ||
77 | void nfs_clear_inode(struct inode *inode) | 77 | void nfs_clear_inode(struct inode *inode) |
78 | { | 78 | { |
79 | struct nfs_inode *nfsi = NFS_I(inode); | ||
80 | struct rpc_cred *cred; | ||
81 | |||
82 | /* | 79 | /* |
83 | * The following should never happen... | 80 | * The following should never happen... |
84 | */ | 81 | */ |
85 | BUG_ON(nfs_have_writebacks(inode)); | 82 | BUG_ON(nfs_have_writebacks(inode)); |
86 | BUG_ON (!list_empty(&nfsi->open_files)); | 83 | BUG_ON(!list_empty(&NFS_I(inode)->open_files)); |
84 | BUG_ON(atomic_read(&NFS_I(inode)->data_updates) != 0); | ||
87 | nfs_zap_acl_cache(inode); | 85 | nfs_zap_acl_cache(inode); |
88 | cred = nfsi->cache_access.cred; | 86 | nfs_access_zap_cache(inode); |
89 | if (cred) | ||
90 | put_rpccred(cred); | ||
91 | BUG_ON(atomic_read(&nfsi->data_updates) != 0); | ||
92 | } | 87 | } |
93 | 88 | ||
94 | /** | 89 | /** |
@@ -290,7 +285,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) | |||
290 | nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); | 285 | nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); |
291 | nfsi->attrtimeo_timestamp = jiffies; | 286 | nfsi->attrtimeo_timestamp = jiffies; |
292 | memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); | 287 | memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); |
293 | nfsi->cache_access.cred = NULL; | 288 | nfsi->access_cache = RB_ROOT; |
294 | 289 | ||
295 | unlock_new_inode(inode); | 290 | unlock_new_inode(inode); |
296 | } else | 291 | } else |
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 6c2066caeaab..cc013ed2e52e 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h | |||
@@ -42,6 +42,7 @@ | |||
42 | #include <linux/in.h> | 42 | #include <linux/in.h> |
43 | #include <linux/mm.h> | 43 | #include <linux/mm.h> |
44 | #include <linux/pagemap.h> | 44 | #include <linux/pagemap.h> |
45 | #include <linux/rbtree.h> | ||
45 | #include <linux/rwsem.h> | 46 | #include <linux/rwsem.h> |
46 | #include <linux/wait.h> | 47 | #include <linux/wait.h> |
47 | 48 | ||
@@ -69,6 +70,7 @@ | |||
69 | * NFSv3/v4 Access mode cache entry | 70 | * NFSv3/v4 Access mode cache entry |
70 | */ | 71 | */ |
71 | struct nfs_access_entry { | 72 | struct nfs_access_entry { |
73 | struct rb_node rb_node; | ||
72 | unsigned long jiffies; | 74 | unsigned long jiffies; |
73 | struct rpc_cred * cred; | 75 | struct rpc_cred * cred; |
74 | int mask; | 76 | int mask; |
@@ -145,7 +147,7 @@ struct nfs_inode { | |||
145 | */ | 147 | */ |
146 | atomic_t data_updates; | 148 | atomic_t data_updates; |
147 | 149 | ||
148 | struct nfs_access_entry cache_access; | 150 | struct rb_root access_cache; |
149 | #ifdef CONFIG_NFS_V3_ACL | 151 | #ifdef CONFIG_NFS_V3_ACL |
150 | struct posix_acl *acl_access; | 152 | struct posix_acl *acl_access; |
151 | struct posix_acl *acl_default; | 153 | struct posix_acl *acl_default; |
@@ -297,6 +299,7 @@ extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *); | |||
297 | extern int nfs_permission(struct inode *, int, struct nameidata *); | 299 | extern int nfs_permission(struct inode *, int, struct nameidata *); |
298 | extern int nfs_access_get_cached(struct inode *, struct rpc_cred *, struct nfs_access_entry *); | 300 | extern int nfs_access_get_cached(struct inode *, struct rpc_cred *, struct nfs_access_entry *); |
299 | extern void nfs_access_add_cache(struct inode *, struct nfs_access_entry *); | 301 | extern void nfs_access_add_cache(struct inode *, struct nfs_access_entry *); |
302 | extern void nfs_access_zap_cache(struct inode *inode); | ||
300 | extern int nfs_open(struct inode *, struct file *); | 303 | extern int nfs_open(struct inode *, struct file *); |
301 | extern int nfs_release(struct inode *, struct file *); | 304 | extern int nfs_release(struct inode *, struct file *); |
302 | extern int nfs_attribute_timeout(struct inode *inode); | 305 | extern int nfs_attribute_timeout(struct inode *inode); |