mirror of https://github.com/xemu-project/xemu.git
virtio-blk: add SGI_IO passthru support
[had the qemu list address wrong the first time, reply to this message, not the previous if you were on Cc] Add support for SG_IO passthru (packet commands) to the virtio-blk backend. Conceptually based on an older patch from Hannes Reinecke but largely rewritten to match the code structure and layering in virtio-blk. Note that currently we issue the hose SG_IO synchronously. We could easily switch to async I/O, but that would required either bloating the VirtIOBlockReq by the size of struct sg_io_hdr or an additional memory allocation for each SG_IO request. Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
parent
451c4abd8c
commit
1063b8b15f
123
hw/virtio-blk.c
123
hw/virtio-blk.c
|
@ -15,6 +15,9 @@
|
|||
#include <sysemu.h>
|
||||
#include "virtio-blk.h"
|
||||
#include "block_int.h"
|
||||
#ifdef __linux__
|
||||
# include <scsi/sg.h>
|
||||
#endif
|
||||
|
||||
typedef struct VirtIOBlock
|
||||
{
|
||||
|
@ -35,6 +38,7 @@ typedef struct VirtIOBlockReq
|
|||
VirtQueueElement elem;
|
||||
struct virtio_blk_inhdr *in;
|
||||
struct virtio_blk_outhdr *out;
|
||||
struct virtio_scsi_inhdr *scsi;
|
||||
QEMUIOVector qiov;
|
||||
struct VirtIOBlockReq *next;
|
||||
} VirtIOBlockReq;
|
||||
|
@ -103,6 +107,108 @@ static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s)
|
|||
return req;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
static void virtio_blk_handle_scsi(VirtIOBlockReq *req)
|
||||
{
|
||||
struct sg_io_hdr hdr;
|
||||
int ret, size = 0;
|
||||
int status;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* We require at least one output segment each for the virtio_blk_outhdr
|
||||
* and the SCSI command block.
|
||||
*
|
||||
* We also at least require the virtio_blk_inhdr, the virtio_scsi_inhdr
|
||||
* and the sense buffer pointer in the input segments.
|
||||
*/
|
||||
if (req->elem.out_num < 2 || req->elem.in_num < 3) {
|
||||
virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* No support for bidirection commands yet.
|
||||
*/
|
||||
if (req->elem.out_num > 2 && req->elem.in_num > 3) {
|
||||
virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The scsi inhdr is placed in the second-to-last input segment, just
|
||||
* before the regular inhdr.
|
||||
*/
|
||||
req->scsi = (void *)req->elem.in_sg[req->elem.in_num - 2].iov_base;
|
||||
size = sizeof(*req->in) + sizeof(*req->scsi);
|
||||
|
||||
memset(&hdr, 0, sizeof(struct sg_io_hdr));
|
||||
hdr.interface_id = 'S';
|
||||
hdr.cmd_len = req->elem.out_sg[1].iov_len;
|
||||
hdr.cmdp = req->elem.out_sg[1].iov_base;
|
||||
hdr.dxfer_len = 0;
|
||||
|
||||
if (req->elem.out_num > 2) {
|
||||
/*
|
||||
* If there are more than the minimally required 2 output segments
|
||||
* there is write payload starting from the third iovec.
|
||||
*/
|
||||
hdr.dxfer_direction = SG_DXFER_TO_DEV;
|
||||
hdr.iovec_count = req->elem.out_num - 2;
|
||||
|
||||
for (i = 0; i < hdr.iovec_count; i++)
|
||||
hdr.dxfer_len += req->elem.out_sg[i + 2].iov_len;
|
||||
|
||||
hdr.dxferp = req->elem.out_sg + 2;
|
||||
|
||||
} else if (req->elem.in_num > 3) {
|
||||
/*
|
||||
* If we have more than 3 input segments the guest wants to actually
|
||||
* read data.
|
||||
*/
|
||||
hdr.dxfer_direction = SG_DXFER_FROM_DEV;
|
||||
hdr.iovec_count = req->elem.in_num - 3;
|
||||
for (i = 0; i < hdr.iovec_count; i++)
|
||||
hdr.dxfer_len += req->elem.in_sg[i].iov_len;
|
||||
|
||||
hdr.dxferp = req->elem.in_sg;
|
||||
size += hdr.dxfer_len;
|
||||
} else {
|
||||
/*
|
||||
* Some SCSI commands don't actually transfer any data.
|
||||
*/
|
||||
hdr.dxfer_direction = SG_DXFER_NONE;
|
||||
}
|
||||
|
||||
hdr.sbp = req->elem.in_sg[req->elem.in_num - 3].iov_base;
|
||||
hdr.mx_sb_len = req->elem.in_sg[req->elem.in_num - 3].iov_len;
|
||||
size += hdr.mx_sb_len;
|
||||
|
||||
ret = bdrv_ioctl(req->dev->bs, SG_IO, &hdr);
|
||||
if (ret) {
|
||||
status = VIRTIO_BLK_S_UNSUPP;
|
||||
hdr.status = ret;
|
||||
hdr.resid = hdr.dxfer_len;
|
||||
} else if (hdr.status) {
|
||||
status = VIRTIO_BLK_S_IOERR;
|
||||
} else {
|
||||
status = VIRTIO_BLK_S_OK;
|
||||
}
|
||||
|
||||
req->scsi->errors = hdr.status;
|
||||
req->scsi->residual = hdr.resid;
|
||||
req->scsi->sense_len = hdr.sb_len_wr;
|
||||
req->scsi->data_len = hdr.dxfer_len;
|
||||
|
||||
virtio_blk_req_complete(req, status);
|
||||
}
|
||||
#else
|
||||
static void virtio_blk_handle_scsi(VirtIOBlockReq *req)
|
||||
{
|
||||
virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
|
||||
}
|
||||
#endif /* __linux__ */
|
||||
|
||||
static void virtio_blk_handle_write(VirtIOBlockReq *req)
|
||||
{
|
||||
bdrv_aio_writev(req->dev->bs, req->out->sector, &req->qiov,
|
||||
|
@ -136,12 +242,7 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
|
|||
req->in = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base;
|
||||
|
||||
if (req->out->type & VIRTIO_BLK_T_SCSI_CMD) {
|
||||
unsigned int len = sizeof(*req->in);
|
||||
|
||||
req->in->status = VIRTIO_BLK_S_UNSUPP;
|
||||
virtqueue_push(vq, &req->elem, len);
|
||||
virtio_notify(vdev, vq);
|
||||
qemu_free(req);
|
||||
virtio_blk_handle_scsi(req);
|
||||
} else if (req->out->type & VIRTIO_BLK_T_OUT) {
|
||||
qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1],
|
||||
req->elem.out_num - 1);
|
||||
|
@ -203,7 +304,15 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config)
|
|||
|
||||
static uint32_t virtio_blk_get_features(VirtIODevice *vdev)
|
||||
{
|
||||
return (1 << VIRTIO_BLK_F_SEG_MAX | 1 << VIRTIO_BLK_F_GEOMETRY);
|
||||
uint32_t features = 0;
|
||||
|
||||
features |= (1 << VIRTIO_BLK_F_SEG_MAX);
|
||||
features |= (1 << VIRTIO_BLK_F_GEOMETRY);
|
||||
#ifdef __linux__
|
||||
features |= (1 << VIRTIO_BLK_F_SCSI);
|
||||
#endif
|
||||
|
||||
return features;
|
||||
}
|
||||
|
||||
static void virtio_blk_save(QEMUFile *f, void *opaque)
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
#define VIRTIO_BLK_F_SIZE_MAX 1 /* Indicates maximum segment size */
|
||||
#define VIRTIO_BLK_F_SEG_MAX 2 /* Indicates maximum # of segments */
|
||||
#define VIRTIO_BLK_F_GEOMETRY 4 /* Indicates support of legacy geometry */
|
||||
#define VIRTIO_BLK_F_RO 5 /* Disk is read-only */
|
||||
#define VIRTIO_BLK_F_BLK_SIZE 6 /* Block size of disk is available*/
|
||||
#define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */
|
||||
|
||||
struct virtio_blk_config
|
||||
{
|
||||
|
@ -70,6 +73,15 @@ struct virtio_blk_inhdr
|
|||
unsigned char status;
|
||||
};
|
||||
|
||||
/* SCSI pass-through header */
|
||||
struct virtio_scsi_inhdr
|
||||
{
|
||||
uint32_t errors;
|
||||
uint32_t data_len;
|
||||
uint32_t sense_len;
|
||||
uint32_t residual;
|
||||
};
|
||||
|
||||
void *virtio_blk_init(PCIBus *bus, BlockDriverState *bs);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue