diff options
| author | Benjamin Block <bblock@linux.vnet.ibm.com> | 2017-08-23 19:57:56 -0400 |
|---|---|---|
| committer | Jens Axboe <axboe@kernel.dk> | 2017-08-24 10:22:10 -0400 |
| commit | 50b4d485528d1dbe0bd249f2073140e3444f4a7b (patch) | |
| tree | 2060f67c0c03c8f87f37fe672c619389529c24a0 | |
| parent | 1e6ec9ea89d30739b9447c1860fcb07fc29f3aef (diff) | |
bsg-lib: fix kernel panic resulting from missing allocation of reply-buffer
Since we split the scsi_request out of struct request bsg fails to
provide a reply-buffer for the drivers. This was done via the pointer
for sense-data, that is not preallocated anymore.
Failing to allocate/assign it results in illegal dereferences because
LLDs use this pointer unquestioned.
An example panic on s390x, using the zFCP driver, looks like this (I had
debugging on, otherwise NULL-pointer dereferences wouldn't even panic on
s390x):
Unable to handle kernel pointer dereference in virtual kernel address space
Failing address: 6b6b6b6b6b6b6000 TEID: 6b6b6b6b6b6b6403
Fault in home space mode while using kernel ASCE.
AS:0000000001590007 R3:0000000000000024
Oops: 0038 ilc:2 [#1] PREEMPT SMP DEBUG_PAGEALLOC
Modules linked in: <Long List>
CPU: 2 PID: 0 Comm: swapper/2 Not tainted 4.12.0-bsg-regression+ #3
Hardware name: IBM 2964 N96 702 (z/VM 6.4.0)
task: 0000000065cb0100 task.stack: 0000000065cb4000
Krnl PSW : 0704e00180000000 000003ff801e4156 (zfcp_fc_ct_els_job_handler+0x16/0x58 [zfcp])
R:0 T:1 IO:1 EX:1 Key:0 M:1 W:0 P:0 AS:3 CC:2 PM:0 RI:0 EA:3
Krnl GPRS: 0000000000000001 000000005fa9d0d0 000000005fa9d078 0000000000e16866
000003ff00000290 6b6b6b6b6b6b6b6b 0000000059f78f00 000000000000000f
00000000593a0958 00000000593a0958 0000000060d88800 000000005ddd4c38
0000000058b50100 07000000659cba08 000003ff801e8556 00000000659cb9a8
Krnl Code: 000003ff801e4146: e31020500004 lg %r1,80(%r2)
000003ff801e414c: 58402040 l %r4,64(%r2)
#000003ff801e4150: e35020200004 lg %r5,32(%r2)
>000003ff801e4156: 50405004 st %r4,4(%r5)
000003ff801e415a: e54c50080000 mvhi 8(%r5),0
000003ff801e4160: e33010280012 lt %r3,40(%r1)
000003ff801e4166: a718fffb lhi %r1,-5
000003ff801e416a: 1803 lr %r0,%r3
Call Trace:
([<000003ff801e8556>] zfcp_fsf_req_complete+0x726/0x768 [zfcp])
[<000003ff801ea82a>] zfcp_fsf_reqid_check+0x102/0x180 [zfcp]
[<000003ff801eb980>] zfcp_qdio_int_resp+0x230/0x278 [zfcp]
[<00000000009b91b6>] qdio_kick_handler+0x2ae/0x2c8
[<00000000009b9e3e>] __tiqdio_inbound_processing+0x406/0xc10
[<00000000001684c2>] tasklet_action+0x15a/0x1d8
[<0000000000bd28ec>] __do_softirq+0x3ec/0x848
[<00000000001675a4>] irq_exit+0x74/0xf8
[<000000000010dd6a>] do_IRQ+0xba/0xf0
[<0000000000bd19e8>] io_int_handler+0x104/0x2d4
[<00000000001033b6>] enabled_wait+0xb6/0x188
([<000000000010339e>] enabled_wait+0x9e/0x188)
[<000000000010396a>] arch_cpu_idle+0x32/0x50
[<0000000000bd0112>] default_idle_call+0x52/0x68
[<00000000001cd0fa>] do_idle+0x102/0x188
[<00000000001cd41e>] cpu_startup_entry+0x3e/0x48
[<0000000000118c64>] smp_start_secondary+0x11c/0x130
[<0000000000bd2016>] restart_int_handler+0x62/0x78
[<0000000000000000>] (null)
INFO: lockdep is turned off.
Last Breaking-Event-Address:
[<000003ff801e41d6>] zfcp_fc_ct_job_handler+0x3e/0x48 [zfcp]
Kernel panic - not syncing: Fatal exception in interrupt
This patch moves bsg-lib to allocate and setup struct bsg_job ahead of
time, including the allocation of a buffer for the reply-data.
This means, struct bsg_job is not allocated separately anymore, but as part
of struct request allocation - similar to struct scsi_cmd. Reflect this in
the function names that used to handle creation/destruction of struct
bsg_job.
Reported-by: Steffen Maier <maier@linux.vnet.ibm.com>
Suggested-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Benjamin Block <bblock@linux.vnet.ibm.com>
Fixes: 82ed4db499b8 ("block: split scsi_request out of struct request")
Cc: <stable@vger.kernel.org> #4.11+
Signed-off-by: Jens Axboe <axboe@kernel.dk>
| -rw-r--r-- | block/bsg-lib.c | 74 | ||||
| -rw-r--r-- | include/linux/blkdev.h | 1 | ||||
| -rw-r--r-- | include/linux/bsg-lib.h | 2 |
3 files changed, 46 insertions, 31 deletions
diff --git a/block/bsg-lib.c b/block/bsg-lib.c index c4513b23f57a..dd56d7460cb9 100644 --- a/block/bsg-lib.c +++ b/block/bsg-lib.c | |||
| @@ -29,26 +29,25 @@ | |||
| 29 | #include <scsi/scsi_cmnd.h> | 29 | #include <scsi/scsi_cmnd.h> |
| 30 | 30 | ||
| 31 | /** | 31 | /** |
| 32 | * bsg_destroy_job - routine to teardown/delete a bsg job | 32 | * bsg_teardown_job - routine to teardown a bsg job |
| 33 | * @job: bsg_job that is to be torn down | 33 | * @job: bsg_job that is to be torn down |
| 34 | */ | 34 | */ |
| 35 | static void bsg_destroy_job(struct kref *kref) | 35 | static void bsg_teardown_job(struct kref *kref) |
| 36 | { | 36 | { |
| 37 | struct bsg_job *job = container_of(kref, struct bsg_job, kref); | 37 | struct bsg_job *job = container_of(kref, struct bsg_job, kref); |
| 38 | struct request *rq = job->req; | 38 | struct request *rq = job->req; |
| 39 | 39 | ||
| 40 | blk_end_request_all(rq, BLK_STS_OK); | ||
| 41 | |||
| 42 | put_device(job->dev); /* release reference for the request */ | 40 | put_device(job->dev); /* release reference for the request */ |
| 43 | 41 | ||
| 44 | kfree(job->request_payload.sg_list); | 42 | kfree(job->request_payload.sg_list); |
| 45 | kfree(job->reply_payload.sg_list); | 43 | kfree(job->reply_payload.sg_list); |
| 46 | kfree(job); | 44 | |
| 45 | blk_end_request_all(rq, BLK_STS_OK); | ||
| 47 | } | 46 | } |
| 48 | 47 | ||
| 49 | void bsg_job_put(struct bsg_job *job) | 48 | void bsg_job_put(struct bsg_job *job) |
| 50 | { | 49 | { |
| 51 | kref_put(&job->kref, bsg_destroy_job); | 50 | kref_put(&job->kref, bsg_teardown_job); |
| 52 | } | 51 | } |
| 53 | EXPORT_SYMBOL_GPL(bsg_job_put); | 52 | EXPORT_SYMBOL_GPL(bsg_job_put); |
| 54 | 53 | ||
| @@ -100,7 +99,7 @@ EXPORT_SYMBOL_GPL(bsg_job_done); | |||
| 100 | */ | 99 | */ |
| 101 | static void bsg_softirq_done(struct request *rq) | 100 | static void bsg_softirq_done(struct request *rq) |
| 102 | { | 101 | { |
| 103 | struct bsg_job *job = rq->special; | 102 | struct bsg_job *job = blk_mq_rq_to_pdu(rq); |
| 104 | 103 | ||
| 105 | bsg_job_put(job); | 104 | bsg_job_put(job); |
| 106 | } | 105 | } |
| @@ -122,33 +121,20 @@ static int bsg_map_buffer(struct bsg_buffer *buf, struct request *req) | |||
| 122 | } | 121 | } |
| 123 | 122 | ||
| 124 | /** | 123 | /** |
| 125 | * bsg_create_job - create the bsg_job structure for the bsg request | 124 | * bsg_prepare_job - create the bsg_job structure for the bsg request |
| 126 | * @dev: device that is being sent the bsg request | 125 | * @dev: device that is being sent the bsg request |
| 127 | * @req: BSG request that needs a job structure | 126 | * @req: BSG request that needs a job structure |
| 128 | */ | 127 | */ |
| 129 | static int bsg_create_job(struct device *dev, struct request *req) | 128 | static int bsg_prepare_job(struct device *dev, struct request *req) |
| 130 | { | 129 | { |
| 131 | struct request *rsp = req->next_rq; | 130 | struct request *rsp = req->next_rq; |
| 132 | struct request_queue *q = req->q; | ||
| 133 | struct scsi_request *rq = scsi_req(req); | 131 | struct scsi_request *rq = scsi_req(req); |
| 134 | struct bsg_job *job; | 132 | struct bsg_job *job = blk_mq_rq_to_pdu(req); |
| 135 | int ret; | 133 | int ret; |
| 136 | 134 | ||
| 137 | BUG_ON(req->special); | ||
| 138 | |||
| 139 | job = kzalloc(sizeof(struct bsg_job) + q->bsg_job_size, GFP_KERNEL); | ||
| 140 | if (!job) | ||
| 141 | return -ENOMEM; | ||
| 142 | |||
| 143 | req->special = job; | ||
| 144 | job->req = req; | ||
| 145 | if (q->bsg_job_size) | ||
| 146 | job->dd_data = (void *)&job[1]; | ||
| 147 | job->request = rq->cmd; | 135 | job->request = rq->cmd; |
| 148 | job->request_len = rq->cmd_len; | 136 | job->request_len = rq->cmd_len; |
| 149 | job->reply = rq->sense; | 137 | |
| 150 | job->reply_len = SCSI_SENSE_BUFFERSIZE; /* Size of sense buffer | ||
| 151 | * allocated */ | ||
| 152 | if (req->bio) { | 138 | if (req->bio) { |
| 153 | ret = bsg_map_buffer(&job->request_payload, req); | 139 | ret = bsg_map_buffer(&job->request_payload, req); |
| 154 | if (ret) | 140 | if (ret) |
| @@ -187,7 +173,6 @@ static void bsg_request_fn(struct request_queue *q) | |||
| 187 | { | 173 | { |
| 188 | struct device *dev = q->queuedata; | 174 | struct device *dev = q->queuedata; |
| 189 | struct request *req; | 175 | struct request *req; |
| 190 | struct bsg_job *job; | ||
| 191 | int ret; | 176 | int ret; |
| 192 | 177 | ||
| 193 | if (!get_device(dev)) | 178 | if (!get_device(dev)) |
| @@ -199,7 +184,7 @@ static void bsg_request_fn(struct request_queue *q) | |||
| 199 | break; | 184 | break; |
| 200 | spin_unlock_irq(q->queue_lock); | 185 | spin_unlock_irq(q->queue_lock); |
| 201 | 186 | ||
| 202 | ret = bsg_create_job(dev, req); | 187 | ret = bsg_prepare_job(dev, req); |
| 203 | if (ret) { | 188 | if (ret) { |
| 204 | scsi_req(req)->result = ret; | 189 | scsi_req(req)->result = ret; |
| 205 | blk_end_request_all(req, BLK_STS_OK); | 190 | blk_end_request_all(req, BLK_STS_OK); |
| @@ -207,8 +192,7 @@ static void bsg_request_fn(struct request_queue *q) | |||
| 207 | continue; | 192 | continue; |
| 208 | } | 193 | } |
| 209 | 194 | ||
| 210 | job = req->special; | 195 | ret = q->bsg_job_fn(blk_mq_rq_to_pdu(req)); |
| 211 | ret = q->bsg_job_fn(job); | ||
| 212 | spin_lock_irq(q->queue_lock); | 196 | spin_lock_irq(q->queue_lock); |
| 213 | if (ret) | 197 | if (ret) |
| 214 | break; | 198 | break; |
| @@ -219,6 +203,35 @@ static void bsg_request_fn(struct request_queue *q) | |||
| 219 | spin_lock_irq(q->queue_lock); | 203 | spin_lock_irq(q->queue_lock); |
| 220 | } | 204 | } |
| 221 | 205 | ||
| 206 | static int bsg_init_rq(struct request_queue *q, struct request *req, gfp_t gfp) | ||
| 207 | { | ||
| 208 | struct bsg_job *job = blk_mq_rq_to_pdu(req); | ||
| 209 | struct scsi_request *sreq = &job->sreq; | ||
| 210 | |||
| 211 | memset(job, 0, sizeof(*job)); | ||
| 212 | |||
| 213 | scsi_req_init(sreq); | ||
| 214 | sreq->sense_len = SCSI_SENSE_BUFFERSIZE; | ||
| 215 | sreq->sense = kzalloc(sreq->sense_len, gfp); | ||
| 216 | if (!sreq->sense) | ||
| 217 | return -ENOMEM; | ||
| 218 | |||
| 219 | job->req = req; | ||
| 220 | job->reply = sreq->sense; | ||
| 221 | job->reply_len = sreq->sense_len; | ||
| 222 | job->dd_data = job + 1; | ||
| 223 | |||
| 224 | return 0; | ||
| 225 | } | ||
| 226 | |||
| 227 | static void bsg_exit_rq(struct request_queue *q, struct request *req) | ||
| 228 | { | ||
| 229 | struct bsg_job *job = blk_mq_rq_to_pdu(req); | ||
| 230 | struct scsi_request *sreq = &job->sreq; | ||
| 231 | |||
| 232 | kfree(sreq->sense); | ||
| 233 | } | ||
| 234 | |||
| 222 | /** | 235 | /** |
| 223 | * bsg_setup_queue - Create and add the bsg hooks so we can receive requests | 236 | * bsg_setup_queue - Create and add the bsg hooks so we can receive requests |
| 224 | * @dev: device to attach bsg device to | 237 | * @dev: device to attach bsg device to |
| @@ -235,7 +248,9 @@ struct request_queue *bsg_setup_queue(struct device *dev, char *name, | |||
| 235 | q = blk_alloc_queue(GFP_KERNEL); | 248 | q = blk_alloc_queue(GFP_KERNEL); |
| 236 | if (!q) | 249 | if (!q) |
| 237 | return ERR_PTR(-ENOMEM); | 250 | return ERR_PTR(-ENOMEM); |
| 238 | q->cmd_size = sizeof(struct scsi_request); | 251 | q->cmd_size = sizeof(struct bsg_job) + dd_job_size; |
| 252 | q->init_rq_fn = bsg_init_rq; | ||
| 253 | q->exit_rq_fn = bsg_exit_rq; | ||
| 239 | q->request_fn = bsg_request_fn; | 254 | q->request_fn = bsg_request_fn; |
| 240 | 255 | ||
| 241 | ret = blk_init_allocated_queue(q); | 256 | ret = blk_init_allocated_queue(q); |
| @@ -243,7 +258,6 @@ struct request_queue *bsg_setup_queue(struct device *dev, char *name, | |||
| 243 | goto out_cleanup_queue; | 258 | goto out_cleanup_queue; |
| 244 | 259 | ||
| 245 | q->queuedata = dev; | 260 | q->queuedata = dev; |
| 246 | q->bsg_job_size = dd_job_size; | ||
| 247 | q->bsg_job_fn = job_fn; | 261 | q->bsg_job_fn = job_fn; |
| 248 | queue_flag_set_unlocked(QUEUE_FLAG_BIDI, q); | 262 | queue_flag_set_unlocked(QUEUE_FLAG_BIDI, q); |
| 249 | queue_flag_set_unlocked(QUEUE_FLAG_SCSI_PASSTHROUGH, q); | 263 | queue_flag_set_unlocked(QUEUE_FLAG_SCSI_PASSTHROUGH, q); |
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 25f6a0cb27d3..2a5d52fa90f5 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h | |||
| @@ -568,7 +568,6 @@ struct request_queue { | |||
| 568 | 568 | ||
| 569 | #if defined(CONFIG_BLK_DEV_BSG) | 569 | #if defined(CONFIG_BLK_DEV_BSG) |
| 570 | bsg_job_fn *bsg_job_fn; | 570 | bsg_job_fn *bsg_job_fn; |
| 571 | int bsg_job_size; | ||
| 572 | struct bsg_class_device bsg_dev; | 571 | struct bsg_class_device bsg_dev; |
| 573 | #endif | 572 | #endif |
| 574 | 573 | ||
diff --git a/include/linux/bsg-lib.h b/include/linux/bsg-lib.h index e34dde2da0ef..637a20cfb237 100644 --- a/include/linux/bsg-lib.h +++ b/include/linux/bsg-lib.h | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | #define _BLK_BSG_ | 24 | #define _BLK_BSG_ |
| 25 | 25 | ||
| 26 | #include <linux/blkdev.h> | 26 | #include <linux/blkdev.h> |
| 27 | #include <scsi/scsi_request.h> | ||
| 27 | 28 | ||
| 28 | struct request; | 29 | struct request; |
| 29 | struct device; | 30 | struct device; |
| @@ -37,6 +38,7 @@ struct bsg_buffer { | |||
| 37 | }; | 38 | }; |
| 38 | 39 | ||
| 39 | struct bsg_job { | 40 | struct bsg_job { |
| 41 | struct scsi_request sreq; | ||
| 40 | struct device *dev; | 42 | struct device *dev; |
| 41 | struct request *req; | 43 | struct request *req; |
| 42 | 44 | ||
