diff options
-rw-r--r-- | block/blk-core.c | 1 | ||||
-rw-r--r-- | block/blk-mq-sysfs.c | 12 | ||||
-rw-r--r-- | block/blk-sysfs.c | 46 | ||||
-rw-r--r-- | block/blk.h | 2 | ||||
-rw-r--r-- | block/elevator.c | 55 | ||||
-rw-r--r-- | include/linux/blkdev.h | 1 |
6 files changed, 84 insertions, 33 deletions
diff --git a/block/blk-core.c b/block/blk-core.c index 5d0d7441a443..77807a5d7f9e 100644 --- a/block/blk-core.c +++ b/block/blk-core.c | |||
@@ -520,6 +520,7 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id) | |||
520 | mutex_init(&q->blk_trace_mutex); | 520 | mutex_init(&q->blk_trace_mutex); |
521 | #endif | 521 | #endif |
522 | mutex_init(&q->sysfs_lock); | 522 | mutex_init(&q->sysfs_lock); |
523 | mutex_init(&q->sysfs_dir_lock); | ||
523 | spin_lock_init(&q->queue_lock); | 524 | spin_lock_init(&q->queue_lock); |
524 | 525 | ||
525 | init_waitqueue_head(&q->mq_freeze_wq); | 526 | init_waitqueue_head(&q->mq_freeze_wq); |
diff --git a/block/blk-mq-sysfs.c b/block/blk-mq-sysfs.c index 6ddde3774ebe..a0d3ce30fa08 100644 --- a/block/blk-mq-sysfs.c +++ b/block/blk-mq-sysfs.c | |||
@@ -270,7 +270,7 @@ void blk_mq_unregister_dev(struct device *dev, struct request_queue *q) | |||
270 | struct blk_mq_hw_ctx *hctx; | 270 | struct blk_mq_hw_ctx *hctx; |
271 | int i; | 271 | int i; |
272 | 272 | ||
273 | lockdep_assert_held(&q->sysfs_lock); | 273 | lockdep_assert_held(&q->sysfs_dir_lock); |
274 | 274 | ||
275 | queue_for_each_hw_ctx(q, hctx, i) | 275 | queue_for_each_hw_ctx(q, hctx, i) |
276 | blk_mq_unregister_hctx(hctx); | 276 | blk_mq_unregister_hctx(hctx); |
@@ -320,7 +320,7 @@ int __blk_mq_register_dev(struct device *dev, struct request_queue *q) | |||
320 | int ret, i; | 320 | int ret, i; |
321 | 321 | ||
322 | WARN_ON_ONCE(!q->kobj.parent); | 322 | WARN_ON_ONCE(!q->kobj.parent); |
323 | lockdep_assert_held(&q->sysfs_lock); | 323 | lockdep_assert_held(&q->sysfs_dir_lock); |
324 | 324 | ||
325 | ret = kobject_add(q->mq_kobj, kobject_get(&dev->kobj), "%s", "mq"); | 325 | ret = kobject_add(q->mq_kobj, kobject_get(&dev->kobj), "%s", "mq"); |
326 | if (ret < 0) | 326 | if (ret < 0) |
@@ -354,7 +354,7 @@ void blk_mq_sysfs_unregister(struct request_queue *q) | |||
354 | struct blk_mq_hw_ctx *hctx; | 354 | struct blk_mq_hw_ctx *hctx; |
355 | int i; | 355 | int i; |
356 | 356 | ||
357 | mutex_lock(&q->sysfs_lock); | 357 | mutex_lock(&q->sysfs_dir_lock); |
358 | if (!q->mq_sysfs_init_done) | 358 | if (!q->mq_sysfs_init_done) |
359 | goto unlock; | 359 | goto unlock; |
360 | 360 | ||
@@ -362,7 +362,7 @@ void blk_mq_sysfs_unregister(struct request_queue *q) | |||
362 | blk_mq_unregister_hctx(hctx); | 362 | blk_mq_unregister_hctx(hctx); |
363 | 363 | ||
364 | unlock: | 364 | unlock: |
365 | mutex_unlock(&q->sysfs_lock); | 365 | mutex_unlock(&q->sysfs_dir_lock); |
366 | } | 366 | } |
367 | 367 | ||
368 | int blk_mq_sysfs_register(struct request_queue *q) | 368 | int blk_mq_sysfs_register(struct request_queue *q) |
@@ -370,7 +370,7 @@ int blk_mq_sysfs_register(struct request_queue *q) | |||
370 | struct blk_mq_hw_ctx *hctx; | 370 | struct blk_mq_hw_ctx *hctx; |
371 | int i, ret = 0; | 371 | int i, ret = 0; |
372 | 372 | ||
373 | mutex_lock(&q->sysfs_lock); | 373 | mutex_lock(&q->sysfs_dir_lock); |
374 | if (!q->mq_sysfs_init_done) | 374 | if (!q->mq_sysfs_init_done) |
375 | goto unlock; | 375 | goto unlock; |
376 | 376 | ||
@@ -381,7 +381,7 @@ int blk_mq_sysfs_register(struct request_queue *q) | |||
381 | } | 381 | } |
382 | 382 | ||
383 | unlock: | 383 | unlock: |
384 | mutex_unlock(&q->sysfs_lock); | 384 | mutex_unlock(&q->sysfs_dir_lock); |
385 | 385 | ||
386 | return ret; | 386 | return ret; |
387 | } | 387 | } |
diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 5b0b5224cfd4..107513495220 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c | |||
@@ -938,6 +938,7 @@ int blk_register_queue(struct gendisk *disk) | |||
938 | int ret; | 938 | int ret; |
939 | struct device *dev = disk_to_dev(disk); | 939 | struct device *dev = disk_to_dev(disk); |
940 | struct request_queue *q = disk->queue; | 940 | struct request_queue *q = disk->queue; |
941 | bool has_elevator = false; | ||
941 | 942 | ||
942 | if (WARN_ON(!q)) | 943 | if (WARN_ON(!q)) |
943 | return -ENXIO; | 944 | return -ENXIO; |
@@ -945,7 +946,6 @@ int blk_register_queue(struct gendisk *disk) | |||
945 | WARN_ONCE(blk_queue_registered(q), | 946 | WARN_ONCE(blk_queue_registered(q), |
946 | "%s is registering an already registered queue\n", | 947 | "%s is registering an already registered queue\n", |
947 | kobject_name(&dev->kobj)); | 948 | kobject_name(&dev->kobj)); |
948 | blk_queue_flag_set(QUEUE_FLAG_REGISTERED, q); | ||
949 | 949 | ||
950 | /* | 950 | /* |
951 | * SCSI probing may synchronously create and destroy a lot of | 951 | * SCSI probing may synchronously create and destroy a lot of |
@@ -965,8 +965,7 @@ int blk_register_queue(struct gendisk *disk) | |||
965 | if (ret) | 965 | if (ret) |
966 | return ret; | 966 | return ret; |
967 | 967 | ||
968 | /* Prevent changes through sysfs until registration is completed. */ | 968 | mutex_lock(&q->sysfs_dir_lock); |
969 | mutex_lock(&q->sysfs_lock); | ||
970 | 969 | ||
971 | ret = kobject_add(&q->kobj, kobject_get(&dev->kobj), "%s", "queue"); | 970 | ret = kobject_add(&q->kobj, kobject_get(&dev->kobj), "%s", "queue"); |
972 | if (ret < 0) { | 971 | if (ret < 0) { |
@@ -987,26 +986,36 @@ int blk_register_queue(struct gendisk *disk) | |||
987 | blk_mq_debugfs_register(q); | 986 | blk_mq_debugfs_register(q); |
988 | } | 987 | } |
989 | 988 | ||
990 | kobject_uevent(&q->kobj, KOBJ_ADD); | 989 | /* |
991 | 990 | * The flag of QUEUE_FLAG_REGISTERED isn't set yet, so elevator | |
992 | wbt_enable_default(q); | 991 | * switch won't happen at all. |
993 | 992 | */ | |
994 | blk_throtl_register_queue(q); | ||
995 | |||
996 | if (q->elevator) { | 993 | if (q->elevator) { |
997 | ret = elv_register_queue(q); | 994 | ret = elv_register_queue(q, false); |
998 | if (ret) { | 995 | if (ret) { |
999 | mutex_unlock(&q->sysfs_lock); | 996 | mutex_unlock(&q->sysfs_dir_lock); |
1000 | kobject_uevent(&q->kobj, KOBJ_REMOVE); | ||
1001 | kobject_del(&q->kobj); | 997 | kobject_del(&q->kobj); |
1002 | blk_trace_remove_sysfs(dev); | 998 | blk_trace_remove_sysfs(dev); |
1003 | kobject_put(&dev->kobj); | 999 | kobject_put(&dev->kobj); |
1004 | return ret; | 1000 | return ret; |
1005 | } | 1001 | } |
1002 | has_elevator = true; | ||
1006 | } | 1003 | } |
1004 | |||
1005 | mutex_lock(&q->sysfs_lock); | ||
1006 | blk_queue_flag_set(QUEUE_FLAG_REGISTERED, q); | ||
1007 | wbt_enable_default(q); | ||
1008 | blk_throtl_register_queue(q); | ||
1009 | |||
1010 | /* Now everything is ready and send out KOBJ_ADD uevent */ | ||
1011 | kobject_uevent(&q->kobj, KOBJ_ADD); | ||
1012 | if (has_elevator) | ||
1013 | kobject_uevent(&q->elevator->kobj, KOBJ_ADD); | ||
1014 | mutex_unlock(&q->sysfs_lock); | ||
1015 | |||
1007 | ret = 0; | 1016 | ret = 0; |
1008 | unlock: | 1017 | unlock: |
1009 | mutex_unlock(&q->sysfs_lock); | 1018 | mutex_unlock(&q->sysfs_dir_lock); |
1010 | return ret; | 1019 | return ret; |
1011 | } | 1020 | } |
1012 | EXPORT_SYMBOL_GPL(blk_register_queue); | 1021 | EXPORT_SYMBOL_GPL(blk_register_queue); |
@@ -1021,6 +1030,7 @@ EXPORT_SYMBOL_GPL(blk_register_queue); | |||
1021 | void blk_unregister_queue(struct gendisk *disk) | 1030 | void blk_unregister_queue(struct gendisk *disk) |
1022 | { | 1031 | { |
1023 | struct request_queue *q = disk->queue; | 1032 | struct request_queue *q = disk->queue; |
1033 | bool has_elevator; | ||
1024 | 1034 | ||
1025 | if (WARN_ON(!q)) | 1035 | if (WARN_ON(!q)) |
1026 | return; | 1036 | return; |
@@ -1035,25 +1045,25 @@ void blk_unregister_queue(struct gendisk *disk) | |||
1035 | * concurrent elv_iosched_store() calls. | 1045 | * concurrent elv_iosched_store() calls. |
1036 | */ | 1046 | */ |
1037 | mutex_lock(&q->sysfs_lock); | 1047 | mutex_lock(&q->sysfs_lock); |
1038 | |||
1039 | blk_queue_flag_clear(QUEUE_FLAG_REGISTERED, q); | 1048 | blk_queue_flag_clear(QUEUE_FLAG_REGISTERED, q); |
1049 | has_elevator = !!q->elevator; | ||
1050 | mutex_unlock(&q->sysfs_lock); | ||
1040 | 1051 | ||
1052 | mutex_lock(&q->sysfs_dir_lock); | ||
1041 | /* | 1053 | /* |
1042 | * Remove the sysfs attributes before unregistering the queue data | 1054 | * Remove the sysfs attributes before unregistering the queue data |
1043 | * structures that can be modified through sysfs. | 1055 | * structures that can be modified through sysfs. |
1044 | */ | 1056 | */ |
1045 | if (queue_is_mq(q)) | 1057 | if (queue_is_mq(q)) |
1046 | blk_mq_unregister_dev(disk_to_dev(disk), q); | 1058 | blk_mq_unregister_dev(disk_to_dev(disk), q); |
1047 | mutex_unlock(&q->sysfs_lock); | ||
1048 | 1059 | ||
1049 | kobject_uevent(&q->kobj, KOBJ_REMOVE); | 1060 | kobject_uevent(&q->kobj, KOBJ_REMOVE); |
1050 | kobject_del(&q->kobj); | 1061 | kobject_del(&q->kobj); |
1051 | blk_trace_remove_sysfs(disk_to_dev(disk)); | 1062 | blk_trace_remove_sysfs(disk_to_dev(disk)); |
1052 | 1063 | ||
1053 | mutex_lock(&q->sysfs_lock); | 1064 | if (has_elevator) |
1054 | if (q->elevator) | ||
1055 | elv_unregister_queue(q); | 1065 | elv_unregister_queue(q); |
1056 | mutex_unlock(&q->sysfs_lock); | 1066 | mutex_unlock(&q->sysfs_dir_lock); |
1057 | 1067 | ||
1058 | kobject_put(&disk_to_dev(disk)->kobj); | 1068 | kobject_put(&disk_to_dev(disk)->kobj); |
1059 | } | 1069 | } |
diff --git a/block/blk.h b/block/blk.h index de6b2e146d6e..e4619fc5c99a 100644 --- a/block/blk.h +++ b/block/blk.h | |||
@@ -188,7 +188,7 @@ int elevator_init_mq(struct request_queue *q); | |||
188 | int elevator_switch_mq(struct request_queue *q, | 188 | int elevator_switch_mq(struct request_queue *q, |
189 | struct elevator_type *new_e); | 189 | struct elevator_type *new_e); |
190 | void __elevator_exit(struct request_queue *, struct elevator_queue *); | 190 | void __elevator_exit(struct request_queue *, struct elevator_queue *); |
191 | int elv_register_queue(struct request_queue *q); | 191 | int elv_register_queue(struct request_queue *q, bool uevent); |
192 | void elv_unregister_queue(struct request_queue *q); | 192 | void elv_unregister_queue(struct request_queue *q); |
193 | 193 | ||
194 | static inline void elevator_exit(struct request_queue *q, | 194 | static inline void elevator_exit(struct request_queue *q, |
diff --git a/block/elevator.c b/block/elevator.c index 03d923196569..4781c4205a5d 100644 --- a/block/elevator.c +++ b/block/elevator.c | |||
@@ -470,13 +470,16 @@ static struct kobj_type elv_ktype = { | |||
470 | .release = elevator_release, | 470 | .release = elevator_release, |
471 | }; | 471 | }; |
472 | 472 | ||
473 | int elv_register_queue(struct request_queue *q) | 473 | /* |
474 | * elv_register_queue is called from either blk_register_queue or | ||
475 | * elevator_switch, elevator switch is prevented from being happen | ||
476 | * in the two paths, so it is safe to not hold q->sysfs_lock. | ||
477 | */ | ||
478 | int elv_register_queue(struct request_queue *q, bool uevent) | ||
474 | { | 479 | { |
475 | struct elevator_queue *e = q->elevator; | 480 | struct elevator_queue *e = q->elevator; |
476 | int error; | 481 | int error; |
477 | 482 | ||
478 | lockdep_assert_held(&q->sysfs_lock); | ||
479 | |||
480 | error = kobject_add(&e->kobj, &q->kobj, "%s", "iosched"); | 483 | error = kobject_add(&e->kobj, &q->kobj, "%s", "iosched"); |
481 | if (!error) { | 484 | if (!error) { |
482 | struct elv_fs_entry *attr = e->type->elevator_attrs; | 485 | struct elv_fs_entry *attr = e->type->elevator_attrs; |
@@ -487,24 +490,34 @@ int elv_register_queue(struct request_queue *q) | |||
487 | attr++; | 490 | attr++; |
488 | } | 491 | } |
489 | } | 492 | } |
490 | kobject_uevent(&e->kobj, KOBJ_ADD); | 493 | if (uevent) |
494 | kobject_uevent(&e->kobj, KOBJ_ADD); | ||
495 | |||
496 | mutex_lock(&q->sysfs_lock); | ||
491 | e->registered = 1; | 497 | e->registered = 1; |
498 | mutex_unlock(&q->sysfs_lock); | ||
492 | } | 499 | } |
493 | return error; | 500 | return error; |
494 | } | 501 | } |
495 | 502 | ||
503 | /* | ||
504 | * elv_unregister_queue is called from either blk_unregister_queue or | ||
505 | * elevator_switch, elevator switch is prevented from being happen | ||
506 | * in the two paths, so it is safe to not hold q->sysfs_lock. | ||
507 | */ | ||
496 | void elv_unregister_queue(struct request_queue *q) | 508 | void elv_unregister_queue(struct request_queue *q) |
497 | { | 509 | { |
498 | lockdep_assert_held(&q->sysfs_lock); | ||
499 | |||
500 | if (q) { | 510 | if (q) { |
501 | struct elevator_queue *e = q->elevator; | 511 | struct elevator_queue *e = q->elevator; |
502 | 512 | ||
503 | kobject_uevent(&e->kobj, KOBJ_REMOVE); | 513 | kobject_uevent(&e->kobj, KOBJ_REMOVE); |
504 | kobject_del(&e->kobj); | 514 | kobject_del(&e->kobj); |
515 | |||
516 | mutex_lock(&q->sysfs_lock); | ||
505 | e->registered = 0; | 517 | e->registered = 0; |
506 | /* Re-enable throttling in case elevator disabled it */ | 518 | /* Re-enable throttling in case elevator disabled it */ |
507 | wbt_enable_default(q); | 519 | wbt_enable_default(q); |
520 | mutex_unlock(&q->sysfs_lock); | ||
508 | } | 521 | } |
509 | } | 522 | } |
510 | 523 | ||
@@ -567,10 +580,32 @@ int elevator_switch_mq(struct request_queue *q, | |||
567 | lockdep_assert_held(&q->sysfs_lock); | 580 | lockdep_assert_held(&q->sysfs_lock); |
568 | 581 | ||
569 | if (q->elevator) { | 582 | if (q->elevator) { |
570 | if (q->elevator->registered) | 583 | if (q->elevator->registered) { |
584 | mutex_unlock(&q->sysfs_lock); | ||
585 | |||
586 | /* | ||
587 | * Concurrent elevator switch can't happen becasue | ||
588 | * sysfs write is always exclusively on same file. | ||
589 | * | ||
590 | * Also the elevator queue won't be freed after | ||
591 | * sysfs_lock is released becasue kobject_del() in | ||
592 | * blk_unregister_queue() waits for completion of | ||
593 | * .store & .show on its attributes. | ||
594 | */ | ||
571 | elv_unregister_queue(q); | 595 | elv_unregister_queue(q); |
596 | |||
597 | mutex_lock(&q->sysfs_lock); | ||
598 | } | ||
572 | ioc_clear_queue(q); | 599 | ioc_clear_queue(q); |
573 | elevator_exit(q, q->elevator); | 600 | elevator_exit(q, q->elevator); |
601 | |||
602 | /* | ||
603 | * sysfs_lock may be dropped, so re-check if queue is | ||
604 | * unregistered. If yes, don't switch to new elevator | ||
605 | * any more | ||
606 | */ | ||
607 | if (!blk_queue_registered(q)) | ||
608 | return 0; | ||
574 | } | 609 | } |
575 | 610 | ||
576 | ret = blk_mq_init_sched(q, new_e); | 611 | ret = blk_mq_init_sched(q, new_e); |
@@ -578,7 +613,11 @@ int elevator_switch_mq(struct request_queue *q, | |||
578 | goto out; | 613 | goto out; |
579 | 614 | ||
580 | if (new_e) { | 615 | if (new_e) { |
581 | ret = elv_register_queue(q); | 616 | mutex_unlock(&q->sysfs_lock); |
617 | |||
618 | ret = elv_register_queue(q, true); | ||
619 | |||
620 | mutex_lock(&q->sysfs_lock); | ||
582 | if (ret) { | 621 | if (ret) { |
583 | elevator_exit(q, q->elevator); | 622 | elevator_exit(q, q->elevator); |
584 | goto out; | 623 | goto out; |
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index d5077f3fdfd6..1ac790178787 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h | |||
@@ -535,6 +535,7 @@ struct request_queue { | |||
535 | struct delayed_work requeue_work; | 535 | struct delayed_work requeue_work; |
536 | 536 | ||
537 | struct mutex sysfs_lock; | 537 | struct mutex sysfs_lock; |
538 | struct mutex sysfs_dir_lock; | ||
538 | 539 | ||
539 | /* | 540 | /* |
540 | * for reusing dead hctx instance in case of updating | 541 | * for reusing dead hctx instance in case of updating |