]> xenbits.xensource.com Git - libvirt.git/commitdiff
nodedev: add mdev support to virNodeDeviceCreateXML()
authorJonathon Jongsma <jjongsma@redhat.com>
Thu, 18 Jun 2020 21:05:59 +0000 (16:05 -0500)
committerErik Skultety <eskultet@redhat.com>
Fri, 19 Jun 2020 08:39:55 +0000 (10:39 +0200)
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 <jjongsma@redhat.com>
Reviewed-by: Erik Skultety <eskultet@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
libvirt.spec.in
m4/virt-external-programs.m4
src/conf/virnodedeviceobj.c
src/conf/virnodedeviceobj.h
src/libvirt_private.syms
src/node_device/node_device_driver.c
src/node_device/node_device_driver.h

index 450c97b46de70b6d46485a80b8dc47894d424d33..cd7c33ab1a3b3464df61a303c13a9d4f5bd82f3b 100644 (file)
@@ -522,6 +522,8 @@ Requires: libvirt-daemon = %{version}-%{release}
 Requires: libvirt-libs = %{version}-%{release}
 # needed for device enumeration
 Requires: systemd >= 185
+# For managing persistent mediated devices
+Requires: mdevctl
 
 %description daemon-driver-nodedev
 The nodedev driver plugin for the libvirtd daemon, providing
index 9046e3bf07380de40e0ad35338cdc1cace41f037..bd3cb1f757649cdf485bb6bbfc0e877f549b2fdf 100644 (file)
@@ -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])
 
   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 algorithms)])
   AC_DEFINE_UNQUOTED([ADDR2LINE], ["$ADDR2LINE"],
                      [Location of addr2line program])
+  AC_DEFINE_UNQUOTED([MDEVCTL], ["$MDEVCTL"],
+                     [Location or name of the mdevctl program])
 
   AC_PATH_PROG([IP_PATH], [ip], [/sbin/ip], [$LIBVIRT_SBIN_PATH])
   AC_DEFINE_UNQUOTED([IP_PATH], ["$IP_PATH"], [path to ip binary])
index 3a34a324ca0b4bbdc3fd6f99e4c401d5e9434f19..bfd524121ca3e78ac332f3ea7489839db2a3dc50 100644 (file)
@@ -399,6 +399,40 @@ virNodeDeviceObjListFindSCSIHostByWWNs(virNodeDeviceObjListPtr devs,
                                       &data);
 }
 
+static int
+virNodeDeviceObjListFindMediatedDeviceByUUIDCallback(const void *payload,
+                                                     const void *name G_GNUC_UNUSED,
+                                                     const void *opaque)
+{
+    virNodeDeviceObjPtr obj = (virNodeDeviceObjPtr) payload;
+    const char *uuid = (const char *) opaque;
+    virNodeDevCapsDefPtr cap;
+    int want = 0;
+
+    virObjectLock(obj);
+
+    for (cap = obj->def->caps; cap != NULL; cap = cap->next) {
+        if (cap->data.type == VIR_NODE_DEV_CAP_MDEV) {
+            if (STREQ(cap->data.mdev.uuid, uuid)) {
+                want = 1;
+                break;
+            }
+        }
+     }
+
+    virObjectUnlock(obj);
+    return want;
+}
+
+
+virNodeDeviceObjPtr
+virNodeDeviceObjListFindMediatedDeviceByUUID(virNodeDeviceObjListPtr devs,
+                                             const char *uuid)
+{
+    return virNodeDeviceObjListSearch(devs,
+                                      virNodeDeviceObjListFindMediatedDeviceByUUIDCallback,
+                                      uuid);
+}
 
 static void
 virNodeDeviceObjListDispose(void *obj)
index c9df8dedab1d8fdacfb292a81c99ae0274a6e8c1..6efdb23d365d2e45ed39c7a66a23737af540bf90 100644 (file)
@@ -118,3 +118,6 @@ virNodeDeviceObjListExport(virConnectPtr conn,
 void
 virNodeDeviceObjSetSkipUpdateCaps(virNodeDeviceObjPtr obj,
                                   bool skipUpdateCaps);
+virNodeDeviceObjPtr
+virNodeDeviceObjListFindMediatedDeviceByUUID(virNodeDeviceObjListPtr devs,
+                                             const char *uuid);
index 22bd8b9c175b6616982557100736c559aa362b21..42f8d7c222ef4e610658bddccb4b267f530b3ad4 100644 (file)
@@ -1179,6 +1179,7 @@ virNodeDeviceObjListAssignDef;
 virNodeDeviceObjListExport;
 virNodeDeviceObjListFindByName;
 virNodeDeviceObjListFindBySysfsPath;
+virNodeDeviceObjListFindMediatedDeviceByUUID;
 virNodeDeviceObjListFindSCSIHostByWWNs;
 virNodeDeviceObjListFree;
 virNodeDeviceObjListGetNames;
index d6255a43c852d9afaf0c68bb9d89adcdd34dd03c..35016782d262611745da38cfe934d9d09b72d4de 100644 (file)
@@ -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"
 
 #define VIR_FROM_THIS VIR_FROM_NODEDEV
 
@@ -304,6 +306,30 @@ nodeDeviceLookupSCSIHostByWWN(virConnectPtr conn,
     return device;
 }
 
+static virNodeDevicePtr
+nodeDeviceLookupMediatedDeviceByUUID(virConnectPtr conn,
+                                     const char *uuid,
+                                     unsigned int flags)
+{
+    virNodeDeviceObjPtr obj = NULL;
+    virNodeDeviceDefPtr def;
+    virNodeDevicePtr device = NULL;
+
+    virCheckFlags(0, NULL);
+
+    if (!(obj = virNodeDeviceObjListFindMediatedDeviceByUUID(driver->devs,
+                                                             uuid)))
+        return NULL;
+
+    def = virNodeDeviceObjGetDef(obj);
+
+    if ((device = virGetNodeDevice(conn, def->name)))
+        device->parentName = g_strdup(def->parent);
+
+    virNodeDeviceObjEndAPI(&obj);
+    return device;
+}
+
 
 char *
 nodeDeviceGetXMLDesc(virNodeDevicePtr device,
@@ -492,6 +518,26 @@ nodeDeviceFindNewDevice(virConnectPtr conn,
 }
 
 
+static virNodeDevicePtr
+nodeDeviceFindNewMediatedDeviceFunc(virConnectPtr conn,
+                                    const void *opaque)
+{
+    const char *uuid = 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, virNodeDevCapType type)
 }
 
 
+/* 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 = &def->caps->data.mdev;
+    g_autoptr(virJSONValue) json = 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 = virJSONValueNewArray();
+
+        for (i = 0; i < mdev->nattributes; i++) {
+            virMediatedDeviceAttrPtr attr = mdev->attributes[i];
+            g_autoptr(virJSONValue) jsonattr = virJSONValueNewObject();
+
+            if (virJSONValueObjectAppendString(jsonattr, attr->name, attr->value) < 0)
+                return -1;
+
+            if (virJSONValueArrayAppend(attributes, g_steal_pointer(&jsonattr)) < 0)
+                return -1;
+        }
+
+        if (virJSONValueObjectAppend(json, "attrs", g_steal_pointer(&attributes)) < 0)
+            return -1;
+    }
+
+    *buf = virJSONValueToString(json, false);
+    if (!*buf)
+        return -1;
+
+    return 0;
+}
+
+
+static char *
+nodeDeviceFindAddressByName(const char *name)
+{
+    virNodeDeviceDefPtr def = NULL;
+    virNodeDevCapsDefPtr caps = NULL;
+    char *pci_addr = NULL;
+    virNodeDeviceObjPtr dev = virNodeDeviceObjListFindByName(driver->devs, name);
+
+    if (!dev) {
+        virReportError(VIR_ERR_NO_NODE_DEVICE,
+                       _("could not find device '%s'"), name);
+        return NULL;
+    }
+
+    def = virNodeDeviceObjGetDef(dev);
+    for (caps = def->caps; caps != NULL; caps = caps->next) {
+        if (caps->data.type == VIR_NODE_DEV_CAP_PCI_DEV) {
+            virPCIDeviceAddress addr = {
+                .domain = caps->data.pci_dev.domain,
+                .bus = caps->data.pci_dev.bus,
+                .slot = caps->data.pci_dev.slot,
+                .function = caps->data.pci_dev.function
+            };
+
+            pci_addr = virPCIDeviceAddressAsString(&addr);
+            break;
+        }
+    }
+
+    virNodeDeviceObjEndAPI(&dev);
+
+    return pci_addr;
+}
+
+
+virCommandPtr
+nodeDeviceGetMdevctlStartCommand(virNodeDeviceDefPtr def,
+                                 char **uuid_out)
+{
+    virCommandPtr cmd;
+    g_autofree char *json = NULL;
+    g_autofree char *parent_pci = nodeDeviceFindAddressByName(def->parent);
+
+    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 = 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 = nodeDeviceGetMdevctlStartCommand(def, uuid);
+    if (!cmd)
+        return -1;
+
+    /* an auto-generated uuid is returned via stdout if no uuid is specified in
+     * the mdevctl args */
+    if (virCommandRun(cmd, &status) < 0 || status != 0)
+        return -1;
+
+    /* remove newline */
+    *uuid = g_strstrip(*uuid);
+
+    return 0;
+}
+
+
+static virNodeDevicePtr
+nodeDeviceCreateXMLMdev(virConnectPtr conn,
+                        virNodeDeviceDefPtr def)
+{
+    g_autofree char *uuid = 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 = nodeDeviceCreateXMLMdev(conn, def);
     } else {
         virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                        _("Unsupported device type"));
index eae5e2cb170ecf566702254dd0d5e40a0296c769..e42c14f6c77c2ec679619b5e83d8e329b58c665c 100644 (file)
@@ -24,6 +24,7 @@
 #include "internal.h"
 #include "driver.h"
 #include "virnodedeviceobj.h"
+#include "vircommand.h"
 
 #define LINUX_NEW_DEVICE_WAIT_TIME 60
 
@@ -116,3 +117,7 @@ nodeConnectNodeDeviceEventRegisterAny(virConnectPtr conn,
 int
 nodeConnectNodeDeviceEventDeregisterAny(virConnectPtr conn,
                                         int callbackID);
+
+virCommandPtr
+nodeDeviceGetMdevctlStartCommand(virNodeDeviceDefPtr def,
+                                 char **uuid_out);