/* * qemu_extdevice.c: QEMU external devices support * * Copyright (C) 2014, 2018 IBM Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #include #include "qemu_extdevice.h" #include "qemu_vhost_user_gpu.h" #include "qemu_dbus.h" #include "qemu_domain.h" #include "qemu_tpm.h" #include "qemu_passt.h" #include "qemu_slirp.h" #include "qemu_virtiofs.h" #include "virlog.h" #include "virtime.h" #define VIR_FROM_THIS VIR_FROM_QEMU VIR_LOG_INIT("qemu.qemu_extdevice"); int qemuExtDeviceLogCommand(virQEMUDriver *driver, virDomainObj *vm, virCommand *cmd, const char *info) { g_autofree char *timestamp = virTimeStringNow(); g_autofree char *cmds = virCommandToString(cmd, false); if (!timestamp || !cmds) return -1; return qemuDomainLogAppendMessage(driver, vm, _("%1$s: Starting external device: %2$s\n%3$s\n"), timestamp, info, cmds); } /* * qemuExtDevicesInitPaths: * * @driver: QEMU driver * @def: domain definition * * Initialize paths of external devices so that it is known where state is * stored and we can remove directories and files in case of domain XML * changes. */ int qemuExtDevicesInitPaths(virQEMUDriver *driver, virDomainDef *def) { size_t i; for (i = 0; i < def->ntpms; i++) { virDomainTPMDef *tpm = def->tpms[i]; if (tpm->type == VIR_DOMAIN_TPM_TYPE_EMULATOR && qemuExtTPMInitPaths(driver, def, tpm) < 0) return -1; } return 0; } /* * qemuExtDevicesPrepareDomain: * * @driver: QEMU driver * @vm: domain * * Code that modifies live XML of a domain which is about to start. */ int qemuExtDevicesPrepareDomain(virQEMUDriver *driver, virDomainObj *vm) { int ret = 0; size_t i; if (qemuExtDevicesInitPaths(driver, vm->def) < 0) return -1; for (i = 0; i < vm->def->nvideos; i++) { virDomainVideoDef *video = vm->def->videos[i]; if (video->backend == VIR_DOMAIN_VIDEO_BACKEND_TYPE_VHOSTUSER) { if ((ret = qemuExtVhostUserGPUPrepareDomain(driver, video)) < 0) break; } } for (i = 0; i < vm->def->nfss; i++) { virDomainFSDef *fs = vm->def->fss[i]; if (fs->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_VIRTIOFS) { if (qemuVirtioFSPrepareDomain(driver, fs) < 0) return -1; } } return ret; } /* * qemuExtDevicesPrepareHost: * * @driver: QEMU driver * @def: domain definition * * Prepare host storage paths for external devices. */ int qemuExtDevicesPrepareHost(virQEMUDriver *driver, virDomainObj *vm) { virDomainDef *def = vm->def; size_t i; for (i = 0; i < def->ntpms; i++) { virDomainTPMDef *tpm = def->tpms[i]; if (tpm->type == VIR_DOMAIN_TPM_TYPE_EMULATOR && qemuExtTPMPrepareHost(driver, def, tpm) < 0) return -1; } return 0; } void qemuExtDevicesCleanupHost(virQEMUDriver *driver, virDomainDef *def, virDomainUndefineFlagsValues flags, bool outgoingMigration) { size_t i; if (qemuExtDevicesInitPaths(driver, def) < 0) return; for (i = 0; i < def->ntpms; i++) { virDomainTPMDef *tpm = def->tpms[i]; if (tpm->type == VIR_DOMAIN_TPM_TYPE_EMULATOR) qemuExtTPMCleanupHost(tpm, flags, outgoingMigration); } } int qemuExtDevicesStart(virQEMUDriver *driver, virDomainObj *vm, bool incomingMigration) { virDomainDef *def = vm->def; size_t i; for (i = 0; i < def->nvideos; i++) { virDomainVideoDef *video = def->videos[i]; if (video->backend == VIR_DOMAIN_VIDEO_BACKEND_TYPE_VHOSTUSER) { if (qemuExtVhostUserGPUStart(driver, vm, video) < 0) return -1; } } for (i = 0; i < def->ntpms; i++) { virDomainTPMDef *tpm = def->tpms[i]; if (tpm->type == VIR_DOMAIN_TPM_TYPE_EMULATOR && qemuExtTPMStart(driver, vm, tpm, incomingMigration) < 0) return -1; } for (i = 0; i < def->nnets; i++) { virDomainNetDef *net = def->nets[i]; if (net->type != VIR_DOMAIN_NET_TYPE_USER) continue; if (net->backend.type == VIR_DOMAIN_NET_BACKEND_PASST) { if (qemuPasstStart(vm, net) < 0) return -1; } else { if (qemuSlirpStart(vm, net, incomingMigration) < 0) return -1; } } for (i = 0; i < def->nfss; i++) { virDomainFSDef *fs = def->fss[i]; if (fs->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_VIRTIOFS && !fs->sock) { if (qemuVirtioFSStart(driver, vm, fs) < 0) return -1; } } for (i = 0; i < def->ngraphics; i++) { virDomainGraphicsDef *graphics = def->graphics[i]; if (graphics->type != VIR_DOMAIN_GRAPHICS_TYPE_DBUS) continue; if (graphics->data.dbus.p2p || graphics->data.dbus.fromConfig) continue; if (qemuDBusStart(driver, vm) < 0) return -1; } for (i = 0; i < def->ndisks; i++) { virDomainDiskDef *disk = def->disks[i]; if (qemuNbdkitStartStorageSource(driver, vm, disk->src) < 0) return -1; } if (def->os.loader && def->os.loader->nvram) { if (qemuNbdkitStartStorageSource(driver, vm, def->os.loader->nvram) < 0) return -1; } return 0; } void qemuExtDevicesStop(virQEMUDriver *driver, virDomainObj *vm, bool outgoingMigration) { virDomainDef *def = vm->def; size_t i; if (qemuExtDevicesInitPaths(driver, def) < 0) return; for (i = 0; i < def->nvideos; i++) { virDomainVideoDef *video = def->videos[i]; if (video->backend == VIR_DOMAIN_VIDEO_BACKEND_TYPE_VHOSTUSER) qemuExtVhostUserGPUStop(driver, vm, video); } for (i = 0; i < def->ntpms; i++) { if (def->tpms[i]->type == VIR_DOMAIN_TPM_TYPE_EMULATOR) qemuExtTPMStop(driver, vm, outgoingMigration); } for (i = 0; i < def->nnets; i++) { virDomainNetDef *net = def->nets[i]; virDomainNetType actualType = virDomainNetGetActualType(net); qemuSlirp *slirp = QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp; if (slirp) qemuSlirpStop(slirp, vm, driver, net); if (net->type == VIR_DOMAIN_NET_TYPE_USER && net->backend.type == VIR_DOMAIN_NET_BACKEND_PASST) { qemuPasstStop(vm, net); } if (actualType == VIR_DOMAIN_NET_TYPE_ETHERNET && net->downscript) virNetDevRunEthernetScript(net->ifname, net->downscript); } for (i = 0; i < def->nfss; i++) { virDomainFSDef *fs = def->fss[i]; if (!fs->sock && fs->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_VIRTIOFS) qemuVirtioFSStop(driver, vm, fs); } for (i = 0; i < def->ndisks; i++) { virDomainDiskDef *disk = def->disks[i]; qemuNbdkitStopStorageSource(disk->src, vm); } if (def->os.loader && def->os.loader->nvram) qemuNbdkitStopStorageSource(def->os.loader->nvram, vm); } bool qemuExtDevicesHasDevice(virDomainDef *def) { size_t i; for (i = 0; i < def->nvideos; i++) { if (def->videos[i]->backend == VIR_DOMAIN_VIDEO_BACKEND_TYPE_VHOSTUSER) return true; } for (i = 0; i < def->nnets; i++) { virDomainNetDef *net = def->nets[i]; if (QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp) return true; if (net->type == VIR_DOMAIN_NET_TYPE_USER && net->backend.type == VIR_DOMAIN_NET_BACKEND_PASST) return true; } for (i = 0; i < def->ntpms; i++) { if (def->tpms[i]->type == VIR_DOMAIN_TPM_TYPE_EMULATOR) return true; } for (i = 0; i < def->nfss; i++) { virDomainFSDef *fs = def->fss[i]; if (fs->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_VIRTIOFS) return true; } for (i = 0; i < def->ndisks; i++) { virDomainDiskDef *disk = def->disks[i]; virStorageSource *backing; for (backing = disk->src; backing; backing = backing->backingStore) { qemuDomainStorageSourcePrivate* priv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(backing); if (priv && priv->nbdkitProcess) return true; } } return false; } /* recursively setup nbdkit cgroups for backing chain of src */ static int qemuExtDevicesSetupCgroupNbdkit(virStorageSource *src, virCgroup *cgroup) { virStorageSource *backing; for (backing = src; backing; backing = backing->backingStore) { qemuDomainStorageSourcePrivate *priv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src); if (priv && priv->nbdkitProcess && qemuNbdkitProcessSetupCgroup(priv->nbdkitProcess, cgroup) < 0) return -1; } return 0; } int qemuExtDevicesSetupCgroup(virQEMUDriver *driver, virDomainObj *vm, virCgroup *cgroup) { virDomainDef *def = vm->def; size_t i; /* Don't forget to adjust qemuExtDevicesHasDevice() accordingly. * Otherwise, this function might not be called at all. */ if (qemuDBusSetupCgroup(driver, vm, cgroup) < 0) return -1; for (i = 0; i < def->nvideos; i++) { virDomainVideoDef *video = def->videos[i]; if (video->backend == VIR_DOMAIN_VIDEO_BACKEND_TYPE_VHOSTUSER && qemuExtVhostUserGPUSetupCgroup(driver, def, video, cgroup) < 0) return -1; } for (i = 0; i < def->nnets; i++) { virDomainNetDef *net = def->nets[i]; qemuSlirp *slirp = QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp; if (slirp && qemuSlirpSetupCgroup(slirp, cgroup) < 0) return -1; if (net->type == VIR_DOMAIN_NET_TYPE_USER && net->backend.type == VIR_DOMAIN_NET_BACKEND_PASST && qemuPasstSetupCgroup(vm, net, cgroup) < 0) { return -1; } } for (i = 0; i < def->ntpms; i++) { if (def->tpms[i]->type == VIR_DOMAIN_TPM_TYPE_EMULATOR && qemuExtTPMSetupCgroup(driver, def, cgroup) < 0) return -1; } for (i = 0; i < def->ndisks; i++) { virDomainDiskDef *disk = def->disks[i]; if (qemuExtDevicesSetupCgroupNbdkit(disk->src, cgroup) < 0) return -1; } if (def->os.loader && def->os.loader->nvram) { if (qemuExtDevicesSetupCgroupNbdkit(def->os.loader->nvram, cgroup) < 0) return -1; } for (i = 0; i < def->nfss; i++) { virDomainFSDef *fs = def->fss[i]; if (!fs->sock && fs->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_VIRTIOFS && qemuVirtioFSSetupCgroup(vm, fs, cgroup) < 0) return -1; } return 0; }