From: Nicholas A. Bellinger on
From: Nicholas Bellinger <nab(a)linux-iscsi.org>

Greetings Jens and co,

This patch adds the missing support to block/bsg.c:bsg_map_hdr() to accept
struct sg_io_v4->d[out,in]_iovec_count and process struct sg_io_v4->d[out,in]_xferp memory
containing userspace iovecs for kernel level BSG. It adds a new wrapper bsg_rq_map_user_iov()
that will call copy_from_user() and blk_rq_map_user_iov() following the original SG_IO logic in
drivers/scsi/sg.c:sg_start_req().

So far this has been tested on a x86_64 v2.6.34 KVM Host with TCM_Loop Virtual SAS Port/LUNs
into a x86_64 v2.6.26 KVM Guest with Megasas 8707EM2 HBA Emulation + my new scsi-bsg backstore code.

Please consider this for v2.6.36 as it will be required in order for QEMU-KVM MegaSAS and VirtIO HBA
emulation using QEMU scatterlist memory and BSG backstores.

Signed-off-by: Nicholas A. Bellinger <nab(a)linux-iscsi.org>
---
block/bsg.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++--------
1 files changed, 47 insertions(+), 8 deletions(-)

diff --git a/block/bsg.c b/block/bsg.c
index 82d5882..3f789cf 100644
--- a/block/bsg.c
+++ b/block/bsg.c
@@ -239,6 +239,33 @@ bsg_validate_sgv4_hdr(struct request_queue *q, struct sg_io_v4 *hdr, int *rw)
return ret;
}

+static int
+bsg_rq_map_user_iov(struct request_queue *q, struct request *rq, void *dxferp,
+ unsigned int dxfer_len, int iovec_count, int gfp)
+{
+ struct iovec *iov;
+ int len, ret, size = sizeof(struct sg_iovec) * iovec_count;
+
+ iov = kzalloc(size, GFP_ATOMIC);
+ if (!iov)
+ return -ENOMEM;
+
+ if (copy_from_user(iov, dxferp, size)) {
+ kfree(iov);
+ return -EFAULT;
+ }
+
+ len = iov_length(iov, iovec_count);
+ if (dxfer_len < len) {
+ iovec_count = iov_shorten(iov, iovec_count, dxfer_len);
+ len = dxfer_len;
+ }
+ ret = blk_rq_map_user_iov(q, rq, NULL, dxferp, iovec_count,
+ len, gfp);
+ kfree(iov);
+ return ret;
+}
+
/*
* map sg_io_v4 to a request.
*/
@@ -248,7 +275,7 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr, fmode_t has_write_perm,
{
struct request_queue *q = bd->queue;
struct request *rq, *next_rq = NULL;
- int ret, rw;
+ int ret, rw, iovec_count;
unsigned int dxfer_len;
void *dxferp = NULL;

@@ -284,28 +311,40 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr, fmode_t has_write_perm,
rq->next_rq = next_rq;
next_rq->cmd_type = rq->cmd_type;

+ iovec_count = hdr->din_iovec_count;
dxferp = (void*)(unsigned long)hdr->din_xferp;
- ret = blk_rq_map_user(q, next_rq, NULL, dxferp,
- hdr->din_xfer_len, GFP_KERNEL);
+
+ if (iovec_count)
+ ret = bsg_rq_map_user_iov(q, next_rq, dxferp,
+ hdr->din_xfer_len, iovec_count,
+ GFP_KERNEL);
+ else
+ ret = blk_rq_map_user(q, next_rq, NULL, dxferp,
+ hdr->din_xfer_len, GFP_KERNEL);
if (ret)
goto out;
}

if (hdr->dout_xfer_len) {
dxfer_len = hdr->dout_xfer_len;
+ iovec_count = hdr->dout_iovec_count;
dxferp = (void*)(unsigned long)hdr->dout_xferp;
} else if (hdr->din_xfer_len) {
dxfer_len = hdr->din_xfer_len;
+ iovec_count = hdr->din_iovec_count;
dxferp = (void*)(unsigned long)hdr->din_xferp;
} else
dxfer_len = 0;

- if (dxfer_len) {
+ if (iovec_count && dxfer_len)
+ ret = bsg_rq_map_user_iov(q, rq, dxferp, dxfer_len,
+ iovec_count, GFP_KERNEL);
+ else if (dxfer_len)
ret = blk_rq_map_user(q, rq, NULL, dxferp, dxfer_len,
- GFP_KERNEL);
- if (ret)
- goto out;
- }
+ GFP_KERNEL);
+
+ if (ret)
+ goto out;

rq->sense = sense;
rq->sense_len = 0;
--
1.5.6.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/