percpu-stats.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. /*
  2. * mm/percpu-debug.c
  3. *
  4. * Copyright (C) 2017 Facebook Inc.
  5. * Copyright (C) 2017 Dennis Zhou <dennisz@fb.com>
  6. *
  7. * This file is released under the GPLv2.
  8. *
  9. * Prints statistics about the percpu allocator and backing chunks.
  10. */
  11. #include <linux/debugfs.h>
  12. #include <linux/list.h>
  13. #include <linux/percpu.h>
  14. #include <linux/seq_file.h>
  15. #include <linux/sort.h>
  16. #include <linux/vmalloc.h>
  17. #include "percpu-internal.h"
  18. #define P(X, Y) \
  19. seq_printf(m, " %-24s: %8lld\n", X, (long long int)Y)
  20. struct percpu_stats pcpu_stats;
  21. struct pcpu_alloc_info pcpu_stats_ai;
  22. static int cmpint(const void *a, const void *b)
  23. {
  24. return *(int *)a - *(int *)b;
  25. }
  26. /*
  27. * Iterates over all chunks to find the max # of map entries used.
  28. */
  29. static int find_max_map_used(void)
  30. {
  31. struct pcpu_chunk *chunk;
  32. int slot, max_map_used;
  33. max_map_used = 0;
  34. for (slot = 0; slot < pcpu_nr_slots; slot++)
  35. list_for_each_entry(chunk, &pcpu_slot[slot], list)
  36. max_map_used = max(max_map_used, chunk->map_used);
  37. return max_map_used;
  38. }
  39. /*
  40. * Prints out chunk state. Fragmentation is considered between
  41. * the beginning of the chunk to the last allocation.
  42. */
  43. static void chunk_map_stats(struct seq_file *m, struct pcpu_chunk *chunk,
  44. void *buffer)
  45. {
  46. int i, s_index, last_alloc, alloc_sign, as_len;
  47. int *alloc_sizes, *p;
  48. /* statistics */
  49. int sum_frag = 0, max_frag = 0;
  50. int cur_min_alloc = 0, cur_med_alloc = 0, cur_max_alloc = 0;
  51. alloc_sizes = buffer;
  52. s_index = chunk->has_reserved ? 1 : 0;
  53. /* find last allocation */
  54. last_alloc = -1;
  55. for (i = chunk->map_used - 1; i >= s_index; i--) {
  56. if (chunk->map[i] & 1) {
  57. last_alloc = i;
  58. break;
  59. }
  60. }
  61. /* if the chunk is not empty - ignoring reserve */
  62. if (last_alloc >= s_index) {
  63. as_len = last_alloc + 1 - s_index;
  64. /*
  65. * Iterate through chunk map computing size info.
  66. * The first bit is overloaded to be a used flag.
  67. * negative = free space, positive = allocated
  68. */
  69. for (i = 0, p = chunk->map + s_index; i < as_len; i++, p++) {
  70. alloc_sign = (*p & 1) ? 1 : -1;
  71. alloc_sizes[i] = alloc_sign *
  72. ((p[1] & ~1) - (p[0] & ~1));
  73. }
  74. sort(alloc_sizes, as_len, sizeof(chunk->map[0]), cmpint, NULL);
  75. /* Iterate through the unallocated fragements. */
  76. for (i = 0, p = alloc_sizes; *p < 0 && i < as_len; i++, p++) {
  77. sum_frag -= *p;
  78. max_frag = max(max_frag, -1 * (*p));
  79. }
  80. cur_min_alloc = alloc_sizes[i];
  81. cur_med_alloc = alloc_sizes[(i + as_len - 1) / 2];
  82. cur_max_alloc = alloc_sizes[as_len - 1];
  83. }
  84. P("nr_alloc", chunk->nr_alloc);
  85. P("max_alloc_size", chunk->max_alloc_size);
  86. P("free_size", chunk->free_size);
  87. P("contig_hint", chunk->contig_hint);
  88. P("sum_frag", sum_frag);
  89. P("max_frag", max_frag);
  90. P("cur_min_alloc", cur_min_alloc);
  91. P("cur_med_alloc", cur_med_alloc);
  92. P("cur_max_alloc", cur_max_alloc);
  93. seq_putc(m, '\n');
  94. }
  95. static int percpu_stats_show(struct seq_file *m, void *v)
  96. {
  97. struct pcpu_chunk *chunk;
  98. int slot, max_map_used;
  99. void *buffer;
  100. alloc_buffer:
  101. spin_lock_irq(&pcpu_lock);
  102. max_map_used = find_max_map_used();
  103. spin_unlock_irq(&pcpu_lock);
  104. buffer = vmalloc(max_map_used * sizeof(pcpu_first_chunk->map[0]));
  105. if (!buffer)
  106. return -ENOMEM;
  107. spin_lock_irq(&pcpu_lock);
  108. /* if the buffer allocated earlier is too small */
  109. if (max_map_used < find_max_map_used()) {
  110. spin_unlock_irq(&pcpu_lock);
  111. vfree(buffer);
  112. goto alloc_buffer;
  113. }
  114. #define PL(X) \
  115. seq_printf(m, " %-24s: %8lld\n", #X, (long long int)pcpu_stats_ai.X)
  116. seq_printf(m,
  117. "Percpu Memory Statistics\n"
  118. "Allocation Info:\n"
  119. "----------------------------------------\n");
  120. PL(unit_size);
  121. PL(static_size);
  122. PL(reserved_size);
  123. PL(dyn_size);
  124. PL(atom_size);
  125. PL(alloc_size);
  126. seq_putc(m, '\n');
  127. #undef PL
  128. #define PU(X) \
  129. seq_printf(m, " %-18s: %14llu\n", #X, (unsigned long long)pcpu_stats.X)
  130. seq_printf(m,
  131. "Global Stats:\n"
  132. "----------------------------------------\n");
  133. PU(nr_alloc);
  134. PU(nr_dealloc);
  135. PU(nr_cur_alloc);
  136. PU(nr_max_alloc);
  137. PU(nr_chunks);
  138. PU(nr_max_chunks);
  139. PU(min_alloc_size);
  140. PU(max_alloc_size);
  141. seq_putc(m, '\n');
  142. #undef PU
  143. seq_printf(m,
  144. "Per Chunk Stats:\n"
  145. "----------------------------------------\n");
  146. if (pcpu_reserved_chunk) {
  147. seq_puts(m, "Chunk: <- Reserved Chunk\n");
  148. chunk_map_stats(m, pcpu_reserved_chunk, buffer);
  149. }
  150. for (slot = 0; slot < pcpu_nr_slots; slot++) {
  151. list_for_each_entry(chunk, &pcpu_slot[slot], list) {
  152. if (chunk == pcpu_first_chunk) {
  153. seq_puts(m, "Chunk: <- First Chunk\n");
  154. chunk_map_stats(m, chunk, buffer);
  155. } else {
  156. seq_puts(m, "Chunk:\n");
  157. chunk_map_stats(m, chunk, buffer);
  158. }
  159. }
  160. }
  161. spin_unlock_irq(&pcpu_lock);
  162. vfree(buffer);
  163. return 0;
  164. }
  165. static int percpu_stats_open(struct inode *inode, struct file *filp)
  166. {
  167. return single_open(filp, percpu_stats_show, NULL);
  168. }
  169. static const struct file_operations percpu_stats_fops = {
  170. .open = percpu_stats_open,
  171. .read = seq_read,
  172. .llseek = seq_lseek,
  173. .release = single_release,
  174. };
  175. static int __init init_percpu_stats_debugfs(void)
  176. {
  177. debugfs_create_file("percpu_stats", 0444, NULL, NULL,
  178. &percpu_stats_fops);
  179. return 0;
  180. }
  181. late_initcall(init_percpu_stats_debugfs);