IOMMUs currently have no common representation to userspace, most
seem to have no representation at all aside from a few printks
on bootup.  There are, however, features of IOMMUs that are useful
to know about.  For instance, the IOMMU might support superpages,
making use of processor large/huge pages more important in a device
assignment scenario.  It's also useful to create cross links between
devices and IOMMU hardware units, so that users might be able to
load balance their devices to avoid thrashing a single hardware unit.

This patch adds a registration and de-registration interface as well
as device linking, making it very lightweight for an IOMMU driver to
add basic support.  IOMMU drivers can provide additional attributes
automatically by using an attribute_group.

The attributes exposed are expected to be relatively device specific,
the means to retrieve them certainly are.  So there are currently
no common attributes for the new iommu_class created here.

Signed-off-by: Alex Williamson <[email protected]>
---
 drivers/iommu/Makefile      |    1 
 drivers/iommu/iommu-sysfs.c |  147 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/iommu.h       |   28 ++++++++
 3 files changed, 176 insertions(+)
 create mode 100644 drivers/iommu/iommu-sysfs.c

diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 5d58bf1..437c231 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -1,5 +1,6 @@
 obj-$(CONFIG_IOMMU_API) += iommu.o
 obj-$(CONFIG_IOMMU_API) += iommu-traces.o
+obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o
 obj-$(CONFIG_OF_IOMMU) += of_iommu.o
 obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
 obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
diff --git a/drivers/iommu/iommu-sysfs.c b/drivers/iommu/iommu-sysfs.c
new file mode 100644
index 0000000..123f20c
--- /dev/null
+++ b/drivers/iommu/iommu-sysfs.c
@@ -0,0 +1,147 @@
+#include <linux/device.h>
+#include <linux/iommu.h>
+#include <linux/module.h>
+
+struct iommu_device {
+       struct device dev;
+       struct kobject *devices;
+};
+
+#define to_iommu_device(dev) container_of(dev, struct iommu_device, dev)
+
+static void iommu_release_device(struct device *dev)
+{
+       struct iommu_device *iommu = to_iommu_device(dev);
+       kobject_put(iommu->devices);
+       kfree(iommu);
+}
+
+static struct class iommu_class = {
+       .name = "iommu",
+       .dev_release = iommu_release_device,
+};
+
+static int __init iommu_dev_init(void)
+{
+       return class_register(&iommu_class);
+}
+postcore_initcall(iommu_dev_init);
+
+static int __match_platform_data(struct device *dev, const void *platform_data)
+{
+       return dev->platform_data == platform_data;
+}
+
+int iommu_register_device(struct device *parent, const char *name,
+                         const struct attribute_group **groups,
+                         void *platform_data)
+{
+       struct device *dev;
+       struct iommu_device *iommu;
+       int ret;
+
+       dev = class_find_device(&iommu_class, NULL,
+                               platform_data, __match_platform_data);
+       if (dev) {
+               put_device(dev);
+               return -EEXIST;
+       }
+
+       iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
+       if (!iommu)
+               return -ENOMEM;
+
+       device_initialize(&iommu->dev);
+
+       iommu->dev.class = &iommu_class;
+       iommu->dev.parent = parent;
+       dev_set_name(&iommu->dev, "%s", name);
+       iommu->dev.groups = groups;
+       iommu->dev.platform_data = platform_data;
+
+       ret = device_add(&iommu->dev);
+       if (ret)
+               goto error;
+
+       iommu->devices = kobject_create_and_add("devices", &iommu->dev.kobj);
+       if (!iommu->devices) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       /* devices holds ref */
+       put_device(&iommu->dev);
+
+       return 0;
+
+error:
+       pr_warn("Unable to register iommu device %s\n", name);
+       put_device(&iommu->dev);
+
+       return ret;
+}
+
+void iommu_unregister_device(void *platform_data)
+{
+       struct device *dev;
+       struct iommu_device *iommu;
+
+       dev = class_find_device(&iommu_class, NULL,
+                               platform_data, __match_platform_data);
+       if (!dev)
+               return;
+
+       iommu = to_iommu_device(dev);
+       /* class_find_device gives us a new ref to put in device_unregister */
+       device_unregister(&iommu->dev);
+       kobject_put(iommu->devices);
+}
+
+int iommu_device_link(const void *platform_data, struct device *link)
+{
+       struct device *dev;
+       struct iommu_device *iommu;
+       int ret;
+
+       dev = class_find_device(&iommu_class, NULL,
+                               platform_data, __match_platform_data);
+       if (!dev)
+               return -ENODEV;
+
+       iommu = to_iommu_device(dev);
+
+       ret = sysfs_create_link(&link->kobj, &dev->kobj, "iommu");
+       if (ret)
+               goto error;
+
+       ret = sysfs_create_link_nowarn(iommu->devices,
+                                      &link->kobj, dev_name(link));
+       if (ret) {
+               sysfs_remove_link(&link->kobj, "iommu");
+               goto error;
+       }
+
+       kobject_get(iommu->devices);
+error:
+       put_device(dev);
+       return ret;
+}
+
+void iommu_device_unlink(const void *platform_data, struct device *link)
+{
+       struct device *dev;
+       struct iommu_device *iommu;
+
+       dev = class_find_device(&iommu_class, NULL,
+                               platform_data, __match_platform_data);
+       if (!dev)
+               return;
+
+       iommu = to_iommu_device(dev);
+
+       sysfs_remove_link(iommu->devices, dev_name(link));
+       sysfs_remove_link(&link->kobj, "iommu");
+
+       kobject_put(iommu->devices);
+       put_device(dev);
+}
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index b96a5b2..e9bd3a0 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -186,6 +186,12 @@ extern int iommu_domain_get_attr(struct iommu_domain 
*domain, enum iommu_attr,
                                 void *data);
 extern int iommu_domain_set_attr(struct iommu_domain *domain, enum iommu_attr,
                                 void *data);
+extern int iommu_register_device(struct device *parent, const char *name,
+                                const struct attribute_group **groups,
+                                void *platform_data);
+extern void iommu_unregister_device(void *platform_data);
+extern int iommu_device_link(const void *platform_data, struct device *link);
+extern void iommu_device_unlink(const void *platform_data, struct device 
*link);
 
 /* Window handling function prototypes */
 extern int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr,
@@ -396,6 +402,28 @@ static inline int iommu_domain_set_attr(struct 
iommu_domain *domain,
        return -EINVAL;
 }
 
+static inline int iommu_register_device(struct device *parent, const char 
*name,
+                                       const struct attribute_group **groups,
+                                       void *platform_data)
+{
+       return 0;
+}
+
+static inline void iommu_unregister_device(void *platform_data)
+{
+}
+
+static inline int iommu_device_link(const void *platform_data,
+                                   struct device *link)
+{
+       return 0;
+}
+
+static inline void iommu_device_unlink(const void *platform_data,
+                                      struct device *link)
+{
+}
+
 #endif /* CONFIG_IOMMU_API */
 
 #endif /* __LINUX_IOMMU_H */

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to