diff options
author | Christoph Hellwig <hch@lst.de> | 2016-10-20 00:50:07 -0400 |
---|---|---|
committer | Dave Chinner <david@fromorbit.com> | 2016-10-20 00:50:07 -0400 |
commit | 5faaf4fa0a20d38edc4df57baf24ea35b7e91178 (patch) | |
tree | 04fbdf80c31a03ec499bc5628cd0454f128420c4 | |
parent | ec40759902556f21f37641ad9f19d02c4dd4b555 (diff) |
xfs: merge xfs_reflink_remap_range and xfs_file_share_range
There is no clear division of responsibility between those functions, so
just merge them into one to keep the code simple. Also move
xfs_file_wait_for_io to xfs_reflink.c together with its only caller.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
-rw-r--r-- | fs/xfs/xfs_file.c | 132 | ||||
-rw-r--r-- | fs/xfs/xfs_reflink.c | 178 | ||||
-rw-r--r-- | fs/xfs/xfs_reflink.h | 7 |
3 files changed, 143 insertions, 174 deletions
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 93729752bccb..6e4f7f900fea 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c | |||
@@ -909,132 +909,6 @@ out_unlock: | |||
909 | return error; | 909 | return error; |
910 | } | 910 | } |
911 | 911 | ||
912 | /* Hook up to the VFS reflink function */ | ||
913 | STATIC int | ||
914 | xfs_file_share_range( | ||
915 | struct file *file_in, | ||
916 | loff_t pos_in, | ||
917 | struct file *file_out, | ||
918 | loff_t pos_out, | ||
919 | u64 len, | ||
920 | bool is_dedupe) | ||
921 | { | ||
922 | struct inode *inode_in; | ||
923 | struct inode *inode_out; | ||
924 | ssize_t ret; | ||
925 | loff_t bs; | ||
926 | loff_t isize; | ||
927 | int same_inode; | ||
928 | loff_t blen; | ||
929 | unsigned int flags = 0; | ||
930 | |||
931 | inode_in = file_inode(file_in); | ||
932 | inode_out = file_inode(file_out); | ||
933 | bs = inode_out->i_sb->s_blocksize; | ||
934 | |||
935 | /* Lock both files against IO */ | ||
936 | same_inode = (inode_in == inode_out); | ||
937 | if (same_inode) { | ||
938 | xfs_ilock(XFS_I(inode_in), XFS_IOLOCK_EXCL); | ||
939 | xfs_ilock(XFS_I(inode_in), XFS_MMAPLOCK_EXCL); | ||
940 | } else { | ||
941 | xfs_lock_two_inodes(XFS_I(inode_in), XFS_I(inode_out), | ||
942 | XFS_IOLOCK_EXCL); | ||
943 | xfs_lock_two_inodes(XFS_I(inode_in), XFS_I(inode_out), | ||
944 | XFS_MMAPLOCK_EXCL); | ||
945 | } | ||
946 | |||
947 | /* Don't touch certain kinds of inodes */ | ||
948 | ret = -EPERM; | ||
949 | if (IS_IMMUTABLE(inode_out)) | ||
950 | goto out_unlock; | ||
951 | ret = -ETXTBSY; | ||
952 | if (IS_SWAPFILE(inode_in) || IS_SWAPFILE(inode_out)) | ||
953 | goto out_unlock; | ||
954 | |||
955 | /* Don't reflink dirs, pipes, sockets... */ | ||
956 | ret = -EISDIR; | ||
957 | if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode)) | ||
958 | goto out_unlock; | ||
959 | ret = -EINVAL; | ||
960 | if (S_ISFIFO(inode_in->i_mode) || S_ISFIFO(inode_out->i_mode)) | ||
961 | goto out_unlock; | ||
962 | if (!S_ISREG(inode_in->i_mode) || !S_ISREG(inode_out->i_mode)) | ||
963 | goto out_unlock; | ||
964 | |||
965 | /* Don't share DAX file data for now. */ | ||
966 | if (IS_DAX(inode_in) || IS_DAX(inode_out)) | ||
967 | goto out_unlock; | ||
968 | |||
969 | /* Are we going all the way to the end? */ | ||
970 | isize = i_size_read(inode_in); | ||
971 | if (isize == 0) { | ||
972 | ret = 0; | ||
973 | goto out_unlock; | ||
974 | } | ||
975 | |||
976 | if (len == 0) | ||
977 | len = isize - pos_in; | ||
978 | |||
979 | /* Ensure offsets don't wrap and the input is inside i_size */ | ||
980 | if (pos_in + len < pos_in || pos_out + len < pos_out || | ||
981 | pos_in + len > isize) | ||
982 | goto out_unlock; | ||
983 | |||
984 | /* Don't allow dedupe past EOF in the dest file */ | ||
985 | if (is_dedupe) { | ||
986 | loff_t disize; | ||
987 | |||
988 | disize = i_size_read(inode_out); | ||
989 | if (pos_out >= disize || pos_out + len > disize) | ||
990 | goto out_unlock; | ||
991 | } | ||
992 | |||
993 | /* If we're linking to EOF, continue to the block boundary. */ | ||
994 | if (pos_in + len == isize) | ||
995 | blen = ALIGN(isize, bs) - pos_in; | ||
996 | else | ||
997 | blen = len; | ||
998 | |||
999 | /* Only reflink if we're aligned to block boundaries */ | ||
1000 | if (!IS_ALIGNED(pos_in, bs) || !IS_ALIGNED(pos_in + blen, bs) || | ||
1001 | !IS_ALIGNED(pos_out, bs) || !IS_ALIGNED(pos_out + blen, bs)) | ||
1002 | goto out_unlock; | ||
1003 | |||
1004 | /* Don't allow overlapped reflink within the same file */ | ||
1005 | if (same_inode && pos_out + blen > pos_in && pos_out < pos_in + blen) | ||
1006 | goto out_unlock; | ||
1007 | |||
1008 | /* Wait for the completion of any pending IOs on both files */ | ||
1009 | inode_dio_wait(inode_in); | ||
1010 | if (!same_inode) | ||
1011 | inode_dio_wait(inode_out); | ||
1012 | |||
1013 | ret = filemap_write_and_wait_range(inode_in->i_mapping, | ||
1014 | pos_in, pos_in + len - 1); | ||
1015 | if (ret) | ||
1016 | goto out_unlock; | ||
1017 | |||
1018 | ret = filemap_write_and_wait_range(inode_out->i_mapping, | ||
1019 | pos_out, pos_out + len - 1); | ||
1020 | if (ret) | ||
1021 | goto out_unlock; | ||
1022 | |||
1023 | if (is_dedupe) | ||
1024 | flags |= XFS_REFLINK_DEDUPE; | ||
1025 | ret = xfs_reflink_remap_range(XFS_I(inode_in), pos_in, XFS_I(inode_out), | ||
1026 | pos_out, len, flags); | ||
1027 | |||
1028 | out_unlock: | ||
1029 | xfs_iunlock(XFS_I(inode_in), XFS_MMAPLOCK_EXCL); | ||
1030 | xfs_iunlock(XFS_I(inode_in), XFS_IOLOCK_EXCL); | ||
1031 | if (!same_inode) { | ||
1032 | xfs_iunlock(XFS_I(inode_out), XFS_MMAPLOCK_EXCL); | ||
1033 | xfs_iunlock(XFS_I(inode_out), XFS_IOLOCK_EXCL); | ||
1034 | } | ||
1035 | return ret; | ||
1036 | } | ||
1037 | |||
1038 | STATIC ssize_t | 912 | STATIC ssize_t |
1039 | xfs_file_copy_range( | 913 | xfs_file_copy_range( |
1040 | struct file *file_in, | 914 | struct file *file_in, |
@@ -1046,7 +920,7 @@ xfs_file_copy_range( | |||
1046 | { | 920 | { |
1047 | int error; | 921 | int error; |
1048 | 922 | ||
1049 | error = xfs_file_share_range(file_in, pos_in, file_out, pos_out, | 923 | error = xfs_reflink_remap_range(file_in, pos_in, file_out, pos_out, |
1050 | len, false); | 924 | len, false); |
1051 | if (error) | 925 | if (error) |
1052 | return error; | 926 | return error; |
@@ -1061,7 +935,7 @@ xfs_file_clone_range( | |||
1061 | loff_t pos_out, | 935 | loff_t pos_out, |
1062 | u64 len) | 936 | u64 len) |
1063 | { | 937 | { |
1064 | return xfs_file_share_range(file_in, pos_in, file_out, pos_out, | 938 | return xfs_reflink_remap_range(file_in, pos_in, file_out, pos_out, |
1065 | len, false); | 939 | len, false); |
1066 | } | 940 | } |
1067 | 941 | ||
@@ -1084,7 +958,7 @@ xfs_file_dedupe_range( | |||
1084 | if (len > XFS_MAX_DEDUPE_LEN) | 958 | if (len > XFS_MAX_DEDUPE_LEN) |
1085 | len = XFS_MAX_DEDUPE_LEN; | 959 | len = XFS_MAX_DEDUPE_LEN; |
1086 | 960 | ||
1087 | error = xfs_file_share_range(src_file, loff, dst_file, dst_loff, | 961 | error = xfs_reflink_remap_range(src_file, loff, dst_file, dst_loff, |
1088 | len, true); | 962 | len, true); |
1089 | if (error) | 963 | if (error) |
1090 | return error; | 964 | return error; |
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index 3b1c1a6bb5da..6592daa833a4 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c | |||
@@ -1312,19 +1312,26 @@ out_error: | |||
1312 | */ | 1312 | */ |
1313 | int | 1313 | int |
1314 | xfs_reflink_remap_range( | 1314 | xfs_reflink_remap_range( |
1315 | struct xfs_inode *src, | 1315 | struct file *file_in, |
1316 | xfs_off_t srcoff, | 1316 | loff_t pos_in, |
1317 | struct xfs_inode *dest, | 1317 | struct file *file_out, |
1318 | xfs_off_t destoff, | 1318 | loff_t pos_out, |
1319 | xfs_off_t len, | 1319 | u64 len, |
1320 | unsigned int flags) | 1320 | bool is_dedupe) |
1321 | { | 1321 | { |
1322 | struct inode *inode_in = file_inode(file_in); | ||
1323 | struct xfs_inode *src = XFS_I(inode_in); | ||
1324 | struct inode *inode_out = file_inode(file_out); | ||
1325 | struct xfs_inode *dest = XFS_I(inode_out); | ||
1322 | struct xfs_mount *mp = src->i_mount; | 1326 | struct xfs_mount *mp = src->i_mount; |
1327 | loff_t bs = inode_out->i_sb->s_blocksize; | ||
1328 | bool same_inode = (inode_in == inode_out); | ||
1323 | xfs_fileoff_t sfsbno, dfsbno; | 1329 | xfs_fileoff_t sfsbno, dfsbno; |
1324 | xfs_filblks_t fsblen; | 1330 | xfs_filblks_t fsblen; |
1325 | int error; | ||
1326 | xfs_extlen_t cowextsize; | 1331 | xfs_extlen_t cowextsize; |
1327 | bool is_same; | 1332 | loff_t isize; |
1333 | ssize_t ret; | ||
1334 | loff_t blen; | ||
1328 | 1335 | ||
1329 | if (!xfs_sb_version_hasreflink(&mp->m_sb)) | 1336 | if (!xfs_sb_version_hasreflink(&mp->m_sb)) |
1330 | return -EOPNOTSUPP; | 1337 | return -EOPNOTSUPP; |
@@ -1332,48 +1339,135 @@ xfs_reflink_remap_range( | |||
1332 | if (XFS_FORCED_SHUTDOWN(mp)) | 1339 | if (XFS_FORCED_SHUTDOWN(mp)) |
1333 | return -EIO; | 1340 | return -EIO; |
1334 | 1341 | ||
1342 | /* Lock both files against IO */ | ||
1343 | if (same_inode) { | ||
1344 | xfs_ilock(src, XFS_IOLOCK_EXCL); | ||
1345 | xfs_ilock(src, XFS_MMAPLOCK_EXCL); | ||
1346 | } else { | ||
1347 | xfs_lock_two_inodes(src, dest, XFS_IOLOCK_EXCL); | ||
1348 | xfs_lock_two_inodes(src, dest, XFS_MMAPLOCK_EXCL); | ||
1349 | } | ||
1350 | |||
1351 | /* Don't touch certain kinds of inodes */ | ||
1352 | ret = -EPERM; | ||
1353 | if (IS_IMMUTABLE(inode_out)) | ||
1354 | goto out_unlock; | ||
1355 | |||
1356 | ret = -ETXTBSY; | ||
1357 | if (IS_SWAPFILE(inode_in) || IS_SWAPFILE(inode_out)) | ||
1358 | goto out_unlock; | ||
1359 | |||
1360 | |||
1361 | /* Don't reflink dirs, pipes, sockets... */ | ||
1362 | ret = -EISDIR; | ||
1363 | if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode)) | ||
1364 | goto out_unlock; | ||
1365 | ret = -EINVAL; | ||
1366 | if (S_ISFIFO(inode_in->i_mode) || S_ISFIFO(inode_out->i_mode)) | ||
1367 | goto out_unlock; | ||
1368 | if (!S_ISREG(inode_in->i_mode) || !S_ISREG(inode_out->i_mode)) | ||
1369 | goto out_unlock; | ||
1370 | |||
1335 | /* Don't reflink realtime inodes */ | 1371 | /* Don't reflink realtime inodes */ |
1336 | if (XFS_IS_REALTIME_INODE(src) || XFS_IS_REALTIME_INODE(dest)) | 1372 | if (XFS_IS_REALTIME_INODE(src) || XFS_IS_REALTIME_INODE(dest)) |
1337 | return -EINVAL; | 1373 | goto out_unlock; |
1374 | |||
1375 | /* Don't share DAX file data for now. */ | ||
1376 | if (IS_DAX(inode_in) || IS_DAX(inode_out)) | ||
1377 | goto out_unlock; | ||
1378 | |||
1379 | /* Are we going all the way to the end? */ | ||
1380 | isize = i_size_read(inode_in); | ||
1381 | if (isize == 0) { | ||
1382 | ret = 0; | ||
1383 | goto out_unlock; | ||
1384 | } | ||
1385 | |||
1386 | if (len == 0) | ||
1387 | len = isize - pos_in; | ||
1388 | |||
1389 | /* Ensure offsets don't wrap and the input is inside i_size */ | ||
1390 | if (pos_in + len < pos_in || pos_out + len < pos_out || | ||
1391 | pos_in + len > isize) | ||
1392 | goto out_unlock; | ||
1338 | 1393 | ||
1339 | if (flags & ~XFS_REFLINK_ALL) | 1394 | /* Don't allow dedupe past EOF in the dest file */ |
1340 | return -EINVAL; | 1395 | if (is_dedupe) { |
1396 | loff_t disize; | ||
1341 | 1397 | ||
1342 | trace_xfs_reflink_remap_range(src, srcoff, len, dest, destoff); | 1398 | disize = i_size_read(inode_out); |
1399 | if (pos_out >= disize || pos_out + len > disize) | ||
1400 | goto out_unlock; | ||
1401 | } | ||
1402 | |||
1403 | /* If we're linking to EOF, continue to the block boundary. */ | ||
1404 | if (pos_in + len == isize) | ||
1405 | blen = ALIGN(isize, bs) - pos_in; | ||
1406 | else | ||
1407 | blen = len; | ||
1408 | |||
1409 | /* Only reflink if we're aligned to block boundaries */ | ||
1410 | if (!IS_ALIGNED(pos_in, bs) || !IS_ALIGNED(pos_in + blen, bs) || | ||
1411 | !IS_ALIGNED(pos_out, bs) || !IS_ALIGNED(pos_out + blen, bs)) | ||
1412 | goto out_unlock; | ||
1413 | |||
1414 | /* Don't allow overlapped reflink within the same file */ | ||
1415 | if (same_inode) { | ||
1416 | if (pos_out + blen > pos_in && pos_out < pos_in + blen) | ||
1417 | goto out_unlock; | ||
1418 | } | ||
1419 | |||
1420 | /* Wait for the completion of any pending IOs on both files */ | ||
1421 | inode_dio_wait(inode_in); | ||
1422 | if (!same_inode) | ||
1423 | inode_dio_wait(inode_out); | ||
1424 | |||
1425 | ret = filemap_write_and_wait_range(inode_in->i_mapping, | ||
1426 | pos_in, pos_in + len - 1); | ||
1427 | if (ret) | ||
1428 | goto out_unlock; | ||
1429 | |||
1430 | ret = filemap_write_and_wait_range(inode_out->i_mapping, | ||
1431 | pos_out, pos_out + len - 1); | ||
1432 | if (ret) | ||
1433 | goto out_unlock; | ||
1434 | |||
1435 | trace_xfs_reflink_remap_range(src, pos_in, len, dest, pos_out); | ||
1343 | 1436 | ||
1344 | /* | 1437 | /* |
1345 | * Check that the extents are the same. | 1438 | * Check that the extents are the same. |
1346 | */ | 1439 | */ |
1347 | if (flags & XFS_REFLINK_DEDUPE) { | 1440 | if (is_dedupe) { |
1348 | is_same = false; | 1441 | bool is_same = false; |
1349 | error = xfs_compare_extents(VFS_I(src), srcoff, VFS_I(dest), | 1442 | |
1350 | destoff, len, &is_same); | 1443 | ret = xfs_compare_extents(inode_in, pos_in, inode_out, pos_out, |
1351 | if (error) | 1444 | len, &is_same); |
1352 | goto out_error; | 1445 | if (ret) |
1446 | goto out_unlock; | ||
1353 | if (!is_same) { | 1447 | if (!is_same) { |
1354 | error = -EBADE; | 1448 | ret = -EBADE; |
1355 | goto out_error; | 1449 | goto out_unlock; |
1356 | } | 1450 | } |
1357 | } | 1451 | } |
1358 | 1452 | ||
1359 | error = xfs_reflink_set_inode_flag(src, dest); | 1453 | ret = xfs_reflink_set_inode_flag(src, dest); |
1360 | if (error) | 1454 | if (ret) |
1361 | goto out_error; | 1455 | goto out_unlock; |
1362 | 1456 | ||
1363 | /* | 1457 | /* |
1364 | * Invalidate the page cache so that we can clear any CoW mappings | 1458 | * Invalidate the page cache so that we can clear any CoW mappings |
1365 | * in the destination file. | 1459 | * in the destination file. |
1366 | */ | 1460 | */ |
1367 | truncate_inode_pages_range(&VFS_I(dest)->i_data, destoff, | 1461 | truncate_inode_pages_range(&inode_out->i_data, pos_out, |
1368 | PAGE_ALIGN(destoff + len) - 1); | 1462 | PAGE_ALIGN(pos_out + len) - 1); |
1369 | 1463 | ||
1370 | dfsbno = XFS_B_TO_FSBT(mp, destoff); | 1464 | dfsbno = XFS_B_TO_FSBT(mp, pos_out); |
1371 | sfsbno = XFS_B_TO_FSBT(mp, srcoff); | 1465 | sfsbno = XFS_B_TO_FSBT(mp, pos_in); |
1372 | fsblen = XFS_B_TO_FSB(mp, len); | 1466 | fsblen = XFS_B_TO_FSB(mp, len); |
1373 | error = xfs_reflink_remap_blocks(src, sfsbno, dest, dfsbno, fsblen, | 1467 | ret = xfs_reflink_remap_blocks(src, sfsbno, dest, dfsbno, fsblen, |
1374 | destoff + len); | 1468 | pos_out + len); |
1375 | if (error) | 1469 | if (ret) |
1376 | goto out_error; | 1470 | goto out_unlock; |
1377 | 1471 | ||
1378 | /* | 1472 | /* |
1379 | * Carry the cowextsize hint from src to dest if we're sharing the | 1473 | * Carry the cowextsize hint from src to dest if we're sharing the |
@@ -1381,20 +1475,24 @@ xfs_reflink_remap_range( | |||
1381 | * has a cowextsize hint, and the destination file does not. | 1475 | * has a cowextsize hint, and the destination file does not. |
1382 | */ | 1476 | */ |
1383 | cowextsize = 0; | 1477 | cowextsize = 0; |
1384 | if (srcoff == 0 && len == i_size_read(VFS_I(src)) && | 1478 | if (pos_in == 0 && len == i_size_read(inode_in) && |
1385 | (src->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE) && | 1479 | (src->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE) && |
1386 | destoff == 0 && len >= i_size_read(VFS_I(dest)) && | 1480 | pos_out == 0 && len >= i_size_read(inode_out) && |
1387 | !(dest->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE)) | 1481 | !(dest->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE)) |
1388 | cowextsize = src->i_d.di_cowextsize; | 1482 | cowextsize = src->i_d.di_cowextsize; |
1389 | 1483 | ||
1390 | error = xfs_reflink_update_dest(dest, destoff + len, cowextsize); | 1484 | ret = xfs_reflink_update_dest(dest, pos_out + len, cowextsize); |
1391 | if (error) | ||
1392 | goto out_error; | ||
1393 | 1485 | ||
1394 | out_error: | 1486 | out_unlock: |
1395 | if (error) | 1487 | xfs_iunlock(src, XFS_MMAPLOCK_EXCL); |
1396 | trace_xfs_reflink_remap_range_error(dest, error, _RET_IP_); | 1488 | xfs_iunlock(src, XFS_IOLOCK_EXCL); |
1397 | return error; | 1489 | if (src->i_ino != dest->i_ino) { |
1490 | xfs_iunlock(dest, XFS_MMAPLOCK_EXCL); | ||
1491 | xfs_iunlock(dest, XFS_IOLOCK_EXCL); | ||
1492 | } | ||
1493 | if (ret) | ||
1494 | trace_xfs_reflink_remap_range_error(dest, ret, _RET_IP_); | ||
1495 | return ret; | ||
1398 | } | 1496 | } |
1399 | 1497 | ||
1400 | /* | 1498 | /* |
diff --git a/fs/xfs/xfs_reflink.h b/fs/xfs/xfs_reflink.h index 5dc3c8ac12aa..7ddd9f69560d 100644 --- a/fs/xfs/xfs_reflink.h +++ b/fs/xfs/xfs_reflink.h | |||
@@ -43,11 +43,8 @@ extern int xfs_reflink_cancel_cow_range(struct xfs_inode *ip, xfs_off_t offset, | |||
43 | extern int xfs_reflink_end_cow(struct xfs_inode *ip, xfs_off_t offset, | 43 | extern int xfs_reflink_end_cow(struct xfs_inode *ip, xfs_off_t offset, |
44 | xfs_off_t count); | 44 | xfs_off_t count); |
45 | extern int xfs_reflink_recover_cow(struct xfs_mount *mp); | 45 | extern int xfs_reflink_recover_cow(struct xfs_mount *mp); |
46 | #define XFS_REFLINK_DEDUPE 1 /* only reflink if contents match */ | 46 | extern int xfs_reflink_remap_range(struct file *file_in, loff_t pos_in, |
47 | #define XFS_REFLINK_ALL (XFS_REFLINK_DEDUPE) | 47 | struct file *file_out, loff_t pos_out, u64 len, bool is_dedupe); |
48 | extern int xfs_reflink_remap_range(struct xfs_inode *src, xfs_off_t srcoff, | ||
49 | struct xfs_inode *dest, xfs_off_t destoff, xfs_off_t len, | ||
50 | unsigned int flags); | ||
51 | extern int xfs_reflink_clear_inode_flag(struct xfs_inode *ip, | 48 | extern int xfs_reflink_clear_inode_flag(struct xfs_inode *ip, |
52 | struct xfs_trans **tpp); | 49 | struct xfs_trans **tpp); |
53 | extern int xfs_reflink_unshare(struct xfs_inode *ip, xfs_off_t offset, | 50 | extern int xfs_reflink_unshare(struct xfs_inode *ip, xfs_off_t offset, |