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