Skip to content

Commit 5c8f577

Browse files
trondmygregkh
authored andcommitted
NFS: Directory page cache pages need to be locked when read
[ Upstream commit 114de38 ] When a NFS directory page cache page is removed from the page cache, its contents are freed through a call to nfs_readdir_clear_array(). To prevent the removal of the page cache entry until after we've finished reading it, we must take the page lock. Fixes: 11de3b1 ("NFS: Fix a memory leak in nfs_readdir") Cc: [email protected] # v2.6.37+ Signed-off-by: Trond Myklebust <[email protected]> Reviewed-by: Benjamin Coddington <[email protected]> Signed-off-by: Anna Schumaker <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 574940d commit 5c8f577

File tree

1 file changed

+19
-11
lines changed

1 file changed

+19
-11
lines changed

fs/nfs/dir.c

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -741,8 +741,6 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page)
741741
static
742742
void cache_page_release(nfs_readdir_descriptor_t *desc)
743743
{
744-
if (!desc->page->mapping)
745-
nfs_readdir_clear_array(desc->page);
746744
put_page(desc->page);
747745
desc->page = NULL;
748746
}
@@ -756,19 +754,28 @@ struct page *get_cache_page(nfs_readdir_descriptor_t *desc)
756754

757755
/*
758756
* Returns 0 if desc->dir_cookie was found on page desc->page_index
757+
* and locks the page to prevent removal from the page cache.
759758
*/
760759
static
761-
int find_cache_page(nfs_readdir_descriptor_t *desc)
760+
int find_and_lock_cache_page(nfs_readdir_descriptor_t *desc)
762761
{
763762
int res;
764763

765764
desc->page = get_cache_page(desc);
766765
if (IS_ERR(desc->page))
767766
return PTR_ERR(desc->page);
768-
769-
res = nfs_readdir_search_array(desc);
767+
res = lock_page_killable(desc->page);
770768
if (res != 0)
771-
cache_page_release(desc);
769+
goto error;
770+
res = -EAGAIN;
771+
if (desc->page->mapping != NULL) {
772+
res = nfs_readdir_search_array(desc);
773+
if (res == 0)
774+
return 0;
775+
}
776+
unlock_page(desc->page);
777+
error:
778+
cache_page_release(desc);
772779
return res;
773780
}
774781

@@ -783,7 +790,7 @@ int readdir_search_pagecache(nfs_readdir_descriptor_t *desc)
783790
desc->last_cookie = 0;
784791
}
785792
do {
786-
res = find_cache_page(desc);
793+
res = find_and_lock_cache_page(desc);
787794
} while (res == -EAGAIN);
788795
return res;
789796
}
@@ -828,7 +835,6 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc)
828835

829836
nfs_readdir_release_array(desc->page);
830837
out:
831-
cache_page_release(desc);
832838
dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n",
833839
(unsigned long long)*desc->dir_cookie, res);
834840
return res;
@@ -874,13 +880,13 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc)
874880

875881
status = nfs_do_filldir(desc);
876882

883+
out_release:
884+
nfs_readdir_clear_array(desc->page);
885+
cache_page_release(desc);
877886
out:
878887
dfprintk(DIRCACHE, "NFS: %s: returns %d\n",
879888
__func__, status);
880889
return status;
881-
out_release:
882-
cache_page_release(desc);
883-
goto out;
884890
}
885891

886892
/* The file offset position represents the dirent entry number. A
@@ -945,6 +951,8 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
945951
break;
946952

947953
res = nfs_do_filldir(desc);
954+
unlock_page(desc->page);
955+
cache_page_release(desc);
948956
if (res < 0)
949957
break;
950958
} while (!desc->eof);

0 commit comments

Comments
 (0)