Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9ac3d3e8 authored by Al Viro's avatar Al Viro
Browse files

nfs: switch to ->iterate_shared()



aside of the usual care about seeding dcache from readdir, we need
to be careful about the pagecache evictions here.

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 9cf843e3
Loading
Loading
Loading
Loading
+43 −28
Original line number Diff line number Diff line
@@ -57,7 +57,7 @@ static void nfs_readdir_clear_array(struct page*);
const struct file_operations nfs_dir_operations = {
	.llseek		= nfs_llseek_dir,
	.read		= generic_read_dir,
	.iterate	= nfs_readdir,
	.iterate_shared	= nfs_readdir,
	.open		= nfs_opendir,
	.release	= nfs_closedir,
	.fsync		= nfs_fsync_dir,
@@ -145,6 +145,7 @@ struct nfs_cache_array_entry {
};

struct nfs_cache_array {
	atomic_t refcount;
	int size;
	int eof_index;
	u64 last_cookie;
@@ -200,11 +201,20 @@ void nfs_readdir_clear_array(struct page *page)
	int i;

	array = kmap_atomic(page);
	if (atomic_dec_and_test(&array->refcount))
		for (i = 0; i < array->size; i++)
			kfree(array->array[i].string.name);
	kunmap_atomic(array);
}

static bool grab_page(struct page *page)
{
	struct nfs_cache_array *array = kmap_atomic(page);
	bool res = atomic_inc_not_zero(&array->refcount);
	kunmap_atomic(array);
	return res;
}

/*
 * the caller is responsible for freeing qstr.name
 * when called by nfs_readdir_add_to_array, the strings will be freed in
@@ -470,6 +480,7 @@ static
void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
{
	struct qstr filename = QSTR_INIT(entry->name, entry->len);
	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
	struct dentry *dentry;
	struct dentry *alias;
	struct inode *dir = d_inode(parent);
@@ -489,7 +500,13 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
	filename.hash = full_name_hash(filename.name, filename.len);

	dentry = d_lookup(parent, &filename);
	if (dentry != NULL) {
again:
	if (!dentry) {
		dentry = d_alloc_parallel(parent, &filename, &wq);
		if (IS_ERR(dentry))
			return;
	}
	if (!d_in_lookup(dentry)) {
		/* Is there a mountpoint here? If so, just exit */
		if (!nfs_fsid_equal(&NFS_SB(dentry->d_sb)->fsid,
					&entry->fattr->fsid))
@@ -503,26 +520,21 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
		} else {
			d_invalidate(dentry);
			dput(dentry);
			dentry = NULL;
			goto again;
		}
	}

	dentry = d_alloc(parent, &filename);
	if (dentry == NULL)
		return;

	inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr, entry->label);
	if (IS_ERR(inode))
		goto out;

	alias = d_splice_alias(inode, dentry);
	d_lookup_done(dentry);
	if (alias) {
		if (IS_ERR(alias))
			goto out;
	else if (alias) {
		nfs_set_verifier(alias, nfs_save_change_attribute(dir));
		dput(alias);
	} else
		dput(dentry);
		dentry = alias;
	}
	nfs_set_verifier(dentry, nfs_save_change_attribute(dir));

out:
	dput(dentry);
}
@@ -643,6 +655,7 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
		goto out_label_free;
	}
	memset(array, 0, sizeof(struct nfs_cache_array));
	atomic_set(&array->refcount, 1);
	array->eof_index = -1;

	status = nfs_readdir_alloc_pages(pages, array_size);
@@ -705,7 +718,6 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page)
static
void cache_page_release(nfs_readdir_descriptor_t *desc)
{
	if (!desc->page->mapping)
	nfs_readdir_clear_array(desc->page);
	put_page(desc->page);
	desc->page = NULL;
@@ -714,8 +726,16 @@ void cache_page_release(nfs_readdir_descriptor_t *desc)
static
struct page *get_cache_page(nfs_readdir_descriptor_t *desc)
{
	return read_cache_page(file_inode(desc->file)->i_mapping,
	struct page *page;

	for (;;) {
		page = read_cache_page(file_inode(desc->file)->i_mapping,
			desc->page_index, (filler_t *)nfs_readdir_filler, desc);
		if (IS_ERR(page) || grab_page(page))
			break;
		put_page(page);
	}
	return page;
}

/*
@@ -934,13 +954,11 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)

static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence)
{
	struct inode *inode = file_inode(filp);
	struct nfs_open_dir_context *dir_ctx = filp->private_data;

	dfprintk(FILE, "NFS: llseek dir(%pD2, %lld, %d)\n",
			filp, offset, whence);

	inode_lock(inode);
	switch (whence) {
		case 1:
			offset += filp->f_pos;
@@ -948,16 +966,13 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence)
			if (offset >= 0)
				break;
		default:
			offset = -EINVAL;
			goto out;
			return -EINVAL;
	}
	if (offset != filp->f_pos) {
		filp->f_pos = offset;
		dir_ctx->dir_cookie = 0;
		dir_ctx->duped = 0;
	}
out:
	inode_unlock(inode);
	return offset;
}