Skip to content

Commit 83cbce9

Browse files
mcgrofaxboe
authored andcommitted
block: add error handling for device_add_disk / add_disk
Properly unwind on errors in device_add_disk. This is the initial work as drivers are not converted yet, which will follow in separate patches. Signed-off-by: Luis Chamberlain <[email protected]> [hch: major rebase. All bugs are probably mine] Signed-off-by: Christoph Hellwig <[email protected]> Reviewed-by: Hannes Reinecke <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jens Axboe <[email protected]>
1 parent 92e7755 commit 83cbce9

File tree

2 files changed

+62
-38
lines changed

2 files changed

+62
-38
lines changed

block/genhd.c

Lines changed: 58 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -417,11 +417,8 @@ static void disk_scan_partitions(struct gendisk *disk)
417417
*
418418
* This function registers the partitioning information in @disk
419419
* with the kernel.
420-
*
421-
* FIXME: error handling
422420
*/
423-
424-
void device_add_disk(struct device *parent, struct gendisk *disk,
421+
int device_add_disk(struct device *parent, struct gendisk *disk,
425422
const struct attribute_group **groups)
426423

427424
{
@@ -444,27 +441,29 @@ void device_add_disk(struct device *parent, struct gendisk *disk,
444441
* and all partitions from the extended dev_t space.
445442
*/
446443
if (disk->major) {
447-
WARN_ON(!disk->minors);
444+
if (WARN_ON(!disk->minors))
445+
return -EINVAL;
448446

449447
if (disk->minors > DISK_MAX_PARTS) {
450448
pr_err("block: can't allocate more than %d partitions\n",
451449
DISK_MAX_PARTS);
452450
disk->minors = DISK_MAX_PARTS;
453451
}
454452
} else {
455-
WARN_ON(disk->minors);
453+
if (WARN_ON(disk->minors))
454+
return -EINVAL;
456455

457456
ret = blk_alloc_ext_minor();
458-
if (ret < 0) {
459-
WARN_ON(1);
460-
return;
461-
}
457+
if (ret < 0)
458+
return ret;
462459
disk->major = BLOCK_EXT_MAJOR;
463460
disk->first_minor = MINOR(ret);
464461
disk->flags |= GENHD_FL_EXT_DEVT;
465462
}
466463

467-
disk_alloc_events(disk);
464+
ret = disk_alloc_events(disk);
465+
if (ret)
466+
goto out_free_ext_minor;
468467

469468
/* delay uevents, until we scanned partition table */
470469
dev_set_uevent_suppress(ddev, 1);
@@ -474,15 +473,14 @@ void device_add_disk(struct device *parent, struct gendisk *disk,
474473
dev_set_name(ddev, "%s", disk->disk_name);
475474
if (!(disk->flags & GENHD_FL_HIDDEN))
476475
ddev->devt = MKDEV(disk->major, disk->first_minor);
477-
if (device_add(ddev))
478-
return;
476+
ret = device_add(ddev);
477+
if (ret)
478+
goto out_disk_release_events;
479479
if (!sysfs_deprecated) {
480480
ret = sysfs_create_link(block_depr, &ddev->kobj,
481481
kobject_name(&ddev->kobj));
482-
if (ret) {
483-
device_del(ddev);
484-
return;
485-
}
482+
if (ret)
483+
goto out_device_del;
486484
}
487485

488486
/*
@@ -492,23 +490,25 @@ void device_add_disk(struct device *parent, struct gendisk *disk,
492490
*/
493491
pm_runtime_set_memalloc_noio(ddev, true);
494492

495-
blk_integrity_add(disk);
493+
ret = blk_integrity_add(disk);
494+
if (ret)
495+
goto out_del_block_link;
496496

497497
disk->part0->bd_holder_dir =
498498
kobject_create_and_add("holders", &ddev->kobj);
499+
if (!disk->part0->bd_holder_dir)
500+
goto out_del_integrity;
499501
disk->slave_dir = kobject_create_and_add("slaves", &ddev->kobj);
502+
if (!disk->slave_dir)
503+
goto out_put_holder_dir;
500504

501-
/*
502-
* XXX: this is a mess, can't wait for real error handling in add_disk.
503-
* Make sure ->slave_dir is NULL if we failed some of the registration
504-
* so that the cleanup in bd_unlink_disk_holder works properly.
505-
*/
506-
if (bd_register_pending_holders(disk) < 0) {
507-
kobject_put(disk->slave_dir);
508-
disk->slave_dir = NULL;
509-
}
505+
ret = bd_register_pending_holders(disk);
506+
if (ret < 0)
507+
goto out_put_slave_dir;
510508

511-
blk_register_queue(disk);
509+
ret = blk_register_queue(disk);
510+
if (ret)
511+
goto out_put_slave_dir;
512512

513513
if (disk->flags & GENHD_FL_HIDDEN) {
514514
/*
@@ -520,13 +520,13 @@ void device_add_disk(struct device *parent, struct gendisk *disk,
520520
} else {
521521
ret = bdi_register(disk->bdi, "%u:%u",
522522
disk->major, disk->first_minor);
523-
WARN_ON(ret);
523+
if (ret)
524+
goto out_unregister_queue;
524525
bdi_set_owner(disk->bdi, ddev);
525-
if (disk->bdi->dev) {
526-
ret = sysfs_create_link(&ddev->kobj,
527-
&disk->bdi->dev->kobj, "bdi");
528-
WARN_ON(ret);
529-
}
526+
ret = sysfs_create_link(&ddev->kobj,
527+
&disk->bdi->dev->kobj, "bdi");
528+
if (ret)
529+
goto out_unregister_bdi;
530530

531531
bdev_add(disk->part0, ddev->devt);
532532
disk_scan_partitions(disk);
@@ -541,6 +541,30 @@ void device_add_disk(struct device *parent, struct gendisk *disk,
541541

542542
disk_update_readahead(disk);
543543
disk_add_events(disk);
544+
return 0;
545+
546+
out_unregister_bdi:
547+
if (!(disk->flags & GENHD_FL_HIDDEN))
548+
bdi_unregister(disk->bdi);
549+
out_unregister_queue:
550+
blk_unregister_queue(disk);
551+
out_put_slave_dir:
552+
kobject_put(disk->slave_dir);
553+
out_put_holder_dir:
554+
kobject_put(disk->part0->bd_holder_dir);
555+
out_del_integrity:
556+
blk_integrity_del(disk);
557+
out_del_block_link:
558+
if (!sysfs_deprecated)
559+
sysfs_remove_link(block_depr, dev_name(ddev));
560+
out_device_del:
561+
device_del(ddev);
562+
out_disk_release_events:
563+
disk_release_events(disk);
564+
out_free_ext_minor:
565+
if (disk->major == BLOCK_EXT_MAJOR)
566+
blk_free_ext_minor(disk->first_minor);
567+
return WARN_ON_ONCE(ret); /* keep until all callers handle errors */
544568
}
545569
EXPORT_SYMBOL(device_add_disk);
546570

include/linux/genhd.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -214,11 +214,11 @@ static inline dev_t disk_devt(struct gendisk *disk)
214214
void disk_uevent(struct gendisk *disk, enum kobject_action action);
215215

216216
/* block/genhd.c */
217-
extern void device_add_disk(struct device *parent, struct gendisk *disk,
218-
const struct attribute_group **groups);
219-
static inline void add_disk(struct gendisk *disk)
217+
int device_add_disk(struct device *parent, struct gendisk *disk,
218+
const struct attribute_group **groups);
219+
static inline int add_disk(struct gendisk *disk)
220220
{
221-
device_add_disk(NULL, disk, NULL);
221+
return device_add_disk(NULL, disk, NULL);
222222
}
223223
extern void del_gendisk(struct gendisk *gp);
224224

0 commit comments

Comments
 (0)