All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/3] Avoid that blk_{un,}register_queue() trigger lock inversion
@ 2018-01-17 19:48 Bart Van Assche
  2018-01-17 19:48 ` [PATCH v2 1/3] block: Unexport elv_register_queue() and elv_unregister_queue() Bart Van Assche
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: Bart Van Assche @ 2018-01-17 19:48 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-block, Christoph Hellwig, Bart Van Assche

Hello Jens,

The three patches in this series are what I came up with after having
analyzed a lockdep complaint triggered by blk_unregister_queue(). Please
consider these patches for kernel v4.16.

Thanks,

Bart.

Changes compared to v1:
- Restored the code that protects against races between changing the scheduler
  from sysfs and unregistering a block layer queue.

Bart Van Assche (3):
  block: Unexport elv_register_queue() and elv_unregister_queue()
  block: Document scheduler modification locking requirements
  block: Protect less code with sysfs_lock in blk_{un,}register_queue()

 block/blk-sysfs.c        | 37 ++++++++++++++++++++++++++++---------
 block/blk.h              |  3 +++
 block/elevator.c         | 10 ++++++++--
 include/linux/elevator.h |  2 --
 4 files changed, 39 insertions(+), 13 deletions(-)

-- 
2.15.1

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v2 1/3] block: Unexport elv_register_queue() and elv_unregister_queue()
  2018-01-17 19:48 [PATCH v2 0/3] Avoid that blk_{un,}register_queue() trigger lock inversion Bart Van Assche
@ 2018-01-17 19:48 ` Bart Van Assche
  2018-01-18 16:27   ` Christoph Hellwig
  2018-01-17 19:48 ` [PATCH v2 2/3] block: Document scheduler modification locking requirements Bart Van Assche
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 8+ messages in thread
From: Bart Van Assche @ 2018-01-17 19:48 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-block, Christoph Hellwig, Bart Van Assche

These two functions are only called from inside the block layer so
unexport them.

Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
---
 block/blk.h              | 3 +++
 block/elevator.c         | 2 --
 include/linux/elevator.h | 2 --
 3 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/block/blk.h b/block/blk.h
index 662005e46560..46db5dc83dcb 100644
--- a/block/blk.h
+++ b/block/blk.h
@@ -162,6 +162,9 @@ static inline void elv_deactivate_rq(struct request_queue *q, struct request *rq
 		e->type->ops.sq.elevator_deactivate_req_fn(q, rq);
 }
 
+int elv_register_queue(struct request_queue *q);
+void elv_unregister_queue(struct request_queue *q);
+
 struct hd_struct *__disk_get_part(struct gendisk *disk, int partno);
 
 #ifdef CONFIG_FAIL_IO_TIMEOUT
diff --git a/block/elevator.c b/block/elevator.c
index 138faeb08a7c..4f00b53cd5fd 100644
--- a/block/elevator.c
+++ b/block/elevator.c
@@ -886,7 +886,6 @@ int elv_register_queue(struct request_queue *q)
 	}
 	return error;
 }
-EXPORT_SYMBOL(elv_register_queue);
 
 void elv_unregister_queue(struct request_queue *q)
 {
@@ -900,7 +899,6 @@ void elv_unregister_queue(struct request_queue *q)
 		wbt_enable_default(q);
 	}
 }
-EXPORT_SYMBOL(elv_unregister_queue);
 
 int elv_register(struct elevator_type *e)
 {
diff --git a/include/linux/elevator.h b/include/linux/elevator.h
index 3d794b3dc532..6d9e230dffd2 100644
--- a/include/linux/elevator.h
+++ b/include/linux/elevator.h
@@ -198,8 +198,6 @@ extern bool elv_attempt_insert_merge(struct request_queue *, struct request *);
 extern void elv_requeue_request(struct request_queue *, struct request *);
 extern struct request *elv_former_request(struct request_queue *, struct request *);
 extern struct request *elv_latter_request(struct request_queue *, struct request *);
-extern int elv_register_queue(struct request_queue *q);
-extern void elv_unregister_queue(struct request_queue *q);
 extern int elv_may_queue(struct request_queue *, unsigned int);
 extern void elv_completed_request(struct request_queue *, struct request *);
 extern int elv_set_request(struct request_queue *q, struct request *rq,
-- 
2.15.1

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v2 2/3] block: Document scheduler modification locking requirements
  2018-01-17 19:48 [PATCH v2 0/3] Avoid that blk_{un,}register_queue() trigger lock inversion Bart Van Assche
  2018-01-17 19:48 ` [PATCH v2 1/3] block: Unexport elv_register_queue() and elv_unregister_queue() Bart Van Assche
@ 2018-01-17 19:48 ` Bart Van Assche
  2018-01-18 16:27   ` Christoph Hellwig
  2018-01-17 19:48 ` [PATCH v2 3/3] block: Protect less code with sysfs_lock in blk_{un,}register_queue() Bart Van Assche
  2018-01-18 19:55 ` [PATCH v2 0/3] Avoid that blk_{un,}register_queue() trigger lock inversion Jens Axboe
  3 siblings, 1 reply; 8+ messages in thread
From: Bart Van Assche @ 2018-01-17 19:48 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-block, Christoph Hellwig, Bart Van Assche

This patch does not change any functionality.

Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
---
 block/elevator.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/block/elevator.c b/block/elevator.c
index 4f00b53cd5fd..e87e9b43aba0 100644
--- a/block/elevator.c
+++ b/block/elevator.c
@@ -869,6 +869,8 @@ int elv_register_queue(struct request_queue *q)
 	struct elevator_queue *e = q->elevator;
 	int error;
 
+	lockdep_assert_held(&q->sysfs_lock);
+
 	error = kobject_add(&e->kobj, &q->kobj, "%s", "iosched");
 	if (!error) {
 		struct elv_fs_entry *attr = e->type->elevator_attrs;
@@ -889,6 +891,8 @@ int elv_register_queue(struct request_queue *q)
 
 void elv_unregister_queue(struct request_queue *q)
 {
+	lockdep_assert_held(&q->sysfs_lock);
+
 	if (q) {
 		struct elevator_queue *e = q->elevator;
 
@@ -965,6 +969,8 @@ static int elevator_switch_mq(struct request_queue *q,
 {
 	int ret;
 
+	lockdep_assert_held(&q->sysfs_lock);
+
 	blk_mq_freeze_queue(q);
 	blk_mq_quiesce_queue(q);
 
@@ -1010,6 +1016,8 @@ static int elevator_switch(struct request_queue *q, struct elevator_type *new_e)
 	bool old_registered = false;
 	int err;
 
+	lockdep_assert_held(&q->sysfs_lock);
+
 	if (q->mq_ops)
 		return elevator_switch_mq(q, new_e);
 
-- 
2.15.1

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v2 3/3] block: Protect less code with sysfs_lock in blk_{un,}register_queue()
  2018-01-17 19:48 [PATCH v2 0/3] Avoid that blk_{un,}register_queue() trigger lock inversion Bart Van Assche
  2018-01-17 19:48 ` [PATCH v2 1/3] block: Unexport elv_register_queue() and elv_unregister_queue() Bart Van Assche
  2018-01-17 19:48 ` [PATCH v2 2/3] block: Document scheduler modification locking requirements Bart Van Assche
@ 2018-01-17 19:48 ` Bart Van Assche
  2018-01-18 16:28   ` Christoph Hellwig
  2018-01-18 19:55 ` [PATCH v2 0/3] Avoid that blk_{un,}register_queue() trigger lock inversion Jens Axboe
  3 siblings, 1 reply; 8+ messages in thread
From: Bart Van Assche @ 2018-01-17 19:48 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-block, Christoph Hellwig, Bart Van Assche

The __blk_mq_register_dev(), blk_mq_unregister_dev(),
elv_register_queue() and elv_unregister_queue() calls need to be
protected with sysfs_lock but other code in these functions not.
Hence protect only this code with sysfs_lock. This patch fixes a
locking inversion issue in blk_unregister_queue() and also in an
error path of blk_register_queue(): it is not allowed to hold
sysfs_lock around the kobject_del(&q->kobj) call.

Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
---
 block/blk-sysfs.c | 37 ++++++++++++++++++++++++++++---------
 1 file changed, 28 insertions(+), 9 deletions(-)

diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c
index 4a6a40ffd78e..cbea895a5547 100644
--- a/block/blk-sysfs.c
+++ b/block/blk-sysfs.c
@@ -853,6 +853,10 @@ struct kobj_type blk_queue_ktype = {
 	.release	= blk_release_queue,
 };
 
+/**
+ * blk_register_queue - register a block layer queue with sysfs
+ * @disk: Disk of which the request queue should be registered with sysfs.
+ */
 int blk_register_queue(struct gendisk *disk)
 {
 	int ret;
@@ -909,11 +913,12 @@ int blk_register_queue(struct gendisk *disk)
 	if (q->request_fn || (q->mq_ops && q->elevator)) {
 		ret = elv_register_queue(q);
 		if (ret) {
+			mutex_unlock(&q->sysfs_lock);
 			kobject_uevent(&q->kobj, KOBJ_REMOVE);
 			kobject_del(&q->kobj);
 			blk_trace_remove_sysfs(dev);
 			kobject_put(&dev->kobj);
-			goto unlock;
+			return ret;
 		}
 	}
 	ret = 0;
@@ -923,6 +928,13 @@ int blk_register_queue(struct gendisk *disk)
 }
 EXPORT_SYMBOL_GPL(blk_register_queue);
 
+/**
+ * blk_unregister_queue - counterpart of blk_register_queue()
+ * @disk: Disk of which the request queue should be unregistered from sysfs.
+ *
+ * Note: the caller is responsible for guaranteeing that this function is called
+ * after blk_register_queue() has finished.
+ */
 void blk_unregister_queue(struct gendisk *disk)
 {
 	struct request_queue *q = disk->queue;
@@ -935,8 +947,9 @@ void blk_unregister_queue(struct gendisk *disk)
 		return;
 
 	/*
-	 * Protect against the 'queue' kobj being accessed
-	 * while/after it is removed.
+	 * Since sysfs_remove_dir() prevents adding new directory entries
+	 * before removal of existing entries starts, protect against
+	 * concurrent elv_iosched_store() calls.
 	 */
 	mutex_lock(&q->sysfs_lock);
 
@@ -944,18 +957,24 @@ void blk_unregister_queue(struct gendisk *disk)
 	queue_flag_clear(QUEUE_FLAG_REGISTERED, q);
 	spin_unlock_irq(q->queue_lock);
 
-	wbt_exit(q);
-
+	/*
+	 * Remove the sysfs attributes before unregistering the queue data
+	 * structures that can be modified through sysfs.
+	 */
 	if (q->mq_ops)
 		blk_mq_unregister_dev(disk_to_dev(disk), q);
-
-	if (q->request_fn || (q->mq_ops && q->elevator))
-		elv_unregister_queue(q);
+	mutex_unlock(&q->sysfs_lock);
 
 	kobject_uevent(&q->kobj, KOBJ_REMOVE);
 	kobject_del(&q->kobj);
 	blk_trace_remove_sysfs(disk_to_dev(disk));
-	kobject_put(&disk_to_dev(disk)->kobj);
 
+	wbt_exit(q);
+
+	mutex_lock(&q->sysfs_lock);
+	if (q->request_fn || (q->mq_ops && q->elevator))
+		elv_unregister_queue(q);
 	mutex_unlock(&q->sysfs_lock);
+
+	kobject_put(&disk_to_dev(disk)->kobj);
 }
-- 
2.15.1

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 1/3] block: Unexport elv_register_queue() and elv_unregister_queue()
  2018-01-17 19:48 ` [PATCH v2 1/3] block: Unexport elv_register_queue() and elv_unregister_queue() Bart Van Assche
@ 2018-01-18 16:27   ` Christoph Hellwig
  0 siblings, 0 replies; 8+ messages in thread
From: Christoph Hellwig @ 2018-01-18 16:27 UTC (permalink / raw)
  To: Bart Van Assche; +Cc: Jens Axboe, linux-block, Christoph Hellwig

Looks fine,

Reviewed-by: Christoph Hellwig <hch@lst.de>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 2/3] block: Document scheduler modification locking requirements
  2018-01-17 19:48 ` [PATCH v2 2/3] block: Document scheduler modification locking requirements Bart Van Assche
@ 2018-01-18 16:27   ` Christoph Hellwig
  0 siblings, 0 replies; 8+ messages in thread
From: Christoph Hellwig @ 2018-01-18 16:27 UTC (permalink / raw)
  To: Bart Van Assche; +Cc: Jens Axboe, linux-block, Christoph Hellwig

Looks fine,

Reviewed-by: Christoph Hellwig <hch@lst.de>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 3/3] block: Protect less code with sysfs_lock in blk_{un,}register_queue()
  2018-01-17 19:48 ` [PATCH v2 3/3] block: Protect less code with sysfs_lock in blk_{un,}register_queue() Bart Van Assche
@ 2018-01-18 16:28   ` Christoph Hellwig
  0 siblings, 0 replies; 8+ messages in thread
From: Christoph Hellwig @ 2018-01-18 16:28 UTC (permalink / raw)
  To: Bart Van Assche; +Cc: Jens Axboe, linux-block, Christoph Hellwig

Looks good,

Reviewed-by: Christoph Hellwig <hch@lst.de>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 0/3] Avoid that blk_{un,}register_queue() trigger lock inversion
  2018-01-17 19:48 [PATCH v2 0/3] Avoid that blk_{un,}register_queue() trigger lock inversion Bart Van Assche
                   ` (2 preceding siblings ...)
  2018-01-17 19:48 ` [PATCH v2 3/3] block: Protect less code with sysfs_lock in blk_{un,}register_queue() Bart Van Assche
@ 2018-01-18 19:55 ` Jens Axboe
  3 siblings, 0 replies; 8+ messages in thread
From: Jens Axboe @ 2018-01-18 19:55 UTC (permalink / raw)
  To: Bart Van Assche; +Cc: linux-block, Christoph Hellwig

On 1/17/18 12:48 PM, Bart Van Assche wrote:
> Hello Jens,
> 
> The three patches in this series are what I came up with after having
> analyzed a lockdep complaint triggered by blk_unregister_queue(). Please
> consider these patches for kernel v4.16.

Thanks Bart, applied.

-- 
Jens Axboe

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2018-01-18 19:55 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-01-17 19:48 [PATCH v2 0/3] Avoid that blk_{un,}register_queue() trigger lock inversion Bart Van Assche
2018-01-17 19:48 ` [PATCH v2 1/3] block: Unexport elv_register_queue() and elv_unregister_queue() Bart Van Assche
2018-01-18 16:27   ` Christoph Hellwig
2018-01-17 19:48 ` [PATCH v2 2/3] block: Document scheduler modification locking requirements Bart Van Assche
2018-01-18 16:27   ` Christoph Hellwig
2018-01-17 19:48 ` [PATCH v2 3/3] block: Protect less code with sysfs_lock in blk_{un,}register_queue() Bart Van Assche
2018-01-18 16:28   ` Christoph Hellwig
2018-01-18 19:55 ` [PATCH v2 0/3] Avoid that blk_{un,}register_queue() trigger lock inversion Jens Axboe

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.