2020-11-02 18:18:02

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 00/12] Readdir enhancements

From: Trond Myklebust <[email protected]>

The following patch series performs a number of cleanups on the readdir
code.
It also adds support for 1MB readdir RPC calls on-the-wire, and modifies
the caching code to ensure that we cache the entire contents of that
1MB call (instead of discarding the data that doesn't fit into a single
page).

Trond Myklebust (12):
NFS: Ensure contents of struct nfs_open_dir_context are consistent
NFS: Clean up readdir struct nfs_cache_array
NFS: Clean up nfs_readdir_page_filler()
NFS: Clean up directory array handling
NFS: Don't discard readdir results
NFS: Remove unnecessary kmap in nfs_readdir_xdr_to_array()
NFS: Replace kmap() with kmap_atomic() in nfs_readdir_search_array()
NFS: Simplify struct nfs_cache_array_entry
NFS: Support larger readdir buffers
NFS: More readdir cleanups
NFS: nfs_do_filldir() does not return a value
NFS: Reduce readdir stack usage

fs/nfs/client.c | 4 +-
fs/nfs/dir.c | 555 ++++++++++++++++++++++++-----------------
fs/nfs/internal.h | 6 -
include/linux/nfs_fs.h | 1 -
4 files changed, 325 insertions(+), 241 deletions(-)

--
2.28.0


2020-11-02 18:19:00

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 01/12] NFS: Ensure contents of struct nfs_open_dir_context are consistent

From: Trond Myklebust <[email protected]>

Ensure that the contents of struct nfs_open_dir_context are consistent
by setting them under the file->f_lock from a private copy (that is
known to be consistent).

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/dir.c | 72 +++++++++++++++++++++++++++++++---------------------
1 file changed, 43 insertions(+), 29 deletions(-)

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 4e011adaf967..67d8595cd6e5 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -144,20 +144,23 @@ struct nfs_cache_array {
struct nfs_cache_array_entry array[];
};

-typedef struct {
+typedef struct nfs_readdir_descriptor {
struct file *file;
struct page *page;
struct dir_context *ctx;
unsigned long page_index;
- u64 *dir_cookie;
+ u64 dir_cookie;
u64 last_cookie;
+ u64 dup_cookie;
loff_t current_index;
loff_t prev_index;

unsigned long dir_verifier;
unsigned long timestamp;
unsigned long gencount;
+ unsigned long attr_gencount;
unsigned int cache_entry_index;
+ signed char duped;
bool plus;
bool eof;
} nfs_readdir_descriptor_t;
@@ -273,7 +276,7 @@ int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descri
}

index = (unsigned int)diff;
- *desc->dir_cookie = array->array[index].cookie;
+ desc->dir_cookie = array->array[index].cookie;
desc->cache_entry_index = index;
return 0;
out_eof:
@@ -298,33 +301,32 @@ int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_des
int status = -EAGAIN;

for (i = 0; i < array->size; i++) {
- if (array->array[i].cookie == *desc->dir_cookie) {
+ if (array->array[i].cookie == desc->dir_cookie) {
struct nfs_inode *nfsi = NFS_I(file_inode(desc->file));
- struct nfs_open_dir_context *ctx = desc->file->private_data;

new_pos = desc->current_index + i;
- if (ctx->attr_gencount != nfsi->attr_gencount ||
+ if (desc->attr_gencount != nfsi->attr_gencount ||
!nfs_readdir_inode_mapping_valid(nfsi)) {
- ctx->duped = 0;
- ctx->attr_gencount = nfsi->attr_gencount;
+ desc->duped = 0;
+ desc->attr_gencount = nfsi->attr_gencount;
} else if (new_pos < desc->prev_index) {
- if (ctx->duped > 0
- && ctx->dup_cookie == *desc->dir_cookie) {
+ if (desc->duped > 0
+ && desc->dup_cookie == desc->dir_cookie) {
if (printk_ratelimit()) {
pr_notice("NFS: directory %pD2 contains a readdir loop."
"Please contact your server vendor. "
"The file: %.*s has duplicate cookie %llu\n",
desc->file, array->array[i].string.len,
- array->array[i].string.name, *desc->dir_cookie);
+ array->array[i].string.name, desc->dir_cookie);
}
status = -ELOOP;
goto out;
}
- ctx->dup_cookie = *desc->dir_cookie;
- ctx->duped = -1;
+ desc->dup_cookie = desc->dir_cookie;
+ desc->duped = -1;
}
if (nfs_readdir_use_cookie(desc->file))
- desc->ctx->pos = *desc->dir_cookie;
+ desc->ctx->pos = desc->dir_cookie;
else
desc->ctx->pos = new_pos;
desc->prev_index = new_pos;
@@ -334,7 +336,7 @@ int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_des
}
if (array->eof_index >= 0) {
status = -EBADCOOKIE;
- if (*desc->dir_cookie == array->last_cookie)
+ if (desc->dir_cookie == array->last_cookie)
desc->eof = true;
}
out:
@@ -349,7 +351,7 @@ int nfs_readdir_search_array(nfs_readdir_descriptor_t *desc)

array = kmap(desc->page);

- if (*desc->dir_cookie == 0)
+ if (desc->dir_cookie == 0)
status = nfs_readdir_search_for_pos(array, desc);
else
status = nfs_readdir_search_for_cookie(array, desc);
@@ -801,7 +803,6 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc)
int i = 0;
int res = 0;
struct nfs_cache_array *array = NULL;
- struct nfs_open_dir_context *ctx = file->private_data;

array = kmap(desc->page);
for (i = desc->cache_entry_index; i < array->size; i++) {
@@ -814,22 +815,22 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc)
break;
}
if (i < (array->size-1))
- *desc->dir_cookie = array->array[i+1].cookie;
+ desc->dir_cookie = array->array[i+1].cookie;
else
- *desc->dir_cookie = array->last_cookie;
+ desc->dir_cookie = array->last_cookie;
if (nfs_readdir_use_cookie(file))
- desc->ctx->pos = *desc->dir_cookie;
+ desc->ctx->pos = desc->dir_cookie;
else
desc->ctx->pos++;
- if (ctx->duped != 0)
- ctx->duped = 1;
+ if (desc->duped != 0)
+ desc->duped = 1;
}
if (array->eof_index >= 0)
desc->eof = true;

kunmap(desc->page);
dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n",
- (unsigned long long)*desc->dir_cookie, res);
+ (unsigned long long)desc->dir_cookie, res);
return res;
}

@@ -851,10 +852,9 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc)
struct page *page = NULL;
int status;
struct inode *inode = file_inode(desc->file);
- struct nfs_open_dir_context *ctx = desc->file->private_data;

dfprintk(DIRCACHE, "NFS: uncached_readdir() searching for cookie %Lu\n",
- (unsigned long long)*desc->dir_cookie);
+ (unsigned long long)desc->dir_cookie);

page = alloc_page(GFP_HIGHUSER);
if (!page) {
@@ -863,9 +863,9 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc)
}

desc->page_index = 0;
- desc->last_cookie = *desc->dir_cookie;
+ desc->last_cookie = desc->dir_cookie;
desc->page = page;
- ctx->duped = 0;
+ desc->duped = 0;

status = nfs_readdir_xdr_to_array(desc, page, inode);
if (status < 0)
@@ -894,7 +894,6 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
nfs_readdir_descriptor_t my_desc = {
.file = file,
.ctx = ctx,
- .dir_cookie = &dir_ctx->dir_cookie,
.plus = nfs_use_readdirplus(inode, ctx),
},
*desc = &my_desc;
@@ -915,13 +914,20 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
if (res < 0)
goto out;

+ spin_lock(&file->f_lock);
+ desc->dir_cookie = dir_ctx->dir_cookie;
+ desc->dup_cookie = dir_ctx->dup_cookie;
+ desc->duped = dir_ctx->duped;
+ desc->attr_gencount = dir_ctx->attr_gencount;
+ spin_unlock(&file->f_lock);
+
do {
res = readdir_search_pagecache(desc);

if (res == -EBADCOOKIE) {
res = 0;
/* This means either end of directory */
- if (*desc->dir_cookie && !desc->eof) {
+ if (desc->dir_cookie && !desc->eof) {
/* Or that the server has 'lost' a cookie */
res = uncached_readdir(desc);
if (res == 0)
@@ -946,6 +952,14 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
if (res < 0)
break;
} while (!desc->eof);
+
+ spin_lock(&file->f_lock);
+ dir_ctx->dir_cookie = desc->dir_cookie;
+ dir_ctx->dup_cookie = desc->dup_cookie;
+ dir_ctx->duped = desc->duped;
+ dir_ctx->attr_gencount = desc->attr_gencount;
+ spin_unlock(&file->f_lock);
+
out:
if (res > 0)
res = 0;
--
2.28.0

2020-11-02 18:19:31

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 02/12] NFS: Clean up readdir struct nfs_cache_array

From: Trond Myklebust <[email protected]>

Since the 'eof_index' is only ever used as a flag, make it so.
Also add a flag to detect if the page has been completely filled.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/dir.c | 66 ++++++++++++++++++++++++++++++++++++++--------------
1 file changed, 49 insertions(+), 17 deletions(-)

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 67d8595cd6e5..604ebe015387 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -138,9 +138,10 @@ struct nfs_cache_array_entry {
};

struct nfs_cache_array {
- int size;
- int eof_index;
u64 last_cookie;
+ unsigned int size;
+ unsigned char page_full : 1,
+ page_is_eof : 1;
struct nfs_cache_array_entry array[];
};

@@ -172,7 +173,6 @@ void nfs_readdir_init_array(struct page *page)

array = kmap_atomic(page);
memset(array, 0, sizeof(struct nfs_cache_array));
- array->eof_index = -1;
kunmap_atomic(array);
}

@@ -192,6 +192,17 @@ void nfs_readdir_clear_array(struct page *page)
kunmap_atomic(array);
}

+static void nfs_readdir_array_set_eof(struct nfs_cache_array *array)
+{
+ array->page_is_eof = 1;
+ array->page_full = 1;
+}
+
+static bool nfs_readdir_array_is_full(struct nfs_cache_array *array)
+{
+ return array->page_full;
+}
+
/*
* the caller is responsible for freeing qstr.name
* when called by nfs_readdir_add_to_array, the strings will be freed in
@@ -213,6 +224,23 @@ int nfs_readdir_make_qstr(struct qstr *string, const char *name, unsigned int le
return 0;
}

+/*
+ * Check that the next array entry lies entirely within the page bounds
+ */
+static int nfs_readdir_array_can_expand(struct nfs_cache_array *array)
+{
+ struct nfs_cache_array_entry *cache_entry;
+
+ if (array->page_full)
+ return -ENOSPC;
+ cache_entry = &array->array[array->size + 1];
+ if ((char *)cache_entry - (char *)array > PAGE_SIZE) {
+ array->page_full = 1;
+ return -ENOSPC;
+ }
+ return 0;
+}
+
static
int nfs_readdir_add_to_array(struct nfs_entry *entry, struct page *page)
{
@@ -220,13 +248,11 @@ int nfs_readdir_add_to_array(struct nfs_entry *entry, struct page *page)
struct nfs_cache_array_entry *cache_entry;
int ret;

- cache_entry = &array->array[array->size];
-
- /* Check that this entry lies within the page bounds */
- ret = -ENOSPC;
- if ((char *)&cache_entry[1] - (char *)page_address(page) > PAGE_SIZE)
+ ret = nfs_readdir_array_can_expand(array);
+ if (ret)
goto out;

+ cache_entry = &array->array[array->size];
cache_entry->cookie = entry->prev_cookie;
cache_entry->ino = entry->ino;
cache_entry->d_type = entry->d_type;
@@ -236,12 +262,21 @@ int nfs_readdir_add_to_array(struct nfs_entry *entry, struct page *page)
array->last_cookie = entry->cookie;
array->size++;
if (entry->eof != 0)
- array->eof_index = array->size;
+ nfs_readdir_array_set_eof(array);
out:
kunmap(page);
return ret;
}

+static void nfs_readdir_page_set_eof(struct page *page)
+{
+ struct nfs_cache_array *array;
+
+ array = kmap_atomic(page);
+ nfs_readdir_array_set_eof(array);
+ kunmap_atomic(array);
+}
+
static inline
int is_32bit_api(void)
{
@@ -270,7 +305,7 @@ int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descri
if (diff < 0)
goto out_eof;
if (diff >= array->size) {
- if (array->eof_index >= 0)
+ if (array->page_is_eof)
goto out_eof;
return -EAGAIN;
}
@@ -334,7 +369,7 @@ int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_des
return 0;
}
}
- if (array->eof_index >= 0) {
+ if (array->page_is_eof) {
status = -EBADCOOKIE;
if (desc->dir_cookie == array->last_cookie)
desc->eof = true;
@@ -566,7 +601,6 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
struct xdr_stream stream;
struct xdr_buf buf;
struct page *scratch;
- struct nfs_cache_array *array;
unsigned int count = 0;
int status;

@@ -604,10 +638,8 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en

out_nopages:
if (count == 0 || (status == -EBADCOOKIE && entry->eof != 0)) {
- array = kmap(page);
- array->eof_index = array->size;
+ nfs_readdir_page_set_eof(page);
status = 0;
- kunmap(page);
}

put_page(scratch);
@@ -689,7 +721,7 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
status = 0;
break;
}
- } while (array->eof_index < 0);
+ } while (!nfs_readdir_array_is_full(array));

nfs_readdir_free_pages(pages, array_size);
out_release_array:
@@ -825,7 +857,7 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc)
if (desc->duped != 0)
desc->duped = 1;
}
- if (array->eof_index >= 0)
+ if (array->page_is_eof)
desc->eof = true;

kunmap(desc->page);
--
2.28.0

2020-11-02 20:46:02

by David Wysochanski

[permalink] [raw]
Subject: Re: [PATCH 00/12] Readdir enhancements

On Mon, Nov 2, 2020 at 1:17 PM <[email protected]> wrote:
>
> From: Trond Myklebust <[email protected]>
>
> The following patch series performs a number of cleanups on the readdir
> code.
> It also adds support for 1MB readdir RPC calls on-the-wire, and modifies
> the caching code to ensure that we cache the entire contents of that
> 1MB call (instead of discarding the data that doesn't fit into a single
> page).
>
> Trond Myklebust (12):
> NFS: Ensure contents of struct nfs_open_dir_context are consistent
> NFS: Clean up readdir struct nfs_cache_array
> NFS: Clean up nfs_readdir_page_filler()
> NFS: Clean up directory array handling
> NFS: Don't discard readdir results
> NFS: Remove unnecessary kmap in nfs_readdir_xdr_to_array()
> NFS: Replace kmap() with kmap_atomic() in nfs_readdir_search_array()
> NFS: Simplify struct nfs_cache_array_entry
> NFS: Support larger readdir buffers
> NFS: More readdir cleanups
> NFS: nfs_do_filldir() does not return a value
> NFS: Reduce readdir stack usage
>
> fs/nfs/client.c | 4 +-
> fs/nfs/dir.c | 555 ++++++++++++++++++++++++-----------------
> fs/nfs/internal.h | 6 -
> include/linux/nfs_fs.h | 1 -
> 4 files changed, 325 insertions(+), 241 deletions(-)
>
> --
> 2.28.0
>

Nice to see these, especially
[PATCH 05/12] NFS: Don't discard readdir results

Are you testing these on top of 5.10-rc2 or something else?

2020-11-02 21:33:33

by Trond Myklebust

[permalink] [raw]
Subject: Re: [PATCH 00/12] Readdir enhancements

On Mon, 2020-11-02 at 15:40 -0500, David Wysochanski wrote:
> On Mon, Nov 2, 2020 at 1:17 PM <[email protected]> wrote:
> >
> > From: Trond Myklebust <[email protected]>
> >
> > The following patch series performs a number of cleanups on the
> > readdir
> > code.
> > It also adds support for 1MB readdir RPC calls on-the-wire, and
> > modifies
> > the caching code to ensure that we cache the entire contents of
> > that
> > 1MB call (instead of discarding the data that doesn't fit into a
> > single
> > page).
> >
> > Trond Myklebust (12):
> >   NFS: Ensure contents of struct nfs_open_dir_context are
> > consistent
> >   NFS: Clean up readdir struct nfs_cache_array
> >   NFS: Clean up nfs_readdir_page_filler()
> >   NFS: Clean up directory array handling
> >   NFS: Don't discard readdir results
> >   NFS: Remove unnecessary kmap in nfs_readdir_xdr_to_array()
> >   NFS: Replace kmap() with kmap_atomic() in
> > nfs_readdir_search_array()
> >   NFS: Simplify struct nfs_cache_array_entry
> >   NFS: Support larger readdir buffers
> >   NFS: More readdir cleanups
> >   NFS: nfs_do_filldir() does not return a value
> >   NFS: Reduce readdir stack usage
> >
> >  fs/nfs/client.c        |   4 +-
> >  fs/nfs/dir.c           | 555 ++++++++++++++++++++++++-------------
> > ----
> >  fs/nfs/internal.h      |   6 -
> >  include/linux/nfs_fs.h |   1 -
> >  4 files changed, 325 insertions(+), 241 deletions(-)
> >
> > --
> > 2.28.0
> >
>
> Nice to see these, especially
> [PATCH 05/12] NFS: Don't discard readdir results
>
> Are you testing these on top of 5.10-rc2 or something else?
>
They are being tested on 5.10-rc2.

--
Trond Myklebust
Linux NFS client maintainer, Hammerspace
[email protected]


2020-11-03 13:36:00

by Benjamin Coddington

[permalink] [raw]
Subject: Re: [PATCH 02/12] NFS: Clean up readdir struct nfs_cache_array

On 2 Nov 2020, at 13:06, [email protected] wrote:

> From: Trond Myklebust <[email protected]>
>
> Since the 'eof_index' is only ever used as a flag, make it so.
> Also add a flag to detect if the page has been completely filled.
>
> Signed-off-by: Trond Myklebust <[email protected]>
> ---
> fs/nfs/dir.c | 66
> ++++++++++++++++++++++++++++++++++++++--------------
> 1 file changed, 49 insertions(+), 17 deletions(-)
>
> diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
> index 67d8595cd6e5..604ebe015387 100644
> --- a/fs/nfs/dir.c
> +++ b/fs/nfs/dir.c
> @@ -138,9 +138,10 @@ struct nfs_cache_array_entry {
> };
>
> struct nfs_cache_array {
> - int size;
> - int eof_index;
> u64 last_cookie;
> + unsigned int size;
> + unsigned char page_full : 1,
> + page_is_eof : 1;
> struct nfs_cache_array_entry array[];
> };
>
> @@ -172,7 +173,6 @@ void nfs_readdir_init_array(struct page *page)
>
> array = kmap_atomic(page);
> memset(array, 0, sizeof(struct nfs_cache_array));
> - array->eof_index = -1;
> kunmap_atomic(array);
> }
>
> @@ -192,6 +192,17 @@ void nfs_readdir_clear_array(struct page *page)
> kunmap_atomic(array);
> }
>
> +static void nfs_readdir_array_set_eof(struct nfs_cache_array *array)
> +{
> + array->page_is_eof = 1;
> + array->page_full = 1;
> +}
> +
> +static bool nfs_readdir_array_is_full(struct nfs_cache_array *array)
> +{
> + return array->page_full;
> +}
> +
> /*
> * the caller is responsible for freeing qstr.name
> * when called by nfs_readdir_add_to_array, the strings will be freed
> in
> @@ -213,6 +224,23 @@ int nfs_readdir_make_qstr(struct qstr *string,
> const char *name, unsigned int le
> return 0;
> }
>
> +/*
> + * Check that the next array entry lies entirely within the page
> bounds
> + */
> +static int nfs_readdir_array_can_expand(struct nfs_cache_array
> *array)
> +{
> + struct nfs_cache_array_entry *cache_entry;
> +
> + if (array->page_full)
> + return -ENOSPC;
> + cache_entry = &array->array[array->size + 1];
> + if ((char *)cache_entry - (char *)array > PAGE_SIZE) {
> + array->page_full = 1;
> + return -ENOSPC;
> + }
> + return 0;
> +}
> +

I think we could do this calculation at compile-time instead of doing it
for
each entry, for dubious nominal gains..

Ben


> static
> int nfs_readdir_add_to_array(struct nfs_entry *entry, struct page
> *page)
> {
> @@ -220,13 +248,11 @@ int nfs_readdir_add_to_array(struct nfs_entry
> *entry, struct page *page)
> struct nfs_cache_array_entry *cache_entry;
> int ret;
>
> - cache_entry = &array->array[array->size];
> -
> - /* Check that this entry lies within the page bounds */
> - ret = -ENOSPC;
> - if ((char *)&cache_entry[1] - (char *)page_address(page) >
> PAGE_SIZE)
> + ret = nfs_readdir_array_can_expand(array);
> + if (ret)
> goto out;
>
> + cache_entry = &array->array[array->size];
> cache_entry->cookie = entry->prev_cookie;
> cache_entry->ino = entry->ino;
> cache_entry->d_type = entry->d_type;
> @@ -236,12 +262,21 @@ int nfs_readdir_add_to_array(struct nfs_entry
> *entry, struct page *page)
> array->last_cookie = entry->cookie;
> array->size++;
> if (entry->eof != 0)
> - array->eof_index = array->size;
> + nfs_readdir_array_set_eof(array);
> out:
> kunmap(page);
> return ret;
> }
>
> +static void nfs_readdir_page_set_eof(struct page *page)
> +{
> + struct nfs_cache_array *array;
> +
> + array = kmap_atomic(page);
> + nfs_readdir_array_set_eof(array);
> + kunmap_atomic(array);
> +}
> +
> static inline
> int is_32bit_api(void)
> {
> @@ -270,7 +305,7 @@ int nfs_readdir_search_for_pos(struct
> nfs_cache_array *array, nfs_readdir_descri
> if (diff < 0)
> goto out_eof;
> if (diff >= array->size) {
> - if (array->eof_index >= 0)
> + if (array->page_is_eof)
> goto out_eof;
> return -EAGAIN;
> }
> @@ -334,7 +369,7 @@ int nfs_readdir_search_for_cookie(struct
> nfs_cache_array *array, nfs_readdir_des
> return 0;
> }
> }
> - if (array->eof_index >= 0) {
> + if (array->page_is_eof) {
> status = -EBADCOOKIE;
> if (desc->dir_cookie == array->last_cookie)
> desc->eof = true;
> @@ -566,7 +601,6 @@ int
> nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct
> nfs_entry *en
> struct xdr_stream stream;
> struct xdr_buf buf;
> struct page *scratch;
> - struct nfs_cache_array *array;
> unsigned int count = 0;
> int status;
>
> @@ -604,10 +638,8 @@ int
> nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct
> nfs_entry *en
>
> out_nopages:
> if (count == 0 || (status == -EBADCOOKIE && entry->eof != 0)) {
> - array = kmap(page);
> - array->eof_index = array->size;
> + nfs_readdir_page_set_eof(page);
> status = 0;
> - kunmap(page);
> }
>
> put_page(scratch);
> @@ -689,7 +721,7 @@ int
> nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page
> *page,
> status = 0;
> break;
> }
> - } while (array->eof_index < 0);
> + } while (!nfs_readdir_array_is_full(array));
>
> nfs_readdir_free_pages(pages, array_size);
> out_release_array:
> @@ -825,7 +857,7 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc)
> if (desc->duped != 0)
> desc->duped = 1;
> }
> - if (array->eof_index >= 0)
> + if (array->page_is_eof)
> desc->eof = true;
>
> kunmap(desc->page);
> --
> 2.28.0

2020-11-03 14:12:30

by Benjamin Coddington

[permalink] [raw]
Subject: Re: [PATCH 02/12] NFS: Clean up readdir struct nfs_cache_array

On 3 Nov 2020, at 8:35, Benjamin Coddington wrote:

> On 2 Nov 2020, at 13:06, [email protected] wrote:
>
>> From: Trond Myklebust <[email protected]>
>>
>> Since the 'eof_index' is only ever used as a flag, make it so.
>> Also add a flag to detect if the page has been completely filled.
>>
>> Signed-off-by: Trond Myklebust <[email protected]>
>> ---
>> fs/nfs/dir.c | 66
>> ++++++++++++++++++++++++++++++++++++++--------------
>> 1 file changed, 49 insertions(+), 17 deletions(-)
>>
>> diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
>> index 67d8595cd6e5..604ebe015387 100644
>> --- a/fs/nfs/dir.c
>> +++ b/fs/nfs/dir.c
>> @@ -138,9 +138,10 @@ struct nfs_cache_array_entry {
>> };
>>
>> struct nfs_cache_array {
>> - int size;
>> - int eof_index;
>> u64 last_cookie;
>> + unsigned int size;
>> + unsigned char page_full : 1,
>> + page_is_eof : 1;
>> struct nfs_cache_array_entry array[];
>> };
>>
>> @@ -172,7 +173,6 @@ void nfs_readdir_init_array(struct page *page)
>>
>> array = kmap_atomic(page);
>> memset(array, 0, sizeof(struct nfs_cache_array));
>> - array->eof_index = -1;
>> kunmap_atomic(array);
>> }
>>
>> @@ -192,6 +192,17 @@ void nfs_readdir_clear_array(struct page *page)
>> kunmap_atomic(array);
>> }
>>
>> +static void nfs_readdir_array_set_eof(struct nfs_cache_array *array)
>> +{
>> + array->page_is_eof = 1;
>> + array->page_full = 1;
>> +}
>> +
>> +static bool nfs_readdir_array_is_full(struct nfs_cache_array *array)
>> +{
>> + return array->page_full;
>> +}
>> +
>> /*
>> * the caller is responsible for freeing qstr.name
>> * when called by nfs_readdir_add_to_array, the strings will be
>> freed in
>> @@ -213,6 +224,23 @@ int nfs_readdir_make_qstr(struct qstr *string,
>> const char *name, unsigned int le
>> return 0;
>> }
>>
>> +/*
>> + * Check that the next array entry lies entirely within the page
>> bounds
>> + */
>> +static int nfs_readdir_array_can_expand(struct nfs_cache_array
>> *array)
>> +{
>> + struct nfs_cache_array_entry *cache_entry;
>> +
>> + if (array->page_full)
>> + return -ENOSPC;
>> + cache_entry = &array->array[array->size + 1];
>> + if ((char *)cache_entry - (char *)array > PAGE_SIZE) {
>> + array->page_full = 1;
>> + return -ENOSPC;
>> + }
>> + return 0;
>> +}
>> +
>
> I think we could do this calculation at compile-time instead of doing
> it for
> each entry, for dubious nominal gains..

and doing so might allow us to detect the case where the array is full
before doing another RPC that we'll discard.

Ben

2020-11-03 14:37:15

by Benjamin Coddington

[permalink] [raw]
Subject: Re: [PATCH 02/12] NFS: Clean up readdir struct nfs_cache_array

On 3 Nov 2020, at 9:09, Benjamin Coddington wrote:

> On 3 Nov 2020, at 8:35, Benjamin Coddington wrote:
>
>> On 2 Nov 2020, at 13:06, [email protected] wrote:
>>
>>> From: Trond Myklebust <[email protected]>
>>>
>>> Since the 'eof_index' is only ever used as a flag, make it so.
>>> Also add a flag to detect if the page has been completely filled.
>>>
>>> Signed-off-by: Trond Myklebust <[email protected]>
>>> ---
>>> fs/nfs/dir.c | 66
>>> ++++++++++++++++++++++++++++++++++++++--------------
>>> 1 file changed, 49 insertions(+), 17 deletions(-)
>>>
>>> diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
>>> index 67d8595cd6e5..604ebe015387 100644
>>> --- a/fs/nfs/dir.c
>>> +++ b/fs/nfs/dir.c
>>> @@ -138,9 +138,10 @@ struct nfs_cache_array_entry {
>>> };
>>>
>>> struct nfs_cache_array {
>>> - int size;
>>> - int eof_index;
>>> u64 last_cookie;
>>> + unsigned int size;
>>> + unsigned char page_full : 1,
>>> + page_is_eof : 1;
>>> struct nfs_cache_array_entry array[];
>>> };
>>>
>>> @@ -172,7 +173,6 @@ void nfs_readdir_init_array(struct page *page)
>>>
>>> array = kmap_atomic(page);
>>> memset(array, 0, sizeof(struct nfs_cache_array));
>>> - array->eof_index = -1;
>>> kunmap_atomic(array);
>>> }
>>>
>>> @@ -192,6 +192,17 @@ void nfs_readdir_clear_array(struct page *page)
>>> kunmap_atomic(array);
>>> }
>>>
>>> +static void nfs_readdir_array_set_eof(struct nfs_cache_array
>>> *array)
>>> +{
>>> + array->page_is_eof = 1;
>>> + array->page_full = 1;
>>> +}
>>> +
>>> +static bool nfs_readdir_array_is_full(struct nfs_cache_array
>>> *array)
>>> +{
>>> + return array->page_full;
>>> +}
>>> +
>>> /*
>>> * the caller is responsible for freeing qstr.name
>>> * when called by nfs_readdir_add_to_array, the strings will be
>>> freed in
>>> @@ -213,6 +224,23 @@ int nfs_readdir_make_qstr(struct qstr *string,
>>> const char *name, unsigned int le
>>> return 0;
>>> }
>>>
>>> +/*
>>> + * Check that the next array entry lies entirely within the page
>>> bounds
>>> + */
>>> +static int nfs_readdir_array_can_expand(struct nfs_cache_array
>>> *array)
>>> +{
>>> + struct nfs_cache_array_entry *cache_entry;
>>> +
>>> + if (array->page_full)
>>> + return -ENOSPC;
>>> + cache_entry = &array->array[array->size + 1];
>>> + if ((char *)cache_entry - (char *)array > PAGE_SIZE) {
>>> + array->page_full = 1;
>>> + return -ENOSPC;
>>> + }
>>> + return 0;
>>> +}
>>> +
>>
>> I think we could do this calculation at compile-time instead of doing
>> it for
>> each entry, for dubious nominal gains..
>
> and doing so might allow us to detect the case where the array is full
> before doing another RPC that we'll discard.

And you handle this case in patch 4/12...

Ben