From nobody Tue Feb 10 07:40:20 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 216.205.24.124 as permitted sender) client-ip=216.205.24.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 216.205.24.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=1616777347; cv=none; d=zohomail.com; s=zohoarc; b=Tq3UNFW0Gu9V6KyPu3XlSnxcF5NDtjqeKoMysiy7CuOQUCIFymmBNldnA63gfULIJFt8YdPvBPhFEf2TFfNqUhiFqKbRd6ZQplA+/K3dMeXTtvKa5KgYZzcRzkLIrESBhLy7V+UOz+hPlLKMpOq6mmAL9u1LRcQKd0wF1mImZo8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1616777347; 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=AOmXMAiWb4eQCnqAiDygLwAwpYlRi1Xfoqh45DnHU6I=; b=fwwaN7HiRbfQNhZP5Jb/xw4auN2zm29gco1hYqdF0/SdtttxKCDSR1ax3PJjpxN4bRIHq8xojFf5ldomFtsxOG2t5v9XRo+w0Re5Te1RdLHXFiuouXDUvJjfOvy0wD5fPCLmBFCkiHgcKgRJ1rXvHOIlj3FcD4aXIvZvUES5VcM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 216.205.24.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 [216.205.24.124]) by mx.zohomail.com with SMTPS id 1616777347512788.8122048202154; Fri, 26 Mar 2021 09:49:07 -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-55-vtjHxNKMPtSn6RhAKZPUrQ-1; Fri, 26 Mar 2021 12:49:01 -0400 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 27E24107ACCA; Fri, 26 Mar 2021 16:48:53 +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 0366D5D6DC; Fri, 26 Mar 2021 16:48:53 +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 B45AB4A708; Fri, 26 Mar 2021 16:48:52 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 12QGmcsP003002 for ; Fri, 26 Mar 2021 12:48:38 -0400 Received: by smtp.corp.redhat.com (Postfix) id 689115D756; Fri, 26 Mar 2021 16:48:38 +0000 (UTC) Received: from himantopus.redhat.com (ovpn-113-51.phx2.redhat.com [10.3.113.51]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 2B1585D755; Fri, 26 Mar 2021 16:48:38 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1616777346; 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=AOmXMAiWb4eQCnqAiDygLwAwpYlRi1Xfoqh45DnHU6I=; b=ZDtx8IjZGtZd71CmIYXKy8DDWZ29q/AP/kXaBdCTwQDqzo1pczaZU4dk9rggBB5BJNqLZq zkVJwARzNUJA8lv3tjBzVYUnCZ4tIF6mCT22lapmGlLahWIHrvo5jrPT8lrEtSZe4tfCUh oDa3I7GbLX6dg/2kPoM7Ms7cv+SXTfE= X-MC-Unique: vtjHxNKMPtSn6RhAKZPUrQ-1 From: Jonathon Jongsma To: libvir-list@redhat.com Subject: [libvirt PATCH v6 14/30] nodedev: Refresh mdev devices when changes are detected Date: Fri, 26 Mar 2021 11:48:10 -0500 Message-Id: <20210326164826.1720062-15-jjongsma@redhat.com> In-Reply-To: <20210326164826.1720062-1-jjongsma@redhat.com> References: <20210326164826.1720062-1-jjongsma@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 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.79 on 10.5.11.15 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 query mdevctl for changes to device definitions since an administrator can define new devices by executing 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 directory for changes to files. When a change is detected, we query mdevctl and update our device list. The mdevctl querying is handled in a throwaway thread, and these threads are synchronized with a mutex. Signed-off-by: Jonathon Jongsma --- src/node_device/node_device_udev.c | 161 +++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_devi= ce_udev.c index 38ebe7b5c5..78144b7762 100644 --- a/src/node_device/node_device_udev.c +++ b/src/node_device/node_device_udev.c @@ -19,6 +19,7 @@ */ =20 #include +#include #include #include #include @@ -69,6 +70,10 @@ struct _udevEventData { =20 /* init thread */ virThread initThread; + + GList *mdevctlMonitors; + virMutex mdevctlLock; + int mdevctlTimeout; }; =20 static virClassPtr udevEventDataClass; @@ -89,6 +94,11 @@ udevEventDataDispose(void *obj) udev_monitor_unref(priv->udev_monitor); udev_unref(udev); =20 + virMutexLock(&priv->mdevctlLock); + g_list_free_full(priv->mdevctlMonitors, g_object_unref); + virMutexUnlock(&priv->mdevctlLock); + virMutexDestroy(&priv->mdevctlLock); + virCondDestroy(&priv->threadCond); } =20 @@ -120,6 +130,11 @@ udevEventDataNew(void) return NULL; } =20 + if (virMutexInit(&ret->mdevctlLock) < 0) { + virObjectUnref(ret); + return NULL; + } + ret->watch =3D -1; return ret; } @@ -2004,6 +2019,137 @@ udevPCITranslateInit(bool privileged G_GNUC_UNUSED) } =20 =20 +static void +mdevctlHandlerThread(void *opaque G_GNUC_UNUSED) +{ + udevEventData *priv =3D driver->privateData; + + /* ensure only a single thread can query mdevctl at a time */ + virMutexLock(&priv->mdevctlLock); + if (nodeDeviceUpdateMediatedDevices() < 0) + VIR_WARN("mdevctl failed to updated mediated devices"); + virMutexUnlock(&priv->mdevctlLock); +} + + +static void +scheduleMdevctlHandler(int timer G_GNUC_UNUSED, void *opaque) +{ + udevEventData *priv =3D opaque; + virThread thread; + + if (priv->mdevctlTimeout > 0) { + virEventRemoveTimeout(priv->mdevctlTimeout); + priv->mdevctlTimeout =3D -1; + } + + if (virThreadCreateFull(&thread, false, mdevctlHandlerThread, + "mdevctl-thread", false, NULL) < 0) { + virReportSystemError(errno, "%s", + _("failed to create mdevctl thread")); + } +} + + +static void +mdevctlEventHandleCallback(GFileMonitor *monitor G_GNUC_UNUSED, + GFile *file, + GFile *other_file G_GNUC_UNUSED, + 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(udevEventData *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))) + 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 =3D NULL; + GFile *child =3D NULL; + GList *child_monitors =3D NULL; + + if (!g_file_enumerator_iterate(children, &info, &child, NULL, &err= or)) + goto error; + + if (!info) + break; + + if (g_file_query_file_type(child, G_FILE_QUERY_INFO_NONE, NULL) = =3D=3D + G_FILE_TYPE_DIRECTORY) { + + 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); + g_clear_error(&error); + return NULL; +} + + +static void +mdevctlEventHandleCallback(GFileMonitor *monitor G_GNUC_UNUSED, + GFile *file, + GFile *other_file G_GNUC_UNUSED, + GFileMonitorEvent event_type, + gpointer user_data) +{ + udevEventData *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); + virMutexLock(&priv->mdevctlLock); + priv->mdevctlMonitors =3D g_list_concat(priv->mdevctlMonitors, new= monitors); + virMutexUnlock(&priv->mdevctlLock); + } + + /* 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); +} + + static int nodeStateInitialize(bool privileged, const char *root, @@ -2012,6 +2158,7 @@ nodeStateInitialize(bool privileged, { udevEventDataPtr priv =3D NULL; struct udev *udev =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", @@ -2113,6 +2260,20 @@ 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 */ + virMutexLock(&priv->mdevctlLock); + if (!(priv->mdevctlMonitors =3D monitorFileRecursively(priv, + mdevctlConfigDir)= )) { + virMutexUnlock(&priv->mdevctlLock); + goto cleanup; + } + virMutexUnlock(&priv->mdevctlLock); + virObjectUnlock(priv); =20 /* Create a fictional 'computer' device to root the device tree. */ --=20 2.26.3