From nobody Tue Feb 10 00:57:56 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=1592514402; cv=none; d=zohomail.com; s=zohoarc; b=I8idcQa7Y59Ju07YYYWGcc9yFAWZceycPqQJNBWOS6Axiz+v106gV3QdtMspMTD1g1QFJJi0gRrdf06OUKQHrkj9dxbnHqSyPHCCbSfUhjcquOw4VNh0VJz9/MuxE7NymBsFs57DMknrK0ZGC27MdxJ7hT07yAJf6cXa7SFpQao= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1592514402; 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=Qbo6EIlEgovq/QD+0DLOMCRlwDJTrSPjUib20G54Ts4=; b=cLR4ZUj52miXEkwqQqcg6X4JH1xAi8X3m8yQf9ZY8pj9chI75zoxqQ2zxwrKRB91zqMiAiwzzmCtmEpIF41kMroIr2+DaOYXc9E+zJ+R4T/jwnblNvh1X/OB+0m71fWU+SyilMDOG0E1jxSXgUTmjy3nxTXWLQYDrwE/lzVEsJA= 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 1592514402164924.0761220841081; Thu, 18 Jun 2020 14:06:42 -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-45-pjXA3DbJMheHeFnybrtfwQ-1; Thu, 18 Jun 2020 17:06:35 -0400 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id D5CFA107ACF6; Thu, 18 Jun 2020 21:06:29 +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 B0D891000239; Thu, 18 Jun 2020 21:06:29 +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 81EBA833D1; Thu, 18 Jun 2020 21:06:29 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 05IL6CB8002546 for ; Thu, 18 Jun 2020 17:06:12 -0400 Received: by smtp.corp.redhat.com (Postfix) id 661C060E3E; Thu, 18 Jun 2020 21:06:12 +0000 (UTC) Received: from himantopus.redhat.com (ovpn-114-51.phx2.redhat.com [10.3.114.51]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 0AA8B60BF4; Thu, 18 Jun 2020 21:06:11 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1592514400; 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=Qbo6EIlEgovq/QD+0DLOMCRlwDJTrSPjUib20G54Ts4=; b=WmxqLvZPULzkQN0iKwhfOrpFWMmNyUnnn2z4fP8VinTzQ3s8x7Ww6gPuGE33JtqvGseUKL JRKa+Q1lUiDkwiMK4NhdSmbrxDzHP0tsrJmGnJyi9WI9odAKKR1jDOon8LjSUsxg8RSO5t JIzt1khwi2gR1RZU56zcWSYGHWCFinw= X-MC-Unique: pjXA3DbJMheHeFnybrtfwQ-1 From: Jonathon Jongsma To: libvir-list@redhat.com Subject: [libvirt PATCH v4 06/12] nodedev: add mdev support to virNodeDeviceCreateXML() Date: Thu, 18 Jun 2020 16:05:59 -0500 Message-Id: <20200618210605.12308-7-jjongsma@redhat.com> In-Reply-To: <20200618210605.12308-1-jjongsma@redhat.com> References: <20200618210605.12308-1-jjongsma@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-loop: libvir-list@redhat.com Cc: mprivozn@redhat.com, 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.22 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" 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 | 197 +++++++++++++++++++++++++++ src/node_device/node_device_driver.h | 5 + 7 files changed, 245 insertions(+) diff --git a/libvirt.spec.in b/libvirt.spec.in index 450c97b46d..cd7c33ab1a 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 22bd8b9c17..42f8d7c222 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1179,6 +1179,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 d6255a43c8..35016782d2 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,26 @@ 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 { @@ -536,6 +582,155 @@ 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, + char **uuid_out) +{ + virCommandPtr cmd; + g_autofree char *json =3D NULL; + g_autofree char *parent_pci =3D nodeDeviceFindAddressByName(def->paren= t); + + 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; + } + + cmd =3D virCommandNewArgList(MDEVCTL, "start", + "-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, uu= id); + 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, @@ -580,6 +775,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..e42c14f6c7 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,7 @@ nodeConnectNodeDeviceEventRegisterAny(virConnectPtr con= n, int nodeConnectNodeDeviceEventDeregisterAny(virConnectPtr conn, int callbackID); + +virCommandPtr +nodeDeviceGetMdevctlStartCommand(virNodeDeviceDefPtr def, + char **uuid_out); --=20 2.21.3