VIR_FREE(data->httpcookiesecretAlias);
VIR_FREE(data->driveCmd);
VIR_FREE(data->driveAlias);
+ VIR_FREE(data->chardevAlias);
+ VIR_FREE(data->chardevCmd);
VIR_FREE(data);
}
data->driveAdded = true;
}
+ if (data->chardevDef) {
+ if (qemuMonitorAttachCharDev(mon, data->chardevAlias, data->chardevDef) < 0)
+ return -1;
+
+ data->chardevAdded = true;
+ }
+
return 0;
}
virErrorPreserveLast(&orig_err);
+ if (data->chardevAdded) {
+ if (qemuMonitorDetachCharDev(mon, data->chardevAlias) < 0) {
+ VIR_WARN("Unable to remove chardev %s after failed " "qemuMonitorAddDevice",
+ data->chardevAlias);
+ }
+ }
+
if (data->driveAdded) {
if (qemuMonitorDriveDel(mon, data->driveAlias) < 0)
VIR_WARN("Unable to remove drive %s (%s) after failed "
}
+/**
+ * qemuBlockStorageSourceChainDetachPrepareChardev
+ * @src: storage source chain to remove
+ *
+ * Prepares qemuBlockStorageSourceChainDataPtr for detaching @src and its
+ * backingStore if -chardev was used.
+ */
+qemuBlockStorageSourceChainDataPtr
+qemuBlockStorageSourceChainDetachPrepareChardev(char *chardevAlias)
+{
+ g_autoptr(qemuBlockStorageSourceAttachData) backend = NULL;
+ g_autoptr(qemuBlockStorageSourceChainData) data = NULL;
+
+ data = g_new0(qemuBlockStorageSourceChainData, 1);
+ backend = g_new0(qemuBlockStorageSourceAttachData, 1);
+
+ backend->chardevAlias = chardevAlias;
+ backend->chardevAdded = true;
+
+ if (VIR_APPEND_ELEMENT(data->srcdata, data->nsrcdata, backend) < 0)
+ return NULL;
+
+ return g_steal_pointer(&data);
+}
+
+
/**
* qemuBlockStorageSourceChainAttach:
* @mon: monitor object
char *driveAlias;
bool driveAdded;
+ virDomainChrSourceDefPtr chardevDef;
+ char *chardevAlias;
+ char *chardevCmd;
+ bool chardevAdded;
+
virJSONValuePtr authsecretProps;
char *authsecretAlias;
qemuBlockStorageSourceChainDataPtr
qemuBlockStorageSourceChainDetachPrepareDrive(virStorageSourcePtr src,
char *driveAlias);
+qemuBlockStorageSourceChainDataPtr
+qemuBlockStorageSourceChainDetachPrepareChardev(char *chardevAlias);
int
qemuBlockStorageSourceChainAttach(qemuMonitorPtr mon,
break;
case VIR_DOMAIN_DISK_BUS_VIRTIO:
- if (qemuBuildVirtioDevStr(&opt, "virtio-blk", qemuCaps,
- VIR_DOMAIN_DEVICE_DISK, disk) < 0) {
- return NULL;
+ if (virStorageSourceGetActualType(disk->src) == VIR_STORAGE_TYPE_VHOST_USER) {
+ if (qemuBuildVirtioDevStr(&opt, "vhost-user-blk", qemuCaps,
+ VIR_DOMAIN_DEVICE_DISK, disk) < 0) {
+ return NULL;
+ }
+ } else {
+ if (qemuBuildVirtioDevStr(&opt, "virtio-blk", qemuCaps,
+ VIR_DOMAIN_DEVICE_DISK, disk) < 0) {
+ return NULL;
+ }
}
if (disk->iothread)
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DISK_SHARE_RW))
virBufferAddLit(&opt, ",share-rw=on");
- if (qemuDomainDiskGetBackendAlias(disk, qemuCaps, &backendAlias) < 0)
- return NULL;
+ if (virStorageSourceGetActualType(disk->src) == VIR_STORAGE_TYPE_VHOST_USER) {
+ backendAlias = qemuDomainGetVhostUserChrAlias(disk->info.alias);
- if (backendAlias)
- virBufferAsprintf(&opt, ",drive=%s", backendAlias);
+ virBufferAsprintf(&opt, ",chardev=%s", backendAlias);
+ } else {
+ if (qemuDomainDiskGetBackendAlias(disk, qemuCaps, &backendAlias) < 0)
+ return NULL;
+
+ if (backendAlias)
+ virBufferAsprintf(&opt, ",drive=%s", backendAlias);
+ }
virBufferAsprintf(&opt, ",id=%s", disk->info.alias);
if (bootindex)
if (data->driveCmd)
virCommandAddArgList(cmd, "-drive", data->driveCmd, NULL);
+ if (data->chardevCmd)
+ virCommandAddArgList(cmd, "-chardev", data->chardevCmd, NULL);
+
if (data->storageProps) {
if (!(tmp = virJSONValueToString(data->storageProps, false)))
return -1;
g_autofree char *copyOnReadPropsStr = NULL;
size_t i;
- if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_BLOCKDEV) &&
+ if (virStorageSourceGetActualType(disk->src) == VIR_STORAGE_TYPE_VHOST_USER) {
+ if (!(data = qemuBuildStorageSourceChainAttachPrepareChardev(disk)))
+ return -1;
+ } else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_BLOCKDEV) &&
!qemuDiskBusIsSD(disk->bus)) {
if (virStorageSourceIsEmpty(disk->src))
return 0;
}
+/**
+ * qemuBuildStorageSourceAttachPrepareChardev:
+ * @src: disk source to prepare
+ *
+ * Prepare qemuBlockStorageSourceAttachDataPtr for vhost-user disk
+ * to be used with -chardev.
+ */
+qemuBlockStorageSourceAttachDataPtr
+qemuBuildStorageSourceAttachPrepareChardev(virDomainDiskDefPtr disk)
+{
+ g_autoptr(qemuBlockStorageSourceAttachData) data = NULL;
+ g_auto(virBuffer) chardev = VIR_BUFFER_INITIALIZER;
+
+ data = g_new0(qemuBlockStorageSourceAttachData, 1);
+
+ data->chardevDef = disk->src->vhostuser;
+ data->chardevAlias = qemuDomainGetVhostUserChrAlias(disk->info.alias);
+
+ virBufferAddLit(&chardev, "socket");
+ virBufferAsprintf(&chardev, ",id=%s", data->chardevAlias);
+ virBufferAddLit(&chardev, ",path=");
+ virQEMUBuildBufferEscapeComma(&chardev, disk->src->vhostuser->data.nix.path);
+
+ qemuBuildChrChardevReconnectStr(&chardev,
+ &disk->src->vhostuser->data.nix.reconnect);
+
+ if (!(data->chardevCmd = virBufferContentAndReset(&chardev)))
+ return NULL;
+
+ return g_steal_pointer(&data);
+}
+
+
/**
* qemuBuildStorageSourceAttachPrepareCommon:
* @src: storage source
}
+/**
+ * qemuBuildStorageSourceChainAttachPrepareChardev:
+ * @src: disk definition
+ *
+ * Prepares qemuBlockStorageSourceChainDataPtr for attaching a vhost-user
+ * disk's backend via -chardev.
+ */
+qemuBlockStorageSourceChainDataPtr
+qemuBuildStorageSourceChainAttachPrepareChardev(virDomainDiskDefPtr disk)
+{
+ g_autoptr(qemuBlockStorageSourceAttachData) elem = NULL;
+ g_autoptr(qemuBlockStorageSourceChainData) data = NULL;
+
+ data = g_new0(qemuBlockStorageSourceChainData, 1);
+
+ if (!(elem = qemuBuildStorageSourceAttachPrepareChardev(disk)))
+ return NULL;
+
+ if (VIR_APPEND_ELEMENT(data->srcdata, data->nsrcdata, elem) < 0)
+ return NULL;
+
+ return g_steal_pointer(&data);
+}
+
+
static int
qemuBuildStorageSourceChainAttachPrepareBlockdevOne(qemuBlockStorageSourceChainData *data,
virStorageSourcePtr src,
qemuBlockStorageSourceAttachDataPtr
qemuBuildStorageSourceAttachPrepareDrive(virDomainDiskDefPtr disk,
virQEMUCapsPtr qemuCaps);
+
+qemuBlockStorageSourceAttachDataPtr
+qemuBuildStorageSourceAttachPrepareChardev(virDomainDiskDefPtr disk);
+
int
qemuBuildStorageSourceAttachPrepareCommon(virStorageSourcePtr src,
qemuBlockStorageSourceAttachDataPtr data,
virQEMUCapsPtr qemuCaps);
+qemuBlockStorageSourceChainDataPtr
+qemuBuildStorageSourceChainAttachPrepareChardev(virDomainDiskDefPtr disk);
+
+
qemuBlockStorageSourceChainDataPtr
qemuBuildStorageSourceChainAttachPrepareBlockdev(virStorageSourcePtr top,
virQEMUCapsPtr qemuCaps);
qemuDomainObjPrivatePtr priv,
virQEMUDriverConfigPtr cfg)
{
+ /* Nothing to prepare as it will use -chardev instead
+ * of -blockdev/-drive option. */
+ if (disk->src->type == VIR_STORAGE_TYPE_VHOST_USER)
+ return 0;
+
qemuDomainPrepareDiskCachemode(disk);
/* set default format for storage pool based disks */
return false;
}
+ if (virStorageSourceGetActualType(disk->src) == VIR_STORAGE_TYPE_VHOST_USER) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
+ _("block jobs are not supported on vhostuser disk '%s'"),
+ disk->dst);
+ return false;
+ }
+
return true;
}
goto endjob;
}
+ if (virStorageSourceGetActualType(disk->src) == VIR_STORAGE_TYPE_VHOST_USER) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+ _("block resize is not supported for vhostuser disk"));
+ goto endjob;
+ }
+
/* qcow2 and qed must be sized on 512 byte blocks/sectors,
* so adjust size if necessary to round up.
*/
goto cleanup;
}
+ if (virStorageSourceGetActualType(disk->src) == VIR_STORAGE_TYPE_VHOST_USER) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+ _("block stats are not supported for vhostuser disk"));
+ goto cleanup;
+ }
+
if (blockdev && QEMU_DOMAIN_DISK_PRIVATE(disk)->qomName) {
entryname = QEMU_DOMAIN_DISK_PRIVATE(disk)->qomName;
} else {
disk = vm->def->disks[i];
entryname = disk->info.alias;
+ /* No stats to report for vhost-user disk */
+ if (virStorageSourceGetActualType(disk->src) == VIR_STORAGE_TYPE_VHOST_USER)
+ continue;
+
if (blockdev && QEMU_DOMAIN_DISK_PRIVATE(disk)->qomName)
entryname = QEMU_DOMAIN_DISK_PRIVATE(disk)->qomName;
if (!(disk = qemuDomainDiskByName(vm->def, path)))
goto cleanup;
+ if (virStorageSourceGetActualType(disk->src) == VIR_STORAGE_TYPE_VHOST_USER) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+ _("peeking is not supported for vhostuser disk"));
+ goto cleanup;
+ }
+
if (disk->src->format != VIR_STORAGE_FILE_RAW) {
virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
_("peeking is only supported for disk with 'raw' format not '%s'"),
goto endjob;
}
+ if (virStorageSourceGetActualType(disk->src) == VIR_STORAGE_TYPE_VHOST_USER) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+ _("block info is not supported for vhostuser disk"));
+ goto endjob;
+ }
+
if (virStorageSourceIsEmpty(disk->src)) {
virReportError(VIR_ERR_INVALID_ARG,
_("disk '%s' does not currently have a source assigned"),
} qemuBlockIoTuneSetFlags;
+static bool
+qemuDomainDiskBlockIoTuneIsSupported(virStorageSourcePtr src)
+{
+ if (virStorageSourceGetActualType(src) == VIR_STORAGE_TYPE_VHOST_USER) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("a block I/O throttling is not supported for vhostuser disk"));
+ return false;
+ }
+
+ return true;
+}
+
+
/* If the user didn't specify bytes limits, inherit previous values;
* likewise if the user didn't specify iops limits. */
static int
if (!(disk = qemuDomainDiskByName(def, path)))
goto endjob;
+ if (!qemuDomainDiskBlockIoTuneIsSupported(disk->src))
+ goto endjob;
+
if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV) &&
QEMU_DOMAIN_DISK_PRIVATE(disk)->qomName) {
qdevid = QEMU_DOMAIN_DISK_PRIVATE(disk)->qomName;
goto endjob;
}
+ if (!qemuDomainDiskBlockIoTuneIsSupported(conf_disk->src))
+ goto endjob;
+
conf_cur_info = qemuDomainFindGroupBlockIoTune(persistentDef, conf_disk, &info);
if (qemuDomainSetBlockIoTuneDefaults(&conf_info, conf_cur_info,
if (!(disk = qemuDomainDiskByName(def, path)))
goto endjob;
+ if (!qemuDomainDiskBlockIoTuneIsSupported(disk->src))
+ goto endjob;
+
if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV) &&
QEMU_DOMAIN_DISK_PRIVATE(disk)->qomName) {
qdevid = QEMU_DOMAIN_DISK_PRIVATE(disk)->qomName;
path);
goto endjob;
}
+
+ if (!qemuDomainDiskBlockIoTuneIsSupported(disk->src))
+ goto endjob;
+
reply = disk->blkdeviotune;
/* Group name needs to be copied since qemuMonitorGetBlockIoThrottle
params);
}
+ /* vhost-user disk doesn't support getting block stats */
+ if (virStorageSourceGetActualType(disk->src) == VIR_STORAGE_TYPE_VHOST_USER) {
+ if (qemuDomainGetStatsBlockExportHeader(disk, disk->src, *recordnr,
+ params) < 0) {
+ return -1;
+ }
+
+ (*recordnr)++;
+
+ return 0;
+ }
+
for (n = disk->src; virStorageSourceIsBacking(n); n = n->backingStore) {
g_autofree char *alias = NULL;
if (!(src = qemuDomainGetStorageSourceByDevstr(dev, vm->def, priv->backup)))
goto endjob;
+ if (virStorageSourceGetActualType(src) == VIR_STORAGE_TYPE_VHOST_USER) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+ _("setting device threshold is not supported for vhostuser disk"));
+ goto endjob;
+ }
+
if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV) &&
!src->nodestorage &&
qemuBlockNodeNamesDetect(driver, vm, QEMU_ASYNC_JOB_NONE) < 0)
if (qemuDomainPrepareDiskSource(disk, priv, cfg) < 0)
goto cleanup;
- if (blockdev) {
+ if (virStorageSourceGetActualType(disk->src) == VIR_STORAGE_TYPE_VHOST_USER) {
+ if (!(data = qemuBuildStorageSourceChainAttachPrepareChardev(disk)))
+ goto cleanup;
+ } else if (blockdev) {
if (disk->copy_on_read == VIR_TRISTATE_SWITCH_ON) {
if (!(corProps = qemuBlockStorageGetCopyOnReadProps(disk)))
goto cleanup;
disk->info.alias, vm, vm->def->name);
- if (blockdev &&
- !qemuDiskBusIsSD(disk->bus)) {
+ if (virStorageSourceGetActualType(disk->src) == VIR_STORAGE_TYPE_VHOST_USER) {
+ char *chardevAlias = qemuDomainGetVhostUserChrAlias(disk->info.alias);
+
+ if (!(diskBackend = qemuBlockStorageSourceChainDetachPrepareChardev(chardevAlias)))
+ goto cleanup;
+ } else if (blockdev &&
+ !qemuDiskBusIsSD(disk->bus)) {
corAlias = g_strdup(diskPriv->nodeCopyOnRead);
if (diskPriv->blockjob) {
return -1;
}
+ if (disk->src->type == VIR_STORAGE_TYPE_VHOST_USER) {
+ if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VHOST_USER_BLK)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("vhostuser disk is not supported with this QEMU binary"));
+ return -1;
+ }
+
+ if (qemuValidateDomainDefVhostUserRequireSharedMemory(def, "vhostuser",
+ qemuCaps) < 0) {
+ return -1;
+ }
+ }
+
return 0;
}
--- /dev/null
+LC_ALL=C \
+PATH=/bin \
+HOME=/tmp/lib/domain--1-QEMUGuest1 \
+USER=test \
+LOGNAME=test \
+XDG_DATA_HOME=/tmp/lib/domain--1-QEMUGuest1/.local/share \
+XDG_CACHE_HOME=/tmp/lib/domain--1-QEMUGuest1/.cache \
+XDG_CONFIG_HOME=/tmp/lib/domain--1-QEMUGuest1/.config \
+QEMU_AUDIO_DRV=none \
+/usr/bin/qemu-system-x86_64 \
+-name guest=QEMUGuest1,debug-threads=on \
+-S \
+-object secret,id=masterKey0,format=raw,\
+file=/tmp/lib/domain--1-QEMUGuest1/master-key.aes \
+-machine pc,accel=tcg,usb=off,dump-guest-core=off,memory-backend=pc.ram \
+-cpu qemu64 \
+-m 214 \
+-object memory-backend-memfd,id=pc.ram,share=yes,size=224395264 \
+-overcommit mem-lock=off \
+-smp 1,sockets=1,cores=1,threads=1 \
+-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \
+-display none \
+-no-user-config \
+-nodefaults \
+-chardev socket,id=charmonitor,fd=1729,server,nowait \
+-mon chardev=charmonitor,id=monitor,mode=control \
+-rtc base=utc \
+-no-shutdown \
+-no-acpi \
+-boot strict=on \
+-device piix3-usb-uhci,id=usb,bus=pci.0,addr=0x1.0x2 \
+-chardev socket,id=chr-vu-virtio-disk0,path=/tmp/vhost1.sock \
+-device vhost-user-blk-pci,bus=pci.0,addr=0x2,chardev=chr-vu-virtio-disk0,\
+id=virtio-disk0,bootindex=1 \
+-chardev socket,id=chr-vu-virtio-disk1,path=/tmp/vhost1.sock,reconnect=10 \
+-device vhost-user-blk-pci,bus=pci.0,addr=0x3,chardev=chr-vu-virtio-disk1,\
+id=virtio-disk1 \
+-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x4 \
+-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,\
+resourcecontrol=deny \
+-msg timestamp=on
VIR_FREE(driver.config->vxhsTLSx509certdir);
DO_TEST("disk-no-boot", NONE);
DO_TEST_CAPS_LATEST("disk-nvme");
+ DO_TEST_CAPS_LATEST("disk-vhostuser");
DO_TEST_PARSE_ERROR("disk-device-lun-type-invalid",
QEMU_CAPS_VIRTIO_SCSI);
DO_TEST_CAPS_LATEST_PARSE_ERROR("disk-attaching-partition-nosupport");