From nobody Mon Feb 9 22:18:47 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 207.211.31.120 as permitted sender) client-ip=207.211.31.120; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-1.mimecast.com; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 207.211.31.120 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=1591739093; cv=none; d=zohomail.com; s=zohoarc; b=jahXgicq0c498hZZjoT1vvsqovjRqRxUg3UwQ0/exTy2LtPui08lYZY51zEi7rnhkSNuTVV7p+u4Wi6DX7ex/8A4f3wJiI8EqbpEJrSipwU98NZ7hiJ6KWu64XSAMToqZGI2JtpHfsqHdtA9RcF8tZbqjMmn2QUfHIXapOoebhM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1591739093; 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=bcG5k7cq8kXeFAIejYtwU3vm4CBs6ELwDP2yQzrrKhQ=; b=RyNkAVpVfUAu2OGhXaFojzB+KFYVhpa+x2pRAhwXf6tQkbIpiR9r8zzJdw0vGvNzA6wLIPs7CkGQHKJ09jr9ESJ6bDU8lY3hq3RTb9qTtI1XOMAXYbnQW2GrlewLBguwnxtOmuL0nt39d35B4kFt2j4y3U6Vc5rlfv9c97RCOrQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 207.211.31.120 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-1.mimecast.com (us-smtp-delivery-1.mimecast.com [207.211.31.120]) by mx.zohomail.com with SMTPS id 1591739093071705.5591048744253; Tue, 9 Jun 2020 14:44:53 -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-491-i6ouRnUrM9SBZ2yM_f4Hng-1; Tue, 09 Jun 2020 17:44:49 -0400 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 12BBA188360A; Tue, 9 Jun 2020 21:44:44 +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 E7B8882052; Tue, 9 Jun 2020 21:44:43 +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 B50401806B0A; Tue, 9 Jun 2020 21:44:43 +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 059Lht4Y030095 for ; Tue, 9 Jun 2020 17:43:55 -0400 Received: by smtp.corp.redhat.com (Postfix) id 4D1927BFE9; Tue, 9 Jun 2020 21:43:55 +0000 (UTC) Received: from himantopus.redhat.com (ovpn-115-112.phx2.redhat.com [10.3.115.112]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 133B97BFE3 for ; Tue, 9 Jun 2020 21:43:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1591739091; 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=bcG5k7cq8kXeFAIejYtwU3vm4CBs6ELwDP2yQzrrKhQ=; b=D6fNQ4vV6astjfDaKn/45x+CSA3b8TM4N+jz7g0gBhrHpfWgs7HC1zgFz92buE7HXr8HIr UiPkQ8dEkCB1NfrDPLAZnrMn00ahhGlYx3UBzqGB6saoJWGUJtPT9PakHDLH2oENNRtUOB /oKymge3pmRVKbRlD1D8BzGtp+vBpF0= X-MC-Unique: i6ouRnUrM9SBZ2yM_f4Hng-1 From: Jonathon Jongsma To: libvir-list@redhat.com Subject: [libvirt PATCH v2 05/10] nodedev: add mdev support to virNodeDeviceCreateXML() Date: Tue, 9 Jun 2020 16:43:45 -0500 Message-Id: <20200609214350.14156-6-jjongsma@redhat.com> In-Reply-To: <20200609214350.14156-1-jjongsma@redhat.com> References: <20200609214350.14156-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 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.11 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" With recent additions to the node device xml schema, an xml schema can now describe a mdev device sufficiently for libvirt to create and start the device using the mdevctl utility. Note that some of the the configuration for a mediated device must be passed to mdevctl as a JSON-formatted file. In order to avoid creating and cleaning up temporary files, the JSON is instead fed to stdin and we pass the filename /dev/stdin to mdevctl. While this may not be portable, neither are mediated devices, so I don't believe it should cause any problems. Signed-off-by: Jonathon Jongsma --- libvirt.spec.in | 2 + m4/virt-external-programs.m4 | 3 + src/conf/virnodedeviceobj.c | 34 +++++ src/conf/virnodedeviceobj.h | 3 + src/libvirt_private.syms | 1 + src/node_device/node_device_driver.c | 203 +++++++++++++++++++++++++++ src/node_device/node_device_driver.h | 6 + 7 files changed, 252 insertions(+) diff --git a/libvirt.spec.in b/libvirt.spec.in index 262e66f3cc..2b3a4d2e71 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -522,6 +522,8 @@ Requires: libvirt-daemon =3D %{version}-%{release} Requires: libvirt-libs =3D %{version}-%{release} # needed for device enumeration Requires: systemd >=3D 185 +# For managing persistent mediated devices +Requires: mdevctl =20 %description daemon-driver-nodedev The nodedev driver plugin for the libvirtd daemon, providing diff --git a/m4/virt-external-programs.m4 b/m4/virt-external-programs.m4 index 9046e3bf07..bd3cb1f757 100644 --- a/m4/virt-external-programs.m4 +++ b/m4/virt-external-programs.m4 @@ -65,6 +65,7 @@ AC_DEFUN([LIBVIRT_CHECK_EXTERNAL_PROGRAMS], [ AC_PATH_PROG([OVSVSCTL], [ovs-vsctl], [ovs-vsctl], [$LIBVIRT_SBIN_PATH]) AC_PATH_PROG([SCRUB], [scrub], [scrub], [$LIBVIRT_SBIN_PATH]) AC_PATH_PROG([ADDR2LINE], [addr2line], [addr2line], [$LIBVIRT_SBIN_PATH]) + AC_PATH_PROG([MDEVCTL], [mdevctl], [mdevctl], [$LIBVIRT_SBIN_PATH]) =20 AC_DEFINE_UNQUOTED([DMIDECODE], ["$DMIDECODE"], [Location or name of the dmidecode program]) @@ -88,6 +89,8 @@ AC_DEFUN([LIBVIRT_CHECK_EXTERNAL_PROGRAMS], [ [Location or name of the scrub program (for wiping al= gorithms)]) AC_DEFINE_UNQUOTED([ADDR2LINE], ["$ADDR2LINE"], [Location of addr2line program]) + AC_DEFINE_UNQUOTED([MDEVCTL], ["$MDEVCTL"], + [Location or name of the mdevctl program]) =20 AC_PATH_PROG([IP_PATH], [ip], [/sbin/ip], [$LIBVIRT_SBIN_PATH]) AC_DEFINE_UNQUOTED([IP_PATH], ["$IP_PATH"], [path to ip binary]) diff --git a/src/conf/virnodedeviceobj.c b/src/conf/virnodedeviceobj.c index 3a34a324ca..fd20d5f9e2 100644 --- a/src/conf/virnodedeviceobj.c +++ b/src/conf/virnodedeviceobj.c @@ -399,6 +399,40 @@ virNodeDeviceObjListFindSCSIHostByWWNs(virNodeDeviceOb= jListPtr devs, &data); } =20 +static int +virNodeDeviceObjListFindMediatedDeviceByUUIDCallback(const void *payload, + const void *name G_GN= UC_UNUSED, + const void *opaque G_= GNUC_UNUSED) +{ + virNodeDeviceObjPtr obj =3D (virNodeDeviceObjPtr) payload; + const char *uuid =3D (const char *) opaque; + virNodeDevCapsDefPtr cap; + int want =3D 0; + + virObjectLock(obj); + + for (cap =3D obj->def->caps; cap !=3D NULL; cap =3D cap->next) { + if (cap->data.type =3D=3D VIR_NODE_DEV_CAP_MDEV) { + if (STREQ(cap->data.mdev.uuid, uuid)) { + want =3D 1; + break; + } + } + } + + virObjectUnlock(obj); + return want; +} + + +virNodeDeviceObjPtr +virNodeDeviceObjListFindMediatedDeviceByUUID(virNodeDeviceObjListPtr devs, + const char *uuid) +{ + return virNodeDeviceObjListSearch(devs, + virNodeDeviceObjListFindMediatedDevi= ceByUUIDCallback, + uuid); +} =20 static void virNodeDeviceObjListDispose(void *obj) diff --git a/src/conf/virnodedeviceobj.h b/src/conf/virnodedeviceobj.h index c9df8dedab..6efdb23d36 100644 --- a/src/conf/virnodedeviceobj.h +++ b/src/conf/virnodedeviceobj.h @@ -118,3 +118,6 @@ virNodeDeviceObjListExport(virConnectPtr conn, void virNodeDeviceObjSetSkipUpdateCaps(virNodeDeviceObjPtr obj, bool skipUpdateCaps); +virNodeDeviceObjPtr +virNodeDeviceObjListFindMediatedDeviceByUUID(virNodeDeviceObjListPtr devs, + const char *uuid); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index b8e6f058c3..532169d93d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1178,6 +1178,7 @@ virNodeDeviceObjListAssignDef; virNodeDeviceObjListExport; virNodeDeviceObjListFindByName; virNodeDeviceObjListFindBySysfsPath; +virNodeDeviceObjListFindMediatedDeviceByUUID; virNodeDeviceObjListFindSCSIHostByWWNs; virNodeDeviceObjListFree; virNodeDeviceObjListGetNames; diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_de= vice_driver.c index 629d4bcf91..dbc7eb4d1e 100644 --- a/src/node_device/node_device_driver.c +++ b/src/node_device/node_device_driver.c @@ -30,6 +30,7 @@ #include "datatypes.h" #include "viralloc.h" #include "virfile.h" +#include "virjson.h" #include "virstring.h" #include "node_device_conf.h" #include "node_device_event.h" @@ -40,6 +41,7 @@ #include "viraccessapicheck.h" #include "virnetdev.h" #include "virutil.h" +#include "vircommand.h" =20 #define VIR_FROM_THIS VIR_FROM_NODEDEV =20 @@ -304,6 +306,30 @@ nodeDeviceLookupSCSIHostByWWN(virConnectPtr conn, return device; } =20 +static virNodeDevicePtr +nodeDeviceLookupMediatedDeviceByUUID(virConnectPtr conn, + const char *uuid, + unsigned int flags) +{ + virNodeDeviceObjPtr obj =3D NULL; + virNodeDeviceDefPtr def; + virNodeDevicePtr device =3D NULL; + + virCheckFlags(0, NULL); + + if (!(obj =3D virNodeDeviceObjListFindMediatedDeviceByUUID(driver->dev= s, + uuid))) + return NULL; + + def =3D virNodeDeviceObjGetDef(obj); + + if ((device =3D virGetNodeDevice(conn, def->name))) + device->parentName =3D g_strdup(def->parent); + + virNodeDeviceObjEndAPI(&obj); + return device; +} + =20 char * nodeDeviceGetXMLDesc(virNodeDevicePtr device, @@ -492,6 +518,25 @@ nodeDeviceFindNewDevice(virConnectPtr conn, } =20 =20 +static virNodeDevicePtr +nodeDeviceFindNewMediatedDeviceFunc(virConnectPtr conn, + const void *opaque) +{ + const char *uuid =3D opaque; + return nodeDeviceLookupMediatedDeviceByUUID(conn, uuid, 0); +} + + +static virNodeDevicePtr +nodeDeviceFindNewMediatedDevice(virConnectPtr conn, + const char *mdev_uuid) +{ + return nodeDeviceFindNewDevice(conn, + nodeDeviceFindNewMediatedDeviceFunc, + mdev_uuid); +} + + typedef struct _NewSCSIHostFuncData NewSCSIHostFuncData; struct _NewSCSIHostFuncData { @@ -534,6 +579,162 @@ nodeDeviceHasCapability(virNodeDeviceDefPtr def, virN= odeDevCapType type) } =20 =20 +/* format a json string that provides configuration information about this= mdev + * to the mdevctl utility */ +static int +nodeDeviceDefToMdevctlConfig(virNodeDeviceDefPtr def, char **buf) +{ + size_t i; + virNodeDevCapMdevPtr mdev =3D &def->caps->data.mdev; + g_autoptr(virJSONValue) json =3D virJSONValueNewObject(); + + if (virJSONValueObjectAppendString(json, "mdev_type", mdev->type) < 0) + return -1; + if (virJSONValueObjectAppendString(json, "start", "manual") < 0) + return -1; + if (mdev->attributes) { + g_autoptr(virJSONValue) attributes =3D virJSONValueNewArray(); + for (i =3D 0; i < mdev->nattributes; i++) { + virMediatedDeviceAttrPtr attr =3D mdev->attributes[i]; + g_autoptr(virJSONValue) jsonattr =3D virJSONValueNewObject(); + + if (virJSONValueObjectAppendString(jsonattr, attr->name, attr-= >value) < 0) + return -1; + if (virJSONValueArrayAppend(attributes, g_steal_pointer(&jsona= ttr)) < 0) + return -1; + } + if (virJSONValueObjectAppend(json, "attrs", g_steal_pointer(&attri= butes)) < 0) + return -1; + } + + *buf =3D virJSONValueToString(json, false); + if (!*buf) + return -1; + + return 0; +} + + +static char * +nodeDeviceFindAddressByName(const char *name) +{ + virNodeDeviceDefPtr def =3D NULL; + virNodeDevCapsDefPtr caps =3D NULL; + char *pci_addr =3D NULL; + virNodeDeviceObjPtr dev =3D virNodeDeviceObjListFindByName(driver->dev= s, name); + + if (!dev) { + virReportError(VIR_ERR_NO_NODE_DEVICE, + _("could not find device '%s'"), name); + return NULL; + } + + def =3D virNodeDeviceObjGetDef(dev); + for (caps =3D def->caps; caps !=3D NULL; caps =3D caps->next) { + if (caps->data.type =3D=3D VIR_NODE_DEV_CAP_PCI_DEV) { + virPCIDeviceAddress addr =3D { + .domain =3D caps->data.pci_dev.domain, + .bus =3D caps->data.pci_dev.bus, + .slot =3D caps->data.pci_dev.slot, + .function =3D caps->data.pci_dev.function + }; + + pci_addr =3D virPCIDeviceAddressAsString(&addr); + break; + } + } + + virNodeDeviceObjEndAPI(&dev); + + return pci_addr; +} + + +virCommandPtr +nodeDeviceGetMdevctlStartCommand(virNodeDeviceDefPtr def, + bool persist, + char **uuid_out) +{ + virCommandPtr cmd; + const char *subcommand; + g_autofree char *json =3D NULL; + g_autofree char *mdevctl =3D virFindFileInPath(MDEVCTL); + g_autofree char *parent_pci =3D nodeDeviceFindAddressByName(def->paren= t); + + if (!mdevctl) + return NULL; + + if (!parent_pci) { + virReportError(VIR_ERR_NO_NODE_DEVICE, + _("unable to find PCI address for parent device '%s= '"), def->parent); + return NULL; + } + + if (nodeDeviceDefToMdevctlConfig(def, &json) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("couldn't convert node device def to mdevctl JSON= ")); + return NULL; + } + + if (persist) + subcommand =3D "define"; + else + subcommand =3D "start"; + + cmd =3D virCommandNewArgList(mdevctl, subcommand, + "-p", parent_pci, + "--jsonfile", "/dev/stdin", + NULL); + + virCommandSetInputBuffer(cmd, json); + virCommandSetOutputBuffer(cmd, uuid_out); + + return cmd; +} + +static int +virMdevctlStart(virNodeDeviceDefPtr def, char **uuid) +{ + int status; + g_autoptr(virCommand) cmd =3D nodeDeviceGetMdevctlStartCommand(def, fa= lse, + uuid); + if (!cmd) + return -1; + + /* an auto-generated uuid is returned via stdout if no uuid is specifi= ed in + * the mdevctl args */ + if (virCommandRun(cmd, &status) < 0 || status !=3D 0) + return -1; + + /* remove newline */ + *uuid =3D g_strstrip(*uuid); + + return 0; +} + + +static virNodeDevicePtr +nodeDeviceCreateXMLMdev(virConnectPtr conn, + virNodeDeviceDefPtr def) +{ + g_autofree char *uuid =3D NULL; + + if (!def->parent) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("cannot create a mediated device without a parent= ")); + return NULL; + } + + if (virMdevctlStart(def, &uuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to start mediated device")); + return NULL; + } + + return nodeDeviceFindNewMediatedDevice(conn, uuid); +} + + virNodeDevicePtr nodeDeviceCreateXML(virConnectPtr conn, const char *xmlDesc, @@ -578,6 +779,8 @@ nodeDeviceCreateXML(virConnectPtr conn, _("no node device for '%s' with matching " "wwnn '%s' and wwpn '%s'"), def->name, wwnn, wwpn); + } else if (nodeDeviceHasCapability(def, VIR_NODE_DEV_CAP_MDEV)) { + device =3D nodeDeviceCreateXMLMdev(conn, def); } else { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Unsupported device type")); diff --git a/src/node_device/node_device_driver.h b/src/node_device/node_de= vice_driver.h index eae5e2cb17..576f75375f 100644 --- a/src/node_device/node_device_driver.h +++ b/src/node_device/node_device_driver.h @@ -24,6 +24,7 @@ #include "internal.h" #include "driver.h" #include "virnodedeviceobj.h" +#include "vircommand.h" =20 #define LINUX_NEW_DEVICE_WAIT_TIME 60 =20 @@ -116,3 +117,8 @@ nodeConnectNodeDeviceEventRegisterAny(virConnectPtr con= n, int nodeConnectNodeDeviceEventDeregisterAny(virConnectPtr conn, int callbackID); + +virCommandPtr +nodeDeviceGetMdevctlStartCommand(virNodeDeviceDefPtr def, + bool persist, + char **uuid_out); --=20 2.21.3