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

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
...@@ -57,7 +57,7 @@ static void nfs_readdir_clear_array(struct page*); ...@@ -57,7 +57,7 @@ static void nfs_readdir_clear_array(struct page*);
const struct file_operations nfs_dir_operations = { const struct file_operations nfs_dir_operations = {
.llseek = nfs_llseek_dir, .llseek = nfs_llseek_dir,
.read = generic_read_dir, .read = generic_read_dir,
.iterate = nfs_readdir, .iterate_shared = nfs_readdir,
.open = nfs_opendir, .open = nfs_opendir,
.release = nfs_closedir, .release = nfs_closedir,
.fsync = nfs_fsync_dir, .fsync = nfs_fsync_dir,
...@@ -145,6 +145,7 @@ struct nfs_cache_array_entry { ...@@ -145,6 +145,7 @@ struct nfs_cache_array_entry {
}; };
struct nfs_cache_array { struct nfs_cache_array {
atomic_t refcount;
int size; int size;
int eof_index; int eof_index;
u64 last_cookie; u64 last_cookie;
...@@ -200,11 +201,20 @@ void nfs_readdir_clear_array(struct page *page) ...@@ -200,11 +201,20 @@ void nfs_readdir_clear_array(struct page *page)
int i; int i;
array = kmap_atomic(page); array = kmap_atomic(page);
for (i = 0; i < array->size; i++) if (atomic_dec_and_test(&array->refcount))
kfree(array->array[i].string.name); for (i = 0; i < array->size; i++)
kfree(array->array[i].string.name);
kunmap_atomic(array); 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 * the caller is responsible for freeing qstr.name
* when called by nfs_readdir_add_to_array, the strings will be freed in * when called by nfs_readdir_add_to_array, the strings will be freed in
...@@ -470,6 +480,7 @@ static ...@@ -470,6 +480,7 @@ static
void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry) void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
{ {
struct qstr filename = QSTR_INIT(entry->name, entry->len); struct qstr filename = QSTR_INIT(entry->name, entry->len);
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
struct dentry *dentry; struct dentry *dentry;
struct dentry *alias; struct dentry *alias;
struct inode *dir = d_inode(parent); struct inode *dir = d_inode(parent);
...@@ -489,7 +500,13 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry) ...@@ -489,7 +500,13 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
filename.hash = full_name_hash(filename.name, filename.len); filename.hash = full_name_hash(filename.name, filename.len);
dentry = d_lookup(parent, &filename); 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 */ /* Is there a mountpoint here? If so, just exit */
if (!nfs_fsid_equal(&NFS_SB(dentry->d_sb)->fsid, if (!nfs_fsid_equal(&NFS_SB(dentry->d_sb)->fsid,
&entry->fattr->fsid)) &entry->fattr->fsid))
...@@ -503,26 +520,21 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry) ...@@ -503,26 +520,21 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
} else { } else {
d_invalidate(dentry); d_invalidate(dentry);
dput(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); inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr, entry->label);
if (IS_ERR(inode))
goto out;
alias = d_splice_alias(inode, dentry); alias = d_splice_alias(inode, dentry);
if (IS_ERR(alias)) d_lookup_done(dentry);
goto out; if (alias) {
else if (alias) { if (IS_ERR(alias))
nfs_set_verifier(alias, nfs_save_change_attribute(dir)); goto out;
dput(alias); dput(dentry);
} else dentry = alias;
nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); }
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
out: out:
dput(dentry); dput(dentry);
} }
...@@ -643,6 +655,7 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, ...@@ -643,6 +655,7 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
goto out_label_free; goto out_label_free;
} }
memset(array, 0, sizeof(struct nfs_cache_array)); memset(array, 0, sizeof(struct nfs_cache_array));
atomic_set(&array->refcount, 1);
array->eof_index = -1; array->eof_index = -1;
status = nfs_readdir_alloc_pages(pages, array_size); status = nfs_readdir_alloc_pages(pages, array_size);
...@@ -705,8 +718,7 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page) ...@@ -705,8 +718,7 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page)
static static
void cache_page_release(nfs_readdir_descriptor_t *desc) void cache_page_release(nfs_readdir_descriptor_t *desc)
{ {
if (!desc->page->mapping) nfs_readdir_clear_array(desc->page);
nfs_readdir_clear_array(desc->page);
put_page(desc->page); put_page(desc->page);
desc->page = NULL; desc->page = NULL;
} }
...@@ -714,8 +726,16 @@ void cache_page_release(nfs_readdir_descriptor_t *desc) ...@@ -714,8 +726,16 @@ void cache_page_release(nfs_readdir_descriptor_t *desc)
static static
struct page *get_cache_page(nfs_readdir_descriptor_t *desc) 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); 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) ...@@ -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) 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; struct nfs_open_dir_context *dir_ctx = filp->private_data;
dfprintk(FILE, "NFS: llseek dir(%pD2, %lld, %d)\n", dfprintk(FILE, "NFS: llseek dir(%pD2, %lld, %d)\n",
filp, offset, whence); filp, offset, whence);
inode_lock(inode);
switch (whence) { switch (whence) {
case 1: case 1:
offset += filp->f_pos; offset += filp->f_pos;
...@@ -948,16 +966,13 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence) ...@@ -948,16 +966,13 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence)
if (offset >= 0) if (offset >= 0)
break; break;
default: default:
offset = -EINVAL; return -EINVAL;
goto out;
} }
if (offset != filp->f_pos) { if (offset != filp->f_pos) {
filp->f_pos = offset; filp->f_pos = offset;
dir_ctx->dir_cookie = 0; dir_ctx->dir_cookie = 0;
dir_ctx->duped = 0; dir_ctx->duped = 0;
} }
out:
inode_unlock(inode);
return offset; return offset;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment