iommu: iommu-debug: Rework CONFIG_IOMMU_DEBUG_TRACKING
If CONFIG_IOMMU_DEBUG_TRACKING=y, a deadlock will occur if userspace
writes to the debugfs file "attach":
-001|context_switch(inline)
-001|__schedule()
-002|__preempt_count_sub(inline)
-002|schedule()
-003|schedule_timeout()
-004|do_wait_for_common(inline)
-004|__wait_for_common(inline)
-004|wait_for_common()
-005|wait_for_completion()
-006|__synchronize_srcu()
-007|synchronize_srcu()
-008|debugfs_remove_recursive()
-009|iommu_debug_detach_device()
-010|__iommu_detach_device()
-011|__iommu_group_for_each_dev(inline)
-011|__iommu_detach_group()
-012|iommu_detach_device()
-013|iommu_debug_device_profiling()
-014|iommu_debug_device_show()
-015|seq_read()
-016|full_proxy_read()
-017|__vfs_read()
-018|vfs_read()
Since the files in the attachment directory are rarely used, remove
them. Update the data saved by this config option to work properly
with iommu groups.
Change-Id: I3f0f1d0836f37412915ce3e78d8aefe92dc8d096
Signed-off-by: Patrick Daly <pdaly@codeaurora.org>
diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c
index bea5f03..0c49a64 100644
--- a/drivers/iommu/iommu-debug.c
+++ b/drivers/iommu/iommu-debug.c
@@ -81,330 +81,58 @@
static DEFINE_MUTEX(iommu_debug_attachments_lock);
static LIST_HEAD(iommu_debug_attachments);
-static struct dentry *debugfs_attachments_dir;
+/*
+ * Each group may have more than one domain; but each domain may
+ * only have one group.
+ * Used by debug tools to display the name of the device(s) associated
+ * with a particular domain.
+ */
struct iommu_debug_attachment {
struct iommu_domain *domain;
- struct device *dev;
- struct dentry *dentry;
+ struct iommu_group *group;
struct list_head list;
- unsigned long reg_offset;
};
-static int iommu_debug_attachment_info_show(struct seq_file *s, void *ignored)
-{
- struct iommu_debug_attachment *attach = s->private;
- int secure_vmid;
-
- seq_printf(s, "Domain: 0x%p\n", attach->domain);
-
- seq_puts(s, "SECURE_VMID: ");
- if (iommu_domain_get_attr(attach->domain,
- DOMAIN_ATTR_SECURE_VMID,
- &secure_vmid))
- seq_puts(s, "(Unknown)\n");
- else
- seq_printf(s, "%s (0x%x)\n",
- msm_secure_vmid_to_string(secure_vmid), secure_vmid);
-
- return 0;
-}
-
-static int iommu_debug_attachment_info_open(struct inode *inode,
- struct file *file)
-{
- return single_open(file, iommu_debug_attachment_info_show,
- inode->i_private);
-}
-
-static const struct file_operations iommu_debug_attachment_info_fops = {
- .open = iommu_debug_attachment_info_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static ssize_t iommu_debug_attachment_reg_offset_write(
- struct file *file, const char __user *ubuf, size_t count,
- loff_t *offset)
-{
- struct iommu_debug_attachment *attach = file->private_data;
- unsigned long reg_offset;
-
- if (kstrtoul_from_user(ubuf, count, 0, ®_offset)) {
- pr_err("Invalid reg_offset format\n");
- return -EFAULT;
- }
-
- attach->reg_offset = reg_offset;
-
- return count;
-}
-
-static const struct file_operations iommu_debug_attachment_reg_offset_fops = {
- .open = simple_open,
- .write = iommu_debug_attachment_reg_offset_write,
-};
-
-static ssize_t iommu_debug_attachment_reg_read_read(
- struct file *file, char __user *ubuf, size_t count, loff_t *offset)
-{
- struct iommu_debug_attachment *attach = file->private_data;
- unsigned long val;
- char *val_str;
- ssize_t val_str_len;
-
- if (*offset)
- return 0;
-
- val = iommu_reg_read(attach->domain, attach->reg_offset);
- val_str = kasprintf(GFP_KERNEL, "0x%lx\n", val);
- if (!val_str)
- return -ENOMEM;
- val_str_len = strlen(val_str);
-
- if (copy_to_user(ubuf, val_str, val_str_len)) {
- pr_err("copy_to_user failed\n");
- val_str_len = -EFAULT;
- goto out;
- }
- *offset = 1; /* non-zero means we're done */
-
-out:
- kfree(val_str);
- return val_str_len;
-}
-
-static const struct file_operations iommu_debug_attachment_reg_read_fops = {
- .open = simple_open,
- .read = iommu_debug_attachment_reg_read_read,
-};
-
-static ssize_t iommu_debug_attachment_reg_write_write(
- struct file *file, const char __user *ubuf, size_t count,
- loff_t *offset)
-{
- struct iommu_debug_attachment *attach = file->private_data;
- unsigned long val;
-
- if (kstrtoul_from_user(ubuf, count, 0, &val)) {
- pr_err("Invalid val format\n");
- return -EFAULT;
- }
-
- iommu_reg_write(attach->domain, attach->reg_offset, val);
-
- return count;
-}
-
-static const struct file_operations iommu_debug_attachment_reg_write_fops = {
- .open = simple_open,
- .write = iommu_debug_attachment_reg_write_write,
-};
-
-/* should be called with iommu_debug_attachments_lock locked */
-static int iommu_debug_attach_add_debugfs(
- struct iommu_debug_attachment *attach)
-{
- const char *attach_name;
- struct device *dev = attach->dev;
- struct iommu_domain *domain = attach->domain;
- int is_dynamic;
-
- if (iommu_domain_get_attr(domain, DOMAIN_ATTR_DYNAMIC, &is_dynamic))
- is_dynamic = 0;
-
- if (is_dynamic) {
- uuid_le uuid;
-
- uuid_le_gen(&uuid);
- attach_name = kasprintf(GFP_KERNEL, "%s-%pUl", dev_name(dev),
- uuid.b);
- if (!attach_name)
- return -ENOMEM;
- } else {
- attach_name = dev_name(dev);
- }
-
- attach->dentry = debugfs_create_dir(attach_name,
- debugfs_attachments_dir);
- if (!attach->dentry) {
- pr_err("Couldn't create iommu/attachments/%s debugfs directory for domain 0x%p\n",
- attach_name, domain);
- if (is_dynamic)
- kfree(attach_name);
- return -EIO;
- }
-
- if (is_dynamic)
- kfree(attach_name);
-
- if (!debugfs_create_file(
- "info", S_IRUSR, attach->dentry, attach,
- &iommu_debug_attachment_info_fops)) {
- pr_err("Couldn't create iommu/attachments/%s/info debugfs file for domain 0x%p\n",
- dev_name(dev), domain);
- goto err_rmdir;
- }
-
- if (!debugfs_create_file(
- "reg_offset", S_IRUSR, attach->dentry, attach,
- &iommu_debug_attachment_reg_offset_fops)) {
- pr_err("Couldn't create iommu/attachments/%s/reg_offset debugfs file for domain 0x%p\n",
- dev_name(dev), domain);
- goto err_rmdir;
- }
-
- if (!debugfs_create_file(
- "reg_read", S_IRUSR, attach->dentry, attach,
- &iommu_debug_attachment_reg_read_fops)) {
- pr_err("Couldn't create iommu/attachments/%s/reg_read debugfs file for domain 0x%p\n",
- dev_name(dev), domain);
- goto err_rmdir;
- }
-
- if (!debugfs_create_file(
- "reg_write", S_IRUSR, attach->dentry, attach,
- &iommu_debug_attachment_reg_write_fops)) {
- pr_err("Couldn't create iommu/attachments/%s/reg_write debugfs file for domain 0x%p\n",
- dev_name(dev), domain);
- goto err_rmdir;
- }
-
- return 0;
-
-err_rmdir:
- debugfs_remove_recursive(attach->dentry);
- return -EIO;
-}
-
-void iommu_debug_domain_add(struct iommu_domain *domain)
-{
- struct iommu_debug_attachment *attach;
-
- mutex_lock(&iommu_debug_attachments_lock);
-
- attach = kmalloc(sizeof(*attach), GFP_KERNEL);
- if (!attach)
- goto out_unlock;
-
- attach->domain = domain;
- attach->dev = NULL;
- list_add(&attach->list, &iommu_debug_attachments);
-
-out_unlock:
- mutex_unlock(&iommu_debug_attachments_lock);
-}
-
-void iommu_debug_domain_remove(struct iommu_domain *domain)
-{
- struct iommu_debug_attachment *it;
-
- mutex_lock(&iommu_debug_attachments_lock);
- list_for_each_entry(it, &iommu_debug_attachments, list)
- if (it->domain == domain && it->dev == NULL)
- break;
-
- if (&it->list == &iommu_debug_attachments) {
- WARN(1, "Couldn't find debug attachment for domain=0x%p",
- domain);
- } else {
- list_del(&it->list);
- kfree(it);
- }
- mutex_unlock(&iommu_debug_attachments_lock);
-}
-
void iommu_debug_attach_device(struct iommu_domain *domain,
struct device *dev)
{
struct iommu_debug_attachment *attach;
+ struct iommu_group *group;
+
+ group = iommu_group_get(dev);
+ if (!group)
+ return;
+
+ attach = kzalloc(sizeof(*attach), GFP_KERNEL);
+ if (!attach)
+ return;
+
+ attach->domain = domain;
+ attach->group = group;
+ INIT_LIST_HEAD(&attach->list);
mutex_lock(&iommu_debug_attachments_lock);
+ list_add(&attach->list, &iommu_debug_attachments);
+ mutex_unlock(&iommu_debug_attachments_lock);
+}
- list_for_each_entry(attach, &iommu_debug_attachments, list)
- if (attach->domain == domain && attach->dev == NULL)
- break;
+void iommu_debug_domain_remove(struct iommu_domain *domain)
+{
+ struct iommu_debug_attachment *it, *tmp;
- if (&attach->list == &iommu_debug_attachments) {
- WARN(1, "Couldn't find debug attachment for domain=0x%p dev=%s",
- domain, dev_name(dev));
- } else {
- attach->dev = dev;
-
- /*
- * we might not init until after other drivers start calling
- * iommu_attach_device. Only set up the debugfs nodes if we've
- * already init'd to avoid polluting the top-level debugfs
- * directory (by calling debugfs_create_dir with a NULL
- * parent). These will be flushed out later once we init.
- */
-
- if (debugfs_attachments_dir)
- iommu_debug_attach_add_debugfs(attach);
+ mutex_lock(&iommu_debug_attachments_lock);
+ list_for_each_entry_safe(it, tmp, &iommu_debug_attachments, list) {
+ if (it->domain != domain)
+ continue;
+ list_del(&it->list);
+ iommu_group_put(it->group);
+ kfree(it);
}
mutex_unlock(&iommu_debug_attachments_lock);
}
-void iommu_debug_detach_device(struct iommu_domain *domain,
- struct device *dev)
-{
- struct iommu_debug_attachment *it;
-
- mutex_lock(&iommu_debug_attachments_lock);
- list_for_each_entry(it, &iommu_debug_attachments, list)
- if (it->domain == domain && it->dev == dev)
- break;
-
- if (&it->list == &iommu_debug_attachments) {
- WARN(1, "Couldn't find debug attachment for domain=0x%p dev=%s",
- domain, dev_name(dev));
- } else {
- /*
- * Just remove debugfs entry and mark dev as NULL on
- * iommu_detach call. We would remove the actual
- * attachment entry from the list only on domain_free call.
- * This is to ensure we keep track of unattached domains too.
- */
-
- debugfs_remove_recursive(it->dentry);
- it->dev = NULL;
- }
- mutex_unlock(&iommu_debug_attachments_lock);
-}
-
-static int iommu_debug_init_tracking(void)
-{
- int ret = 0;
- struct iommu_debug_attachment *attach;
-
- mutex_lock(&iommu_debug_attachments_lock);
- debugfs_attachments_dir = debugfs_create_dir("attachments",
- iommu_debugfs_top);
- if (!debugfs_attachments_dir) {
- pr_err("Couldn't create iommu/attachments debugfs directory\n");
- ret = -ENODEV;
- goto out_unlock;
- }
-
- /* set up debugfs entries for attachments made during early boot */
- list_for_each_entry(attach, &iommu_debug_attachments, list)
- if (attach->dev)
- iommu_debug_attach_add_debugfs(attach);
-
-out_unlock:
- mutex_unlock(&iommu_debug_attachments_lock);
- return ret;
-}
-
-static void iommu_debug_destroy_tracking(void)
-{
- debugfs_remove_recursive(debugfs_attachments_dir);
-}
-#else
-static inline int iommu_debug_init_tracking(void) { return 0; }
-static inline void iommu_debug_destroy_tracking(void) { }
#endif
#ifdef CONFIG_IOMMU_TESTS
@@ -2045,9 +1773,6 @@
static int iommu_debug_init(void)
{
- if (iommu_debug_init_tracking())
- return -ENODEV;
-
if (iommu_debug_init_tests())
return -ENODEV;
@@ -2057,7 +1782,6 @@
static void iommu_debug_exit(void)
{
platform_driver_unregister(&iommu_debug_driver);
- iommu_debug_destroy_tracking();
iommu_debug_destroy_tests();
}