]> xenbits.xensource.com Git - libvirt.git/commitdiff
Pass pre-opened PCI device sysfs config file to QEMU
authorAlex Williamson <alex.williamson@redhat.com>
Wed, 26 May 2010 12:32:38 +0000 (13:32 +0100)
committerDaniel P. Berrange <berrange@redhat.com>
Wed, 26 May 2010 16:41:55 +0000 (17:41 +0100)
This allows libvirt to open the PCI device sysfs config file prior
to dropping privileges so qemu can access the full config space.
Without this, a de-privileged qemu can only access the first 64
bytes of config space.

* src/qemu/qemu_conf.c, src/qemu/qemu_conf.h: Detect support
  for pci-assign.configfd option. Use this option when formatting
  PCI device string if possible
* src/qemu/qemu_driver.c: Pre-open PCI sysfs config file and pass
  to QEMU

src/qemu/qemu_conf.c
src/qemu/qemu_conf.h
src/qemu/qemu_driver.c

index 08d811b18ffd0578e5cf3c422b289ddcb1a037af..66a949e6e43fd00675daf4c0fed7262c4f36e266 100644 (file)
@@ -1354,6 +1354,48 @@ fail:
     return -1;
 }
 
+static void qemudParsePCIDeviceStrs(const char *qemu, unsigned long long *flags)
+{
+    const char *const qemuarg[] = { qemu, "-device", "pci-assign,?", NULL };
+    const char *const qemuenv[] = { "LC_ALL=C", NULL };
+    pid_t child;
+    int status;
+    int newstderr = -1;
+
+    if (virExec(qemuarg, qemuenv, NULL,
+                &child, -1, NULL, &newstderr, VIR_EXEC_CLEAR_CAPS) < 0)
+        return;
+
+    char *pciassign = NULL;
+    enum { MAX_PCI_OUTPUT_SIZE = 1024*4 };
+    int len = virFileReadLimFD(newstderr, MAX_PCI_OUTPUT_SIZE, &pciassign);
+    if (len < 0) {
+        virReportSystemError(errno,
+                             _("Unable to read %s pci-assign device output"),
+                             qemu);
+        goto cleanup;
+    }
+
+    if (strstr(pciassign, "pci-assign.configfd"))
+        *flags |= QEMUD_CMD_FLAG_PCI_CONFIGFD;
+
+cleanup:
+    VIR_FREE(pciassign);
+    close(newstderr);
+rewait:
+    if (waitpid(child, &status, 0) != child) {
+        if (errno == EINTR)
+            goto rewait;
+
+        VIR_ERROR(_("Unexpected exit status from qemu %d pid %lu"),
+                  WEXITSTATUS(status), (unsigned long)child);
+    }
+    if (WEXITSTATUS(status) != 0) {
+        VIR_WARN("Unexpected exit status '%d', qemu probably failed",
+                 WEXITSTATUS(status));
+    }
+}
+
 int qemudExtractVersionInfo(const char *qemu,
                             unsigned int *retversion,
                             unsigned long long *retflags) {
@@ -1387,6 +1429,9 @@ int qemudExtractVersionInfo(const char *qemu,
                           &version, &is_kvm, &kvm_version) == -1)
         goto cleanup2;
 
+    if (flags & QEMUD_CMD_FLAG_DEVICE)
+        qemudParsePCIDeviceStrs(qemu, &flags);
+
     if (retversion)
         *retversion = version;
     if (retflags)
@@ -2896,8 +2941,33 @@ error:
 }
 
 
+int
+qemudOpenPCIConfig(virDomainHostdevDefPtr dev)
+{
+    char *path = NULL;
+    int configfd = -1;
+
+    if (virAsprintf(&path, "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/config",
+                    dev->source.subsys.u.pci.domain,
+                    dev->source.subsys.u.pci.bus,
+                    dev->source.subsys.u.pci.slot,
+                    dev->source.subsys.u.pci.function) < 0) {
+        virReportOOMError();
+        return -1;
+    }
+
+    configfd = open(path, O_RDWR, 0);
+
+    if (configfd < 0)
+        virReportSystemError(errno, _("Failed opening %s"), path);
+
+    VIR_FREE(path);
+
+    return configfd;
+}
+
 char *
-qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev)
+qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev, const char *configfd)
 {
     virBuffer buf = VIR_BUFFER_INITIALIZER;
 
@@ -2907,6 +2977,8 @@ qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev)
                       dev->source.subsys.u.pci.slot,
                       dev->source.subsys.u.pci.function);
     virBufferVSprintf(&buf, ",id=%s", dev->info.alias);
+    if (configfd && *configfd)
+        virBufferVSprintf(&buf, ",configfd=%s", configfd);
     if (qemuBuildDeviceAddressStr(&buf, &dev->info) < 0)
         goto error;
 
@@ -4611,8 +4683,30 @@ int qemudBuildCommandLine(virConnectPtr conn,
         if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
             hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
             if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+                char *configfd_name = NULL;
+                if (qemuCmdFlags & QEMUD_CMD_FLAG_PCI_CONFIGFD) {
+                    int configfd = qemudOpenPCIConfig(hostdev);
+
+                    if (configfd >= 0) {
+                        if (virAsprintf(&configfd_name, "%d", configfd) < 0) {
+                            close(configfd);
+                            virReportOOMError();
+                            goto no_memory;
+                        }
+
+                        if (VIR_REALLOC_N(*vmfds, (*nvmfds)+1) < 0) {
+                            VIR_FREE(configfd_name);
+                            close(configfd);
+                            goto no_memory;
+                        }
+
+                        (*vmfds)[(*nvmfds)++] = configfd;
+                    }
+                }
                 ADD_ARG_LIT("-device");
-                if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev)))
+                devstr = qemuBuildPCIHostdevDevStr(hostdev, configfd_name);
+                VIR_FREE(configfd_name);
+                if (!devstr)
                     goto error;
                 ADD_ARG(devstr);
             } else if (qemuCmdFlags & QEMUD_CMD_FLAG_PCIDEVICE) {
index 516a59bd0859fd4215e7367c7178b2c7d7260b85..7616d151b21f57453e6085ed91f0291b2d75ce86 100644 (file)
@@ -89,6 +89,7 @@ enum qemud_cmd_flags {
     QEMUD_CMD_FLAG_NO_HPET       = (1LL << 33), /* -no-hpet flag is supported */
     QEMUD_CMD_FLAG_NO_KVM_PIT    = (1LL << 34), /* -no-kvm-pit-reinjection supported */
     QEMUD_CMD_FLAG_TDF           = (1LL << 35), /* -tdf flag (user-mode pit catchup) */
+    QEMUD_CMD_FLAG_PCI_CONFIGFD  = (1LL << 36), /* pci-assign.configfd */
 };
 
 /* Main driver state */
@@ -247,7 +248,10 @@ char * qemuBuildSoundDevStr(virDomainSoundDefPtr sound);
 /* Legacy, pre device support */
 char * qemuBuildPCIHostdevPCIDevStr(virDomainHostdevDefPtr dev);
 /* Current, best practice */
-char * qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev);
+char * qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev,
+                                 const char *configfd);
+
+int qemudOpenPCIConfig(virDomainHostdevDefPtr dev);
 
 /* Current, best practice */
 char * qemuBuildChrChardevStr(virDomainChrDefPtr dev);
index 3b2b0d8dff5761c4e4fb0f5935bd1f06f6634108..76ec3bf5e2ca9b7d1e80a1816b541be7efa1a35c 100644 (file)
@@ -7658,6 +7658,8 @@ static int qemudDomainAttachHostPciDevice(struct qemud_driver *driver,
     pciDevice *pci;
     int ret;
     char *devstr = NULL;
+    int configfd = -1;
+    char *configfd_name = NULL;
 
     if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) {
         virReportOOMError();
@@ -7688,8 +7690,32 @@ static int qemudDomainAttachHostPciDevice(struct qemud_driver *driver,
             goto error;
         if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &hostdev->info) < 0)
             goto error;
+        if (qemuCmdFlags & QEMUD_CMD_FLAG_PCI_CONFIGFD) {
+            configfd = qemudOpenPCIConfig(hostdev);
+            if (configfd >= 0) {
+                if (virAsprintf(&configfd_name, "fd-%s",
+                                hostdev->info.alias) < 0) {
+                    virReportOOMError();
+                    goto error;
+                }
+
+                qemuDomainObjEnterMonitorWithDriver(driver, vm);
+                if (qemuMonitorSendFileHandle(priv->mon, configfd_name,
+                                              configfd) < 0) {
+                    qemuDomainObjExitMonitorWithDriver(driver, vm);
+                    goto error;
+                }
+                qemuDomainObjExitMonitorWithDriver(driver, vm);
+            }
+        }
+
+        if (!virDomainObjIsActive(vm)) {
+            qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                            _("guest unexpectedly quit during hotplug"));
+            goto error;
+        }
 
-        if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev)))
+        if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev, configfd_name)))
             goto error;
 
         qemuDomainObjEnterMonitorWithDriver(driver, vm);
@@ -7713,6 +7739,9 @@ static int qemudDomainAttachHostPciDevice(struct qemud_driver *driver,
     vm->def->hostdevs[vm->def->nhostdevs++] = hostdev;
 
     VIR_FREE(devstr);
+    VIR_FREE(configfd_name);
+    if (configfd >= 0)
+        close(configfd);
 
     return 0;
 
@@ -7724,6 +7753,9 @@ error:
 
     VIR_FREE(devstr);
     pciDeviceListDel(driver->activePciHostdevs, pci);
+    VIR_FREE(configfd_name);
+    if (configfd >= 0)
+        close(configfd);
 
     return -1;
 }