regmap: debugfs: Fix a boot time crash with early regmap init
authorTero Kristo <t-kristo@ti.com>
Thu, 24 Oct 2013 12:03:41 +0000 (15:03 +0300)
committerMark Brown <broonie@linaro.org>
Thu, 24 Oct 2013 12:50:58 +0000 (13:50 +0100)
If called early enough, regmap_debugfs_init causes a crash, if the
fs subsystem does not have its mount cache created yet. Even if this
would work, the root node for the regmap debugfs is still missing,
thus postpone the regmap_debugfs_init in this case until the root
node is created. A special regmap_debugfs_early list is created for
this purpose which is parsed later in the boot.

Signed-off-by: Tero Kristo <t-kristo@ti.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
drivers/base/regmap/regmap-debugfs.c

index de11eca..c5471cd 100644 (file)
 #include <linux/debugfs.h>
 #include <linux/uaccess.h>
 #include <linux/device.h>
+#include <linux/list.h>
 
 #include "internal.h"
 
+struct regmap_debugfs_node {
+       struct regmap *map;
+       const char *name;
+       struct list_head link;
+};
+
 static struct dentry *regmap_debugfs_root;
+static LIST_HEAD(regmap_debugfs_early_list);
+static DEFINE_MUTEX(regmap_debugfs_early_lock);
 
 /* Calculate the length of a fixed format  */
 static size_t regmap_calc_reg_len(int max_val, char *buf, size_t buf_size)
@@ -465,6 +474,20 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
        struct rb_node *next;
        struct regmap_range_node *range_node;
 
+       /* If we don't have the debugfs root yet, postpone init */
+       if (!regmap_debugfs_root) {
+               struct regmap_debugfs_node *node;
+               node = kzalloc(sizeof(*node), GFP_KERNEL);
+               if (!node)
+                       return;
+               node->map = map;
+               node->name = name;
+               mutex_lock(&regmap_debugfs_early_lock);
+               list_add(&node->link, &regmap_debugfs_early_list);
+               mutex_unlock(&regmap_debugfs_early_lock);
+               return;
+       }
+
        INIT_LIST_HEAD(&map->debugfs_off_cache);
        mutex_init(&map->cache_lock);
 
@@ -519,18 +542,42 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
 
 void regmap_debugfs_exit(struct regmap *map)
 {
-       debugfs_remove_recursive(map->debugfs);
-       mutex_lock(&map->cache_lock);
-       regmap_debugfs_free_dump_cache(map);
-       mutex_unlock(&map->cache_lock);
-       kfree(map->debugfs_name);
+       if (map->debugfs) {
+               debugfs_remove_recursive(map->debugfs);
+               mutex_lock(&map->cache_lock);
+               regmap_debugfs_free_dump_cache(map);
+               mutex_unlock(&map->cache_lock);
+               kfree(map->debugfs_name);
+       } else {
+               struct regmap_debugfs_node *node, *tmp;
+
+               mutex_lock(&regmap_debugfs_early_lock);
+               list_for_each_entry_safe(node, tmp, &regmap_debugfs_early_list,
+                                        link) {
+                       if (node->map == map) {
+                               list_del(&node->link);
+                               kfree(node);
+                       }
+               }
+               mutex_unlock(&regmap_debugfs_early_lock);
+       }
 }
 
 void regmap_debugfs_initcall(void)
 {
+       struct regmap_debugfs_node *node, *tmp;
+
        regmap_debugfs_root = debugfs_create_dir("regmap", NULL);
        if (!regmap_debugfs_root) {
                pr_warn("regmap: Failed to create debugfs root\n");
                return;
        }
+
+       mutex_lock(&regmap_debugfs_early_lock);
+       list_for_each_entry_safe(node, tmp, &regmap_debugfs_early_list, link) {
+               regmap_debugfs_init(node->map, node->name);
+               list_del(&node->link);
+               kfree(node);
+       }
+       mutex_unlock(&regmap_debugfs_early_lock);
 }