Skip to content

Commit ad48080

Browse files
committed
xfs: drop write error injection is unfixable, remove it
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2155605 Tested: With xfstests and bz reproducer With the changes to scan the page cache for dirty data to avoid data corruptions from partial write cleanup racing with other page cache operations, the drop writes error injection no longer works the same way it used to and causes xfs/196 to fail. This is because xfs/196 writes to the file and populates the page cache before it turns on the error injection and starts failing -overwrites-. The result is that the original drop-writes code failed writes only -after- overwriting the data in the cache, followed by invalidates the cached data, then punching out the delalloc extent from under that data. On the surface, this looks fine. The problem is that page cache invalidation *doesn't guarantee that it removes anything from the page cache* and it doesn't change the dirty state of the folio. When block size == page size and we do page aligned IO (as xfs/196 does) everything happens to align perfectly and page cache invalidation removes the single page folios that span the written data. Hence the followup delalloc punch pass does not find cached data over that range and it can punch the extent out. IOWs, xfs/196 "works" for block size == page size with the new code. I say "works", because it actually only works for the case where IO is page aligned, and no data was read from disk before writes occur. Because the moment we actually read data first, the readahead code allocates multipage folios and suddenly the invalidate code goes back to zeroing subfolio ranges without changing dirty state. Hence, with multipage folios in play, block size == page size is functionally identical to block size < page size behaviour, and drop-writes is manifestly broken w.r.t to this case. Invalidation of a subfolio range doesn't result in the folio being removed from the cache, just the range gets zeroed. Hence after we've sequentially walked over a folio that we've dirtied (via write data) and then invalidated, we end up with a dirty folio full of zeroed data. And because the new code skips punching ranges that have dirty folios covering them, we end up leaving the delalloc range intact after failing all the writes. Hence failed writes now end up writing zeroes to disk in the cases where invalidation zeroes folios rather than removing them from cache. This is a fundamental change of behaviour that is needed to avoid the data corruption vectors that exist in the old write fail path, and it renders the drop-writes injection non-functional and unworkable as it stands. As it is, I think the error injection is also now unnecessary, as partial writes that need delalloc extent are going to be a lot more common with stale iomap detection in place. Hence this patch removes the drop-writes error injection completely. xfs/196 can remain for testing kernels that don't have this data corruption fix, but those that do will report: xfs/196 3s ... [not run] XFS error injection drop_writes unknown on this kernel. Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Darrick J. Wong <djwong@kernel.org> (cherry picked from commit 6e8af15) Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com>
1 parent 5625ea0 commit ad48080

File tree

3 files changed

+25
-23
lines changed

3 files changed

+25
-23
lines changed

fs/xfs/libxfs/xfs_errortag.h

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,12 @@
4040
#define XFS_ERRTAG_REFCOUNT_FINISH_ONE 25
4141
#define XFS_ERRTAG_BMAP_FINISH_ONE 26
4242
#define XFS_ERRTAG_AG_RESV_CRITICAL 27
43+
4344
/*
44-
* DEBUG mode instrumentation to test and/or trigger delayed allocation
45-
* block killing in the event of failed writes. When enabled, all
46-
* buffered writes are silenty dropped and handled as if they failed.
47-
* All delalloc blocks in the range of the write (including pre-existing
48-
* delalloc blocks!) are tossed as part of the write failure error
49-
* handling sequence.
45+
* Drop-writes support removed because write error handling cannot trash
46+
* pre-existing delalloc extents in any useful way anymore. We retain the
47+
* definition so that we can reject it as an invalid value in
48+
* xfs_errortag_valid().
5049
*/
5150
#define XFS_ERRTAG_DROP_WRITES 28
5251
#define XFS_ERRTAG_LOG_BAD_CRC 29
@@ -92,7 +91,6 @@
9291
#define XFS_RANDOM_REFCOUNT_FINISH_ONE 1
9392
#define XFS_RANDOM_BMAP_FINISH_ONE 1
9493
#define XFS_RANDOM_AG_RESV_CRITICAL 4
95-
#define XFS_RANDOM_DROP_WRITES 1
9694
#define XFS_RANDOM_LOG_BAD_CRC 1
9795
#define XFS_RANDOM_LOG_ITEM_PIN 1
9896
#define XFS_RANDOM_BUF_LRU_REF 2

fs/xfs/xfs_error.c

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ static unsigned int xfs_errortag_random_default[] = {
4646
XFS_RANDOM_REFCOUNT_FINISH_ONE,
4747
XFS_RANDOM_BMAP_FINISH_ONE,
4848
XFS_RANDOM_AG_RESV_CRITICAL,
49-
XFS_RANDOM_DROP_WRITES,
49+
0, /* XFS_RANDOM_DROP_WRITES has been removed */
5050
XFS_RANDOM_LOG_BAD_CRC,
5151
XFS_RANDOM_LOG_ITEM_PIN,
5252
XFS_RANDOM_BUF_LRU_REF,
@@ -159,7 +159,6 @@ XFS_ERRORTAG_ATTR_RW(refcount_continue_update, XFS_ERRTAG_REFCOUNT_CONTINUE_UPDA
159159
XFS_ERRORTAG_ATTR_RW(refcount_finish_one, XFS_ERRTAG_REFCOUNT_FINISH_ONE);
160160
XFS_ERRORTAG_ATTR_RW(bmap_finish_one, XFS_ERRTAG_BMAP_FINISH_ONE);
161161
XFS_ERRORTAG_ATTR_RW(ag_resv_critical, XFS_ERRTAG_AG_RESV_CRITICAL);
162-
XFS_ERRORTAG_ATTR_RW(drop_writes, XFS_ERRTAG_DROP_WRITES);
163162
XFS_ERRORTAG_ATTR_RW(log_bad_crc, XFS_ERRTAG_LOG_BAD_CRC);
164163
XFS_ERRORTAG_ATTR_RW(log_item_pin, XFS_ERRTAG_LOG_ITEM_PIN);
165164
XFS_ERRORTAG_ATTR_RW(buf_lru_ref, XFS_ERRTAG_BUF_LRU_REF);
@@ -200,7 +199,6 @@ static struct attribute *xfs_errortag_attrs[] = {
200199
XFS_ERRORTAG_ATTR_LIST(refcount_finish_one),
201200
XFS_ERRORTAG_ATTR_LIST(bmap_finish_one),
202201
XFS_ERRORTAG_ATTR_LIST(ag_resv_critical),
203-
XFS_ERRORTAG_ATTR_LIST(drop_writes),
204202
XFS_ERRORTAG_ATTR_LIST(log_bad_crc),
205203
XFS_ERRORTAG_ATTR_LIST(log_item_pin),
206204
XFS_ERRORTAG_ATTR_LIST(buf_lru_ref),
@@ -241,6 +239,19 @@ xfs_errortag_del(
241239
kmem_free(mp->m_errortag);
242240
}
243241

242+
static bool
243+
xfs_errortag_valid(
244+
unsigned int error_tag)
245+
{
246+
if (error_tag >= XFS_ERRTAG_MAX)
247+
return false;
248+
249+
/* Error out removed injection types */
250+
if (error_tag == XFS_ERRTAG_DROP_WRITES)
251+
return false;
252+
return true;
253+
}
254+
244255
bool
245256
xfs_errortag_test(
246257
struct xfs_mount *mp,
@@ -262,7 +273,9 @@ xfs_errortag_test(
262273
if (!mp->m_errortag)
263274
return false;
264275

265-
ASSERT(error_tag < XFS_ERRTAG_MAX);
276+
if (!xfs_errortag_valid(error_tag))
277+
return false;
278+
266279
randfactor = mp->m_errortag[error_tag];
267280
if (!randfactor || prandom_u32() % randfactor)
268281
return false;
@@ -278,7 +291,7 @@ xfs_errortag_get(
278291
struct xfs_mount *mp,
279292
unsigned int error_tag)
280293
{
281-
if (error_tag >= XFS_ERRTAG_MAX)
294+
if (!xfs_errortag_valid(error_tag))
282295
return -EINVAL;
283296

284297
return mp->m_errortag[error_tag];
@@ -290,7 +303,7 @@ xfs_errortag_set(
290303
unsigned int error_tag,
291304
unsigned int tag_value)
292305
{
293-
if (error_tag >= XFS_ERRTAG_MAX)
306+
if (!xfs_errortag_valid(error_tag))
294307
return -EINVAL;
295308

296309
mp->m_errortag[error_tag] = tag_value;
@@ -304,7 +317,7 @@ xfs_errortag_add(
304317
{
305318
BUILD_BUG_ON(ARRAY_SIZE(xfs_errortag_random_default) != XFS_ERRTAG_MAX);
306319

307-
if (error_tag >= XFS_ERRTAG_MAX)
320+
if (!xfs_errortag_valid(error_tag))
308321
return -EINVAL;
309322

310323
return xfs_errortag_set(mp, error_tag,

fs/xfs/xfs_iomap.c

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,15 +1146,6 @@ xfs_buffered_write_iomap_end(
11461146
struct xfs_mount *mp = XFS_M(inode->i_sb);
11471147
int error;
11481148

1149-
/*
1150-
* Behave as if the write failed if drop writes is enabled. Set the NEW
1151-
* flag to force delalloc cleanup.
1152-
*/
1153-
if (XFS_TEST_ERROR(false, mp, XFS_ERRTAG_DROP_WRITES)) {
1154-
iomap->flags |= IOMAP_F_NEW;
1155-
written = 0;
1156-
}
1157-
11581149
error = iomap_file_buffered_write_punch_delalloc(inode, iomap, offset,
11591150
length, written, &xfs_buffered_write_delalloc_punch);
11601151
if (error && !xfs_is_shutdown(mp)) {

0 commit comments

Comments
 (0)