From nobody Sat Feb 7 09:29:32 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=fail(p=none dis=none) header.from=virtuozzo.com ARC-Seal: i=1; a=rsa-sha256; t=1567166952; cv=none; d=zoho.com; s=zohoarc; b=dr/8Nt+Bz5Z67I3TdjstyqXR4aR1NH76Y64O/bYtfgVFFNuL9xGvUUOCh5A/fbngiLEc9m2ZXmqnpb8CESpb0KdFDmoeBHhZRly/Cqielm/34b5kWmiwjNRujVM5cb/7Dy+pqt8Z5qu57HLSjqomUxc4yyQyhl+db6fCntYYxUw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1567166952; h=Content-Type:Content-Transfer-Encoding:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=LowsaZAK6kEs/C0Co4YN11vIoDk7ABzH3D2qsX1UhM0=; b=Km4DQX//KMHo9wjFjfolMxuDAkAu/Bem4QuKgHN1nCHyfWxpjUD3nUUrZ5mgi16kLbUUNfy2uYOQwi/WDxG0HImprKjP+w8vekmLpHVD7egd2GezL3ci08OBzLZDe6mGrDnOyH+hkc0GJd7dUvQx/pny0lFwpO0mFiD/9vvmu4c= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1567166952201669.868161867941; Fri, 30 Aug 2019 05:09:12 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id D205C81129; Fri, 30 Aug 2019 12:09:10 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 8EE1960C05; Fri, 30 Aug 2019 12:09:10 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id CB82C1806B09; Fri, 30 Aug 2019 12:09:09 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id x7UC98Va018310 for ; Fri, 30 Aug 2019 08:09:08 -0400 Received: by smtp.corp.redhat.com (Postfix) id A21BB60A9D; Fri, 30 Aug 2019 12:09:08 +0000 (UTC) Received: from mx1.redhat.com (ext-mx24.extmail.prod.ext.phx2.redhat.com [10.5.110.65]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 9C11760605 for ; Fri, 30 Aug 2019 12:09:06 +0000 (UTC) Received: from relay.sw.ru (relay.sw.ru [185.231.240.75]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 6563510C6976 for ; Fri, 30 Aug 2019 12:09:04 +0000 (UTC) Received: from [172.16.24.106] (helo=shiny.sw.ru) by relay.sw.ru with esmtp (Exim 4.92) (envelope-from ) id 1i3fiA-0000GK-G3 for libvir-list@redhat.com; Fri, 30 Aug 2019 15:09:02 +0300 From: Nikolay Shirokovskiy To: libvir-list@redhat.com Date: Fri, 30 Aug 2019 15:08:30 +0300 Message-Id: <20190830120834.27404-3-nshirokovskiy@virtuozzo.com> In-Reply-To: <20190830120834.27404-1-nshirokovskiy@virtuozzo.com> References: <20190830120834.27404-1-nshirokovskiy@virtuozzo.com> MIME-Version: 1.0 X-Greylist: Sender passed SPF test, ACL 264 matched, not delayed by milter-greylist-4.6.2 (mx1.redhat.com [10.5.110.65]); Fri, 30 Aug 2019 12:09:05 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.6.2 (mx1.redhat.com [10.5.110.65]); Fri, 30 Aug 2019 12:09:05 +0000 (UTC) for IP:'185.231.240.75' DOMAIN:'relay.sw.ru' HELO:'relay.sw.ru' FROM:'nshirokovskiy@virtuozzo.com' RCPT:'' X-RedHat-Spam-Score: 0 (SPF_HELO_NONE, SPF_PASS) 185.231.240.75 relay.sw.ru 185.231.240.75 relay.sw.ru X-Scanned-By: MIMEDefang 2.84 on 10.5.110.65 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-loop: libvir-list@redhat.com Subject: [libvirt] [PATCH 2/6] qemu: handle usb hostdev add/del udev events X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.27]); Fri, 30 Aug 2019 12:09:11 +0000 (UTC) Content-Type: text/plain; charset="utf-8" In either case detach qemu hostdev device (it is dummy in case node hostdev is absent). Meaningful processing will be in DEVICE_DELETED event handler. Signed-off-by: Nikolay Shirokovskiy --- src/qemu/Makefile.inc.am | 2 + src/qemu/qemu_conf.h | 3 + src/qemu/qemu_domain.c | 2 + src/qemu/qemu_domain.h | 2 + src/qemu/qemu_driver.c | 344 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 352 insertions(+), 1 deletion(-) diff --git a/src/qemu/Makefile.inc.am b/src/qemu/Makefile.inc.am index d16b315ebc..8be0dee396 100644 --- a/src/qemu/Makefile.inc.am +++ b/src/qemu/Makefile.inc.am @@ -85,6 +85,7 @@ libvirt_driver_qemu_impl_la_CFLAGS =3D \ -I$(srcdir)/conf \ -I$(srcdir)/secret \ $(AM_CFLAGS) \ + $(UDEV_CFLAGS) \ $(NULL) libvirt_driver_qemu_impl_la_LDFLAGS =3D $(AM_LDFLAGS) libvirt_driver_qemu_impl_la_LIBADD =3D \ @@ -93,6 +94,7 @@ libvirt_driver_qemu_impl_la_LIBADD =3D \ $(LIBNL_LIBS) \ $(SELINUX_LIBS) \ $(LIBXML_LIBS) \ + $(UDEV_LIBS) \ $(NULL) libvirt_driver_qemu_impl_la_SOURCES =3D $(QEMU_DRIVER_SOURCES) =20 diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 0cbddd7a9c..2e50bb0950 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -294,6 +294,9 @@ struct _virQEMUDriver { =20 /* Immutable pointer, self-locking APIs */ virHashAtomicPtr migrationErrors; + + struct udev_monitor *udev_monitor; + int udev_watch; }; =20 virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 657f3ecfe4..4784804d1e 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -15034,6 +15034,8 @@ qemuProcessEventFree(struct qemuProcessEvent *event) case QEMU_PROCESS_EVENT_SERIAL_CHANGED: case QEMU_PROCESS_EVENT_BLOCK_JOB: case QEMU_PROCESS_EVENT_MONITOR_EOF: + case QEMU_PROCESS_EVENT_USB_REMOVED: + case QEMU_PROCESS_EVENT_USB_ADDED: VIR_FREE(event->data); break; case QEMU_PROCESS_EVENT_JOB_STATUS_CHANGE: diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index d097f23342..94aea62693 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -521,6 +521,8 @@ typedef enum { QEMU_PROCESS_EVENT_MONITOR_EOF, QEMU_PROCESS_EVENT_PR_DISCONNECT, QEMU_PROCESS_EVENT_RDMA_GID_STATUS_CHANGED, + QEMU_PROCESS_EVENT_USB_REMOVED, + QEMU_PROCESS_EVENT_USB_ADDED, =20 QEMU_PROCESS_EVENT_LAST } qemuProcessEventType; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 2378a2e7d0..33b75a3c71 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -34,6 +34,7 @@ #include #include #include +#include =20 =20 #include "qemu_driver.h" @@ -719,6 +720,254 @@ qemuDomainFindMaxID(virDomainObjPtr vm, } =20 =20 +struct qemuUdevUSBRemoveData { + unsigned int bus; + unsigned int device; +}; + +struct qemuUdevUSBAddData { + unsigned int vendor; + unsigned int product; +}; + +struct qemuUdevUSBEventData { + union { + struct qemuUdevUSBRemoveData remove; + struct qemuUdevUSBAddData add; + } data; + bool found; + bool remove; +}; + +static int +qemuUdevUSBHandleEvent(virDomainObjPtr vm, void *opaque) +{ + struct qemuUdevUSBEventData *data =3D opaque; + struct qemuProcessEvent *event =3D NULL; + size_t i; + + if (data->found) + return 0; + + virObjectLock(vm); + + if (!virDomainObjIsActive(vm)) + goto cleanup; + + for (i =3D 0; i < vm->def->nhostdevs; i++) { + virDomainHostdevDefPtr hostdev =3D vm->def->hostdevs[i]; + virDomainHostdevSubsysUSBPtr usbsrc =3D &hostdev->source.subsys.u.= usb; + + if (hostdev->source.subsys.type !=3D VIR_DOMAIN_HOSTDEV_SUBSYS_TYP= E_USB) + continue; + + if (data->remove) { + if (usbsrc->bus !=3D data->data.remove.bus || + usbsrc->device !=3D data->data.remove.device) + continue; + } else { + if (usbsrc->vendor !=3D data->data.add.vendor || + usbsrc->product !=3D data->data.add.product) + continue; + } + + data->found =3D true; + + if (VIR_ALLOC(event) < 0) + goto cleanup; + + if (data->remove) { + struct qemuUdevUSBRemoveData *rm_data; + + + if (VIR_ALLOC(rm_data) < 0) + goto cleanup; + + *rm_data =3D data->data.remove; + event->data =3D rm_data; + event->eventType =3D QEMU_PROCESS_EVENT_USB_REMOVED; + } else { + struct qemuUdevUSBAddData *add_data; + + if (VIR_ALLOC(add_data) < 0) + goto cleanup; + + *add_data =3D data->data.add; + event->data =3D add_data; + event->eventType =3D QEMU_PROCESS_EVENT_USB_ADDED; + } + + event->vm =3D virObjectRef(vm); + + if (virThreadPoolSendJob(qemu_driver->workerPool, 0, event) < 0) { + virObjectUnref(vm); + goto cleanup; + } + + event =3D NULL; + + break; + } + + cleanup: + virObjectUnlock(vm); + + qemuProcessEventFree(event); + + return 0; +} + + +static void +qemuUdevEventHandleCallback(int watch ATTRIBUTE_UNUSED, + int fd ATTRIBUTE_UNUSED, + int events ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED) +{ + struct qemuUdevUSBEventData event_data; + struct udev_device *dev =3D NULL; + const char *action; + const char *devtype; + const char *tmp; + + /* libvirtd daemon do not run event loop before full state drivers + * initialization. Also state drivers uninitialized only after + * full stop of event loop. In short driver initialization/uninitializ= ation + * and handling events occurs in same main loop thread. Thus we + * don't need any locking here. */ + + if (!(dev =3D udev_monitor_receive_device(qemu_driver->udev_monitor)))= { + VIR_WARNINGS_NO_WLOGICALOP_EQUAL_EXPR + if (errno =3D=3D EAGAIN || errno =3D=3D EWOULDBLOCK) { + VIR_WARNINGS_RESET + return; + } + + virReportSystemError(errno, "%s", + _("failed to receive device from udev monitor= ")); + return; + } + + devtype =3D udev_device_get_devtype(dev); + + if (STRNEQ_NULLABLE(devtype, "usb_device")) + goto cleanup; + + if (!(action =3D udev_device_get_action(dev))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to receive action from udev monitor")); + goto cleanup; + } + + if (STREQ(action, "remove")) { + struct qemuUdevUSBRemoveData *rm_data =3D &event_data.data.remove; + + if (!(tmp =3D udev_device_get_property_value(dev, "BUSNUM"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to receive busnum from udev monitor")= ); + goto cleanup; + } + if (virStrToLong_ui(tmp, NULL, 10, &rm_data->bus) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed to convert busnum to int")); + goto cleanup; + } + + if (!(tmp =3D udev_device_get_property_value(dev, "DEVNUM"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to receive devnum from udev monitor")= ); + goto cleanup; + } + if (virStrToLong_ui(tmp, NULL, 10, &rm_data->device) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed to convert devnum to int")); + goto cleanup; + } + event_data.remove =3D true; + } else if (STREQ(action, "add")) { + struct qemuUdevUSBAddData *add_data =3D &event_data.data.add; + + if (!(tmp =3D udev_device_get_property_value(dev, "ID_VENDOR_ID"))= ) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to receive vendor from udev monitor")= ); + goto cleanup; + } + if (virStrToLong_ui(tmp, NULL, 16, &add_data->vendor) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed to convert vendor to int")); + goto cleanup; + } + + if (!(tmp =3D udev_device_get_property_value(dev, "ID_MODEL_ID")))= { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to receive product from udev monitor"= )); + goto cleanup; + } + if (virStrToLong_ui(tmp, NULL, 16, &add_data->product) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed to convert product to int")); + goto cleanup; + } + event_data.remove =3D false; + } + + event_data.found =3D false; + virDomainObjListForEach(qemu_driver->domains, qemuUdevUSBHandleEvent, = &event_data); + + cleanup: + udev_device_unref(dev); +} + + +static int +qemuUdevInitialize(void) +{ + struct udev *udev; + + if (!(udev =3D udev_new())) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create udev context")); + return -1; + } + + if (!(qemu_driver->udev_monitor =3D udev_monitor_new_from_netlink(udev= , "udev"))) { + udev_unref(udev); + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("udev_monitor_new_from_netlink returned NULL")); + return -1; + } + + udev_monitor_enable_receiving(qemu_driver->udev_monitor); + + qemu_driver->udev_watch =3D virEventAddHandle(udev_monitor_get_fd(qemu= _driver->udev_monitor), + VIR_EVENT_HANDLE_READABLE, + qemuUdevEventHandleCallbac= k, NULL, NULL); + + if (qemu_driver->udev_watch < 0) + return -1; + + return 0; +} + + +static void +qemuUdevCleanup(void) +{ + if (qemu_driver->udev_monitor) { + struct udev *udev =3D udev_monitor_get_udev(qemu_driver->udev_moni= tor); + + udev_monitor_unref(qemu_driver->udev_monitor); + udev_unref(udev); + qemu_driver->udev_monitor =3D NULL; + } + + if (qemu_driver->udev_watch > 0) { + virEventRemoveHandle(qemu_driver->udev_watch); + qemu_driver->udev_watch =3D 0; + } +} + + /** * qemuStateInitialize: * @@ -1030,6 +1279,9 @@ qemuStateInitialize(bool privileged, if (!(qemu_driver->closeCallbacks =3D virCloseCallbacksNew())) goto error; =20 + if (qemuUdevInitialize() < 0) + goto error; + /* Get all the running persistent or transient configs first */ if (virDomainObjListLoadAllConfigs(qemu_driver->domains, cfg->stateDir, @@ -1239,6 +1491,8 @@ qemuStateCleanup(void) =20 virLockManagerPluginUnref(qemu_driver->lockManager); =20 + qemuUdevCleanup(); + virMutexDestroy(&qemu_driver->lock); VIR_FREE(qemu_driver); =20 @@ -5011,7 +5265,89 @@ processRdmaGidStatusChangedEvent(virDomainObjPtr vm, } =20 =20 -static void qemuProcessEventHandler(void *data, void *opaque) +static void +processUSBAddedEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + struct qemuUdevUSBAddData *data) +{ + virDomainDeviceDef dev =3D { .type =3D VIR_DOMAIN_DEVICE_HOSTDEV }; + virDomainHostdevDefPtr hostdev; + size_t i; + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + return; + + if (!virDomainObjIsActive(vm)) { + VIR_DEBUG("Domain is not running"); + goto cleanup; + } + + for (i =3D 0; i < vm->def->nhostdevs; i++) { + virDomainHostdevSubsysUSBPtr usbsrc; + + hostdev =3D vm->def->hostdevs[i]; + usbsrc =3D &hostdev->source.subsys.u.usb; + + if (hostdev->source.subsys.type =3D=3D VIR_DOMAIN_HOSTDEV_SUBSYS_T= YPE_USB && + usbsrc->vendor =3D=3D data->vendor && usbsrc->product =3D=3D d= ata->product && + hostdev->missing) + break; + } + + if (i =3D=3D vm->def->nhostdevs) + goto cleanup; + + dev.data.hostdev =3D hostdev; + if (qemuDomainDetachDeviceLive(vm, &dev, driver, true, true) < 0) + goto cleanup; + + cleanup: + qemuDomainObjEndJob(driver, vm); +} + + +static void +processUSBRemovedEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + struct qemuUdevUSBRemoveData *data) +{ + size_t i; + virDomainDeviceDef dev =3D { .type =3D VIR_DOMAIN_DEVICE_HOSTDEV }; + virDomainHostdevDefPtr hostdev; + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + return; + + if (!virDomainObjIsActive(vm)) { + VIR_DEBUG("Domain is not running"); + goto cleanup; + } + + for (i =3D 0; i < vm->def->nhostdevs; i++) { + virDomainHostdevSubsysUSBPtr usbsrc; + + hostdev =3D vm->def->hostdevs[i]; + usbsrc =3D &hostdev->source.subsys.u.usb; + + if (hostdev->source.subsys.type =3D=3D VIR_DOMAIN_HOSTDEV_SUBSYS_T= YPE_USB && + usbsrc->bus =3D=3D data->bus && usbsrc->device =3D=3D data->de= vice) + break; + } + + if (i =3D=3D vm->def->nhostdevs) + goto cleanup; + + dev.data.hostdev =3D hostdev; + if (qemuDomainDetachDeviceLive(vm, &dev, driver, true, true) < 0) + goto cleanup; + + cleanup: + qemuDomainObjEndJob(driver, vm); +} + + +static void +qemuProcessEventHandler(void *data, void *opaque) { struct qemuProcessEvent *processEvent =3D data; virDomainObjPtr vm =3D processEvent->vm; @@ -5057,6 +5393,12 @@ static void qemuProcessEventHandler(void *data, void= *opaque) case QEMU_PROCESS_EVENT_RDMA_GID_STATUS_CHANGED: processRdmaGidStatusChangedEvent(vm, processEvent->data); break; + case QEMU_PROCESS_EVENT_USB_REMOVED: + processUSBRemovedEvent(driver, vm, processEvent->data); + break; + case QEMU_PROCESS_EVENT_USB_ADDED: + processUSBAddedEvent(driver, vm, processEvent->data); + break; case QEMU_PROCESS_EVENT_LAST: break; } --=20 2.23.0 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list