"/dev/null", "/dev/full", "/dev/zero",
"/dev/random", "/dev/urandom",
"/dev/ptmx", "/dev/kvm", "/dev/kqemu",
- "/dev/rtc", "/dev/hpet", "/dev/vfio/vfio",
+ "/dev/rtc", "/dev/hpet",
NULL,
};
#define DEVICE_PTY_MAJOR 136
#define DEVICE_SND_MAJOR 116
+#define DEV_VFIO "/dev/vfio/vfio"
static int
qemuSetupImagePathCgroup(virDomainObjPtr vm,
virDomainHostdevDefPtr dev)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
- char *path = NULL;
- int perms;
- int ret = -1;
+ char **path = NULL;
+ int *perms = NULL;
+ size_t i, npaths = 0;
+ int rv, ret = -1;
- if (qemuDomainGetHostdevPath(dev, &path, &perms) < 0)
+ if (qemuDomainGetHostdevPath(dev, &npaths, &path, &perms) < 0)
goto cleanup;
- if (!path) {
- /* There's no path that we need to allow. Claim success. */
- ret = 0;
- goto cleanup;
+ for (i = 0; i < npaths; i++) {
+ VIR_DEBUG("Cgroup allow %s perms=%d", path[i], perms[i]);
+ rv = virCgroupAllowDevicePath(priv->cgroup, path[i], perms[i], false);
+ virDomainAuditCgroupPath(vm, priv->cgroup, "allow", path[i],
+ virCgroupGetDevicePermsString(perms[i]),
+ ret == 0);
+ if (rv < 0)
+ goto cleanup;
}
- VIR_DEBUG("Cgroup allow %s perms=%d", path, perms);
- ret = virCgroupAllowDevicePath(priv->cgroup, path, perms, false);
- virDomainAuditCgroupPath(vm, priv->cgroup, "allow", path,
- virCgroupGetDevicePermsString(perms), ret == 0);
+ ret = 0;
cleanup:
+ for (i = 0; i < npaths; i++)
+ VIR_FREE(path[i]);
VIR_FREE(path);
+ VIR_FREE(perms);
return ret;
}
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
if (pcisrc->backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO) {
int rv;
+ size_t i, vfios = 0;
pci = virPCIDeviceNew(pcisrc->addr.domain,
pcisrc->addr.bus,
"deny", path, "rwm", rv == 0);
if (rv < 0)
goto cleanup;
+
+ /* If this is the last hostdev with VFIO backend deny
+ * /dev/vfio/vfio too. */
+ for (i = 0; i < vm->def->nhostdevs; i++) {
+ virDomainHostdevDefPtr tmp = vm->def->hostdevs[i];
+ if (tmp->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
+ tmp->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI &&
+ tmp->source.subsys.u.pci.backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO)
+ vfios++;
+ }
+
+ if (vfios == 0) {
+ VIR_DEBUG("Cgroup deny " DEV_VFIO " for PCI device assignment");
+ rv = virCgroupDenyDevicePath(priv->cgroup, DEV_VFIO,
+ VIR_CGROUP_DEVICE_RWM, false);
+ virDomainAuditCgroupPath(vm, priv->cgroup,
+ "deny", DEV_VFIO, "rwm", rv == 0);
+ if (rv < 0)
+ goto cleanup;
+ }
}
break;
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
#define PROC_MOUNTS "/proc/mounts"
#define DEVPREFIX "/dev/"
+#define DEV_VFIO "/dev/vfio/vfio"
struct _qemuDomainLogContext {
/**
* qemuDomainGetHostdevPath:
* @dev: host device definition
+ * @npaths: number of items in @path and @perms arrays
* @path: resulting path to @dev
* @perms: Optional pointer to VIR_CGROUP_DEVICE_* perms
*
- * For given device @dev fetch its host path and store it at @path. Optionally,
- * caller can get @perms on the path (e.g. rw/ro).
+ * For given device @dev fetch its host path and store it at
+ * @path. If a device requires other paths to be present/allowed
+ * they are stored in the @path array after the actual path.
+ * Optionally, caller can get @perms on the path (e.g. rw/ro).
+ *
+ * The caller is responsible for freeing the memory.
*
* Returns 0 on success, -1 otherwise.
*/
int
qemuDomainGetHostdevPath(virDomainHostdevDefPtr dev,
- char **path,
- int *perms)
+ size_t *npaths,
+ char ***path,
+ int **perms)
{
int ret = -1;
virDomainHostdevSubsysUSBPtr usbsrc = &dev->source.subsys.u.usb;
virSCSIVHostDevicePtr host = NULL;
char *tmpPath = NULL;
bool freeTmpPath = false;
+ bool includeVFIO = false;
+ char **tmpPaths = NULL;
+ int *tmpPerms = NULL;
+ size_t i, tmpNpaths = 0;
+ int perm = 0;
- *path = NULL;
+ *npaths = 0;
switch ((virDomainHostdevMode) dev->mode) {
case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS:
if (!(tmpPath = virPCIDeviceGetIOMMUGroupDev(pci)))
goto cleanup;
freeTmpPath = true;
- if (perms)
- *perms = VIR_CGROUP_DEVICE_RW;
+
+ perm = VIR_CGROUP_DEVICE_RW;
+ includeVFIO = true;
}
break;
if (!(tmpPath = (char *) virUSBDeviceGetPath(usb)))
goto cleanup;
- if (perms)
- *perms = VIR_CGROUP_DEVICE_RW;
+ perm = VIR_CGROUP_DEVICE_RW;
break;
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI:
if (!(tmpPath = (char *) virSCSIDeviceGetPath(scsi)))
goto cleanup;
- if (perms)
- *perms = virSCSIDeviceGetReadonly(scsi) ?
- VIR_CGROUP_DEVICE_READ : VIR_CGROUP_DEVICE_RW;
+ perm = virSCSIDeviceGetReadonly(scsi) ?
+ VIR_CGROUP_DEVICE_READ : VIR_CGROUP_DEVICE_RW;
}
break;
if (!(tmpPath = (char *) virSCSIVHostDeviceGetPath(host)))
goto cleanup;
- if (perms)
- *perms = VIR_CGROUP_DEVICE_RW;
+ perm = VIR_CGROUP_DEVICE_RW;
}
break;
}
break;
}
- if (VIR_STRDUP(*path, tmpPath) < 0)
- goto cleanup;
+ if (tmpPath) {
+ size_t toAlloc = 1;
+
+ if (includeVFIO)
+ toAlloc = 2;
+
+ if (VIR_ALLOC_N(tmpPaths, toAlloc) < 0 ||
+ VIR_ALLOC_N(tmpPerms, toAlloc) < 0 ||
+ VIR_STRDUP(tmpPaths[0], tmpPath) < 0)
+ goto cleanup;
+ tmpNpaths = toAlloc;
+ tmpPerms[0] = perm;
+
+ if (includeVFIO) {
+ if (VIR_STRDUP(tmpPaths[1], DEV_VFIO) < 0)
+ goto cleanup;
+ tmpPerms[1] = VIR_CGROUP_DEVICE_RW;
+ }
+ }
+ *npaths = tmpNpaths;
+ tmpNpaths = 0;
+ *path = tmpPaths;
+ tmpPaths = NULL;
+ if (perms) {
+ *perms = tmpPerms;
+ tmpPerms = NULL;
+ }
ret = 0;
cleanup:
+ for (i = 0; i < tmpNpaths; i++)
+ VIR_FREE(tmpPaths[i]);
+ VIR_FREE(tmpPaths);
+ VIR_FREE(tmpPerms);
virPCIDeviceFree(pci);
virUSBDeviceFree(usb);
virSCSIDeviceFree(scsi);
const char *devPath)
{
int ret = -1;
- char *path = NULL;
+ char **path = NULL;
+ size_t i, npaths = 0;
- if (qemuDomainGetHostdevPath(dev, &path, NULL) < 0)
+ if (qemuDomainGetHostdevPath(dev, &npaths, &path, NULL) < 0)
goto cleanup;
- if (!path) {
- /* There's no /dev device that we need to create. Claim success. */
- ret = 0;
- goto cleanup;
+ for (i = 0; i < npaths; i++) {
+ if (qemuDomainCreateDevice(path[i], devPath, false) < 0)
+ goto cleanup;
}
- if (qemuDomainCreateDevice(path, devPath, false) < 0)
- goto cleanup;
-
ret = 0;
cleanup:
+ for (i = 0; i < npaths; i++)
+ VIR_FREE(path[i]);
VIR_FREE(path);
return ret;
}
virDomainHostdevDefPtr hostdev)
{
int ret = -1;
- char *path = NULL;
+ char **path = NULL;
+ size_t i, npaths = 0;
if (!qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
return 0;
- if (qemuDomainGetHostdevPath(hostdev, &path, NULL) < 0)
+ if (qemuDomainGetHostdevPath(hostdev, &npaths, &path, NULL) < 0)
goto cleanup;
- if (!path) {
- /* There's no /dev device that we need to create. Claim success. */
- ret = 0;
+ for (i = 0; i < npaths; i++) {
+ if (qemuDomainAttachDeviceMknod(driver,
+ vm,
+ path[i]) < 0)
goto cleanup;
}
- if (qemuDomainAttachDeviceMknod(driver,
- vm,
- path) < 0)
- goto cleanup;
ret = 0;
cleanup:
+ for (i = 0; i < npaths; i++)
+ VIR_FREE(path[i]);
VIR_FREE(path);
return ret;
}
virDomainHostdevDefPtr hostdev)
{
int ret = -1;
- char *path = NULL;
+ char **path = NULL;
+ size_t i, npaths = 0;
if (!qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
return 0;
- if (qemuDomainGetHostdevPath(hostdev, &path, NULL) < 0)
+ if (qemuDomainGetHostdevPath(hostdev, &npaths, &path, NULL) < 0)
goto cleanup;
- if (!path) {
- /* There's no /dev device that we need to create. Claim success. */
- ret = 0;
- goto cleanup;
- }
-
- if (qemuDomainDetachDeviceUnlink(driver, vm, path) < 0)
+ /* Don't remove other paths than for the @hostdev itself.
+ * They might be still in use by other devices. */
+ if (npaths > 0 &&
+ qemuDomainDetachDeviceUnlink(driver, vm, path[0]) < 0)
goto cleanup;
ret = 0;
cleanup:
+ for (i = 0; i < npaths; i++)
+ VIR_FREE(path[i]);
VIR_FREE(path);
return ret;
}