diff options
| author | Tejun Heo <tj@kernel.org> | 2017-02-11 15:33:02 -0500 |
|---|---|---|
| committer | Tejun Heo <tj@kernel.org> | 2017-02-21 15:49:25 -0500 |
| commit | f83f3c515654474e19c7fc86e3b06564bb5cb4d4 (patch) | |
| tree | 17389a81a3e78f0051e4fb5d948f3d440f539cce /fs/kernfs | |
| parent | 63f1ca59453aadae81f702840c7ac6ea8b9f9262 (diff) | |
kernfs: fix locking around kernfs_ops->release() callback
The release callback may be called from two places - file release
operation and kernfs open file draining. kernfs_open_file->mutex is
used to synchronize the two callsites. This unfortunately leads to
possible circular locking because of->mutex is used to protect the
usual kernfs operations which may use locking constructs which are
held while removing and thus draining kernfs files.
@of->mutex is for synchronizing concurrent kernfs access operations
and all we need here is synchronization between the releaes and drain
paths. As the drain path has to grab kernfs_open_file_mutex anyway,
let's use the mutex to synchronize the release operation instead.
Signed-off-by: Tejun Heo <tj@kernel.org>
Reported-and-tested-by: Tony Lindgren <tony@atomide.com>
Fixes: 0e67db2f9fe9 ("kernfs: add kernfs_ops->open/release() callbacks")
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/kernfs')
| -rw-r--r-- | fs/kernfs/file.c | 19 |
1 files changed, 14 insertions, 5 deletions
diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 20c396291ac1..14da136028de 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c | |||
| @@ -747,10 +747,15 @@ err_out: | |||
| 747 | static void kernfs_release_file(struct kernfs_node *kn, | 747 | static void kernfs_release_file(struct kernfs_node *kn, |
| 748 | struct kernfs_open_file *of) | 748 | struct kernfs_open_file *of) |
| 749 | { | 749 | { |
| 750 | if (!(kn->flags & KERNFS_HAS_RELEASE)) | 750 | /* |
| 751 | return; | 751 | * @of is guaranteed to have no other file operations in flight and |
| 752 | * we just want to synchronize release and drain paths. | ||
| 753 | * @kernfs_open_file_mutex is enough. @of->mutex can't be used | ||
| 754 | * here because drain path may be called from places which can | ||
| 755 | * cause circular dependency. | ||
| 756 | */ | ||
| 757 | lockdep_assert_held(&kernfs_open_file_mutex); | ||
| 752 | 758 | ||
| 753 | mutex_lock(&of->mutex); | ||
| 754 | if (!of->released) { | 759 | if (!of->released) { |
| 755 | /* | 760 | /* |
| 756 | * A file is never detached without being released and we | 761 | * A file is never detached without being released and we |
| @@ -760,7 +765,6 @@ static void kernfs_release_file(struct kernfs_node *kn, | |||
| 760 | kn->attr.ops->release(of); | 765 | kn->attr.ops->release(of); |
| 761 | of->released = true; | 766 | of->released = true; |
| 762 | } | 767 | } |
| 763 | mutex_unlock(&of->mutex); | ||
| 764 | } | 768 | } |
| 765 | 769 | ||
| 766 | static int kernfs_fop_release(struct inode *inode, struct file *filp) | 770 | static int kernfs_fop_release(struct inode *inode, struct file *filp) |
| @@ -768,7 +772,12 @@ static int kernfs_fop_release(struct inode *inode, struct file *filp) | |||
| 768 | struct kernfs_node *kn = filp->f_path.dentry->d_fsdata; | 772 | struct kernfs_node *kn = filp->f_path.dentry->d_fsdata; |
| 769 | struct kernfs_open_file *of = kernfs_of(filp); | 773 | struct kernfs_open_file *of = kernfs_of(filp); |
| 770 | 774 | ||
| 771 | kernfs_release_file(kn, of); | 775 | if (kn->flags & KERNFS_HAS_RELEASE) { |
| 776 | mutex_lock(&kernfs_open_file_mutex); | ||
| 777 | kernfs_release_file(kn, of); | ||
| 778 | mutex_unlock(&kernfs_open_file_mutex); | ||
| 779 | } | ||
| 780 | |||
| 772 | kernfs_put_open_node(kn, of); | 781 | kernfs_put_open_node(kn, of); |
| 773 | seq_release(inode, filp); | 782 | seq_release(inode, filp); |
| 774 | kfree(of->prealloc_buf); | 783 | kfree(of->prealloc_buf); |
