mirror of https://gitee.com/openkylin/qemu.git
Merge remote-tracking branch 'bonzini/scsi-next' into staging
* bonzini/scsi-next: scsi: add SANITIZE command SCSI emulation: should tell the guest that we actually support thin provisioning SCSI emulation: Support unmap via WRITE_SAME_10. scsi: advertise DPOFUA scsi: small refactoring of MMC mode-sense scsi: support FUA on reads scsi: add a started field to SCSIDiskReq scsi: force unit access on VERIFY scsi: add support for FUA on writes scsi: move scsi_flush_complete around scsi: make code more homogeneous in AIO callback functions scsi: add missing test for cancelled request virtio-scsi: add multiqueue capability virtio: add virtio_queue_get_id virtio-scsi: prepare migration format for multiqueue scsi: fix memory leak
This commit is contained in:
commit
4a1873fc8c
|
@ -1200,6 +1200,7 @@ static const char *scsi_command_name(uint8_t cmd)
|
|||
[ UNMAP ] = "UNMAP",
|
||||
[ READ_TOC ] = "READ_TOC",
|
||||
[ REPORT_DENSITY_SUPPORT ] = "REPORT_DENSITY_SUPPORT",
|
||||
[ SANITIZE ] = "SANITIZE",
|
||||
[ GET_CONFIGURATION ] = "GET_CONFIGURATION",
|
||||
[ LOG_SELECT ] = "LOG_SELECT",
|
||||
[ LOG_SENSE ] = "LOG_SENSE",
|
||||
|
@ -1430,15 +1431,18 @@ static char *scsibus_get_dev_path(DeviceState *dev)
|
|||
SCSIDevice *d = DO_UPCAST(SCSIDevice, qdev, dev);
|
||||
DeviceState *hba = dev->parent_bus->parent;
|
||||
char *id = NULL;
|
||||
char *path;
|
||||
|
||||
if (hba && hba->parent_bus && hba->parent_bus->info->get_dev_path) {
|
||||
id = hba->parent_bus->info->get_dev_path(hba);
|
||||
}
|
||||
if (id) {
|
||||
return g_strdup_printf("%s/%d:%d:%d", id, d->channel, d->id, d->lun);
|
||||
path = g_strdup_printf("%s/%d:%d:%d", id, d->channel, d->id, d->lun);
|
||||
} else {
|
||||
return g_strdup_printf("%d:%d:%d", d->channel, d->id, d->lun);
|
||||
path = g_strdup_printf("%d:%d:%d", d->channel, d->id, d->lun);
|
||||
}
|
||||
g_free(id);
|
||||
return path;
|
||||
}
|
||||
|
||||
static char *scsibus_get_fw_dev_path(DeviceState *dev)
|
||||
|
|
|
@ -78,6 +78,7 @@
|
|||
#define READ_TOC 0x43
|
||||
#define REPORT_DENSITY_SUPPORT 0x44
|
||||
#define GET_CONFIGURATION 0x46
|
||||
#define SANITIZE 0x48
|
||||
#define GET_EVENT_STATUS_NOTIFICATION 0x4a
|
||||
#define LOG_SELECT 0x4c
|
||||
#define LOG_SENSE 0x4d
|
||||
|
|
170
hw/scsi-disk.c
170
hw/scsi-disk.c
|
@ -55,6 +55,7 @@ typedef struct SCSIDiskReq {
|
|||
uint64_t sector;
|
||||
uint32_t sector_count;
|
||||
uint32_t buflen;
|
||||
bool started;
|
||||
struct iovec iov;
|
||||
QEMUIOVector qiov;
|
||||
BlockAcctCookie acct;
|
||||
|
@ -153,14 +154,80 @@ static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req)
|
|||
qemu_iovec_init_external(&r->qiov, &r->iov, 1);
|
||||
}
|
||||
|
||||
static void scsi_dma_complete(void *opaque, int ret)
|
||||
static void scsi_flush_complete(void * opaque, int ret)
|
||||
{
|
||||
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||
|
||||
bdrv_acct_done(s->qdev.conf.bs, &r->acct);
|
||||
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
if (scsi_handle_rw_error(r, -ret)) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
scsi_req_complete(&r->req, GOOD);
|
||||
|
||||
done:
|
||||
if (!r->req.io_canceled) {
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
}
|
||||
|
||||
static bool scsi_is_cmd_fua(SCSICommand *cmd)
|
||||
{
|
||||
switch (cmd->buf[0]) {
|
||||
case READ_10:
|
||||
case READ_12:
|
||||
case READ_16:
|
||||
case WRITE_10:
|
||||
case WRITE_12:
|
||||
case WRITE_16:
|
||||
return (cmd->buf[1] & 8) != 0;
|
||||
|
||||
case VERIFY_10:
|
||||
case VERIFY_12:
|
||||
case VERIFY_16:
|
||||
case WRITE_VERIFY_10:
|
||||
case WRITE_VERIFY_12:
|
||||
case WRITE_VERIFY_16:
|
||||
return true;
|
||||
|
||||
case READ_6:
|
||||
case WRITE_6:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void scsi_write_do_fua(SCSIDiskReq *r)
|
||||
{
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||
|
||||
if (scsi_is_cmd_fua(&r->req.cmd)) {
|
||||
bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH);
|
||||
r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_flush_complete, r);
|
||||
return;
|
||||
}
|
||||
|
||||
scsi_req_complete(&r->req, GOOD);
|
||||
if (!r->req.io_canceled) {
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
}
|
||||
|
||||
static void scsi_dma_complete(void *opaque, int ret)
|
||||
{
|
||||
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||
|
||||
if (r->req.aiocb != NULL) {
|
||||
r->req.aiocb = NULL;
|
||||
bdrv_acct_done(s->qdev.conf.bs, &r->acct);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
if (scsi_handle_rw_error(r, -ret)) {
|
||||
goto done;
|
||||
}
|
||||
|
@ -168,10 +235,17 @@ static void scsi_dma_complete(void *opaque, int ret)
|
|||
|
||||
r->sector += r->sector_count;
|
||||
r->sector_count = 0;
|
||||
scsi_req_complete(&r->req, GOOD);
|
||||
if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
|
||||
scsi_write_do_fua(r);
|
||||
return;
|
||||
} else {
|
||||
scsi_req_complete(&r->req, GOOD);
|
||||
}
|
||||
|
||||
done:
|
||||
scsi_req_unref(&r->req);
|
||||
if (!r->req.io_canceled) {
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
}
|
||||
|
||||
static void scsi_read_complete(void * opaque, int ret)
|
||||
|
@ -185,7 +259,7 @@ static void scsi_read_complete(void * opaque, int ret)
|
|||
bdrv_acct_done(s->qdev.conf.bs, &r->acct);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
if (scsi_handle_rw_error(r, -ret)) {
|
||||
goto done;
|
||||
}
|
||||
|
@ -204,10 +278,12 @@ done:
|
|||
}
|
||||
}
|
||||
|
||||
static void scsi_flush_complete(void * opaque, int ret)
|
||||
/* Actually issue a read to the block device. */
|
||||
static void scsi_do_read(void *opaque, int ret)
|
||||
{
|
||||
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
|
||||
SCSIDiskReq *r = opaque;
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||
uint32_t n;
|
||||
|
||||
if (r->req.aiocb != NULL) {
|
||||
r->req.aiocb = NULL;
|
||||
|
@ -220,7 +296,17 @@ static void scsi_flush_complete(void * opaque, int ret)
|
|||
}
|
||||
}
|
||||
|
||||
scsi_req_complete(&r->req, GOOD);
|
||||
if (r->req.sg) {
|
||||
dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_READ);
|
||||
r->req.resid -= r->req.sg->size;
|
||||
r->req.aiocb = dma_bdrv_read(s->qdev.conf.bs, r->req.sg, r->sector,
|
||||
scsi_dma_complete, r);
|
||||
} else {
|
||||
n = scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
|
||||
bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ);
|
||||
r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n,
|
||||
scsi_read_complete, r);
|
||||
}
|
||||
|
||||
done:
|
||||
if (!r->req.io_canceled) {
|
||||
|
@ -233,11 +319,12 @@ static void scsi_read_data(SCSIRequest *req)
|
|||
{
|
||||
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||
uint32_t n;
|
||||
bool first;
|
||||
|
||||
if (r->sector_count == (uint32_t)-1) {
|
||||
DPRINTF("Read buf_len=%zd\n", r->iov.iov_len);
|
||||
r->sector_count = 0;
|
||||
r->started = true;
|
||||
scsi_req_data(&r->req, r->iov.iov_len);
|
||||
return;
|
||||
}
|
||||
|
@ -264,16 +351,13 @@ static void scsi_read_data(SCSIRequest *req)
|
|||
return;
|
||||
}
|
||||
|
||||
if (r->req.sg) {
|
||||
dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_READ);
|
||||
r->req.resid -= r->req.sg->size;
|
||||
r->req.aiocb = dma_bdrv_read(s->qdev.conf.bs, r->req.sg, r->sector,
|
||||
scsi_dma_complete, r);
|
||||
first = !r->started;
|
||||
r->started = true;
|
||||
if (first && scsi_is_cmd_fua(&r->req.cmd)) {
|
||||
bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH);
|
||||
r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_do_read, r);
|
||||
} else {
|
||||
n = scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
|
||||
bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ);
|
||||
r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n,
|
||||
scsi_read_complete, r);
|
||||
scsi_do_read(r, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -333,7 +417,7 @@ static void scsi_write_complete(void * opaque, int ret)
|
|||
bdrv_acct_done(s->qdev.conf.bs, &r->acct);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
if (scsi_handle_rw_error(r, -ret)) {
|
||||
goto done;
|
||||
}
|
||||
|
@ -343,7 +427,8 @@ static void scsi_write_complete(void * opaque, int ret)
|
|||
r->sector += n;
|
||||
r->sector_count -= n;
|
||||
if (r->sector_count == 0) {
|
||||
scsi_req_complete(&r->req, GOOD);
|
||||
scsi_write_do_fua(r);
|
||||
return;
|
||||
} else {
|
||||
scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
|
||||
DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, r->qiov.size);
|
||||
|
@ -375,6 +460,7 @@ static void scsi_write_data(SCSIRequest *req)
|
|||
|
||||
if (!r->req.sg && !r->qiov.size) {
|
||||
/* Called for the first time. Ask the driver to send us more data. */
|
||||
r->started = true;
|
||||
scsi_write_complete(r, 0);
|
||||
return;
|
||||
}
|
||||
|
@ -383,6 +469,16 @@ static void scsi_write_data(SCSIRequest *req)
|
|||
return;
|
||||
}
|
||||
|
||||
if (r->req.cmd.buf[0] == VERIFY_10 || r->req.cmd.buf[0] == VERIFY_12 ||
|
||||
r->req.cmd.buf[0] == VERIFY_16) {
|
||||
if (r->req.sg) {
|
||||
scsi_dma_complete(r, 0);
|
||||
} else {
|
||||
scsi_write_complete(r, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (r->req.sg) {
|
||||
dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_WRITE);
|
||||
r->req.resid -= r->req.sg->size;
|
||||
|
@ -531,8 +627,8 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
|
|||
{
|
||||
outbuf[3] = buflen = 8;
|
||||
outbuf[4] = 0;
|
||||
outbuf[5] = 0x40; /* write same with unmap supported */
|
||||
outbuf[6] = 0;
|
||||
outbuf[5] = 0x60; /* write_same 10/16 supported */
|
||||
outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1;
|
||||
outbuf[7] = 0;
|
||||
break;
|
||||
}
|
||||
|
@ -984,11 +1080,12 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf)
|
|||
{
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||
uint64_t nb_sectors;
|
||||
int page, dbd, buflen, ret, page_control;
|
||||
bool dbd;
|
||||
int page, buflen, ret, page_control;
|
||||
uint8_t *p;
|
||||
uint8_t dev_specific_param;
|
||||
|
||||
dbd = r->req.cmd.buf[1] & 0x8;
|
||||
dbd = (r->req.cmd.buf[1] & 0x8) != 0;
|
||||
page = r->req.cmd.buf[2] & 0x3f;
|
||||
page_control = (r->req.cmd.buf[2] & 0xc0) >> 6;
|
||||
DPRINTF("Mode Sense(%d) (page %d, xfer %zd, page_control %d)\n",
|
||||
|
@ -996,10 +1093,16 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf)
|
|||
memset(outbuf, 0, r->req.cmd.xfer);
|
||||
p = outbuf;
|
||||
|
||||
if (bdrv_is_read_only(s->qdev.conf.bs)) {
|
||||
dev_specific_param = 0x80; /* Readonly. */
|
||||
if (s->qdev.type == TYPE_DISK) {
|
||||
dev_specific_param = 0x10; /* DPOFUA */
|
||||
if (bdrv_is_read_only(s->qdev.conf.bs)) {
|
||||
dev_specific_param |= 0x80; /* Readonly. */
|
||||
}
|
||||
} else {
|
||||
/* MMC prescribes that CD/DVD drives have no block descriptors,
|
||||
* and defines no device-specific parameter. */
|
||||
dev_specific_param = 0x00;
|
||||
dbd = true;
|
||||
}
|
||||
|
||||
if (r->req.cmd.buf[0] == MODE_SENSE) {
|
||||
|
@ -1014,9 +1117,8 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf)
|
|||
p += 8;
|
||||
}
|
||||
|
||||
/* MMC prescribes that CD/DVD drives have no block descriptors. */
|
||||
bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
|
||||
if (!dbd && s->qdev.type == TYPE_DISK && nb_sectors) {
|
||||
if (!dbd && nb_sectors) {
|
||||
if (r->req.cmd.buf[0] == MODE_SENSE) {
|
||||
outbuf[3] = 8; /* Block descriptor length */
|
||||
} else { /* MODE_SENSE_10 */
|
||||
|
@ -1306,8 +1408,6 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r)
|
|||
}
|
||||
DPRINTF("Unsupported Service Action In\n");
|
||||
goto illegal_request;
|
||||
case VERIFY_10:
|
||||
break;
|
||||
default:
|
||||
scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE));
|
||||
return -1;
|
||||
|
@ -1391,7 +1491,6 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
|
|||
case MECHANISM_STATUS:
|
||||
case SERVICE_ACTION_IN_16:
|
||||
case REQUEST_SENSE:
|
||||
case VERIFY_10:
|
||||
rc = scsi_disk_emulate_command(r);
|
||||
if (rc < 0) {
|
||||
return 0;
|
||||
|
@ -1417,6 +1516,9 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
|
|||
r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512);
|
||||
r->sector_count = len * (s->qdev.blocksize / 512);
|
||||
break;
|
||||
case VERIFY_10:
|
||||
case VERIFY_12:
|
||||
case VERIFY_16:
|
||||
case WRITE_6:
|
||||
case WRITE_10:
|
||||
case WRITE_12:
|
||||
|
@ -1456,10 +1558,11 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
|
|||
goto illegal_lba;
|
||||
}
|
||||
break;
|
||||
case WRITE_SAME_10:
|
||||
case WRITE_SAME_16:
|
||||
len = r->req.cmd.xfer / s->qdev.blocksize;
|
||||
|
||||
DPRINTF("WRITE SAME(16) (sector %" PRId64 ", count %d)\n",
|
||||
DPRINTF("WRITE SAME() (sector %" PRId64 ", count %d)\n",
|
||||
r->req.cmd.lba, len);
|
||||
|
||||
if (r->req.cmd.lba > s->qdev.max_lba) {
|
||||
|
@ -1767,6 +1870,9 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag,
|
|||
case READ_10:
|
||||
case READ_12:
|
||||
case READ_16:
|
||||
case VERIFY_10:
|
||||
case VERIFY_12:
|
||||
case VERIFY_16:
|
||||
case WRITE_6:
|
||||
case WRITE_10:
|
||||
case WRITE_12:
|
||||
|
|
|
@ -129,12 +129,12 @@ typedef struct {
|
|||
VirtIOSCSIConf *conf;
|
||||
|
||||
SCSIBus bus;
|
||||
VirtQueue *ctrl_vq;
|
||||
VirtQueue *event_vq;
|
||||
VirtQueue *cmd_vq;
|
||||
uint32_t sense_size;
|
||||
uint32_t cdb_size;
|
||||
int resetting;
|
||||
VirtQueue *ctrl_vq;
|
||||
VirtQueue *event_vq;
|
||||
VirtQueue *cmd_vqs[0];
|
||||
} VirtIOSCSI;
|
||||
|
||||
typedef struct VirtIOSCSIReq {
|
||||
|
@ -240,7 +240,10 @@ static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq)
|
|||
static void virtio_scsi_save_request(QEMUFile *f, SCSIRequest *sreq)
|
||||
{
|
||||
VirtIOSCSIReq *req = sreq->hba_private;
|
||||
uint32_t n = virtio_queue_get_id(req->vq) - 2;
|
||||
|
||||
assert(n < req->dev->conf->num_queues);
|
||||
qemu_put_be32s(f, &n);
|
||||
qemu_put_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem));
|
||||
}
|
||||
|
||||
|
@ -249,10 +252,13 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq)
|
|||
SCSIBus *bus = sreq->bus;
|
||||
VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
|
||||
VirtIOSCSIReq *req;
|
||||
uint32_t n;
|
||||
|
||||
req = g_malloc(sizeof(*req));
|
||||
qemu_get_be32s(f, &n);
|
||||
assert(n < s->conf->num_queues);
|
||||
qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem));
|
||||
virtio_scsi_parse_req(s, s->cmd_vq, req);
|
||||
virtio_scsi_parse_req(s, s->cmd_vqs[n], req);
|
||||
|
||||
scsi_req_ref(sreq);
|
||||
req->sreq = sreq;
|
||||
|
@ -579,10 +585,12 @@ VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *proxyconf)
|
|||
{
|
||||
VirtIOSCSI *s;
|
||||
static int virtio_scsi_id;
|
||||
size_t sz;
|
||||
int i;
|
||||
|
||||
sz = sizeof(VirtIOSCSI) + proxyconf->num_queues * sizeof(VirtQueue *);
|
||||
s = (VirtIOSCSI *)virtio_common_init("virtio-scsi", VIRTIO_ID_SCSI,
|
||||
sizeof(VirtIOSCSIConfig),
|
||||
sizeof(VirtIOSCSI));
|
||||
sizeof(VirtIOSCSIConfig), sz);
|
||||
|
||||
s->qdev = dev;
|
||||
s->conf = proxyconf;
|
||||
|
@ -597,8 +605,10 @@ VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *proxyconf)
|
|||
virtio_scsi_handle_ctrl);
|
||||
s->event_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE,
|
||||
NULL);
|
||||
s->cmd_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE,
|
||||
virtio_scsi_handle_cmd);
|
||||
for (i = 0; i < s->conf->num_queues; i++) {
|
||||
s->cmd_vqs[i] = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE,
|
||||
virtio_scsi_handle_cmd);
|
||||
}
|
||||
|
||||
scsi_bus_new(&s->bus, dev, &virtio_scsi_scsi_info);
|
||||
if (!dev->hotplugged) {
|
||||
|
|
|
@ -624,6 +624,13 @@ int virtio_queue_get_num(VirtIODevice *vdev, int n)
|
|||
return vdev->vq[n].vring.num;
|
||||
}
|
||||
|
||||
int virtio_queue_get_id(VirtQueue *vq)
|
||||
{
|
||||
VirtIODevice *vdev = vq->vdev;
|
||||
assert(vq >= &vdev->vq[0] && vq < &vdev->vq[VIRTIO_PCI_QUEUE_MAX]);
|
||||
return vq - &vdev->vq[0];
|
||||
}
|
||||
|
||||
void virtio_queue_notify_vq(VirtQueue *vq)
|
||||
{
|
||||
if (vq->vring.desc) {
|
||||
|
|
|
@ -229,6 +229,7 @@ target_phys_addr_t virtio_queue_get_ring_size(VirtIODevice *vdev, int n);
|
|||
uint16_t virtio_queue_get_last_avail_idx(VirtIODevice *vdev, int n);
|
||||
void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx);
|
||||
VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n);
|
||||
int virtio_queue_get_id(VirtQueue *vq);
|
||||
EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq);
|
||||
EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq);
|
||||
void virtio_queue_notify_vq(VirtQueue *vq);
|
||||
|
|
Loading…
Reference in New Issue