summaryrefslogtreecommitdiff
path: root/fs/nfs/dir.c
diff options
context:
space:
mode:
authorTrond Myklebust <trond.myklebust@hammerspace.com>2020-11-01 13:14:10 -0500
committerTrond Myklebust <trond.myklebust@hammerspace.com>2020-12-02 14:05:51 -0500
commit3b2a09f127e025674945e82c1ec0c88d6740280e (patch)
tree56ec985a50c0c7f09dc5c5db50a0869b45424974 /fs/nfs/dir.c
parent1f1d4aa4e4bcb4721d3c51f4c07dda790b6accd9 (diff)
NFS: Don't discard readdir results
If a readdir call returns more data than we can fit into one page cache page, then allocate a new one for that data rather than discarding the data. Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com> Reviewed-by: Benjamin Coddington <bcodding@redhat.com> Tested-by: Benjamin Coddington <bcodding@redhat.com> Tested-by: Dave Wysochanski <dwysocha@redhat.com>
Diffstat (limited to 'fs/nfs/dir.c')
-rw-r--r--fs/nfs/dir.c46
1 files changed, 42 insertions, 4 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 842f69120a01..f7248145c333 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -320,6 +320,26 @@ static void nfs_readdir_page_set_eof(struct page *page)
kunmap_atomic(array);
}
+static void nfs_readdir_page_unlock_and_put(struct page *page)
+{
+ unlock_page(page);
+ put_page(page);
+}
+
+static struct page *nfs_readdir_page_get_next(struct address_space *mapping,
+ pgoff_t index, u64 cookie)
+{
+ struct page *page;
+
+ page = nfs_readdir_page_get_locked(mapping, index, cookie);
+ if (page) {
+ if (nfs_readdir_page_last_cookie(page) == cookie)
+ return page;
+ nfs_readdir_page_unlock_and_put(page);
+ }
+ return NULL;
+}
+
static inline
int is_32bit_api(void)
{
@@ -637,13 +657,15 @@ out:
}
/* Perform conversion from xdr to cache array */
-static
-int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry,
- struct page **xdr_pages, struct page *page, unsigned int buflen)
+static int nfs_readdir_page_filler(struct nfs_readdir_descriptor *desc,
+ struct nfs_entry *entry,
+ struct page **xdr_pages,
+ struct page *fillme, unsigned int buflen)
{
+ struct address_space *mapping = desc->file->f_mapping;
struct xdr_stream stream;
struct xdr_buf buf;
- struct page *scratch;
+ struct page *scratch, *new, *page = fillme;
int status;
scratch = alloc_page(GFP_KERNEL);
@@ -666,6 +688,19 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
desc->dir_verifier);
status = nfs_readdir_add_to_array(entry, page);
+ if (status != -ENOSPC)
+ continue;
+
+ if (page->mapping != mapping)
+ break;
+ new = nfs_readdir_page_get_next(mapping, page->index + 1,
+ entry->prev_cookie);
+ if (!new)
+ break;
+ if (page != fillme)
+ nfs_readdir_page_unlock_and_put(page);
+ page = new;
+ status = nfs_readdir_add_to_array(entry, page);
} while (!status && !entry->eof);
switch (status) {
@@ -681,6 +716,9 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
break;
}
+ if (page != fillme)
+ nfs_readdir_page_unlock_and_put(page);
+
put_page(scratch);
return status;
}