NFS: Use the 64-bit server readdir cookies when possible
When we're running as a 64-bit architecture and are not running in 32-bit compatibility mode, it is better to use the 64-bit readdir cookies that supplied by the server. Doing so improves the accuracy of telldir()/seekdir(), particularly when the directory is changing, for instance, when doing 'rm -rf'. We still fall back to using the 32-bit offsets on 32-bit architectures and when in compatibility mode. Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
This commit is contained in:
61
fs/nfs/dir.c
61
fs/nfs/dir.c
@@ -144,7 +144,6 @@ struct nfs_cache_array {
|
|||||||
struct nfs_cache_array_entry array[0];
|
struct nfs_cache_array_entry array[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef int (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, bool);
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
struct file *file;
|
struct file *file;
|
||||||
struct page *page;
|
struct page *page;
|
||||||
@@ -153,7 +152,7 @@ typedef struct {
|
|||||||
u64 *dir_cookie;
|
u64 *dir_cookie;
|
||||||
u64 last_cookie;
|
u64 last_cookie;
|
||||||
loff_t current_index;
|
loff_t current_index;
|
||||||
decode_dirent_t decode;
|
loff_t prev_index;
|
||||||
|
|
||||||
unsigned long dir_verifier;
|
unsigned long dir_verifier;
|
||||||
unsigned long timestamp;
|
unsigned long timestamp;
|
||||||
@@ -240,6 +239,25 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
int is_32bit_api(void)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
return in_compat_syscall();
|
||||||
|
#else
|
||||||
|
return (BITS_PER_LONG == 32);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
bool nfs_readdir_use_cookie(const struct file *filp)
|
||||||
|
{
|
||||||
|
if ((filp->f_mode & FMODE_32BITHASH) ||
|
||||||
|
(!(filp->f_mode & FMODE_64BITHASH) && is_32bit_api()))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descriptor_t *desc)
|
int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descriptor_t *desc)
|
||||||
{
|
{
|
||||||
@@ -289,7 +307,7 @@ int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_des
|
|||||||
!nfs_readdir_inode_mapping_valid(nfsi)) {
|
!nfs_readdir_inode_mapping_valid(nfsi)) {
|
||||||
ctx->duped = 0;
|
ctx->duped = 0;
|
||||||
ctx->attr_gencount = nfsi->attr_gencount;
|
ctx->attr_gencount = nfsi->attr_gencount;
|
||||||
} else if (new_pos < desc->ctx->pos) {
|
} else if (new_pos < desc->prev_index) {
|
||||||
if (ctx->duped > 0
|
if (ctx->duped > 0
|
||||||
&& ctx->dup_cookie == *desc->dir_cookie) {
|
&& ctx->dup_cookie == *desc->dir_cookie) {
|
||||||
if (printk_ratelimit()) {
|
if (printk_ratelimit()) {
|
||||||
@@ -305,7 +323,11 @@ int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_des
|
|||||||
ctx->dup_cookie = *desc->dir_cookie;
|
ctx->dup_cookie = *desc->dir_cookie;
|
||||||
ctx->duped = -1;
|
ctx->duped = -1;
|
||||||
}
|
}
|
||||||
desc->ctx->pos = new_pos;
|
if (nfs_readdir_use_cookie(desc->file))
|
||||||
|
desc->ctx->pos = *desc->dir_cookie;
|
||||||
|
else
|
||||||
|
desc->ctx->pos = new_pos;
|
||||||
|
desc->prev_index = new_pos;
|
||||||
desc->cache_entry_index = i;
|
desc->cache_entry_index = i;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -376,9 +398,10 @@ error:
|
|||||||
static int xdr_decode(nfs_readdir_descriptor_t *desc,
|
static int xdr_decode(nfs_readdir_descriptor_t *desc,
|
||||||
struct nfs_entry *entry, struct xdr_stream *xdr)
|
struct nfs_entry *entry, struct xdr_stream *xdr)
|
||||||
{
|
{
|
||||||
|
struct inode *inode = file_inode(desc->file);
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
error = desc->decode(xdr, entry, desc->plus);
|
error = NFS_PROTO(inode)->decode_dirent(xdr, entry, desc->plus);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
entry->fattr->time_start = desc->timestamp;
|
entry->fattr->time_start = desc->timestamp;
|
||||||
@@ -756,6 +779,7 @@ int readdir_search_pagecache(nfs_readdir_descriptor_t *desc)
|
|||||||
|
|
||||||
if (desc->page_index == 0) {
|
if (desc->page_index == 0) {
|
||||||
desc->current_index = 0;
|
desc->current_index = 0;
|
||||||
|
desc->prev_index = 0;
|
||||||
desc->last_cookie = 0;
|
desc->last_cookie = 0;
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
@@ -786,11 +810,14 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc)
|
|||||||
desc->eof = true;
|
desc->eof = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
desc->ctx->pos++;
|
|
||||||
if (i < (array->size-1))
|
if (i < (array->size-1))
|
||||||
*desc->dir_cookie = array->array[i+1].cookie;
|
*desc->dir_cookie = array->array[i+1].cookie;
|
||||||
else
|
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;
|
||||||
|
else
|
||||||
|
desc->ctx->pos++;
|
||||||
if (ctx->duped != 0)
|
if (ctx->duped != 0)
|
||||||
ctx->duped = 1;
|
ctx->duped = 1;
|
||||||
}
|
}
|
||||||
@@ -860,9 +887,14 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
|
|||||||
{
|
{
|
||||||
struct dentry *dentry = file_dentry(file);
|
struct dentry *dentry = file_dentry(file);
|
||||||
struct inode *inode = d_inode(dentry);
|
struct inode *inode = d_inode(dentry);
|
||||||
nfs_readdir_descriptor_t my_desc,
|
|
||||||
*desc = &my_desc;
|
|
||||||
struct nfs_open_dir_context *dir_ctx = file->private_data;
|
struct nfs_open_dir_context *dir_ctx = file->private_data;
|
||||||
|
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;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
|
||||||
dfprintk(FILE, "NFS: readdir(%pD2) starting at cookie %llu\n",
|
dfprintk(FILE, "NFS: readdir(%pD2) starting at cookie %llu\n",
|
||||||
@@ -875,14 +907,6 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
|
|||||||
* to either find the entry with the appropriate number or
|
* to either find the entry with the appropriate number or
|
||||||
* revalidate the cookie.
|
* revalidate the cookie.
|
||||||
*/
|
*/
|
||||||
memset(desc, 0, sizeof(*desc));
|
|
||||||
|
|
||||||
desc->file = file;
|
|
||||||
desc->ctx = ctx;
|
|
||||||
desc->dir_cookie = &dir_ctx->dir_cookie;
|
|
||||||
desc->decode = NFS_PROTO(inode)->decode_dirent;
|
|
||||||
desc->plus = nfs_use_readdirplus(inode, ctx);
|
|
||||||
|
|
||||||
if (ctx->pos == 0 || nfs_attribute_cache_expired(inode))
|
if (ctx->pos == 0 || nfs_attribute_cache_expired(inode))
|
||||||
res = nfs_revalidate_mapping(inode, file->f_mapping);
|
res = nfs_revalidate_mapping(inode, file->f_mapping);
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
@@ -954,7 +978,10 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence)
|
|||||||
}
|
}
|
||||||
if (offset != filp->f_pos) {
|
if (offset != filp->f_pos) {
|
||||||
filp->f_pos = offset;
|
filp->f_pos = offset;
|
||||||
dir_ctx->dir_cookie = 0;
|
if (nfs_readdir_use_cookie(filp))
|
||||||
|
dir_ctx->dir_cookie = offset;
|
||||||
|
else
|
||||||
|
dir_ctx->dir_cookie = 0;
|
||||||
dir_ctx->duped = 0;
|
dir_ctx->duped = 0;
|
||||||
}
|
}
|
||||||
inode_unlock(inode);
|
inode_unlock(inode);
|
||||||
|
|||||||
Reference in New Issue
Block a user