]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
NFS: Add an ACCESS cache memory shrinker
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Tue, 25 Jul 2006 15:28:19 +0000 (11:28 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Sat, 23 Sep 2006 03:24:29 +0000 (23:24 -0400)
A pinned inode may in theory end up filling memory with cached ACCESS
calls. This patch ensures that the VM may shrink away the cache in these
particular cases.
The shrinker works by iterating through the list of inodes on the global
nfs_access_lru_list, and removing the least recently used access
cache entry until it is done (or until the entire cache is empty).

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
fs/nfs/dir.c
fs/nfs/internal.h
fs/nfs/super.c

index bf4f5ffda703c5f28ac6446bc6a7bbf4d6c98162..067d144d141b0d37a3953167d2a4056612194ab4 100644 (file)
@@ -1651,6 +1651,50 @@ static void nfs_access_free_entry(struct nfs_access_entry *entry)
        smp_mb__after_atomic_dec();
 }
 
+int nfs_access_cache_shrinker(int nr_to_scan, gfp_t gfp_mask)
+{
+       LIST_HEAD(head);
+       struct nfs_inode *nfsi;
+       struct nfs_access_entry *cache;
+
+       spin_lock(&nfs_access_lru_lock);
+restart:
+       list_for_each_entry(nfsi, &nfs_access_lru_list, access_cache_inode_lru) {
+               struct inode *inode;
+
+               if (nr_to_scan-- == 0)
+                       break;
+               inode = igrab(&nfsi->vfs_inode);
+               if (inode == NULL)
+                       continue;
+               spin_lock(&inode->i_lock);
+               if (list_empty(&nfsi->access_cache_entry_lru))
+                       goto remove_lru_entry;
+               cache = list_entry(nfsi->access_cache_entry_lru.next,
+                               struct nfs_access_entry, lru);
+               list_move(&cache->lru, &head);
+               rb_erase(&cache->rb_node, &nfsi->access_cache);
+               if (!list_empty(&nfsi->access_cache_entry_lru))
+                       list_move_tail(&nfsi->access_cache_inode_lru,
+                                       &nfs_access_lru_list);
+               else {
+remove_lru_entry:
+                       list_del_init(&nfsi->access_cache_inode_lru);
+                       clear_bit(NFS_INO_ACL_LRU_SET, &nfsi->flags);
+               }
+               spin_unlock(&inode->i_lock);
+               iput(inode);
+               goto restart;
+       }
+       spin_unlock(&nfs_access_lru_lock);
+       while (!list_empty(&head)) {
+               cache = list_entry(head.next, struct nfs_access_entry, lru);
+               list_del(&cache->lru);
+               nfs_access_free_entry(cache);
+       }
+       return (atomic_long_read(&nfs_access_nr_entries) / 100) * sysctl_vfs_cache_pressure;
+}
+
 static void __nfs_access_zap_cache(struct inode *inode)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
index e4f4e5def0fcc095edd2d3c3e32adfd41dac6428..660e9ff5341cb5a4126b80b233e2f2fefb978de2 100644 (file)
@@ -66,6 +66,9 @@ extern int nfs4_proc_fs_locations(struct inode *dir, struct dentry *dentry,
                                  struct page *page);
 #endif
 
+/* dir.c */
+extern int nfs_access_cache_shrinker(int nr_to_scan, gfp_t gfp_mask);
+
 /* inode.c */
 extern struct inode *nfs_alloc_inode(struct super_block *sb);
 extern void nfs_destroy_inode(struct inode *);
index e8a9bee74d9d2b8bdbbd42f4c1020d0d7c00adde..06c321beacfe660aa067752de37225ccac841096 100644 (file)
@@ -221,6 +221,8 @@ module_param_call(idmap_cache_timeout, param_set_idmap_timeout, param_get_int,
                 &nfs_idmap_cache_timeout, 0644);
 #endif
 
+static struct shrinker *acl_shrinker;
+
 /*
  * Register the NFS filesystems
  */
@@ -240,6 +242,7 @@ int __init register_nfs_fs(void)
        if (ret < 0)
                goto error_2;
 #endif
+       acl_shrinker = set_shrinker(DEFAULT_SEEKS, nfs_access_cache_shrinker);
        return 0;
 
 #ifdef CONFIG_NFS_V4
@@ -257,6 +260,8 @@ error_0:
  */
 void __exit unregister_nfs_fs(void)
 {
+       if (acl_shrinker != NULL)
+               remove_shrinker(acl_shrinker);
 #ifdef CONFIG_NFS_V4
        unregister_filesystem(&nfs4_fs_type);
        nfs_unregister_sysctl();