diff options
Diffstat (limited to 'drivers/block/virtio_blk.c')
-rw-r--r-- | drivers/block/virtio_blk.c | 56 |
1 files changed, 49 insertions, 7 deletions
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 3c64af05fa8..258bc2ae288 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -1,5 +1,6 @@ //#define DEBUG #include <linux/spinlock.h> +#include <linux/slab.h> #include <linux/blkdev.h> #include <linux/hdreg.h> #include <linux/virtio.h> @@ -49,7 +50,7 @@ static void blk_done(struct virtqueue *vq) unsigned long flags; spin_lock_irqsave(&vblk->lock, flags); - while ((vbr = vblk->vq->vq_ops->get_buf(vblk->vq, &len)) != NULL) { + while ((vbr = virtqueue_get_buf(vblk->vq, &len)) != NULL) { int error; switch (vbr->status) { @@ -69,6 +70,8 @@ static void blk_done(struct virtqueue *vq) vbr->req->sense_len = vbr->in_hdr.sense_len; vbr->req->errors = vbr->in_hdr.errors; } + if (blk_special_request(vbr->req)) + vbr->req->errors = (error != 0); __blk_end_request_all(vbr->req, error); list_del(&vbr->list); @@ -102,6 +105,11 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk, vbr->out_hdr.sector = 0; vbr->out_hdr.ioprio = req_get_ioprio(vbr->req); break; + case REQ_TYPE_SPECIAL: + vbr->out_hdr.type = VIRTIO_BLK_T_GET_ID; + vbr->out_hdr.sector = 0; + vbr->out_hdr.ioprio = req_get_ioprio(vbr->req); + break; case REQ_TYPE_LINUX_BLOCK: if (req->cmd[0] == REQ_LB_OP_FLUSH) { vbr->out_hdr.type = VIRTIO_BLK_T_FLUSH; @@ -150,7 +158,7 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk, } } - if (vblk->vq->vq_ops->add_buf(vblk->vq, vblk->sg, out, in, vbr) < 0) { + if (virtqueue_add_buf(vblk->vq, vblk->sg, out, in, vbr) < 0) { mempool_free(vbr, vblk->pool); return false; } @@ -179,7 +187,7 @@ static void do_virtblk_request(struct request_queue *q) } if (issued) - vblk->vq->vq_ops->kick(vblk->vq); + virtqueue_kick(vblk->vq); } static void virtblk_prepare_flush(struct request_queue *q, struct request *req) @@ -188,12 +196,45 @@ static void virtblk_prepare_flush(struct request_queue *q, struct request *req) req->cmd[0] = REQ_LB_OP_FLUSH; } +/* return id (s/n) string for *disk to *id_str + */ +static int virtblk_get_id(struct gendisk *disk, char *id_str) +{ + struct virtio_blk *vblk = disk->private_data; + struct request *req; + struct bio *bio; + + bio = bio_map_kern(vblk->disk->queue, id_str, VIRTIO_BLK_ID_BYTES, + GFP_KERNEL); + if (IS_ERR(bio)) + return PTR_ERR(bio); + + req = blk_make_request(vblk->disk->queue, bio, GFP_KERNEL); + if (IS_ERR(req)) { + bio_put(bio); + return PTR_ERR(req); + } + + req->cmd_type = REQ_TYPE_SPECIAL; + return blk_execute_rq(vblk->disk->queue, vblk->disk, req, false); +} + static int virtblk_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, unsigned long data) { struct gendisk *disk = bdev->bd_disk; struct virtio_blk *vblk = disk->private_data; + if (cmd == 0x56424944) { /* 'VBID' */ + void __user *usr_data = (void __user *)data; + char id_str[VIRTIO_BLK_ID_BYTES]; + int err; + + err = virtblk_get_id(disk, id_str); + if (!err && copy_to_user(usr_data, id_str, VIRTIO_BLK_ID_BYTES)) + err = -EFAULT; + return err; + } /* * Only allow the generic SCSI ioctls if the host can support it. */ @@ -257,7 +298,9 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) err = virtio_config_val(vdev, VIRTIO_BLK_F_SEG_MAX, offsetof(struct virtio_blk_config, seg_max), &sg_elems); - if (err) + + /* We need at least one SG element, whatever they say. */ + if (err || !sg_elems) sg_elems = 1; /* We need an extra sg elements at head and tail. */ @@ -347,14 +390,13 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) set_capacity(vblk->disk, cap); /* We can handle whatever the host told us to handle. */ - blk_queue_max_phys_segments(q, vblk->sg_elems-2); - blk_queue_max_hw_segments(q, vblk->sg_elems-2); + blk_queue_max_segments(q, vblk->sg_elems-2); /* No need to bounce any requests */ blk_queue_bounce_limit(q, BLK_BOUNCE_ANY); /* No real sector limit. */ - blk_queue_max_sectors(q, -1U); + blk_queue_max_hw_sectors(q, -1U); /* Host can optionally specify maximum segment size and number of * segments. */ |