From nobody Mon Feb 9 19:04:55 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=1588283014; cv=none; d=zohomail.com; s=zohoarc; b=hKU9TJx2h23PuxG/GHcKAdS9MC0iAhO/hKnLdtqm6tWXwrG5Qu80v/aJ2cSPao0W/t+5n9azpG8U/18mGhSKCvwOqwzNL+HoPTN4bgGCvrY6M3fcZxLjW+4QkjSaqOeqPMga+1fTgjgbyIhSAI+NpFw3C2urwebocvqdxej2T/A= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1588283014; 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=ovpH6OaVWiyUVnvbIBQ6cDd+0twXhNfwpVwWimvrfK0=; b=brskAN7FzMa9thTs/7BxFJhHJeTP5YCUw4XDliIX4q1UlXY9Ro5Xa7/w5fTSEtyEt1QNytxK0lTVCXJ3BQa5xki/DXH5TQga4nZf/g0MmnyrgpKmSMly8bQ4s5mH7VZ//Sy38YhUpbMypeOUVWCuKwXVXl6wH2UGoj9duouBXyM= 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-1.mimecast.com [207.211.31.81]) by mx.zohomail.com with SMTPS id 1588283014319910.9871340079444; Thu, 30 Apr 2020 14:43:34 -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-159-e07i2U4nMVuEPVTbYY54Lw-1; Thu, 30 Apr 2020 17:43:30 -0400 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id AFCD9872FEF; Thu, 30 Apr 2020 21:43:24 +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 4259B5D9F5; Thu, 30 Apr 2020 21:43:24 +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 035F8180954D; Thu, 30 Apr 2020 21:43:24 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 03ULh36G003334 for ; Thu, 30 Apr 2020 17:43:03 -0400 Received: by smtp.corp.redhat.com (Postfix) id 14C521001281; Thu, 30 Apr 2020 21:43:03 +0000 (UTC) Received: from himantopus.redhat.com (ovpn-112-99.phx2.redhat.com [10.3.112.99]) by smtp.corp.redhat.com (Postfix) with ESMTPS id CD24810013BD for ; Thu, 30 Apr 2020 21:43:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1588283013; 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=ovpH6OaVWiyUVnvbIBQ6cDd+0twXhNfwpVwWimvrfK0=; b=h+fD5vrDbYloOLakHq0h9jlB6cTwULVRoEwLXueHg+o0aeOn0CY3xm1Rab8qEmxlZLqbp8 x88Qo2KlxvMikTfwMCWV0iBhaGaJmXhYVOAQsviw8YSYnqN/Y0QWKVRuFpZm7bs3OqPIzJ I1Hmjt45K+SvGfHQy2wQfT/TPdL1B9M= X-MC-Unique: e07i2U4nMVuEPVTbYY54Lw-1 From: Jonathon Jongsma To: libvir-list@redhat.com Subject: [libvirt PATCH 5/6] nodedev: add mdev support to virNodeDeviceCreateXML() Date: Thu, 30 Apr 2020 16:42:57 -0500 Message-Id: <20200430214258.18498-6-jjongsma@redhat.com> In-Reply-To: <20200430214258.18498-1-jjongsma@redhat.com> References: <20200430214258.18498-1-jjongsma@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 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.14 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. Signed-off-by: Jonathon Jongsma --- libvirt.spec.in | 3 + 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 | 177 +++++++++++++++++++++++++++ 6 files changed, 221 insertions(+) diff --git a/libvirt.spec.in b/libvirt.spec.in index 6abf97df85..411846a9fc 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -466,6 +466,9 @@ Requires: dbus # For uid creation during pre Requires(pre): shadow-utils =20 +# For managing persistent mediated devices +Recommends: mdevctl + %description daemon Server side daemon required to manage the virtualization capabilities of recent versions of Linux. Requires a hypervisor specific sub-RPM 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 94b206aa21..0468f68f52 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 f948dfbf69..c271f3bae5 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, @@ -488,6 +514,23 @@ nodeDeviceFindNewDevice(virConnectPtr conn, return device; } =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 _NewSCISHostFuncData { const char *wwnn; @@ -525,6 +568,68 @@ nodeDeviceHasCapability(virNodeDeviceDefPtr def, virNo= deDevCapType type) return false; } =20 +static int +virNodeDeviceDefToMdevctlConfig(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 =3D=3D NULL) + return -1; + + return 0; +} + +static int +virMdevctlStart(const char *parent, const char *json_file, char **uuid) +{ + int status; + g_autofree char *mdevctl =3D virFindFileInPath(MDEVCTL); + if (!mdevctl) + return -1; + + g_autoptr(virCommand) cmd =3D virCommandNewArgList(mdevctl, + "start", + "-p", + parent, + "--jsonfile", + json_file, + NULL); + virCommandAddEnvPassCommon(cmd); + virCommandSetOutputBuffer(cmd, uuid); + + /* 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; +} + virNodeDevicePtr nodeDeviceCreateXML(virConnectPtr conn, const char *xmlDesc, @@ -569,6 +674,78 @@ 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)) { + virNodeDeviceObjPtr parent_dev =3D NULL; + virNodeDeviceDefPtr parent_def =3D NULL; + virNodeDevCapsDefPtr parent_caps =3D NULL; + g_autofree char *uuid =3D NULL; + g_autofree char *parent_pci =3D NULL; + + if (def->parent =3D=3D NULL) { + virReportError(VIR_ERR_NO_NODE_DEVICE, "%s", + _("cannot create a mediated device without a pa= rent")); + return NULL; + } + + if ((parent_dev =3D virNodeDeviceObjListFindByName(driver->devs, d= ef->parent)) =3D=3D NULL) { + virReportError(VIR_ERR_NO_NODE_DEVICE, + _("could not find parent device '%s'"), def->pa= rent); + return NULL; + } + + parent_def =3D virNodeDeviceObjGetDef(parent_dev); + for (parent_caps =3D parent_def->caps; parent_caps !=3D NULL; pare= nt_caps =3D parent_caps->next) { + if (parent_caps->data.type =3D=3D VIR_NODE_DEV_CAP_PCI_DEV) { + virPCIDeviceAddress addr =3D { + .domain =3D parent_caps->data.pci_dev.domain, + .bus =3D parent_caps->data.pci_dev.bus, + .slot =3D parent_caps->data.pci_dev.slot, + .function =3D parent_caps->data.pci_dev.function + }; + + parent_pci =3D virPCIDeviceAddressAsString(&addr); + break; + } + } + + virNodeDeviceObjEndAPI(&parent_dev); + + if (parent_pci =3D=3D NULL) { + virReportError(VIR_ERR_NO_NODE_DEVICE, + _("unable to find PCI address for parent device= '%s'"), def->parent); + return NULL; + } + + /* Convert node device definition to a JSON string formatted for + * mdevctl */ + g_autofree char *json =3D NULL; + if (virNodeDeviceDefToMdevctlConfig(def, &json) < 0) { + virReportError(VIR_ERR_NO_NODE_DEVICE, "%s", + _("couldn't convert node device def to mdevctl = JSON")); + return NULL; + } + + g_autofree char *tmp =3D g_build_filename(driver->stateDir, "mdev-= json-XXXXXX", NULL); + gint tmpfd; + + if ((tmpfd =3D g_mkstemp_full(tmp, O_RDWR | O_CLOEXEC, S_IRUSR | S= _IWUSR)) < 0) { + virReportSystemError(errno, "%s", _("Failed to open temp file = for write")); + return NULL; + } + if (safewrite(tmpfd, json, strlen(json)) !=3D strlen(json) || + VIR_CLOSE(tmpfd) < 0) { + virReportSystemError(errno, "%s", _("Failed to write temp file= ")); + return NULL; + } + + if (virMdevctlStart(parent_pci, tmp, &uuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to start mediated device")); + return NULL; + } + + if (uuid && uuid[0] !=3D '\0') + device =3D nodeDeviceFindNewMediatedDevice(conn, uuid); } else { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Unsupported device type")); --=20 2.21.1