]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
iommu: Add iommu_device_group callback and iommu_group sysfs entry
authorAlex Williamson <alex.williamson@redhat.com>
Fri, 21 Oct 2011 19:56:05 +0000 (15:56 -0400)
committerJoerg Roedel <joerg.roedel@amd.com>
Tue, 15 Nov 2011 11:22:23 +0000 (12:22 +0100)
An IOMMU group is a set of devices for which the IOMMU cannot
distinguish transactions.  For PCI devices, a group often occurs
when a PCI bridge is involved.  Transactions from any device
behind the bridge appear to be sourced from the bridge itself.
We leave it to the IOMMU driver to define the grouping restraints
for their platform.

Using this new interface, the group for a device can be retrieved
using the iommu_device_group() callback.  Users will compare the
value returned against the value returned for other devices to
determine whether they are part of the same group.  Devices with
no group are not translated by the IOMMU.  There should be no
expectations about the group numbers as they may be arbitrarily
assigned by the IOMMU driver and may not be persistent across boots.

We also provide a sysfs interface to the group numbers here so
that userspace can understand IOMMU dependencies between devices
for managing safe, userspace drivers.

[Some code changes by Joerg Roedel <joerg.roedel@amd.com>]

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
drivers/iommu/iommu.c
include/linux/iommu.h

index 2fb2963df55376a3a8efbf09490457e08b28b836..9c35be4b333f1904f6f43a51da2df7dfaa17cb84 100644 (file)
 #include <linux/errno.h>
 #include <linux/iommu.h>
 
+static ssize_t show_iommu_group(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       unsigned int groupid;
+
+       if (iommu_device_group(dev, &groupid))
+               return 0;
+
+       return sprintf(buf, "%u", groupid);
+}
+static DEVICE_ATTR(iommu_group, S_IRUGO, show_iommu_group, NULL);
+
+static int add_iommu_group(struct device *dev, void *data)
+{
+       unsigned int groupid;
+
+       if (iommu_device_group(dev, &groupid) == 0)
+               return device_create_file(dev, &dev_attr_iommu_group);
+
+       return 0;
+}
+
+static int remove_iommu_group(struct device *dev)
+{
+       unsigned int groupid;
+
+       if (iommu_device_group(dev, &groupid) == 0)
+               device_remove_file(dev, &dev_attr_iommu_group);
+
+       return 0;
+}
+
+static int iommu_device_notifier(struct notifier_block *nb,
+                                unsigned long action, void *data)
+{
+       struct device *dev = data;
+
+       if (action == BUS_NOTIFY_ADD_DEVICE)
+               return add_iommu_group(dev, NULL);
+       else if (action == BUS_NOTIFY_DEL_DEVICE)
+               return remove_iommu_group(dev);
+
+       return 0;
+}
+
+static struct notifier_block iommu_device_nb = {
+       .notifier_call = iommu_device_notifier,
+};
+
 static void iommu_bus_init(struct bus_type *bus, struct iommu_ops *ops)
 {
+       bus_register_notifier(bus, &iommu_device_nb);
+       bus_for_each_dev(bus, NULL, NULL, add_iommu_group);
 }
 
 /**
@@ -186,3 +237,12 @@ int iommu_unmap(struct iommu_domain *domain, unsigned long iova, int gfp_order)
        return domain->ops->unmap(domain, iova, gfp_order);
 }
 EXPORT_SYMBOL_GPL(iommu_unmap);
+
+int iommu_device_group(struct device *dev, unsigned int *groupid)
+{
+       if (iommu_present(dev->bus) && dev->bus->iommu_ops->device_group)
+               return dev->bus->iommu_ops->device_group(dev, groupid);
+
+       return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(iommu_device_group);
index 432acc4c054df1134dddb9be501de5c27d84d926..93617e7779a1057b20e56861a14bd33091919d76 100644 (file)
@@ -61,6 +61,7 @@ struct iommu_ops {
                                    unsigned long iova);
        int (*domain_has_cap)(struct iommu_domain *domain,
                              unsigned long cap);
+       int (*device_group)(struct device *dev, unsigned int *groupid);
 };
 
 extern int bus_set_iommu(struct bus_type *bus, struct iommu_ops *ops);
@@ -81,6 +82,7 @@ extern int iommu_domain_has_cap(struct iommu_domain *domain,
                                unsigned long cap);
 extern void iommu_set_fault_handler(struct iommu_domain *domain,
                                        iommu_fault_handler_t handler);
+extern int iommu_device_group(struct device *dev, unsigned int *groupid);
 
 /**
  * report_iommu_fault() - report about an IOMMU fault to the IOMMU framework
@@ -179,6 +181,11 @@ static inline void iommu_set_fault_handler(struct iommu_domain *domain,
 {
 }
 
+static inline int iommu_device_group(struct device *dev, unsigned int *groupid);
+{
+       return -ENODEV;
+}
+
 #endif /* CONFIG_IOMMU_API */
 
 #endif /* __LINUX_IOMMU_H */