[libvirt] [RFC PATCH v2] qemu: Add suport for pci-assign.configfd option
Daniel P. Berrange
berrange at redhat.com
Mon May 24 09:46:38 UTC 2010
On Thu, May 20, 2010 at 10:02:10AM -0400, Alex Williamson wrote:
> 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.
>
> Signed-off-by: Alex Williamson <alex.williamson at redhat.com>
> ---
>
> v2:
> - fix qemuargs, two separate args (thanks Chris)
> - don't hardcode pci segment
> - error message of open() fail
>
> src/qemu/qemu_conf.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++-
> src/qemu/qemu_conf.h | 8 ++++
> src/qemu/qemu_driver.c | 2 +
> 3 files changed, 95 insertions(+), 4 deletions(-)
>
> diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
> index d7bc798..24c360e 100644
> --- a/src/qemu/qemu_conf.c
> +++ b/src/qemu/qemu_conf.c
> @@ -1345,6 +1345,48 @@ fail:
> return -1;
> }
>
> +void qemudParsePCIDeviceStrs(const char *qemu, unsigned long long *flags)
Minor point, that this function can be declared static.
> +{
> + 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) {
> @@ -1378,6 +1420,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)
> @@ -2887,8 +2932,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, int configfd)
> {
> virBuffer buf = VIR_BUFFER_INITIALIZER;
>
> @@ -2898,6 +2968,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 >= 0)
> + virBufferVSprintf(&buf, ",configfd=%d", configfd);
For hotplug to work, it will actually be neccessary to make this take
a const char * string and use %s. For CLI args the string will just be
the FD number formatted normally, for hotplug the string will be a
symbolic FD name.
> @@ -4600,8 +4672,21 @@ 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) {
> + int configfd = -1;
const char configfdstr[20];
> + if (qemuCmdFlags & QEMUD_CMD_FLAG_PCI_CONFIGFD) {
> + configfd = qemudOpenPCIConfig(hostdev);
> +
> + if (configfd >= 0) {
snprintf(configfdstr, sizeof(configfdstr), "%d", configfd);
> + if (VIR_REALLOC_N(*vmfds, (*nvmfds)+1) < 0) {
> + close(configfd);
> + goto no_memory;
> + }
> +
> + (*vmfds)[(*nvmfds)++] = configfd;
> + }
> + }
> ADD_ARG_LIT("-device");
> - if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev)))
> + if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev, configfd)))
And pass the configfdstr here.
> goto error;
> ADD_ARG(devstr);
> } else if (qemuCmdFlags & QEMUD_CMD_FLAG_PCIDEVICE) {
> diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h
> index a101e47..64fab60 100644
> --- a/src/qemu/qemu_conf.h
> +++ b/src/qemu/qemu_conf.h
> @@ -88,6 +88,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 */
> @@ -190,6 +191,9 @@ int qemudParseHelpStr (const char *qemu,
> unsigned int *is_kvm,
> unsigned int *kvm_version);
>
> +void qemudParsePCIDeviceStrs (const char *qemu,
> + unsigned long long *qemuCmdFlags);
> +
> int qemudBuildCommandLine (virConnectPtr conn,
> struct qemud_driver *driver,
> virDomainDefPtr def,
> @@ -242,7 +246,9 @@ char * qemuBuildSoundDevStr(virDomainSoundDefPtr sound);
> /* Legacy, pre device support */
> char * qemuBuildPCIHostdevPCIDevStr(virDomainHostdevDefPtr dev);
> /* Current, best practice */
> -char * qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev);
> +char * qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev, int configfd);
> +
> +int qemudOpenPCIConfig(virDomainHostdevDefPtr dev);
>
> /* Current, best practice */
> char * qemuBuildChrChardevStr(virDomainChrDefPtr dev);
> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
> index dd5bd24..219a973 100644
> --- a/src/qemu/qemu_driver.c
> +++ b/src/qemu/qemu_driver.c
> @@ -7567,7 +7567,7 @@ static int qemudDomainAttachHostPciDevice(struct qemud_driver *driver,
> if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &hostdev->info) < 0)
> goto error;
>
> - if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev)))
> + if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev, -1)))
> goto error;
> }
This is the only thing still needing work. On the libvirt side there is a
function qemuMonitorSendFileHandle() that can be used to give QEMU the
pre-opened file handle. You the 'fdname' parameter is what's passed into
the qemuBuildPCIHostdevDevStr() earlier as the named file handle.
Regards,
Daniel
--
|: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :|
|: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
More information about the libvir-list
mailing list