[dm-devel] [PATCH] fix crash in blk_queue_abort

Mikulas Patocka mpatocka at redhat.com
Thu Apr 16 23:52:17 UTC 2009


Hi

This fixes a crash in blk_abort_queue. The crash can be triggered with 
device mapper multipath.

I believe that since there is method make_request_fn, the cleanest 
solution is to add another method, abort_queue_fn. But you can use 
different solution if you want (like testing some bit whether the device 
is request-based ... or so).

Mikulas

---

Fix a crash due to blk_abort_queue being called on non-request device.

The crash can be reproduced in the following way:

# dmsetup create alias1 --table "0 `blockdev --getsize /dev/sda` linear /dev/sda 0"
# dmsetup create alias2 --table "0 `blockdev --getsize /dev/sda` linear /dev/sda 0"
# dmsetup create mpath --table "0 `blockdev --getsize /dev/sda` multipath 0 0 2 1 round-robin 0 1 0 /dev/mapper/alias1 round-robin 0 1 0 /dev/mapper/alias2"
# dmsetup reload alias1 --table "0 `blockdev --getsize /dev/sda` error"
# dmsetup suspend alias1
# dmsetup resume alias1
# less -f /dev/mapper/mpath

TPC: <__lock_acquire+0x5c/0x1c00>
Caller[000000000047f468]: lock_acquire+0xa8/0xc0
Caller[000000000065a978]: _spin_lock_irqsave+0x38/0x60
Caller[000000000054f7b4]: blk_abort_queue+0x34/0x140
Caller[00000000100ffe04]: deactivate_path+0x44/0x60 [dm_multipath]
Caller[0000000000468898]: worker_thread+0x1d8/0x2e0
Caller[000000000046d4ac]: kthread+0x4c/0x80
Caller[000000000042bc1c]: kernel_thread+0x3c/0x60
Caller[000000000046d3e4]: kthreadd+0x104/0x180

The crash happens because queue spinlock pointer is NULL and blk_abort_queue is
called.

The problem is that blk_abort_queue assumes that the underlying device is
request-based. If it uses bios, not requests, it accesses uninitialized data
structures and crashes.

This patch changes it to provide a method, abort_queue_fn, that will
abort the queue. On request-based devices, it points to generic_abort_queue,
on non-request based devices it can be NULL (no abort) or the driver can
register its own abort function there.

Signed-off-by: Mikulas Patocka <mpatocka at redhat.com>

---
 block/blk-core.c       |    1 +
 block/blk-timeout.c    |   10 ++++++++--
 include/linux/blkdev.h |    3 +++
 3 files changed, 12 insertions(+), 2 deletions(-)

Index: linux-2.6.30-rc2-devel/block/blk-core.c
===================================================================
--- linux-2.6.30-rc2-devel.orig/block/blk-core.c	2009-04-16 22:20:59.000000000 +0200
+++ linux-2.6.30-rc2-devel/block/blk-core.c	2009-04-16 23:44:25.000000000 +0200
@@ -598,6 +598,7 @@ blk_init_queue_node(request_fn_proc *rfn
 		lock = &q->__queue_lock;
 
 	q->request_fn		= rfn;
+	q->abort_queue_fn	= generic_abort_queue;
 	q->prep_rq_fn		= NULL;
 	q->unplug_fn		= generic_unplug_device;
 	q->queue_flags		= QUEUE_FLAG_DEFAULT;
Index: linux-2.6.30-rc2-devel/block/blk-timeout.c
===================================================================
--- linux-2.6.30-rc2-devel.orig/block/blk-timeout.c	2009-04-16 22:30:27.000000000 +0200
+++ linux-2.6.30-rc2-devel/block/blk-timeout.c	2009-04-16 23:46:28.000000000 +0200
@@ -205,7 +205,7 @@ void blk_add_timer(struct request *req)
  * @queue:	pointer to queue
  *
  */
-void blk_abort_queue(struct request_queue *q)
+void generic_abort_queue(struct request_queue *q)
 {
 	unsigned long flags;
 	struct request *rq, *tmp;
@@ -227,4 +227,10 @@ void blk_abort_queue(struct request_queu
 	spin_unlock_irqrestore(q->queue_lock, flags);
 
 }
-EXPORT_SYMBOL_GPL(blk_abort_queue);
+
+void blk_abort_queue(struct request_queue *q)
+{
+	if (q->abort_queue_fn)
+		q->abort_queue_fn(q);
+}
+EXPORT_SYMBOL(blk_abort_queue);
Index: linux-2.6.30-rc2-devel/include/linux/blkdev.h
===================================================================
--- linux-2.6.30-rc2-devel.orig/include/linux/blkdev.h	2009-04-16 22:20:43.000000000 +0200
+++ linux-2.6.30-rc2-devel/include/linux/blkdev.h	2009-04-16 22:32:27.000000000 +0200
@@ -267,6 +267,7 @@ struct request_pm_state
 
 typedef void (request_fn_proc) (struct request_queue *q);
 typedef int (make_request_fn) (struct request_queue *q, struct bio *bio);
+typedef void (abort_queue_fn) (struct request_queue *q);
 typedef int (prep_rq_fn) (struct request_queue *, struct request *);
 typedef void (unplug_fn) (struct request_queue *);
 typedef int (prepare_discard_fn) (struct request_queue *, struct request *);
@@ -332,6 +333,7 @@ struct request_queue
 
 	request_fn_proc		*request_fn;
 	make_request_fn		*make_request_fn;
+	abort_queue_fn		*abort_queue_fn;
 	prep_rq_fn		*prep_rq_fn;
 	unplug_fn		*unplug_fn;
 	prepare_discard_fn	*prepare_discard_fn;
@@ -903,6 +905,7 @@ extern bool blk_ordered_complete_seq(str
 extern int blk_rq_map_sg(struct request_queue *, struct request *, struct scatterlist *);
 extern void blk_dump_rq_flags(struct request *, char *);
 extern void generic_unplug_device(struct request_queue *);
+extern void generic_abort_queue(struct request_queue *);
 extern long nr_blockdev_pages(void);
 
 int blk_get_queue(struct request_queue *);




More information about the dm-devel mailing list