If we want to do bio-based I/O in virtio-blk we have to implement reading the serial attribute ourselves. Do that and also prepare struct virtblk_req for dealing with different types of requests. Signed-off-by: Christoph Hellwig <hch@xxxxxx> Index: linux-2.6/drivers/block/virtio_blk.c =================================================================== --- linux-2.6.orig/drivers/block/virtio_blk.c 2011-10-03 20:32:12.997713070 +0200 +++ linux-2.6/drivers/block/virtio_blk.c 2011-10-03 20:37:28.836714193 +0200 @@ -38,12 +38,42 @@ struct virtio_blk struct virtblk_req { - struct request *req; + void *private; struct virtio_blk_outhdr out_hdr; struct virtio_scsi_inhdr in_hdr; + u8 kind; +#define VIRTIO_BLK_REQUEST 0x00 +#define VIRTIO_BLK_INTERNAL 0x01 u8 status; }; +static inline int virtblk_result(struct virtblk_req *vbr) +{ + switch (vbr->status) { + case VIRTIO_BLK_S_OK: + return 0; + case VIRTIO_BLK_S_UNSUPP: + return -ENOTTY; + default: + return -EIO; + } +} + +static void virtblk_request_done(struct virtio_blk *vblk, + struct virtblk_req *vbr) +{ + struct request *req = vbr->private; + + if (req->cmd_type == REQ_TYPE_BLOCK_PC) { + req->resid_len = vbr->in_hdr.residual; + req->sense_len = vbr->in_hdr.sense_len; + req->errors = vbr->in_hdr.errors; + } + + __blk_end_request_all(req, virtblk_result(vbr)); + mempool_free(vbr, vblk->pool); +} + static void blk_done(struct virtqueue *vq) { struct virtio_blk *vblk = vq->vdev->priv; @@ -53,35 +83,16 @@ static void blk_done(struct virtqueue *v spin_lock_irqsave(&vblk->lock, flags); while ((vbr = virtqueue_get_buf(vblk->vq, &len)) != NULL) { - int error; - - switch (vbr->status) { - case VIRTIO_BLK_S_OK: - error = 0; - break; - case VIRTIO_BLK_S_UNSUPP: - error = -ENOTTY; - break; - default: - error = -EIO; - break; - } - - switch (vbr->req->cmd_type) { - case REQ_TYPE_BLOCK_PC: - vbr->req->resid_len = vbr->in_hdr.residual; - vbr->req->sense_len = vbr->in_hdr.sense_len; - vbr->req->errors = vbr->in_hdr.errors; + switch (vbr->kind) { + case VIRTIO_BLK_REQUEST: + virtblk_request_done(vblk, vbr); break; - case REQ_TYPE_SPECIAL: - vbr->req->errors = (error != 0); + case VIRTIO_BLK_INTERNAL: + complete(vbr->private); break; default: - break; + BUG(); } - - __blk_end_request_all(vbr->req, error); - mempool_free(vbr, vblk->pool); } /* In case queue is stopped waiting for more buffers. */ blk_start_queue(vblk->disk->queue); @@ -99,28 +110,24 @@ static bool do_req(struct request_queue /* When another request finishes we'll try again. */ return false; - vbr->req = req; + vbr->private = req; + vbr->kind = VIRTIO_BLK_REQUEST; if (req->cmd_flags & REQ_FLUSH) { vbr->out_hdr.type = VIRTIO_BLK_T_FLUSH; vbr->out_hdr.sector = 0; - vbr->out_hdr.ioprio = req_get_ioprio(vbr->req); + vbr->out_hdr.ioprio = req_get_ioprio(req); } else { switch (req->cmd_type) { case REQ_TYPE_FS: vbr->out_hdr.type = 0; - vbr->out_hdr.sector = blk_rq_pos(vbr->req); - vbr->out_hdr.ioprio = req_get_ioprio(vbr->req); + vbr->out_hdr.sector = blk_rq_pos(req); + vbr->out_hdr.ioprio = req_get_ioprio(req); break; case REQ_TYPE_BLOCK_PC: vbr->out_hdr.type = VIRTIO_BLK_T_SCSI_CMD; 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); + vbr->out_hdr.ioprio = req_get_ioprio(req); break; default: /* We don't put anything else in the queue. */ @@ -136,13 +143,14 @@ static bool do_req(struct request_queue * block, and before the normal inhdr we put the sense data and the * inhdr with additional status information before the normal inhdr. */ - if (vbr->req->cmd_type == REQ_TYPE_BLOCK_PC) - sg_set_buf(&vblk->sg[out++], vbr->req->cmd, vbr->req->cmd_len); + if (req->cmd_type == REQ_TYPE_BLOCK_PC) + sg_set_buf(&vblk->sg[out++], req->cmd, req->cmd_len); - num = blk_rq_map_sg(q, vbr->req, vblk->sg + out); + num = blk_rq_map_sg(q, req, vblk->sg + out); - if (vbr->req->cmd_type == REQ_TYPE_BLOCK_PC) { - sg_set_buf(&vblk->sg[num + out + in++], vbr->req->sense, SCSI_SENSE_BUFFERSIZE); + if (req->cmd_type == REQ_TYPE_BLOCK_PC) { + sg_set_buf(&vblk->sg[num + out + in++], req->sense, + SCSI_SENSE_BUFFERSIZE); sg_set_buf(&vblk->sg[num + out + in++], &vbr->in_hdr, sizeof(vbr->in_hdr)); } @@ -151,7 +159,7 @@ static bool do_req(struct request_queue sizeof(vbr->status)); if (num) { - if (rq_data_dir(vbr->req) == WRITE) { + if (rq_data_dir(req) == WRITE) { vbr->out_hdr.type |= VIRTIO_BLK_T_OUT; out += num; } else { @@ -196,26 +204,39 @@ static void do_virtblk_request(struct re static int virtblk_get_id(struct gendisk *disk, char *id_str) { struct virtio_blk *vblk = disk->private_data; - struct request *req; - struct bio *bio; - int err; - - 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; - err = blk_execute_rq(vblk->disk->queue, vblk->disk, req, false); - blk_put_request(req); + struct virtblk_req *vbr; + DECLARE_COMPLETION_ONSTACK(done); + int error; - return err; + vbr = kmalloc(sizeof(*vbr), GFP_KERNEL); + if (!vbr) + return -ENOMEM; + vbr->private = &done; + vbr->kind = VIRTIO_BLK_INTERNAL; + + vbr->out_hdr.type = VIRTIO_BLK_T_GET_ID | VIRTIO_BLK_T_IN; + vbr->out_hdr.sector = 0; + vbr->out_hdr.ioprio = 0; + + sg_set_buf(&vblk->sg[0], &vbr->out_hdr, sizeof(vbr->out_hdr)); + sg_set_buf(&vblk->sg[1], id_str, VIRTIO_BLK_ID_BYTES); + sg_set_buf(&vblk->sg[2], &vbr->status, sizeof(vbr->status)); + + spin_lock_irq(&vblk->lock); + if (virtqueue_add_buf(vblk->vq, vblk->sg, 1, 2, vbr) < 0) { + spin_unlock_irq(&vblk->lock); + /* XXX: eventuall wait for free space */ + error = -EBUSY; + goto out_free; + } + virtqueue_kick(vblk->vq); + spin_unlock_irq(&vblk->lock); + + wait_for_completion(&done); + error = virtblk_result(vbr); +out_free: + kfree(vbr); + return error; } static int virtblk_ioctl(struct block_device *bdev, fmode_t mode, -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html