]> nv-tegra.nvidia Code Review - linux-4.9.git/commitdiff
blk-mq: flush pending hctx->run_work before freeing hctx
authorManikanta Maddireddy <mmaddireddy@nvidia.com>
Sun, 14 Oct 2018 17:54:29 +0000 (10:54 -0700)
committermobile promotions <svcmobile_promotions@nvidia.com>
Sat, 20 Oct 2018 06:34:40 +0000 (23:34 -0700)
During resume block driver starts the queues and add hctx->run_work to wq
to process the pending request in hardware queue. In the same sequence
block driver frees the hw queues if respective mapped CPU is offline.
If the hctx->run_work schedules afterwards, kernel crashes because hctx
is freed. To fix the issue flush hctx->run_work before freeing hctx.

Unable to handle kernel NULL pointer dereference at virtual address 00000000
Mem abort info:
  ESR = 0x96000005
  Exception class = DABT (current EL), IL = 32 bits
  SET = 0, FnV = 0
  EA = 0, S1PTW = 0
Data abort info:
  ISV = 0, ISS = 0x00000005
  CM = 0, WnR = 0
user pgtable: 4k pages, 39-bit VAs, pgd = ffffffc364390000
[0000000000000000] *pgd=0000000000000000, *pud=0000000000000000
Internal error: Oops: 96000005 [#1] PREEMPT SMP
CPU: 1 PID: 49 Comm: kworker/6:0H Tainted: G         C      4.9.132-tegra-g8cc25a4-dirty #9
Hardware name: galen (DT)
Workqueue: kblockd blk_mq_run_work_fn
task: ffffffc3ecc08d40 task.stack: ffffffc3ecc14000
PC is at __blk_mq_run_hw_queue+0xfc/0x858
LR is at __blk_mq_run_hw_queue+0x618/0x858
pc : [<ffffff80087b565c>] lr : [<ffffff80087b5b78>] pstate: 80c00045
sp : ffffffc3ecc17c00
x29: ffffffc3ecc17c00 x28: ffffffc3ecc17cc8
x27: 0000000000000000 x26: 0000000000000000
x25: 0000000000000000 x24: ffffffc3d4088000
x23: 0000000000000000 x22: ffffffc3ecc17cb8
x21: ffffffc3d3843090 x20: ffffffc3d3843000
x19: 0000000000000000 x18: ffffff800a7d7450
x17: 0000000000000000 x16: 0000000000000002
x15: ffffff800a7d7468 x14: 3d3d3d3d3d3d3d3d
x13: 3d3d3d3d3d3d3d3d x12: 3d3d3d3d3d3d3d3d
x11: 3d3d3d3d3d3d3d3d x10: 3d3d3d3d3d3d3d3d
x9 : 3d3d3d3d3d3d3d3d x8 : ffffffc3ffc53838
x7 : 0000000000000000 x6 : ffffffc3ffc53838
x5 : 0000000000000000 x4 : 0000000000000001
x3 : 0000000000000050 x2 : 0000000000000000
x1 : 0000000000040984 x0 : 0000000000000000

Process kworker/6:0H (pid: 49, stack limit = 0xffffffc3ecc14028)
Call trace:
[<ffffff80087b565c>] __blk_mq_run_hw_queue+0xfc/0x858
[<ffffff80087b5e18>] blk_mq_run_work_fn+0x20/0x30
[<ffffff8008105398>] process_one_work+0x1d8/0xb30
[<ffffff8008105e78>] worker_thread+0x188/0x790
[<ffffff800811119c>] kthread+0x12c/0x200
[<ffffff80080837d0>] ret_from_fork+0x10/0x40

bug 200456501

Change-Id: Iecf16bb18daa514bb6700080e3ff7951a9c5126a
Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/1926876
GVS: Gerrit_Virtual_Submit
Reviewed-by: Vidya Sagar <vidyas@nvidia.com>
Reviewed-by: Ashutosh Jha <ajha@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
block/blk-mq.c

index a4b0057f7f0c6f9ac148bc49868f9d49eb4c2ff3..d9c9b4d8bb3fa863d2f4aa3e6cec11aab0e316d7 100644 (file)
@@ -942,12 +942,22 @@ void blk_mq_run_hw_queues(struct request_queue *q, bool async)
 }
 EXPORT_SYMBOL(blk_mq_run_hw_queues);
 
-void blk_mq_stop_hw_queue(struct blk_mq_hw_ctx *hctx)
+static void __blk_mq_stop_hw_queue(struct blk_mq_hw_ctx *hctx, bool sync)
 {
-       cancel_work(&hctx->run_work);
-       cancel_delayed_work(&hctx->delay_work);
+       if (sync) {
+               cancel_work_sync(&hctx->run_work);
+               cancel_delayed_work_sync(&hctx->delay_work);
+       } else {
+               cancel_work(&hctx->run_work);
+               cancel_delayed_work(&hctx->delay_work);
+       }
        set_bit(BLK_MQ_S_STOPPED, &hctx->state);
 }
+
+void blk_mq_stop_hw_queue(struct blk_mq_hw_ctx *hctx)
+{
+       __blk_mq_stop_hw_queue(hctx, false);
+}
 EXPORT_SYMBOL(blk_mq_stop_hw_queue);
 
 void blk_mq_stop_hw_queues(struct request_queue *q)
@@ -1951,6 +1961,7 @@ static void blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set,
                struct blk_mq_hw_ctx *hctx = hctxs[j];
 
                if (hctx) {
+                       __blk_mq_stop_hw_queue(hctx, true);
                        if (hctx->tags) {
                                blk_mq_free_rq_map(set, hctx->tags, j);
                                set->tags[j] = NULL;