[PATCH] bsg: port to bidi

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Changes required to make bsg patches work on top of bidi patches.  Adds
capability to bsg to handle bidirectional commands and extended CDBs.

Signed-off-by: Pete Wyckoff <pw@xxxxxxx>
---
 block/bsg.c            |  106 ++++++++++++++++++++++++++++++------------------
 block/ll_rw_blk.c      |   30 +++++++++-----
 include/linux/blkdev.h |    1 +
 3 files changed, 87 insertions(+), 50 deletions(-)

diff --git a/block/bsg.c b/block/bsg.c
index 92be6fa..9d09505 100644
--- a/block/bsg.c
+++ b/block/bsg.c
@@ -95,6 +95,7 @@ struct bsg_command {
 	struct list_head list;
 	struct request *rq;
 	struct bio *bio;
+	struct bio *bidi_read_bio;
 	int err;
 	struct sg_io_v4 hdr;
 	struct sg_io_v4 __user *uhdr;
@@ -225,18 +226,31 @@ static struct bsg_command *bsg_get_command(struct bsg_device *bd)
 static int blk_fill_sgv4_hdr_rq(request_queue_t *q, struct request *rq,
 				struct sg_io_v4 *hdr, int has_write_perm)
 {
+	int len = hdr->request_len;
+	int cmd_len = min(len, BLK_MAX_CDB);
+
 	memset(rq->cmd, 0, BLK_MAX_CDB); /* ATAPI hates garbage after CDB */
 
 	if (copy_from_user(rq->cmd, (void *)(unsigned long)hdr->request,
-			   hdr->request_len))
+	                   cmd_len))
 		return -EFAULT;
+	if (len > BLK_MAX_CDB) {
+		rq->varlen_cdb_len = len;
+		rq->varlen_cdb = kmalloc(len, GFP_KERNEL);
+		if (rq->varlen_cdb == NULL)
+			return -ENOMEM;
+		if (copy_from_user(rq->varlen_cdb,
+		                   (void *)(unsigned long)hdr->request, len))
+			return -EFAULT;
+	}
+
 	if (blk_verify_command(rq->cmd, has_write_perm))
 		return -EPERM;
 
 	/*
 	 * fill in request structure
 	 */
-	rq->cmd_len = hdr->request_len;
+	rq->cmd_len = cmd_len;
 	rq->cmd_type = REQ_TYPE_BLOCK_PC;
 
 	rq->timeout = (hdr->timeout * HZ) / 1000;
@@ -252,12 +266,11 @@ static int blk_fill_sgv4_hdr_rq(request_queue_t *q, struct request *rq,
  * Check if sg_io_v4 from user is allowed and valid
  */
 static int
-bsg_validate_sgv4_hdr(request_queue_t *q, struct sg_io_v4 *hdr, int *rw)
+bsg_validate_sgv4_hdr(request_queue_t *q, struct sg_io_v4 *hdr,
+                      enum dma_data_direction *dir)
 {
 	if (hdr->guard != 'Q')
 		return -EINVAL;
-	if (hdr->request_len > BLK_MAX_CDB)
-		return -EINVAL;
 	if (hdr->dout_xfer_len > (q->max_sectors << 9) ||
 	    hdr->din_xfer_len > (q->max_sectors << 9))
 		return -EIO;
@@ -266,17 +279,15 @@ bsg_validate_sgv4_hdr(request_queue_t *q, struct sg_io_v4 *hdr, int *rw)
 	if (hdr->protocol || hdr->subprotocol)
 		return -EINVAL;
 
-	/*
-	 * looks sane, if no data then it should be fine from our POV
-	 */
-	if (!hdr->dout_xfer_len && !hdr->din_xfer_len)
-		return 0;
-
-	/* not supported currently */
-	if (hdr->dout_xfer_len && hdr->din_xfer_len)
-		return -EINVAL;
-
-	*rw = hdr->dout_xfer_len ? WRITE : READ;
+	if (hdr->dout_xfer_len) {
+		if (hdr->din_xfer_len)
+			*dir = DMA_BIDIRECTIONAL;
+		else
+			*dir = DMA_TO_DEVICE;
+	} else if (hdr->din_xfer_len)
+		*dir = DMA_FROM_DEVICE;
+	else
+		*dir = DMA_NONE;
 
 	return 0;
 }
@@ -289,7 +300,8 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr)
 {
 	request_queue_t *q = bd->queue;
 	struct request *rq;
-	int ret, rw = 0; /* shut up gcc */
+	enum dma_data_direction dir;
+	int ret;
 	unsigned int dxfer_len;
 	void *dxferp = NULL;
 
@@ -297,39 +309,45 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr)
 		hdr->dout_xfer_len, (unsigned long long) hdr->din_xferp,
 		hdr->din_xfer_len);
 
-	ret = bsg_validate_sgv4_hdr(q, hdr, &rw);
+	ret = bsg_validate_sgv4_hdr(q, hdr, &dir);
 	if (ret)
 		return ERR_PTR(ret);
 
 	/*
 	 * map scatter-gather elements seperately and string them to request
 	 */
-	rq = blk_get_request(q, rw, GFP_KERNEL);
+	rq = blk_get_request(q, dir, GFP_KERNEL);
 	ret = blk_fill_sgv4_hdr_rq(q, rq, hdr, test_bit(BSG_F_WRITE_PERM,
 						       &bd->flags));
-	if (ret) {
-		blk_put_request(rq);
-		return ERR_PTR(ret);
-	}
+	if (ret)
+		goto errout;
 
 	if (hdr->dout_xfer_len) {
 		dxfer_len = hdr->dout_xfer_len;
 		dxferp = (void*)(unsigned long)hdr->dout_xferp;
-	} else if (hdr->din_xfer_len) {
+		ret = blk_rq_map_user_bidi(q, rq, dxferp, dxfer_len, dir);
+		if (ret)
+			goto errout;
+	}
+
+	if (hdr->din_xfer_len) {
 		dxfer_len = hdr->din_xfer_len;
 		dxferp = (void*)(unsigned long)hdr->din_xferp;
-	} else
-		dxfer_len = 0;
-
-	if (dxfer_len) {
-		ret = blk_rq_map_user(q, rq, dxferp, dxfer_len);
+		ret = blk_rq_map_user_bidi(q, rq, dxferp, dxfer_len, dir);
 		if (ret) {
-			dprintk("failed map at %d\n", ret);
-			blk_put_request(rq);
-			rq = ERR_PTR(ret);
+			blk_rq_unmap_user(rq->uni.bio);
+			goto errout;
 		}
 	}
 
+	goto out;
+
+errout:
+	kfree(rq->varlen_cdb);
+ 	blk_put_request(rq);
+	rq = ERR_PTR(ret);
+
+out:
 	return rq;
 }
 
@@ -368,7 +386,9 @@ static void bsg_add_command(struct bsg_device *bd, request_queue_t *q,
 	 * add bc command to busy queue and submit rq for io
 	 */
 	bc->rq = rq;
-	bc->bio = rq_uni(rq)->bio;
+	bc->bio = rq->uni.bio;
+	bc->bidi_read_bio = (rq->cmd_flags & REQ_BIDI)
+	                    ? rq->bidi_read.bio : NULL;
 	bc->hdr.duration = jiffies;
 	spin_lock_irq(&bd->lock);
 	list_add_tail(&bc->list, &bd->busy_list);
@@ -432,7 +452,7 @@ bsg_get_done_cmd_nosignals(struct bsg_device *bd)
 }
 
 static int blk_complete_sgv4_hdr_rq(struct request *rq, struct sg_io_v4 *hdr,
-				    struct bio *bio)
+				    struct bio *bio, struct bio *bidi_read_bio)
 {
 	int ret = 0;
 
@@ -446,7 +466,7 @@ static int blk_complete_sgv4_hdr_rq(struct request *rq, struct sg_io_v4 *hdr,
 	hdr->info = 0;
 	if (hdr->device_status || hdr->transport_status || hdr->driver_status)
 		hdr->info |= SG_INFO_CHECK;
-	hdr->din_resid = rq_uni(rq)->data_len;
+	hdr->din_resid = rq_in(rq)->data_len;
 	hdr->response_len = 0;
 
 	if (rq->sense_len && hdr->response) {
@@ -462,6 +482,8 @@ static int blk_complete_sgv4_hdr_rq(struct request *rq, struct sg_io_v4 *hdr,
 	}
 
 	blk_rq_unmap_user(bio);
+	blk_rq_unmap_user(bidi_read_bio);
+	kfree(rq->varlen_cdb);
 	blk_put_request(rq);
 
 	return ret;
@@ -509,7 +531,8 @@ static int bsg_complete_all_commands(struct bsg_device *bd)
 			break;
 		}
 
-		tret = blk_complete_sgv4_hdr_rq(bc->rq, &bc->hdr, bc->bio);
+		tret = blk_complete_sgv4_hdr_rq(bc->rq, &bc->hdr, bc->bio,
+		                                bc->bidi_read_bio);
 		if (!ret)
 			ret = tret;
 
@@ -545,7 +568,8 @@ __bsg_read(char __user *buf, size_t count, bsg_command_callback get_bc,
 		 * after completing the request. so do that here,
 		 * bsg_complete_work() cannot do that for us
 		 */
-		ret = blk_complete_sgv4_hdr_rq(bc->rq, &bc->hdr, bc->bio);
+		ret = blk_complete_sgv4_hdr_rq(bc->rq, &bc->hdr, bc->bio,
+		                               bc->bidi_read_bio);
 
 		if (copy_to_user(buf, (char *) &bc->hdr, sizeof(bc->hdr)))
 			ret = -EFAULT;
@@ -905,7 +929,7 @@ bsg_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 	}
 	case SG_IO: {
 		struct request *rq;
-		struct bio *bio;
+		struct bio *bio, *bidi_read_bio;
 		struct sg_io_v4 hdr;
 
 		if (copy_from_user(&hdr, uarg, sizeof(hdr)))
@@ -915,9 +939,11 @@ bsg_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 		if (IS_ERR(rq))
 			return PTR_ERR(rq);
 
-		bio = rq_uni(rq)->bio;
+		bio = rq->uni.bio;
+		bidi_read_bio = (rq->cmd_flags & REQ_BIDI)
+		                ? rq->bidi_read.bio : NULL;
 		blk_execute_rq(bd->queue, NULL, rq, 0);
-		blk_complete_sgv4_hdr_rq(rq, &hdr, bio);
+		blk_complete_sgv4_hdr_rq(rq, &hdr, bio, bidi_read_bio);
 
 		if (copy_to_user(uarg, &hdr, sizeof(hdr)))
 			return -EFAULT;
diff --git a/block/ll_rw_blk.c b/block/ll_rw_blk.c
index ab3629e..80b558a 100644
--- a/block/ll_rw_blk.c
+++ b/block/ll_rw_blk.c
@@ -2376,7 +2376,8 @@ static int __blk_rq_unmap_user(struct bio *bio)
 }
 
 static int __blk_rq_map_user(request_queue_t *q, struct request *rq,
-			     void __user *ubuf, unsigned int len)
+			     void __user *ubuf, unsigned int len,
+			     enum dma_data_direction dir)
 {
 	unsigned long uaddr;
 	struct bio *bio, *orig_bio;
@@ -2406,16 +2407,16 @@ static int __blk_rq_map_user(request_queue_t *q, struct request *rq,
 	 */
 	bio_get(bio);
 
-	if (!rq_uni(rq)->bio)
-		blk_rq_bio_prep(q, rq, bio);
+	if (!rq_io(rq, dir)->bio)
+		blk_rq_bio_prep_bidi(q, rq, bio, dir);
 	else if (!ll_back_merge_fn(q, rq, bio, rq->data_dir)) {
 		ret = -EINVAL;
 		goto unmap_bio;
 	} else {
-		rq_uni(rq)->biotail->bi_next = bio;
-		rq_uni(rq)->biotail = bio;
+		rq_io(rq, dir)->biotail->bi_next = bio;
+		rq_io(rq, dir)->biotail = bio;
 
-		rq_uni(rq)->data_len += bio->bi_size;
+		rq_io(rq, dir)->data_len += bio->bi_size;
 	}
 
 	return bio->bi_size;
@@ -2448,8 +2449,9 @@ unmap_bio:
  *    original bio must be passed back in to blk_rq_unmap_user() for proper
  *    unmapping.
  */
-int blk_rq_map_user(request_queue_t *q, struct request *rq, void __user *ubuf,
-		    unsigned long len)
+int blk_rq_map_user_bidi(request_queue_t *q, struct request *rq,
+                         void __user *ubuf, unsigned long len,
+			 enum dma_data_direction dir)
 {
 	unsigned long bytes_read = 0;
 	struct bio *bio = NULL;
@@ -2476,11 +2478,11 @@ int blk_rq_map_user(request_queue_t *q, struct request *rq, void __user *ubuf,
 		if (end - start > BIO_MAX_PAGES)
 			map_len -= PAGE_SIZE;
 
-		ret = __blk_rq_map_user(q, rq, ubuf, map_len);
+		ret = __blk_rq_map_user(q, rq, ubuf, map_len, dir);
 		if (ret < 0)
 			goto unmap_rq;
 		if (!bio)
-			bio = rq_uni(rq)->bio;
+			bio = rq_io(rq, dir)->bio;
 		bytes_read += ret;
 		ubuf += ret;
 	}
@@ -2492,6 +2494,14 @@ unmap_rq:
 	return ret;
 }
 
+EXPORT_SYMBOL(blk_rq_map_user_bidi);
+
+int blk_rq_map_user(request_queue_t *q, struct request *rq, void __user *ubuf,
+		    unsigned long len)
+{
+	return blk_rq_map_user_bidi(q, rq, ubuf, len, rq->data_dir);
+}
+
 EXPORT_SYMBOL(blk_rq_map_user);
 
 /**
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index c8fe1fd..6506231 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -767,6 +767,7 @@ extern void __blk_stop_queue(request_queue_t *q);
 extern void blk_run_queue(request_queue_t *);
 extern void blk_start_queueing(request_queue_t *);
 extern int blk_rq_map_user(request_queue_t *, struct request *, void __user *, unsigned long);
+extern int blk_rq_map_user_bidi(request_queue_t *, struct request *, void __user *, unsigned long, enum dma_data_direction);
 extern int blk_rq_unmap_user(struct bio *);
 extern int blk_rq_map_kern(request_queue_t *, struct request *, void *, unsigned int, gfp_t);
 extern int blk_rq_map_user_iov(request_queue_t *, struct request *,
-- 
1.5.0.6

-
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux