From nobody Mon Feb 9 19:53:12 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 63.128.21.124 as permitted sender) client-ip=63.128.21.124; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-delivery-124.mimecast.com; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 63.128.21.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1608819436; cv=none; d=zohomail.com; s=zohoarc; b=Vk9FjQhmg37QMGydVt/wNs4F9hVH2fXMHVsXO6SQ2ji4YzyMKq0hTyH5eNCrf4poAXxqU0dHXUXZm8KvwTejGuyhvF/ADog9/eoUSVsdsy11pvxIsA5N0e3sUxv/qXk5HWLjxbtyvLYjKADmtoUFzNDOhW3TnFg1TLeEqE+qAWc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1608819436; h=Content-Type:Content-Transfer-Encoding:Cc: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; bh=XCxUpLUD7Wz1AobkrQJOvIRf42ZIjdIKuaezo1GK6NA=; b=mw5UNNxpLrp6/JCofure+gORj5jl2Du26NE+86TAbtRtR3NOBFQtWr0R6ANFvT7SUHl62Ce5yXAN3dS/Fz1kItVEtO6SyBtyrxfNUaz5z/02ASJuyhh2lFp/9c91Wa5VhYOj4NarjIC5las2KCjyOjnVgQdHLlo6qUQy8fEMpPU= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 63.128.21.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass header.from= (p=none dis=none) header.from= Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [63.128.21.124]) by mx.zohomail.com with SMTPS id 1608819435988261.52398967802924; Thu, 24 Dec 2020 06:17:15 -0800 (PST) Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-473-JX-4aVuAPBGGB2_xWcyunQ-1; Thu, 24 Dec 2020 09:15:29 -0500 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 287CF8030B3; Thu, 24 Dec 2020 14:15:22 +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 01AC0271AD; Thu, 24 Dec 2020 14:15:22 +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 C3B7E1809CAC; Thu, 24 Dec 2020 14:15:21 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 0BOEEse1005672 for ; Thu, 24 Dec 2020 09:14:54 -0500 Received: by smtp.corp.redhat.com (Postfix) id 7714563747; Thu, 24 Dec 2020 14:14:54 +0000 (UTC) Received: from himantopus.redhat.com (ovpn-112-15.phx2.redhat.com [10.3.112.15]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 30CF05C1A3; Thu, 24 Dec 2020 14:14:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1608819434; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:list-id:list-help: list-unsubscribe:list-subscribe:list-post; bh=XCxUpLUD7Wz1AobkrQJOvIRf42ZIjdIKuaezo1GK6NA=; b=eW8axEYT2U+c5LQdnA31tJ8S+mNznsUs2i66Re8g++iIzB5ZF1GXpP3uHMle/jq57MMgNl d3IGIOX3vFNVIwpygDaOPnwoVYsGkwVyFqslQg3mDDAiYWTsFPXmO+VwSEfeU13H0NJAkh 2LYwOrxlzjVQ7jXczlXF3O2zXC6F1H4= X-MC-Unique: JX-4aVuAPBGGB2_xWcyunQ-1 From: Jonathon Jongsma To: libvir-list@redhat.com Subject: [libvirt PATCH v3 11/21] nodedev: Refresh mdev devices when changes are detected Date: Thu, 24 Dec 2020 08:14:35 -0600 Message-Id: <20201224141445.163819-12-jjongsma@redhat.com> In-Reply-To: <20201224141445.163819-1-jjongsma@redhat.com> References: <20201224141445.163819-1-jjongsma@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-loop: libvir-list@redhat.com Cc: eskultet@redhat.com 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: , Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=libvir-list-bounces@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @redhat.com) Content-Type: text/plain; charset="utf-8" We need to periodically query mdevctl for changes to device definitions since an administrator can define new devices with mdevctl outside of libvirt. In the future, mdevctl may add a way to signal device add/remove via events, but for now we resort to a bit of a workaround: monitoring the mdevctl config directories for changes to files. When a change is detected, we query mdevctl and update our device list. The mdevctl querying is handled by the existing udev thread. Signed-off-by: Jonathon Jongsma --- src/node_device/node_device_udev.c | 204 ++++++++++++++++++++++++----- 1 file changed, 171 insertions(+), 33 deletions(-) diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_devi= ce_udev.c index d7f7ab4370..5729fea264 100644 --- a/src/node_device/node_device_udev.c +++ b/src/node_device/node_device_udev.c @@ -41,6 +41,7 @@ #include "virnetdev.h" #include "virmdev.h" #include "virutil.h" +#include =20 #include "configmake.h" =20 @@ -66,6 +67,10 @@ struct _udevEventData { virCond threadCond; bool threadQuit; bool udevReady; + bool mdevReady; + + GList *mdevctlMonitors; + int mdevctlTimeout; }; =20 static virClassPtr udevEventDataClass; @@ -85,6 +90,7 @@ udevEventDataDispose(void *obj) udev =3D udev_monitor_get_udev(priv->udev_monitor); udev_monitor_unref(priv->udev_monitor); udev_unref(udev); + g_list_free_full(priv->mdevctlMonitors, g_object_unref); =20 virCondDestroy(&priv->threadCond); } @@ -1821,7 +1827,7 @@ udevEventHandleThread(void *opaque G_GNUC_UNUSED) /* continue rather than break from the loop on non-fatal errors */ while (1) { virObjectLock(priv); - while (!priv->udevReady && !priv->threadQuit) { + while (!priv->udevReady && !priv->mdevReady && !priv->threadQuit) { if (virCondWait(&priv->threadCond, &priv->parent.lock)) { virReportSystemError(errno, "%s", _("handler failed to wait on conditio= n")); @@ -1835,46 +1841,58 @@ udevEventHandleThread(void *opaque G_GNUC_UNUSED) return; } =20 - errno =3D 0; - device =3D udev_monitor_receive_device(priv->udev_monitor); - virObjectUnlock(priv); + if (priv->udevReady) { + errno =3D 0; + device =3D udev_monitor_receive_device(priv->udev_monitor); + virObjectUnlock(priv); =20 - if (!device) { - if (errno =3D=3D 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("failed to receive device from udev monit= or")); - return; - } + if (!device) { + if (errno =3D=3D 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to receive device from udev m= onitor")); + return; + } + + /* POSIX allows both EAGAIN and EWOULDBLOCK to be used + * interchangeably when the read would block or timeout wa= s fired + */ + VIR_WARNINGS_NO_WLOGICALOP_EQUAL_EXPR + if (errno !=3D EAGAIN && errno !=3D EWOULDBLOCK) { + VIR_WARNINGS_RESET + virReportSystemError(errno, "%s", + _("failed to receive device from = udev " + "monitor")); + return; + } + + /* Trying to move the reset of the @priv->udevReady flag to + * after the udev_monitor_receive_device wouldn't help much + * due to event mgmt and scheduler timing. */ + virObjectLock(priv); + priv->udevReady =3D false; + virObjectUnlock(priv); =20 - /* POSIX allows both EAGAIN and EWOULDBLOCK to be used - * interchangeably when the read would block or timeout was fi= red - */ - VIR_WARNINGS_NO_WLOGICALOP_EQUAL_EXPR - if (errno !=3D EAGAIN && errno !=3D EWOULDBLOCK) { - VIR_WARNINGS_RESET - virReportSystemError(errno, "%s", - _("failed to receive device from udev= " - "monitor")); - return; + continue; } =20 - /* Trying to move the reset of the @priv->udevReady flag to - * after the udev_monitor_receive_device wouldn't help much - * due to event mgmt and scheduler timing. */ - virObjectLock(priv); - priv->udevReady =3D false; - virObjectUnlock(priv); + udevHandleOneDevice(device); + udev_device_unref(device); =20 - continue; + /* Instead of waiting for the next event after processing @dev= ice + * data, let's keep reading from the udev monitor and only wait + * for the next event once either a EAGAIN or a EWOULDBLOCK er= ror + * is encountered. */ } =20 - udevHandleOneDevice(device); - udev_device_unref(device); + if (priv->mdevReady) { + virObjectUnlock(priv); + if (nodeDeviceUpdateMediatedDevices() < 0) + VIR_WARN("mdevctl failed to updated mediated devices"); =20 - /* Instead of waiting for the next event after processing @device - * data, let's keep reading from the udev monitor and only wait - * for the next event once either a EAGAIN or a EWOULDBLOCK error - * is encountered. */ + virObjectLock(priv); + priv->mdevReady =3D false; + virObjectUnlock(priv); + } } } =20 @@ -1899,6 +1917,117 @@ udevEventHandleCallback(int watch G_GNUC_UNUSED, } =20 =20 +static void +scheduleMdevctlHandler(int timer G_GNUC_UNUSED, void *opaque) +{ + udevEventDataPtr priv =3D opaque; + + if (priv->mdevctlTimeout > 0) { + virEventRemoveTimeout(priv->mdevctlTimeout); + priv->mdevctlTimeout =3D -1; + } + + virObjectLock(priv); + priv->mdevReady =3D true; + virCondSignal(&priv->threadCond); + virObjectUnlock(priv); +} + + +static void +mdevctlEventHandleCallback(GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data); + + +/* Recursively monitors a directory and its subdirectories for file change= s and + * returns a GList of GFileMonitor objects */ +static GList* +monitorFileRecursively(udevEventDataPtr udev, + GFile *file) +{ + GList *monitors =3D NULL; + g_autoptr(GError) error =3D NULL; + g_autoptr(GFileEnumerator) children =3D NULL; + GFileMonitor *mon; + + if (!(children =3D g_file_enumerate_children(file, "standard::*", + G_FILE_QUERY_INFO_NONE, NUL= L, &error))) { + if (error->code =3D=3D G_IO_ERROR_NOT_DIRECTORY) + return NULL; + goto error; + } + + if (!(mon =3D g_file_monitor(file, G_FILE_MONITOR_NONE, NULL, &error))) + goto error; + + g_signal_connect(mon, "changed", + G_CALLBACK(mdevctlEventHandleCallback), udev); + monitors =3D g_list_append(monitors, mon); + + while (true) { + GFileInfo *info; + GFile *child; + GList *child_monitors =3D NULL; + + if (!g_file_enumerator_iterate(children, &info, &child, NULL, &err= or)) + goto error; + if (!info) + break; + + child_monitors =3D monitorFileRecursively(udev, child); + if (child_monitors) + monitors =3D g_list_concat(monitors, child_monitors); + } + + return monitors; + + error: + g_list_free_full(monitors, g_object_unref); + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to monitor directory: %s"), error->message); + return NULL; +} + + +static void +mdevctlEventHandleCallback(GFileMonitor *monitor G_GNUC_UNUSED, + GFile *file, + GFile *other_file G_GNUC_UNUSED, + GFileMonitorEvent event_type, + gpointer user_data) +{ + udevEventDataPtr priv =3D user_data; + + /* if a new directory appears, monitor that directory for changes */ + if (event_type =3D=3D G_FILE_MONITOR_EVENT_CREATED && + g_file_query_file_type(file, G_FILE_QUERY_INFO_NONE, NULL) =3D=3D + G_FILE_TYPE_DIRECTORY) { + GList *newmonitors =3D monitorFileRecursively(priv, file); + priv->mdevctlMonitors =3D g_list_concat(priv->mdevctlMonitors, new= monitors); + } + + /* When mdevctl creates a device, it can result in multiple notify eve= nts + * emitted for a single logical change (e.g. several CHANGED events, o= r a + * CREATED and CHANGED event followed by CHANGES_DONE_HINT). To avoid + * spawning a mdevctl thread multiple times for a single logical + * configuration change, try to coalesce these changes by waiting for = the + * CHANGES_DONE_HINT event. As a fallback, add a timeout to trigger t= he + * signal if that event never comes */ + if (event_type !=3D G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT) { + if (priv->mdevctlTimeout > 0) + virEventRemoveTimeout(priv->mdevctlTimeout); + priv->mdevctlTimeout =3D virEventAddTimeout(100, scheduleMdevctlHa= ndler, + priv, NULL); + return; + } + + scheduleMdevctlHandler(-1, priv); +} + + /* DMI is intel-compatible specific */ #if defined(__x86_64__) || defined(__i386__) || defined(__amd64__) static void @@ -2052,6 +2181,7 @@ nodeStateInitialize(bool privileged, udevEventDataPtr priv =3D NULL; struct udev *udev =3D NULL; virThread enumThread; + g_autoptr(GFile) mdevctlConfigDir =3D g_file_new_for_path("/etc/mdevct= l.d"); =20 if (root !=3D NULL) { virReportError(VIR_ERR_INVALID_ARG, "%s", @@ -2153,6 +2283,14 @@ nodeStateInitialize(bool privileged, if (priv->watch =3D=3D -1) goto unlock; =20 + /* mdevctl may add notification events in the future: + * https://github.com/mdevctl/mdevctl/issues/27. For now, fall back to + * monitoring the mdevctl configuration directory for changes. + * mdevctl configuration is stored in a directory tree within + * /etc/mdevctl.d/. There is a directory for each parent device, which + * contains a file defining each mediated device */ + priv->mdevctlMonitors =3D monitorFileRecursively(priv, mdevctlConfigDi= r); + virObjectUnlock(priv); =20 /* Create a fictional 'computer' device to root the device tree. */ --=20 2.26.2