diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2010-12-01 14:17:06 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2010-12-02 09:58:00 -0500 |
commit | 11de3b11e08cac26d59e88efaf4e316701883552 (patch) | |
tree | e4cc282e692560897c42c5fbc8df227d5da669d4 /fs/nfs | |
parent | 6072d13c429373c5d63b69dadbbef40a9b035552 (diff) |
NFS: Fix a memory leak in nfs_readdir
We need to ensure that the entries in the nfs_cache_array get cleared
when the page is removed from the page cache. To do so, we use the
freepage address_space operation.
Change nfs_readdir_clear_array to use kmap_atomic(), so that the
function can be safely called from all contexts.
Finally, modify the cache_page_release helper to call
nfs_readdir_clear_array directly, when dealing with an anonymous
page from 'uncached_readdir'.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/dir.c | 18 | ||||
-rw-r--r-- | fs/nfs/inode.c | 1 |
2 files changed, 10 insertions, 9 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index e03537ff9350..d529e0e99efa 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -57,7 +57,7 @@ static int nfs_rename(struct inode *, struct dentry *, | |||
57 | struct inode *, struct dentry *); | 57 | struct inode *, struct dentry *); |
58 | static int nfs_fsync_dir(struct file *, int); | 58 | static int nfs_fsync_dir(struct file *, int); |
59 | static loff_t nfs_llseek_dir(struct file *, loff_t, int); | 59 | static loff_t nfs_llseek_dir(struct file *, loff_t, int); |
60 | static int nfs_readdir_clear_array(struct page*, gfp_t); | 60 | static void nfs_readdir_clear_array(struct page*); |
61 | 61 | ||
62 | const struct file_operations nfs_dir_operations = { | 62 | const struct file_operations nfs_dir_operations = { |
63 | .llseek = nfs_llseek_dir, | 63 | .llseek = nfs_llseek_dir, |
@@ -83,8 +83,8 @@ const struct inode_operations nfs_dir_inode_operations = { | |||
83 | .setattr = nfs_setattr, | 83 | .setattr = nfs_setattr, |
84 | }; | 84 | }; |
85 | 85 | ||
86 | const struct address_space_operations nfs_dir_addr_space_ops = { | 86 | const struct address_space_operations nfs_dir_aops = { |
87 | .releasepage = nfs_readdir_clear_array, | 87 | .freepage = nfs_readdir_clear_array, |
88 | }; | 88 | }; |
89 | 89 | ||
90 | #ifdef CONFIG_NFS_V3 | 90 | #ifdef CONFIG_NFS_V3 |
@@ -214,17 +214,15 @@ void nfs_readdir_release_array(struct page *page) | |||
214 | * we are freeing strings created by nfs_add_to_readdir_array() | 214 | * we are freeing strings created by nfs_add_to_readdir_array() |
215 | */ | 215 | */ |
216 | static | 216 | static |
217 | int nfs_readdir_clear_array(struct page *page, gfp_t mask) | 217 | void nfs_readdir_clear_array(struct page *page) |
218 | { | 218 | { |
219 | struct nfs_cache_array *array = nfs_readdir_get_array(page); | 219 | struct nfs_cache_array *array; |
220 | int i; | 220 | int i; |
221 | 221 | ||
222 | if (IS_ERR(array)) | 222 | array = kmap_atomic(page, KM_USER0); |
223 | return PTR_ERR(array); | ||
224 | for (i = 0; i < array->size; i++) | 223 | for (i = 0; i < array->size; i++) |
225 | kfree(array->array[i].string.name); | 224 | kfree(array->array[i].string.name); |
226 | nfs_readdir_release_array(page); | 225 | kunmap_atomic(array, KM_USER0); |
227 | return 0; | ||
228 | } | 226 | } |
229 | 227 | ||
230 | /* | 228 | /* |
@@ -639,6 +637,8 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page) | |||
639 | static | 637 | static |
640 | void cache_page_release(nfs_readdir_descriptor_t *desc) | 638 | void cache_page_release(nfs_readdir_descriptor_t *desc) |
641 | { | 639 | { |
640 | if (!desc->page->mapping) | ||
641 | nfs_readdir_clear_array(desc->page); | ||
642 | page_cache_release(desc->page); | 642 | page_cache_release(desc->page); |
643 | desc->page = NULL; | 643 | desc->page = NULL; |
644 | } | 644 | } |
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 314f57164602..e67e31c73416 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
@@ -289,6 +289,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) | |||
289 | } else if (S_ISDIR(inode->i_mode)) { | 289 | } else if (S_ISDIR(inode->i_mode)) { |
290 | inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops; | 290 | inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops; |
291 | inode->i_fop = &nfs_dir_operations; | 291 | inode->i_fop = &nfs_dir_operations; |
292 | inode->i_data.a_ops = &nfs_dir_aops; | ||
292 | if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS)) | 293 | if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS)) |
293 | set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags); | 294 | set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags); |
294 | /* Deal with crossing mountpoints */ | 295 | /* Deal with crossing mountpoints */ |