diff options
author | Tahsin Erdogan <tahsin@google.com> | 2017-02-14 22:27:38 -0500 |
---|---|---|
committer | Jens Axboe <axboe@fb.com> | 2017-02-15 10:40:04 -0500 |
commit | b410aff2bd9f101bc096069612391aecce204cd1 (patch) | |
tree | 977805735b8ce122d67da721e8a960c15ef89769 | |
parent | 3d492c2e0146ccaad08c7cbe16e2e229328b5e79 (diff) |
block: do not allow updates through sysfs until registration completes
When a new disk shows up, sysfs queue directory is created before elevator
is registered. This allows a user to attempt a scheduler switch even though
the initial registration hasn't completed yet.
In one scenario, blk_register_queue() calls elv_register_queue() and
right before cfq_registered_queue() is called, another process executes
elevator_switch() and replaces q->elevator with deadline scheduler. When
cfq_registered_queue() executes it interprets e->elevator_data as struct
cfq_data even though it is actually struct deadline_data.
Grab q->sysfs_lock in blk_register_queue() to synchronize with sysfs
callers.
Signed-off-by: Tahsin Erdogan <tahsin@google.com>
Signed-off-by: Jens Axboe <axboe@fb.com>
-rw-r--r-- | block/blk-sysfs.c | 19 |
1 files changed, 12 insertions, 7 deletions
diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 070d81bae1d5..002af836aa87 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c | |||
@@ -902,17 +902,20 @@ int blk_register_queue(struct gendisk *disk) | |||
902 | if (ret) | 902 | if (ret) |
903 | return ret; | 903 | return ret; |
904 | 904 | ||
905 | if (q->mq_ops) | ||
906 | blk_mq_register_dev(dev, q); | ||
907 | |||
908 | /* Prevent changes through sysfs until registration is completed. */ | ||
909 | mutex_lock(&q->sysfs_lock); | ||
910 | |||
905 | ret = kobject_add(&q->kobj, kobject_get(&dev->kobj), "%s", "queue"); | 911 | ret = kobject_add(&q->kobj, kobject_get(&dev->kobj), "%s", "queue"); |
906 | if (ret < 0) { | 912 | if (ret < 0) { |
907 | blk_trace_remove_sysfs(dev); | 913 | blk_trace_remove_sysfs(dev); |
908 | return ret; | 914 | goto unlock; |
909 | } | 915 | } |
910 | 916 | ||
911 | kobject_uevent(&q->kobj, KOBJ_ADD); | 917 | kobject_uevent(&q->kobj, KOBJ_ADD); |
912 | 918 | ||
913 | if (q->mq_ops) | ||
914 | blk_mq_register_dev(dev, q); | ||
915 | |||
916 | blk_wb_init(q); | 919 | blk_wb_init(q); |
917 | 920 | ||
918 | if (q->request_fn || (q->mq_ops && q->elevator)) { | 921 | if (q->request_fn || (q->mq_ops && q->elevator)) { |
@@ -922,11 +925,13 @@ int blk_register_queue(struct gendisk *disk) | |||
922 | kobject_del(&q->kobj); | 925 | kobject_del(&q->kobj); |
923 | blk_trace_remove_sysfs(dev); | 926 | blk_trace_remove_sysfs(dev); |
924 | kobject_put(&dev->kobj); | 927 | kobject_put(&dev->kobj); |
925 | return ret; | 928 | goto unlock; |
926 | } | 929 | } |
927 | } | 930 | } |
928 | 931 | ret = 0; | |
929 | return 0; | 932 | unlock: |
933 | mutex_unlock(&q->sysfs_lock); | ||
934 | return ret; | ||
930 | } | 935 | } |
931 | 936 | ||
932 | void blk_unregister_queue(struct gendisk *disk) | 937 | void blk_unregister_queue(struct gendisk *disk) |