1 // SPDX-License-Identifier: GPL-2.0 2 #include <linux/idr.h> 3 #include <linux/slab.h> 4 #include <linux/debugfs.h> 5 #include <linux/seq_file.h> 6 #include <linux/shrinker.h> 7 #include <linux/memcontrol.h> 8 9 /* defined in vmscan.c */ 10 extern struct rw_semaphore shrinker_rwsem; 11 extern struct list_head shrinker_list; 12 13 static DEFINE_IDA(shrinker_debugfs_ida); 14 static struct dentry *shrinker_debugfs_root; 15 16 static unsigned long shrinker_count_objects(struct shrinker *shrinker, 17 struct mem_cgroup *memcg, 18 unsigned long *count_per_node) 19 { 20 unsigned long nr, total = 0; 21 int nid; 22 23 for_each_node(nid) { 24 if (nid == 0 || (shrinker->flags & SHRINKER_NUMA_AWARE)) { 25 struct shrink_control sc = { 26 .gfp_mask = GFP_KERNEL, 27 .nid = nid, 28 .memcg = memcg, 29 }; 30 31 nr = shrinker->count_objects(shrinker, &sc); 32 if (nr == SHRINK_EMPTY) 33 nr = 0; 34 } else { 35 nr = 0; 36 } 37 38 count_per_node[nid] = nr; 39 total += nr; 40 } 41 42 return total; 43 } 44 45 static int shrinker_debugfs_count_show(struct seq_file *m, void *v) 46 { 47 struct shrinker *shrinker = m->private; 48 unsigned long *count_per_node; 49 struct mem_cgroup *memcg; 50 unsigned long total; 51 bool memcg_aware; 52 int ret, nid; 53 54 count_per_node = kcalloc(nr_node_ids, sizeof(unsigned long), GFP_KERNEL); 55 if (!count_per_node) 56 return -ENOMEM; 57 58 ret = down_read_killable(&shrinker_rwsem); 59 if (ret) { 60 kfree(count_per_node); 61 return ret; 62 } 63 rcu_read_lock(); 64 65 memcg_aware = shrinker->flags & SHRINKER_MEMCG_AWARE; 66 67 memcg = mem_cgroup_iter(NULL, NULL, NULL); 68 do { 69 if (memcg && !mem_cgroup_online(memcg)) 70 continue; 71 72 total = shrinker_count_objects(shrinker, 73 memcg_aware ? memcg : NULL, 74 count_per_node); 75 if (total) { 76 seq_printf(m, "%lu", mem_cgroup_ino(memcg)); 77 for_each_node(nid) 78 seq_printf(m, " %lu", count_per_node[nid]); 79 seq_putc(m, '\n'); 80 } 81 82 if (!memcg_aware) { 83 mem_cgroup_iter_break(NULL, memcg); 84 break; 85 } 86 87 if (signal_pending(current)) { 88 mem_cgroup_iter_break(NULL, memcg); 89 ret = -EINTR; 90 break; 91 } 92 } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)) != NULL); 93 94 rcu_read_unlock(); 95 up_read(&shrinker_rwsem); 96 97 kfree(count_per_node); 98 return ret; 99 } 100 DEFINE_SHOW_ATTRIBUTE(shrinker_debugfs_count); 101 102 int shrinker_debugfs_add(struct shrinker *shrinker) 103 { 104 struct dentry *entry; 105 char buf[128]; 106 int id; 107 108 lockdep_assert_held(&shrinker_rwsem); 109 110 /* debugfs isn't initialized yet, add debugfs entries later. */ 111 if (!shrinker_debugfs_root) 112 return 0; 113 114 id = ida_alloc(&shrinker_debugfs_ida, GFP_KERNEL); 115 if (id < 0) 116 return id; 117 shrinker->debugfs_id = id; 118 119 snprintf(buf, sizeof(buf), "%s-%d", shrinker->name, id); 120 121 /* create debugfs entry */ 122 entry = debugfs_create_dir(buf, shrinker_debugfs_root); 123 if (IS_ERR(entry)) { 124 ida_free(&shrinker_debugfs_ida, id); 125 return PTR_ERR(entry); 126 } 127 shrinker->debugfs_entry = entry; 128 129 debugfs_create_file("count", 0220, entry, shrinker, 130 &shrinker_debugfs_count_fops); 131 return 0; 132 } 133 134 int shrinker_debugfs_rename(struct shrinker *shrinker, const char *fmt, ...) 135 { 136 struct dentry *entry; 137 char buf[128]; 138 const char *new, *old; 139 va_list ap; 140 int ret = 0; 141 142 va_start(ap, fmt); 143 new = kvasprintf_const(GFP_KERNEL, fmt, ap); 144 va_end(ap); 145 146 if (!new) 147 return -ENOMEM; 148 149 down_write(&shrinker_rwsem); 150 151 old = shrinker->name; 152 shrinker->name = new; 153 154 if (shrinker->debugfs_entry) { 155 snprintf(buf, sizeof(buf), "%s-%d", shrinker->name, 156 shrinker->debugfs_id); 157 158 entry = debugfs_rename(shrinker_debugfs_root, 159 shrinker->debugfs_entry, 160 shrinker_debugfs_root, buf); 161 if (IS_ERR(entry)) 162 ret = PTR_ERR(entry); 163 else 164 shrinker->debugfs_entry = entry; 165 } 166 167 up_write(&shrinker_rwsem); 168 169 kfree_const(old); 170 171 return ret; 172 } 173 EXPORT_SYMBOL(shrinker_debugfs_rename); 174 175 void shrinker_debugfs_remove(struct shrinker *shrinker) 176 { 177 lockdep_assert_held(&shrinker_rwsem); 178 179 kfree_const(shrinker->name); 180 181 if (!shrinker->debugfs_entry) 182 return; 183 184 debugfs_remove_recursive(shrinker->debugfs_entry); 185 ida_free(&shrinker_debugfs_ida, shrinker->debugfs_id); 186 } 187 188 static int __init shrinker_debugfs_init(void) 189 { 190 struct shrinker *shrinker; 191 struct dentry *dentry; 192 int ret = 0; 193 194 dentry = debugfs_create_dir("shrinker", NULL); 195 if (IS_ERR(dentry)) 196 return PTR_ERR(dentry); 197 shrinker_debugfs_root = dentry; 198 199 /* Create debugfs entries for shrinkers registered at boot */ 200 down_write(&shrinker_rwsem); 201 list_for_each_entry(shrinker, &shrinker_list, list) 202 if (!shrinker->debugfs_entry) { 203 ret = shrinker_debugfs_add(shrinker); 204 if (ret) 205 break; 206 } 207 up_write(&shrinker_rwsem); 208 209 return ret; 210 } 211 late_initcall(shrinker_debugfs_init); 212