Commit 5166b7c006eeb4f6becc0822974d8da259484ba1

Authored by Mark Brown
1 parent afab2f7b21

regmap: debugfs: Cache offsets of valid regions for dump

Avoid doing a linear scan of the entire register map for each read() of
the debugfs register dump by recording the offsets where valid registers
exist when we first read the registers file. This assumes the set of
valid registers never changes, if this is not the case invalidation of
the cache will be required.

This could be further improved for large blocks of contiguous registers
by calculating the register we will read from within the block - currently
we do a linear scan of the block. An rbtree may also be worthwhile.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

Showing 2 changed files with 63 additions and 14 deletions Side-by-side Diff

drivers/base/regmap/internal.h
... ... @@ -15,10 +15,18 @@
15 15  
16 16 #include <linux/regmap.h>
17 17 #include <linux/fs.h>
  18 +#include <linux/list.h>
18 19  
19 20 struct regmap;
20 21 struct regcache_ops;
21 22  
  23 +struct regmap_debugfs_off_cache {
  24 + struct list_head list;
  25 + off_t min;
  26 + off_t max;
  27 + unsigned int base_reg;
  28 +};
  29 +
22 30 struct regmap_format {
23 31 size_t buf_size;
24 32 size_t reg_bytes;
... ... @@ -54,6 +62,8 @@
54 62 unsigned int debugfs_reg_len;
55 63 unsigned int debugfs_val_len;
56 64 unsigned int debugfs_tot_len;
  65 +
  66 + struct list_head debugfs_off_cache;
57 67 #endif
58 68  
59 69 unsigned int max_register;
drivers/base/regmap/regmap-debugfs.c
... ... @@ -65,25 +65,53 @@
65 65 loff_t from,
66 66 loff_t *pos)
67 67 {
68   - loff_t p = *pos;
69   - unsigned int i;
  68 + struct regmap_debugfs_off_cache *c = NULL;
  69 + loff_t p = 0;
  70 + unsigned int i, ret;
70 71  
71   - for (i = base; i <= map->max_register; i += map->reg_stride) {
72   - if (!regmap_readable(map, i))
73   - continue;
  72 + /*
  73 + * If we don't have a cache build one so we don't have to do a
  74 + * linear scan each time.
  75 + */
  76 + if (list_empty(&map->debugfs_off_cache)) {
  77 + for (i = base; i <= map->max_register; i += map->reg_stride) {
  78 + /* Skip unprinted registers, closing off cache entry */
  79 + if (!regmap_readable(map, i) ||
  80 + regmap_precious(map, i)) {
  81 + if (c) {
  82 + c->max = p - 1;
  83 + list_add_tail(&c->list,
  84 + &map->debugfs_off_cache);
  85 + c = NULL;
  86 + }
74 87  
75   - if (regmap_precious(map, i))
76   - continue;
  88 + continue;
  89 + }
77 90  
78   - if (i >= from) {
79   - *pos = p;
80   - return i;
  91 + /* No cache entry? Start a new one */
  92 + if (!c) {
  93 + c = kzalloc(sizeof(*c), GFP_KERNEL);
  94 + if (!c)
  95 + break;
  96 + c->min = p;
  97 + c->base_reg = i;
  98 + }
  99 +
  100 + p += map->debugfs_tot_len;
81 101 }
  102 + }
82 103  
83   - p += map->debugfs_tot_len;
  104 + /* Find the relevant block */
  105 + list_for_each_entry(c, &map->debugfs_off_cache, list) {
  106 + if (*pos >= c->min && *pos <= c->max) {
  107 + *pos = c->min;
  108 + return c->base_reg;
  109 + }
  110 +
  111 + ret = c->max;
84 112 }
85 113  
86   - return base;
  114 + return ret;
87 115 }
88 116  
89 117 static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from,
... ... @@ -309,6 +337,8 @@
309 337 struct rb_node *next;
310 338 struct regmap_range_node *range_node;
311 339  
  340 + INIT_LIST_HEAD(&map->debugfs_off_cache);
  341 +
312 342 if (name) {
313 343 map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s",
314 344 dev_name(map->dev), name);
315 345  
... ... @@ -357,7 +387,16 @@
357 387  
358 388 void regmap_debugfs_exit(struct regmap *map)
359 389 {
  390 + struct regmap_debugfs_off_cache *c;
  391 +
360 392 debugfs_remove_recursive(map->debugfs);
  393 + while (!list_empty(&map->debugfs_off_cache)) {
  394 + c = list_first_entry(&map->debugfs_off_cache,
  395 + struct regmap_debugfs_off_cache,
  396 + list);
  397 + list_del(&c->list);
  398 + kfree(c);
  399 + }
361 400 kfree(map->debugfs_name);
362 401 }
363 402