diff options
author | Hannes Reinecke <hare@suse.de> | 2009-05-18 08:41:30 -0400 |
---|---|---|
committer | Jens Axboe <jens.axboe@oracle.com> | 2009-05-18 08:41:30 -0400 |
commit | 1cde26f928863d90e9e7c1217880c8450464d305 (patch) | |
tree | e0f6e5157cccc8ab8b87b1192a1da6ddc6113e82 /drivers/block/virtio_blk.c | |
parent | 6c3b46f74587d46e71b8c2b78fdca626a3aca280 (diff) |
virtio_blk: SG_IO passthru support
Add support for SG_IO passthru to virtio_blk. We add the scsi command
block after the normal outhdr, and the scsi inhdr with full status
information aswell as the sense buffer before the regular inhdr.
[hch: forward ported, added the VIRTIO_BLK_F_SCSI flags, some comments
and tested the whole beast]
[axboe: updated to use ->resid and not dual-path the byte count]
Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> (+ checkpatch.pl tweak)
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
Diffstat (limited to 'drivers/block/virtio_blk.c')
-rw-r--r-- | drivers/block/virtio_blk.c | 64 |
1 files changed, 50 insertions, 14 deletions
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index dfe9ee5f1696..62275dbdf2eb 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c | |||
@@ -37,6 +37,7 @@ struct virtblk_req | |||
37 | struct list_head list; | 37 | struct list_head list; |
38 | struct request *req; | 38 | struct request *req; |
39 | struct virtio_blk_outhdr out_hdr; | 39 | struct virtio_blk_outhdr out_hdr; |
40 | struct virtio_scsi_inhdr in_hdr; | ||
40 | u8 status; | 41 | u8 status; |
41 | }; | 42 | }; |
42 | 43 | ||
@@ -49,7 +50,9 @@ static void blk_done(struct virtqueue *vq) | |||
49 | 50 | ||
50 | spin_lock_irqsave(&vblk->lock, flags); | 51 | spin_lock_irqsave(&vblk->lock, flags); |
51 | while ((vbr = vblk->vq->vq_ops->get_buf(vblk->vq, &len)) != NULL) { | 52 | while ((vbr = vblk->vq->vq_ops->get_buf(vblk->vq, &len)) != NULL) { |
53 | unsigned int nr_bytes; | ||
52 | int error; | 54 | int error; |
55 | |||
53 | switch (vbr->status) { | 56 | switch (vbr->status) { |
54 | case VIRTIO_BLK_S_OK: | 57 | case VIRTIO_BLK_S_OK: |
55 | error = 0; | 58 | error = 0; |
@@ -62,6 +65,12 @@ static void blk_done(struct virtqueue *vq) | |||
62 | break; | 65 | break; |
63 | } | 66 | } |
64 | 67 | ||
68 | if (blk_pc_request(vbr->req)) { | ||
69 | vbr->req->resid_len = vbr->in_hdr.residual; | ||
70 | vbr->req->sense_len = vbr->in_hdr.sense_len; | ||
71 | vbr->req->errors = vbr->in_hdr.errors; | ||
72 | } | ||
73 | |||
65 | __blk_end_request_all(vbr->req, error); | 74 | __blk_end_request_all(vbr->req, error); |
66 | list_del(&vbr->list); | 75 | list_del(&vbr->list); |
67 | mempool_free(vbr, vblk->pool); | 76 | mempool_free(vbr, vblk->pool); |
@@ -74,7 +83,7 @@ static void blk_done(struct virtqueue *vq) | |||
74 | static bool do_req(struct request_queue *q, struct virtio_blk *vblk, | 83 | static bool do_req(struct request_queue *q, struct virtio_blk *vblk, |
75 | struct request *req) | 84 | struct request *req) |
76 | { | 85 | { |
77 | unsigned long num, out, in; | 86 | unsigned long num, out = 0, in = 0; |
78 | struct virtblk_req *vbr; | 87 | struct virtblk_req *vbr; |
79 | 88 | ||
80 | vbr = mempool_alloc(vblk->pool, GFP_ATOMIC); | 89 | vbr = mempool_alloc(vblk->pool, GFP_ATOMIC); |
@@ -99,18 +108,36 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk, | |||
99 | if (blk_barrier_rq(vbr->req)) | 108 | if (blk_barrier_rq(vbr->req)) |
100 | vbr->out_hdr.type |= VIRTIO_BLK_T_BARRIER; | 109 | vbr->out_hdr.type |= VIRTIO_BLK_T_BARRIER; |
101 | 110 | ||
102 | sg_set_buf(&vblk->sg[0], &vbr->out_hdr, sizeof(vbr->out_hdr)); | 111 | sg_set_buf(&vblk->sg[out++], &vbr->out_hdr, sizeof(vbr->out_hdr)); |
103 | num = blk_rq_map_sg(q, vbr->req, vblk->sg+1); | ||
104 | sg_set_buf(&vblk->sg[num+1], &vbr->status, sizeof(vbr->status)); | ||
105 | 112 | ||
106 | if (rq_data_dir(vbr->req) == WRITE) { | 113 | /* |
107 | vbr->out_hdr.type |= VIRTIO_BLK_T_OUT; | 114 | * If this is a packet command we need a couple of additional headers. |
108 | out = 1 + num; | 115 | * Behind the normal outhdr we put a segment with the scsi command |
109 | in = 1; | 116 | * block, and before the normal inhdr we put the sense data and the |
110 | } else { | 117 | * inhdr with additional status information before the normal inhdr. |
111 | vbr->out_hdr.type |= VIRTIO_BLK_T_IN; | 118 | */ |
112 | out = 1; | 119 | if (blk_pc_request(vbr->req)) |
113 | in = 1 + num; | 120 | sg_set_buf(&vblk->sg[out++], vbr->req->cmd, vbr->req->cmd_len); |
121 | |||
122 | num = blk_rq_map_sg(q, vbr->req, vblk->sg + out); | ||
123 | |||
124 | if (blk_pc_request(vbr->req)) { | ||
125 | sg_set_buf(&vblk->sg[num + out + in++], vbr->req->sense, 96); | ||
126 | sg_set_buf(&vblk->sg[num + out + in++], &vbr->in_hdr, | ||
127 | sizeof(vbr->in_hdr)); | ||
128 | } | ||
129 | |||
130 | sg_set_buf(&vblk->sg[num + out + in++], &vbr->status, | ||
131 | sizeof(vbr->status)); | ||
132 | |||
133 | if (num) { | ||
134 | if (rq_data_dir(vbr->req) == WRITE) { | ||
135 | vbr->out_hdr.type |= VIRTIO_BLK_T_OUT; | ||
136 | out += num; | ||
137 | } else { | ||
138 | vbr->out_hdr.type |= VIRTIO_BLK_T_IN; | ||
139 | in += num; | ||
140 | } | ||
114 | } | 141 | } |
115 | 142 | ||
116 | if (vblk->vq->vq_ops->add_buf(vblk->vq, vblk->sg, out, in, vbr)) { | 143 | if (vblk->vq->vq_ops->add_buf(vblk->vq, vblk->sg, out, in, vbr)) { |
@@ -148,8 +175,16 @@ static void do_virtblk_request(struct request_queue *q) | |||
148 | static int virtblk_ioctl(struct block_device *bdev, fmode_t mode, | 175 | static int virtblk_ioctl(struct block_device *bdev, fmode_t mode, |
149 | unsigned cmd, unsigned long data) | 176 | unsigned cmd, unsigned long data) |
150 | { | 177 | { |
151 | return scsi_cmd_ioctl(bdev->bd_disk->queue, | 178 | struct gendisk *disk = bdev->bd_disk; |
152 | bdev->bd_disk, mode, cmd, | 179 | struct virtio_blk *vblk = disk->private_data; |
180 | |||
181 | /* | ||
182 | * Only allow the generic SCSI ioctls if the host can support it. | ||
183 | */ | ||
184 | if (!virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_SCSI)) | ||
185 | return -ENOIOCTLCMD; | ||
186 | |||
187 | return scsi_cmd_ioctl(disk->queue, disk, mode, cmd, | ||
153 | (void __user *)data); | 188 | (void __user *)data); |
154 | } | 189 | } |
155 | 190 | ||
@@ -356,6 +391,7 @@ static struct virtio_device_id id_table[] = { | |||
356 | static unsigned int features[] = { | 391 | static unsigned int features[] = { |
357 | VIRTIO_BLK_F_BARRIER, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, | 392 | VIRTIO_BLK_F_BARRIER, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, |
358 | VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_BLK_SIZE, | 393 | VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_BLK_SIZE, |
394 | VIRTIO_BLK_F_SCSI, | ||
359 | }; | 395 | }; |
360 | 396 | ||
361 | static struct virtio_driver virtio_blk = { | 397 | static struct virtio_driver virtio_blk = { |