| From foo@baz Sun Jun 17 12:07:34 CEST 2018 |
| From: Ashish Samant <ashish.samant@oracle.com> |
| Date: Fri, 11 May 2018 16:02:07 -0700 |
| Subject: ocfs2: take inode cluster lock before moving reflinked inode from orphan dir |
| |
| From: Ashish Samant <ashish.samant@oracle.com> |
| |
| [ Upstream commit e4383029201470523c3ffe339bd7d57e9b4a7d65 ] |
| |
| While reflinking an inode, we create a new inode in orphan directory, |
| then take EX lock on it, reflink the original inode to orphan inode and |
| release EX lock. Once the lock is released another node could request |
| it in EX mode from ocfs2_recover_orphans() which causes downconvert of |
| the lock, on this node, to NL mode. |
| |
| Later we attempt to initialize security acl for the orphan inode and |
| move it to the reflink destination. However, while doing this we dont |
| take EX lock on the inode. This could potentially cause problems |
| because we could be starting transaction, accessing journal and |
| modifying metadata of the inode while holding NL lock and with another |
| node holding EX lock on the inode. |
| |
| Fix this by taking orphan inode cluster lock in EX mode before |
| initializing security and moving orphan inode to reflink destination. |
| Use the __tracker variant while taking inode lock to avoid recursive |
| locking in the ocfs2_init_security_and_acl() call chain. |
| |
| Link: http://lkml.kernel.org/r/1523475107-7639-1-git-send-email-ashish.samant@oracle.com |
| Signed-off-by: Ashish Samant <ashish.samant@oracle.com> |
| Reviewed-by: Joseph Qi <jiangqi903@gmail.com> |
| Reviewed-by: Junxiao Bi <junxiao.bi@oracle.com> |
| Acked-by: Jun Piao <piaojun@huawei.com> |
| Cc: Mark Fasheh <mark@fasheh.com> |
| Cc: Joel Becker <jlbec@evilplan.org> |
| Cc: Changwei Ge <ge.changwei@h3c.com> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Sasha Levin <alexander.levin@microsoft.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/ocfs2/refcounttree.c | 14 ++++++++++++-- |
| 1 file changed, 12 insertions(+), 2 deletions(-) |
| |
| --- a/fs/ocfs2/refcounttree.c |
| +++ b/fs/ocfs2/refcounttree.c |
| @@ -4250,10 +4250,11 @@ out: |
| static int ocfs2_reflink(struct dentry *old_dentry, struct inode *dir, |
| struct dentry *new_dentry, bool preserve) |
| { |
| - int error; |
| + int error, had_lock; |
| struct inode *inode = d_inode(old_dentry); |
| struct buffer_head *old_bh = NULL; |
| struct inode *new_orphan_inode = NULL; |
| + struct ocfs2_lock_holder oh; |
| |
| if (!ocfs2_refcount_tree(OCFS2_SB(inode->i_sb))) |
| return -EOPNOTSUPP; |
| @@ -4295,6 +4296,14 @@ static int ocfs2_reflink(struct dentry * |
| goto out; |
| } |
| |
| + had_lock = ocfs2_inode_lock_tracker(new_orphan_inode, NULL, 1, |
| + &oh); |
| + if (had_lock < 0) { |
| + error = had_lock; |
| + mlog_errno(error); |
| + goto out; |
| + } |
| + |
| /* If the security isn't preserved, we need to re-initialize them. */ |
| if (!preserve) { |
| error = ocfs2_init_security_and_acl(dir, new_orphan_inode, |
| @@ -4302,14 +4311,15 @@ static int ocfs2_reflink(struct dentry * |
| if (error) |
| mlog_errno(error); |
| } |
| -out: |
| if (!error) { |
| error = ocfs2_mv_orphaned_inode_to_new(dir, new_orphan_inode, |
| new_dentry); |
| if (error) |
| mlog_errno(error); |
| } |
| + ocfs2_inode_unlock_tracker(new_orphan_inode, 1, &oh, had_lock); |
| |
| +out: |
| if (new_orphan_inode) { |
| /* |
| * We need to open_unlock the inode no matter whether we |