Skip to content

Commit 98e68f6

Browse files
Ming Leiaxboe
authored andcommitted
block: prevent adding/deleting disk during updating nr_hw_queues
Both adding/deleting disk code are reader of `nr_hw_queues`, so we can't allow them in-progress when updating nr_hw_queues, kernel panic and kasan has been reported in [1]. Prevent adding/deleting disk during updating nr_hw_queues by adding rw_semaphore to tagset, write lock is grabbed in blk_mq_update_nr_hw_queues(), and read lock is acquired when adding/deleting disk. Also mark GFP_NOIO allocation scope for adding/deleting disk because blk_mq_update_nr_hw_queues() is part of some driver's error handler. This way avoids lot of trouble. Reviewed-by: Christoph Hellwig <[email protected]> Reviewed-by: Hannes Reinecke <[email protected]> Reviewed-by: Nilay Shroff <[email protected]> Suggested-by: Nilay Shroff <[email protected]> Reported-by: Nilay Shroff <[email protected]> Closes: https://lore.kernel.org/linux-block/[email protected]/ Signed-off-by: Ming Lei <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jens Axboe <[email protected]>
1 parent 5fad149 commit 98e68f6

File tree

3 files changed

+86
-34
lines changed

3 files changed

+86
-34
lines changed

block/blk-mq.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4846,6 +4846,8 @@ int blk_mq_alloc_tag_set(struct blk_mq_tag_set *set)
48464846
goto out_free_srcu;
48474847
}
48484848

4849+
init_rwsem(&set->update_nr_hwq_lock);
4850+
48494851
ret = -ENOMEM;
48504852
set->tags = kcalloc_node(set->nr_hw_queues,
48514853
sizeof(struct blk_mq_tags *), GFP_KERNEL,
@@ -5141,9 +5143,11 @@ static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set,
51415143

51425144
void blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, int nr_hw_queues)
51435145
{
5146+
down_write(&set->update_nr_hwq_lock);
51445147
mutex_lock(&set->tag_list_lock);
51455148
__blk_mq_update_nr_hw_queues(set, nr_hw_queues);
51465149
mutex_unlock(&set->tag_list_lock);
5150+
up_write(&set->update_nr_hwq_lock);
51475151
}
51485152
EXPORT_SYMBOL_GPL(blk_mq_update_nr_hw_queues);
51495153

block/genhd.c

Lines changed: 79 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -415,19 +415,9 @@ static void add_disk_final(struct gendisk *disk)
415415
set_bit(GD_ADDED, &disk->state);
416416
}
417417

418-
/**
419-
* add_disk_fwnode - add disk information to kernel list with fwnode
420-
* @parent: parent device for the disk
421-
* @disk: per-device partitioning information
422-
* @groups: Additional per-device sysfs groups
423-
* @fwnode: attached disk fwnode
424-
*
425-
* This function registers the partitioning information in @disk
426-
* with the kernel. Also attach a fwnode to the disk device.
427-
*/
428-
int __must_check add_disk_fwnode(struct device *parent, struct gendisk *disk,
429-
const struct attribute_group **groups,
430-
struct fwnode_handle *fwnode)
418+
static int __add_disk(struct device *parent, struct gendisk *disk,
419+
const struct attribute_group **groups,
420+
struct fwnode_handle *fwnode)
431421

432422
{
433423
struct device *ddev = disk_to_dev(disk);
@@ -550,7 +540,6 @@ int __must_check add_disk_fwnode(struct device *parent, struct gendisk *disk,
550540
*/
551541
disk->part0->bd_dev = MKDEV(disk->major, disk->first_minor);
552542
}
553-
add_disk_final(disk);
554543
return 0;
555544

556545
out_unregister_bdi:
@@ -580,6 +569,45 @@ int __must_check add_disk_fwnode(struct device *parent, struct gendisk *disk,
580569
}
581570
return ret;
582571
}
572+
573+
/**
574+
* add_disk_fwnode - add disk information to kernel list with fwnode
575+
* @parent: parent device for the disk
576+
* @disk: per-device partitioning information
577+
* @groups: Additional per-device sysfs groups
578+
* @fwnode: attached disk fwnode
579+
*
580+
* This function registers the partitioning information in @disk
581+
* with the kernel. Also attach a fwnode to the disk device.
582+
*/
583+
int __must_check add_disk_fwnode(struct device *parent, struct gendisk *disk,
584+
const struct attribute_group **groups,
585+
struct fwnode_handle *fwnode)
586+
{
587+
struct blk_mq_tag_set *set;
588+
unsigned int memflags;
589+
int ret;
590+
591+
if (queue_is_mq(disk->queue)) {
592+
set = disk->queue->tag_set;
593+
memflags = memalloc_noio_save();
594+
down_read(&set->update_nr_hwq_lock);
595+
ret = __add_disk(parent, disk, groups, fwnode);
596+
up_read(&set->update_nr_hwq_lock);
597+
memalloc_noio_restore(memflags);
598+
} else {
599+
ret = __add_disk(parent, disk, groups, fwnode);
600+
}
601+
602+
/*
603+
* add_disk_final() needn't to read `nr_hw_queues`, so move it out
604+
* of read lock `set->update_nr_hwq_lock` for avoiding unnecessary
605+
* lock dependency on `disk->open_mutex` from scanning partition.
606+
*/
607+
if (!ret)
608+
add_disk_final(disk);
609+
return ret;
610+
}
583611
EXPORT_SYMBOL_GPL(add_disk_fwnode);
584612

585613
/**
@@ -660,26 +688,7 @@ void blk_mark_disk_dead(struct gendisk *disk)
660688
}
661689
EXPORT_SYMBOL_GPL(blk_mark_disk_dead);
662690

663-
/**
664-
* del_gendisk - remove the gendisk
665-
* @disk: the struct gendisk to remove
666-
*
667-
* Removes the gendisk and all its associated resources. This deletes the
668-
* partitions associated with the gendisk, and unregisters the associated
669-
* request_queue.
670-
*
671-
* This is the counter to the respective __device_add_disk() call.
672-
*
673-
* The final removal of the struct gendisk happens when its refcount reaches 0
674-
* with put_disk(), which should be called after del_gendisk(), if
675-
* __device_add_disk() was used.
676-
*
677-
* Drivers exist which depend on the release of the gendisk to be synchronous,
678-
* it should not be deferred.
679-
*
680-
* Context: can sleep
681-
*/
682-
void del_gendisk(struct gendisk *disk)
691+
static void __del_gendisk(struct gendisk *disk)
683692
{
684693
struct request_queue *q = disk->queue;
685694
struct block_device *part;
@@ -772,6 +781,42 @@ void del_gendisk(struct gendisk *disk)
772781
if (start_drain)
773782
blk_unfreeze_release_lock(q);
774783
}
784+
785+
/**
786+
* del_gendisk - remove the gendisk
787+
* @disk: the struct gendisk to remove
788+
*
789+
* Removes the gendisk and all its associated resources. This deletes the
790+
* partitions associated with the gendisk, and unregisters the associated
791+
* request_queue.
792+
*
793+
* This is the counter to the respective __device_add_disk() call.
794+
*
795+
* The final removal of the struct gendisk happens when its refcount reaches 0
796+
* with put_disk(), which should be called after del_gendisk(), if
797+
* __device_add_disk() was used.
798+
*
799+
* Drivers exist which depend on the release of the gendisk to be synchronous,
800+
* it should not be deferred.
801+
*
802+
* Context: can sleep
803+
*/
804+
void del_gendisk(struct gendisk *disk)
805+
{
806+
struct blk_mq_tag_set *set;
807+
unsigned int memflags;
808+
809+
if (!queue_is_mq(disk->queue)) {
810+
__del_gendisk(disk);
811+
} else {
812+
set = disk->queue->tag_set;
813+
memflags = memalloc_noio_save();
814+
down_read(&set->update_nr_hwq_lock);
815+
__del_gendisk(disk);
816+
up_read(&set->update_nr_hwq_lock);
817+
memalloc_noio_restore(memflags);
818+
}
819+
}
775820
EXPORT_SYMBOL(del_gendisk);
776821

777822
/**

include/linux/blk-mq.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/prefetch.h>
1010
#include <linux/srcu.h>
1111
#include <linux/rw_hint.h>
12+
#include <linux/rwsem.h>
1213

1314
struct blk_mq_tags;
1415
struct blk_flush_queue;
@@ -527,6 +528,8 @@ struct blk_mq_tag_set {
527528
struct mutex tag_list_lock;
528529
struct list_head tag_list;
529530
struct srcu_struct *srcu;
531+
532+
struct rw_semaphore update_nr_hwq_lock;
530533
};
531534

532535
/**

0 commit comments

Comments
 (0)