diff options
Diffstat (limited to 'drivers/block/virtio_blk.c')
-rw-r--r-- | drivers/block/virtio_blk.c | 120 |
1 files changed, 93 insertions, 27 deletions
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 5d34764c8a87..43db3ea15b54 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 | ||
@@ -50,6 +51,7 @@ static void blk_done(struct virtqueue *vq) | |||
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) { |
52 | int error; | 53 | int error; |
54 | |||
53 | switch (vbr->status) { | 55 | switch (vbr->status) { |
54 | case VIRTIO_BLK_S_OK: | 56 | case VIRTIO_BLK_S_OK: |
55 | error = 0; | 57 | error = 0; |
@@ -62,7 +64,13 @@ static void blk_done(struct virtqueue *vq) | |||
62 | break; | 64 | break; |
63 | } | 65 | } |
64 | 66 | ||
65 | __blk_end_request(vbr->req, error, blk_rq_bytes(vbr->req)); | 67 | if (blk_pc_request(vbr->req)) { |
68 | vbr->req->resid_len = vbr->in_hdr.residual; | ||
69 | vbr->req->sense_len = vbr->in_hdr.sense_len; | ||
70 | vbr->req->errors = vbr->in_hdr.errors; | ||
71 | } | ||
72 | |||
73 | __blk_end_request_all(vbr->req, error); | ||
66 | list_del(&vbr->list); | 74 | list_del(&vbr->list); |
67 | mempool_free(vbr, vblk->pool); | 75 | mempool_free(vbr, vblk->pool); |
68 | } | 76 | } |
@@ -74,7 +82,7 @@ static void blk_done(struct virtqueue *vq) | |||
74 | static bool do_req(struct request_queue *q, struct virtio_blk *vblk, | 82 | static bool do_req(struct request_queue *q, struct virtio_blk *vblk, |
75 | struct request *req) | 83 | struct request *req) |
76 | { | 84 | { |
77 | unsigned long num, out, in; | 85 | unsigned long num, out = 0, in = 0; |
78 | struct virtblk_req *vbr; | 86 | struct virtblk_req *vbr; |
79 | 87 | ||
80 | vbr = mempool_alloc(vblk->pool, GFP_ATOMIC); | 88 | vbr = mempool_alloc(vblk->pool, GFP_ATOMIC); |
@@ -85,7 +93,7 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk, | |||
85 | vbr->req = req; | 93 | vbr->req = req; |
86 | if (blk_fs_request(vbr->req)) { | 94 | if (blk_fs_request(vbr->req)) { |
87 | vbr->out_hdr.type = 0; | 95 | vbr->out_hdr.type = 0; |
88 | vbr->out_hdr.sector = vbr->req->sector; | 96 | vbr->out_hdr.sector = blk_rq_pos(vbr->req); |
89 | vbr->out_hdr.ioprio = req_get_ioprio(vbr->req); | 97 | vbr->out_hdr.ioprio = req_get_ioprio(vbr->req); |
90 | } else if (blk_pc_request(vbr->req)) { | 98 | } else if (blk_pc_request(vbr->req)) { |
91 | vbr->out_hdr.type = VIRTIO_BLK_T_SCSI_CMD; | 99 | vbr->out_hdr.type = VIRTIO_BLK_T_SCSI_CMD; |
@@ -99,18 +107,36 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk, | |||
99 | if (blk_barrier_rq(vbr->req)) | 107 | if (blk_barrier_rq(vbr->req)) |
100 | vbr->out_hdr.type |= VIRTIO_BLK_T_BARRIER; | 108 | vbr->out_hdr.type |= VIRTIO_BLK_T_BARRIER; |
101 | 109 | ||
102 | sg_set_buf(&vblk->sg[0], &vbr->out_hdr, sizeof(vbr->out_hdr)); | 110 | 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 | 111 | ||
106 | if (rq_data_dir(vbr->req) == WRITE) { | 112 | /* |
107 | vbr->out_hdr.type |= VIRTIO_BLK_T_OUT; | 113 | * If this is a packet command we need a couple of additional headers. |
108 | out = 1 + num; | 114 | * Behind the normal outhdr we put a segment with the scsi command |
109 | in = 1; | 115 | * block, and before the normal inhdr we put the sense data and the |
110 | } else { | 116 | * inhdr with additional status information before the normal inhdr. |
111 | vbr->out_hdr.type |= VIRTIO_BLK_T_IN; | 117 | */ |
112 | out = 1; | 118 | if (blk_pc_request(vbr->req)) |
113 | in = 1 + num; | 119 | sg_set_buf(&vblk->sg[out++], vbr->req->cmd, vbr->req->cmd_len); |
120 | |||
121 | num = blk_rq_map_sg(q, vbr->req, vblk->sg + out); | ||
122 | |||
123 | if (blk_pc_request(vbr->req)) { | ||
124 | sg_set_buf(&vblk->sg[num + out + in++], vbr->req->sense, 96); | ||
125 | sg_set_buf(&vblk->sg[num + out + in++], &vbr->in_hdr, | ||
126 | sizeof(vbr->in_hdr)); | ||
127 | } | ||
128 | |||
129 | sg_set_buf(&vblk->sg[num + out + in++], &vbr->status, | ||
130 | sizeof(vbr->status)); | ||
131 | |||
132 | if (num) { | ||
133 | if (rq_data_dir(vbr->req) == WRITE) { | ||
134 | vbr->out_hdr.type |= VIRTIO_BLK_T_OUT; | ||
135 | out += num; | ||
136 | } else { | ||
137 | vbr->out_hdr.type |= VIRTIO_BLK_T_IN; | ||
138 | in += num; | ||
139 | } | ||
114 | } | 140 | } |
115 | 141 | ||
116 | if (vblk->vq->vq_ops->add_buf(vblk->vq, vblk->sg, out, in, vbr)) { | 142 | if (vblk->vq->vq_ops->add_buf(vblk->vq, vblk->sg, out, in, vbr)) { |
@@ -124,12 +150,11 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk, | |||
124 | 150 | ||
125 | static void do_virtblk_request(struct request_queue *q) | 151 | static void do_virtblk_request(struct request_queue *q) |
126 | { | 152 | { |
127 | struct virtio_blk *vblk = NULL; | 153 | struct virtio_blk *vblk = q->queuedata; |
128 | struct request *req; | 154 | struct request *req; |
129 | unsigned int issued = 0; | 155 | unsigned int issued = 0; |
130 | 156 | ||
131 | while ((req = elv_next_request(q)) != NULL) { | 157 | while ((req = blk_peek_request(q)) != NULL) { |
132 | vblk = req->rq_disk->private_data; | ||
133 | BUG_ON(req->nr_phys_segments + 2 > vblk->sg_elems); | 158 | BUG_ON(req->nr_phys_segments + 2 > vblk->sg_elems); |
134 | 159 | ||
135 | /* If this request fails, stop queue and wait for something to | 160 | /* If this request fails, stop queue and wait for something to |
@@ -138,7 +163,7 @@ static void do_virtblk_request(struct request_queue *q) | |||
138 | blk_stop_queue(q); | 163 | blk_stop_queue(q); |
139 | break; | 164 | break; |
140 | } | 165 | } |
141 | blkdev_dequeue_request(req); | 166 | blk_start_request(req); |
142 | issued++; | 167 | issued++; |
143 | } | 168 | } |
144 | 169 | ||
@@ -146,12 +171,51 @@ static void do_virtblk_request(struct request_queue *q) | |||
146 | vblk->vq->vq_ops->kick(vblk->vq); | 171 | vblk->vq->vq_ops->kick(vblk->vq); |
147 | } | 172 | } |
148 | 173 | ||
174 | /* return ATA identify data | ||
175 | */ | ||
176 | static int virtblk_identify(struct gendisk *disk, void *argp) | ||
177 | { | ||
178 | struct virtio_blk *vblk = disk->private_data; | ||
179 | void *opaque; | ||
180 | int err = -ENOMEM; | ||
181 | |||
182 | opaque = kmalloc(VIRTIO_BLK_ID_BYTES, GFP_KERNEL); | ||
183 | if (!opaque) | ||
184 | goto out; | ||
185 | |||
186 | err = virtio_config_buf(vblk->vdev, VIRTIO_BLK_F_IDENTIFY, | ||
187 | offsetof(struct virtio_blk_config, identify), opaque, | ||
188 | VIRTIO_BLK_ID_BYTES); | ||
189 | |||
190 | if (err) | ||
191 | goto out_kfree; | ||
192 | |||
193 | if (copy_to_user(argp, opaque, VIRTIO_BLK_ID_BYTES)) | ||
194 | err = -EFAULT; | ||
195 | |||
196 | out_kfree: | ||
197 | kfree(opaque); | ||
198 | out: | ||
199 | return err; | ||
200 | } | ||
201 | |||
149 | static int virtblk_ioctl(struct block_device *bdev, fmode_t mode, | 202 | static int virtblk_ioctl(struct block_device *bdev, fmode_t mode, |
150 | unsigned cmd, unsigned long data) | 203 | unsigned cmd, unsigned long data) |
151 | { | 204 | { |
152 | return scsi_cmd_ioctl(bdev->bd_disk->queue, | 205 | struct gendisk *disk = bdev->bd_disk; |
153 | bdev->bd_disk, mode, cmd, | 206 | struct virtio_blk *vblk = disk->private_data; |
154 | (void __user *)data); | 207 | void __user *argp = (void __user *)data; |
208 | |||
209 | if (cmd == HDIO_GET_IDENTITY) | ||
210 | return virtblk_identify(disk, argp); | ||
211 | |||
212 | /* | ||
213 | * Only allow the generic SCSI ioctls if the host can support it. | ||
214 | */ | ||
215 | if (!virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_SCSI)) | ||
216 | return -ENOIOCTLCMD; | ||
217 | |||
218 | return scsi_cmd_ioctl(disk->queue, disk, mode, cmd, argp); | ||
155 | } | 219 | } |
156 | 220 | ||
157 | /* We provide getgeo only to please some old bootloader/partitioning tools */ | 221 | /* We provide getgeo only to please some old bootloader/partitioning tools */ |
@@ -190,7 +254,7 @@ static int index_to_minor(int index) | |||
190 | return index << PART_BITS; | 254 | return index << PART_BITS; |
191 | } | 255 | } |
192 | 256 | ||
193 | static int virtblk_probe(struct virtio_device *vdev) | 257 | static int __devinit virtblk_probe(struct virtio_device *vdev) |
194 | { | 258 | { |
195 | struct virtio_blk *vblk; | 259 | struct virtio_blk *vblk; |
196 | int err; | 260 | int err; |
@@ -224,7 +288,7 @@ static int virtblk_probe(struct virtio_device *vdev) | |||
224 | sg_init_table(vblk->sg, vblk->sg_elems); | 288 | sg_init_table(vblk->sg, vblk->sg_elems); |
225 | 289 | ||
226 | /* We expect one virtqueue, for output. */ | 290 | /* We expect one virtqueue, for output. */ |
227 | vblk->vq = vdev->config->find_vq(vdev, 0, blk_done); | 291 | vblk->vq = virtio_find_single_vq(vdev, blk_done, "requests"); |
228 | if (IS_ERR(vblk->vq)) { | 292 | if (IS_ERR(vblk->vq)) { |
229 | err = PTR_ERR(vblk->vq); | 293 | err = PTR_ERR(vblk->vq); |
230 | goto out_free_vblk; | 294 | goto out_free_vblk; |
@@ -249,6 +313,7 @@ static int virtblk_probe(struct virtio_device *vdev) | |||
249 | goto out_put_disk; | 313 | goto out_put_disk; |
250 | } | 314 | } |
251 | 315 | ||
316 | vblk->disk->queue->queuedata = vblk; | ||
252 | queue_flag_set_unlocked(QUEUE_FLAG_VIRT, vblk->disk->queue); | 317 | queue_flag_set_unlocked(QUEUE_FLAG_VIRT, vblk->disk->queue); |
253 | 318 | ||
254 | if (index < 26) { | 319 | if (index < 26) { |
@@ -313,7 +378,7 @@ static int virtblk_probe(struct virtio_device *vdev) | |||
313 | offsetof(struct virtio_blk_config, blk_size), | 378 | offsetof(struct virtio_blk_config, blk_size), |
314 | &blk_size); | 379 | &blk_size); |
315 | if (!err) | 380 | if (!err) |
316 | blk_queue_hardsect_size(vblk->disk->queue, blk_size); | 381 | blk_queue_logical_block_size(vblk->disk->queue, blk_size); |
317 | 382 | ||
318 | add_disk(vblk->disk); | 383 | add_disk(vblk->disk); |
319 | return 0; | 384 | return 0; |
@@ -323,14 +388,14 @@ out_put_disk: | |||
323 | out_mempool: | 388 | out_mempool: |
324 | mempool_destroy(vblk->pool); | 389 | mempool_destroy(vblk->pool); |
325 | out_free_vq: | 390 | out_free_vq: |
326 | vdev->config->del_vq(vblk->vq); | 391 | vdev->config->del_vqs(vdev); |
327 | out_free_vblk: | 392 | out_free_vblk: |
328 | kfree(vblk); | 393 | kfree(vblk); |
329 | out: | 394 | out: |
330 | return err; | 395 | return err; |
331 | } | 396 | } |
332 | 397 | ||
333 | static void virtblk_remove(struct virtio_device *vdev) | 398 | static void __devexit virtblk_remove(struct virtio_device *vdev) |
334 | { | 399 | { |
335 | struct virtio_blk *vblk = vdev->priv; | 400 | struct virtio_blk *vblk = vdev->priv; |
336 | 401 | ||
@@ -344,7 +409,7 @@ static void virtblk_remove(struct virtio_device *vdev) | |||
344 | blk_cleanup_queue(vblk->disk->queue); | 409 | blk_cleanup_queue(vblk->disk->queue); |
345 | put_disk(vblk->disk); | 410 | put_disk(vblk->disk); |
346 | mempool_destroy(vblk->pool); | 411 | mempool_destroy(vblk->pool); |
347 | vdev->config->del_vq(vblk->vq); | 412 | vdev->config->del_vqs(vdev); |
348 | kfree(vblk); | 413 | kfree(vblk); |
349 | } | 414 | } |
350 | 415 | ||
@@ -356,6 +421,7 @@ static struct virtio_device_id id_table[] = { | |||
356 | static unsigned int features[] = { | 421 | static unsigned int features[] = { |
357 | VIRTIO_BLK_F_BARRIER, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, | 422 | 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, | 423 | VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_BLK_SIZE, |
424 | VIRTIO_BLK_F_SCSI, VIRTIO_BLK_F_IDENTIFY | ||
359 | }; | 425 | }; |
360 | 426 | ||
361 | static struct virtio_driver virtio_blk = { | 427 | static struct virtio_driver virtio_blk = { |