Pull MD changes from Yu Kuai:

"Bug Fixes:
- Wake RAID10 barrier waiters when a pending-count rollback satisfies
  an active array freeze. (Zizhi Wo)
- Reject v0.90 superblocks whose device count cannot fit the metadata
  format, preventing out-of-bounds access. (Jérémy Jean)
- Track RAID5 disk-array allocation size and avoid removing an
  unregistered CPU hotplug instance on setup or resize failure.
  (Cao Guanghui)
- Validate the RAID0 device count before allocating its device list.
  (Chandradhar Kumar)
- Validate RAID5 journal payload header, size, and checksum bounds
  before accessing them. (Martin Wilck)
- Reject inconsistent RAID5 reshape metadata instead of triggering a
  kernel BUG. (Yogesh Gaur)
- Reject invalid RAID4/5 disk counts before setup and invalid reshape
  disk counts in raid5_run. (Chen Cheng)
- Fix a NULL-pointer dereference in RAID5 setup error handling.
  (Zhihao Cheng)
- Reject RAID5 per-device sizes smaller than one chunk. (Zizhi Wo)
- Fix mddev_unlock() on an unacquired mutex in lbs_store().
  (Jack Wang)
- Give RAID5 io_unit slab caches per-array names to avoid duplicate
  cache names. (Yogesh Gaur)
- Validate v1.x bad-block log size when loading superblocks.
  (Yuchao Zhang)
- Take reconfig_mutex while enabling PPL on an inactive array to
  prevent a NULL PPL configuration. (Nguyen Ngoc Thang)
- Drain released RAID5 stripes before destroying the stripe cache on
  takeover setup failure. (Li Youhong)
- Protect RAID5 big_stripe_tree lookup with RCU. (Sang-Hoon Choi)
- Prevent a NULL mddev->private dereference when stopping an array
  while I/O is being submitted. (Zhihao Cheng)
- Don't publish an md array before md_alloc() finishes initializing
  mddev->kobj. (Yogesh Gaur)

Cleanups:
- Annotate intentional lockless RAID10 freeze-state accesses.
  (Zizhi Wo)
- Add shared RAID write-error and bad-block-sector helpers, and share
  flush_bio_list between RAID1 and RAID10. (Li Youhong)
- Extract RAID5 min_raid_disks() helper. (Chen Cheng)
- Use assign_bit() where applicable in MD/RAID. (Peng Fan)"

* tag 'md-7.4-20261013' of https://git.kernel.org/pub/scm/linux/kernel/git/mdraid/linux: (25 commits)
  md: don't hand out the array before md_alloc() has added mddev->kobj
  md: Fix the null-ptr-deref of 'mddev->private' while submitting IO
  md/raid5: protect big_stripe_tree lookup with RCU
  md/raid5: drain released_stripes before destroying the cache
  md: take reconfig_mutex when enabling PPL on an inactive array
  md: validate bblog_size when loading v1.x badblocks metadata
  md/raid: use assign_bit() where applicable
  md/raid5: reject invalid reshape disks in raid5_run()
  md/raid5: extract min_raid_disks()
  md/raid5: give the io_unit slab caches per-array names
  md: fix mddev_unlock() called without holding lock in lbs_store()
  md/raid1-10: share flush_bio_list between raid1 and raid10
  md/raid1-10: add rdev_bb_block_sectors() helper
  md: add rdev_record_write_error() helper
  md/raid5: reject a per-device size smaller than one chunk
  md/raid5: setup_conf: Fix null-ptr-def in error handling path
  md/raid5: reject raid4/5 arrays with too few disks
  md/raid5: don't BUG() on an inconsistent reshape state from the superblock
  md/raid5: validate payload size before calculating payload_len
  md/raid0: validate device count before allocating devlist
  ...
This commit is contained in:
Jens Axboe 2026-10-24 21:04:39 -06:00
commit f89fa411b8
10 changed files with 244 additions and 152 deletions

View File

@ -414,6 +414,20 @@ bool md_handle_request(struct mddev *mddev, struct bio *bio)
if (!percpu_ref_tryget_live(&mddev->active_io))
goto check_suspended;
}
if (!mddev->pers) {
/*
* The __md_stop() sets 'mddev->private' to NULL during
* the IO submitting, check 'mddev->pers' before the IO
* being processed by specific driver to avoid the
* null-ptr-deref of 'mddev-><member>'. The check is
* safe because the IO has got the 'mddev->active_io'
* reference, and all __md_stop() callers will wait for
* the reference to be zero.
*/
bio_io_error(bio);
percpu_ref_put(&mddev->active_io);
return true;
}
if (!mddev->pers->make_request(mddev, bio)) {
percpu_ref_put(&mddev->active_io);
if (mddev_is_dm(mddev) && mddev->pers->prepare_suspend)
@ -1393,7 +1407,7 @@ static int super_90_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor
goto abort;
}
if (sb->raid_disks <= 0)
if (sb->raid_disks <= 0 || sb->raid_disks > MD_SB_DISKS)
goto abort;
if (md_csum_fold(calc_sb_csum(sb)) != md_csum_fold(sb->sb_csum)) {
@ -1934,8 +1948,11 @@ static int super_1_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor_
if (!badblocks_set(&rdev->badblocks, sector, count, 1))
return -EINVAL;
}
} else if (sb->bblog_offset != 0)
} else if (sb->bblog_offset != 0) {
if (le16_to_cpu(sb->bblog_size) > (PAGE_SIZE / 512))
return -EINVAL;
rdev->badblocks.shift = 0;
}
if ((le32_to_cpu(sb->feature_map) &
(MD_FEATURE_PPL | MD_FEATURE_MULTIPLE_PPLS))) {
@ -3586,10 +3603,7 @@ static ssize_t recovery_start_store(struct md_rdev *rdev, const char *buf, size_
return -EBUSY;
rdev->recovery_offset = recovery_start;
if (recovery_start == MaxSector)
set_bit(In_sync, &rdev->flags);
else
clear_bit(In_sync, &rdev->flags);
assign_bit(In_sync, &rdev->flags, recovery_start == MaxSector);
return len;
}
@ -4670,7 +4684,8 @@ array_state_store(struct mddev *mddev, const char *buf, size_t len)
case readonly:
case inactive:
case read_auto:
if (!mddev->pers || !md_is_rdwr(mddev))
if (!mddev->pers ||
(st != clear && st != inactive && !md_is_rdwr(mddev)))
break;
/* write sysfs will not open mddev and opener should be 0 */
err = mddev_set_closing_and_sync_blockdev(mddev, 0);
@ -5884,7 +5899,15 @@ consistency_policy_store(struct mddev *mddev, const char *buf, size_t len)
else
err = -EBUSY;
} else if (mddev->external && strncmp(buf, "ppl", 3) == 0) {
set_bit(MD_HAS_PPL, &mddev->flags);
/* md_run() holds the lock until ->pers is set */
err = mddev_lock(mddev);
if (err)
return err;
if (mddev->pers)
err = -EBUSY;
else
set_bit(MD_HAS_PPL, &mddev->flags);
mddev_unlock(mddev);
} else {
err = -EINVAL;
}
@ -5915,10 +5938,7 @@ fail_last_dev_store(struct mddev *mddev, const char *buf, size_t len)
if (ret)
return ret;
if (value)
set_bit(MD_FAILLAST_DEV, &mddev->flags);
else
clear_bit(MD_FAILLAST_DEV, &mddev->flags);
assign_bit(MD_FAILLAST_DEV, &mddev->flags, value);
return len;
}
@ -6048,11 +6068,10 @@ lbs_store(struct mddev *mddev, const char *buf, size_t len)
err = mddev_lock(mddev);
if (err)
goto unlock;
return err;
err = mddev_set_logical_block_size(mddev, lbs);
unlock:
mddev_unlock(mddev);
return err ?: len;
}
@ -6385,12 +6404,22 @@ struct mddev *md_alloc(dev_t dev, char *name)
disk->events |= DISK_EVENT_MEDIA_CHANGE;
mddev->gendisk = disk;
/*
* add_disk() makes the array openable before mddev->kobj exists.
* Hold reconfig_mutex until kobject_add() is done so that ioctls
* issued in between wait instead of using an uninitialised kobj.
*/
mddev_lock_nointr(mddev);
error = add_disk(disk);
if (error)
if (error) {
mddev_unlock(mddev);
goto out_put_disk;
}
kobject_init(&mddev->kobj, &md_ktype);
error = kobject_add(&mddev->kobj, &disk_to_dev(disk)->kobj, "%s", "md");
mddev_unlock(mddev);
if (error) {
/*
* The disk is already live at this point. Clear the hold flag
@ -7574,14 +7603,10 @@ int md_add_new_disk(struct mddev *mddev, struct mdu_disk_info_s *info)
}
clear_bit(In_sync, &rdev->flags); /* just to be sure */
if (info->state & (1<<MD_DISK_WRITEMOSTLY))
set_bit(WriteMostly, &rdev->flags);
else
clear_bit(WriteMostly, &rdev->flags);
if (info->state & (1<<MD_DISK_FAILFAST))
set_bit(FailFast, &rdev->flags);
else
clear_bit(FailFast, &rdev->flags);
assign_bit(WriteMostly, &rdev->flags,
info->state & (1 << MD_DISK_WRITEMOSTLY));
assign_bit(FailFast, &rdev->flags,
info->state & (1 << MD_DISK_FAILFAST));
if (info->state & (1<<MD_DISK_JOURNAL)) {
struct md_rdev *rdev2;
@ -8299,6 +8324,7 @@ static bool md_ioctl_need_suspend(unsigned int cmd)
case HOT_REMOVE_DISK:
case SET_BITMAP_FILE:
case SET_ARRAY_INFO:
case STOP_ARRAY:
return true;
default:
return false;

View File

@ -989,6 +989,13 @@ static inline void rdev_dec_pending(struct md_rdev *rdev, struct mddev *mddev)
}
}
static inline void rdev_record_write_error(struct md_rdev *rdev)
{
set_bit(WriteErrorSeen, &rdev->flags);
if (!test_and_set_bit(WantReplacement, &rdev->flags))
set_bit(MD_RECOVERY_NEEDED, &rdev->mddev->recovery);
}
static inline int mddev_is_clustered(struct mddev *mddev)
{
return mddev->cluster_info && mddev->bitmap_info.nodes > 1;

View File

@ -79,7 +79,10 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf)
*private_conf = ERR_PTR(-ENOMEM);
if (!conf)
return -ENOMEM;
cnt = 0;
rdev_for_each(rdev1, mddev) {
cnt++;
pr_debug("md/raid0:%s: looking at %pg\n",
mdname(mddev),
rdev1->bdev);
@ -144,6 +147,14 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf)
}
err = -ENOMEM;
if (cnt != mddev->raid_disks) {
pr_warn("md/raid0:%s: too few disks (%d of %d) - aborting!\n",
mdname(mddev), cnt, mddev->raid_disks);
err = -EINVAL;
goto abort;
}
conf->strip_zone = kvzalloc_objs(struct strip_zone, conf->nr_strip_zones);
if (!conf->strip_zone)
goto abort;
@ -200,11 +211,6 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf)
smallest = rdev1;
cnt++;
}
if (cnt != mddev->raid_disks) {
pr_warn("md/raid0:%s: too few disks (%d of %d) - aborting!\n",
mdname(mddev), cnt, mddev->raid_disks);
goto abort;
}
zone->nb_dev = cnt;
zone->zone_end = smallest->sectors * cnt;

View File

@ -155,6 +155,16 @@ static inline bool raid1_add_bio_to_plug(struct mddev *mddev, struct bio *bio,
return true;
}
/* Block size used when narrowing a write error down to badblocks granularity. */
static inline int rdev_bb_block_sectors(struct md_rdev *rdev)
{
int lbs = bdev_logical_block_size(rdev->bdev) >> 9;
if (rdev->badblocks.shift < 0)
return lbs;
return roundup(1 << rdev->badblocks.shift, lbs);
}
/*
* current->bio_list will be set under submit_bio() context, in this case bitmap
* io will be added to the list and wait for current io submission to finish,
@ -166,6 +176,23 @@ static inline void raid1_prepare_flush_writes(struct mddev *mddev)
mddev->bitmap_ops->unplug(mddev, current->bio_list == NULL);
}
static inline void flush_bio_list(struct mddev *mddev, struct bio *bio,
wait_queue_head_t *wait)
{
/* flush any pending bitmap writes to disk before proceeding w/ I/O */
raid1_prepare_flush_writes(mddev);
if (wq_has_sleeper(wait))
wake_up(wait);
while (bio) { /* submit pending writes */
struct bio *next = bio->bi_next;
raid1_submit_write(bio);
bio = next;
cond_resched();
}
}
/*
* Used by fix_read_error() to decay the per rdev read_errors.
* We halve the read error count for every hour that has elapsed

View File

@ -483,10 +483,7 @@ static void raid1_end_write_request(struct bio *bio)
* 'one mirror IO has finished' event handler:
*/
if (bio->bi_status && !ignore_error) {
set_bit(WriteErrorSeen, &rdev->flags);
if (!test_and_set_bit(WantReplacement, &rdev->flags))
set_bit(MD_RECOVERY_NEEDED, &
conf->mddev->recovery);
rdev_record_write_error(rdev);
if (test_bit(FailFast, &rdev->flags) &&
(bio->bi_opf & MD_FAILFAST) &&
@ -922,21 +919,6 @@ static void wake_up_barrier(struct r1conf *conf)
wake_up(&conf->wait_barrier);
}
static void flush_bio_list(struct r1conf *conf, struct bio *bio)
{
/* flush any pending bitmap writes to disk before proceeding w/ I/O */
raid1_prepare_flush_writes(conf->mddev);
wake_up_barrier(conf);
while (bio) { /* submit pending writes */
struct bio *next = bio->bi_next;
raid1_submit_write(bio);
bio = next;
cond_resched();
}
}
static void flush_pending_writes(struct r1conf *conf)
{
/* Any writes that have been queued but are awaiting
@ -962,7 +944,7 @@ static void flush_pending_writes(struct r1conf *conf)
*/
__set_current_state(TASK_RUNNING);
blk_start_plug(&plug);
flush_bio_list(conf, bio);
flush_bio_list(conf->mddev, bio, &conf->wait_barrier);
blk_finish_plug(&plug);
} else
spin_unlock_irq(&conf->device_lock);
@ -1292,7 +1274,7 @@ static void raid1_unplug(struct blk_plug_cb *cb, bool from_schedule)
/* we aren't scheduling, so we can do the write-out directly. */
bio = bio_list_get(&plug->pending);
flush_bio_list(conf, bio);
flush_bio_list(conf->mddev, bio, &conf->wait_barrier);
kfree(plug);
}
@ -2067,10 +2049,7 @@ static void end_sync_write(struct bio *bio)
if (bio->bi_status) {
abort_sync_write(mddev, r1_bio);
set_bit(WriteErrorSeen, &rdev->flags);
if (!test_and_set_bit(WantReplacement, &rdev->flags))
set_bit(MD_RECOVERY_NEEDED, &
mddev->recovery);
rdev_record_write_error(rdev);
set_bit(R1BIO_WriteError, &r1_bio->state);
} else if (rdev_has_badblock(rdev, r1_bio->sector, r1_bio->sectors) &&
!rdev_has_badblock(conf->mirrors[r1_bio->read_disk].rdev,
@ -2088,11 +2067,7 @@ static int r1_sync_page_io(struct md_rdev *rdev, sector_t sector,
/* success */
return 1;
if (rw == REQ_OP_WRITE) {
set_bit(WriteErrorSeen, &rdev->flags);
if (!test_and_set_bit(WantReplacement,
&rdev->flags))
set_bit(MD_RECOVERY_NEEDED, &
rdev->mddev->recovery);
rdev_record_write_error(rdev);
}
/* need to record an error - either for the block or the device */
rdev_set_badblocks(rdev, sector, sectors, 0);
@ -2475,16 +2450,11 @@ static void narrow_write_error(struct r1bio *r1_bio, int i)
* We currently own a reference on the rdev.
*/
int block_sectors, lbs = bdev_logical_block_size(rdev->bdev) >> 9;
int block_sectors = rdev_bb_block_sectors(rdev);
sector_t sector;
int sectors;
int sect_to_write = r1_bio->sectors;
if (rdev->badblocks.shift < 0)
block_sectors = lbs;
else
block_sectors = roundup(1 << rdev->badblocks.shift, lbs);
sector = r1_bio->sector;
sectors = ((sector + block_sectors)
& ~(sector_t)(block_sectors - 1))

View File

@ -496,10 +496,7 @@ static void raid10_end_write_request(struct bio *bio)
*/
md_error(rdev->mddev, rdev);
else {
set_bit(WriteErrorSeen, &rdev->flags);
if (!test_and_set_bit(WantReplacement, &rdev->flags))
set_bit(MD_RECOVERY_NEEDED,
&rdev->mddev->recovery);
rdev_record_write_error(rdev);
dec_rdev = 0;
if (test_bit(FailFast, &rdev->flags) &&
@ -898,16 +895,7 @@ static void flush_pending_writes(struct r10conf *conf)
__set_current_state(TASK_RUNNING);
blk_start_plug(&plug);
raid1_prepare_flush_writes(conf->mddev);
wake_up(&conf->wait_barrier);
while (bio) { /* submit pending writes */
struct bio *next = bio->bi_next;
raid1_submit_write(bio);
bio = next;
cond_resched();
}
flush_bio_list(conf->mddev, bio, &conf->wait_barrier);
blk_finish_plug(&plug);
} else
spin_unlock_irq(&conf->device_lock);
@ -1010,7 +998,8 @@ static bool wait_barrier_nolock(struct r10conf *conf)
if (!read_seqretry(&conf->resync_lock, seq))
return true;
if (atomic_dec_and_test(&conf->nr_pending))
if (atomic_dec_and_test(&conf->nr_pending) ||
READ_ONCE(conf->array_freeze_pending))
wake_up_barrier(conf);
return false;
@ -1037,7 +1026,7 @@ static void wait_barrier(struct r10conf *conf)
static void allow_barrier(struct r10conf *conf)
{
if ((atomic_dec_and_test(&conf->nr_pending)) ||
(conf->array_freeze_pending))
READ_ONCE(conf->array_freeze_pending))
wake_up_barrier(conf);
}
@ -1056,12 +1045,12 @@ static void freeze_array(struct r10conf *conf, int extra)
* we continue.
*/
write_seqlock_irq(&conf->resync_lock);
conf->array_freeze_pending++;
WRITE_ONCE(conf->array_freeze_pending, conf->array_freeze_pending + 1);
WRITE_ONCE(conf->barrier, conf->barrier + 1);
conf->nr_waiting++;
wait_event_barrier_cmd(conf, atomic_read(&conf->nr_pending) ==
conf->nr_queued + extra, flush_pending_writes(conf));
conf->array_freeze_pending--;
WRITE_ONCE(conf->array_freeze_pending, conf->array_freeze_pending - 1);
write_sequnlock_irq(&conf->resync_lock);
}
@ -1104,16 +1093,7 @@ static void raid10_unplug(struct blk_plug_cb *cb, bool from_schedule)
/* we aren't scheduling, so we can do the write-out directly. */
bio = bio_list_get(&plug->pending);
raid1_prepare_flush_writes(mddev);
wake_up_barrier(conf);
while (bio) { /* submit pending writes */
struct bio *next = bio->bi_next;
raid1_submit_write(bio);
bio = next;
cond_resched();
}
flush_bio_list(mddev, bio, &conf->wait_barrier);
kfree(plug);
}
@ -2299,10 +2279,7 @@ static void end_sync_write(struct bio *bio)
if (repl)
md_error(mddev, rdev);
else {
set_bit(WriteErrorSeen, &rdev->flags);
if (!test_and_set_bit(WantReplacement, &rdev->flags))
set_bit(MD_RECOVERY_NEEDED,
&rdev->mddev->recovery);
rdev_record_write_error(rdev);
set_bit(R10BIO_WriteError, &r10_bio->state);
}
} else if (rdev_has_badblock(rdev, r10_bio->devs[slot].addr,
@ -2500,11 +2477,7 @@ static void fix_recovery_read_error(struct r10bio *r10_bio)
pages[idx],
REQ_OP_WRITE, false);
if (!ok) {
set_bit(WriteErrorSeen, &rdev->flags);
if (!test_and_set_bit(WantReplacement,
&rdev->flags))
set_bit(MD_RECOVERY_NEEDED,
&rdev->mddev->recovery);
rdev_record_write_error(rdev);
}
}
if (!ok) {
@ -2585,10 +2558,7 @@ static int r10_sync_page_io(struct md_rdev *rdev, sector_t sector,
/* success */
return 1;
if (op == REQ_OP_WRITE) {
set_bit(WriteErrorSeen, &rdev->flags);
if (!test_and_set_bit(WantReplacement, &rdev->flags))
set_bit(MD_RECOVERY_NEEDED,
&rdev->mddev->recovery);
rdev_record_write_error(rdev);
}
/* need to record an error - either for the block or the device */
rdev_set_badblocks(rdev, sector, sectors, 0);
@ -2775,16 +2745,11 @@ static void narrow_write_error(struct r10bio *r10_bio, int i)
* We currently own a reference to the rdev.
*/
int block_sectors, lbs = bdev_logical_block_size(rdev->bdev) >> 9;
int block_sectors = rdev_bb_block_sectors(rdev);
sector_t sector;
int sectors;
int sect_to_write = r10_bio->sectors;
if (rdev->badblocks.shift < 0)
block_sectors = lbs;
else
block_sectors = roundup(1 << rdev->badblocks.shift, lbs);
sector = r10_bio->sector;
sectors = ((r10_bio->sector + block_sectors)
& ~(sector_t)(block_sectors - 1))

View File

@ -432,10 +432,8 @@ static inline void r5c_update_log_state(struct r5l_log *log)
wake_reclaim = true;
clear_bit(R5C_LOG_CRITICAL, &conf->cache_state);
}
if (free_space < 3 * reclaim_space)
set_bit(R5C_LOG_TIGHT, &conf->cache_state);
else
clear_bit(R5C_LOG_TIGHT, &conf->cache_state);
assign_bit(R5C_LOG_TIGHT, &conf->cache_state,
free_space < 3 * reclaim_space);
if (wake_reclaim)
r5l_wake_reclaim(log, 0);
@ -2001,27 +1999,36 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
if (!page)
return -ENOMEM;
while (mb_offset < le32_to_cpu(mb->meta_size)) {
while (mb_offset + sizeof(struct r5l_payload_header) <= le32_to_cpu(mb->meta_size)) {
u32 payload_size;
sector_t payload_len;
payload = (void *)mb + mb_offset;
payload_flush = (void *)mb + mb_offset;
if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) {
if (mb_offset + sizeof(struct r5l_payload_data_parity)
> le32_to_cpu(mb->meta_size))
goto mismatch;
payload_size = le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9);
payload_len = sizeof(struct r5l_payload_data_parity) +
(sector_t)sizeof(__le32) *
(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
(sector_t)sizeof(__le32) * payload_size;
if (mb_offset + payload_len > le32_to_cpu(mb->meta_size) ||
payload_size < 1)
goto mismatch;
if (r5l_recovery_verify_data_checksum(
log, ctx, page, log_offset,
payload->checksum[0]) < 0)
goto mismatch;
} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_PARITY) {
if (mb_offset + sizeof(struct r5l_payload_data_parity)
> le32_to_cpu(mb->meta_size))
goto mismatch;
payload_size = le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9);
payload_len = sizeof(struct r5l_payload_data_parity) +
(sector_t)sizeof(__le32) *
(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
(sector_t)sizeof(__le32) * payload_size;
if (mb_offset + payload_len > le32_to_cpu(mb->meta_size) ||
payload_size < conf->max_degraded)
goto mismatch;
if (r5l_recovery_verify_data_checksum(
log, ctx, page, log_offset,
@ -2035,6 +2042,9 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
payload->checksum[1]) < 0)
goto mismatch;
} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
if (mb_offset + sizeof(struct r5l_payload_flush)
> le32_to_cpu(mb->meta_size))
goto mismatch;
payload_len = sizeof(struct r5l_payload_flush) +
(sector_t)le32_to_cpu(payload_flush->size);
if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
@ -2096,7 +2106,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
mb_offset = sizeof(struct r5l_meta_block);
log_offset = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
while (mb_offset < le32_to_cpu(mb->meta_size)) {
while (mb_offset + sizeof(struct r5l_payload_header) < le32_to_cpu(mb->meta_size)) {
sector_t payload_len;
int dd;
@ -2106,6 +2116,9 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
int i, count;
if (mb_offset + sizeof(struct r5l_payload_flush) >
le32_to_cpu(mb->meta_size))
return -EINVAL;
payload_len = sizeof(struct r5l_payload_flush) +
(sector_t)le32_to_cpu(payload_flush->size);
if (mb_offset + payload_len >
@ -2130,6 +2143,9 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
}
/* DATA or PARITY payload */
if (mb_offset + sizeof(struct r5l_payload_data_parity) >
le32_to_cpu(mb->meta_size))
return -EINVAL;
payload_len = sizeof(struct r5l_payload_data_parity) +
(sector_t)sizeof(__le32) *
(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
@ -2952,7 +2968,9 @@ bool r5c_big_stripe_cached(struct r5conf *conf, sector_t sect)
return false;
tree_index = r5c_tree_index(conf, sect);
rcu_read_lock();
slot = radix_tree_lookup(&log->big_stripe_tree, tree_index);
rcu_read_unlock();
return slot != NULL;
}
@ -3066,6 +3084,7 @@ int r5l_init_log(struct r5conf *conf, struct md_rdev *rdev)
{
struct r5l_log *log;
struct md_thread *thread;
char cache_name[48];
int ret;
pr_debug("md/raid:%s: using device %pg as journal\n",
@ -3105,7 +3124,16 @@ int r5l_init_log(struct r5conf *conf, struct md_rdev *rdev)
INIT_LIST_HEAD(&log->flushing_ios);
INIT_LIST_HEAD(&log->finished_ios);
log->io_kc = KMEM_CACHE(r5l_io_unit, 0);
if (mddev_is_dm(conf->mddev))
snprintf(cache_name, sizeof(cache_name), "r5l_io_unit-%p",
conf->mddev);
else
snprintf(cache_name, sizeof(cache_name), "r5l_io_unit-%s",
mdname(conf->mddev));
log->io_kc = kmem_cache_create(cache_name, sizeof(struct r5l_io_unit),
__alignof__(struct r5l_io_unit),
0, NULL);
if (!log->io_kc)
goto io_kc;

View File

@ -1318,6 +1318,7 @@ int ppl_init_log(struct r5conf *conf)
{
struct ppl_conf *ppl_conf;
struct mddev *mddev = conf->mddev;
char cache_name[48];
int ret = 0;
int max_disks;
int i;
@ -1360,7 +1361,16 @@ int ppl_init_log(struct r5conf *conf)
ppl_conf->mddev = mddev;
ppl_conf->io_kc = KMEM_CACHE(ppl_io_unit, 0);
if (mddev_is_dm(mddev))
snprintf(cache_name, sizeof(cache_name), "ppl_io_unit-%p", mddev);
else
snprintf(cache_name, sizeof(cache_name), "ppl_io_unit-%s",
mdname(mddev));
ppl_conf->io_kc = kmem_cache_create(cache_name,
sizeof(struct ppl_io_unit),
__alignof__(struct ppl_io_unit),
0, NULL);
if (!ppl_conf->io_kc) {
ret = -ENOMEM;
goto err;

View File

@ -2642,6 +2642,7 @@ static int resize_stripes(struct r5conf *conf, int newsize)
} else {
kfree(conf->disks);
conf->disks = ndisks;
conf->disks_cnt = newsize;
}
} else
err = -ENOMEM;
@ -2712,6 +2713,37 @@ static int drop_one_stripe(struct r5conf *conf)
static void shrink_stripes(struct r5conf *conf)
{
struct list_head temp[NR_STRIPE_HASH_LOCKS];
int i;
/*
* grow_one_stripe() parks new stripes on released_stripes when
* mddev->thread is already set. Takeover leaves the previous
* personality's thread there, so raid5d never moves them to
* inactive_list. Drain them before the cache is destroyed.
* slab_cache is assigned only after device_lock and the hash
* lists exist; earlier abort paths have nothing to drain.
*/
if (conf->slab_cache) {
for (i = 0; i < NR_STRIPE_HASH_LOCKS; i++)
INIT_LIST_HEAD(&temp[i]);
spin_lock_irq(&conf->device_lock);
release_stripe_list(conf, temp);
spin_unlock_irq(&conf->device_lock);
for (i = 0; i < NR_STRIPE_HASH_LOCKS; i++) {
if (list_empty(&temp[i]))
continue;
spin_lock_irq(conf->hash_locks + i);
if (list_empty(conf->inactive_list + i))
atomic_dec(&conf->empty_inactive_list_nr);
list_splice_tail_init(&temp[i],
conf->inactive_list + i);
spin_unlock_irq(conf->hash_locks + i);
}
}
while (conf->max_nr_stripes &&
drop_one_stripe(conf))
;
@ -2888,11 +2920,8 @@ static void raid5_end_write_request(struct bio *bi)
set_bit(R5_MadeGoodRepl, &sh->dev[i].flags);
} else {
if (bi->bi_status) {
set_bit(WriteErrorSeen, &rdev->flags);
rdev_record_write_error(rdev);
set_bit(R5_WriteError, &sh->dev[i].flags);
if (!test_and_set_bit(WantReplacement, &rdev->flags))
set_bit(MD_RECOVERY_NEEDED,
&rdev->mddev->recovery);
} else if (rdev_has_badblock(rdev, sh->sector,
RAID5_STRIPE_SECTORS(conf))) {
set_bit(R5_MadeGood, &sh->dev[i].flags);
@ -4748,10 +4777,8 @@ static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s)
RAID5_STRIPE_SECTORS(conf)))
set_bit(R5_ReadRepl, &dev->flags);
else {
if (rdev && !test_bit(Faulty, &rdev->flags))
set_bit(R5_NeedReplace, &dev->flags);
else
clear_bit(R5_NeedReplace, &dev->flags);
assign_bit(R5_NeedReplace, &dev->flags,
rdev && !test_bit(Faulty, &rdev->flags));
rdev = conf->disks[i].rdev;
clear_bit(R5_ReadRepl, &dev->flags);
}
@ -7538,7 +7565,8 @@ static void raid5_free_percpu(struct r5conf *conf)
if (!conf->percpu)
return;
cpuhp_state_remove_instance(CPUHP_MD_RAID5_PREPARE, &conf->node);
if (!hlist_unhashed_lockless(&conf->node))
cpuhp_state_remove_instance(CPUHP_MD_RAID5_PREPARE, &conf->node);
free_percpu(conf->percpu);
}
@ -7552,7 +7580,7 @@ static void free_conf(struct r5conf *conf)
free_thread_groups(conf);
shrink_stripes(conf);
raid5_free_percpu(conf);
for (i = 0; i < conf->pool_size; i++)
for (i = 0; i < conf->disks_cnt; i++)
if (conf->disks[i].extra_page)
put_page(conf->disks[i].extra_page);
kfree(conf->disks);
@ -7630,10 +7658,15 @@ static unsigned long raid5_cache_count(struct shrinker *shrink,
return max_stripes - min_stripes;
}
static int min_raid_disks(int level)
{
return level == 6 ? 4 : 2;
}
static struct r5conf *setup_conf(struct mddev *mddev)
{
struct r5conf *conf;
int raid_disk, memory, max_disks;
int raid_disk, memory, max_disks, min_disks;
struct md_rdev *rdev;
struct disk_info *disk;
char pers_name[6];
@ -7657,9 +7690,11 @@ static struct r5conf *setup_conf(struct mddev *mddev)
mdname(mddev), mddev->new_layout);
return ERR_PTR(-EIO);
}
if (mddev->new_level == 6 && mddev->raid_disks < 4) {
pr_warn("md/raid:%s: not enough configured devices (%d, minimum 4)\n",
mdname(mddev), mddev->raid_disks);
min_disks = min_raid_disks(mddev->new_level);
if (mddev->raid_disks < min_disks) {
pr_warn("md/raid:%s: not enough configured devices (%d, minimum %d)\n",
mdname(mddev), mddev->raid_disks,
min_disks);
return ERR_PTR(-EINVAL);
}
@ -7675,6 +7710,7 @@ static struct r5conf *setup_conf(struct mddev *mddev)
if (conf == NULL)
goto abort;
conf->mddev = mddev;
#if PAGE_SIZE != DEFAULT_STRIPE_SIZE
conf->stripe_size = DEFAULT_STRIPE_SIZE;
conf->stripe_shift = ilog2(DEFAULT_STRIPE_SIZE) - 9;
@ -7733,7 +7769,7 @@ static struct r5conf *setup_conf(struct mddev *mddev)
if (!conf->disks)
goto abort;
conf->disks_cnt = max_disks;
for (i = 0; i < max_disks; i++) {
conf->disks[i].extra_page = alloc_page(GFP_KERNEL);
if (!conf->disks[i].extra_page)
@ -7743,7 +7779,6 @@ static struct r5conf *setup_conf(struct mddev *mddev)
ret = bioset_init(&conf->bio_split, BIO_POOL_SIZE, 0, 0);
if (ret)
goto abort;
conf->mddev = mddev;
ret = -ENOMEM;
conf->stripe_hashtbl = kzalloc(PAGE_SIZE, GFP_KERNEL);
@ -8046,6 +8081,7 @@ static int raid5_run(struct mddev *mddev)
sector_t here_new, here_old;
int old_disks;
int max_degraded = (mddev->level == 6 ? 2 : 1);
int min_disks = min_raid_disks(mddev->level);
int chunk_sectors;
int new_data_disks;
@ -8061,6 +8097,12 @@ static int raid5_run(struct mddev *mddev)
return -EINVAL;
}
old_disks = mddev->raid_disks - mddev->delta_disks;
if (mddev->raid_disks < min_disks || old_disks < min_disks) {
pr_warn("md/raid:%s: invalid reshape disks (%d -> %d, min %d)\n",
mdname(mddev), old_disks, mddev->raid_disks,
min_disks);
return -EINVAL;
}
/* reshape_position must be on a new-stripe boundary, and one
* further up in new geometry must map after here in old
* geometry.
@ -8110,11 +8152,17 @@ static int raid5_run(struct mddev *mddev)
}
pr_debug("md/raid:%s: reshape will continue\n", mdname(mddev));
/* OK, we should be able to continue; */
} else {
BUG_ON(mddev->level != mddev->new_level);
BUG_ON(mddev->layout != mddev->new_layout);
BUG_ON(mddev->chunk_sectors != mddev->new_chunk_sectors);
BUG_ON(mddev->delta_disks != 0);
} else if (mddev->level != mddev->new_level ||
mddev->layout != mddev->new_layout ||
mddev->chunk_sectors != mddev->new_chunk_sectors ||
mddev->delta_disks != 0) {
/* No reshape is in progress, but the array describes one.
* The superblock validators do not cross-check these against
* reshape_position, so this is reachable from disk.
*/
pr_warn("md/raid:%s: inconsistent reshape state - aborting.\n",
mdname(mddev));
return -EINVAL;
}
if (test_bit(MD_HAS_JOURNAL, &mddev->flags) &&
@ -8203,6 +8251,12 @@ static int raid5_run(struct mddev *mddev)
/* device size must be a multiple of chunk size */
mddev->dev_sectors &= ~((sector_t)mddev->chunk_sectors - 1);
if (!mddev->dev_sectors) {
pr_warn("md/raid:%s: device size is smaller than one chunk\n",
mdname(mddev));
ret = -EINVAL;
goto abort;
}
mddev->resync_max_sectors = mddev->dev_sectors;
if (mddev->degraded > dirty_parity_disks &&
@ -8536,6 +8590,8 @@ static int raid5_resize(struct mddev *mddev, sector_t sectors)
if (raid5_has_log(conf) || raid5_has_ppl(conf))
return -EINVAL;
sectors &= ~((sector_t)conf->chunk_sectors - 1);
if (!sectors)
return -EINVAL;
newsize = raid5_size(mddev, sectors, mddev->raid_disks);
if (mddev->external_size &&
mddev->array_sectors > newsize)
@ -8600,13 +8656,9 @@ static int check_reshape(struct mddev *mddev)
if (mddev->delta_disks < 0 && mddev->reshape_position == MaxSector) {
/* We might be able to shrink, but the devices must
* be made bigger first.
* For raid6, 4 is the minimum size.
* Otherwise 2 is the minimum
*/
int min = 2;
if (mddev->level == 6)
min = 4;
if (mddev->raid_disks + mddev->delta_disks < min)
if (mddev->raid_disks + mddev->delta_disks <
min_raid_disks(mddev->level))
return -EINVAL;
}

View File

@ -667,6 +667,7 @@ struct r5conf {
unsigned long cache_state;
struct shrinker *shrinker;
int pool_size; /* number of disks in stripeheads in pool */
int disks_cnt; /* number of entries in disks[] array */
spinlock_t device_lock;
struct disk_info *disks;
struct bio_set bio_split;