From: Frieder Schrempf <[email protected]>

blk_post_probe() binds a UCLASS_PARTITION child for each partition
found on the device. Removing the block device removes these children,
but does not unbind them. When the block device is probed again, a
second set of partition devices is bound next to the first one.

This happens for example when the probe of a block device fails in the
EVT_DM_POST_PROBE event and is retried later. The EFI disk code then
iterates over all children and tries to install a second EFI handle
with the same device path for the stale partition device, which fails
with EFI_ALREADY_STARTED:

  Adding partition for [email protected]:1 failed
  switch to partitions #0, ERROR

As every further probe adds another set of partition devices, the
block device can't be used anymore until the next reset. This was seen
on an i.MX93 board where a single failed read during the first probe
of the SD card made it unusable for the rest of the boot.

Unbind the partition devices in the pre_remove hook of the block
uclass, so that they are bound and unbound together with the probe and
remove of the block device. When only some devices are removed, like
before booting an OS, the partition devices don't match the remove
flags and are still active at this point, so remove them first.

Fixes: bf76031d19fa ("dm: blk: add a device-probe hook for scanning disk 
partitions")
Assisted-by: LLM
Signed-off-by: Frieder Schrempf <[email protected]>
---
 drivers/block/blk-uclass.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c
index 37d87be3a57..cf6357d3041 100644
--- a/drivers/block/blk-uclass.c
+++ b/drivers/block/blk-uclass.c
@@ -841,9 +841,31 @@ static int blk_post_probe(struct udevice *dev)
        return 0;
 }
 
+static int blk_pre_remove(struct udevice *dev)
+{
+       struct driver *drv = DM_DRIVER_GET(blk_partition);
+       int ret;
+
+       if (!CONFIG_IS_ENABLED(PARTITIONS) || !blk_enabled())
+               return 0;
+
+       /*
+        * The partition devices are bound in blk_post_probe(), so unbind them
+        * here. Otherwise the next probe adds a second set of them. They are
+        * still active if the remove flags don't match their driver, so
+        * remove them first.
+        */
+       ret = device_chld_remove(dev, drv, DM_REMOVE_NORMAL);
+       if (ret)
+               return ret;
+
+       return device_chld_unbind(dev, drv);
+}
+
 UCLASS_DRIVER(blk) = {
        .id             = UCLASS_BLK,
        .name           = "blk",
        .post_probe     = blk_post_probe,
+       .pre_remove     = blk_pre_remove,
        .per_device_plat_auto   = sizeof(struct blk_desc),
 };

-- 
2.55.0

Reply via email to