diff --git a/block/genhd.c b/block/genhd.c index 298ee78c1bda..e7c75c5aa831 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -162,6 +162,7 @@ static struct blk_major_name { int major; char name[16]; void (*probe)(dev_t devt); + struct module *owner; } *major_names[BLKDEV_MAJOR_HASH_SIZE]; static DEFINE_MUTEX(major_names_lock); @@ -190,7 +191,8 @@ void blkdev_show(struct seq_file *seqf, off_t offset) * @major: the requested major device number [1..BLKDEV_MAJOR_MAX-1]. If * @major = 0, try to allocate any unused major number. * @name: the name of the new block device as a zero terminated string - * @probe: allback that is called on access to any minor number of @major + * @probe: callback that is called on access to any minor number of @major + * @owner: THIS_MODULE if @probe is not NULL, ignored if @probe is NULL. * * The @name must be unique within the system. * @@ -208,7 +210,7 @@ void blkdev_show(struct seq_file *seqf, off_t offset) * Use register_blkdev instead for any new code. */ int __register_blkdev(unsigned int major, const char *name, - void (*probe)(dev_t devt)) + void (*probe)(dev_t devt), struct module *owner) { struct blk_major_name **n, *p; int index, ret = 0; @@ -248,6 +250,7 @@ int __register_blkdev(unsigned int major, const char *name, p->major = major; p->probe = probe; + p->owner = owner; strlcpy(p->name, name, sizeof(p->name)); p->next = NULL; index = major_to_index(major); @@ -653,14 +656,29 @@ void blk_request_module(dev_t devt) { unsigned int major = MAJOR(devt); struct blk_major_name **n; + void (*probe_fn)(dev_t devt); mutex_lock(&major_names_lock); for (n = &major_names[major_to_index(major)]; *n; n = &(*n)->next) { - if ((*n)->major == major && (*n)->probe) { - (*n)->probe(devt); - mutex_unlock(&major_names_lock); - return; - } + if ((*n)->major != major || !(*n)->probe) + continue; + if (!try_module_get((*n)->owner)) + break; + /* + * Calling probe function with major_names_lock held causes + * circular locking dependency problem. Thus, call it after + * releasing major_names_lock. + */ + probe_fn = (*n)->probe; + mutex_unlock(&major_names_lock); + /* + * Assuming that unregister_blkdev() is called from module's + * __exit function, a module refcount taken above allows us + * to safely call probe function without major_names_lock held. + */ + probe_fn(devt); + module_put((*n)->owner); + return; } mutex_unlock(&major_names_lock); diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c index a093644ac39f..1b7fe10d49e7 100644 --- a/drivers/block/ataflop.c +++ b/drivers/block/ataflop.c @@ -2016,7 +2016,7 @@ static int __init atari_floppy_init (void) return -ENODEV; mutex_lock(&ataflop_probe_lock); - ret = __register_blkdev(FLOPPY_MAJOR, "fd", ataflop_probe); + ret = __register_blkdev(FLOPPY_MAJOR, "fd", ataflop_probe, THIS_MODULE); if (ret) goto out_unlock; diff --git a/drivers/block/brd.c b/drivers/block/brd.c index 95694113e38e..d0bdfd56dfc8 100644 --- a/drivers/block/brd.c +++ b/drivers/block/brd.c @@ -487,7 +487,7 @@ static int __init brd_init(void) * dynamically. */ - if (__register_blkdev(RAMDISK_MAJOR, "ramdisk", brd_probe)) + if (__register_blkdev(RAMDISK_MAJOR, "ramdisk", brd_probe, THIS_MODULE)) return -EIO; brd_check_and_reset_par(); diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 87460e0e5c72..ee33ba03e6bd 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -4570,7 +4570,7 @@ static int __init do_floppy_init(void) timer_setup(&motor_off_timer[drive], motor_off_callback, 0); } - err = __register_blkdev(FLOPPY_MAJOR, "fd", floppy_probe); + err = __register_blkdev(FLOPPY_MAJOR, "fd", floppy_probe, THIS_MODULE); if (err) goto out_put_disk; diff --git a/drivers/block/loop.c b/drivers/block/loop.c index f0cdff0c5fbf..d6606c3b7d74 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -2564,7 +2564,7 @@ static int __init loop_init(void) goto err_out; - if (__register_blkdev(LOOP_MAJOR, "loop", loop_probe)) { + if (__register_blkdev(LOOP_MAJOR, "loop", loop_probe, THIS_MODULE)) { err = -EIO; goto misc_out; } diff --git a/drivers/md/md.c b/drivers/md/md.c index ae8fe54ea358..c13f45c0f502 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -9584,11 +9584,11 @@ static int __init md_init(void) if (!md_rdev_misc_wq) goto err_rdev_misc_wq; - ret = __register_blkdev(MD_MAJOR, "md", md_probe); + ret = __register_blkdev(MD_MAJOR, "md", md_probe, THIS_MODULE); if (ret < 0) goto err_md; - ret = __register_blkdev(0, "mdp", md_probe); + ret = __register_blkdev(0, "mdp", md_probe, THIS_MODULE); if (ret < 0) goto err_mdp; mdp_major = ret; diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index b8d55af763f9..ddd67a1045e7 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -3729,7 +3729,7 @@ static int __init init_sd(void) SCSI_LOG_HLQUEUE(3, printk("init_sd: sd driver entry point\n")); for (i = 0; i < SD_MAJORS; i++) { - if (__register_blkdev(sd_major(i), "sd", sd_default_probe)) + if (__register_blkdev(sd_major(i), "sd", sd_default_probe, THIS_MODULE)) continue; majors++; } diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 13b34177cc85..b0948003071d 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -303,9 +303,9 @@ struct gendisk *__blk_alloc_disk(int node); void blk_cleanup_disk(struct gendisk *disk); int __register_blkdev(unsigned int major, const char *name, - void (*probe)(dev_t devt)); + void (*probe)(dev_t devt), struct module *owner); #define register_blkdev(major, name) \ - __register_blkdev(major, name, NULL) + __register_blkdev(major, name, NULL, NULL) void unregister_blkdev(unsigned int major, const char *name); bool bdev_check_media_change(struct block_device *bdev);