Commit 59e356a9 authored by Trond Myklebust's avatar Trond Myklebust
Browse files

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: default avatarTrond Myklebust <trond.myklebust@hammerspace.com>
parent fb33c651
Loading
Loading
Loading
Loading
+44 −17
Original line number Diff line number Diff line
@@ -144,7 +144,6 @@ struct nfs_cache_array {
	struct nfs_cache_array_entry array[0];
};

typedef int (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, bool);
typedef struct {
	struct file	*file;
	struct page	*page;
@@ -153,7 +152,7 @@ typedef struct {
	u64		*dir_cookie;
	u64		last_cookie;
	loff_t		current_index;
	decode_dirent_t	decode;
	loff_t		prev_index;

	unsigned long	dir_verifier;
	unsigned long	timestamp;
@@ -240,6 +239,25 @@ out:
	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
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)) {
				ctx->duped = 0;
				ctx->attr_gencount = nfsi->attr_gencount;
			} else if (new_pos < desc->ctx->pos) {
			} else if (new_pos < desc->prev_index) {
				if (ctx->duped > 0
				    && ctx->dup_cookie == *desc->dir_cookie) {
					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->duped = -1;
			}
			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;
			return 0;
		}
@@ -376,9 +398,10 @@ error:
static int xdr_decode(nfs_readdir_descriptor_t *desc,
		      struct nfs_entry *entry, struct xdr_stream *xdr)
{
	struct inode *inode = file_inode(desc->file);
	int error;

	error = desc->decode(xdr, entry, desc->plus);
	error = NFS_PROTO(inode)->decode_dirent(xdr, entry, desc->plus);
	if (error)
		return error;
	entry->fattr->time_start = desc->timestamp;
@@ -756,6 +779,7 @@ int readdir_search_pagecache(nfs_readdir_descriptor_t *desc)

	if (desc->page_index == 0) {
		desc->current_index = 0;
		desc->prev_index = 0;
		desc->last_cookie = 0;
	}
	do {
@@ -786,11 +810,14 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc)
			desc->eof = true;
			break;
		}
		desc->ctx->pos++;
		if (i < (array->size-1))
			*desc->dir_cookie = array->array[i+1].cookie;
		else
			*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)
			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 inode	*inode = d_inode(dentry);
	nfs_readdir_descriptor_t my_desc,
			*desc = &my_desc;
	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;

	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
	 * 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))
		res = nfs_revalidate_mapping(inode, file->f_mapping);
	if (res < 0)
@@ -954,6 +978,9 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence)
	}
	if (offset != filp->f_pos) {
		filp->f_pos = offset;
		if (nfs_readdir_use_cookie(filp))
			dir_ctx->dir_cookie = offset;
		else
			dir_ctx->dir_cookie = 0;
		dir_ctx->duped = 0;
	}