Skip to content

Commit 3f4ff56

Browse files
larsclausenbroonie
authored andcommitted
regmap: rbtree: Make cache_present bitmap per node
With devices which have a dense and small register map but placed at a large offset the global cache_present bitmap imposes a huge memory overhead. Making the cache_present per rbtree node avoids the issue and easily reduces the memory footprint by a factor of ten. For devices with a more sparse map or without a large base register offset the memory usage might increase slightly by a few bytes, but not significantly. E.g. for a device which has ~50 registers at offset 0x4000 the memory footprint of the register cache goes down form 2496 bytes to 175 bytes. Moving the bitmap to a per node basis means that the handling of the bitmap is now cache implementation specific and can no longer be managed by the core. The regcache_sync_block() function is extended by a additional parameter so that the cache implementation can tell the core which registers in the block are set and which are not. The parameter is optional and if NULL the core assumes that all registers are set. The rbtree cache also needs to implement its own drop callback instead of relying on the core to handle this. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Signed-off-by: Mark Brown <broonie@linaro.org>
1 parent 472fdec commit 3f4ff56

File tree

3 files changed

+92
-80
lines changed

3 files changed

+92
-80
lines changed

drivers/base/regmap/internal.h

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,6 @@ struct regmap {
128128
void *cache;
129129
u32 cache_dirty;
130130

131-
unsigned long *cache_present;
132-
unsigned int cache_present_nbits;
133-
134131
struct reg_default *patch;
135132
int patch_regs;
136133

@@ -203,6 +200,7 @@ int regcache_write(struct regmap *map,
203200
unsigned int reg, unsigned int value);
204201
int regcache_sync(struct regmap *map);
205202
int regcache_sync_block(struct regmap *map, void *block,
203+
unsigned long *cache_present,
206204
unsigned int block_base, unsigned int start,
207205
unsigned int end);
208206

@@ -218,16 +216,6 @@ unsigned int regcache_get_val(struct regmap *map, const void *base,
218216
bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,
219217
unsigned int val);
220218
int regcache_lookup_reg(struct regmap *map, unsigned int reg);
221-
int regcache_set_reg_present(struct regmap *map, unsigned int reg);
222-
223-
static inline bool regcache_reg_present(struct regmap *map, unsigned int reg)
224-
{
225-
if (!map->cache_present)
226-
return false;
227-
if (reg > map->cache_present_nbits)
228-
return false;
229-
return map->cache_present[BIT_WORD(reg)] & BIT_MASK(reg);
230-
}
231219

232220
int _regmap_raw_write(struct regmap *map, unsigned int reg,
233221
const void *val, size_t val_len, bool async);

drivers/base/regmap/regcache-rbtree.c

Lines changed: 72 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ struct regcache_rbtree_node {
2929
unsigned int base_reg;
3030
/* block of adjacent registers */
3131
void *block;
32+
/* Which registers are present */
33+
long *cache_present;
3234
/* number of registers available in the block */
3335
unsigned int blklen;
3436
} __attribute__ ((packed));
@@ -57,6 +59,7 @@ static void regcache_rbtree_set_register(struct regmap *map,
5759
struct regcache_rbtree_node *rbnode,
5860
unsigned int idx, unsigned int val)
5961
{
62+
set_bit(idx, rbnode->cache_present);
6063
regcache_set_val(map, rbnode->block, idx, val);
6164
}
6265

@@ -146,13 +149,13 @@ static int rbtree_show(struct seq_file *s, void *ignored)
146149
map->lock(map->lock_arg);
147150

148151
mem_size = sizeof(*rbtree_ctx);
149-
mem_size += BITS_TO_LONGS(map->cache_present_nbits) * sizeof(long);
150152

151153
for (node = rb_first(&rbtree_ctx->root); node != NULL;
152154
node = rb_next(node)) {
153155
n = container_of(node, struct regcache_rbtree_node, node);
154156
mem_size += sizeof(*n);
155157
mem_size += (n->blklen * map->cache_word_size);
158+
mem_size += BITS_TO_LONGS(n->blklen) * sizeof(long);
156159

157160
regcache_rbtree_get_base_top_reg(map, n, &base, &top);
158161
this_registers = ((top - base) / map->reg_stride) + 1;
@@ -245,6 +248,7 @@ static int regcache_rbtree_exit(struct regmap *map)
245248
rbtree_node = rb_entry(next, struct regcache_rbtree_node, node);
246249
next = rb_next(&rbtree_node->node);
247250
rb_erase(&rbtree_node->node, &rbtree_ctx->root);
251+
kfree(rbtree_node->cache_present);
248252
kfree(rbtree_node->block);
249253
kfree(rbtree_node);
250254
}
@@ -265,7 +269,7 @@ static int regcache_rbtree_read(struct regmap *map,
265269
rbnode = regcache_rbtree_lookup(map, reg);
266270
if (rbnode) {
267271
reg_tmp = (reg - rbnode->base_reg) / map->reg_stride;
268-
if (!regcache_reg_present(map, reg))
272+
if (!test_bit(reg_tmp, rbnode->cache_present))
269273
return -ENOENT;
270274
*value = regcache_rbtree_get_register(map, rbnode, reg_tmp);
271275
} else {
@@ -285,6 +289,7 @@ static int regcache_rbtree_insert_to_block(struct regmap *map,
285289
{
286290
unsigned int blklen;
287291
unsigned int pos, offset;
292+
unsigned long *present;
288293
u8 *blk;
289294

290295
blklen = (top_reg - base_reg) / map->reg_stride + 1;
@@ -297,15 +302,25 @@ static int regcache_rbtree_insert_to_block(struct regmap *map,
297302
if (!blk)
298303
return -ENOMEM;
299304

305+
present = krealloc(rbnode->cache_present,
306+
BITS_TO_LONGS(blklen) * sizeof(*present), GFP_KERNEL);
307+
if (!present) {
308+
kfree(blk);
309+
return -ENOMEM;
310+
}
311+
300312
/* insert the register value in the correct place in the rbnode block */
301-
if (pos == 0)
313+
if (pos == 0) {
302314
memmove(blk + offset * map->cache_word_size,
303315
blk, rbnode->blklen * map->cache_word_size);
316+
bitmap_shift_right(present, present, offset, blklen);
317+
}
304318

305319
/* update the rbnode block, its size and the base register */
306320
rbnode->block = blk;
307321
rbnode->blklen = blklen;
308322
rbnode->base_reg = base_reg;
323+
rbnode->cache_present = present;
309324

310325
regcache_rbtree_set_register(map, rbnode, pos, value);
311326
return 0;
@@ -345,12 +360,21 @@ regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg)
345360

346361
rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size,
347362
GFP_KERNEL);
348-
if (!rbnode->block) {
349-
kfree(rbnode);
350-
return NULL;
351-
}
363+
if (!rbnode->block)
364+
goto err_free;
365+
366+
rbnode->cache_present = kzalloc(BITS_TO_LONGS(rbnode->blklen) *
367+
sizeof(*rbnode->cache_present), GFP_KERNEL);
368+
if (!rbnode->cache_present)
369+
goto err_free_block;
352370

353371
return rbnode;
372+
373+
err_free_block:
374+
kfree(rbnode->block);
375+
err_free:
376+
kfree(rbnode);
377+
return NULL;
354378
}
355379

356380
static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
@@ -363,10 +387,6 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
363387
int ret;
364388

365389
rbtree_ctx = map->cache;
366-
/* update the reg_present bitmap, make space if necessary */
367-
ret = regcache_set_reg_present(map, reg);
368-
if (ret < 0)
369-
return ret;
370390

371391
/* if we can't locate it in the cached rbnode we'll have
372392
* to traverse the rbtree looking for it.
@@ -461,21 +481,59 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min,
461481
else
462482
end = rbnode->blklen;
463483

464-
ret = regcache_sync_block(map, rbnode->block, rbnode->base_reg,
465-
start, end);
484+
ret = regcache_sync_block(map, rbnode->block,
485+
rbnode->cache_present,
486+
rbnode->base_reg, start, end);
466487
if (ret != 0)
467488
return ret;
468489
}
469490

470491
return regmap_async_complete(map);
471492
}
472493

494+
static int regcache_rbtree_drop(struct regmap *map, unsigned int min,
495+
unsigned int max)
496+
{
497+
struct regcache_rbtree_ctx *rbtree_ctx;
498+
struct regcache_rbtree_node *rbnode;
499+
struct rb_node *node;
500+
unsigned int base_reg, top_reg;
501+
unsigned int start, end;
502+
503+
rbtree_ctx = map->cache;
504+
for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) {
505+
rbnode = rb_entry(node, struct regcache_rbtree_node, node);
506+
507+
regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
508+
&top_reg);
509+
if (base_reg > max)
510+
break;
511+
if (top_reg < min)
512+
continue;
513+
514+
if (min > base_reg)
515+
start = (min - base_reg) / map->reg_stride;
516+
else
517+
start = 0;
518+
519+
if (max < top_reg)
520+
end = (max - base_reg) / map->reg_stride + 1;
521+
else
522+
end = rbnode->blklen;
523+
524+
bitmap_clear(rbnode->cache_present, start, end - start);
525+
}
526+
527+
return 0;
528+
}
529+
473530
struct regcache_ops regcache_rbtree_ops = {
474531
.type = REGCACHE_RBTREE,
475532
.name = "rbtree",
476533
.init = regcache_rbtree_init,
477534
.exit = regcache_rbtree_exit,
478535
.read = regcache_rbtree_read,
479536
.write = regcache_rbtree_write,
480-
.sync = regcache_rbtree_sync
537+
.sync = regcache_rbtree_sync,
538+
.drop = regcache_rbtree_drop,
481539
};

drivers/base/regmap/regcache.c

Lines changed: 19 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,6 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
121121
map->reg_defaults_raw = config->reg_defaults_raw;
122122
map->cache_word_size = DIV_ROUND_UP(config->val_bits, 8);
123123
map->cache_size_raw = map->cache_word_size * config->num_reg_defaults_raw;
124-
map->cache_present = NULL;
125-
map->cache_present_nbits = 0;
126124

127125
map->cache = NULL;
128126
map->cache_ops = cache_types[i];
@@ -181,7 +179,6 @@ void regcache_exit(struct regmap *map)
181179

182180
BUG_ON(!map->cache_ops);
183181

184-
kfree(map->cache_present);
185182
kfree(map->reg_defaults);
186183
if (map->cache_free)
187184
kfree(map->reg_defaults_raw);
@@ -407,22 +404,16 @@ EXPORT_SYMBOL_GPL(regcache_sync_region);
407404
int regcache_drop_region(struct regmap *map, unsigned int min,
408405
unsigned int max)
409406
{
410-
unsigned int reg;
411407
int ret = 0;
412408

413-
if (!map->cache_present && !(map->cache_ops && map->cache_ops->drop))
409+
if (!map->cache_ops || !map->cache_ops->drop)
414410
return -EINVAL;
415411

416412
map->lock(map->lock_arg);
417413

418414
trace_regcache_drop_region(map->dev, min, max);
419415

420-
if (map->cache_present)
421-
for (reg = min; reg < max + 1; reg++)
422-
clear_bit(reg, map->cache_present);
423-
424-
if (map->cache_ops && map->cache_ops->drop)
425-
ret = map->cache_ops->drop(map, min, max);
416+
ret = map->cache_ops->drop(map, min, max);
426417

427418
map->unlock(map->lock_arg);
428419

@@ -490,42 +481,6 @@ void regcache_cache_bypass(struct regmap *map, bool enable)
490481
}
491482
EXPORT_SYMBOL_GPL(regcache_cache_bypass);
492483

493-
int regcache_set_reg_present(struct regmap *map, unsigned int reg)
494-
{
495-
unsigned long *cache_present;
496-
unsigned int cache_present_size;
497-
unsigned int nregs;
498-
int i;
499-
500-
nregs = reg + 1;
501-
cache_present_size = BITS_TO_LONGS(nregs);
502-
cache_present_size *= sizeof(long);
503-
504-
if (!map->cache_present) {
505-
cache_present = kmalloc(cache_present_size, GFP_KERNEL);
506-
if (!cache_present)
507-
return -ENOMEM;
508-
bitmap_zero(cache_present, nregs);
509-
map->cache_present = cache_present;
510-
map->cache_present_nbits = nregs;
511-
}
512-
513-
if (nregs > map->cache_present_nbits) {
514-
cache_present = krealloc(map->cache_present,
515-
cache_present_size, GFP_KERNEL);
516-
if (!cache_present)
517-
return -ENOMEM;
518-
for (i = 0; i < nregs; i++)
519-
if (i >= map->cache_present_nbits)
520-
clear_bit(i, cache_present);
521-
map->cache_present = cache_present;
522-
map->cache_present_nbits = nregs;
523-
}
524-
525-
set_bit(reg, map->cache_present);
526-
return 0;
527-
}
528-
529484
bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,
530485
unsigned int val)
531486
{
@@ -617,7 +572,16 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg)
617572
return -ENOENT;
618573
}
619574

575+
static bool regcache_reg_present(unsigned long *cache_present, unsigned int idx)
576+
{
577+
if (!cache_present)
578+
return true;
579+
580+
return test_bit(idx, cache_present);
581+
}
582+
620583
static int regcache_sync_block_single(struct regmap *map, void *block,
584+
unsigned long *cache_present,
621585
unsigned int block_base,
622586
unsigned int start, unsigned int end)
623587
{
@@ -627,7 +591,7 @@ static int regcache_sync_block_single(struct regmap *map, void *block,
627591
for (i = start; i < end; i++) {
628592
regtmp = block_base + (i * map->reg_stride);
629593

630-
if (!regcache_reg_present(map, regtmp))
594+
if (!regcache_reg_present(cache_present, i))
631595
continue;
632596

633597
val = regcache_get_val(map, block, i);
@@ -678,6 +642,7 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data,
678642
}
679643

680644
static int regcache_sync_block_raw(struct regmap *map, void *block,
645+
unsigned long *cache_present,
681646
unsigned int block_base, unsigned int start,
682647
unsigned int end)
683648
{
@@ -690,7 +655,7 @@ static int regcache_sync_block_raw(struct regmap *map, void *block,
690655
for (i = start; i < end; i++) {
691656
regtmp = block_base + (i * map->reg_stride);
692657

693-
if (!regcache_reg_present(map, regtmp)) {
658+
if (!regcache_reg_present(cache_present, i)) {
694659
ret = regcache_sync_block_raw_flush(map, &data,
695660
base, regtmp);
696661
if (ret != 0)
@@ -721,13 +686,14 @@ static int regcache_sync_block_raw(struct regmap *map, void *block,
721686
}
722687

723688
int regcache_sync_block(struct regmap *map, void *block,
689+
unsigned long *cache_present,
724690
unsigned int block_base, unsigned int start,
725691
unsigned int end)
726692
{
727693
if (regmap_can_raw_write(map))
728-
return regcache_sync_block_raw(map, block, block_base,
729-
start, end);
694+
return regcache_sync_block_raw(map, block, cache_present,
695+
block_base, start, end);
730696
else
731-
return regcache_sync_block_single(map, block, block_base,
732-
start, end);
697+
return regcache_sync_block_single(map, block, cache_present,
698+
block_base, start, end);
733699
}

0 commit comments

Comments
 (0)