From nobody Mon Feb 9 10:12:36 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 207.211.31.81 as permitted sender) client-ip=207.211.31.81; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-delivery-1.mimecast.com; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 207.211.31.81 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=1594938151; cv=none; d=zohomail.com; s=zohoarc; b=Eiri93/kYO3MJ97PuSdXAurh8iVo+C28+/e0k4QIdvY9mU6pyesb1E5bgoWSvEsSDeCBKT2PbpsCV4e+dtqCzK+xINLdO160mS5eR3KYfpYaWjNz0fTakW/0VllErAAhFiCAjttLGUSam+ncvkPYQy1VfmP/+BUXLXP+heFGyEE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1594938151; 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; bh=yrR8XuW1PE8bB+pvWE/SHOpFD22nPTDof68cjuAJ/Js=; b=NoMpjXPHePbph2wFgA62rusztLIgw1LO+dAMZmoE0xBcDl0pCUlk+942Z1aAhtsgcVsLsDFOB34n0lSSNk8gljM16uoA7rSBaLEn+uHm1c3ZY4jeF2fuU1df0qCggdVDyFnXgaOC6eWlSAVhJc3pZGBtBNr4T/GPsKzQdTSpfFo= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 207.211.31.81 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-1.mimecast.com (us-smtp-2.mimecast.com [207.211.31.81]) by mx.zohomail.com with SMTPS id 1594938151308108.28812854127875; Thu, 16 Jul 2020 15:22:31 -0700 (PDT) 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-53-fHk9UXToO_2V3gYNLGHFrQ-1; Thu, 16 Jul 2020 18:22:14 -0400 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 mimecast-mx01.redhat.com (Postfix) with ESMTPS id E2C0A108B; Thu, 16 Jul 2020 22:22:08 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id BF4629F77; Thu, 16 Jul 2020 22:22:08 +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 8DA6496243; Thu, 16 Jul 2020 22:22:08 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 06GMLrdh022310 for ; Thu, 16 Jul 2020 18:21:53 -0400 Received: by smtp.corp.redhat.com (Postfix) id 1D39D6FED1; Thu, 16 Jul 2020 22:21:53 +0000 (UTC) Received: from himantopus.redhat.com (ovpn-112-38.phx2.redhat.com [10.3.112.38]) by smtp.corp.redhat.com (Postfix) with ESMTPS id E350A724B8 for ; Thu, 16 Jul 2020 22:21:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1594938150; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to: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=yrR8XuW1PE8bB+pvWE/SHOpFD22nPTDof68cjuAJ/Js=; b=fHvUHkHo8qYWiOX9YnT5k7iF7hQIaPDPQYmB6DjT+luXKkBcZUS6aWFLASV24k9xJesH2d 1FAjwDzOvTNH77khpuh5ZhzY1jNE2Ij5cIuH/ZcFQmKwgC2LIAHOHr1Wyo72D5hiEk/thh 41HBhFDpCdQc5AhwiCimdlXX5/ITfxs= X-MC-Unique: fHk9UXToO_2V3gYNLGHFrQ-1 From: Jonathon Jongsma To: libvir-list@redhat.com Subject: [libvirt PATCH 09/16] nodedev: add an mdevctl thread Date: Thu, 16 Jul 2020 17:21:39 -0500 Message-Id: <20200716222146.1241-10-jjongsma@redhat.com> In-Reply-To: <20200716222146.1241-1-jjongsma@redhat.com> References: <20200716222146.1241-1-jjongsma@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-loop: libvir-list@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.79 on 10.5.11.12 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 peridocally query mdevctl for changes to device definitions since an administrator can define new devices with mdevctl outside of libvirt. In order to do this, a new thread is created to handle the querying. 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. Signed-off-by: Jonathon Jongsma --- src/node_device/node_device_udev.c | 182 +++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_devi= ce_udev.c index e06584a3dc..02017b5325 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,11 @@ struct _udevEventData { virCond threadCond; bool threadQuit; bool dataReady; + + virThread mdevctlThread; + virCond mdevctlCond; + bool mdevctlReady; + GList *mdevctl_monitors; }; =20 static virClassPtr udevEventDataClass; @@ -85,8 +91,10 @@ 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->mdevctl_monitors, g_object_unref); =20 virCondDestroy(&priv->threadCond); + virCondDestroy(&priv->mdevctlCond); } =20 =20 @@ -117,6 +125,11 @@ udevEventDataNew(void) return NULL; } =20 + if (virCondInit(&ret->mdevctlCond) < 0) { + virObjectUnref(ret); + return NULL; + } + ret->watch =3D -1; return ret; } @@ -1520,6 +1533,7 @@ nodeStateCleanup(void) virObjectLock(priv); priv->threadQuit =3D true; virCondSignal(&priv->threadCond); + virCondSignal(&priv->mdevctlCond); virObjectUnlock(priv); virThreadJoin(&priv->th); } @@ -1601,6 +1615,40 @@ udevEventMonitorSanityCheck(udevEventDataPtr priv, } =20 =20 +/* Thread to query mdevctl for the current state of persistent mediated de= vice + * defintions when any changes are detected */ +static +void mdevctlThread(void *opaque G_GNUC_UNUSED) +{ + udevEventDataPtr priv =3D driver->privateData; + + while (1) { + virObjectLock(priv); + while (!priv->mdevctlReady && !priv->threadQuit) { + if (virCondWait(&priv->mdevctlCond, &priv->parent.lock)) { + virReportSystemError(errno, "%s", + _("handler failed to wait on conditio= n")); + virObjectUnlock(priv); + return; + } + } + + if (priv->threadQuit) { + virObjectUnlock(priv); + return; + } + + virObjectUnlock(priv); + + mdevctlEnumerateDevices(); + + virObjectLock(priv); + priv->mdevctlReady =3D false; + virObjectUnlock(priv); + } +} + + /** * udevEventHandleThread * @opaque: unused @@ -1708,6 +1756,69 @@ udevEventHandleCallback(int watch G_GNUC_UNUSED, } =20 =20 +static int timeout_id =3D 0; + +static gboolean +signalMdevctlThread(void *user_data) +{ + udevEventDataPtr priv =3D user_data; + + if (timeout_id) { + g_source_remove(timeout_id); + timeout_id =3D 0; + } + + virObjectLock(priv); + priv->mdevctlReady =3D true; + virCondSignal(&priv->mdevctlCond); + virObjectUnlock(priv); + + return G_SOURCE_REMOVE; +} + + +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 (g_file_query_file_type(file, G_FILE_QUERY_INFO_NONE, NULL) =3D=3D + G_FILE_TYPE_DIRECTORY) { + GFileMonitor *mon; + + if ((mon =3D g_file_monitor(file, G_FILE_MONITOR_NONE, NULL, NULL)= )) { + g_signal_connect(mon, "changed", + G_CALLBACK(mdevctlEventHandleCallback), + user_data); + virObjectLock(priv); + priv->mdevctl_monitors =3D g_list_append(priv->mdevctl_monitor= s, mon); + virObjectUnlock(priv); + } + } + + /* Sometimes a single configuration change results in multiple notify + * events (e.g. several CHANGED events, or a CREATED and CHANGED event + * followed by CHANGES_DONE_HINT). To avoid triggering the mdevctl th= read + * multiple times for a single configuration change, try to coalesce t= hese + * changes by waiting for the CHANGES_DONE_HINT event. As a fallback, = add + * a timeout to trigger the signal if that event never comes */ + if (event_type =3D=3D G_FILE_MONITOR_EVENT_CREATED || + event_type =3D=3D G_FILE_MONITOR_EVENT_CHANGED) { + if (timeout_id) + g_source_remove(timeout_id); + timeout_id =3D g_timeout_add(100, signalMdevctlThread, priv); + return; + } + + signalMdevctlThread(priv); +} + + /* DMI is intel-compatible specific */ #if defined(__x86_64__) || defined(__i386__) || defined(__amd64__) static void @@ -1858,6 +1969,58 @@ udevPCITranslateInit(bool privileged G_GNUC_UNUSED) } =20 =20 +/* Recursively monitors dir and any subdirectory for file changes and retu= rns a + * GList of GFileMonitor objects */ +static GList* +monitorDirRecursively(GFile *dir, + GCallback changed_cb, + gpointer user_data) +{ + GList *monitors =3D NULL; + g_autoptr(GFileInfo) dirinfo =3D NULL; + g_autoptr(GError) error =3D NULL; + g_autoptr(GFileEnumerator) children =3D NULL; + GFileMonitor *mon; + + if (!(children =3D g_file_enumerate_children(dir, "standard::*", + G_FILE_QUERY_INFO_NONE, NUL= L, &error))) { + if (error->code =3D=3D G_IO_ERROR_NOT_DIRECTORY) + return NULL; + goto bail; + } + + if (!(mon =3D g_file_monitor(dir, G_FILE_MONITOR_NONE, NULL, &error))) + goto bail; + + g_signal_connect(mon, "changed", changed_cb, user_data); + 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 bail; + if (!info) + break; + + child_monitors =3D monitorDirRecursively(child, changed_cb, user_d= ata); + if (child_monitors) + monitors =3D g_list_concat(monitors, child_monitors); + } + + return monitors; + + bail: + if (error) + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to monitor directory: %s"), error->messag= e); + + g_list_free_full(monitors, g_object_unref); + return NULL; +} + static int nodeStateInitialize(bool privileged, const char *root, @@ -1867,6 +2030,8 @@ nodeStateInitialize(bool privileged, udevEventDataPtr priv =3D NULL; struct udev *udev =3D NULL; virThread enumThread; + g_autoptr(GError) error =3D NULL; + 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", @@ -1969,6 +2134,23 @@ nodeStateInitialize(bool privileged, if (priv->watch =3D=3D -1) goto unlock; =20 + if (virThreadCreateFull(&priv->mdevctlThread, true, mdevctlThread, + "mdevctl-event", false, NULL) < 0) { + virReportSystemError(errno, "%s", + _("failed to create mdevctl handler thread")); + goto unlock; + } + + /* 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->mdevctl_monitors =3D monitorDirRecursively(mdevctlConfigDir, + G_CALLBACK(mdevctlEvent= HandleCallback), + priv); + virObjectUnlock(priv); =20 /* Create a fictional 'computer' device to root the device tree. */ --=20 2.21.3