forked from luck/tmp_suning_uos_patched
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: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
9cf843e3f4
commit
9ac3d3e846
71
fs/nfs/dir.c
71
fs/nfs/dir.c
|
@ -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 {
|
||||||
};
|
};
|
||||||
|
|
||||||
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)
|
||||||
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
|
||||||
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)
|
||||||
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)
|
||||||
} 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,
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user