From nobody Mon Feb 9 10:27:37 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 170.10.133.124 as permitted sender) client-ip=170.10.133.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 170.10.133.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=1698036912; cv=none; d=zohomail.com; s=zohoarc; b=GGIZvNrts5wFe6K6JwOnBbF/NrYoflR2NMiUPg64CrtWyBUPiU8hDC97HeW7lPhZNMwpFrNx1JJUJu6wEPXHHwbCBhKtlPlrg9w14vmr8T1dbAZANt8YipYd9AI76Dpfbn55cZ59C5iQSNSv5OnbVqNtWnbu2yEtXpORmwY59Vg= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1698036912; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=K/rkSs5OQIqqhgmrke2oCyfmvsc38lK3VJYSQpuvPgo=; b=bIIopZ8m7NJsm/XVPcW7rSTQhGmJ/6K6p80Ao5kZuvMjYuEcoUCgguRdR0CJhNQEgiKL+thTNK7bgf/ixDBZPduKL3CNbBRBztumFBq9ccS8+ZlnRLb0sMvc9r5gJYU+jlEebcPrNZ/RYasgFfPDkLG2UGzSpOsF7bImIDCIGQY= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 170.10.133.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by mx.zohomail.com with SMTPS id 1698036912563984.1560454019842; Sun, 22 Oct 2023 21:55:12 -0700 (PDT) Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-348-zQ44ziWaOcm37KZOUcxy7A-1; Mon, 23 Oct 2023 00:55:06 -0400 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id A760E185A7B2; Mon, 23 Oct 2023 04:55:00 +0000 (UTC) Received: from mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com [10.30.29.100]) by smtp.corp.redhat.com (Postfix) with ESMTP id 8C584C1597A; Mon, 23 Oct 2023 04:55:00 +0000 (UTC) Received: from mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (localhost [IPv6:::1]) by mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (Postfix) with ESMTP id 5CDCB19451D2; Mon, 23 Oct 2023 04:54:58 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) by mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (Postfix) with ESMTP id 109BA19465A0 for ; Mon, 23 Oct 2023 04:54:56 +0000 (UTC) Received: by smtp.corp.redhat.com (Postfix) id 00A5AC1597C; Mon, 23 Oct 2023 04:54:56 +0000 (UTC) Received: from vhost3.router.laine.org (unknown [10.22.8.125]) by smtp.corp.redhat.com (Postfix) with ESMTP id BC446C1596D; Mon, 23 Oct 2023 04:54:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1698036911; 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=K/rkSs5OQIqqhgmrke2oCyfmvsc38lK3VJYSQpuvPgo=; b=MzcykzqXjcqLZ+Hrqla2TEHewsjWYFPnpYPsxDwg+S6N9PAsIcHnjIIW0GIs/NzgdjGMNF 5rIjBwRo1cXrVocbgd1IwJJpzkiXJSTtqYVor2EcJ/qUNY7mXPEkxlyPG7YNNo9X8f70ep NyzKIa8XSOVNXcBOWnF0Zw3nFUT5iS4= X-MC-Unique: zQ44ziWaOcm37KZOUcxy7A-1 X-Original-To: libvir-list@listman.corp.redhat.com From: Laine Stump To: libvir-list@redhat.com Subject: [libvirt PATCH 14/15] util: new function virPCIDeviceFindBestVFIOVariant() Date: Mon, 23 Oct 2023 00:54:50 -0400 Message-ID: <20231023045451.210711-15-laine@redhat.com> In-Reply-To: <20231023045451.210711-1-laine@redhat.com> References: <20231023045451.210711-1-laine@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.8 X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Joao Martins , Cedric Le Goater , Jason Gunthorpe Errors-To: libvir-list-bounces@redhat.com Sender: "libvir-list" X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.8 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1698036913312100002 Content-Type: text/plain; charset="utf-8"; x-default="true" This function reads the modalias file for the given device from sysfs, then looks through /lib/modules/${kernel_release}/modules.alias for the vfio_pci alias that matches with the least number of wildcard ('*') fields. This can be used to find an appropriate "VFIO variant" driver for a device (it will be the PCI driver implemented by the discovered module) - these drivers are compatible with (and provide the entire API of) the standard vfio-pci driver, but have additional device-specific APIs that can be useful for, e.g., saving/restoring state for migration. Signed-off-by: Laine Stump --- src/util/virpci.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/virpci.h | 2 + 2 files changed, 226 insertions(+) diff --git a/src/util/virpci.c b/src/util/virpci.c index 513ae948c0..70fcedc4a5 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -30,6 +30,10 @@ #include #include =20 +#ifndef WIN32 +# include +#endif + #include "virlog.h" #include "virerror.h" #include "virfile.h" @@ -1539,6 +1543,226 @@ virPCIDeviceReattach(virPCIDevice *dev, return 0; } =20 + +#ifndef WIN32 +typedef struct { + /* this is the decomposed version of a string like: + * + * vNNNNNNNNdNNNNNNNNsvNNNNNNNNsdNNNNNNNNbcNNscNNiNN + * + * (followed by a space or newline). The "NNNN" are always of the + * length in the example unless replaced with a wildcard ("*"), + * but we make no assumptions about length. + * + * Rather than name each field, we just put them + * all in an array of 6 elements, so that we + * can write a simple loop to compare them + */ + char *fields[7]; /* v, d, sv, sd, bc, sc, i */ +} virPCIDeviceAliasInfo; + + +/* NULL in last position makes parsing loop simpler */ +static const char *fieldnames[] =3D { "v", "d", "sv", "sd", "bc", "sc", "i= ", NULL }; + + +static void +virPCIDeviceAliasInfoFree(virPCIDeviceAliasInfo *info) +{ + if (info) { + size_t i; + + for (i =3D 0; i < G_N_ELEMENTS(info->fields); i++) + g_free(info->fields[i]); + + g_free(info); + } +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(virPCIDeviceAliasInfo, virPCIDeviceAliasInfo= Free); + + +static virPCIDeviceAliasInfo * +virPCIDeviceAliasInfoNew(const char *str) +{ + const char *field =3D str; + + size_t i; + g_autoptr(virPCIDeviceAliasInfo) ret =3D g_new0(virPCIDeviceAliasInfo,= 1); + + if (!str) + return g_steal_pointer(&ret); + + /* initialize from str */ + for (i =3D 0; i < G_N_ELEMENTS(ret->fields); i++) { + int len =3D strlen(fieldnames[i]); + const char *next; + + if (strncmp(field, fieldnames[i], len)) + return NULL; + + field +=3D len; + if (fieldnames[i + 1]) { + if (!(next =3D strstr(field, fieldnames[i + 1]))) + return NULL; + } else { + next =3D field; + virSkipToSpace(&next); + } + + ret->fields[i] =3D g_strndup(field, next - field); + field =3D next; + } + + return g_steal_pointer(&ret); +} + + +static void +virPCIDeviceAliasInfoPrint(virPCIDeviceAliasInfo *info) +{ + size_t i; + + for (i =3D 0; i < G_N_ELEMENTS(info->fields); i++) + VIR_DEBUG("%s: '%s'", fieldnames[i], info->fields[i]); +} + + +static bool +virPCIDeviceAliasInfoMatch(virPCIDeviceAliasInfo *orig, + virPCIDeviceAliasInfo *match, + int *wildCardCt) +{ + size_t i; + + *wildCardCt =3D 0; + + for (i =3D 0; i < G_N_ELEMENTS(orig->fields); i++) { + if (STREQ(match->fields[i], "*")) + (*wildCardCt)++; + else if (STRNEQ(orig->fields[i], match->fields[i])) + return false; + } + return true; +} + + +/* virPCIDeviceFindBestVFIOVariant: + * + * Find the "best" match of all vfio_pci aliases for @dev in the host + * modules.alias file. This uses the algorithm of finding every + * modules.alias line that begins with "vfio_pci:", then picking the + * one that matches the device's own modalias value (from the file of + * that name in the device's sysfs directory) with the fewest + * "wildcards" (* character, meaning "match any value for this + * attribute"). + */ +int +virPCIDeviceFindBestVFIOVariant(virPCIDevice *dev, + char **moduleName) +{ + g_autofree char *devModAliasPath =3D NULL; + g_autofree char *devModAliasContent =3D NULL; + const char *devModAlias; + g_autoptr(virPCIDeviceAliasInfo) devModAliasInfo =3D NULL; + struct utsname unameInfo; + g_autofree char *modulesAliasPath =3D NULL; + g_autofree char *modulesAliasContent =3D NULL; + const char *line; + int currentBestWildcardCt =3D INT_MAX; + + *moduleName =3D NULL; + + /* get the modalias values for the device from sysfs */ + devModAliasPath =3D virPCIFile(dev->name, "modalias"); + if (virFileReadAll(devModAliasPath, 100, &devModAliasContent) < 0) + return 0; + + VIR_DEBUG("modalias path: '%s' contents: '%s'", + devModAliasPath, devModAliasContent); + + /* "pci:vNNNNNNNNdNNNNNNNNsvNNNNNNNNsdNNNNNNNNbcNNscNNiNN\n" */ + if ((devModAlias =3D STRSKIP(devModAliasContent, "pci:")) =3D=3D NULL= || + !(devModAliasInfo =3D virPCIDeviceAliasInfoNew(devModAlias))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("device modalias file %1$s content has improper f= ormat"), + devModAliasPath); + return -1; + } + + virPCIDeviceAliasInfoPrint(devModAliasInfo); + + uname(&unameInfo); + modulesAliasPath =3D g_strdup_printf("/lib/modules/%s/modules.alias", + unameInfo.release); + if (virFileReadAll(modulesAliasPath, + 4 * 1024 * 1024, &modulesAliasContent) < 0) { + return -1; + } + + /* Look for all lines that are aliases for vfio_pci drivers. + * (The first line is always a comment, so we can be sure "alias" + * is preceded by a newline) + */ + line =3D modulesAliasContent; + + while ((line =3D strstr(line, "\nalias vfio_pci:"))) { + g_autoptr(virPCIDeviceAliasInfo) fileModAliasInfo =3D NULL; + int wildCardCt; + + /* "alias vfio_pci:vNNNNNNNNdNNNNNNNNsvNNNNNNNNsdNNNNNNNNbcNNscNNi= NN XXXX\n" */ + line +=3D strlen("\nalias vfio_pci:"); + if (!(fileModAliasInfo =3D virPCIDeviceAliasInfoNew(line))) + continue; + + virPCIDeviceAliasInfoPrint(fileModAliasInfo); + + if (virPCIDeviceAliasInfoMatch(devModAliasInfo, + fileModAliasInfo, &wildCardCt)) { + + const char *aliasStart =3D strchr(line, ' '); + const char *aliasEnd =3D NULL; + g_autofree char *aliasName =3D NULL; + + if (!aliasStart) { + VIR_WARN("malformed modules.alias vfio_pci: line"); + continue; + } + + aliasStart++; + line =3D aliasEnd =3D strchrnul(aliasStart, '\n'); + aliasName =3D g_strndup(aliasStart, aliasEnd - aliasStart); + + VIR_DEBUG("matching alias '%s' found, %d wildcards", + aliasName, wildCardCt); + + if (wildCardCt < currentBestWildcardCt) { + + /* this is a better match than previous */ + currentBestWildcardCt =3D wildCardCt; + g_free(*moduleName); + *moduleName =3D g_steal_pointer(&aliasName); + } + } + } + return 0; +} + + +#else /* WIN32 */ + + +int +virPCIDeviceFindBestVFIOVariant(virPCIDevice *dev G_GNUC_UNUSED, + char **moduleName G_GNUC_UNUSED) +{ + virReportSystemError(ENOSYS, "%s", + _("VFIO device assignment is not available on thi= s platform")); + return -1; +} +#endif /* WIN32 */ + + static char * virPCIDeviceReadID(virPCIDevice *dev, const char *id_name) { diff --git a/src/util/virpci.h b/src/util/virpci.h index faca6cf6f9..ca94145207 100644 --- a/src/util/virpci.h +++ b/src/util/virpci.h @@ -286,6 +286,8 @@ int virPCIDeviceGetCurrentDriverPathAndName(virPCIDevic= e *dev, int virPCIDeviceGetCurrentDriverNameAndType(virPCIDevice *dev, char **drvName, virPCIStubDriver *drvType); +int virPCIDeviceFindBestVFIOVariant(virPCIDevice *dev, + char **driverName); =20 int virPCIDeviceIsPCIExpress(virPCIDevice *dev); int virPCIDeviceHasPCIExpressLink(virPCIDevice *dev); --=20 2.41.0