From: Trond Myklebust <[email protected]>
Avoid clearing the entire readdir page cache if we're just doing forced
readdirplus for the 'ls -l' heuristic.
Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/dir.c | 56 +++++++++++++++++++++++++++++-------------
fs/nfs/nfstrace.h | 1 +
include/linux/nfs_fs.h | 1 +
3 files changed, 41 insertions(+), 17 deletions(-)
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 707ad0fd5a4e..68b0f19053ac 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -170,6 +170,7 @@ struct nfs_readdir_descriptor {
unsigned int cache_entry_index;
unsigned int buffer_fills;
unsigned int dtsize;
+ bool clear_cache;
bool plus;
bool eob;
bool eof;
@@ -227,6 +228,13 @@ static void nfs_readdir_clear_array(struct page *page)
kunmap_atomic(array);
}
+static void nfs_readdir_page_reinit_array(struct page *page, u64 last_cookie,
+ u64 change_attr)
+{
+ nfs_readdir_clear_array(page);
+ nfs_readdir_page_init_array(page, last_cookie, change_attr);
+}
+
static struct page *
nfs_readdir_page_array_alloc(u64 last_cookie, gfp_t gfp_flags)
{
@@ -428,12 +436,11 @@ static struct page *nfs_readdir_page_get_next(struct address_space *mapping,
struct page *page;
page = nfs_readdir_page_get_locked(mapping, cookie, change_attr);
- if (page) {
- if (nfs_readdir_page_last_cookie(page) == cookie)
- return page;
- nfs_readdir_page_unlock_and_put(page);
- }
- return NULL;
+ if (!page)
+ return NULL;
+ if (nfs_readdir_page_last_cookie(page) != cookie)
+ nfs_readdir_page_reinit_array(page, cookie, change_attr);
+ return page;
}
static inline
@@ -958,9 +965,15 @@ nfs_readdir_page_get_cached(struct nfs_readdir_descriptor *desc)
{
struct address_space *mapping = desc->file->f_mapping;
u64 change_attr = inode_peek_iversion_raw(mapping->host);
+ u64 cookie = desc->last_cookie;
+ struct page *page;
- return nfs_readdir_page_get_locked(mapping, desc->last_cookie,
- change_attr);
+ page = nfs_readdir_page_get_locked(mapping, cookie, change_attr);
+ if (!page)
+ return NULL;
+ if (desc->clear_cache && !nfs_readdir_page_needs_filling(page))
+ nfs_readdir_page_reinit_array(page, cookie, change_attr);
+ return page;
}
/*
@@ -1011,6 +1024,7 @@ static int find_and_lock_cache_page(struct nfs_readdir_descriptor *desc)
trace_nfs_readdir_invalidate_cache_range(
inode, 1, MAX_LFS_FILESIZE);
}
+ desc->clear_cache = false;
}
res = nfs_readdir_search_array(desc);
if (res == 0)
@@ -1145,16 +1159,17 @@ static int uncached_readdir(struct nfs_readdir_descriptor *desc)
#define NFS_READDIR_CACHE_MISS_THRESHOLD (16UL)
-static void nfs_readdir_handle_cache_misses(struct inode *inode,
+static bool nfs_readdir_handle_cache_misses(struct inode *inode,
struct nfs_readdir_descriptor *desc,
- unsigned int cache_misses)
+ unsigned int cache_misses,
+ bool force_clear)
{
- if (desc->ctx->pos == 0 ||
- cache_misses <= NFS_READDIR_CACHE_MISS_THRESHOLD)
- return;
- if (invalidate_mapping_pages(inode->i_mapping, 0, -1) == 0)
- return;
- trace_nfs_readdir_invalidate_cache_range(inode, 0, MAX_LFS_FILESIZE);
+ if (desc->ctx->pos == 0 || !desc->plus)
+ return false;
+ if (cache_misses <= NFS_READDIR_CACHE_MISS_THRESHOLD && !force_clear)
+ return false;
+ trace_nfs_readdir_force_readdirplus(inode);
+ return true;
}
/* The file offset position represents the dirent entry number. A
@@ -1169,6 +1184,7 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
struct nfs_open_dir_context *dir_ctx = file->private_data;
struct nfs_readdir_descriptor *desc;
unsigned int cache_hits, cache_misses;
+ bool force_clear;
int res;
dfprintk(FILE, "NFS: readdir(%pD2) starting at cookie %llu\n",
@@ -1201,6 +1217,7 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
memcpy(desc->verf, dir_ctx->verf, sizeof(desc->verf));
cache_hits = atomic_xchg(&dir_ctx->cache_hits, 0);
cache_misses = atomic_xchg(&dir_ctx->cache_misses, 0);
+ force_clear = dir_ctx->force_clear;
spin_unlock(&file->f_lock);
if (desc->eof) {
@@ -1209,7 +1226,9 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
}
desc->plus = nfs_use_readdirplus(inode, ctx, cache_hits, cache_misses);
- nfs_readdir_handle_cache_misses(inode, desc, cache_misses);
+ force_clear = nfs_readdir_handle_cache_misses(inode, desc, cache_misses,
+ force_clear);
+ desc->clear_cache = force_clear;
do {
res = readdir_search_pagecache(desc);
@@ -1238,6 +1257,8 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
nfs_do_filldir(desc, nfsi->cookieverf);
nfs_readdir_page_unlock_and_put_cached(desc);
+ if (desc->page_index == desc->page_index_max)
+ desc->clear_cache = force_clear;
} while (!desc->eob && !desc->eof);
spin_lock(&file->f_lock);
@@ -1245,6 +1266,7 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
dir_ctx->last_cookie = desc->last_cookie;
dir_ctx->attr_gencount = desc->attr_gencount;
dir_ctx->page_index = desc->page_index;
+ dir_ctx->force_clear = force_clear;
dir_ctx->eof = desc->eof;
dir_ctx->dtsize = desc->dtsize;
memcpy(dir_ctx->verf, desc->verf, sizeof(dir_ctx->verf));
diff --git a/fs/nfs/nfstrace.h b/fs/nfs/nfstrace.h
index ec2645d20abf..59f4ca803fd0 100644
--- a/fs/nfs/nfstrace.h
+++ b/fs/nfs/nfstrace.h
@@ -160,6 +160,7 @@ DEFINE_NFS_INODE_EVENT(nfs_fsync_enter);
DEFINE_NFS_INODE_EVENT_DONE(nfs_fsync_exit);
DEFINE_NFS_INODE_EVENT(nfs_access_enter);
DEFINE_NFS_INODE_EVENT_DONE(nfs_set_cache_invalid);
+DEFINE_NFS_INODE_EVENT(nfs_readdir_force_readdirplus);
DEFINE_NFS_INODE_EVENT_DONE(nfs_readdir_cache_fill_done);
DEFINE_NFS_INODE_EVENT_DONE(nfs_readdir_uncached_done);
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 42aad886d3c0..3893386ceaed 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -109,6 +109,7 @@ struct nfs_open_dir_context {
__u64 last_cookie;
pgoff_t page_index;
unsigned int dtsize;
+ bool force_clear;
bool eof;
struct rcu_head rcu_head;
};
--
2.35.1
From: Trond Myklebust <[email protected]>
Now that the directory page cache entries police themselves, don't
bother with marking the page cache for invalidation.
Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/dir.c | 5 -----
fs/nfs/inode.c | 9 +++------
fs/nfs/nfs4proc.c | 2 --
include/linux/nfs_fs.h | 2 --
4 files changed, 3 insertions(+), 15 deletions(-)
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 68b0f19053ac..5a2c98b2cc15 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -83,11 +83,6 @@ alloc_nfs_open_dir_context(struct inode *dir)
ctx->attr_gencount = nfsi->attr_gencount;
ctx->dtsize = NFS_INIT_DTSIZE;
spin_lock(&dir->i_lock);
- if (list_empty(&nfsi->open_files) &&
- (nfsi->cache_validity & NFS_INO_DATA_INVAL_DEFER))
- nfs_set_cache_invalid(dir,
- NFS_INO_INVALID_DATA |
- NFS_INO_REVAL_FORCED);
list_add_tail_rcu(&ctx->list, &nfsi->open_files);
memcpy(ctx->verf, nfsi->cookieverf, sizeof(ctx->verf));
spin_unlock(&dir->i_lock);
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 10d17cfb8639..43af1b6de5a6 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -210,6 +210,8 @@ void nfs_set_cache_invalid(struct inode *inode, unsigned long flags)
if (flags & NFS_INO_INVALID_DATA)
nfs_fscache_invalidate(inode, 0);
flags &= ~NFS_INO_REVAL_FORCED;
+ if (S_ISDIR(inode->i_mode))
+ flags &= ~(NFS_INO_INVALID_DATA | NFS_INO_DATA_INVAL_DEFER);
nfsi->cache_validity |= flags;
@@ -1429,10 +1431,7 @@ static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr)
&& (fattr->valid & NFS_ATTR_FATTR_CHANGE)
&& inode_eq_iversion_raw(inode, fattr->pre_change_attr)) {
inode_set_iversion_raw(inode, fattr->change_attr);
- if (S_ISDIR(inode->i_mode))
- nfs_set_cache_invalid(inode, NFS_INO_INVALID_DATA);
- else if (nfs_server_capable(inode, NFS_CAP_XATTR))
- nfs_set_cache_invalid(inode, NFS_INO_INVALID_XATTR);
+ nfs_set_cache_invalid(inode, NFS_INO_INVALID_XATTR);
}
/* If we have atomic WCC data, we may update some attributes */
ts = inode->i_ctime;
@@ -1851,8 +1850,6 @@ EXPORT_SYMBOL_GPL(nfs_refresh_inode);
static int nfs_post_op_update_inode_locked(struct inode *inode,
struct nfs_fattr *fattr, unsigned int invalid)
{
- if (S_ISDIR(inode->i_mode))
- invalid |= NFS_INO_INVALID_DATA;
nfs_set_cache_invalid(inode, invalid);
if ((fattr->valid & NFS_ATTR_FATTR) == 0)
return 0;
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 8b875355824b..f1aa6b3c8523 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -1206,8 +1206,6 @@ nfs4_update_changeattr_locked(struct inode *inode,
u64 change_attr = inode_peek_iversion_raw(inode);
cache_validity |= NFS_INO_INVALID_CTIME | NFS_INO_INVALID_MTIME;
- if (S_ISDIR(inode->i_mode))
- cache_validity |= NFS_INO_INVALID_DATA;
switch (NFS_SERVER(inode)->change_attr_type) {
case NFS4_CHANGE_TYPE_IS_UNDEFINED:
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 3893386ceaed..72f42b1d0d3c 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -360,8 +360,6 @@ static inline void nfs_mark_for_revalidate(struct inode *inode)
nfsi->cache_validity |= NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL |
NFS_INO_INVALID_CHANGE | NFS_INO_INVALID_CTIME |
NFS_INO_INVALID_SIZE;
- if (S_ISDIR(inode->i_mode))
- nfsi->cache_validity |= NFS_INO_INVALID_DATA;
spin_unlock(&inode->i_lock);
}
--
2.35.1