[PATCH] mtd: serialize dynamic partition registration with removal

James Hilliard james.hilliard1 at gmail.com
Sun Nov 8 13:13:59 PST 2026


mtd_add_partition() drops partitions_lock after linking the new child
but before registering its MTD device and adding the partition sysfs
attributes. Concurrent removal can unlink that child before it has been
registered, or release it after registration but before the attributes
are added. It can also finish deleting partitions before a new child
is linked, leaving a partition published after master removal.

Keep partitions_lock held across allocation, publication and attribute
creation. Record that master unregistration has started under the same
lock and reject new dynamic partitions from that point. Check before
allocation, which can scan the parent's bad-block markers, so it cannot
start hardware access after removal has passed this synchronization point.

Initialize the state when registering the master, allowing drivers that
reuse their MTD object to register it again.

Fixes: 5daa7b21496a ("mtd: prepare partition add and del functions for ioctl requests")
Cc: stable at vger.kernel.org
Signed-off-by: James Hilliard <james.hilliard1 at gmail.com>
---
 drivers/mtd/mtdcore.c   |  5 +++++
 drivers/mtd/mtdpart.c   | 20 +++++++++++++-------
 include/linux/mtd/mtd.h |  2 ++
 3 files changed, 20 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 491a27d0315f..7a5205810bf0 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -918,6 +918,7 @@ static void mtd_set_dev_defaults(struct mtd_info *mtd)
 	INIT_LIST_HEAD(&mtd->partitions);
 	mutex_init(&mtd->master.partitions_lock);
 	mutex_init(&mtd->master.chrdev_lock);
+	mtd->master.unregistering = false;
 }
 
 static ssize_t mtd_otp_size(struct mtd_info *mtd, bool is_user)
@@ -1206,6 +1207,10 @@ int mtd_device_unregister(struct mtd_info *master)
 {
 	int err;
 
+	mutex_lock(&master->master.partitions_lock);
+	master->master.unregistering = true;
+	mutex_unlock(&master->master.partitions_lock);
+
 	if (master->_reboot) {
 		unregister_reboot_notifier(&master->reboot_notifier);
 		memset(&master->reboot_notifier, 0, sizeof(master->reboot_notifier));
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index ddded0dbe77e..c316cfe92c7e 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -278,28 +278,34 @@ int mtd_add_partition(struct mtd_info *parent, const char *name,
 	part.size = length;
 	part.offset = offset;
 
+	mutex_lock(&master->master.partitions_lock);
+	if (master->master.unregistering) {
+		ret = -ENODEV;
+		goto err_unlock;
+	}
+
 	child = allocate_partition(parent, &part, -1, offset);
-	if (IS_ERR(child))
-		return PTR_ERR(child);
+	if (IS_ERR(child)) {
+		ret = PTR_ERR(child);
+		goto err_unlock;
+	}
 
-	mutex_lock(&master->master.partitions_lock);
 	list_add_tail(&child->part.node, &parent->partitions);
-	mutex_unlock(&master->master.partitions_lock);
 
 	ret = add_mtd_device(child);
 	if (ret)
 		goto err_remove_part;
 
 	mtd_add_partition_attrs(child);
+	mutex_unlock(&master->master.partitions_lock);
 
 	return 0;
 
 err_remove_part:
-	mutex_lock(&master->master.partitions_lock);
 	list_del(&child->part.node);
-	mutex_unlock(&master->master.partitions_lock);
-
 	free_partition(child);
+err_unlock:
+	mutex_unlock(&master->master.partitions_lock);
 
 	return ret;
 }
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 8d10d9d2e830..cd7b4671c456 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -224,6 +224,7 @@ struct mtd_part {
  *		     not only the master partition list, but also all
  *		     sub-partitions.
  * @suspended: set to 1 when the device is suspended, 0 otherwise
+ * @unregistering: removal has started; protected by partitions_lock
  *
  * This struct is embedded in mtd_info and contains master-specific
  * properties/fields. The master is the root MTD device from the MTD partition
@@ -233,6 +234,7 @@ struct mtd_master {
 	struct mutex partitions_lock;
 	struct mutex chrdev_lock;
 	unsigned int suspended : 1;
+	bool unregistering;
 };
 
 struct mtd_info {

---
base-commit: 1c1a342aceec79528b3ba51f376eca2be5928fe7
change-id: 20261108-submit-mtd-partition-publication-v1-4629be94d7e6

Best regards,
--  
James Hilliard <james.hilliard1 at gmail.com>




More information about the linux-mtd mailing list