introduce a parallel variant of ->iterate()
New method: ->iterate_shared(). Same arguments as in ->iterate(), called with the directory locked only shared. Once all filesystems switch, the old one will be gone. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
63b6df1413
commit
6192269444
|
@ -557,3 +557,21 @@ in your dentry operations instead.
|
|||
will not happen in parallel ("same" in the sense of your ->d_compare()).
|
||||
Lookups on different names in the same directory can and do happen in
|
||||
parallel now.
|
||||
--
|
||||
[recommended]
|
||||
->iterate_shared() is added; it's a parallel variant of ->iterate().
|
||||
Exclusion on struct file level is still provided (as well as that
|
||||
between it and lseek on the same struct file), but if your directory
|
||||
has been opened several times, you can get these called in parallel.
|
||||
Exclusion between that method and all directory-modifying ones is
|
||||
still provided, of course.
|
||||
|
||||
Often enough ->iterate() can serve as ->iterate_shared() without any
|
||||
changes - it is a read-only operation, after all. If you have any
|
||||
per-inode or per-dentry in-core data structures modified by ->iterate(),
|
||||
you might need something to serialize the access to them. If you
|
||||
do dcache pre-seeding, you'll need to switch to d_alloc_parallel() for
|
||||
that; look for in-tree examples.
|
||||
|
||||
Old method is only used if the new one is absent; eventually it will
|
||||
be removed. Switch while you still can; the old one won't stay.
|
||||
|
|
|
@ -424,16 +424,22 @@ static int coda_readdir(struct file *coda_file, struct dir_context *ctx)
|
|||
BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
|
||||
host_file = cfi->cfi_container;
|
||||
|
||||
if (host_file->f_op->iterate) {
|
||||
if (host_file->f_op->iterate || host_file->f_op->iterate_shared) {
|
||||
struct inode *host_inode = file_inode(host_file);
|
||||
|
||||
inode_lock(host_inode);
|
||||
ret = -ENOENT;
|
||||
if (!IS_DEADDIR(host_inode)) {
|
||||
ret = host_file->f_op->iterate(host_file, ctx);
|
||||
file_accessed(host_file);
|
||||
if (host_file->f_op->iterate_shared) {
|
||||
inode_lock_shared(host_inode);
|
||||
ret = host_file->f_op->iterate_shared(host_file, ctx);
|
||||
file_accessed(host_file);
|
||||
inode_unlock_shared(host_inode);
|
||||
} else {
|
||||
inode_lock(host_inode);
|
||||
ret = host_file->f_op->iterate(host_file, ctx);
|
||||
file_accessed(host_file);
|
||||
inode_unlock(host_inode);
|
||||
}
|
||||
}
|
||||
inode_unlock(host_inode);
|
||||
return ret;
|
||||
}
|
||||
/* Venus: we must read Venus dirents from a file */
|
||||
|
|
|
@ -312,7 +312,7 @@ static int get_name(const struct path *path, char *name, struct dentry *child)
|
|||
goto out;
|
||||
|
||||
error = -EINVAL;
|
||||
if (!file->f_op->iterate)
|
||||
if (!file->f_op->iterate && !file->f_op->iterate_shared)
|
||||
goto out_close;
|
||||
|
||||
buffer.sequence = 0;
|
||||
|
|
20
fs/readdir.c
20
fs/readdir.c
|
@ -24,15 +24,21 @@
|
|||
int iterate_dir(struct file *file, struct dir_context *ctx)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
bool shared = false;
|
||||
int res = -ENOTDIR;
|
||||
if (!file->f_op->iterate)
|
||||
if (file->f_op->iterate_shared)
|
||||
shared = true;
|
||||
else if (!file->f_op->iterate)
|
||||
goto out;
|
||||
|
||||
res = security_file_permission(file, MAY_READ);
|
||||
if (res)
|
||||
goto out;
|
||||
|
||||
inode_lock(inode);
|
||||
if (shared)
|
||||
inode_lock_shared(inode);
|
||||
else
|
||||
inode_lock(inode);
|
||||
// res = mutex_lock_killable(&inode->i_mutex);
|
||||
// if (res)
|
||||
// goto out;
|
||||
|
@ -40,12 +46,18 @@ int iterate_dir(struct file *file, struct dir_context *ctx)
|
|||
res = -ENOENT;
|
||||
if (!IS_DEADDIR(inode)) {
|
||||
ctx->pos = file->f_pos;
|
||||
res = file->f_op->iterate(file, ctx);
|
||||
if (shared)
|
||||
res = file->f_op->iterate_shared(file, ctx);
|
||||
else
|
||||
res = file->f_op->iterate(file, ctx);
|
||||
file->f_pos = ctx->pos;
|
||||
fsnotify_access(file);
|
||||
file_accessed(file);
|
||||
}
|
||||
inode_unlock(inode);
|
||||
if (shared)
|
||||
inode_unlock_shared(inode);
|
||||
else
|
||||
inode_unlock(inode);
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -1674,6 +1674,7 @@ struct file_operations {
|
|||
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
|
||||
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
|
||||
int (*iterate) (struct file *, struct dir_context *);
|
||||
int (*iterate_shared) (struct file *, struct dir_context *);
|
||||
unsigned int (*poll) (struct file *, struct poll_table_struct *);
|
||||
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
|
||||
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
|
||||
|
|
Loading…
Reference in New Issue