On 2013年03月04日 14:01, Han Cheng wrote:
For scsi-generic, the command line will be like:
-drive file=/dev/sg0,if=none,id=drive-hostdev-scsi0-0-0-0 -device
scsi-generic,bus=scsi0.0,channel=0,scsi-id=4,lun=8,drive=drive-hostdev-scsi0-0-0-0,id=hostdev-scsi0-0-0-0
The relationship between the libvirt address attrs and the qdev
properties are(channel should always be 0):
bus=scsi<controller>.0
scsi-id=<target>
lun=<unit>
---
src/qemu/qemu_command.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 153 insertions(+), 3 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 5eb9999..1d2540e 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -49,6 +49,8 @@
#include<sys/stat.h>
#include<fcntl.h>
+#include<dirent.h>
+#include<sys/types.h>
#define VIR_FROM_THIS VIR_FROM_QEMU
@@ -651,7 +653,16 @@ qemuAssignDeviceHostdevAlias(virDomainDefPtr def,
virDomainHostdevDefPtr hostdev
}
}
- if (virAsprintf(&hostdev->info->alias, "hostdev%d", idx)< 0)
{
+ if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) {
+ if (virAsprintf(&hostdev->info->alias,
"hostdev-scsi%d-%d-%d-%d",
+ hostdev->source.subsys.u.scsi.adapter,
+ hostdev->source.subsys.u.scsi.bus,
+ hostdev->source.subsys.u.scsi.target,
+ hostdev->source.subsys.u.scsi.unit)< 0) {
+ virReportOOMError();
+ return -1;
+ }
+ } else if (virAsprintf(&hostdev->info->alias, "hostdev%d",
idx)< 0) {
virReportOOMError();
return -1;
}
@@ -3864,6 +3875,110 @@ qemuBuildUSBHostdevUsbDevStr(virDomainHostdevDefPtr dev)
return ret;
}
+static int
+scsiGetDeviceId(unsigned int adapter,
+ unsigned int bus,
+ unsigned int target,
+ unsigned int unit)
This should be a util function, and note that it's Linux platform
specific.
+{
+ DIR *dir = NULL;
+ struct dirent *entry;
+ char *path;
+ int id;
s/int/unsigned int/,
+
+ if (virAsprintf(&path,
+
"/sys/bus/scsi/devices/target%d:%d:%d/%d:%d:%d:%d/scsi_generic",
+ adapter, bus, target, adapter, bus, target, unit)< 0) {
+ virReportOOMError();
+ return -1;
+ }
+ dir = opendir(path);
+ VIR_FREE(path);
+ if (!dir) {
if (!(dir = opendir(path)))
+ VIR_WARN("Failed to open %s", path);
Any special reason for this to be a warning, but not an error
instead?
+ return -1;
+ }
+
+ while ((entry = readdir(dir))) {
+ if (entry->d_name[0] == '.')
+ continue;
+
+ if (sscanf(entry->d_name, "sg%d",&id) != 1) {
+ VIR_WARN("Failed to analyse sg id");
+ return -1;
Since here it returns -1, above should be an error.
+ }
+ }
+
+ return id;
+}
+
+static char *
+qemuBuildSCSIHostdevDrvStr(virDomainHostdevDefPtr dev, virQEMUCapsPtr qemuCaps)
+{
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ int scsiid;
+
+ if ((scsiid = scsiGetDeviceId(dev->source.subsys.u.scsi.adapter,
+ dev->source.subsys.u.scsi.bus,
+ dev->source.subsys.u.scsi.target,
+ dev->source.subsys.u.scsi.unit))< 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("can't get sg* number"));
"*" is magic in the error message. s/sg\*/sg/,
And actually if you throw error in scsiGetDeviceId, there is no need
to report error here anymore.
+ goto error;
+ }
+
+ virBufferAsprintf(&buf, "file=/dev/sg%d,if=none", scsiid);
+ virBufferAsprintf(&buf, ",id=%s-%s",
+ virDomainDeviceAddressTypeToString(dev->info->type),
+ dev->info->alias);
+ if (dev->info->readonly&&
+ virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_READONLY))
+ virBufferAsprintf(&buf, ",readonly=on");
+
+ return virBufferContentAndReset(&buf);
Should check if there is any error of the buf here. Something like:
if (virBufferError(&buf)) {
virReportOOMErrorr();
goto error;
}
+error:
+ virBufferFreeAndReset(&buf);
+ return NULL;
+}
+
+
+static char *
+qemuBuildSCSIHostdevDevStr(virDomainDefPtr def, virDomainHostdevDefPtr dev,
+ virQEMUCapsPtr qemuCaps)
+{
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ int controllerModel = -1;
+
+ controllerModel = virDomainInfoFindControllerModel(def, dev->info,
+
VIR_DOMAIN_CONTROLLER_TYPE_SCSI);
+ if (qemuSetScsiControllerModel(def, qemuCaps,&controllerModel)< 0)
+ goto error;
+ /* TODO: deal with lsi or ibm controller */
+ if (controllerModel == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI) {
+ virBufferAsprintf(&buf, "scsi-generic");
+ if (dev->info->addr.drive.bus != 0)
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("SCSI controller only supports 1
bus"));
XML_ERROR here might be better.
+ /* TODO: deal with early version qemu which does not support
bus... */
+ virBufferAsprintf(&buf,
+ ",bus=scsi%d.0,channel=0,scsi-id=%d,lun=%d",
+ dev->info->addr.drive.controller,
+ dev->info->addr.drive.target,
+ dev->info->addr.drive.unit);
+ virBufferAsprintf(&buf, ",drive=%s-%s,id=%s",
+ virDomainDeviceAddressTypeToString(dev->info->type),
+ dev->info->alias, dev->info->alias);
+ if (dev->info->bootIndex)
+ virBufferAsprintf(&buf, ",bootindex=%d",
dev->info->bootIndex);
+ } else {
+ goto error;
This is magic, you need to give an error message here.
+ }
+
+ return virBufferContentAndReset(&buf);
Need to check if there is error of buf too here.
+error:
+ virBufferFreeAndReset(&buf);
+ return NULL;
+}
/* This function outputs a -chardev command line option which describes only the
@@ -6953,10 +7068,11 @@ qemuBuildCommandLine(virConnectPtr conn,
if (hostdev->info->bootIndex) {
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
(hostdev->source.subsys.type !=
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI&&
- hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB))
{
+ hostdev->source.subsys.type !=
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB&&
+ hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI))
{
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("booting from assigned devices is only"
- " supported for PCI and USB devices"));
+ " supported for PCI, USB and SCSI
devices"));
goto error;
} else {
if (hostdev->source.subsys.type ==
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI&&
@@ -6973,6 +7089,40 @@ qemuBuildCommandLine(virConnectPtr conn,
" supported with this version of
qemu"));
goto error;
}
+ if (hostdev->source.subsys.type ==
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI&&
+ !virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_HOST_BOOTINDEX)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("booting from assigned SCSI devices is
not"
+ " supported with this version of
qemu"));
+ goto error;
+ }
+ }
+ }
+
+ /* SCSI */
+ if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS&&
+ hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) {
+ /* TODO */
+ if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE)&&
+ virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)&&
+ virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_GENERIC)) {
+ char *drvstr;
+
+ virCommandAddArg(cmd, "-drive");
+ if (!(drvstr = qemuBuildSCSIHostdevDrvStr(hostdev, qemuCaps)))
+ goto error;
+ virCommandAddArg(cmd, drvstr);
+ VIR_FREE(drvstr);
+
+ virCommandAddArg(cmd, "-device");
+ if (!(devstr = qemuBuildSCSIHostdevDevStr(def, hostdev, qemuCaps)))
+ goto error;
+ virCommandAddArg(cmd, devstr);
+ VIR_FREE(devstr);
+ } else {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("SCSI assignment is not supported by this version
of qemu"));
In libvirt, we use "passthrough", instead of "assignment".
+ goto error;
}
}