Count the number of superblock writeback errors in the btrfs_device. That means we don't need the folio to stay around until it's waited for, and can avoid the extra call to folio_get/put. Also remove a mention of PageError in a comment. Cc: Chris Mason <clm@xxxxxx> Cc: Josef Bacik <josef@xxxxxxxxxxxxxx> Cc: David Sterba <dsterba@xxxxxxxx> Cc: linux-btrfs@xxxxxxxxxxxxxxx Signed-off-by: Matthew Wilcox (Oracle) <willy@xxxxxxxxxxxxx> --- fs/btrfs/disk-io.c | 44 ++++++++++++++++++-------------------------- fs/btrfs/extent_io.c | 2 +- fs/btrfs/volumes.h | 5 +++++ 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 18c47bf3f383..3fa073f969d8 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -3635,11 +3635,15 @@ static void btrfs_end_super_write(struct bio *bio) "lost sb write due to IO error on %s (%d)", btrfs_dev_name(device), blk_status_to_errno(bio->bi_status)); - folio_set_error(fi.folio); btrfs_dev_stat_inc_and_print(device, BTRFS_DEV_STAT_WRITE_ERRS); + /* Ensure failure if a primary sb fails */ + if (bio->bi_opf & REQ_FUA) + atomic_set(&device->sb_wb_errors, + BTRFS_DEV_PRIMARY_ERROR); + else + atomic_inc(&device->sb_wb_errors); } - folio_unlock(fi.folio); folio_put(fi.folio); } @@ -3743,10 +3747,11 @@ static int write_dev_supers(struct btrfs_device *device, struct address_space *mapping = device->bdev->bd_mapping; SHASH_DESC_ON_STACK(shash, fs_info->csum_shash); int i; - int errors = 0; int ret; u64 bytenr, bytenr_orig; + atomic_set(&device->sb_wb_errors, 0); + if (max_mirrors == 0) max_mirrors = BTRFS_SUPER_MIRROR_MAX; @@ -3766,7 +3771,7 @@ static int write_dev_supers(struct btrfs_device *device, btrfs_err(device->fs_info, "couldn't get super block location for mirror %d", i); - errors++; + atomic_inc(&device->sb_wb_errors); continue; } if (bytenr + BTRFS_SUPER_INFO_SIZE >= @@ -3785,13 +3790,10 @@ static int write_dev_supers(struct btrfs_device *device, btrfs_err(device->fs_info, "couldn't get super block page for bytenr %llu", bytenr); - errors++; + atomic_inc(&device->sb_wb_errors); continue; } - /* Bump the refcount for wait_dev_supers() */ - folio_get(folio); - offset = offset_in_folio(folio, bytenr); disk_super = folio_address(folio) + offset; memcpy(disk_super, sb, BTRFS_SUPER_INFO_SIZE); @@ -3819,9 +3821,9 @@ static int write_dev_supers(struct btrfs_device *device, submit_bio(bio); if (btrfs_advance_sb_log(device, i)) - errors++; + atomic_inc(&device->sb_wb_errors); } - return errors < i ? 0 : -1; + return atomic_read(&device->sb_wb_errors) < i ? 0 : -1; } /* @@ -3860,27 +3862,17 @@ static int wait_dev_supers(struct btrfs_device *device, int max_mirrors) folio = filemap_get_folio(device->bdev->bd_mapping, bytenr >> PAGE_SHIFT); - if (IS_ERR(folio)) { - errors++; - if (i == 0) - primary_failed = true; + /* If the folio has been removed, then we know it completed */ + if (IS_ERR(folio)) continue; - } - /* Folio is unlocked once the write completes */ + /* Folio is unlocked once the IO completes */ folio_wait_locked(folio); - if (folio_test_error(folio)) { - errors++; - if (i == 0) - primary_failed = true; - } - - /* Drop our reference */ - folio_put(folio); - - /* Drop the reference from the writing run */ folio_put(folio); } + errors += atomic_read(&device->sb_wb_errors); + if (errors >= BTRFS_DEV_PRIMARY_ERROR) + primary_failed = true; /* log error, force error return */ if (primary_failed) { btrfs_err(device->fs_info, "error writing primary super block to device %llu", diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 7b10f47d8f83..7a1bd23833e5 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -1602,7 +1602,7 @@ static void set_btree_ioerr(struct extent_buffer *eb) * can be no longer dirty nor marked anymore for writeback (if a * subsequent modification to the extent buffer didn't happen before the * transaction commit), which makes filemap_fdata[write|wait]_range not - * able to find the pages tagged with SetPageError at transaction + * able to find the pages which contain errors at transaction * commit time. So if this happens we must abort the transaction, * otherwise we commit a super block with btree roots that point to * btree nodes/leafs whose content on disk is invalid - either garbage diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index cf555f5b47ce..cdab144410a8 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -92,6 +92,9 @@ enum btrfs_raid_types { #define BTRFS_DEV_STATE_FLUSH_SENT (4) #define BTRFS_DEV_STATE_NO_READA (5) +/* We'll never have this many superblocks */ +#define BTRFS_DEV_PRIMARY_ERROR (INT_MAX / 2) + struct btrfs_fs_devices; struct btrfs_device { @@ -142,6 +145,8 @@ struct btrfs_device { /* type and info about this device */ u64 type; + atomic_t sb_wb_errors; + /* minimal io size for this device */ u32 sector_size; -- 2.43.0