This patch implements PCI device passthrough for Xen. In pci.c there is a fair bit of refactoring to better support both pcistub.ko and pciback.ko. The latter has two extra sysfs files, add_slot and remove_slot for associating the driver with a specific PCI device domain:bus:slot:func address, whereas pcistub can only associate based on vendor:product ID The Xen driver gets an implementation of the dettach/reattach APIs for node devices. The XML <-> SEXPR conversion gets adapted to cope with PCI devices The XM <-> XML conversion gets adapted to load/save PCI device info in /etc/xen config files Daniel Index: src/pci.c =================================================================== RCS file: /data/cvs/libvirt/src/pci.c,v retrieving revision 1.4 diff -u -p -r1.4 pci.c --- src/pci.c 3 Mar 2009 17:00:18 -0000 1.4 +++ src/pci.c 30 Mar 2009 13:09:44 -0000 @@ -614,46 +614,80 @@ pciResetDevice(virConnectPtr conn, pciDe return ret; } -static int -pciBindDeviceToStub(virConnectPtr conn, pciDevice *dev, const char *stub_module) + +static void +pciDriverDir(char *buf, size_t buflen, const char *driver) { - char stub_dir[PATH_MAX]; - char path[PATH_MAX]; + snprintf(buf, buflen, PCI_SYSFS "drivers/%s", driver); +} - snprintf(stub_dir, sizeof(stub_dir), PCI_SYSFS "drivers/%s", stub_module); +static void +pciDriverFile(char *buf, size_t buflen, const char *driver, const char *file) +{ + snprintf(buf, buflen, PCI_SYSFS "drivers/%s/%s", driver, file); +} + +static void +pciDeviceFile(char *buf, size_t buflen, const char *device, const char *file) +{ + snprintf(buf, buflen, PCI_SYSFS "devices/%s/%s", device, file); +} - /* Try loading the stub module if it isn't already loaded; - * Do not return an error if the stub module is not available. - */ - if (!virFileExists(stub_dir)) { - const char *const modprobeargv[] = { MODPROBE, stub_module, NULL }; - if (virRun(conn, modprobeargv, NULL) < 0) { +static const char * +pciFindStubDriver(virConnectPtr conn) +{ + char drvpath[PATH_MAX]; + int probed = 0; + +recheck: + pciDriverDir(drvpath, sizeof(drvpath), "pci-stub"); + if (virFileExists(drvpath)) + return "pci-stub"; + pciDriverDir(drvpath, sizeof(drvpath), "pciback"); + if (virFileExists(drvpath)) + return "pciback"; + + if (!probed) { + const char *const stubprobe[] = { MODPROBE, "pci-stub", NULL }; + const char *const backprobe[] = { MODPROBE, "pciback", NULL }; + + probed = 1; + if (virRun(conn, stubprobe, NULL) < 0 && + virRun(conn, backprobe, NULL) < 0) { char ebuf[1024]; - VIR_WARN(_("modprobe %s failed: %s"), stub_module, + VIR_WARN(_("failed to load pci-stub or pciback drivers: %s"), virStrerror(errno, ebuf, sizeof ebuf)); + return 0; } + + goto recheck; } - if (!virFileExists(stub_dir)) { - VIR_WARN(_("%s module not available, cannot bind device %s to it"), - stub_module, dev->name); - } else { - /* Add the PCI device ID to the stub's dynamic ID table; - * this is needed to allow us to bind the device to the stub. - * Note: if the device is not currently bound to any driver, - * stub will immediately be bound to the device. Also, note - * that if a new device with this ID is hotplugged, or if a probe - * is triggered for such a device, it will also be immediately - * bound by the stub. - */ - snprintf(path, sizeof(path), "%s/new_id", stub_dir); - if (virFileWriteStr(path, dev->id) < 0) { - virReportSystemError(conn, errno, - _("Failed to add PCI device ID '%s' to %s"), - dev->id, stub_module); - return -1; - } + return NULL; +} + + +static int +pciBindDeviceToStub(virConnectPtr conn, pciDevice *dev, const char *driver) +{ + char drvdir[PATH_MAX]; + char path[PATH_MAX]; + + /* Add the PCI device ID to the stub's dynamic ID table; + * this is needed to allow us to bind the device to the stub. + * Note: if the device is not currently bound to any driver, + * stub will immediately be bound to the device. Also, note + * that if a new device with this ID is hotplugged, or if a probe + * is triggered for such a device, it will also be immediately + * bound by the stub. + */ + pciDriverFile(path, sizeof(path), driver, "new_id"); + if (virFileWriteStr(path, dev->id) < 0) { + virReportSystemError(conn, errno, + _("Failed to add PCI device ID '%s' to %s"), + dev->id, driver); + return -1; } /* If the device is already bound to a driver, unbind it. @@ -661,77 +695,100 @@ pciBindDeviceToStub(virConnectPtr conn, * PCI device happens to be IDE controller for the disk hosting * your root filesystem. */ - snprintf(path, sizeof(path), - PCI_SYSFS "devices/%s/driver/unbind", dev->name); + pciDeviceFile(path, sizeof(path), dev->name, "driver/unbind"); if (virFileExists(path) && virFileWriteStr(path, dev->name) < 0) { virReportSystemError(conn, errno, _("Failed to unbind PCI device '%s'"), dev->name); return -1; } - if (virFileExists(stub_dir)) { - /* If the device isn't already bound to pci-stub, try binding it now. - */ - snprintf(path, sizeof(path), PCI_SYSFS "devices/%s/driver", dev->name); - if (!virFileLinkPointsTo(path, stub_dir)) { - snprintf(path, sizeof(path), "%s/bind", stub_dir); - if (virFileWriteStr(path, dev->name) < 0) { - virReportSystemError(conn, errno, - _("Failed to bind PCI device '%s' to %s"), - dev->name, stub_module); - return -1; - } + /* If the device isn't already bound to pci-stub, try binding it now. + */ + pciDriverDir(drvdir, sizeof(drvdir), driver); + pciDeviceFile(path, sizeof(path), dev->name, "driver"); + if (!virFileLinkPointsTo(path, drvdir)) { + /* Xen's pciback.ko wants you to use new_slot first */ + pciDriverFile(path, sizeof(path), driver, "new_slot"); + if (virFileExists(path) && virFileWriteStr(path, dev->name) < 0) { + virReportSystemError(conn, errno, + _("Failed to add slot for PCI device '%s' to %s"), + dev->name, driver); + return -1; } - /* If 'remove_id' exists, remove the device id from pci-stub's dynamic - * ID table so that 'drivers_probe' works below. - */ - snprintf(path, sizeof(path), "%s/remove_id", stub_dir); - if (virFileExists(path) && virFileWriteStr(path, dev->id) < 0) { + pciDriverFile(path, sizeof(path), driver, "bind"); + if (virFileWriteStr(path, dev->name) < 0) { virReportSystemError(conn, errno, - _("Failed to remove PCI ID '%s' from %s"), - dev->id, stub_module); + _("Failed to bind PCI device '%s' to %s"), + dev->name, driver); return -1; } } + /* If 'remove_id' exists, remove the device id from pci-stub's dynamic + * ID table so that 'drivers_probe' works below. + */ + pciDriverFile(path, sizeof(path), driver, "remove_id"); + if (virFileExists(path) && virFileWriteStr(path, dev->id) < 0) { + virReportSystemError(conn, errno, + _("Failed to remove PCI ID '%s' from %s"), + dev->id, driver); + return -1; + } + return 0; } int pciDettachDevice(virConnectPtr conn, pciDevice *dev) { - return pciBindDeviceToStub(conn, dev, "pci-stub"); + const char *driver = pciFindStubDriver(conn); + if (!driver) { + pciReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot find any PCI stub module")); + return -1; + } + + return pciBindDeviceToStub(conn, dev, driver); } static int -pciUnBindDeviceFromStub(virConnectPtr conn, pciDevice *dev, const char *stub_module) +pciUnBindDeviceFromStub(virConnectPtr conn, pciDevice *dev, const char *driver) { - char stub_dir[PATH_MAX]; + char drvdir[PATH_MAX]; char path[PATH_MAX]; - snprintf(stub_dir, sizeof(stub_dir), PCI_SYSFS "drivers/%s", stub_module); - /* If the device is bound to stub, unbind it. */ - snprintf(path, sizeof(path), PCI_SYSFS "devices/%s/driver", dev->name); - if (virFileExists(stub_dir) && virFileLinkPointsTo(path, stub_dir)) { - snprintf(path, sizeof(path), "%s/unbind", stub_dir); + pciDriverDir(drvdir, sizeof(drvdir), driver); + pciDeviceFile(path, sizeof(path), dev->name, "driver"); + if (virFileExists(drvdir) && virFileLinkPointsTo(path, drvdir)) { + pciDriverFile(path, sizeof(path), driver, "unbind"); if (virFileWriteStr(path, dev->name) < 0) { virReportSystemError(conn, errno, _("Failed to bind PCI device '%s' to %s"), - dev->name, stub_module); + dev->name, driver); return -1; } } + /* Xen's pciback.ko wants you to use remove_slot on the specific device */ + pciDriverFile(path, sizeof(path), driver, "remove_slot"); + if (virFileExists(path) && virFileWriteStr(path, dev->name) < 0) { + virReportSystemError(conn, errno, + _("Failed to remove slot for PCI device '%s' to %s"), + dev->name, driver); + return -1; + } + + /* Trigger a re-probe of the device is not in the stub's dynamic * ID table. If the stub is available, but 'remove_id' isn't * available, then re-probing would just cause the device to be * re-bound to the stub. */ - snprintf(path, sizeof(path), "%s/remove_id", stub_dir); - if (!virFileExists(stub_dir) || virFileExists(path)) { + pciDriverFile(path, sizeof(path), driver, "remove_id"); + if (!virFileExists(drvdir) || virFileExists(path)) { if (virFileWriteStr(PCI_SYSFS "drivers_probe", dev->name) < 0) { virReportSystemError(conn, errno, _("Failed to trigger a re-probe for PCI device '%s'"), @@ -746,7 +803,14 @@ pciUnBindDeviceFromStub(virConnectPtr co int pciReAttachDevice(virConnectPtr conn, pciDevice *dev) { - return pciUnBindDeviceFromStub(conn, dev, "pci-stub"); + const char *driver = pciFindStubDriver(conn); + if (!driver) { + pciReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot find any PCI stub module")); + return -1; + } + + return pciUnBindDeviceFromStub(conn, dev, driver); } static char * Index: src/xen_unified.c =================================================================== RCS file: /data/cvs/libvirt/src/xen_unified.c,v retrieving revision 1.83 diff -u -p -r1.83 xen_unified.c --- src/xen_unified.c 5 Feb 2009 16:03:11 -0000 1.83 +++ src/xen_unified.c 30 Mar 2009 13:09:44 -0000 @@ -43,6 +43,8 @@ #include "xml.h" #include "util.h" #include "memory.h" +#include "node_device_conf.h" +#include "pci.h" #define VIR_FROM_THIS VIR_FROM_XEN @@ -1420,6 +1422,123 @@ xenUnifiedDomainEventDeregister (virConn return ret; } + +static int +xenUnifiedNodeDeviceGetPciInfo (virNodeDevicePtr dev, + unsigned *domain, + unsigned *bus, + unsigned *slot, + unsigned *function) +{ + virNodeDeviceDefPtr def = NULL; + virNodeDevCapsDefPtr cap; + char *xml = NULL; + int ret = -1; + + xml = virNodeDeviceGetXMLDesc(dev, 0); + if (!xml) + goto out; + + def = virNodeDeviceDefParseString(dev->conn, xml); + if (!def) + goto out; + + cap = def->caps; + while (cap) { + if (cap->type == VIR_NODE_DEV_CAP_PCI_DEV) { + *domain = cap->data.pci_dev.domain; + *bus = cap->data.pci_dev.bus; + *slot = cap->data.pci_dev.slot; + *function = cap->data.pci_dev.function; + break; + } + + cap = cap->next; + } + + if (!cap) { + xenUnifiedError(dev->conn, VIR_ERR_INVALID_ARG, + _("device %s is not a PCI device"), dev->name); + goto out; + } + + ret = 0; +out: + virNodeDeviceDefFree(def); + VIR_FREE(xml); + return ret; +} + +static int +xenUnifiedNodeDeviceDettach (virNodeDevicePtr dev) +{ + pciDevice *pci; + unsigned domain, bus, slot, function; + int ret = -1; + + if (xenUnifiedNodeDeviceGetPciInfo(dev, &domain, &bus, &slot, &function) < 0) + return -1; + + pci = pciGetDevice(dev->conn, domain, bus, slot, function); + if (!pci) + return -1; + + if (pciDettachDevice(dev->conn, pci) < 0) + goto out; + + ret = 0; +out: + pciFreeDevice(dev->conn, pci); + return ret; +} + +static int +xenUnifiedNodeDeviceReAttach (virNodeDevicePtr dev) +{ + pciDevice *pci; + unsigned domain, bus, slot, function; + int ret = -1; + + if (xenUnifiedNodeDeviceGetPciInfo(dev, &domain, &bus, &slot, &function) < 0) + return -1; + + pci = pciGetDevice(dev->conn, domain, bus, slot, function); + if (!pci) + return -1; + + if (pciReAttachDevice(dev->conn, pci) < 0) + goto out; + + ret = 0; +out: + pciFreeDevice(dev->conn, pci); + return ret; +} + +static int +xenUnifiedNodeDeviceReset (virNodeDevicePtr dev) +{ + pciDevice *pci; + unsigned domain, bus, slot, function; + int ret = -1; + + if (xenUnifiedNodeDeviceGetPciInfo(dev, &domain, &bus, &slot, &function) < 0) + return -1; + + pci = pciGetDevice(dev->conn, domain, bus, slot, function); + if (!pci) + return -1; + + if (pciResetDevice(dev->conn, pci) < 0) + goto out; + + ret = 0; +out: + pciFreeDevice(dev->conn, pci); + return ret; +} + + /*----- Register with libvirt.c, and initialise Xen drivers. -----*/ /* The interface which we export upwards to libvirt.c. */ @@ -1481,6 +1600,9 @@ static virDriver xenUnifiedDriver = { .getFreeMemory = xenUnifiedNodeGetFreeMemory, .domainEventRegister = xenUnifiedDomainEventRegister, .domainEventDeregister = xenUnifiedDomainEventDeregister, + .nodeDeviceDettach = xenUnifiedNodeDeviceDettach, + .nodeDeviceReAttach = xenUnifiedNodeDeviceReAttach, + .nodeDeviceReset = xenUnifiedNodeDeviceReset, }; /** Index: src/xend_internal.c =================================================================== RCS file: /data/cvs/libvirt/src/xend_internal.c,v retrieving revision 1.253 diff -u -p -r1.253 xend_internal.c --- src/xend_internal.c 10 Mar 2009 11:13:32 -0000 1.253 +++ src/xend_internal.c 30 Mar 2009 13:09:45 -0000 @@ -92,6 +92,11 @@ xenDaemonFormatSxprNet(virConnectPtr con int xendConfigVersion, int isAttach); static int +xenDaemonFormatSxprOnePCI(virConnectPtr conn, + virDomainHostdevDefPtr def, + virBufferPtr buf); + +static int virDomainXMLDevID(virDomainPtr domain, virDomainDeviceDefPtr dev, char *class, @@ -2145,6 +2150,131 @@ error: return -1; } +/** + * xenDaemonParseSxprPCI + * @conn: connection + * @root: root sexpr + * + * This parses out block devices from the domain sexpr + * + * Returns 0 if successful or -1 if failed. + */ +static int +xenDaemonParseSxprPCI(virConnectPtr conn, + virDomainDefPtr def, + const struct sexpr *root) +{ + const struct sexpr *cur, *tmp = NULL, *node; + virDomainHostdevDefPtr dev = NULL; + + /* + * With the (domain ...) block we have the following odd setup + * + * (device + * (pci + * (dev (domain 0x0000) (bus 0x00) (slot 0x1b) (func 0x0)) + * (dev (domain 0x0000) (bus 0x00) (slot 0x13) (func 0x0)) + * ) + * ) + * + * Normally there is one (device ...) block per device, but in + * wierd world of Xen PCI, once (device ...) covers multiple + * devices. + */ + + for (cur = root; cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) { + node = cur->u.s.car; + if ((tmp = sexpr_lookup(node, "device/pci")) != NULL) + break; + } + + if (!tmp) + return 0; + + for (cur = tmp; cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) { + const char *domain = NULL; + const char *bus = NULL; + const char *slot = NULL; + const char *func = NULL; + int domainID; + int busID; + int slotID; + int funcID; + + node = cur->u.s.car; + if (!sexpr_lookup(node, "dev")) + continue; + + if (!(domain = sexpr_node(node, "dev/domain"))) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("missing PCI domain")); + goto error; + } + if (!(bus = sexpr_node(node, "dev/bus"))) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("missing PCI bus")); + goto error; + } + if (!(slot = sexpr_node(node, "dev/slot"))) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("missing PCI slot")); + goto error; + } + if (!(func = sexpr_node(node, "dev/func"))) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("missing PCI func")); + goto error; + } + + if (virStrToLong_i(domain, NULL, 0, &domainID) < 0) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot parse PCI domain '%s'"), domain); + goto error; + } + if (virStrToLong_i(bus, NULL, 0, &busID) < 0) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot parse PCI bus '%s'"), bus); + goto error; + } + if (virStrToLong_i(slot, NULL, 0, &slotID) < 0) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot parse PCI slot '%s'"), slot); + goto error; + } + if (virStrToLong_i(func, NULL, 0, &funcID) < 0) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot parse PCI func '%s'"), func); + goto error; + } + + if (VIR_ALLOC(dev) < 0) + goto no_memory; + + dev->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; + dev->managed = 0; + dev->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI; + dev->source.subsys.u.pci.domain = domainID; + dev->source.subsys.u.pci.bus = busID; + dev->source.subsys.u.pci.slot = slotID; + dev->source.subsys.u.pci.function = funcID; + + if (VIR_REALLOC_N(def->hostdevs, def->nhostdevs+1) < 0) { + goto no_memory; + } + + def->hostdevs[def->nhostdevs++] = dev; + } + + return 0; + +no_memory: + virReportOOMError(conn); + +error: + virDomainHostdevDefFree(dev); + return -1; +} + /** * xenDaemonParseSxpr: @@ -2310,6 +2440,9 @@ xenDaemonParseSxpr(virConnectPtr conn, if (xenDaemonParseSxprNets(conn, def, root) < 0) goto error; + if (xenDaemonParseSxprPCI(conn, def, root) < 0) + goto error; + /* New style graphics device config */ if (xenDaemonParseSxprGraphicsNew(conn, def, root) < 0) goto error; @@ -3953,6 +4086,20 @@ xenDaemonAttachDevice(virDomainPtr domai goto cleanup; break; + case VIR_DOMAIN_DEVICE_HOSTDEV: + if (dev->data.hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + dev->data.hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) { + if (xenDaemonFormatSxprOnePCI(domain->conn, + dev->data.hostdev, + &buf) < 0) + goto cleanup; + } else { + virXendError(domain->conn, VIR_ERR_NO_SUPPORT, "%s", + _("unsupported device type")); + goto cleanup; + } + break; + default: virXendError(domain->conn, VIR_ERR_NO_SUPPORT, "%s", _("unsupported device type")); @@ -5263,6 +5410,85 @@ xenDaemonFormatSxprNet(virConnectPtr con return 0; } + +static void +xenDaemonFormatSxprPCI(virDomainHostdevDefPtr def, + virBufferPtr buf) +{ + virBufferVSprintf(buf, "(dev (domain 0x%04x)(bus 0x%02x)(slot 0x%02x)(func 0x%x))", + def->source.subsys.u.pci.domain, + def->source.subsys.u.pci.bus, + def->source.subsys.u.pci.slot, + def->source.subsys.u.pci.function); +} + +static int +xenDaemonFormatSxprOnePCI(virConnectPtr conn, + virDomainHostdevDefPtr def, + virBufferPtr buf) +{ + if (def->managed) { + virXendError(conn, VIR_ERR_NO_SUPPORT, "%s", + _("managed PCI devices not supported with XenD")); + return -1; + } + + virBufferAddLit(buf, "(pci "); + xenDaemonFormatSxprPCI(def, buf); + virBufferAddLit(buf, ")"); + + return 0; +} + +static int +xenDaemonFormatSxprAllPCI(virConnectPtr conn, + virDomainDefPtr def, + virBufferPtr buf) +{ + int hasPCI = 0; + int i; + + for (i = 0 ; i < def->nhostdevs ; i++) + if (def->hostdevs[i]->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + def->hostdevs[i]->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + hasPCI = 1; + + if (!hasPCI) + return 0; + + /* + * With the (domain ...) block we have the following odd setup + * + * (device + * (pci + * (dev (domain 0x0000) (bus 0x00) (slot 0x1b) (func 0x0)) + * (dev (domain 0x0000) (bus 0x00) (slot 0x13) (func 0x0)) + * ) + * ) + * + * Normally there is one (device ...) block per device, but in + * wierd world of Xen PCI, once (device ...) covers multiple + * devices. + */ + + virBufferAddLit(buf, "(device (pci "); + for (i = 0 ; i < def->nhostdevs ; i++) { + if (def->hostdevs[i]->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + def->hostdevs[i]->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) { + if (def->hostdevs[i]->managed) { + virXendError(conn, VIR_ERR_NO_SUPPORT, "%s", + _("managed PCI devices not supported with XenD")); + return -1; + } + + xenDaemonFormatSxprPCI(def->hostdevs[i], buf); + } + } + virBufferAddLit(buf, "))"); + + return 0; +} + int xenDaemonFormatSxprSound(virConnectPtr conn, virDomainDefPtr def, @@ -5529,6 +5755,9 @@ xenDaemonFormatSxpr(virConnectPtr conn, &buf, hvm, xendConfigVersion, 0) < 0) goto error; + if (xenDaemonFormatSxprAllPCI(conn, def, &buf) < 0) + goto error; + /* New style PV graphics config xen >= 3.0.4, * or HVM graphics config xen >= 3.0.5 */ if ((xendConfigVersion >= XEND_CONFIG_MIN_VERS_PVFB_NEWCONF && !hvm) || @@ -5611,6 +5840,9 @@ virDomainXMLDevID(virDomainPtr domain, strncpy(ref, xref, ref_len); free(xref); ref[ref_len - 1] = '\0'; + } else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV && + dev->data.hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + dev->data.hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) { } else { virXendError(NULL, VIR_ERR_NO_SUPPORT, "%s", _("hotplug of device type not supported")); Index: src/xm_internal.c =================================================================== RCS file: /data/cvs/libvirt/src/xm_internal.c,v retrieving revision 1.118 diff -u -p -r1.118 xm_internal.c --- src/xm_internal.c 24 Mar 2009 11:16:29 -0000 1.118 +++ src/xm_internal.c 30 Mar 2009 13:09:45 -0000 @@ -673,6 +673,7 @@ xenXMDomainConfigParse(virConnectPtr con virDomainDiskDefPtr disk = NULL; virDomainNetDefPtr net = NULL; virDomainGraphicsDefPtr graphics = NULL; + virDomainHostdevDefPtr hostdev = NULL; int i; const char *defaultArch, *defaultMachine; @@ -1115,6 +1116,88 @@ xenXMDomainConfigParse(virConnectPtr con } } + list = virConfGetValue(conf, "pci"); + if (list && list->type == VIR_CONF_LIST) { + list = list->list; + while (list) { + char domain[5]; + char bus[3]; + char slot[3]; + char func[2]; + char *key, *nextkey; + int domainID; + int busID; + int slotID; + int funcID; + + domain[0] = bus[0] = slot[0] = func[0] = '\0'; + + if ((list->type != VIR_CONF_STRING) || (list->str == NULL)) + goto skippci; + + /* pci=['0000:00:1b.0','0000:00:13.0'] */ + key = list->str; + if (!(key = list->str)) + goto skippci; + if (!(nextkey = strchr(key, ':'))) + goto skippci; + + if ((nextkey - key) > (sizeof(domain)-1)) + goto skippci; + + strncpy(domain, key, sizeof(domain)); + domain[sizeof(domain)-1] = '\0'; + + key = nextkey + 1; + if (!(nextkey = strchr(key, ':'))) + goto skippci; + + strncpy(bus, key, sizeof(bus)); + bus[sizeof(bus)-1] = '\0'; + + key = nextkey + 1; + if (!(nextkey = strchr(key, '.'))) + goto skippci; + + strncpy(slot, key, sizeof(slot)); + slot[sizeof(slot)-1] = '\0'; + + key = nextkey + 1; + if (strlen(key) != 1) + goto skippci; + + strncpy(func, key, sizeof(func)); + func[sizeof(func)-1] = '\0'; + + if (virStrToLong_i(domain, NULL, 16, &domainID) < 0) + goto skippci; + if (virStrToLong_i(bus, NULL, 16, &busID) < 0) + goto skippci; + if (virStrToLong_i(slot, NULL, 16, &slotID) < 0) + goto skippci; + if (virStrToLong_i(func, NULL, 16, &funcID) < 0) + goto skippci; + + if (VIR_ALLOC(hostdev) < 0) + goto cleanup; + + hostdev->managed = 0; + hostdev->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI; + hostdev->source.subsys.u.pci.domain = domainID; + hostdev->source.subsys.u.pci.bus = busID; + hostdev->source.subsys.u.pci.slot = slotID; + hostdev->source.subsys.u.pci.function = funcID; + + if (VIR_REALLOC_N(def->hostdevs, def->nhostdevs+1) < 0) + goto no_memory; + def->hostdevs[def->nhostdevs++] = hostdev; + hostdev = NULL; + + skippci: + list = list->next; + } + } + if (hvm) { if (xenXMConfigGetString(conn, conf, "usbdevice", &str, NULL) < 0) goto cleanup; @@ -1939,6 +2022,76 @@ cleanup: +static int +xenXMDomainConfigFormatPCI(virConnectPtr conn, + virConfPtr conf, + virDomainDefPtr def) +{ + + virConfValuePtr pciVal = NULL; + int hasPCI = 0; + int i; + + for (i = 0 ; i < def->nhostdevs ; i++) + if (def->hostdevs[i]->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + def->hostdevs[i]->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + hasPCI = 1; + + if (!hasPCI) + return 0; + + if (VIR_ALLOC(pciVal) < 0) + return -1; + + pciVal->type = VIR_CONF_LIST; + pciVal->list = NULL; + + for (i = 0 ; i < def->nhostdevs ; i++) { + if (def->hostdevs[i]->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + def->hostdevs[i]->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) { + virConfValuePtr val, tmp; + char *buf; + + if (virAsprintf(&buf, "%04x:%02x:%02x.%x", + def->hostdevs[i]->source.subsys.u.pci.domain, + def->hostdevs[i]->source.subsys.u.pci.bus, + def->hostdevs[i]->source.subsys.u.pci.slot, + def->hostdevs[i]->source.subsys.u.pci.function) < 0) + goto error; + + if (VIR_ALLOC(val) < 0) { + VIR_FREE(buf); + virReportOOMError(conn); + goto error; + } + val->type = VIR_CONF_STRING; + val->str = buf; + tmp = pciVal->list; + while (tmp && tmp->next) + tmp = tmp->next; + if (tmp) + tmp->next = val; + else + pciVal->list = val; + } + } + + if (pciVal->list != NULL) { + int ret = virConfSetValue(conf, "pci", pciVal); + pciVal = NULL; + if (ret < 0) + return -1; + } + VIR_FREE(pciVal); + + return 0; + +error: + virConfFreeValue(pciVal); + return -1; +} + + virConfPtr xenXMDomainConfigFormat(virConnectPtr conn, virDomainDefPtr def) { virConfPtr conf = NULL; @@ -2260,6 +2413,9 @@ virConfPtr xenXMDomainConfigFormat(virCo } VIR_FREE(netVal); + if (xenXMDomainConfigFormatPCI(conn, conf, def) < 0) + goto cleanup; + if (hvm) { if (def->nparallels) { virBuffer buf = VIR_BUFFER_INITIALIZER; Index: tests/sexpr2xmltest.c =================================================================== RCS file: /data/cvs/libvirt/tests/sexpr2xmltest.c,v retrieving revision 1.36 diff -u -p -r1.36 sexpr2xmltest.c --- tests/sexpr2xmltest.c 29 Jan 2009 17:02:00 -0000 1.36 +++ tests/sexpr2xmltest.c 30 Mar 2009 13:09:45 -0000 @@ -142,6 +142,7 @@ mymain(int argc, char **argv) DO_TEST("net-e1000", "net-e1000", 2); DO_TEST("bridge-ipaddr", "bridge-ipaddr", 3); DO_TEST("no-source-cdrom", "no-source-cdrom", 2); + DO_TEST("pci-devs", "pci-devs", 2); DO_TEST("fv-utc", "fv-utc", 1); DO_TEST("fv-localtime", "fv-localtime", 1); Index: tests/xmconfigtest.c =================================================================== RCS file: /data/cvs/libvirt/tests/xmconfigtest.c,v retrieving revision 1.26 diff -u -p -r1.26 xmconfigtest.c --- tests/xmconfigtest.c 8 Jan 2009 19:52:15 -0000 1.26 +++ tests/xmconfigtest.c 30 Mar 2009 13:09:45 -0000 @@ -231,6 +231,7 @@ mymain(int argc, char **argv) DO_TEST("escape-paths", 2); DO_TEST("no-source-cdrom", 2); + DO_TEST("pci-devs", 2); virCapabilitiesFree(caps); Index: tests/xml2sexprtest.c =================================================================== RCS file: /data/cvs/libvirt/tests/xml2sexprtest.c,v retrieving revision 1.34 diff -u -p -r1.34 xml2sexprtest.c --- tests/xml2sexprtest.c 23 Jan 2009 01:48:47 -0000 1.34 +++ tests/xml2sexprtest.c 30 Mar 2009 13:09:45 -0000 @@ -128,6 +128,7 @@ mymain(int argc, char **argv) DO_TEST("net-e1000", "net-e1000", "pvtest", 2); DO_TEST("bridge-ipaddr", "bridge-ipaddr", "pvtest", 2); DO_TEST("no-source-cdrom", "no-source-cdrom", "test", 2); + DO_TEST("pci-devs", "pci-devs", "pvtest", 2); DO_TEST("fv-utc", "fv-utc", "fvtest", 1); DO_TEST("fv-localtime", "fv-localtime", "fvtest", 1); Index: tests/sexpr2xmldata/sexpr2xml-pci-devs.sexpr =================================================================== RCS file: tests/sexpr2xmldata/sexpr2xml-pci-devs.sexpr diff -N tests/sexpr2xmldata/sexpr2xml-pci-devs.sexpr --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tests/sexpr2xmldata/sexpr2xml-pci-devs.sexpr 30 Mar 2009 13:09:45 -0000 @@ -0,0 +1,2 @@ +(domain (domid 6)(name 'pvtest')(memory 420)(maxmem 420)(vcpus 2)(uuid '596a5d2171f48fb2e068e2386a5c413e')(on_poweroff 'destroy')(on_reboot 'destroy')(on_crash 'destroy')(image (linux (kernel '/var/lib/xen/vmlinuz.2Dn2YT')(ramdisk '/var/lib/xen/initrd.img.0u-Vhq')(args ' method=http://download.fedora.devel.redhat.com/pub/fedora/linux/core/test/5.91/x86_64/os ')))(device (pci (backend 0)(dev (domain 0x0001) (bus 0x0c) (slot 0x1b) (func 0x2))(dev (domain 0x0000) (bus 0x01) (slot 0x13) (func 0x0))))(device (vbd (dev 'xvda')(uname 'phy:/dev/MainVG/GuestVG')(mode 'w')))) + Index: tests/sexpr2xmldata/sexpr2xml-pci-devs.xml =================================================================== RCS file: tests/sexpr2xmldata/sexpr2xml-pci-devs.xml diff -N tests/sexpr2xmldata/sexpr2xml-pci-devs.xml --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tests/sexpr2xmldata/sexpr2xml-pci-devs.xml 30 Mar 2009 13:09:45 -0000 @@ -0,0 +1,37 @@ +<domain type='xen' id='6'> + <name>pvtest</name> + <uuid>596a5d21-71f4-8fb2-e068-e2386a5c413e</uuid> + <memory>430080</memory> + <currentMemory>430080</currentMemory> + <vcpu>2</vcpu> + <os> + <type>linux</type> + <kernel>/var/lib/xen/vmlinuz.2Dn2YT</kernel> + <initrd>/var/lib/xen/initrd.img.0u-Vhq</initrd> + <cmdline> method=http://download.fedora.devel.redhat.com/pub/fedora/linux/core/test/5.91/x86_64/os </cmdline> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>destroy</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <disk type='block' device='disk'> + <driver name='phy'/> + <source dev='/dev/MainVG/GuestVG'/> + <target dev='xvda' bus='xen'/> + </disk> + <console type='pty'> + <target port='0'/> + </console> + <hostdev mode='subsystem' type='pci' managed='no'> + <source> + <address domain='0x0001' bus='0x0c' slot='0x1b' function='0x2'/> + </source> + </hostdev> + <hostdev mode='subsystem' type='pci' managed='no'> + <source> + <address domain='0x0000' bus='0x01' slot='0x13' function='0x0'/> + </source> + </hostdev> + </devices> +</domain> Index: tests/xmconfigdata/test-pci-devs.cfg =================================================================== RCS file: tests/xmconfigdata/test-pci-devs.cfg diff -N tests/xmconfigdata/test-pci-devs.cfg --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tests/xmconfigdata/test-pci-devs.cfg 30 Mar 2009 13:09:45 -0000 @@ -0,0 +1,24 @@ +name = "test" +uuid = "cc2315e7-d26a-307a-438c-6d188ec4c09c" +maxmem = 382 +memory = 350 +vcpus = 1 +builder = "hvm" +kernel = "/usr/lib/xen/boot/hvmloader" +boot = "c" +pae = 1 +acpi = 1 +apic = 1 +localtime = 0 +on_poweroff = "destroy" +on_reboot = "destroy" +on_crash = "destroy" +device_model = "/usr/lib/xen/bin/qemu-dm" +sdl = 0 +vnc = 1 +vncunused = 1 +disk = [ "phy:/dev/sda8,hda,w", ",hdc:cdrom,r" ] +vif = [ "mac=00:16:3e:0a:7b:39,bridge=xenbr0,type=ioemu" ] +pci = [ "0001:0c:1b.2", "0000:01:13.0" ] +parallel = "none" +serial = "pty" Index: tests/xmconfigdata/test-pci-devs.xml =================================================================== RCS file: tests/xmconfigdata/test-pci-devs.xml diff -N tests/xmconfigdata/test-pci-devs.xml --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tests/xmconfigdata/test-pci-devs.xml 30 Mar 2009 13:09:45 -0000 @@ -0,0 +1,56 @@ +<domain type='xen'> + <name>test</name> + <uuid>cc2315e7-d26a-307a-438c-6d188ec4c09c</uuid> + <memory>391168</memory> + <currentMemory>358400</currentMemory> + <vcpu>1</vcpu> + <os> + <type arch='i686' machine='xenfv'>hvm</type> + <loader>/usr/lib/xen/boot/hvmloader</loader> + <boot dev='hd'/> + </os> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>destroy</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/lib/xen/bin/qemu-dm</emulator> + <disk type='block' device='disk'> + <driver name='phy'/> + <source dev='/dev/sda8'/> + <target dev='hda' bus='ide'/> + </disk> + <disk type='block' device='cdrom'> + <driver name='phy'/> + <target dev='hdc' bus='ide'/> + <readonly/> + </disk> + <interface type='bridge'> + <mac address='00:16:3e:0a:7b:39'/> + <source bridge='xenbr0'/> + </interface> + <serial type='pty'> + <target port='0'/> + </serial> + <console type='pty'> + <target port='0'/> + </console> + <input type='mouse' bus='ps2'/> + <graphics type='vnc' port='-1' autoport='yes'/> + <hostdev mode='subsystem' type='pci' managed='no'> + <source> + <address domain='0x0001' bus='0x0c' slot='0x1b' function='0x2'/> + </source> + </hostdev> + <hostdev mode='subsystem' type='pci' managed='no'> + <source> + <address domain='0x0000' bus='0x01' slot='0x13' function='0x0'/> + </source> + </hostdev> + </devices> +</domain> Index: tests/xml2sexprdata/xml2sexpr-pci-devs.sexpr =================================================================== RCS file: tests/xml2sexprdata/xml2sexpr-pci-devs.sexpr diff -N tests/xml2sexprdata/xml2sexpr-pci-devs.sexpr --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tests/xml2sexprdata/xml2sexpr-pci-devs.sexpr 30 Mar 2009 13:09:45 -0000 @@ -0,0 +1 @@ +(vm (name 'pvtest')(memory 420)(maxmem 420)(vcpus 2)(uuid '596a5d21-71f4-8fb2-e068-e2386a5c413e')(on_poweroff 'destroy')(on_reboot 'destroy')(on_crash 'destroy')(image (linux (kernel '/var/lib/xen/vmlinuz.2Dn2YT')(ramdisk '/var/lib/xen/initrd.img.0u-Vhq')(args ' method=http://download.fedora.devel.redhat.com/pub/fedora/linux/core/test/5.91/x86_64/os ')))(device (vbd (dev 'xvda')(uname 'phy:/dev/MainVG/GuestLV')(mode 'w')))(device (pci (dev (domain 0x0001)(bus 0x0c)(slot 0x1b)(func 0x2))(dev (domain 0x0000)(bus 0x01)(slot 0x13)(func 0x0))))) \ No newline at end of file Index: tests/xml2sexprdata/xml2sexpr-pci-devs.xml =================================================================== RCS file: tests/xml2sexprdata/xml2sexpr-pci-devs.xml diff -N tests/xml2sexprdata/xml2sexpr-pci-devs.xml --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tests/xml2sexprdata/xml2sexpr-pci-devs.xml 30 Mar 2009 13:09:45 -0000 @@ -0,0 +1,33 @@ +<domain type='xen' id='15'> + <name>pvtest</name> + <uuid>596a5d2171f48fb2e068e2386a5c413e</uuid> + <os> + <type>linux</type> + <kernel>/var/lib/xen/vmlinuz.2Dn2YT</kernel> + <initrd>/var/lib/xen/initrd.img.0u-Vhq</initrd> + <cmdline> method=http://download.fedora.devel.redhat.com/pub/fedora/linux/core/test/5.91/x86_64/os </cmdline> + </os> + <memory>430080</memory> + <vcpu>2</vcpu> + <on_poweroff>destroy</on_poweroff> + <on_reboot>destroy</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <disk type='block' device='disk'> + <source dev='/dev/MainVG/GuestLV'/> + <target dev='xvda'/> + </disk> + <console tty='/dev/pts/4'/> + <hostdev mode='subsystem' type='pci' managed='no'> + <source> + <address domain='0x0001' bus='0x0c' slot='0x1b' function='0x2'/> + </source> + </hostdev> + <hostdev mode='subsystem' type='pci' managed='no'> + <source> + <address domain='0x0000' bus='0x01' slot='0x13' function='0x0'/> + </source> + </hostdev> + </devices> +</domain> + -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :| -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list