Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mtd: ubi: introduce pre-removal notification for UBI volumes

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 <daniel@makrotopia.org>
Signed-off-by: Richard Weinberger <richard@nod.at>

authored by

Daniel Golle and committed by
Richard Weinberger
7e84c961 927c1452

+34 -8
+14 -5
drivers/mtd/ubi/build.c
··· 93 93 /* Serializes UBI devices creations and removals */ 94 94 DEFINE_MUTEX(ubi_devices_mutex); 95 95 96 - /* Protects @ubi_devices and @ubi->ref_count */ 96 + /* Protects @ubi_devices, @ubi->ref_count and @ubi->is_dead */ 97 97 static DEFINE_SPINLOCK(ubi_devices_lock); 98 98 99 99 /* "Show" method for files in '/<sysfs>/class/ubi/' */ ··· 261 261 262 262 spin_lock(&ubi_devices_lock); 263 263 ubi = ubi_devices[ubi_num]; 264 + if (ubi && ubi->is_dead) 265 + ubi = NULL; 266 + 264 267 if (ubi) { 265 268 ubi_assert(ubi->ref_count >= 0); 266 269 ubi->ref_count += 1; ··· 301 298 spin_lock(&ubi_devices_lock); 302 299 for (i = 0; i < UBI_MAX_DEVICES; i++) { 303 300 ubi = ubi_devices[i]; 304 - if (ubi && MAJOR(ubi->cdev.dev) == major) { 301 + if (ubi && !ubi->is_dead && MAJOR(ubi->cdev.dev) == major) { 305 302 ubi_assert(ubi->ref_count >= 0); 306 303 ubi->ref_count += 1; 307 304 get_device(&ubi->dev); ··· 330 327 for (i = 0; i < UBI_MAX_DEVICES; i++) { 331 328 struct ubi_device *ubi = ubi_devices[i]; 332 329 333 - if (ubi && MAJOR(ubi->cdev.dev) == major) { 330 + if (ubi && !ubi->is_dead && MAJOR(ubi->cdev.dev) == major) { 334 331 ubi_num = ubi->ubi_num; 335 332 break; 336 333 } ··· 517 514 int i; 518 515 519 516 for (i = from; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) { 520 - if (!ubi->volumes[i]) 517 + if (!ubi->volumes[i] || ubi->volumes[i]->is_dead) 521 518 continue; 522 519 ubi_eba_replace_table(ubi->volumes[i], NULL); 523 520 ubi_fastmap_destroy_checkmap(ubi->volumes[i]); ··· 1102 1099 return -EINVAL; 1103 1100 1104 1101 spin_lock(&ubi_devices_lock); 1105 - put_device(&ubi->dev); 1106 1102 ubi->ref_count -= 1; 1107 1103 if (ubi->ref_count) { 1108 1104 if (!anyway) { ··· 1112 1110 ubi_err(ubi, "%s reference count %d, destroy anyway", 1113 1111 ubi->ubi_name, ubi->ref_count); 1114 1112 } 1113 + ubi->is_dead = true; 1114 + spin_unlock(&ubi_devices_lock); 1115 + 1116 + ubi_notify_all(ubi, UBI_VOLUME_SHUTDOWN, NULL); 1117 + 1118 + spin_lock(&ubi_devices_lock); 1119 + put_device(&ubi->dev); 1115 1120 ubi_devices[ubi_num] = NULL; 1116 1121 spin_unlock(&ubi_devices_lock); 1117 1122
+1 -1
drivers/mtd/ubi/kapi.c
··· 152 152 153 153 spin_lock(&ubi->volumes_lock); 154 154 vol = ubi->volumes[vol_id]; 155 - if (!vol) 155 + if (!vol || vol->is_dead) 156 156 goto out_unlock; 157 157 158 158 err = -EBUSY;
+2
drivers/mtd/ubi/ubi.h
··· 337 337 int writers; 338 338 int exclusive; 339 339 int metaonly; 340 + bool is_dead; 340 341 341 342 int reserved_pebs; 342 343 int vol_type; ··· 562 561 spinlock_t volumes_lock; 563 562 int ref_count; 564 563 int image_seq; 564 + bool is_dead; 565 565 566 566 int rsvd_pebs; 567 567 int avail_pebs;
+15 -2
drivers/mtd/ubi/vmt.c
··· 59 59 struct ubi_device *ubi = vol->ubi; 60 60 61 61 spin_lock(&ubi->volumes_lock); 62 - if (!ubi->volumes[vol->vol_id]) { 62 + if (!ubi->volumes[vol->vol_id] || ubi->volumes[vol->vol_id]->is_dead) { 63 63 spin_unlock(&ubi->volumes_lock); 64 64 return -ENODEV; 65 65 } ··· 189 189 190 190 /* Ensure that the name is unique */ 191 191 for (i = 0; i < ubi->vtbl_slots; i++) 192 - if (ubi->volumes[i] && 192 + if (ubi->volumes[i] && !ubi->volumes[i]->is_dead && 193 193 ubi->volumes[i]->name_len == req->name_len && 194 194 !strcmp(ubi->volumes[i]->name, req->name)) { 195 195 ubi_err(ubi, "volume \"%s\" exists (ID %d)", ··· 352 352 err = -EBUSY; 353 353 goto out_unlock; 354 354 } 355 + 356 + /* 357 + * Mark volume as dead at this point to prevent that anyone 358 + * can take a reference to the volume from now on. 359 + * This is necessary as we have to release the spinlock before 360 + * calling ubi_volume_notify. 361 + */ 362 + vol->is_dead = true; 363 + spin_unlock(&ubi->volumes_lock); 364 + 365 + ubi_volume_notify(ubi, vol, UBI_VOLUME_SHUTDOWN); 366 + 367 + spin_lock(&ubi->volumes_lock); 355 368 ubi->volumes[vol_id] = NULL; 356 369 spin_unlock(&ubi->volumes_lock); 357 370
+2
include/linux/mtd/ubi.h
··· 192 192 * or a volume was removed) 193 193 * @UBI_VOLUME_RESIZED: a volume has been re-sized 194 194 * @UBI_VOLUME_RENAMED: a volume has been re-named 195 + * @UBI_VOLUME_SHUTDOWN: a volume is going to removed, shutdown users 195 196 * @UBI_VOLUME_UPDATED: data has been written to a volume 196 197 * 197 198 * These constants define which type of event has happened when a volume ··· 203 202 UBI_VOLUME_REMOVED, 204 203 UBI_VOLUME_RESIZED, 205 204 UBI_VOLUME_RENAMED, 205 + UBI_VOLUME_SHUTDOWN, 206 206 UBI_VOLUME_UPDATED, 207 207 }; 208 208