@@ -483,8 +483,14 @@ static void end_bio_extent_writepage(struct btrfs_bio *bbio)
bvec->bv_offset, bvec->bv_len);
btrfs_finish_ordered_extent(bbio->ordered, page, start, len, !error);
- if (error)
+ if (error) {
mapping_set_error(page->mapping, error);
+ if (btrfs_test_opt(fs_info, ABORT_DATA))
+ btrfs_handle_fs_error(fs_info, -EIO,
+ "data write back failed, root %lld ino %llu fileoff %llu",
+ BTRFS_I(inode)->root->root_key.objectid,
+ btrfs_ino(BTRFS_I(inode)), start);
+ }
btrfs_page_clear_writeback(fs_info, page, start, len);
}
@@ -189,6 +189,7 @@ enum {
BTRFS_MOUNT_IGNOREDATACSUMS = (1ULL << 28),
BTRFS_MOUNT_NODISCARD = (1ULL << 29),
BTRFS_MOUNT_ABORT_SUPER = (1ULL << 30),
+ BTRFS_MOUNT_ABORT_DATA = (1ULL << 31),
};
/*
@@ -7703,13 +7703,20 @@ static void btrfs_dio_end_io(struct btrfs_bio *bbio)
struct btrfs_dio_private *dip =
container_of(bbio, struct btrfs_dio_private, bbio);
struct btrfs_inode *inode = bbio->inode;
+ struct btrfs_fs_info *fs_info = inode->root->fs_info;
struct bio *bio = &bbio->bio;
if (bio->bi_status) {
- btrfs_warn(inode->root->fs_info,
+ btrfs_warn(fs_info,
"direct IO failed ino %llu op 0x%0x offset %#llx len %u err no %d",
btrfs_ino(inode), bio->bi_opf,
dip->file_offset, dip->bytes, bio->bi_status);
+ if (btrfs_test_opt(fs_info, ABORT_DATA))
+ btrfs_handle_fs_error(fs_info, -EIO,
+ "direct IO data write back failed, root %lld ino %llu fileoff %llu len %u",
+ inode->root->root_key.objectid,
+ btrfs_ino(inode), dip->file_offset,
+ dip->bytes);
}
if (btrfs_op(bio) == BTRFS_MAP_WRITE) {
@@ -128,6 +128,7 @@ enum {
/* Extra abort options. */
Opt_abort,
Opt_abort_super,
+ Opt_abort_data,
Opt_abort_all,
/* Deprecated options */
@@ -233,6 +234,7 @@ static const match_table_t rescue_tokens = {
static const match_table_t abort_tokens = {
{Opt_abort_super, "super"},
+ {Opt_abort_data, "data"},
{Opt_abort_all, "all"},
{Opt_err, NULL},
};
@@ -272,10 +274,16 @@ static int parse_abort_options(struct btrfs_fs_info *info, const char *options)
btrfs_set_and_info(info, ABORT_SUPER,
"will abort if any super block write back failed");
break;
+ case Opt_abort_data:
+ btrfs_set_and_info(info, ABORT_DATA,
+ "will abort if any data write back failed");
+ break;
case Opt_abort_all:
btrfs_info(info, "enabling all abort options");
btrfs_set_and_info(info, ABORT_SUPER,
"will abort if any super block write back failed");
+ btrfs_set_and_info(info, ABORT_DATA,
+ "will abort if any data write back failed");
break;
case Opt_err:
btrfs_info(info, "unrecognized abort option '%s'", p);
@@ -1310,6 +1318,8 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry)
print_rescue_option(seq, "ignoredatacsums", &rescue_printed);
if (btrfs_test_opt(info, ABORT_SUPER))
print_abort_option(seq, "super", &abort_printed);
+ if (btrfs_test_opt(info, ABORT_DATA))
+ print_abort_option(seq, "data", &abort_printed);
if (btrfs_test_opt(info, FLUSHONCOMMIT))
seq_puts(seq, ",flushoncommit");
if (btrfs_test_opt(info, DISCARD_SYNC))
Currently if btrfs fails to write data blocks, it will not really cause a great damage, but mostly -EIO for involved writeback functions like fsync() or direct io for that inode. Normally it's not a big deal, but it can be an indicator of a bigger problem. Thus here we introduce the new "abort=data" mount option to be noisy and mark the whole filesystem read-only as an early warning. This behavior covers buffered, direct writes. Please note that, it only counts as an write error if the write to all mirrors failed. E.g. if data write into a RAID1 data chunk only failed for one mirror, it will not be accounted as an write error. Signed-off-by: Qu Wenruo <wqu@suse.com> --- fs/btrfs/extent_io.c | 8 +++++++- fs/btrfs/fs.h | 1 + fs/btrfs/inode.c | 9 ++++++++- fs/btrfs/super.c | 10 ++++++++++ 4 files changed, 26 insertions(+), 2 deletions(-)