diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2016-04-20 23:08:32 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2016-05-02 19:49:29 -0400 |
commit | 6192269444ebfbfb42e23c7a6a93c76ffe4b5e51 (patch) | |
tree | ec40fbbad46725d88f2c16b11ff5976d87f497bb | |
parent | 63b6df14134ddd048984c8afadb46e721815bfc6 (diff) |
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>
-rw-r--r-- | Documentation/filesystems/porting | 18 | ||||
-rw-r--r-- | fs/coda/dir.c | 18 | ||||
-rw-r--r-- | fs/exportfs/expfs.c | 2 | ||||
-rw-r--r-- | fs/readdir.c | 20 | ||||
-rw-r--r-- | include/linux/fs.h | 1 |
5 files changed, 48 insertions, 11 deletions
diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting index 1567a53857bd..12c57abdaac9 100644 --- a/Documentation/filesystems/porting +++ b/Documentation/filesystems/porting | |||
@@ -557,3 +557,21 @@ in your dentry operations instead. | |||
557 | will not happen in parallel ("same" in the sense of your ->d_compare()). | 557 | will not happen in parallel ("same" in the sense of your ->d_compare()). |
558 | Lookups on different names in the same directory can and do happen in | 558 | Lookups on different names in the same directory can and do happen in |
559 | parallel now. | 559 | parallel now. |
560 | -- | ||
561 | [recommended] | ||
562 | ->iterate_shared() is added; it's a parallel variant of ->iterate(). | ||
563 | Exclusion on struct file level is still provided (as well as that | ||
564 | between it and lseek on the same struct file), but if your directory | ||
565 | has been opened several times, you can get these called in parallel. | ||
566 | Exclusion between that method and all directory-modifying ones is | ||
567 | still provided, of course. | ||
568 | |||
569 | Often enough ->iterate() can serve as ->iterate_shared() without any | ||
570 | changes - it is a read-only operation, after all. If you have any | ||
571 | per-inode or per-dentry in-core data structures modified by ->iterate(), | ||
572 | you might need something to serialize the access to them. If you | ||
573 | do dcache pre-seeding, you'll need to switch to d_alloc_parallel() for | ||
574 | that; look for in-tree examples. | ||
575 | |||
576 | Old method is only used if the new one is absent; eventually it will | ||
577 | be removed. Switch while you still can; the old one won't stay. | ||
diff --git a/fs/coda/dir.c b/fs/coda/dir.c index 42e731b8c80a..6fb8672c0892 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c | |||
@@ -424,16 +424,22 @@ static int coda_readdir(struct file *coda_file, struct dir_context *ctx) | |||
424 | BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); | 424 | BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); |
425 | host_file = cfi->cfi_container; | 425 | host_file = cfi->cfi_container; |
426 | 426 | ||
427 | if (host_file->f_op->iterate) { | 427 | if (host_file->f_op->iterate || host_file->f_op->iterate_shared) { |
428 | struct inode *host_inode = file_inode(host_file); | 428 | struct inode *host_inode = file_inode(host_file); |
429 | |||
430 | inode_lock(host_inode); | ||
431 | ret = -ENOENT; | 429 | ret = -ENOENT; |
432 | if (!IS_DEADDIR(host_inode)) { | 430 | if (!IS_DEADDIR(host_inode)) { |
433 | ret = host_file->f_op->iterate(host_file, ctx); | 431 | if (host_file->f_op->iterate_shared) { |
434 | file_accessed(host_file); | 432 | inode_lock_shared(host_inode); |
433 | ret = host_file->f_op->iterate_shared(host_file, ctx); | ||
434 | file_accessed(host_file); | ||
435 | inode_unlock_shared(host_inode); | ||
436 | } else { | ||
437 | inode_lock(host_inode); | ||
438 | ret = host_file->f_op->iterate(host_file, ctx); | ||
439 | file_accessed(host_file); | ||
440 | inode_unlock(host_inode); | ||
441 | } | ||
435 | } | 442 | } |
436 | inode_unlock(host_inode); | ||
437 | return ret; | 443 | return ret; |
438 | } | 444 | } |
439 | /* Venus: we must read Venus dirents from a file */ | 445 | /* Venus: we must read Venus dirents from a file */ |
diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index 402c5caab5ca..207ba8d627ca 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c | |||
@@ -312,7 +312,7 @@ static int get_name(const struct path *path, char *name, struct dentry *child) | |||
312 | goto out; | 312 | goto out; |
313 | 313 | ||
314 | error = -EINVAL; | 314 | error = -EINVAL; |
315 | if (!file->f_op->iterate) | 315 | if (!file->f_op->iterate && !file->f_op->iterate_shared) |
316 | goto out_close; | 316 | goto out_close; |
317 | 317 | ||
318 | buffer.sequence = 0; | 318 | buffer.sequence = 0; |
diff --git a/fs/readdir.c b/fs/readdir.c index d7308b8f6cf7..a86c6c04b9bc 100644 --- a/fs/readdir.c +++ b/fs/readdir.c | |||
@@ -24,15 +24,21 @@ | |||
24 | int iterate_dir(struct file *file, struct dir_context *ctx) | 24 | int iterate_dir(struct file *file, struct dir_context *ctx) |
25 | { | 25 | { |
26 | struct inode *inode = file_inode(file); | 26 | struct inode *inode = file_inode(file); |
27 | bool shared = false; | ||
27 | int res = -ENOTDIR; | 28 | int res = -ENOTDIR; |
28 | if (!file->f_op->iterate) | 29 | if (file->f_op->iterate_shared) |
30 | shared = true; | ||
31 | else if (!file->f_op->iterate) | ||
29 | goto out; | 32 | goto out; |
30 | 33 | ||
31 | res = security_file_permission(file, MAY_READ); | 34 | res = security_file_permission(file, MAY_READ); |
32 | if (res) | 35 | if (res) |
33 | goto out; | 36 | goto out; |
34 | 37 | ||
35 | inode_lock(inode); | 38 | if (shared) |
39 | inode_lock_shared(inode); | ||
40 | else | ||
41 | inode_lock(inode); | ||
36 | // res = mutex_lock_killable(&inode->i_mutex); | 42 | // res = mutex_lock_killable(&inode->i_mutex); |
37 | // if (res) | 43 | // if (res) |
38 | // goto out; | 44 | // goto out; |
@@ -40,12 +46,18 @@ int iterate_dir(struct file *file, struct dir_context *ctx) | |||
40 | res = -ENOENT; | 46 | res = -ENOENT; |
41 | if (!IS_DEADDIR(inode)) { | 47 | if (!IS_DEADDIR(inode)) { |
42 | ctx->pos = file->f_pos; | 48 | ctx->pos = file->f_pos; |
43 | res = file->f_op->iterate(file, ctx); | 49 | if (shared) |
50 | res = file->f_op->iterate_shared(file, ctx); | ||
51 | else | ||
52 | res = file->f_op->iterate(file, ctx); | ||
44 | file->f_pos = ctx->pos; | 53 | file->f_pos = ctx->pos; |
45 | fsnotify_access(file); | 54 | fsnotify_access(file); |
46 | file_accessed(file); | 55 | file_accessed(file); |
47 | } | 56 | } |
48 | inode_unlock(inode); | 57 | if (shared) |
58 | inode_unlock_shared(inode); | ||
59 | else | ||
60 | inode_unlock(inode); | ||
49 | out: | 61 | out: |
50 | return res; | 62 | return res; |
51 | } | 63 | } |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 3018f31f7aa0..3dc0258a2b64 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -1674,6 +1674,7 @@ struct file_operations { | |||
1674 | ssize_t (*read_iter) (struct kiocb *, struct iov_iter *); | 1674 | ssize_t (*read_iter) (struct kiocb *, struct iov_iter *); |
1675 | ssize_t (*write_iter) (struct kiocb *, struct iov_iter *); | 1675 | ssize_t (*write_iter) (struct kiocb *, struct iov_iter *); |
1676 | int (*iterate) (struct file *, struct dir_context *); | 1676 | int (*iterate) (struct file *, struct dir_context *); |
1677 | int (*iterate_shared) (struct file *, struct dir_context *); | ||
1677 | unsigned int (*poll) (struct file *, struct poll_table_struct *); | 1678 | unsigned int (*poll) (struct file *, struct poll_table_struct *); |
1678 | long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); | 1679 | long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); |
1679 | long (*compat_ioctl) (struct file *, unsigned int, unsigned long); | 1680 | long (*compat_ioctl) (struct file *, unsigned int, unsigned long); |