2022-02-21 21:35:29

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH v6 12/13] NFS: Trace effects of readdirplus on the dcache

From: Trond Myklebust <[email protected]>

Trace the effects of readdirplus on attribute and dentry revalidation.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/dir.c | 5 +++++
fs/nfs/nfstrace.h | 3 +++
2 files changed, 8 insertions(+)

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 273a35851e42..bcbfe03e3835 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -725,8 +725,12 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry,
status = nfs_refresh_inode(d_inode(dentry), entry->fattr);
if (!status)
nfs_setsecurity(d_inode(dentry), entry->fattr);
+ trace_nfs_readdir_lookup_revalidate(d_inode(parent),
+ dentry, 0, status);
goto out;
} else {
+ trace_nfs_readdir_lookup_revalidate_failed(
+ d_inode(parent), dentry, 0);
d_invalidate(dentry);
dput(dentry);
dentry = NULL;
@@ -748,6 +752,7 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry,
dentry = alias;
}
nfs_set_verifier(dentry, dir_verifier);
+ trace_nfs_readdir_lookup(d_inode(parent), dentry, 0);
out:
dput(dentry);
}
diff --git a/fs/nfs/nfstrace.h b/fs/nfs/nfstrace.h
index c2d0543ecb2d..7c1102b991d0 100644
--- a/fs/nfs/nfstrace.h
+++ b/fs/nfs/nfstrace.h
@@ -432,6 +432,9 @@ DEFINE_NFS_LOOKUP_EVENT(nfs_lookup_enter);
DEFINE_NFS_LOOKUP_EVENT_DONE(nfs_lookup_exit);
DEFINE_NFS_LOOKUP_EVENT(nfs_lookup_revalidate_enter);
DEFINE_NFS_LOOKUP_EVENT_DONE(nfs_lookup_revalidate_exit);
+DEFINE_NFS_LOOKUP_EVENT(nfs_readdir_lookup);
+DEFINE_NFS_LOOKUP_EVENT(nfs_readdir_lookup_revalidate_failed);
+DEFINE_NFS_LOOKUP_EVENT_DONE(nfs_readdir_lookup_revalidate);

TRACE_EVENT(nfs_atomic_open_enter,
TP_PROTO(
--
2.35.1


2022-02-22 04:07:52

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH v6 13/13] NFS: Trace effects of the readdirplus heuristic

From: Trond Myklebust <[email protected]>

Enable tracking of when the readdirplus heuristic causes a page cache
invalidation.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/dir.c | 6 +++++-
fs/nfs/nfstrace.h | 50 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 55 insertions(+), 1 deletion(-)

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index bcbfe03e3835..9f48c75dbf4c 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -1141,7 +1141,11 @@ static void nfs_readdir_handle_cache_misses(struct inode *inode,
cache_misses <= NFS_READDIR_CACHE_MISS_THRESHOLD ||
!nfs_readdir_may_fill_pagecache(desc))
return;
- invalidate_mapping_pages(inode->i_mapping, page_index + 1, -1);
+ if (invalidate_mapping_pages(inode->i_mapping, page_index + 1, -1) == 0)
+ return;
+ trace_nfs_readdir_invalidate_cache_range(
+ inode, (loff_t)(page_index + 1) << PAGE_SHIFT,
+ MAX_LFS_FILESIZE);
}

/* The file offset position represents the dirent entry number. A
diff --git a/fs/nfs/nfstrace.h b/fs/nfs/nfstrace.h
index 7c1102b991d0..ec2645d20abf 100644
--- a/fs/nfs/nfstrace.h
+++ b/fs/nfs/nfstrace.h
@@ -273,6 +273,56 @@ DEFINE_NFS_UPDATE_SIZE_EVENT(wcc);
DEFINE_NFS_UPDATE_SIZE_EVENT(update);
DEFINE_NFS_UPDATE_SIZE_EVENT(grow);

+DECLARE_EVENT_CLASS(nfs_inode_range_event,
+ TP_PROTO(
+ const struct inode *inode,
+ loff_t range_start,
+ loff_t range_end
+ ),
+
+ TP_ARGS(inode, range_start, range_end),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(u32, fhandle)
+ __field(u64, fileid)
+ __field(u64, version)
+ __field(loff_t, range_start)
+ __field(loff_t, range_end)
+ ),
+
+ TP_fast_assign(
+ const struct nfs_inode *nfsi = NFS_I(inode);
+
+ __entry->dev = inode->i_sb->s_dev;
+ __entry->fhandle = nfs_fhandle_hash(&nfsi->fh);
+ __entry->fileid = nfsi->fileid;
+ __entry->version = inode_peek_iversion_raw(inode);
+ __entry->range_start = range_start;
+ __entry->range_end = range_end;
+ ),
+
+ TP_printk(
+ "fileid=%02x:%02x:%llu fhandle=0x%08x version=%llu "
+ "range=[%lld, %lld]",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ (unsigned long long)__entry->fileid,
+ __entry->fhandle, __entry->version,
+ __entry->range_start, __entry->range_end
+ )
+);
+
+#define DEFINE_NFS_INODE_RANGE_EVENT(name) \
+ DEFINE_EVENT(nfs_inode_range_event, name, \
+ TP_PROTO( \
+ const struct inode *inode, \
+ loff_t range_start, \
+ loff_t range_end \
+ ), \
+ TP_ARGS(inode, range_start, range_end))
+
+DEFINE_NFS_INODE_RANGE_EVENT(nfs_readdir_invalidate_cache_range);
+
DECLARE_EVENT_CLASS(nfs_readdir_event,
TP_PROTO(
const struct file *file,
--
2.35.1

2022-02-23 13:56:06

by Benjamin Coddington

[permalink] [raw]
Subject: [PATCH v3 1/8] NFS: save the directory's change attribute on pagecache pages

After a pagecache page has been filled with entries, set PagePrivate and
the directory's change attribute on the page. This will help us perform
per-page invalidations in a later patch.

Signed-off-by: Benjamin Coddington <[email protected]>
---
fs/nfs/dir.c | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 9f48c75dbf4c..79bdcedc0cad 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -54,6 +54,9 @@ static int nfs_closedir(struct inode *, struct file *);
static int nfs_readdir(struct file *, struct dir_context *);
static int nfs_fsync_dir(struct file *, loff_t, loff_t, int);
static loff_t nfs_llseek_dir(struct file *, loff_t, int);
+static void nfs_readdir_invalidatepage(struct page *,
+ unsigned int, unsigned int);
+static int nfs_readdir_clear_page(struct page*, gfp_t);
static void nfs_readdir_clear_array(struct page*);

const struct file_operations nfs_dir_operations = {
@@ -66,6 +69,8 @@ const struct file_operations nfs_dir_operations = {
};

const struct address_space_operations nfs_dir_aops = {
+ .invalidatepage = nfs_readdir_invalidatepage,
+ .releasepage = nfs_readdir_clear_page,
.freepage = nfs_readdir_clear_array,
};

@@ -212,6 +217,27 @@ static void nfs_readdir_page_init_array(struct page *page, u64 last_cookie)
array->last_cookie = last_cookie;
array->cookies_are_ordered = 1;
kunmap_atomic(array);
+ set_page_private(page, 0);
+}
+
+static int
+nfs_readdir_clear_page(struct page *page, gfp_t gfp_mask)
+{
+ detach_page_private(page);
+ return 1;
+}
+
+static void
+nfs_readdir_invalidatepage(struct page *page, unsigned int offset,
+ unsigned int length)
+{
+ nfs_readdir_clear_page(page, GFP_KERNEL);
+}
+
+static void
+nfs_readdir_set_page_verifier(struct page *page, unsigned long verf)
+{
+ attach_page_private(page, (void *)verf);
}

/*
@@ -794,6 +820,8 @@ static int nfs_readdir_page_filler(struct nfs_readdir_descriptor *desc,
if (status != -ENOSPC)
continue;

+ nfs_readdir_set_page_verifier(page, desc->dir_verifier);
+
if (page->mapping != mapping) {
if (!--narrays)
break;
@@ -822,10 +850,13 @@ static int nfs_readdir_page_filler(struct nfs_readdir_descriptor *desc,
case -EBADCOOKIE:
if (entry->eof) {
nfs_readdir_page_set_eof(page);
+ nfs_readdir_set_page_verifier(page, desc->dir_verifier);
status = 0;
}
break;
case -ENOSPC:
+ nfs_readdir_set_page_verifier(page, desc->dir_verifier);
+ fallthrough;
case -EAGAIN:
status = 0;
break;
--
2.31.1

2022-02-23 17:59:58

by Benjamin Coddington

[permalink] [raw]
Subject: [PATCH v3 2/8] NFSv4: Send GETATTR with READDIR

For each batch of entries, track whether the directory has changed. We can
use this information to better manage the cache when reading long
directories.

Signed-off-by: Benjamin Coddington <[email protected]>
---
fs/nfs/nfs42proc.c | 2 +-
fs/nfs/nfs4proc.c | 29 +++++++++++++++++++++--------
fs/nfs/nfs4xdr.c | 6 ++++++
include/linux/nfs_fs_sb.h | 5 +++++
include/linux/nfs_xdr.h | 2 ++
5 files changed, 35 insertions(+), 9 deletions(-)

diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c
index 882bf84484ac..3ab54228b2ed 100644
--- a/fs/nfs/nfs42proc.c
+++ b/fs/nfs/nfs42proc.c
@@ -1082,7 +1082,7 @@ static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f,
if (!res.dst_fattr)
return -ENOMEM;

- nfs4_bitmask_set(dst_bitmask, server->cache_consistency_bitmask,
+ nfs4_bitmask_set(dst_bitmask, server->cache_consistency_bitmask_nl,
dst_inode, NFS_INO_INVALID_BLOCKS);

status = nfs4_call_sync(server->client, server, msg,
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 73a9b6de666c..45285447c077 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -3665,7 +3665,7 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
/* Close-to-open cache consistency revalidation */
if (!nfs4_have_delegation(inode, FMODE_READ)) {
nfs4_bitmask_set(calldata->arg.bitmask_store,
- server->cache_consistency_bitmask,
+ server->cache_consistency_bitmask_nl,
inode, 0);
calldata->arg.bitmask = calldata->arg.bitmask_store;
} else
@@ -3905,7 +3905,12 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
memcpy(server->cache_consistency_bitmask, res.attr_bitmask, sizeof(server->cache_consistency_bitmask));
server->cache_consistency_bitmask[0] &= FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE;
server->cache_consistency_bitmask[1] &= FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY;
- server->cache_consistency_bitmask[2] = 0;
+ server->cache_consistency_bitmask[2] = res.attr_bitmask[2] & FATTR4_WORD2_SECURITY_LABEL;
+
+ memcpy(server->cache_consistency_bitmask_nl, server->cache_consistency_bitmask, sizeof(server->cache_consistency_bitmask));
+ server->cache_consistency_bitmask_nl[2] = 0;
+
+

/* Avoid a regression due to buggy server */
for (i = 0; i < ARRAY_SIZE(res.exclcreat_bitmask); i++)
@@ -4576,7 +4581,7 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry
res.fattr = nfs_alloc_fattr();
if (res.fattr == NULL)
return -ENOMEM;
- args.bitmask = server->cache_consistency_bitmask;
+ args.bitmask = server->cache_consistency_bitmask_nl;
}
status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
if (!status) {
@@ -5098,14 +5103,19 @@ static int _nfs4_proc_readdir(struct nfs_readdir_arg *nr_arg,
.rpc_resp = &res,
.rpc_cred = nr_arg->cred,
};
- int status;
+ int status = -ENOMEM;

dprintk("%s: dentry = %pd2, cookie = %llu\n", __func__,
nr_arg->dentry, (unsigned long long)nr_arg->cookie);
if (!(server->caps & NFS_CAP_SECURITY_LABEL))
- args.bitmask = server->attr_bitmask_nl;
+ args.bitmask = server->cache_consistency_bitmask_nl;
else
- args.bitmask = server->attr_bitmask;
+ args.bitmask = server->cache_consistency_bitmask;
+
+ res.dir_attr = nfs_alloc_fattr();
+ if (res.dir_attr == NULL)
+ goto out;
+ res.server = server;

nfs4_setup_readdir(nr_arg->cookie, nr_arg->verf, nr_arg->dentry, &args);
res.pgbase = args.pgbase;
@@ -5118,6 +5128,9 @@ static int _nfs4_proc_readdir(struct nfs_readdir_arg *nr_arg,

nfs_invalidate_atime(dir);

+ nfs_refresh_inode(dir, res.dir_attr);
+ nfs_free_fattr(res.dir_attr);
+out:
dprintk("%s: returns %d\n", __func__, status);
return status;
}
@@ -5583,7 +5596,7 @@ static void nfs4_proc_write_setup(struct nfs_pgio_header *hdr,
hdr->res.fattr = NULL;
} else {
nfs4_bitmask_set(hdr->args.bitmask_store,
- server->cache_consistency_bitmask,
+ server->cache_consistency_bitmask_nl,
hdr->inode, NFS_INO_INVALID_BLOCKS);
hdr->args.bitmask = hdr->args.bitmask_store;
}
@@ -6622,7 +6635,7 @@ static int _nfs4_proc_delegreturn(struct inode *inode, const struct cred *cred,
data->args.fhandle = &data->fh;
data->args.stateid = &data->stateid;
nfs4_bitmask_set(data->args.bitmask_store,
- server->cache_consistency_bitmask, inode, 0);
+ server->cache_consistency_bitmask_nl, inode, 0);
data->args.bitmask = data->args.bitmask_store;
nfs_copy_fh(&data->fh, NFS_FH(inode));
nfs4_stateid_copy(&data->stateid, stateid);
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index b7780b97dc4d..1cd0d49ef992 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -469,10 +469,12 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
#define NFS4_enc_readdir_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \
+ encode_getattr_maxsz + \
encode_readdir_maxsz)
#define NFS4_dec_readdir_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \
+ decode_getattr_maxsz + \
decode_readdir_maxsz)
#define NFS4_enc_write_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
@@ -2529,6 +2531,7 @@ static void nfs4_xdr_enc_readdir(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->fh, &hdr);
+ encode_getfattr(xdr, args->bitmask, &hdr);
encode_readdir(xdr, args, req, &hdr);

rpc_prepare_reply_pages(req, args->pages, args->pgbase,
@@ -6769,6 +6772,9 @@ static int nfs4_xdr_dec_readdir(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
if (status)
goto out;
status = decode_putfh(xdr);
+ if (status)
+ goto out;
+ status = decode_getfattr(xdr, res->dir_attr, res->server);
if (status)
goto out;
status = decode_readdir(xdr, rqstp, res);
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index ca0959e51e81..04bc827e4367 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -213,6 +213,11 @@ struct nfs_server {
of change attribute, size, ctime
and mtime attributes supported by
the server */
+ u32 cache_consistency_bitmask_nl[3];
+ /* V4 bitmask representing the subset
+ of change attribute, size, ctime
+ and mtime attributes supported by
+ the server excluding label support */
u32 acl_bitmask; /* V4 bitmask representing the ACEs
that are supported on this
filesystem */
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 728cb0c1f0b6..fbb8b7695c30 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -1139,6 +1139,8 @@ struct nfs4_readdir_res {
struct nfs4_sequence_res seq_res;
nfs4_verifier verifier;
unsigned int pgbase;
+ struct nfs_fattr *dir_attr;
+ const struct nfs_server *server;
};

struct nfs4_readlink {
--
2.31.1

2022-02-23 21:36:02

by Benjamin Coddington

[permalink] [raw]
Subject: [PATCH v3 3/8] NFS: Add a struct to track readdir pagecache location

Directory entries in the NFS readdir pagecache are referenced by their
cookie value and offset. By defining a structure to group these values,
we'll simplify changes to validate pagecache pages in patches that follow.

Signed-off-by: Benjamin Coddington <[email protected]>
---
fs/nfs/dir.c | 46 ++++++++++++++++++++----------------------
include/linux/nfs_fs.h | 6 ++++++
2 files changed, 28 insertions(+), 24 deletions(-)

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 79bdcedc0cad..009187c0ae0f 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -158,11 +158,10 @@ struct nfs_readdir_descriptor {
struct file *file;
struct page *page;
struct dir_context *ctx;
- pgoff_t page_index;
pgoff_t page_index_max;
u64 dir_cookie;
- u64 last_cookie;
u64 dup_cookie;
+ struct nfs_dir_page_cursor pgc;
loff_t current_index;
loff_t prev_index;

@@ -172,7 +171,6 @@ struct nfs_readdir_descriptor {
unsigned long gencount;
unsigned long attr_gencount;
unsigned int page_fill_misses;
- unsigned int cache_entry_index;
unsigned int buffer_fills;
unsigned int dtsize;
signed char duped;
@@ -457,7 +455,7 @@ static int nfs_readdir_search_for_pos(struct nfs_cache_array *array,

index = (unsigned int)diff;
desc->dir_cookie = array->array[index].cookie;
- desc->cache_entry_index = index;
+ desc->pgc.entry_index = index;
return 0;
out_eof:
desc->eof = true;
@@ -526,7 +524,7 @@ static int nfs_readdir_search_for_cookie(struct nfs_cache_array *array,
else
desc->ctx->pos = new_pos;
desc->prev_index = new_pos;
- desc->cache_entry_index = i;
+ desc->pgc.entry_index = i;
return 0;
}
}
@@ -553,9 +551,9 @@ static int nfs_readdir_search_array(struct nfs_readdir_descriptor *desc)
status = nfs_readdir_search_for_cookie(array, desc);

if (status == -EAGAIN) {
- desc->last_cookie = array->last_cookie;
+ desc->pgc.index_cookie = array->last_cookie;
desc->current_index += array->size;
- desc->page_index++;
+ desc->pgc.page_index++;
}
kunmap_atomic(array);
return status;
@@ -968,8 +966,8 @@ static struct page *
nfs_readdir_page_get_cached(struct nfs_readdir_descriptor *desc)
{
return nfs_readdir_page_get_locked(desc->file->f_mapping,
- desc->page_index,
- desc->last_cookie);
+ desc->pgc.page_index,
+ desc->pgc.index_cookie);
}

#define NFS_READDIR_PAGE_FILL_MISS_MAX 5
@@ -1001,10 +999,10 @@ static int find_and_lock_cache_page(struct nfs_readdir_descriptor *desc)
if (nfs_readdir_page_needs_filling(desc->page)) {
if (!nfs_readdir_may_fill_pagecache(desc))
return -EBADCOOKIE;
- desc->page_index_max = desc->page_index;
+ desc->page_index_max = desc->pgc.page_index;
trace_nfs_readdir_cache_fill(desc->file, nfsi->cookieverf,
- desc->last_cookie,
- desc->page_index, desc->dtsize);
+ desc->pgc.index_cookie,
+ desc->pgc.page_index, desc->dtsize);
res = nfs_readdir_xdr_to_array(desc, nfsi->cookieverf, verf,
&desc->page, 1);
if (res < 0) {
@@ -1012,7 +1010,7 @@ static int find_and_lock_cache_page(struct nfs_readdir_descriptor *desc)
trace_nfs_readdir_cache_fill_done(inode, res);
if (res == -EBADCOOKIE || res == -ENOTSYNC) {
invalidate_inode_pages2(desc->file->f_mapping);
- desc->page_index = 0;
+ desc->pgc.page_index = 0;
return -EAGAIN;
}
return res;
@@ -1020,7 +1018,7 @@ static int find_and_lock_cache_page(struct nfs_readdir_descriptor *desc)
/*
* Set the cookie verifier if the page cache was empty
*/
- if (desc->page_index == 0)
+ if (desc->pgc.page_index == 0)
memcpy(nfsi->cookieverf, verf,
sizeof(nfsi->cookieverf));
desc->page_fill_misses++;
@@ -1040,10 +1038,10 @@ static int readdir_search_pagecache(struct nfs_readdir_descriptor *desc)
int res;

do {
- if (desc->page_index == 0) {
+ if (desc->pgc.page_index == 0) {
desc->current_index = 0;
desc->prev_index = 0;
- desc->last_cookie = 0;
+ desc->pgc.index_cookie = 0;
}
res = find_and_lock_cache_page(desc);
} while (res == -EAGAIN);
@@ -1061,7 +1059,7 @@ static void nfs_do_filldir(struct nfs_readdir_descriptor *desc,
unsigned int i = 0;

array = kmap(desc->page);
- for (i = desc->cache_entry_index; i < array->size; i++) {
+ for (i = desc->pgc.entry_index; i < array->size; i++) {
struct nfs_cache_array_entry *ent;

ent = &array->array[i];
@@ -1119,13 +1117,13 @@ static int uncached_readdir(struct nfs_readdir_descriptor *desc)
if (!arrays[0])
goto out;

- desc->page_index = 0;
- desc->cache_entry_index = 0;
- desc->last_cookie = desc->dir_cookie;
+ desc->pgc.page_index = 0;
+ desc->pgc.entry_index = 0;
+ desc->pgc.index_cookie = desc->dir_cookie;
desc->duped = 0;
desc->page_index_max = 0;

- trace_nfs_readdir_uncached(desc->file, desc->verf, desc->last_cookie,
+ trace_nfs_readdir_uncached(desc->file, desc->verf, desc->pgc.index_cookie,
-1, desc->dtsize);

status = nfs_readdir_xdr_to_array(desc, desc->verf, verf, arrays, sz);
@@ -1258,7 +1256,7 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
}
if (res == -ETOOSMALL && desc->plus) {
nfs_zap_caches(inode);
- desc->page_index = 0;
+ desc->pgc.page_index = 0;
desc->plus = false;
desc->eof = false;
continue;
@@ -1271,7 +1269,7 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
if (desc->eob || desc->eof)
break;
/* Grow the dtsize if we have to go back for more pages */
- if (desc->page_index == desc->page_index_max)
+ if (desc->pgc.page_index == desc->page_index_max)
nfs_grow_dtsize(desc);
} while (!desc->eob && !desc->eof);

@@ -1280,7 +1278,7 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
dir_ctx->dup_cookie = desc->dup_cookie;
dir_ctx->duped = desc->duped;
dir_ctx->attr_gencount = desc->attr_gencount;
- dir_ctx->page_index = desc->page_index;
+ dir_ctx->page_index = desc->pgc.page_index;
dir_ctx->page_fill_misses = desc->page_fill_misses;
dir_ctx->eof = desc->eof;
dir_ctx->dtsize = desc->dtsize;
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 0a5425a58bbd..2f5ded282477 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -99,6 +99,12 @@ struct nfs_open_context {
struct rcu_head rcu_head;
};

+struct nfs_dir_page_cursor {
+ __u64 index_cookie;
+ pgoff_t page_index;
+ unsigned int entry_index;
+};
+
struct nfs_open_dir_context {
struct list_head list;
atomic_t cache_hits;
--
2.31.1

2022-02-24 00:44:05

by Benjamin Coddington

[permalink] [raw]
Subject: [PATCH v3 4/8] NFS: Keep the readdir pagecache cursor updated

Whenever we successfully locate our dir_cookie within the pagecache, or
finish emitting entries to userspace, update the pagecache cursor. These
updates provide marker points to validate pagecache pages in a future
patch.

Signed-off-by: Benjamin Coddington <[email protected]>
---
fs/nfs/dir.c | 32 ++++++++++++++++++++++++++------
1 file changed, 26 insertions(+), 6 deletions(-)

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 009187c0ae0f..2b1a0c1cdce4 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -154,6 +154,10 @@ struct nfs_cache_array {
struct nfs_cache_array_entry array[];
};

+static const int cache_entries_per_page =
+ (PAGE_SIZE - sizeof(struct nfs_cache_array)) /
+ sizeof(struct nfs_cache_array_entry);
+
struct nfs_readdir_descriptor {
struct file *file;
struct page *page;
@@ -282,6 +286,21 @@ static bool nfs_readdir_array_is_full(struct nfs_cache_array *array)
return array->page_full;
}

+static void nfs_readdir_set_cursor(struct nfs_readdir_descriptor *desc, int index)
+{
+ desc->pgc.entry_index = index;
+ desc->pgc.index_cookie = desc->dir_cookie;
+}
+
+static void nfs_readdir_cursor_next(struct nfs_dir_page_cursor *pgc, u64 cookie)
+{
+ pgc->index_cookie = cookie;
+ if (++pgc->entry_index == cache_entries_per_page) {
+ pgc->entry_index = 0;
+ pgc->page_index++;
+ }
+}
+
/*
* the caller is responsible for freeing qstr.name
* when called by nfs_readdir_add_to_array, the strings will be freed in
@@ -455,7 +474,7 @@ static int nfs_readdir_search_for_pos(struct nfs_cache_array *array,

index = (unsigned int)diff;
desc->dir_cookie = array->array[index].cookie;
- desc->pgc.entry_index = index;
+ nfs_readdir_set_cursor(desc, index);
return 0;
out_eof:
desc->eof = true;
@@ -524,7 +543,7 @@ static int nfs_readdir_search_for_cookie(struct nfs_cache_array *array,
else
desc->ctx->pos = new_pos;
desc->prev_index = new_pos;
- desc->pgc.entry_index = i;
+ nfs_readdir_set_cursor(desc, i);
return 0;
}
}
@@ -551,9 +570,9 @@ static int nfs_readdir_search_array(struct nfs_readdir_descriptor *desc)
status = nfs_readdir_search_for_cookie(array, desc);

if (status == -EAGAIN) {
- desc->pgc.index_cookie = array->last_cookie;
+ desc->pgc.entry_index = array->size - 1;
+ nfs_readdir_cursor_next(&desc->pgc, array->last_cookie);
desc->current_index += array->size;
- desc->pgc.page_index++;
}
kunmap_atomic(array);
return status;
@@ -1084,6 +1103,8 @@ static void nfs_do_filldir(struct nfs_readdir_descriptor *desc,
desc->eof = !desc->eob;

kunmap(desc->page);
+ desc->pgc.entry_index = i-1;
+ nfs_readdir_cursor_next(&desc->pgc, desc->dir_cookie);
dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %llu\n",
(unsigned long long)desc->dir_cookie);
}
@@ -1118,8 +1139,7 @@ static int uncached_readdir(struct nfs_readdir_descriptor *desc)
goto out;

desc->pgc.page_index = 0;
- desc->pgc.entry_index = 0;
- desc->pgc.index_cookie = desc->dir_cookie;
+ nfs_readdir_set_cursor(desc, 0);
desc->duped = 0;
desc->page_index_max = 0;

--
2.31.1