diff options
author | Brian Foster <bfoster@redhat.com> | 2017-01-28 02:22:55 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-04-08 03:30:30 -0400 |
commit | 798b1dc5cbdfbbb3ac0d45177a1fc1dd511e3469 (patch) | |
tree | 920b437caca55d7e992381e5a83f94cd00418276 /fs | |
parent | 08a2a26816825b2724fa6e2616df716b31e4a582 (diff) |
xfs: pull up iolock from xfs_free_eofblocks()
commit a36b926180cda375ac2ec89e1748b47137cfc51c upstream.
xfs_free_eofblocks() requires the IOLOCK_EXCL lock, but is called from
different contexts where the lock may or may not be held. The
need_iolock parameter exists for this reason, to indicate whether
xfs_free_eofblocks() must acquire the iolock itself before it can
proceed.
This is ugly and confusing. Simplify the semantics of
xfs_free_eofblocks() to require the caller to acquire the iolock
appropriately and kill the need_iolock parameter. While here, the mp
param can be removed as well as the xfs_mount is accessible from the
xfs_inode structure. This patch does not change behavior.
Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/xfs/xfs_bmap_util.c | 41 | ||||
-rw-r--r-- | fs/xfs/xfs_bmap_util.h | 3 | ||||
-rw-r--r-- | fs/xfs/xfs_icache.c | 24 | ||||
-rw-r--r-- | fs/xfs/xfs_inode.c | 51 |
4 files changed, 60 insertions, 59 deletions
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c index efb8ccd6bbf2..d8ac76ca05a2 100644 --- a/fs/xfs/xfs_bmap_util.c +++ b/fs/xfs/xfs_bmap_util.c | |||
@@ -917,17 +917,18 @@ xfs_can_free_eofblocks(struct xfs_inode *ip, bool force) | |||
917 | */ | 917 | */ |
918 | int | 918 | int |
919 | xfs_free_eofblocks( | 919 | xfs_free_eofblocks( |
920 | xfs_mount_t *mp, | 920 | struct xfs_inode *ip) |
921 | xfs_inode_t *ip, | ||
922 | bool need_iolock) | ||
923 | { | 921 | { |
924 | xfs_trans_t *tp; | 922 | struct xfs_trans *tp; |
925 | int error; | 923 | int error; |
926 | xfs_fileoff_t end_fsb; | 924 | xfs_fileoff_t end_fsb; |
927 | xfs_fileoff_t last_fsb; | 925 | xfs_fileoff_t last_fsb; |
928 | xfs_filblks_t map_len; | 926 | xfs_filblks_t map_len; |
929 | int nimaps; | 927 | int nimaps; |
930 | xfs_bmbt_irec_t imap; | 928 | struct xfs_bmbt_irec imap; |
929 | struct xfs_mount *mp = ip->i_mount; | ||
930 | |||
931 | ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL)); | ||
931 | 932 | ||
932 | /* | 933 | /* |
933 | * Figure out if there are any blocks beyond the end | 934 | * Figure out if there are any blocks beyond the end |
@@ -944,6 +945,10 @@ xfs_free_eofblocks( | |||
944 | error = xfs_bmapi_read(ip, end_fsb, map_len, &imap, &nimaps, 0); | 945 | error = xfs_bmapi_read(ip, end_fsb, map_len, &imap, &nimaps, 0); |
945 | xfs_iunlock(ip, XFS_ILOCK_SHARED); | 946 | xfs_iunlock(ip, XFS_ILOCK_SHARED); |
946 | 947 | ||
948 | /* | ||
949 | * If there are blocks after the end of file, truncate the file to its | ||
950 | * current size to free them up. | ||
951 | */ | ||
947 | if (!error && (nimaps != 0) && | 952 | if (!error && (nimaps != 0) && |
948 | (imap.br_startblock != HOLESTARTBLOCK || | 953 | (imap.br_startblock != HOLESTARTBLOCK || |
949 | ip->i_delayed_blks)) { | 954 | ip->i_delayed_blks)) { |
@@ -954,22 +959,10 @@ xfs_free_eofblocks( | |||
954 | if (error) | 959 | if (error) |
955 | return error; | 960 | return error; |
956 | 961 | ||
957 | /* | ||
958 | * There are blocks after the end of file. | ||
959 | * Free them up now by truncating the file to | ||
960 | * its current size. | ||
961 | */ | ||
962 | if (need_iolock) { | ||
963 | if (!xfs_ilock_nowait(ip, XFS_IOLOCK_EXCL)) | ||
964 | return -EAGAIN; | ||
965 | } | ||
966 | |||
967 | error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, | 962 | error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, |
968 | &tp); | 963 | &tp); |
969 | if (error) { | 964 | if (error) { |
970 | ASSERT(XFS_FORCED_SHUTDOWN(mp)); | 965 | ASSERT(XFS_FORCED_SHUTDOWN(mp)); |
971 | if (need_iolock) | ||
972 | xfs_iunlock(ip, XFS_IOLOCK_EXCL); | ||
973 | return error; | 966 | return error; |
974 | } | 967 | } |
975 | 968 | ||
@@ -997,8 +990,6 @@ xfs_free_eofblocks( | |||
997 | } | 990 | } |
998 | 991 | ||
999 | xfs_iunlock(ip, XFS_ILOCK_EXCL); | 992 | xfs_iunlock(ip, XFS_ILOCK_EXCL); |
1000 | if (need_iolock) | ||
1001 | xfs_iunlock(ip, XFS_IOLOCK_EXCL); | ||
1002 | } | 993 | } |
1003 | return error; | 994 | return error; |
1004 | } | 995 | } |
@@ -1415,7 +1406,7 @@ xfs_shift_file_space( | |||
1415 | * into the accessible region of the file. | 1406 | * into the accessible region of the file. |
1416 | */ | 1407 | */ |
1417 | if (xfs_can_free_eofblocks(ip, true)) { | 1408 | if (xfs_can_free_eofblocks(ip, true)) { |
1418 | error = xfs_free_eofblocks(mp, ip, false); | 1409 | error = xfs_free_eofblocks(ip); |
1419 | if (error) | 1410 | if (error) |
1420 | return error; | 1411 | return error; |
1421 | } | 1412 | } |
diff --git a/fs/xfs/xfs_bmap_util.h b/fs/xfs/xfs_bmap_util.h index 68a621a8e0c0..f1005393785c 100644 --- a/fs/xfs/xfs_bmap_util.h +++ b/fs/xfs/xfs_bmap_util.h | |||
@@ -63,8 +63,7 @@ int xfs_insert_file_space(struct xfs_inode *, xfs_off_t offset, | |||
63 | 63 | ||
64 | /* EOF block manipulation functions */ | 64 | /* EOF block manipulation functions */ |
65 | bool xfs_can_free_eofblocks(struct xfs_inode *ip, bool force); | 65 | bool xfs_can_free_eofblocks(struct xfs_inode *ip, bool force); |
66 | int xfs_free_eofblocks(struct xfs_mount *mp, struct xfs_inode *ip, | 66 | int xfs_free_eofblocks(struct xfs_inode *ip); |
67 | bool need_iolock); | ||
68 | 67 | ||
69 | int xfs_swap_extents(struct xfs_inode *ip, struct xfs_inode *tip, | 68 | int xfs_swap_extents(struct xfs_inode *ip, struct xfs_inode *tip, |
70 | struct xfs_swapext *sx); | 69 | struct xfs_swapext *sx); |
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index 29cc9886a3cb..e4b382aabf9f 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c | |||
@@ -1324,7 +1324,7 @@ xfs_inode_free_eofblocks( | |||
1324 | int flags, | 1324 | int flags, |
1325 | void *args) | 1325 | void *args) |
1326 | { | 1326 | { |
1327 | int ret; | 1327 | int ret = 0; |
1328 | struct xfs_eofblocks *eofb = args; | 1328 | struct xfs_eofblocks *eofb = args; |
1329 | bool need_iolock = true; | 1329 | bool need_iolock = true; |
1330 | int match; | 1330 | int match; |
@@ -1360,19 +1360,25 @@ xfs_inode_free_eofblocks( | |||
1360 | return 0; | 1360 | return 0; |
1361 | 1361 | ||
1362 | /* | 1362 | /* |
1363 | * A scan owner implies we already hold the iolock. Skip it in | 1363 | * A scan owner implies we already hold the iolock. Skip it here |
1364 | * xfs_free_eofblocks() to avoid deadlock. This also eliminates | 1364 | * to avoid deadlock. |
1365 | * the possibility of EAGAIN being returned. | ||
1366 | */ | 1365 | */ |
1367 | if (eofb->eof_scan_owner == ip->i_ino) | 1366 | if (eofb->eof_scan_owner == ip->i_ino) |
1368 | need_iolock = false; | 1367 | need_iolock = false; |
1369 | } | 1368 | } |
1370 | 1369 | ||
1371 | ret = xfs_free_eofblocks(ip->i_mount, ip, need_iolock); | 1370 | /* |
1372 | 1371 | * If the caller is waiting, return -EAGAIN to keep the background | |
1373 | /* don't revisit the inode if we're not waiting */ | 1372 | * scanner moving and revisit the inode in a subsequent pass. |
1374 | if (ret == -EAGAIN && !(flags & SYNC_WAIT)) | 1373 | */ |
1375 | ret = 0; | 1374 | if (need_iolock && !xfs_ilock_nowait(ip, XFS_IOLOCK_EXCL)) { |
1375 | if (flags & SYNC_WAIT) | ||
1376 | ret = -EAGAIN; | ||
1377 | return ret; | ||
1378 | } | ||
1379 | ret = xfs_free_eofblocks(ip); | ||
1380 | if (need_iolock) | ||
1381 | xfs_iunlock(ip, XFS_IOLOCK_EXCL); | ||
1376 | 1382 | ||
1377 | return ret; | 1383 | return ret; |
1378 | } | 1384 | } |
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index a1c7e138dbca..f9f44cb56fe8 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c | |||
@@ -1701,32 +1701,34 @@ xfs_release( | |||
1701 | if (xfs_can_free_eofblocks(ip, false)) { | 1701 | if (xfs_can_free_eofblocks(ip, false)) { |
1702 | 1702 | ||
1703 | /* | 1703 | /* |
1704 | * Check if the inode is being opened, written and closed | ||
1705 | * frequently and we have delayed allocation blocks outstanding | ||
1706 | * (e.g. streaming writes from the NFS server), truncating the | ||
1707 | * blocks past EOF will cause fragmentation to occur. | ||
1708 | * | ||
1709 | * In this case don't do the truncation, but we have to be | ||
1710 | * careful how we detect this case. Blocks beyond EOF show up as | ||
1711 | * i_delayed_blks even when the inode is clean, so we need to | ||
1712 | * truncate them away first before checking for a dirty release. | ||
1713 | * Hence on the first dirty close we will still remove the | ||
1714 | * speculative allocation, but after that we will leave it in | ||
1715 | * place. | ||
1716 | */ | ||
1717 | if (xfs_iflags_test(ip, XFS_IDIRTY_RELEASE)) | ||
1718 | return 0; | ||
1719 | /* | ||
1704 | * If we can't get the iolock just skip truncating the blocks | 1720 | * If we can't get the iolock just skip truncating the blocks |
1705 | * past EOF because we could deadlock with the mmap_sem | 1721 | * past EOF because we could deadlock with the mmap_sem |
1706 | * otherwise. We'll get another chance to drop them once the | 1722 | * otherwise. We'll get another chance to drop them once the |
1707 | * last reference to the inode is dropped, so we'll never leak | 1723 | * last reference to the inode is dropped, so we'll never leak |
1708 | * blocks permanently. | 1724 | * blocks permanently. |
1709 | * | ||
1710 | * Further, check if the inode is being opened, written and | ||
1711 | * closed frequently and we have delayed allocation blocks | ||
1712 | * outstanding (e.g. streaming writes from the NFS server), | ||
1713 | * truncating the blocks past EOF will cause fragmentation to | ||
1714 | * occur. | ||
1715 | * | ||
1716 | * In this case don't do the truncation, either, but we have to | ||
1717 | * be careful how we detect this case. Blocks beyond EOF show | ||
1718 | * up as i_delayed_blks even when the inode is clean, so we | ||
1719 | * need to truncate them away first before checking for a dirty | ||
1720 | * release. Hence on the first dirty close we will still remove | ||
1721 | * the speculative allocation, but after that we will leave it | ||
1722 | * in place. | ||
1723 | */ | 1725 | */ |
1724 | if (xfs_iflags_test(ip, XFS_IDIRTY_RELEASE)) | 1726 | if (xfs_ilock_nowait(ip, XFS_IOLOCK_EXCL)) { |
1725 | return 0; | 1727 | error = xfs_free_eofblocks(ip); |
1726 | 1728 | xfs_iunlock(ip, XFS_IOLOCK_EXCL); | |
1727 | error = xfs_free_eofblocks(mp, ip, true); | 1729 | if (error) |
1728 | if (error && error != -EAGAIN) | 1730 | return error; |
1729 | return error; | 1731 | } |
1730 | 1732 | ||
1731 | /* delalloc blocks after truncation means it really is dirty */ | 1733 | /* delalloc blocks after truncation means it really is dirty */ |
1732 | if (ip->i_delayed_blks) | 1734 | if (ip->i_delayed_blks) |
@@ -1913,8 +1915,11 @@ xfs_inactive( | |||
1913 | * cache. Post-eof blocks must be freed, lest we end up with | 1915 | * cache. Post-eof blocks must be freed, lest we end up with |
1914 | * broken free space accounting. | 1916 | * broken free space accounting. |
1915 | */ | 1917 | */ |
1916 | if (xfs_can_free_eofblocks(ip, true)) | 1918 | if (xfs_can_free_eofblocks(ip, true)) { |
1917 | xfs_free_eofblocks(mp, ip, false); | 1919 | xfs_ilock(ip, XFS_IOLOCK_EXCL); |
1920 | xfs_free_eofblocks(ip); | ||
1921 | xfs_iunlock(ip, XFS_IOLOCK_EXCL); | ||
1922 | } | ||
1918 | 1923 | ||
1919 | return; | 1924 | return; |
1920 | } | 1925 | } |