for-6.12-rc5-tag

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE8rQSAMVO+zA4DBdWxWXV+ddtWDsFAmck8eQACgkQxWXV+ddt
 WDu05g/6AwrnvPkivC4iVOv4Wkzrpk4gm76smx91Y9B8tSDLI1pHaS27CvJz9iWl
 vBKXPN3PQVQHwo6SPn+NjsFOSMkXlbBOVKpPU+MlZwH9Tuw66qcC+EnUCK2wEuAy
 3TN7cUGIA4r/j+SkhgIz+Irlr5pjdb1KkPIMBEVGcVFqDIuvDaTEGBqTn2i/V5aa
 dMn+gK+9rfngTOJ68t/pEFaX7SEWCvgMIcBpBB4/vs1gHm3ve2bcc1sBAdMxb1Se
 SrxgZfq+Rc5tkMn540JaWGwkb0rLzwXlurK6ygTKDKCpH0IMX+pBvDkexh9Zj0ux
 jejlRxiuDzTx3z2a7FjHDyp2sdZWMpq3sPsowpJ1Dsgi5EtSxTy4irmQuSAZY1Uj
 /uo6YwV9aTGeiNDwZeKqKc/wOuAttaMZLr14s37pro9KxndFJ/XZBxeyB+euUCOw
 B8AvAQVVIJAYQLyWINWruNKppqlgiO2RaN15RvvT2pX01d0TOx1KX1XFQku7YFxb
 M/8ZNXzJ96XtkeyHL3euo3zj7N5jWtnCvPINugUG1ADQa+bc8aX336gld1neD6fs
 QqIFIgzZG0l4N95viJilACrI6tW9zFnBqMyNFRhucKiX9aP9glOvhSfxfjcpDuQ/
 i/LIyxVLwp8M3hPNvv8tC345+1C2ug9AD0OyhWjjIYPuiOxtTWs=
 =alpB
 -----END PGP SIGNATURE-----

Merge tag 'for-6.12-rc5-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux

Pull btrfs fixes from David Sterba:
 "A few more stability fixes. There's one patch adding export of MIPS
  cmpxchg helper, used in the error propagation fix.

   - fix error propagation from split bios to the original btrfs bio

   - fix merging of adjacent extents (normal operation, defragmentation)

   - fix potential use after free after freeing btrfs device structures"

* tag 'for-6.12-rc5-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  btrfs: fix defrag not merging contiguous extents due to merged extent maps
  btrfs: fix extent map merging not happening for adjacent extents
  btrfs: fix use-after-free of block device file in __btrfs_free_extra_devids()
  btrfs: fix error propagation of split bios
  MIPS: export __cmpxchg_small()
This commit is contained in:
Linus Torvalds 2024-11-01 07:31:47 -10:00
commit 6b4926494e
6 changed files with 29 additions and 30 deletions

View File

@ -102,3 +102,4 @@ unsigned long __cmpxchg_small(volatile void *ptr, unsigned long old,
return old; return old;
} }
} }
EXPORT_SYMBOL(__cmpxchg_small);

View File

@ -49,6 +49,7 @@ void btrfs_bio_init(struct btrfs_bio *bbio, struct btrfs_fs_info *fs_info,
bbio->end_io = end_io; bbio->end_io = end_io;
bbio->private = private; bbio->private = private;
atomic_set(&bbio->pending_ios, 1); atomic_set(&bbio->pending_ios, 1);
WRITE_ONCE(bbio->status, BLK_STS_OK);
} }
/* /*
@ -113,41 +114,29 @@ static void __btrfs_bio_end_io(struct btrfs_bio *bbio)
} }
} }
static void btrfs_orig_write_end_io(struct bio *bio);
static void btrfs_bbio_propagate_error(struct btrfs_bio *bbio,
struct btrfs_bio *orig_bbio)
{
/*
* For writes we tolerate nr_mirrors - 1 write failures, so we can't
* just blindly propagate a write failure here. Instead increment the
* error count in the original I/O context so that it is guaranteed to
* be larger than the error tolerance.
*/
if (bbio->bio.bi_end_io == &btrfs_orig_write_end_io) {
struct btrfs_io_stripe *orig_stripe = orig_bbio->bio.bi_private;
struct btrfs_io_context *orig_bioc = orig_stripe->bioc;
atomic_add(orig_bioc->max_errors, &orig_bioc->error);
} else {
orig_bbio->bio.bi_status = bbio->bio.bi_status;
}
}
void btrfs_bio_end_io(struct btrfs_bio *bbio, blk_status_t status) void btrfs_bio_end_io(struct btrfs_bio *bbio, blk_status_t status)
{ {
bbio->bio.bi_status = status; bbio->bio.bi_status = status;
if (bbio->bio.bi_pool == &btrfs_clone_bioset) { if (bbio->bio.bi_pool == &btrfs_clone_bioset) {
struct btrfs_bio *orig_bbio = bbio->private; struct btrfs_bio *orig_bbio = bbio->private;
if (bbio->bio.bi_status)
btrfs_bbio_propagate_error(bbio, orig_bbio);
btrfs_cleanup_bio(bbio); btrfs_cleanup_bio(bbio);
bbio = orig_bbio; bbio = orig_bbio;
} }
if (atomic_dec_and_test(&bbio->pending_ios)) /*
* At this point, bbio always points to the original btrfs_bio. Save
* the first error in it.
*/
if (status != BLK_STS_OK)
cmpxchg(&bbio->status, BLK_STS_OK, status);
if (atomic_dec_and_test(&bbio->pending_ios)) {
/* Load split bio's error which might be set above. */
if (status == BLK_STS_OK)
bbio->bio.bi_status = READ_ONCE(bbio->status);
__btrfs_bio_end_io(bbio); __btrfs_bio_end_io(bbio);
}
} }
static int next_repair_mirror(struct btrfs_failed_bio *fbio, int cur_mirror) static int next_repair_mirror(struct btrfs_failed_bio *fbio, int cur_mirror)

View File

@ -79,6 +79,9 @@ struct btrfs_bio {
/* File system that this I/O operates on. */ /* File system that this I/O operates on. */
struct btrfs_fs_info *fs_info; struct btrfs_fs_info *fs_info;
/* Save the first error status of split bio. */
blk_status_t status;
/* /*
* This member must come last, bio_alloc_bioset will allocate enough * This member must come last, bio_alloc_bioset will allocate enough
* bytes for entire btrfs_bio but relies on bio being last. * bytes for entire btrfs_bio but relies on bio being last.

View File

@ -763,12 +763,12 @@ static struct extent_map *defrag_lookup_extent(struct inode *inode, u64 start,
* We can get a merged extent, in that case, we need to re-search * We can get a merged extent, in that case, we need to re-search
* tree to get the original em for defrag. * tree to get the original em for defrag.
* *
* If @newer_than is 0 or em::generation < newer_than, we can trust * This is because even if we have adjacent extents that are contiguous
* this em, as either we don't care about the generation, or the * and compatible (same type and flags), we still want to defrag them
* merged extent map will be rejected anyway. * so that we use less metadata (extent items in the extent tree and
* file extent items in the inode's subvolume tree).
*/ */
if (em && (em->flags & EXTENT_FLAG_MERGED) && if (em && (em->flags & EXTENT_FLAG_MERGED)) {
newer_than && em->generation >= newer_than) {
free_extent_map(em); free_extent_map(em);
em = NULL; em = NULL;
} }

View File

@ -230,7 +230,12 @@ static bool mergeable_maps(const struct extent_map *prev, const struct extent_ma
if (extent_map_end(prev) != next->start) if (extent_map_end(prev) != next->start)
return false; return false;
if (prev->flags != next->flags) /*
* The merged flag is not an on-disk flag, it just indicates we had the
* extent maps of 2 (or more) adjacent extents merged, so factor it out.
*/
if ((prev->flags & ~EXTENT_FLAG_MERGED) !=
(next->flags & ~EXTENT_FLAG_MERGED))
return false; return false;
if (next->disk_bytenr < EXTENT_MAP_LAST_BYTE - 1) if (next->disk_bytenr < EXTENT_MAP_LAST_BYTE - 1)

View File

@ -1105,6 +1105,7 @@ static void btrfs_close_one_device(struct btrfs_device *device)
if (device->bdev) { if (device->bdev) {
fs_devices->open_devices--; fs_devices->open_devices--;
device->bdev = NULL; device->bdev = NULL;
device->bdev_file = NULL;
} }
clear_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state); clear_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state);
btrfs_destroy_dev_zone_info(device); btrfs_destroy_dev_zone_info(device);