blkcache.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright (C) Nelson Integration, LLC 2016
  4. * Author: Eric Nelson<eric@nelint.com>
  5. *
  6. */
  7. #include <common.h>
  8. #include <blk.h>
  9. #include <log.h>
  10. #include <malloc.h>
  11. #include <part.h>
  12. #include <asm/global_data.h>
  13. #include <linux/ctype.h>
  14. #include <linux/list.h>
  15. #ifdef CONFIG_NEEDS_MANUAL_RELOC
  16. DECLARE_GLOBAL_DATA_PTR;
  17. #endif
  18. struct block_cache_node {
  19. struct list_head lh;
  20. int iftype;
  21. int devnum;
  22. lbaint_t start;
  23. lbaint_t blkcnt;
  24. unsigned long blksz;
  25. char *cache;
  26. };
  27. static LIST_HEAD(block_cache);
  28. static struct block_cache_stats _stats = {
  29. .max_blocks_per_entry = 8,
  30. .max_entries = 32
  31. };
  32. #ifdef CONFIG_NEEDS_MANUAL_RELOC
  33. int blkcache_init(void)
  34. {
  35. struct list_head *head = &block_cache;
  36. head->next = (uintptr_t)head->next + gd->reloc_off;
  37. head->prev = (uintptr_t)head->prev + gd->reloc_off;
  38. return 0;
  39. }
  40. #endif
  41. static struct block_cache_node *cache_find(int iftype, int devnum,
  42. lbaint_t start, lbaint_t blkcnt,
  43. unsigned long blksz)
  44. {
  45. struct block_cache_node *node;
  46. list_for_each_entry(node, &block_cache, lh)
  47. if ((node->iftype == iftype) &&
  48. (node->devnum == devnum) &&
  49. (node->blksz == blksz) &&
  50. (node->start <= start) &&
  51. (node->start + node->blkcnt >= start + blkcnt)) {
  52. if (block_cache.next != &node->lh) {
  53. /* maintain MRU ordering */
  54. list_del(&node->lh);
  55. list_add(&node->lh, &block_cache);
  56. }
  57. return node;
  58. }
  59. return 0;
  60. }
  61. int blkcache_read(int iftype, int devnum,
  62. lbaint_t start, lbaint_t blkcnt,
  63. unsigned long blksz, void *buffer)
  64. {
  65. struct block_cache_node *node = cache_find(iftype, devnum, start,
  66. blkcnt, blksz);
  67. if (node) {
  68. const char *src = node->cache + (start - node->start) * blksz;
  69. memcpy(buffer, src, blksz * blkcnt);
  70. debug("hit: start " LBAF ", count " LBAFU "\n",
  71. start, blkcnt);
  72. ++_stats.hits;
  73. return 1;
  74. }
  75. debug("miss: start " LBAF ", count " LBAFU "\n",
  76. start, blkcnt);
  77. ++_stats.misses;
  78. return 0;
  79. }
  80. void blkcache_fill(int iftype, int devnum,
  81. lbaint_t start, lbaint_t blkcnt,
  82. unsigned long blksz, void const *buffer)
  83. {
  84. lbaint_t bytes;
  85. struct block_cache_node *node;
  86. /* don't cache big stuff */
  87. if (blkcnt > _stats.max_blocks_per_entry)
  88. return;
  89. if (_stats.max_entries == 0)
  90. return;
  91. bytes = blksz * blkcnt;
  92. if (_stats.max_entries <= _stats.entries) {
  93. /* pop LRU */
  94. node = (struct block_cache_node *)block_cache.prev;
  95. list_del(&node->lh);
  96. _stats.entries--;
  97. debug("drop: start " LBAF ", count " LBAFU "\n",
  98. node->start, node->blkcnt);
  99. if (node->blkcnt * node->blksz < bytes) {
  100. free(node->cache);
  101. node->cache = 0;
  102. }
  103. } else {
  104. node = malloc(sizeof(*node));
  105. if (!node)
  106. return;
  107. node->cache = 0;
  108. }
  109. if (!node->cache) {
  110. node->cache = malloc(bytes);
  111. if (!node->cache) {
  112. free(node);
  113. return;
  114. }
  115. }
  116. debug("fill: start " LBAF ", count " LBAFU "\n",
  117. start, blkcnt);
  118. node->iftype = iftype;
  119. node->devnum = devnum;
  120. node->start = start;
  121. node->blkcnt = blkcnt;
  122. node->blksz = blksz;
  123. memcpy(node->cache, buffer, bytes);
  124. list_add(&node->lh, &block_cache);
  125. _stats.entries++;
  126. }
  127. void blkcache_invalidate(int iftype, int devnum)
  128. {
  129. struct list_head *entry, *n;
  130. struct block_cache_node *node;
  131. list_for_each_safe(entry, n, &block_cache) {
  132. node = (struct block_cache_node *)entry;
  133. if (iftype == -1 ||
  134. (node->iftype == iftype && node->devnum == devnum)) {
  135. list_del(entry);
  136. free(node->cache);
  137. free(node);
  138. --_stats.entries;
  139. }
  140. }
  141. }
  142. void blkcache_configure(unsigned blocks, unsigned entries)
  143. {
  144. /* invalidate cache if there is a change */
  145. if ((blocks != _stats.max_blocks_per_entry) ||
  146. (entries != _stats.max_entries))
  147. blkcache_invalidate(-1, 0);
  148. _stats.max_blocks_per_entry = blocks;
  149. _stats.max_entries = entries;
  150. _stats.hits = 0;
  151. _stats.misses = 0;
  152. }
  153. void blkcache_stats(struct block_cache_stats *stats)
  154. {
  155. memcpy(stats, &_stats, sizeof(*stats));
  156. _stats.hits = 0;
  157. _stats.misses = 0;
  158. }
  159. void blkcache_free(void)
  160. {
  161. blkcache_invalidate(-1, 0);
  162. }