qemu: Implement support for hotplugging evdev input devices

Unlike other input types, evdev is not a true device since it's backed by
'-object'. We must use object-add/object-del monitor commands instead of
device-add/device-del in this particular case.

This patch adds support for handling live attachment and
detachment of evdev type devices.

Resolves: https://gitlab.com/libvirt/libvirt/-/issues/529
Signed-off-by: Rayhan Faizel <rayhan.faizel@gmail.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
Rayhan Faizel 2024-05-15 15:57:53 +05:30 committed by Michal Privoznik
parent 94108cdd59
commit 57f29f675d
3 changed files with 74 additions and 32 deletions

View File

@ -4367,7 +4367,7 @@ qemuBuildInputUSBDevProps(const virDomainDef *def,
}
static virJSONValue *
virJSONValue *
qemuBuildInputEvdevProps(virDomainInputDef *dev)
{
g_autoptr(virJSONValue) props = NULL;

View File

@ -233,6 +233,9 @@ virJSONValue *
qemuBuildInputUSBDevProps(const virDomainDef *def,
virDomainInputDef *dev);
virJSONValue *
qemuBuildInputEvdevProps(virDomainInputDef *dev);
virJSONValue *
qemuBuildVsockDevProps(virDomainDef *def,
virDomainVsockDef *vsock,

View File

@ -3016,36 +3016,40 @@ qemuDomainAttachInputDevice(virDomainObj *vm,
bool teardowncgroup = false;
qemuAssignDeviceInputAlias(vm->def, input, -1);
if (input->type == VIR_DOMAIN_INPUT_TYPE_EVDEV) {
if (!(devprops = qemuBuildInputEvdevProps(input)))
goto cleanup;
} else {
switch ((virDomainInputBus) input->bus) {
case VIR_DOMAIN_INPUT_BUS_USB:
if (virDomainUSBAddressEnsure(priv->usbaddrs, &input->info) < 0)
return -1;
switch ((virDomainInputBus) input->bus) {
case VIR_DOMAIN_INPUT_BUS_USB:
if (virDomainUSBAddressEnsure(priv->usbaddrs, &input->info) < 0)
releaseaddr = true;
if (!(devprops = qemuBuildInputUSBDevProps(vm->def, input)))
goto cleanup;
break;
case VIR_DOMAIN_INPUT_BUS_VIRTIO:
if (qemuDomainEnsureVirtioAddress(&releaseaddr, vm, &dev) < 0)
goto cleanup;
if (!(devprops = qemuBuildInputVirtioDevProps(vm->def, input, priv->qemuCaps)))
goto cleanup;
break;
case VIR_DOMAIN_INPUT_BUS_DEFAULT:
case VIR_DOMAIN_INPUT_BUS_PS2:
case VIR_DOMAIN_INPUT_BUS_XEN:
case VIR_DOMAIN_INPUT_BUS_PARALLELS:
case VIR_DOMAIN_INPUT_BUS_NONE:
case VIR_DOMAIN_INPUT_BUS_LAST:
virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
_("input device on bus '%1$s' cannot be hot plugged."),
virDomainInputBusTypeToString(input->bus));
return -1;
releaseaddr = true;
if (!(devprops = qemuBuildInputUSBDevProps(vm->def, input)))
goto cleanup;
break;
case VIR_DOMAIN_INPUT_BUS_VIRTIO:
if (qemuDomainEnsureVirtioAddress(&releaseaddr, vm, &dev) < 0)
goto cleanup;
if (!(devprops = qemuBuildInputVirtioDevProps(vm->def, input, priv->qemuCaps)))
goto cleanup;
break;
case VIR_DOMAIN_INPUT_BUS_DEFAULT:
case VIR_DOMAIN_INPUT_BUS_PS2:
case VIR_DOMAIN_INPUT_BUS_XEN:
case VIR_DOMAIN_INPUT_BUS_PARALLELS:
case VIR_DOMAIN_INPUT_BUS_NONE:
case VIR_DOMAIN_INPUT_BUS_LAST:
virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
_("input device on bus '%1$s' cannot be hot plugged."),
virDomainInputBusTypeToString(input->bus));
return -1;
}
}
if (qemuDomainNamespaceSetupInput(vm, input, &teardowndevice) < 0)
@ -3066,9 +3070,14 @@ qemuDomainAttachInputDevice(virDomainObj *vm,
if (qemuDomainAttachExtensionDevice(priv->mon, &input->info) < 0)
goto exit_monitor;
if (qemuMonitorAddDeviceProps(priv->mon, &devprops) < 0) {
ignore_value(qemuDomainDetachExtensionDevice(priv->mon, &input->info));
goto exit_monitor;
if (input->type == VIR_DOMAIN_INPUT_TYPE_EVDEV) {
if (qemuMonitorAddObject(priv->mon, &devprops, NULL) < 0)
goto exit_monitor;
} else {
if (qemuMonitorAddDeviceProps(priv->mon, &devprops) < 0) {
ignore_value(qemuDomainDetachExtensionDevice(priv->mon, &input->info));
goto exit_monitor;
}
}
qemuDomainObjExitMonitor(vm);
@ -6093,6 +6102,29 @@ qemuDomainDetachDeviceLease(virQEMUDriver *driver,
}
static int
qemuDomainDetachDeviceInputEvdev(virQEMUDriver *driver,
virDomainObj *vm,
virDomainDeviceDef *detach)
{
int rc;
virDomainInputDef *input = detach->data.input;
qemuDomainObjPrivate *priv = vm->privateData;
qemuDomainObjEnterMonitor(vm);
rc = qemuMonitorDelObject(priv->mon, input->info.alias, true);
qemuDomainObjExitMonitor(vm);
if (rc < 0)
return -1;
if (qemuDomainRemoveDevice(driver, vm, detach) < 0)
return -1;
return 0;
}
int
qemuDomainDetachDeviceLive(virDomainObj *vm,
virDomainDeviceDef *match,
@ -6176,6 +6208,13 @@ qemuDomainDetachDeviceLive(virDomainObj *vm,
&detach.data.input) < 0) {
return -1;
}
/*
* Input devices of type 'evdev' are regular QOM objects
* (-object instead of -device), so it must be handled differently.
*/
if (detach.data.input->type == VIR_DOMAIN_INPUT_TYPE_EVDEV)
return qemuDomainDetachDeviceInputEvdev(driver, vm, &detach);
break;
case VIR_DOMAIN_DEVICE_REDIRDEV:
if (qemuDomainDetachPrepRedirdev(vm, match->data.redirdev,