|
@@ -1244,6 +1244,50 @@ err:
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Grab the exclusive iolock for a data copy from src to dest, making
|
|
|
+ * sure to abide vfs locking order (lowest pointer value goes first) and
|
|
|
+ * breaking the pnfs layout leases on dest before proceeding. The loop
|
|
|
+ * is needed because we cannot call the blocking break_layout() with the
|
|
|
+ * src iolock held, and therefore have to back out both locks.
|
|
|
+ */
|
|
|
+static int
|
|
|
+xfs_iolock_two_inodes_and_break_layout(
|
|
|
+ struct inode *src,
|
|
|
+ struct inode *dest)
|
|
|
+{
|
|
|
+ int error;
|
|
|
+
|
|
|
+retry:
|
|
|
+ if (src < dest) {
|
|
|
+ inode_lock(src);
|
|
|
+ inode_lock_nested(dest, I_MUTEX_NONDIR2);
|
|
|
+ } else {
|
|
|
+ /* src >= dest */
|
|
|
+ inode_lock(dest);
|
|
|
+ }
|
|
|
+
|
|
|
+ error = break_layout(dest, false);
|
|
|
+ if (error == -EWOULDBLOCK) {
|
|
|
+ inode_unlock(dest);
|
|
|
+ if (src < dest)
|
|
|
+ inode_unlock(src);
|
|
|
+ error = break_layout(dest, true);
|
|
|
+ if (error)
|
|
|
+ return error;
|
|
|
+ goto retry;
|
|
|
+ }
|
|
|
+ if (error) {
|
|
|
+ inode_unlock(dest);
|
|
|
+ if (src < dest)
|
|
|
+ inode_unlock(src);
|
|
|
+ return error;
|
|
|
+ }
|
|
|
+ if (src > dest)
|
|
|
+ inode_lock_nested(src, I_MUTEX_NONDIR2);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Link a range of blocks from one file to another.
|
|
|
*/
|
|
@@ -1274,7 +1318,9 @@ xfs_reflink_remap_range(
|
|
|
return -EIO;
|
|
|
|
|
|
/* Lock both files against IO */
|
|
|
- lock_two_nondirectories(inode_in, inode_out);
|
|
|
+ ret = xfs_iolock_two_inodes_and_break_layout(inode_in, inode_out);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
if (same_inode)
|
|
|
xfs_ilock(src, XFS_MMAPLOCK_EXCL);
|
|
|
else
|