[RFC PATCH 1/2] iommu: Add sysfs support for IOMMUs
From: Alex Williamson
Date: Wed May 07 2014 - 19:17:57 EST
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 <alex.williamson@xxxxxxxxxx>
---
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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/