diff --git a/block/genhd.c b/block/genhd.c index 9f8cb7beaad1..1a9c518ac27b 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -169,6 +169,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); @@ -214,8 +215,8 @@ 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)) +int ____register_blkdev(unsigned int major, const char *name, + void (*probe)(dev_t devt), struct module *owner) { struct blk_major_name **n, *p; int index, ret = 0; @@ -255,6 +256,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); @@ -277,7 +279,7 @@ int __register_blkdev(unsigned int major, const char *name, mutex_unlock(&major_names_lock); return ret; } -EXPORT_SYMBOL(__register_blkdev); +EXPORT_SYMBOL(____register_blkdev); void unregister_blkdev(unsigned int major, const char *name) { @@ -676,14 +678,24 @@ 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; + /* + * Can't call probe callback if module unloading request was + * already accepted, for module unloading function will hold + * major_names_lock via unregister_blkdev(). + */ + if (!try_module_get((*n)->owner)) + break; + probe_fn = (*n)->probe; + mutex_unlock(&major_names_lock); + probe_fn(devt); + module_put((*n)->owner); + return; } mutex_unlock(&major_names_lock); diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 6fc26f7bdf71..070b73c043e6 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -277,10 +277,12 @@ extern void put_disk(struct gendisk *disk); #define alloc_disk(minors) alloc_disk_node(minors, NUMA_NO_NODE) -int __register_blkdev(unsigned int major, const char *name, - void (*probe)(dev_t devt)); +int ____register_blkdev(unsigned int major, const char *name, + void (*probe)(dev_t devt), struct module *owner); +#define __register_blkdev(major, name, probe) \ + ____register_blkdev(major, name, probe, THIS_MODULE) #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);