buffered write and indlen fixes
v2 cover buffered write and indlen fixes
Hi all,
This is v2 of the indlen split fixup series. The indlen patches are mostly the same except for changes to use do_div() rather than built-in division, which causes problems on 32-bit arches.
Patches 1-2 are new and facilitate testing. Patch 1 fixes a couple issues in buffered write failure handling that I ran into. Patch 2 reintroduces the "fail writes" mechanism as a "drop writes" mechanism to re-enable the ability to test the indlen code from userspace. I will follow up with xfstests patches shortly that use this mechanism to reproduce both issues fixed by patches 3 and 4. Thoughts, reviews, flames appreciated.
Brian
v2:
- Use do_div() to fix 32-bit builds.
- Prepend patches 1-2 to re-enable indlen testing from xfstests.
v1: http://www.spinics.net/lists/linux-xfs/msg04083.html
Brian Foster (4):
xfs: clear delalloc and cache on buffered write failure
xfs: resurrect debug mode drop buffered writes mechanism
xfs: handle indlen shortage on delalloc extent merge
xfs: split indlen reservations fairly when under reserved
fs/xfs/libxfs/xfs_bmap.c | 70 +++++++++++++++++++++++++++++++++--------------- fs/xfs/xfs_iomap.c | 9 +++++++ fs/xfs/xfs_mount.h | 15 ++++++----- fs/xfs/xfs_sysfs.c | 14 +++++----- 4 files changed, 73 insertions(+), 35 deletions(-)
Signature · 1 line
2.7.4
PATCH v2 1/4 xfs: clear delalloc and cache on buffered write failure
The buffered write failure handling code in xfs_file_iomap_end_delalloc() has a couple minor problems. First, if written == 0, start_fsb is not rounded down and it fails to kill off a delalloc block if the start offset is block unaligned. This results in a lingering delalloc block and broken delalloc block accounting detected at unmount time. Fix this by rounding down start_fsb in the unlikely event that written == 0.
Second, it is possible for a failed overwrite of a delalloc extent to leave dirty pagecache around over a hole in the file. This is because is possible to hit ->iomap_end() on write failure before the iomap code has attempted to allocate pagecache, and thus has no need to clean it up. If the targeted delalloc extent was successfully written by a previous write, however, then it does still have dirty pages when ->iomap_end() punches out the underlying blocks. This ultimately results in writeback over a hole. To fix this problem, unconditionally punch out the pagecache from XFS before the associated delalloc range.
--- fs/xfs/xfs_iomap.c | 5 +++++ 1 file changed, 5 insertions(+)
fs/xfs/xfs_iomap.c +5 -0
@@ -1103,6 +1103,8 @@ xfs_file_iomap_end_delalloc(int error =0 ; start_fsb =XFS_B_TO_FSB (mp, offset + written); +if (unlikely (!written)) + start_fsb =XFS_B_TO_FSBT (mp, offset); end_fsb =XFS_B_TO_FSB (mp, offset + length);/*
@@ -1114,6 +1116,9 @@ xfs_file_iomap_end_delalloc(* blocks in the range, they are ours. */ if (start_fsb < end_fsb) { +truncate_pagecache_range (VFS_I (ip),XFS_FSB_TO_B (mp, start_fsb), +XFS_FSB_TO_B (mp, end_fsb) -1 ); +xfs_ilock (ip,XFS_ILOCK_EXCL ); error =xfs_bmap_punch_delalloc_range (ip, start_fsb, end_fsb - start_fsb);
Signature · 1 line
2.7.4
PATCH v2 2/4 xfs: resurrect debug mode drop buffered writes mechanism
In reply to Brian FosterA debug mode write failure mechanism was introduced to XFS in commit 801cc4e17a ("xfs: debug mode forced buffered write failure") to facilitate targeted testing of delalloc indirect reservation management from userspace. This code was subsequently rendered ineffective by the move to iomap based buffered writes in commit 68a9f5e700 ("xfs:
implement iomap based buffered write path"). This likely went unnoticed because the associated userspace code had not made it into xfstests.
Resurrect this mechanism to facilitate effective indlen reservation testing from xfstests. The move to iomap based buffered writes relocated the hook this mechanism needs to return write failure from XFS to generic code. The failure trigger must remain in XFS. Given that limitation, convert this from a write failure mechanism to one that simply drops writes without returning failure to userspace. Rename all "fail_writes" references to "drop_writes" to illustrate the point. This is more hacky than preferred, but still triggers the XFS error handling behavior required to drive the indlen tests. This is only available in DEBUG mode and for testing purposes only.
--- fs/xfs/xfs_iomap.c | 4 ++++ fs/xfs/xfs_mount.h | 15 ++++++++------- fs/xfs/xfs_sysfs.c | 14 +++++++------- 3 files changed, 19 insertions(+), 14 deletions(-)
fs/xfs/xfs_iomap.c +4 -0
@@ -1102,6 +1102,10 @@ xfs_file_iomap_end_delalloc(xfs_fileoff_t end_fsb;int error =0 ; +/* behave as if the write failed if drop writes is enabled */ +if (xfs_mp_drop_writes (mp)) + written =0 ; + start_fsb =XFS_B_TO_FSB (mp, offset + written);if (unlikely (!written)) start_fsb =XFS_B_TO_FSBT (mp, offset);
fs/xfs/xfs_mount.h +8 -7
@@ -200,11 +200,12 @@ typedef struct xfs_mount {/* * DEBUG mode instrumentation to test and/or trigger delayed allocation * block killing in the event of failed writes. When enabled, all -* buffered writes are forced to fail. All delalloc blocks in the range -* of the write (including pre-existing delalloc blocks!) are tossed as -* part of the write failure error handling sequence. +* buffered writes are silenty dropped and handled as if they failed. +* All delalloc blocks in the range of the write (including pre-existing +* delalloc blocks!) are tossed as part of the write failure error +* handling sequence. */ -bool m_fail_writes; +bool m_drop_writes;#endif } xfs_mount_t;
@@ -325,13 +326,13 @@ xfs_daddr_to_agbno(struct xfs_mount *mp, xfs_daddr_t d)#ifdef DEBUG static inline bool -xfs_mp_fail_writes (struct xfs_mount *mp) +xfs_mp_drop_writes (struct xfs_mount *mp) { -return mp->m_fail_writes; +return mp->m_drop_writes; }#else static inline bool -xfs_mp_fail_writes (struct xfs_mount *mp) +xfs_mp_drop_writes (struct xfs_mount *mp) {return 0 ; }
fs/xfs/xfs_sysfs.c +7 -7
@@ -93,7 +93,7 @@ to_mp(struct kobject *kobject)#ifdef DEBUG STATIC ssize_t -fail_writes_store ( +drop_writes_store (struct kobject *kobject,const char *buf,size_t count)
@@ -107,9 +107,9 @@ fail_writes_store(return ret;if (val ==1 ) - mp->m_fail_writes = true; + mp->m_drop_writes = true;else if (val ==0 ) - mp->m_fail_writes = false; + mp->m_drop_writes = false;else return -EINVAL ;
@@ -117,21 +117,21 @@ fail_writes_store( } STATIC ssize_t -fail_writes_show ( +drop_writes_show (struct kobject *kobject,char *buf) {struct xfs_mount *mp =to_mp (kobject); -return snprintf (buf,PAGE_SIZE ,"%d\n" , mp->m_fail_writes ?1 :0 ); +return snprintf (buf,PAGE_SIZE ,"%d\n" , mp->m_drop_writes ?1 :0 ); } -XFS_SYSFS_ATTR_RW (fail_writes); +XFS_SYSFS_ATTR_RW (drop_writes);#endif /* DEBUG */ static struct attribute *xfs_mp_attrs[] = {#ifdef DEBUG -ATTR_LIST (fail_writes), +ATTR_LIST (drop_writes), #endif NULL, };
Signature · 1 line
2.7.4
PATCH v2 3/4 xfs: handle indlen shortage on delalloc extent merge
In reply to Brian FosterWhen a delalloc extent is created, it can be merged with pre-existing, contiguous, delalloc extents. When this occurs, xfs_bmap_add_extent_hole_delay() merges the extents along with the associated indirect block reservations. The expectation here is that the combined worst case indlen reservation is always less than or equal to the indlen reservation for the individual extents.
This is not always the case, however, as existing extents can less than the expected indlen reservation if the extent was previously split due to a hole punch. If a new extent merges with such an extent, the total indlen requirement may be larger than the sum of the indlen reservations held by both extents.
xfs_bmap_add_extent_hole_delay() assumes that the worst case indlen reservation is always available and assigns it to the merged extent without consideration for the indlen held by the pre-existing extent. As a result, the subsequent xfs_mod_fdblocks() call can attempt an unintentional allocation rather than a free (indicated by an ASSERT() failure). Further, if the allocation happens to fail in this context, the failure goes unhandled and creates a filesystem wide block accounting inconsistency.
Fix xfs_bmap_add_extent_hole_delay() to function as designed. Cap the indlen reservation assigned to the merged extent to the sum of the indlen reservations held by each of the individual extents.
--- fs/xfs/libxfs/xfs_bmap.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
fs/xfs/libxfs/xfs_bmap.c +6 -3
@@ -2809,7 +2809,8 @@ xfs_bmap_add_extent_hole_delay( oldlen =startblockval (left.br_startblock) +startblockval (new->br_startblock) +startblockval (right.br_startblock); - newlen =xfs_bmap_worst_indlen (ip, temp); + newlen =XFS_FILBLKS_MIN (xfs_bmap_worst_indlen (ip, temp), + oldlen);xfs_bmbt_set_startblock (xfs_iext_get_ext (ifp, *idx),nullstartblock ((int )newlen));trace_xfs_bmap_post_update (ip, *idx, state, _THIS_IP_);
@@ -2830,7 +2831,8 @@ xfs_bmap_add_extent_hole_delay(xfs_bmbt_set_blockcount (xfs_iext_get_ext (ifp, *idx), temp); oldlen =startblockval (left.br_startblock) +startblockval (new->br_startblock); - newlen =xfs_bmap_worst_indlen (ip, temp); + newlen =XFS_FILBLKS_MIN (xfs_bmap_worst_indlen (ip, temp), + oldlen);xfs_bmbt_set_startblock (xfs_iext_get_ext (ifp, *idx),nullstartblock ((int )newlen));trace_xfs_bmap_post_update (ip, *idx, state, _THIS_IP_);
@@ -2846,7 +2848,8 @@ xfs_bmap_add_extent_hole_delay( temp = new->br_blockcount + right.br_blockcount; oldlen =startblockval (new->br_startblock) +startblockval (right.br_startblock); - newlen =xfs_bmap_worst_indlen (ip, temp); + newlen =XFS_FILBLKS_MIN (xfs_bmap_worst_indlen (ip, temp), + oldlen);xfs_bmbt_set_allf (xfs_iext_get_ext (ifp, *idx), new->br_startoff,nullstartblock ((int )newlen), temp, right.br_state);
Signature · 1 line
2.7.4
PATCH v2 4/4 xfs: split indlen reservations fairly when under reserved
In reply to Brian FosterCertain workoads that punch holes into speculative preallocation can cause delalloc indirect reservation splits when the delalloc extent is split in two. If further splits occur, an already short-handed extent can be split into two in a manner that leaves zero indirect blocks for one of the two new extents. This occurs because the shortage is large enough that the xfs_bmap_split_indlen() algorithm completely drains the requested indlen of one of the extents before it honors the existing reservation.
This ultimately results in a warning from xfs_bmap_del_extent(). This has been observed during file copies of large, sparse files using 'cp --sparse=always.'
To avoid this problem, update xfs_bmap_split_indlen() to explicitly apply the reservation shortage fairly between both extents. This smooths out the overall indlen shortage and defers the situation where we end up with a delalloc extent with zero indlen reservation to extreme circumstances.
--- fs/xfs/libxfs/xfs_bmap.c | 61 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 18 deletions(-)
fs/xfs/libxfs/xfs_bmap.c +43 -18
@@ -4790,6 +4790,7 @@ xfs_bmap_split_indlen(xfs_filblks_t len2 = *indlen2;xfs_filblks_t nres = len1 + len2;/* new total res. */ xfs_filblks_t stolen =0 ; +xfs_filblks_t resfactor;trace_printk ("%d: ores %llu len1 %llu len2 %llu\n" , __LINE__, ores, len1, len2);
@@ -4797,29 +4798,53 @@ xfs_bmap_split_indlen(* Steal as many blocks as we can to try and satisfy the worst case * indlen for both new extents. */ -while (nres > ores && avail) { - nres--; - avail--; - stolen++; - } +if (ores < nres && avail) + stolen =XFS_FILBLKS_MIN (nres - ores, avail); + ores += stolen; + +/* nothing else to do if we've satisfied the new reservation */ +if (ores >= nres) +return stolen; + +/* +* We can't meet the total required reservation for the two extents. +* Calculate the percent of the overall shortage between both extents +* and apply this percentage to each of the requested indlen values. +* This distributes the shortage fairly and reduces the chances that one +* of the two extents is left with nothing when extents are repeatedly +* split. +*/ + resfactor = (ores *100 ); +do_div (resfactor, nres); + len1 *= resfactor; +do_div (len1,100 ); + len2 *= resfactor; +do_div (len2,100 ); +ASSERT (len1 + len2 <= ores); +ASSERT (len1 < *indlen1 && len2 < *indlen2);/* -* The only blocks available are those reserved for the original -* extent and what we can steal from the extent being removed. -* If this still isn't enough to satisfy the combined -* requirements for the two new extents, skim blocks off of each -* of the new reservations until they match what is available. +* Hand out the remainder to each extent. If one of the two reservations +* is zero, we want to make sure that one gets a block first. The loop +* below starts with len1, so hand len2 a block right off the bat if it +* is zero. */ -while (nres > ores) { -if (len1) { - len1--; - nres--; + ores -= (len1 + len2); +ASSERT ((*indlen1 - len1) + (*indlen2 - len2) >= ores); +if (ores && !len2 && *indlen2) { + len2++; + ores--; + } +while (ores) { +if (len1 < *indlen1) { + len1++; + ores--; } -if (nres == ores) +if (!ores)break ; -if (len2) { - len2--; - nres--; +if (len2 < *indlen2) { + len2++; + ores--; } }
Signature · 1 line
2.7.4
Re: [PATCH 4/4] xfs: split indlen reservations fairly when under reserved
Quoted 58 lines · Brian Foster
Certain workoads that punch holes into speculative preallocation can cause delalloc indirect reservation splits when the delalloc extent is split in two. If further splits occur, an already short-handed extent can be split into two in a manner that leaves zero indirect blocks for one of the two new extents. This occurs because the shortage is large enough that the
xfs_bmap_split_indlen()algorithm completely drains the requested indlen of one of the extents before it honors the existing reservation.This ultimately results in a warning from
xfs_bmap_del_extent(). This has been observed during file copies of large, sparse files using 'cp --sparse=always.'To avoid this problem, update
xfs_bmap_split_indlen()to explicitly apply the reservation shortage fairly between both extents. This smooths out the overall indlen shortage and defers the situation where we end up with a delalloc extent with zero indlen reservation to extreme circumstances.Reported-by Patrick Dung <[email protected]>Signed-off-by Brian Foster <[email protected]>--- fs/xfs/libxfs/xfs_bmap.c | 61 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 18 deletions(-)fs/xfs/libxfs/xfs_bmap.c +13
-5@@ -4790,6 +4790,7 @@ xfs_bmap_split_indlen(xfs_filblks_t len2 = *indlen2;xfs_filblks_t nres = len1 + len2;/* new total res. */ xfs_filblks_t stolen =0 ; +xfs_filblks_t resfactor;trace_printk ("%d: ores %llu len1 %llu len2 %llu\n" , __LINE__, ores, len1, len2);@@ -4797,29 +4798,53 @@ xfs_bmap_split_indlen(* Steal as many blocks as we can to try and satisfy the worst case * indlen for both new extents. */ -while (nres > ores && avail) { - nres--; - avail--; - stolen++; - } +if (ores < nres && avail) + stolen =XFS_FILBLKS_MIN (nres - ores, avail); + ores += stolen; + +/* nothing else to do if we've satisfied the new reservation */ +if (ores >= nres) +return stolen; + +/* +* We can't meet the total required reservation for the two extents. +* Calculate the percent of the overall shortage between both extents +* and apply this percentage to each of the requested indlen values.
fs/xfs/libxfs/xfs_bmap.c xfs_bmap_split_indlen( · line 4822
Quoted 10 lines
fs/xfs/libxfs/xfs_bmap.c +10
-0+* This distributes the shortage fairly and reduces the chances that one +* of the two extents is left with nothing when extents are repeatedly +* split. +*/ + resfactor = (ores *100 ); +do_div (resfactor, nres); + len1 *= resfactor; +do_div (len1,100 ); + len2 *= resfactor; +do_div (len2,100 );
/me wonders if it's worth saving ourselves two 64bit divisions by changing the 100 to 128 and shifting? Probably not.
--D
Quoted 48 lines
fs/xfs/libxfs/xfs_bmap.c +20
-13+ASSERT (len1 + len2 <= ores); +ASSERT (len1 < *indlen1 && len2 < *indlen2);/* -* The only blocks available are those reserved for the original -* extent and what we can steal from the extent being removed. -* If this still isn't enough to satisfy the combined -* requirements for the two new extents, skim blocks off of each -* of the new reservations until they match what is available. +* Hand out the remainder to each extent. If one of the two reservations +* is zero, we want to make sure that one gets a block first. The loop +* below starts with len1, so hand len2 a block right off the bat if it +* is zero. */ -while (nres > ores) { -if (len1) { - len1--; - nres--; + ores -= (len1 + len2); +ASSERT ((*indlen1 - len1) + (*indlen2 - len2) >= ores); +if (ores && !len2 && *indlen2) { + len2++; + ores--; + } +while (ores) { +if (len1 < *indlen1) { + len1++; + ores--; } -if (nres == ores) +if (!ores)break ; -if (len2) { - len2--; - nres--; +if (len2 < *indlen2) { + len2++; + ores--; } }Signature · 6 lines
2.7.4 -- To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 4/4] xfs: split indlen reservations fairly when under reserved
In reply to Brian FosterQuoted 30 lines · Brian Foster
Certain workoads that punch holes into speculative preallocation can cause delalloc indirect reservation splits when the delalloc extent is split in two. If further splits occur, an already short-handed extent can be split into two in a manner that leaves zero indirect blocks for one of the two new extents. This occurs because the shortage is large enough that the
xfs_bmap_split_indlen()algorithm completely drains the requested indlen of one of the extents before it honors the existing reservation.This ultimately results in a warning from
xfs_bmap_del_extent(). This has been observed during file copies of large, sparse files using 'cp --sparse=always.'To avoid this problem, update
xfs_bmap_split_indlen()to explicitly apply the reservation shortage fairly between both extents. This smooths out the overall indlen shortage and defers the situation where we end up with a delalloc extent with zero indlen reservation to extreme circumstances.Reported-by Patrick Dung <[email protected]>Signed-off-by Brian Foster <[email protected]>--- fs/xfs/libxfs/xfs_bmap.c | 61 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 18 deletions(-)fs/xfs/libxfs/xfs_bmap.c +0
-0
fs/xfs/libxfs/xfs_bmap.c xfs_bmap_split_indlen( · line 4795
Quoted 6 lines
fs/xfs/libxfs/xfs_bmap.c +1
-0@@ -4790,6 +4790,7 @@ xfs_bmap_split_indlen(xfs_filblks_t len2 = *indlen2;xfs_filblks_t nres = len1 + len2;/* new total res. */ xfs_filblks_t stolen =0 ; +xfs_filblks_t resfactor;trace_printk ("%d: ores %llu len1 %llu len2 %llu\n" , __LINE__, ores, len1, len2);
Is this line a leftover from debug code? Patch doesn't apply with this line on top of 4.10-rc8 kernel.
Thanks,
Eryu
Re: [PATCH 4/4] xfs: split indlen reservations fairly when under reserved
Quoted 36 lines · Eryu Guan
Quoted 35 lines · Brian Foster
Certain workoads that punch holes into speculative preallocation can cause delalloc indirect reservation splits when the delalloc extent is split in two. If further splits occur, an already short-handed extent can be split into two in a manner that leaves zero indirect blocks for one of the two new extents. This occurs because the shortage is large enough that the
xfs_bmap_split_indlen()algorithm completely drains the requested indlen of one of the extents before it honors the existing reservation.This ultimately results in a warning from
xfs_bmap_del_extent(). This has been observed during file copies of large, sparse files using 'cp --sparse=always.'To avoid this problem, update
xfs_bmap_split_indlen()to explicitly apply the reservation shortage fairly between both extents. This smooths out the overall indlen shortage and defers the situation where we end up with a delalloc extent with zero indlen reservation to extreme circumstances.Reported-by Patrick Dung <[email protected]>Signed-off-by Brian Foster <[email protected]>--- fs/xfs/libxfs/xfs_bmap.c | 61 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 18 deletions(-)fs/xfs/libxfs/xfs_bmap.c +1
-0@@ -4790,6 +4790,7 @@ xfs_bmap_split_indlen(xfs_filblks_t len2 = *indlen2;xfs_filblks_t nres = len1 + len2;/* new total res. */ xfs_filblks_t stolen =0 ; +xfs_filblks_t resfactor;
Quoted 5 lines
fs/xfs/libxfs/xfs_bmap.c xfs_bmap_split_indlen( · line 4795
Quoted 1 line
fs/xfs/libxfs/xfs_bmap.c +0
-0trace_printk ("%d: ores %llu len1 %llu len2 %llu\n" , __LINE__, ores, len1, len2);Is this line a leftover from debug code? Patch doesn't apply with this line on top of 4.10-rc8 kernel.
Oops.. looks like I forgot to pull that stuff out of my branch. I'll post a new set with it removed.
Brian
Quoted 6 lines
Thanks,
Eryu
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html