passthrough: give XEN_DOMCTL_test_assign_device more sane semantics
authorJan Beulich <jbeulich@suse.com>
Mon, 28 Aug 2017 08:48:55 +0000 (10:48 +0200)
committerJan Beulich <jbeulich@suse.com>
Mon, 28 Aug 2017 08:48:55 +0000 (10:48 +0200)
So far callers of the libxc interface passed in a domain ID which was
then ignored in the hypervisor. Instead, make the hypervisor honor it
(accepting DOMID_INVALID to obtain original behavior), allowing to
query whether a device can be assigned to a particular domain. Do this
by folding the assign and test-assign paths.

Drop XSM's test_assign_{,dt}device hooks as no longer being
individually useful.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Daniel De Graaf <dgdegra@tycho.nsa.gov>
Reviewed-by: Wei Liu <wei.liu2@citrix.com>
xen/common/domctl.c
xen/drivers/passthrough/device_tree.c
xen/drivers/passthrough/pci.c
xen/include/public/domctl.h
xen/include/xsm/dummy.h
xen/include/xsm/xsm.h
xen/xsm/dummy.c
xen/xsm/flask/hooks.c

index d80488bbe95357b0af603358d53123158aa490d0..42658e5744b9d1dd68c2f5da8546576e41a430d7 100644 (file)
@@ -391,11 +391,15 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl)
 
     switch ( op->cmd )
     {
-    case XEN_DOMCTL_createdomain:
     case XEN_DOMCTL_test_assign_device:
+        if ( op->domain == DOMID_INVALID )
+        {
+    case XEN_DOMCTL_createdomain:
     case XEN_DOMCTL_gdbsx_guestmemio:
-        d = NULL;
-        break;
+            d = NULL;
+            break;
+        }
+        /* fall through */
     default:
         d = rcu_lock_domain_by_id(op->domain);
         if ( !d && op->cmd != XEN_DOMCTL_getdomaininfo )
index 99ed49ecd82abe7347db83250397ff761ca341ab..421f00343826ac193c0c7fd20af19c47c7675594 100644 (file)
@@ -143,12 +143,15 @@ int iommu_do_dt_domctl(struct xen_domctl *domctl, struct domain *d,
     switch ( domctl->cmd )
     {
     case XEN_DOMCTL_assign_device:
+        ASSERT(d);
+        /* fall through */
+    case XEN_DOMCTL_test_assign_device:
         ret = -ENODEV;
         if ( domctl->u.assign_device.dev != XEN_DOMCTL_DEV_DT )
             break;
 
         ret = -EINVAL;
-        if ( d->is_dying || domctl->u.assign_device.flags )
+        if ( (d && d->is_dying) || domctl->u.assign_device.flags )
             break;
 
         ret = dt_find_node_by_gpath(domctl->u.assign_device.u.dt.path,
@@ -161,6 +164,17 @@ int iommu_do_dt_domctl(struct xen_domctl *domctl, struct domain *d,
         if ( ret )
             break;
 
+        if ( domctl->cmd == XEN_DOMCTL_test_assign_device )
+        {
+            if ( iommu_dt_device_is_assigned(dev) )
+            {
+                printk(XENLOG_G_ERR "%s already assigned.\n",
+                       dt_node_full_name(dev));
+                ret = -EINVAL;
+            }
+            break;
+        }
+
         ret = iommu_assign_dt_device(d, dev);
 
         if ( ret )
@@ -194,33 +208,6 @@ int iommu_do_dt_domctl(struct xen_domctl *domctl, struct domain *d,
                    dt_node_full_name(dev), d->domain_id, ret);
         break;
 
-    case XEN_DOMCTL_test_assign_device:
-        ret = -ENODEV;
-        if ( domctl->u.assign_device.dev != XEN_DOMCTL_DEV_DT )
-            break;
-
-        ret = -EINVAL;
-        if ( domctl->u.assign_device.flags )
-            break;
-
-        ret = dt_find_node_by_gpath(domctl->u.assign_device.u.dt.path,
-                                    domctl->u.assign_device.u.dt.size,
-                                    &dev);
-        if ( ret )
-            break;
-
-        ret = xsm_test_assign_dtdevice(XSM_HOOK, dt_node_full_name(dev));
-        if ( ret )
-            break;
-
-        if ( iommu_dt_device_is_assigned(dev) )
-        {
-            printk(XENLOG_G_ERR "%s already assigned.\n",
-                   dt_node_full_name(dev));
-            ret = -EINVAL;
-        }
-        break;
-
     default:
         ret = -ENOSYS;
         break;
index ce22aa41a45c57d836a3c3ec4d16eef1e11d96a5..ae1fe3d5ab60299d6484fddc35d99517f7bd6113 100644 (file)
@@ -1593,35 +1593,10 @@ int iommu_do_pci_domctl(
     }
     break;
 
-    case XEN_DOMCTL_test_assign_device:
-        ret = -ENODEV;
-        if ( domctl->u.assign_device.dev != XEN_DOMCTL_DEV_PCI )
-            break;
-
-        ret = -EINVAL;
-        if ( domctl->u.assign_device.flags )
-            break;
-
-        machine_sbdf = domctl->u.assign_device.u.pci.machine_sbdf;
-
-        ret = xsm_test_assign_device(XSM_HOOK, machine_sbdf);
-        if ( ret )
-            break;
-
-        seg = machine_sbdf >> 16;
-        bus = PCI_BUS(machine_sbdf);
-        devfn = PCI_DEVFN2(machine_sbdf);
-
-        if ( device_assigned(seg, bus, devfn) )
-        {
-            printk(XENLOG_G_INFO
-                   "%04x:%02x:%02x.%u already assigned, or non-existent\n",
-                   seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
-            ret = -EINVAL;
-        }
-        break;
-
     case XEN_DOMCTL_assign_device:
+        ASSERT(d);
+        /* fall through */
+    case XEN_DOMCTL_test_assign_device:
         /* Don't support self-assignment of devices. */
         if ( d == current->domain )
         {
@@ -1635,7 +1610,9 @@ int iommu_do_pci_domctl(
 
         ret = -EINVAL;
         flags = domctl->u.assign_device.flags;
-        if ( d->is_dying || (flags & ~XEN_DOMCTL_DEV_RDM_RELAXED) )
+        if ( domctl->cmd == XEN_DOMCTL_assign_device
+             ? d->is_dying || (flags & ~XEN_DOMCTL_DEV_RDM_RELAXED)
+             : flags )
             break;
 
         machine_sbdf = domctl->u.assign_device.u.pci.machine_sbdf;
@@ -1648,8 +1625,20 @@ int iommu_do_pci_domctl(
         bus = PCI_BUS(machine_sbdf);
         devfn = PCI_DEVFN2(machine_sbdf);
 
-        ret = device_assigned(seg, bus, devfn) ?:
-              assign_device(d, seg, bus, devfn, flags);
+        ret = device_assigned(seg, bus, devfn);
+        if ( domctl->cmd == XEN_DOMCTL_test_assign_device )
+        {
+            if ( ret )
+            {
+                printk(XENLOG_G_INFO
+                       "%04x:%02x:%02x.%u already assigned, or non-existent\n",
+                       seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
+                ret = -EINVAL;
+            }
+            break;
+        }
+        if ( !ret )
+            ret = assign_device(d, seg, bus, devfn, flags);
         if ( ret == -ERESTART )
             ret = hypercall_create_continuation(__HYPERVISOR_domctl,
                                                 "h", u_domctl);
index 0669c3161cd3e92ce250d8e63acb20353478b8cf..73d8dc85f7727616ba6a9a561f8e2f4b669e07ab 100644 (file)
@@ -506,7 +506,11 @@ DEFINE_XEN_GUEST_HANDLE(xen_domctl_sendtrigger_t);
 
 /* Assign a device to a guest. Sets up IOMMU structures. */
 /* XEN_DOMCTL_assign_device */
-/* XEN_DOMCTL_test_assign_device */
+/*
+ * XEN_DOMCTL_test_assign_device: Pass DOMID_INVALID to find out whether the
+ * given device is assigned to any DomU at all. Pass a specific domain ID to
+ * find out whether the given device can be assigned to that domain.
+ */
 /*
  * XEN_DOMCTL_deassign_device: The behavior of this DOMCTL differs
  * between the different type of device:
index 62fcea6f04d687724551ed574d4ee9db6073b21e..ba89ea4bc17e8219e2a154bc39224939f4974451 100644 (file)
@@ -337,12 +337,6 @@ static XSM_INLINE int xsm_get_device_group(XSM_DEFAULT_ARG uint32_t machine_bdf)
     return xsm_default_action(action, current->domain, NULL);
 }
 
-static XSM_INLINE int xsm_test_assign_device(XSM_DEFAULT_ARG uint32_t machine_bdf)
-{
-    XSM_ASSERT_ACTION(XSM_HOOK);
-    return xsm_default_action(action, current->domain, NULL);
-}
-
 static XSM_INLINE int xsm_assign_device(XSM_DEFAULT_ARG struct domain *d, uint32_t machine_bdf)
 {
     XSM_ASSERT_ACTION(XSM_HOOK);
@@ -358,12 +352,6 @@ static XSM_INLINE int xsm_deassign_device(XSM_DEFAULT_ARG struct domain *d, uint
 #endif /* HAS_PASSTHROUGH && HAS_PCI */
 
 #if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_DEVICE_TREE)
-static XSM_INLINE int xsm_test_assign_dtdevice(XSM_DEFAULT_ARG const char *dtpath)
-{
-    XSM_ASSERT_ACTION(XSM_HOOK);
-    return xsm_default_action(action, current->domain, NULL);
-}
-
 static XSM_INLINE int xsm_assign_dtdevice(XSM_DEFAULT_ARG struct domain *d,
                                           const char *dtpath)
 {
index 60c0fd6a62e0257ef79885230b6e7895ae88c45c..7f7feffc68ac2a021dea0af224095f421ad70787 100644 (file)
@@ -109,13 +109,11 @@ struct xsm_operations {
 
 #if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_PCI)
     int (*get_device_group) (uint32_t machine_bdf);
-    int (*test_assign_device) (uint32_t machine_bdf);
     int (*assign_device) (struct domain *d, uint32_t machine_bdf);
     int (*deassign_device) (struct domain *d, uint32_t machine_bdf);
 #endif
 
 #if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_DEVICE_TREE)
-    int (*test_assign_dtdevice) (const char *dtpath);
     int (*assign_dtdevice) (struct domain *d, const char *dtpath);
     int (*deassign_dtdevice) (struct domain *d, const char *dtpath);
 #endif
@@ -465,11 +463,6 @@ static inline int xsm_get_device_group(xsm_default_t def, uint32_t machine_bdf)
     return xsm_ops->get_device_group(machine_bdf);
 }
 
-static inline int xsm_test_assign_device(xsm_default_t def, uint32_t machine_bdf)
-{
-    return xsm_ops->test_assign_device(machine_bdf);
-}
-
 static inline int xsm_assign_device(xsm_default_t def, struct domain *d, uint32_t machine_bdf)
 {
     return xsm_ops->assign_device(d, machine_bdf);
@@ -488,12 +481,6 @@ static inline int xsm_assign_dtdevice(xsm_default_t def, struct domain *d,
     return xsm_ops->assign_dtdevice(d, dtpath);
 }
 
-static inline int xsm_test_assign_dtdevice(xsm_default_t def,
-                                           const char *dtpath)
-{
-    return xsm_ops->test_assign_dtdevice(dtpath);
-}
-
 static inline int xsm_deassign_dtdevice(xsm_default_t def, struct domain *d,
                                         const char *dtpath)
 {
index 3cb5492dd354e712be56a213bc576d60e0d4d4fb..479b103614b28cbb12cd8729a0f5146bd718eb8c 100644 (file)
@@ -91,13 +91,11 @@ void __init xsm_fixup_ops (struct xsm_operations *ops)
 
 #if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_PCI)
     set_to_dummy_if_null(ops, get_device_group);
-    set_to_dummy_if_null(ops, test_assign_device);
     set_to_dummy_if_null(ops, assign_device);
     set_to_dummy_if_null(ops, deassign_device);
 #endif
 
 #if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_DEVICE_TREE)
-    set_to_dummy_if_null(ops, test_assign_dtdevice);
     set_to_dummy_if_null(ops, assign_dtdevice);
     set_to_dummy_if_null(ops, deassign_dtdevice);
 #endif
index 276ca97608d2e39a4c4ef693ba6ed8e60f522b07..61cd98533b1e3a905a8673be8773de314b174a55 100644 (file)
@@ -1307,6 +1307,9 @@ static int flask_assign_device(struct domain *d, uint32_t machine_bdf)
     struct avc_audit_data ad;
     u32 dperm = flask_iommu_resource_use_perm();
 
+    if ( !d )
+        return flask_test_assign_device(machine_bdf);
+
     rc = current_has_perm(d, SECCLASS_RESOURCE, RESOURCE__ADD);
     if ( rc )
         return rc;
@@ -1363,6 +1366,9 @@ static int flask_assign_dtdevice(struct domain *d, const char *dtpath)
     struct avc_audit_data ad;
     u32 dperm = flask_iommu_resource_use_perm();
 
+    if ( !d )
+        return flask_test_assign_dtdevice(dtpath);
+
     rc = current_has_perm(d, SECCLASS_RESOURCE, RESOURCE__ADD);
     if ( rc )
         return rc;
@@ -1812,13 +1818,11 @@ static struct xsm_operations flask_ops = {
 
 #if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_PCI)
     .get_device_group = flask_get_device_group,
-    .test_assign_device = flask_test_assign_device,
     .assign_device = flask_assign_device,
     .deassign_device = flask_deassign_device,
 #endif
 
 #if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_DEVICE_TREE)
-    .test_assign_dtdevice = flask_test_assign_dtdevice,
     .assign_dtdevice = flask_assign_dtdevice,
     .deassign_dtdevice = flask_deassign_dtdevice,
 #endif