From 5d9ad485395b057739736ea31efa82d275c57324 Mon Sep 17 00:00:00 2001
From: AlexChen <alex.chen@huawei.com>
Date: Fri, 29 Mar 2024 15:27:46 +0800
Subject: [PATCH] hostdev:Introduce vDPA device to hostdev subsystem as a new
 subtype

The following is the xml of vdpa device:
<devices>
    <hostdev mode='subsystem' type='vdpa'>
        <source dev='/dev/vhost-vdpa-0'/>
    </hostdev>
</devices>
And the command line passed to QEMU is as follows:
-device {"driver":"vhost-vdpa-device-pci","vhostdev":"/dev/vhost-vdpa-0"}

This solution is selected according to the previous discussion
on the solution of supporting the vDPA device.
For details, see the following:
https://listman.redhat.com/archives/libvir-list/2023-March/239018.html

Signed-off-by: AlexChen <alex.chen@huawei.com>
---
 src/conf/domain_audit.c         |  4 +++
 src/conf/domain_conf.c          | 43 +++++++++++++++++++++++++++++++++
 src/conf/domain_conf.h          |  6 +++++
 src/conf/domain_postparse.c     |  1 +
 src/conf/domain_validate.c      |  1 +
 src/conf/virconftypes.h         |  2 ++
 src/qemu/qemu_command.c         | 19 +++++++++++++++
 src/qemu/qemu_command.h         |  3 +++
 src/qemu/qemu_domain.c          |  7 ++++++
 src/qemu/qemu_hotplug.c         |  3 +++
 src/qemu/qemu_migration.c       |  2 ++
 src/qemu/qemu_validate.c        |  2 ++
 src/security/security_dac.c     |  2 ++
 src/security/security_selinux.c |  2 ++
 14 files changed, 97 insertions(+)

diff --git a/src/conf/domain_audit.c b/src/conf/domain_audit.c
index 7a6bb02203..e4d4d6d912 100644
--- a/src/conf/domain_audit.c
+++ b/src/conf/domain_audit.c
@@ -344,6 +344,7 @@ virDomainAuditHostdev(virDomainObj *vm, virDomainHostdevDef *hostdev,
     virDomainHostdevSubsysSCSI *scsisrc = &hostdev->source.subsys.u.scsi;
     virDomainHostdevSubsysSCSIVHost *hostsrc = &hostdev->source.subsys.u.scsi_host;
     virDomainHostdevSubsysMediatedDev *mdevsrc = &hostdev->source.subsys.u.mdev;
+    virDomainHostdevSubsysVDPA *vdpasrc = &hostdev->source.subsys.u.vdpa;
 
     virUUIDFormat(vm->def->uuid, uuidstr);
     if (!(vmname = virAuditEncode("vm", vm->def->name))) {
@@ -383,6 +384,9 @@ virDomainAuditHostdev(virDomainObj *vm, virDomainHostdevDef *hostdev,
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV:
             address = g_strdup(mdevsrc->uuidstr);
             break;
+        case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_VDPA:
+            address = g_strdup(vdpasrc->devpath);
+            break;
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
         default:
             VIR_WARN("Unexpected hostdev type while encoding audit message: %d",
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index f54f99f379..0f4c9012c1 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -1052,6 +1052,7 @@ VIR_ENUM_IMPL(virDomainHostdevSubsys,
               "scsi",
               "scsi_host",
               "mdev",
+              "vdpa",
 );
 
 VIR_ENUM_IMPL(virDomainHostdevSubsysPCIBackend,
@@ -2645,6 +2646,9 @@ virDomainHostdevDefClear(virDomainHostdevDef *def)
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
             g_clear_pointer(&def->source.subsys.u.pci.origstates, virBitmapFree);
             break;
+        case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_VDPA:
+            VIR_FREE(def->source.subsys.u.vdpa.devpath);
+            break;
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV:
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
@@ -6170,6 +6174,22 @@ virDomainHostdevSubsysMediatedDevDefParseXML(virDomainHostdevDef *def,
     return 0;
 }
 
+static int
+virDomainHostdevSubsysVDPADefParseXML(xmlNodePtr sourcenode,
+                                      virDomainHostdevDef *def)
+{
+    g_autofree char *devpath = NULL;
+    virDomainHostdevSubsysVDPA *vdpa = &def->source.subsys.u.vdpa;
+
+    if(!(devpath = virXMLPropString(sourcenode, "dev"))) {
+       virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                      _("Missing 'dev' attribute for element <source>"));
+       return -1;
+    }
+    vdpa->devpath = g_steal_pointer(&devpath);
+    return 0;
+}
+
 static int
 virDomainHostdevDefParseXMLSubsys(xmlNodePtr node,
                                   xmlXPathContextPtr ctxt,
@@ -6315,6 +6335,11 @@ virDomainHostdevDefParseXMLSubsys(xmlNodePtr node,
         if (virDomainHostdevSubsysMediatedDevDefParseXML(def, ctxt) < 0)
             return -1;
         break;
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_VDPA:
+        if (virDomainHostdevSubsysVDPADefParseXML(sourcenode, def) < 0) {
+            return -1;
+        }
+        break;
 
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
     default:
@@ -13005,6 +13030,7 @@ virDomainHostdevDefParseXML(virDomainXMLOption *xmlopt,
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST:
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV:
+        case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_VDPA:
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
             break;
         }
@@ -14152,6 +14178,9 @@ virDomainHostdevMatchSubsys(virDomainHostdevDef *a,
             return 0;
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV:
         return virDomainHostdevMatchSubsysMediatedDev(a, b);
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_VDPA:
+        return STREQ(a->source.subsys.u.vdpa.devpath,
+                     b->source.subsys.u.vdpa.devpath);
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
         return 0;
     }
@@ -23501,6 +23530,16 @@ virDomainHostdevDefFormatSubsysMdev(virBuffer *buf,
     virXMLFormatElement(buf, "source", NULL, &sourceChildBuf);
 }
 
+static void
+virDomainHostdevDefFormatSubsysVDPA(virBuffer *buf,
+                                    virDomainHostdevDef *def)
+{
+    g_auto(virBuffer) sourceAttrBuf = VIR_BUFFER_INITIALIZER;
+    virDomainHostdevSubsysVDPA *vdpasrc = &def->source.subsys.u.vdpa;
+    virBufferAsprintf(&sourceAttrBuf, " dev='%s'", vdpasrc->devpath);
+    virXMLFormatElement(buf, "source", &sourceAttrBuf, NULL);
+}
+
 
 static int
 virDomainHostdevDefFormatSubsys(virBuffer *buf,
@@ -23528,6 +23567,10 @@ virDomainHostdevDefFormatSubsys(virBuffer *buf,
         virDomainHostdevDefFormatSubsysMdev(buf, def);
         return 0;
 
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_VDPA:
+        virDomainHostdevDefFormatSubsysVDPA(buf, def);
+        return 0;
+
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
     default:
         virReportEnumRangeError(virDomainHostdevSubsysType, def->source.subsys.type);
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 196053d950..2a85f7bcf0 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -198,6 +198,7 @@ typedef enum {
     VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI,
     VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST,
     VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV,
+    VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_VDPA,
 
     VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST
 } virDomainHostdevSubsysType;
@@ -290,6 +291,10 @@ struct _virDomainHostdevSubsysMediatedDev {
     virTristateSwitch ramfb;
 };
 
+struct _virDomainHostdevSubsysVDPA {
+    char *devpath; /* vDPA device path */
+};
+
 typedef enum {
     VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_HOST_PROTOCOL_TYPE_NONE = 0,
     VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_HOST_PROTOCOL_TYPE_VHOST,
@@ -324,6 +329,7 @@ struct _virDomainHostdevSubsys {
         virDomainHostdevSubsysSCSI scsi;
         virDomainHostdevSubsysSCSIVHost scsi_host;
         virDomainHostdevSubsysMediatedDev mdev;
+        virDomainHostdevSubsysVDPA vdpa;
     } u;
 };
 
diff --git a/src/conf/domain_postparse.c b/src/conf/domain_postparse.c
index e79913b73f..b948bf0653 100644
--- a/src/conf/domain_postparse.c
+++ b/src/conf/domain_postparse.c
@@ -351,6 +351,7 @@ virDomainHostdevDefPostParse(virDomainHostdevDef *dev,
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST:
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_VDPA:
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
         break;
     }
diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c
index c72108886e..d88ef6b915 100644
--- a/src/conf/domain_validate.c
+++ b/src/conf/domain_validate.c
@@ -2190,6 +2190,7 @@ virDomainHostdevDefValidate(const virDomainHostdevDef *hostdev)
             }
             break;
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV:
+        case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_VDPA:
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
             break;
         }
diff --git a/src/conf/virconftypes.h b/src/conf/virconftypes.h
index 26cb966194..bcdcb8b825 100644
--- a/src/conf/virconftypes.h
+++ b/src/conf/virconftypes.h
@@ -120,6 +120,8 @@ typedef struct _virDomainHostdevSubsys virDomainHostdevSubsys;
 
 typedef struct _virDomainHostdevSubsysMediatedDev virDomainHostdevSubsysMediatedDev;
 
+typedef struct _virDomainHostdevSubsysVDPA virDomainHostdevSubsysVDPA;
+
 typedef struct _virDomainHostdevSubsysPCI virDomainHostdevSubsysPCI;
 
 typedef struct _virDomainHostdevSubsysSCSI virDomainHostdevSubsysSCSI;
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index d54149ed2d..de3be7fc1c 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -5004,6 +5004,18 @@ qemuBuildHostdevMediatedDevProps(const virDomainDef *def,
     return g_steal_pointer(&props);
 }
 
+virJSONValue *
+qemuBuildHostdevVDPADevProps(virDomainHostdevDef *dev)
+{
+    g_autoptr(virJSONValue) props = NULL;
+    virDomainHostdevSubsysVDPA *vdpasrc = &dev->source.subsys.u.vdpa;
+    if (virJSONValueObjectAdd(&props,
+                              "s:driver", "vhost-vdpa-device-pci",
+                              "s:vhostdev", vdpasrc->devpath,
+                              NULL) < 0)
+        return NULL;
+    return g_steal_pointer(&props);
+}
 
 qemuBlockStorageSourceAttachData *
 qemuBuildHostdevSCSIDetachPrepare(virDomainHostdevDef *hostdev,
@@ -5199,6 +5211,13 @@ qemuBuildHostdevCommandLine(virCommand *cmd,
                 return -1;
             break;
 
+        case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_VDPA:
+            if (!(devprops = qemuBuildHostdevVDPADevProps(hostdev)))
+                return -1;
+            if (qemuBuildDeviceCommandlineFromJSON(cmd, devprops, def, qemuCaps) < 0)
+                return -1;
+            break;
+
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
             break;
         }
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h
index 341ec43f9a..432ea59310 100644
--- a/src/qemu/qemu_command.h
+++ b/src/qemu/qemu_command.h
@@ -198,6 +198,9 @@ virJSONValue *
 qemuBuildHostdevMediatedDevProps(const virDomainDef *def,
                                  virDomainHostdevDef *dev);
 
+virJSONValue *
+qemuBuildHostdevVDPADevProps(virDomainHostdevDef *dev);
+
 virJSONValue *
 qemuBuildRedirdevDevProps(const virDomainDef *def,
                           virDomainRedirdevDef *dev);
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index f8a6c43797..5a7e8d2607 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -10528,6 +10528,8 @@ qemuDomainGetHostdevPath(virDomainHostdevDef *dev,
     virDomainHostdevSubsysSCSI *scsisrc = &dev->source.subsys.u.scsi;
     virDomainHostdevSubsysSCSIVHost *hostsrc = &dev->source.subsys.u.scsi_host;
     virDomainHostdevSubsysMediatedDev *mdevsrc = &dev->source.subsys.u.mdev;
+    virDomainHostdevSubsysVDPA *vdpasrc = &dev->source.subsys.u.vdpa;
+
     g_autoptr(virUSBDevice) usb = NULL;
     g_autoptr(virSCSIDevice) scsi = NULL;
     g_autoptr(virSCSIVHostDevice) host = NULL;
@@ -10598,6 +10600,10 @@ qemuDomainGetHostdevPath(virDomainHostdevDef *dev,
             if (!(tmpPath = virMediatedDeviceGetIOMMUGroupDev(mdevsrc->uuidstr)))
                 return -1;
 
+            perm = VIR_CGROUP_DEVICE_RW;
+            break;
+        case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_VDPA:
+            tmpPath = g_strdup(vdpasrc->devpath);
             perm = VIR_CGROUP_DEVICE_RW;
             break;
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
@@ -11373,6 +11379,7 @@ qemuDomainPrepareHostdev(virDomainHostdevDef *hostdev,
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST:
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV:
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_VDPA:
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
         break;
     }
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index 4e2fc724c0..29c0856f62 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -2805,6 +2805,7 @@ qemuDomainAttachHostDevice(virQEMUDriver *driver,
             return -1;
         break;
 
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_VDPA:
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
     default:
         virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
@@ -4717,6 +4718,7 @@ qemuDomainRemoveHostDevice(virQEMUDriver *driver,
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV:
         qemuDomainRemoveMediatedDevice(driver, vm, hostdev);
         break;
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_VDPA:
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
         break;
     }
@@ -5743,6 +5745,7 @@ qemuDomainDetachPrepHostdev(virDomainObj *vm,
             break;
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST:
             break;
+        case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_VDPA:
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
         default:
             virReportError(VIR_ERR_INTERNAL_ERROR,
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 41ce565ede..0a89d102df 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -1310,6 +1310,8 @@ qemuMigrationSrcIsAllowedHostdev(const virDomainDef *def)
             case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI:
             case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST:
             case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV:
+                /* The vDPA devices don't support migration for now */
+            case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_VDPA:
                 virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
                                _("cannot migrate a domain with <hostdev mode='subsystem' type='%1$s'>"),
                                virDomainHostdevSubsysTypeToString(hostdev->source.subsys.type));
diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c
index e475ad035e..18c5b60a35 100644
--- a/src/qemu/qemu_validate.c
+++ b/src/qemu/qemu_validate.c
@@ -2496,6 +2496,8 @@ qemuValidateDomainDeviceDefHostdev(const virDomainHostdevDef *hostdev,
             break;
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV:
             return qemuValidateDomainMdevDef(hostdev, def, qemuCaps);
+        case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_VDPA:
+            break;
         case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
         default:
             virReportEnumRangeError(virDomainHostdevSubsysType,
diff --git a/src/security/security_dac.c b/src/security/security_dac.c
index c07e488db7..96aebfce5b 100644
--- a/src/security/security_dac.c
+++ b/src/security/security_dac.c
@@ -1313,6 +1313,7 @@ virSecurityDACSetHostdevLabel(virSecurityManager *mgr,
         break;
     }
 
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_VDPA:
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
         ret = 0;
         break;
@@ -1469,6 +1470,7 @@ virSecurityDACRestoreHostdevLabel(virSecurityManager *mgr,
         break;
     }
 
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_VDPA:
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
         ret = 0;
         break;
diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c
index 84c5ce75ed..652c086cb0 100644
--- a/src/security/security_selinux.c
+++ b/src/security/security_selinux.c
@@ -2256,6 +2256,7 @@ virSecuritySELinuxSetHostdevSubsysLabel(virSecurityManager *mgr,
         break;
     }
 
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_VDPA:
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
         ret = 0;
         break;
@@ -2487,6 +2488,7 @@ virSecuritySELinuxRestoreHostdevSubsysLabel(virSecurityManager *mgr,
         break;
     }
 
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_VDPA:
     case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
         ret = 0;
         break;
-- 
2.27.0

