From e8597e64126a5cf77fbb9422977268878a6376b6 Mon Sep 17 00:00:00 2001
From: AlexChen <alex.chen@huawei.com>
Date: Wed, 14 Oct 2020 12:08:30 -0500
Subject: [PATCH] Include vdpa devices in node device list

The current udev node device driver ignores all events related to vdpa
devices. Since libvirt now supports vDPA network devices, include these
devices in the device list.

Example output:

virsh # nodedev-list
[...ommitted long list of nodedevs...]
vdpa_vdpa0

virsh # nodedev-dumpxml vdpa_vdpa0
<device>
  <name>vdpa_vdpa0</name>
  <path>/sys/devices/vdpa0</path>
  <parent>computer</parent>
  <driver>
    <name>vhost_vdpa</name>
  </driver>
  <capability type='vdpa'>
    <chardev>/dev/vhost-vdpa-0</chardev>
  </capability>
</device>

NOTE: normally the 'parent' would be a PCI device instead of 'computer',
but this example output is from the vdpa_sim kernel module, so it
doesn't have a normal parent device.

Signed-off-by: Jonathon Jongsma <jjongsma@redhat.com>
Signed-off-by: AlexChen <alex.chen@huawei.com>
---
 docs/formatnode.html.in            |  9 ++++++
 docs/schemas/nodedev.rng           | 10 ++++++
 include/libvirt/libvirt-nodedev.h  |  1 +
 src/conf/node_device_conf.c        | 13 ++++++++
 src/conf/node_device_conf.h        | 11 ++++++-
 src/conf/virnodedeviceobj.c        |  4 ++-
 src/node_device/node_device_udev.c | 51 ++++++++++++++++++++++++++++++
 tools/virsh-nodedev.c              |  3 ++
 8 files changed, 100 insertions(+), 2 deletions(-)

diff --git a/docs/formatnode.html.in b/docs/formatnode.html.in
index c2a8f8fb7a..573391ef0f 100644
--- a/docs/formatnode.html.in
+++ b/docs/formatnode.html.in
@@ -341,6 +341,15 @@
               <dd>The device number.</dd>
             </dl>
           </dd>
+          <dt><code>vdpa</code></dt>
+          <dd>Describes a virtual datapath acceleration (vDPA) network device.
+          <span class="since">Since 6.9.0</span>. Sub-elements include:
+            <dl>
+              <dt><code>chardev</code></dt>
+              <dd>The path to the character device that is used to access the
+                  device.</dd>
+            </dl>
+          </dd>
         </dl>
       </dd>
     </dl>
diff --git a/docs/schemas/nodedev.rng b/docs/schemas/nodedev.rng
index fe6ffa0b53..4f197b327a 100644
--- a/docs/schemas/nodedev.rng
+++ b/docs/schemas/nodedev.rng
@@ -85,6 +85,7 @@
         <ref name="capdrm"/>
         <ref name="capmdev"/>
         <ref name="capccwdev"/>
+        <ref name="capvdpa"/>
       </choice>
     </element>
   </define>
@@ -651,6 +652,15 @@
     </element>
   </define>
 
+  <define name="capvdpa">
+    <attribute name="type">
+      <value>vdpa</value>
+    </attribute>
+    <element name="chardev">
+      <ref name="path"/>
+    </element>
+  </define>
+
   <define name='address'>
     <element name='address'>
       <attribute name='domain'><ref name='hexuint'/></attribute>
diff --git a/include/libvirt/libvirt-nodedev.h b/include/libvirt/libvirt-nodedev.h
index a2ad61ac6d..4129f1afed 100644
--- a/include/libvirt/libvirt-nodedev.h
+++ b/include/libvirt/libvirt-nodedev.h
@@ -81,6 +81,7 @@ typedef enum {
     VIR_CONNECT_LIST_NODE_DEVICES_CAP_MDEV_TYPES    = 1 << 13, /* Capable of mediated devices */
     VIR_CONNECT_LIST_NODE_DEVICES_CAP_MDEV          = 1 << 14, /* Mediated device */
     VIR_CONNECT_LIST_NODE_DEVICES_CAP_CCW_DEV       = 1 << 15, /* CCW device */
+    VIR_CONNECT_LIST_NODE_DEVICES_CAP_VDPA          = 1 << 17, /* vDPA device */
 } virConnectListAllNodeDeviceFlags;
 
 int                     virConnectListAllNodeDevices (virConnectPtr conn,
diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c
index d64f6d3986..41d7c7d12e 100644
--- a/src/conf/node_device_conf.c
+++ b/src/conf/node_device_conf.c
@@ -65,6 +65,7 @@ VIR_ENUM_IMPL(virNodeDevCap,
               "mdev_types",
               "mdev",
               "ccw",
+              "vdpa",
 );
 
 VIR_ENUM_IMPL(virNodeDevNetCap,
@@ -500,6 +501,12 @@ virNodeDeviceCapStorageDefFormat(virBufferPtr buf,
         virBufferAddLit(buf, "<capability type='hotpluggable'/>\n");
 }
 
+static void
+virNodeDeviceCapVDPADefFormat(virBufferPtr buf,
+                              const virNodeDevCapData *data)
+{
+    virBufferEscapeString(buf, "<chardev>%s</chardev>\n", data->vdpa.chardev);
+}
 
 char *
 virNodeDeviceDefFormat(const virNodeDeviceDef *def)
@@ -595,6 +602,9 @@ virNodeDeviceDefFormat(const virNodeDeviceDef *def)
             virBufferAsprintf(&buf, "<devno>0x%04x</devno>\n",
                               data->ccw_dev.devno);
             break;
+        case VIR_NODE_DEV_CAP_VDPA:
+            virNodeDeviceCapVDPADefFormat(&buf, data);
+            break;
         case VIR_NODE_DEV_CAP_MDEV_TYPES:
         case VIR_NODE_DEV_CAP_FC_HOST:
         case VIR_NODE_DEV_CAP_VPORTS:
@@ -1897,6 +1907,7 @@ virNodeDevCapsDefParseXML(xmlXPathContextPtr ctxt,
     case VIR_NODE_DEV_CAP_FC_HOST:
     case VIR_NODE_DEV_CAP_VPORTS:
     case VIR_NODE_DEV_CAP_SCSI_GENERIC:
+    case VIR_NODE_DEV_CAP_VDPA:
     case VIR_NODE_DEV_CAP_LAST:
         virReportError(VIR_ERR_INTERNAL_ERROR,
                        _("unknown capability type '%d' for '%s'"),
@@ -2209,6 +2220,7 @@ virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps)
     case VIR_NODE_DEV_CAP_FC_HOST:
     case VIR_NODE_DEV_CAP_VPORTS:
     case VIR_NODE_DEV_CAP_CCW_DEV:
+    case VIR_NODE_DEV_CAP_VDPA:
     case VIR_NODE_DEV_CAP_LAST:
         /* This case is here to shutup the compiler */
         break;
@@ -2262,6 +2274,7 @@ virNodeDeviceUpdateCaps(virNodeDeviceDefPtr def)
         case VIR_NODE_DEV_CAP_MDEV_TYPES:
         case VIR_NODE_DEV_CAP_MDEV:
         case VIR_NODE_DEV_CAP_CCW_DEV:
+        case VIR_NODE_DEV_CAP_VDPA:
         case VIR_NODE_DEV_CAP_LAST:
             break;
         }
diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h
index 9e4b0847fb..a14eaef742 100644
--- a/src/conf/node_device_conf.h
+++ b/src/conf/node_device_conf.h
@@ -64,6 +64,7 @@ typedef enum {
     VIR_NODE_DEV_CAP_MDEV_TYPES,        /* Device capable of mediated devices */
     VIR_NODE_DEV_CAP_MDEV,              /* Mediated device */
     VIR_NODE_DEV_CAP_CCW_DEV,           /* s390 CCW device */
+    VIR_NODE_DEV_CAP_VDPA,              /* vDPA device */
 
     VIR_NODE_DEV_CAP_LAST
 } virNodeDevCapType;
@@ -271,6 +272,12 @@ struct _virNodeDevCapCCW {
     unsigned int devno;
 };
 
+typedef struct _virNodeDevCapVDPA virNodeDevCapVDPA;
+typedef virNodeDevCapVDPA *virNodeDevCapVDPAPtr;
+struct _virNodeDevCapVDPA {
+    char *chardev;
+};
+
 typedef struct _virNodeDevCapData virNodeDevCapData;
 typedef virNodeDevCapData *virNodeDevCapDataPtr;
 struct _virNodeDevCapData {
@@ -289,6 +296,7 @@ struct _virNodeDevCapData {
         virNodeDevCapDRM drm;
         virNodeDevCapMdev mdev;
         virNodeDevCapCCW ccw_dev;
+        virNodeDevCapVDPA vdpa;
     };
 };
 
@@ -364,7 +372,8 @@ virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps);
                  VIR_CONNECT_LIST_NODE_DEVICES_CAP_DRM           | \
                  VIR_CONNECT_LIST_NODE_DEVICES_CAP_MDEV_TYPES    | \
                  VIR_CONNECT_LIST_NODE_DEVICES_CAP_MDEV          | \
-                 VIR_CONNECT_LIST_NODE_DEVICES_CAP_CCW_DEV)
+                 VIR_CONNECT_LIST_NODE_DEVICES_CAP_CCW_DEV       | \
+                 VIR_CONNECT_LIST_NODE_DEVICES_CAP_VDPA)
 
 int
 virNodeDeviceGetSCSIHostCaps(virNodeDevCapSCSIHostPtr scsi_host);
diff --git a/src/conf/virnodedeviceobj.c b/src/conf/virnodedeviceobj.c
index 3a34a324ca..be111741bf 100644
--- a/src/conf/virnodedeviceobj.c
+++ b/src/conf/virnodedeviceobj.c
@@ -676,6 +676,7 @@ virNodeDeviceObjHasCap(const virNodeDeviceObj *obj,
         case VIR_NODE_DEV_CAP_MDEV_TYPES:
         case VIR_NODE_DEV_CAP_MDEV:
         case VIR_NODE_DEV_CAP_CCW_DEV:
+        case VIR_NODE_DEV_CAP_VDPA:
         case VIR_NODE_DEV_CAP_LAST:
             break;
         }
@@ -826,7 +827,8 @@ virNodeDeviceObjMatch(virNodeDeviceObjPtr obj,
               MATCH(DRM)           ||
               MATCH(MDEV_TYPES)    ||
               MATCH(MDEV)          ||
-              MATCH(CCW_DEV)))
+              MATCH(CCW_DEV)       ||
+              MATCH(VDPA)))
             return false;
     }
 
diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c
index 0d8a7db5c6..2764315325 100644
--- a/src/node_device/node_device_udev.c
+++ b/src/node_device/node_device_udev.c
@@ -1089,6 +1089,53 @@ udevProcessCCW(struct udev_device *device,
     return 0;
 }
 
+static int
+udevGetVDPACharDev(const char *sysfs_path,
+                   virNodeDevCapDataPtr data)
+{
+    struct dirent *entry;
+    DIR *dir = NULL;
+    int direrr;
+
+    if (virDirOpenIfExists(&dir, sysfs_path) <= 0)
+        return -1;
+
+    while ((direrr = virDirRead(dir, &entry, NULL)) > 0) {
+        if (g_str_has_prefix(entry->d_name, "vhost-vdpa")) {
+            g_autofree char *chardev = g_strdup_printf("/dev/%s", entry->d_name);
+
+            if (!virFileExists(chardev)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("vDPA chardev path '%s' does not exist"),
+                               chardev);
+                return -1;
+            }
+            VIR_DEBUG("vDPA chardev is at '%s'", chardev);
+
+            data->vdpa.chardev = g_steal_pointer(&chardev);
+            break;
+        }
+    }
+
+    if (direrr < 0)
+        return -1;
+
+    return 0;
+}
+
+static int
+udevProcessVDPA(struct udev_device *device,
+                virNodeDeviceDefPtr def)
+{
+    if (udevGenerateDeviceName(device, def, NULL) != 0)
+        return -1;
+
+    if (udevGetVDPACharDev(def->sysfs_path, &def->caps->data) < 0)
+        return -1;
+
+    return 0;
+}
+
 
 static int
 udevGetDeviceNodes(struct udev_device *device,
@@ -1168,6 +1215,8 @@ udevGetDeviceType(struct udev_device *device,
             *type = VIR_NODE_DEV_CAP_MDEV;
         else if (STREQ_NULLABLE(subsystem, "ccw"))
             *type = VIR_NODE_DEV_CAP_CCW_DEV;
+        else if (STREQ_NULLABLE(subsystem, "vdpa"))
+            *type = VIR_NODE_DEV_CAP_VDPA;
 
         VIR_FREE(subsystem);
     }
@@ -1212,6 +1261,8 @@ udevGetDeviceDetails(struct udev_device *device,
         return udevProcessMediatedDevice(device, def);
     case VIR_NODE_DEV_CAP_CCW_DEV:
         return udevProcessCCW(device, def);
+    case VIR_NODE_DEV_CAP_VDPA:
+        return udevProcessVDPA(device, def);
     case VIR_NODE_DEV_CAP_MDEV_TYPES:
     case VIR_NODE_DEV_CAP_SYSTEM:
     case VIR_NODE_DEV_CAP_FC_HOST:
diff --git a/tools/virsh-nodedev.c b/tools/virsh-nodedev.c
index 7f091d7cf8..f831b36054 100644
--- a/tools/virsh-nodedev.c
+++ b/tools/virsh-nodedev.c
@@ -462,6 +462,9 @@ cmdNodeListDevices(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED)
         case VIR_NODE_DEV_CAP_CCW_DEV:
             flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_CCW_DEV;
             break;
+        case VIR_NODE_DEV_CAP_VDPA:
+            flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_VDPA;
+            break;
         case VIR_NODE_DEV_CAP_LAST:
             break;
         }
-- 
2.27.0

