xfs: only grab shared inode locks for source file during reflink
authorDarrick J. Wong <darrick.wong@oracle.com>
Thu, 18 Jan 2018 22:07:53 +0000 (14:07 -0800)
committerDarrick J. Wong <darrick.wong@oracle.com>
Mon, 29 Jan 2018 15:27:23 +0000 (07:27 -0800)
Reflink and dedupe operations remap blocks from a source file into a
destination file.  The destination file needs exclusive locks on all
levels because we're updating its block map, but the source file isn't
undergoing any block map changes so we can use a shared lock.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
fs/xfs/xfs_reflink.c
include/linux/fs.h

index bcc58c24287c6bb1d3a9e0715465cbbf8a30ab2e..85a119e1463b83af3e61ff9689223ac2f872d3fe 100644 (file)
@@ -1202,13 +1202,16 @@ xfs_reflink_remap_blocks(
 
        /* drange = (destoff, destoff + len); srange = (srcoff, srcoff + len) */
        while (len) {
+               uint            lock_mode;
+
                trace_xfs_reflink_remap_blocks_loop(src, srcoff, len,
                                dest, destoff);
+
                /* Read extent from the source file */
                nimaps = 1;
-               xfs_ilock(src, XFS_ILOCK_EXCL);
+               lock_mode = xfs_ilock_data_map_shared(src);
                error = xfs_bmapi_read(src, srcoff, len, &imap, &nimaps, 0);
-               xfs_iunlock(src, XFS_ILOCK_EXCL);
+               xfs_iunlock(src, lock_mode);
                if (error)
                        goto err;
                ASSERT(nimaps == 1);
@@ -1260,7 +1263,7 @@ xfs_iolock_two_inodes_and_break_layout(
 
 retry:
        if (src < dest) {
-               inode_lock(src);
+               inode_lock_shared(src);
                inode_lock_nested(dest, I_MUTEX_NONDIR2);
        } else {
                /* src >= dest */
@@ -1271,7 +1274,7 @@ retry:
        if (error == -EWOULDBLOCK) {
                inode_unlock(dest);
                if (src < dest)
-                       inode_unlock(src);
+                       inode_unlock_shared(src);
                error = break_layout(dest, true);
                if (error)
                        return error;
@@ -1280,11 +1283,11 @@ retry:
        if (error) {
                inode_unlock(dest);
                if (src < dest)
-                       inode_unlock(src);
+                       inode_unlock_shared(src);
                return error;
        }
        if (src > dest)
-               inode_lock_nested(src, I_MUTEX_NONDIR2);
+               inode_lock_shared_nested(src, I_MUTEX_NONDIR2);
        return 0;
 }
 
@@ -1324,7 +1327,7 @@ xfs_reflink_remap_range(
        if (same_inode)
                xfs_ilock(src, XFS_MMAPLOCK_EXCL);
        else
-               xfs_lock_two_inodes(src, XFS_MMAPLOCK_EXCL, dest,
+               xfs_lock_two_inodes(src, XFS_MMAPLOCK_SHARED, dest,
                                XFS_MMAPLOCK_EXCL);
 
        /* Check file eligibility and prepare for block sharing. */
@@ -1393,10 +1396,12 @@ xfs_reflink_remap_range(
                        is_dedupe);
 
 out_unlock:
-       xfs_iunlock(src, XFS_MMAPLOCK_EXCL);
+       xfs_iunlock(dest, XFS_MMAPLOCK_EXCL);
+       if (!same_inode)
+               xfs_iunlock(src, XFS_MMAPLOCK_SHARED);
+       inode_unlock(inode_out);
        if (!same_inode)
-               xfs_iunlock(dest, XFS_MMAPLOCK_EXCL);
-       unlock_two_nondirectories(inode_in, inode_out);
+               inode_unlock_shared(inode_in);
        if (ret)
                trace_xfs_reflink_remap_range_error(dest, ret, _RET_IP_);
        return ret;
index 7f8d96d68f34f893d5216ea257da30226d6370a0..5cbeab8a63ca7db2a46889614d51d1a097fb1329 100644 (file)
@@ -748,6 +748,11 @@ static inline void inode_lock_nested(struct inode *inode, unsigned subclass)
        down_write_nested(&inode->i_rwsem, subclass);
 }
 
+static inline void inode_lock_shared_nested(struct inode *inode, unsigned subclass)
+{
+       down_read_nested(&inode->i_rwsem, subclass);
+}
+
 void lock_two_nondirectories(struct inode *, struct inode*);
 void unlock_two_nondirectories(struct inode *, struct inode*);