]> Git Repo - J-linux.git/commitdiff
mtd: ubi: introduce pre-removal notification for UBI volumes
authorDaniel Golle <[email protected]>
Tue, 19 Dec 2023 02:33:24 +0000 (02:33 +0000)
committerRichard Weinberger <[email protected]>
Sun, 25 Feb 2024 21:41:33 +0000 (22:41 +0100)
Introduce a new notification type UBI_VOLUME_SHUTDOWN to inform users
that a volume is just about to be removed.
This is needed because users (such as the NVMEM subsystem) expect that
at the time their removal function is called, the parenting device is
still available (for removal of sysfs nodes, for example, in case of
NVMEM which otherwise WARNs on volume removal).

Signed-off-by: Daniel Golle <[email protected]>
Signed-off-by: Richard Weinberger <[email protected]>
drivers/mtd/ubi/build.c
drivers/mtd/ubi/kapi.c
drivers/mtd/ubi/ubi.h
drivers/mtd/ubi/vmt.c
include/linux/mtd/ubi.h

index 8c3f763e4ddb814488fb3040f663f2387fa321d4..a7e3a6246c0e93309805c588391640936993bce5 100644 (file)
@@ -93,7 +93,7 @@ static struct ubi_device *ubi_devices[UBI_MAX_DEVICES];
 /* Serializes UBI devices creations and removals */
 DEFINE_MUTEX(ubi_devices_mutex);
 
-/* Protects @ubi_devices and @ubi->ref_count */
+/* Protects @ubi_devices, @ubi->ref_count and @ubi->is_dead */
 static DEFINE_SPINLOCK(ubi_devices_lock);
 
 /* "Show" method for files in '/<sysfs>/class/ubi/' */
@@ -261,6 +261,9 @@ struct ubi_device *ubi_get_device(int ubi_num)
 
        spin_lock(&ubi_devices_lock);
        ubi = ubi_devices[ubi_num];
+       if (ubi && ubi->is_dead)
+               ubi = NULL;
+
        if (ubi) {
                ubi_assert(ubi->ref_count >= 0);
                ubi->ref_count += 1;
@@ -298,7 +301,7 @@ struct ubi_device *ubi_get_by_major(int major)
        spin_lock(&ubi_devices_lock);
        for (i = 0; i < UBI_MAX_DEVICES; i++) {
                ubi = ubi_devices[i];
-               if (ubi && MAJOR(ubi->cdev.dev) == major) {
+               if (ubi && !ubi->is_dead && MAJOR(ubi->cdev.dev) == major) {
                        ubi_assert(ubi->ref_count >= 0);
                        ubi->ref_count += 1;
                        get_device(&ubi->dev);
@@ -327,7 +330,7 @@ int ubi_major2num(int major)
        for (i = 0; i < UBI_MAX_DEVICES; i++) {
                struct ubi_device *ubi = ubi_devices[i];
 
-               if (ubi && MAJOR(ubi->cdev.dev) == major) {
+               if (ubi && !ubi->is_dead && MAJOR(ubi->cdev.dev) == major) {
                        ubi_num = ubi->ubi_num;
                        break;
                }
@@ -514,7 +517,7 @@ static void ubi_free_volumes_from(struct ubi_device *ubi, int from)
        int i;
 
        for (i = from; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) {
-               if (!ubi->volumes[i])
+               if (!ubi->volumes[i] || ubi->volumes[i]->is_dead)
                        continue;
                ubi_eba_replace_table(ubi->volumes[i], NULL);
                ubi_fastmap_destroy_checkmap(ubi->volumes[i]);
@@ -1099,7 +1102,6 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
                return -EINVAL;
 
        spin_lock(&ubi_devices_lock);
-       put_device(&ubi->dev);
        ubi->ref_count -= 1;
        if (ubi->ref_count) {
                if (!anyway) {
@@ -1110,6 +1112,13 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
                ubi_err(ubi, "%s reference count %d, destroy anyway",
                        ubi->ubi_name, ubi->ref_count);
        }
+       ubi->is_dead = true;
+       spin_unlock(&ubi_devices_lock);
+
+       ubi_notify_all(ubi, UBI_VOLUME_SHUTDOWN, NULL);
+
+       spin_lock(&ubi_devices_lock);
+       put_device(&ubi->dev);
        ubi_devices[ubi_num] = NULL;
        spin_unlock(&ubi_devices_lock);
 
index fbf3a7fe2af79d4387382d349e3b9ea3ea1c3ded..f1ea8677467fbfc6f3585d54544a50ed2c17c99d 100644 (file)
@@ -152,7 +152,7 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode)
 
        spin_lock(&ubi->volumes_lock);
        vol = ubi->volumes[vol_id];
-       if (!vol)
+       if (!vol || vol->is_dead)
                goto out_unlock;
 
        err = -EBUSY;
index a588381c50adc6360d5ce90b67abddbe7da86bea..32009a24869e7e49584e352ef37c52bba8c2cc4c 100644 (file)
@@ -337,6 +337,7 @@ struct ubi_volume {
        int writers;
        int exclusive;
        int metaonly;
+       bool is_dead;
 
        int reserved_pebs;
        int vol_type;
@@ -561,6 +562,7 @@ struct ubi_device {
        spinlock_t volumes_lock;
        int ref_count;
        int image_seq;
+       bool is_dead;
 
        int rsvd_pebs;
        int avail_pebs;
index 990571287e848e1c1fdd8061affed1b1401d4aad..eaf8328f6fc38715998df35d49c6a2c307bccb86 100644 (file)
@@ -59,7 +59,7 @@ static ssize_t vol_attribute_show(struct device *dev,
        struct ubi_device *ubi = vol->ubi;
 
        spin_lock(&ubi->volumes_lock);
-       if (!ubi->volumes[vol->vol_id]) {
+       if (!ubi->volumes[vol->vol_id] || ubi->volumes[vol->vol_id]->is_dead) {
                spin_unlock(&ubi->volumes_lock);
                return -ENODEV;
        }
@@ -189,7 +189,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
 
        /* Ensure that the name is unique */
        for (i = 0; i < ubi->vtbl_slots; i++)
-               if (ubi->volumes[i] &&
+               if (ubi->volumes[i] && !ubi->volumes[i]->is_dead &&
                    ubi->volumes[i]->name_len == req->name_len &&
                    !strcmp(ubi->volumes[i]->name, req->name)) {
                        ubi_err(ubi, "volume \"%s\" exists (ID %d)",
@@ -352,6 +352,19 @@ int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl)
                err = -EBUSY;
                goto out_unlock;
        }
+
+       /*
+        * Mark volume as dead at this point to prevent that anyone
+        * can take a reference to the volume from now on.
+        * This is necessary as we have to release the spinlock before
+        * calling ubi_volume_notify.
+        */
+       vol->is_dead = true;
+       spin_unlock(&ubi->volumes_lock);
+
+       ubi_volume_notify(ubi, vol, UBI_VOLUME_SHUTDOWN);
+
+       spin_lock(&ubi->volumes_lock);
        ubi->volumes[vol_id] = NULL;
        spin_unlock(&ubi->volumes_lock);
 
index a529347fd75b2a065f618d45771b27a7bd163f80..562f92504f2b7c9aa271eada506c7d91baf4a243 100644 (file)
@@ -192,6 +192,7 @@ struct ubi_device_info {
  *                     or a volume was removed)
  * @UBI_VOLUME_RESIZED: a volume has been re-sized
  * @UBI_VOLUME_RENAMED: a volume has been re-named
+ * @UBI_VOLUME_SHUTDOWN: a volume is going to removed, shutdown users
  * @UBI_VOLUME_UPDATED: data has been written to a volume
  *
  * These constants define which type of event has happened when a volume
@@ -202,6 +203,7 @@ enum {
        UBI_VOLUME_REMOVED,
        UBI_VOLUME_RESIZED,
        UBI_VOLUME_RENAMED,
+       UBI_VOLUME_SHUTDOWN,
        UBI_VOLUME_UPDATED,
 };
 
This page took 0.068488 seconds and 4 git commands to generate.