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 #include "internal.h" 10 11 /* defined in vmscan.c */ 12 extern struct mutex shrinker_mutex; 13 extern struct list_head shrinker_list; 14 15 static DEFINE_IDA(shrinker_debugfs_ida); 16 static struct dentry *shrinker_debugfs_root; 17 18 static unsigned long shrinker_count_objects(struct shrinker *shrinker, 19 struct mem_cgroup *memcg, 20 unsigned long *count_per_node) 21 { 22 unsigned long nr, total = 0; 23 int nid; 24 25 for_each_node(nid) { 26 if (nid == 0 || (shrinker->flags & SHRINKER_NUMA_AWARE)) { 27 struct shrink_control sc = { 28 .gfp_mask = GFP_KERNEL, 29 .nid = nid, 30 .memcg = memcg, 31 }; 32 33 nr = shrinker->count_objects(shrinker, &sc); 34 if (nr == SHRINK_EMPTY) 35 nr = 0; 36 } else { 37 nr = 0; 38 } 39 40 count_per_node[nid] = nr; 41 total += nr; 42 } 43 44 return total; 45 } 46 47 static int shrinker_debugfs_count_show(struct seq_file *m, void *v) 48 { 49 struct shrinker *shrinker = m->private; 50 unsigned long *count_per_node; 51 struct mem_cgroup *memcg; 52 unsigned long total; 53 bool memcg_aware; 54 int ret = 0, nid; 55 56 count_per_node = kcalloc(nr_node_ids, sizeof(unsigned long), GFP_KERNEL); 57 if (!count_per_node) 58 return -ENOMEM; 59 60 rcu_read_lock(); 61 62 memcg_aware = shrinker->flags & SHRINKER_MEMCG_AWARE; 63 64 memcg = mem_cgroup_iter(NULL, NULL, NULL); 65 do { 66 if (memcg && !mem_cgroup_online(memcg)) 67 continue; 68 69 total = shrinker_count_objects(shrinker, 70 memcg_aware ? memcg : NULL, 71 count_per_node); 72 if (total) { 73 seq_printf(m, "%lu", mem_cgroup_ino(memcg)); 74 for_each_node(nid) 75 seq_printf(m, " %lu", count_per_node[nid]); 76 seq_putc(m, '\n'); 77 } 78 79 if (!memcg_aware) { 80 mem_cgroup_iter_break(NULL, memcg); 81 break; 82 } 83 84 if (signal_pending(current)) { 85 mem_cgroup_iter_break(NULL, memcg); 86 ret = -EINTR; 87 break; 88 } 89 } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)) != NULL); 90 91 rcu_read_unlock(); 92 93 kfree(count_per_node); 94 return ret; 95 } 96 DEFINE_SHOW_ATTRIBUTE(shrinker_debugfs_count); 97 98 static int shrinker_debugfs_scan_open(struct inode *inode, struct file *file) 99 { 100 file->private_data = inode->i_private; 101 return nonseekable_open(inode, file); 102 } 103 104 static ssize_t shrinker_debugfs_scan_write(struct file *file, 105 const char __user *buf, 106 size_t size, loff_t *pos) 107 { 108 struct shrinker *shrinker = file->private_data; 109 unsigned long nr_to_scan = 0, ino, read_len; 110 struct shrink_control sc = { 111 .gfp_mask = GFP_KERNEL, 112 }; 113 struct mem_cgroup *memcg = NULL; 114 int nid; 115 char kbuf[72]; 116 117 read_len = size < (sizeof(kbuf) - 1) ? size : (sizeof(kbuf) - 1); 118 if (copy_from_user(kbuf, buf, read_len)) 119 return -EFAULT; 120 kbuf[read_len] = '\0'; 121 122 if (sscanf(kbuf, "%lu %d %lu", &ino, &nid, &nr_to_scan) != 3) 123 return -EINVAL; 124 125 if (nid < 0 || nid >= nr_node_ids) 126 return -EINVAL; 127 128 if (nr_to_scan == 0) 129 return size; 130 131 if (shrinker->flags & SHRINKER_MEMCG_AWARE) { 132 memcg = mem_cgroup_get_from_ino(ino); 133 if (!memcg || IS_ERR(memcg)) 134 return -ENOENT; 135 136 if (!mem_cgroup_online(memcg)) { 137 mem_cgroup_put(memcg); 138 return -ENOENT; 139 } 140 } else if (ino != 0) { 141 return -EINVAL; 142 } 143 144 sc.nid = nid; 145 sc.memcg = memcg; 146 sc.nr_to_scan = nr_to_scan; 147 sc.nr_scanned = nr_to_scan; 148 149 shrinker->scan_objects(shrinker, &sc); 150 151 mem_cgroup_put(memcg); 152 153 return size; 154 } 155 156 static const struct file_operations shrinker_debugfs_scan_fops = { 157 .owner = THIS_MODULE, 158 .open = shrinker_debugfs_scan_open, 159 .write = shrinker_debugfs_scan_write, 160 }; 161 162 int shrinker_debugfs_add(struct shrinker *shrinker) 163 { 164 struct dentry *entry; 165 char buf[128]; 166 int id; 167 168 lockdep_assert_held(&shrinker_mutex); 169 170 /* debugfs isn't initialized yet, add debugfs entries later. */ 171 if (!shrinker_debugfs_root) 172 return 0; 173 174 id = ida_alloc(&shrinker_debugfs_ida, GFP_KERNEL); 175 if (id < 0) 176 return id; 177 shrinker->debugfs_id = id; 178 179 snprintf(buf, sizeof(buf), "%s-%d", shrinker->name, id); 180 181 /* create debugfs entry */ 182 entry = debugfs_create_dir(buf, shrinker_debugfs_root); 183 if (IS_ERR(entry)) { 184 ida_free(&shrinker_debugfs_ida, id); 185 return PTR_ERR(entry); 186 } 187 shrinker->debugfs_entry = entry; 188 189 debugfs_create_file("count", 0440, entry, shrinker, 190 &shrinker_debugfs_count_fops); 191 debugfs_create_file("scan", 0220, entry, shrinker, 192 &shrinker_debugfs_scan_fops); 193 return 0; 194 } 195 196 int shrinker_debugfs_rename(struct shrinker *shrinker, const char *fmt, ...) 197 { 198 struct dentry *entry; 199 char buf[128]; 200 const char *new, *old; 201 va_list ap; 202 int ret = 0; 203 204 va_start(ap, fmt); 205 new = kvasprintf_const(GFP_KERNEL, fmt, ap); 206 va_end(ap); 207 208 if (!new) 209 return -ENOMEM; 210 211 mutex_lock(&shrinker_mutex); 212 213 old = shrinker->name; 214 shrinker->name = new; 215 216 if (shrinker->debugfs_entry) { 217 snprintf(buf, sizeof(buf), "%s-%d", shrinker->name, 218 shrinker->debugfs_id); 219 220 entry = debugfs_rename(shrinker_debugfs_root, 221 shrinker->debugfs_entry, 222 shrinker_debugfs_root, buf); 223 if (IS_ERR(entry)) 224 ret = PTR_ERR(entry); 225 else 226 shrinker->debugfs_entry = entry; 227 } 228 229 mutex_unlock(&shrinker_mutex); 230 231 kfree_const(old); 232 233 return ret; 234 } 235 EXPORT_SYMBOL(shrinker_debugfs_rename); 236 237 struct dentry *shrinker_debugfs_detach(struct shrinker *shrinker, 238 int *debugfs_id) 239 { 240 struct dentry *entry = shrinker->debugfs_entry; 241 242 lockdep_assert_held(&shrinker_mutex); 243 244 *debugfs_id = entry ? shrinker->debugfs_id : -1; 245 shrinker->debugfs_entry = NULL; 246 247 return entry; 248 } 249 250 void shrinker_debugfs_remove(struct dentry *debugfs_entry, int debugfs_id) 251 { 252 debugfs_remove_recursive(debugfs_entry); 253 ida_free(&shrinker_debugfs_ida, debugfs_id); 254 } 255 256 static int __init shrinker_debugfs_init(void) 257 { 258 struct shrinker *shrinker; 259 struct dentry *dentry; 260 int ret = 0; 261 262 dentry = debugfs_create_dir("shrinker", NULL); 263 if (IS_ERR(dentry)) 264 return PTR_ERR(dentry); 265 shrinker_debugfs_root = dentry; 266 267 /* Create debugfs entries for shrinkers registered at boot */ 268 mutex_lock(&shrinker_mutex); 269 list_for_each_entry(shrinker, &shrinker_list, list) 270 if (!shrinker->debugfs_entry) { 271 ret = shrinker_debugfs_add(shrinker); 272 if (ret) 273 break; 274 } 275 mutex_unlock(&shrinker_mutex); 276 277 return ret; 278 } 279 late_initcall(shrinker_debugfs_init); 280
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.