[PATCH 1/2] md/raid5: roll back failed PPL disk additions
From: Yingjie Wang
Date: Mon Nov 09 2026 - 04:09:10 EST
raid5_add_disk() publishes a new device in conf->disks[] before
preparing its PPL log. If PPL validation or the empty-header write
fails, the caller resets rdev->raid_disk, but the RAID5 private pointer
remains. A later removal can free the device while RAID5 still
references it.
Prepare the PPL log before publishing the RAID5 disk pointer and reset
rdev->raid_disk to -1 when preparation fails. If empty-header setup
fails after the PPL log pointer is published, restore the previous log
pointer and PPL size and do not initialize the child log.
Fixes: 6358c239d88c ("raid5-ppl: support disk hot add/remove with PPL")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Yingjie Wang <x-professor@xxxxxxxxxxx>
---
drivers/md/raid5-ppl.c | 11 ++++++++++-
drivers/md/raid5.c | 9 +++++++--
2 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/drivers/md/raid5-ppl.c b/drivers/md/raid5-ppl.c
index 0530632f9d2a..2ce4e0e72282 100644
--- a/drivers/md/raid5-ppl.c
+++ b/drivers/md/raid5-ppl.c
@@ -1461,6 +1461,8 @@ int ppl_modify_log(struct r5conf *conf, struct md_rdev *rdev, bool add)
{
struct ppl_conf *ppl_conf = conf->log_private;
struct ppl_log *log;
+ struct md_rdev *old_rdev;
+ unsigned int old_ppl_size;
int ret = 0;
if (!rdev)
@@ -1480,11 +1482,18 @@ int ppl_modify_log(struct r5conf *conf, struct md_rdev *rdev, bool add)
mutex_lock(&log->io_mutex);
if (add) {
+ old_rdev = log->rdev;
+ old_ppl_size = rdev->ppl.size;
ret = ppl_validate_rdev(rdev);
if (!ret) {
log->rdev = rdev;
ret = ppl_write_empty_header(log);
- ppl_init_child_log(log, rdev);
+ if (ret) {
+ log->rdev = old_rdev;
+ rdev->ppl.size = old_ppl_size;
+ } else {
+ ppl_init_child_log(log, rdev);
+ }
}
} else {
log->rdev = NULL;
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index efb4b0d37cb0..455e36ddceee 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -8550,12 +8550,17 @@ static int raid5_add_disk(struct mddev *mddev, struct md_rdev *rdev)
if (p->rdev == NULL) {
clear_bit(In_sync, &rdev->flags);
rdev->raid_disk = disk;
+
+ err = log_modify(conf, rdev, true);
+ if (err) {
+ rdev->raid_disk = -1;
+ goto out;
+ }
+
if (rdev->saved_raid_disk != disk)
conf->fullsync = 1;
WRITE_ONCE(p->rdev, rdev);
- err = log_modify(conf, rdev, true);
-
goto out;
}
}
--
2.43.0