From 1a809721fcb21452dd22cbdde3910390fba079e0 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Sat, 19 Apr 2014 11:52:34 +0000 Subject: [PATCH] vdisk_blockio: Make COMPARE AND WRITE compatible with the scsi_debug driver This patch fixes the following kernel oops: BUG: unable to handle kernel paging request at ffffeae380000690 Call Trace: [] sg_miter_next+0x9/0xd0 [] sg_copy_buffer+0xa0/0x100 [] do_device_access.isra.8+0xa6/0x150 [scsi_debug] [] resp_read+0xe4/0x240 [scsi_debug] [] scsi_debug_queuecommand_lck+0x11e5/0x2060 [scsi_debug] [] scsi_debug_queuecommand+0x30/0x48 [scsi_debug] [] scsi_dispatch_cmd+0xaf/0x260 [] scsi_request_fn+0x32d/0x540 [] __blk_run_queue+0x2a/0x40 [] blk_queue_bio+0x274/0x350 [] generic_make_request+0xa8/0xf0 [] submit_bio+0x6c/0x140 [] blockio_rw_sync.isra.29+0x106/0x170 [scst_vdisk] [] vdisk_exec_caw+0xd9/0x3c0 [scst_vdisk] [] vdev_do_job+0x9e/0x320 [scst_vdisk] [] non_fileio_exec+0x57/0xd0 [scst_vdisk] [] scst_do_real_exec+0x92/0x3b0 [scst] [] scst_exec_check_blocking+0xe2/0x300 [scst] [] scst_exec_check_sn+0x17b/0x2d0 [scst] [] scst_process_active_cmd+0x431/0x770 [scst] [] scst_do_job_active+0xea/0x180 [scst] [] scst_cmd_thread+0x126/0x290 [scst] [] kthread+0xc1/0xe0 [] ret_from_fork+0x7c/0xb0 Reported-by: Sebastian Herbszt git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@5449 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scst/src/dev_handlers/scst_vdisk.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index b432c7118..58ae8a9f9 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -4870,8 +4870,10 @@ static ssize_t blockio_rw_sync(struct scst_vdisk_dev *virt_dev, void *buf, { DECLARE_COMPLETION_ONSTACK(c); struct block_device *bdev = virt_dev->bdev; + const bool is_vmalloc = is_vmalloc_addr(buf); struct bio *bio; void *p; + struct page *q; int max_nr_vecs, rc; unsigned bytes, off; ssize_t ret = -ENOMEM; @@ -4898,8 +4900,9 @@ static ssize_t blockio_rw_sync(struct scst_vdisk_dev *virt_dev, void *buf, #endif for (p = buf; p < buf + len; p += bytes) { off = offset_in_page(p); - bytes = PAGE_SIZE - off; - rc = bio_add_page(bio, virt_to_page(p), bytes, off); + bytes = min_t(size_t, PAGE_SIZE - off, buf + len - p); + q = is_vmalloc ? vmalloc_to_page(p) : virt_to_page(p); + rc = bio_add_page(bio, q, bytes, off); if (WARN_ON_ONCE(rc < bytes)) goto free; }