1*4971266eSNamhyung Kim // SPDX-License-Identifier: GPL-2.0-only 2*4971266eSNamhyung Kim /* Copyright (c) 2024 Google */ 3*4971266eSNamhyung Kim #include <linux/bpf.h> 4*4971266eSNamhyung Kim #include <linux/btf_ids.h> 5*4971266eSNamhyung Kim #include <linux/slab.h> 6*4971266eSNamhyung Kim #include <linux/kernel.h> 7*4971266eSNamhyung Kim #include <linux/seq_file.h> 8*4971266eSNamhyung Kim 9*4971266eSNamhyung Kim #include "../../mm/slab.h" /* kmem_cache, slab_caches and slab_mutex */ 10*4971266eSNamhyung Kim 11*4971266eSNamhyung Kim struct bpf_iter__kmem_cache { 12*4971266eSNamhyung Kim __bpf_md_ptr(struct bpf_iter_meta *, meta); 13*4971266eSNamhyung Kim __bpf_md_ptr(struct kmem_cache *, s); 14*4971266eSNamhyung Kim }; 15*4971266eSNamhyung Kim 16*4971266eSNamhyung Kim static void *kmem_cache_iter_seq_start(struct seq_file *seq, loff_t *pos) 17*4971266eSNamhyung Kim { 18*4971266eSNamhyung Kim loff_t cnt = 0; 19*4971266eSNamhyung Kim bool found = false; 20*4971266eSNamhyung Kim struct kmem_cache *s; 21*4971266eSNamhyung Kim 22*4971266eSNamhyung Kim mutex_lock(&slab_mutex); 23*4971266eSNamhyung Kim 24*4971266eSNamhyung Kim /* Find an entry at the given position in the slab_caches list instead 25*4971266eSNamhyung Kim * of keeping a reference (of the last visited entry, if any) out of 26*4971266eSNamhyung Kim * slab_mutex. It might miss something if one is deleted in the middle 27*4971266eSNamhyung Kim * while it releases the lock. But it should be rare and there's not 28*4971266eSNamhyung Kim * much we can do about it. 29*4971266eSNamhyung Kim */ 30*4971266eSNamhyung Kim list_for_each_entry(s, &slab_caches, list) { 31*4971266eSNamhyung Kim if (cnt == *pos) { 32*4971266eSNamhyung Kim /* Make sure this entry remains in the list by getting 33*4971266eSNamhyung Kim * a new reference count. Note that boot_cache entries 34*4971266eSNamhyung Kim * have a negative refcount, so don't touch them. 35*4971266eSNamhyung Kim */ 36*4971266eSNamhyung Kim if (s->refcount > 0) 37*4971266eSNamhyung Kim s->refcount++; 38*4971266eSNamhyung Kim found = true; 39*4971266eSNamhyung Kim break; 40*4971266eSNamhyung Kim } 41*4971266eSNamhyung Kim cnt++; 42*4971266eSNamhyung Kim } 43*4971266eSNamhyung Kim mutex_unlock(&slab_mutex); 44*4971266eSNamhyung Kim 45*4971266eSNamhyung Kim if (!found) 46*4971266eSNamhyung Kim return NULL; 47*4971266eSNamhyung Kim 48*4971266eSNamhyung Kim return s; 49*4971266eSNamhyung Kim } 50*4971266eSNamhyung Kim 51*4971266eSNamhyung Kim static void kmem_cache_iter_seq_stop(struct seq_file *seq, void *v) 52*4971266eSNamhyung Kim { 53*4971266eSNamhyung Kim struct bpf_iter_meta meta; 54*4971266eSNamhyung Kim struct bpf_iter__kmem_cache ctx = { 55*4971266eSNamhyung Kim .meta = &meta, 56*4971266eSNamhyung Kim .s = v, 57*4971266eSNamhyung Kim }; 58*4971266eSNamhyung Kim struct bpf_prog *prog; 59*4971266eSNamhyung Kim bool destroy = false; 60*4971266eSNamhyung Kim 61*4971266eSNamhyung Kim meta.seq = seq; 62*4971266eSNamhyung Kim prog = bpf_iter_get_info(&meta, true); 63*4971266eSNamhyung Kim if (prog && !ctx.s) 64*4971266eSNamhyung Kim bpf_iter_run_prog(prog, &ctx); 65*4971266eSNamhyung Kim 66*4971266eSNamhyung Kim if (ctx.s == NULL) 67*4971266eSNamhyung Kim return; 68*4971266eSNamhyung Kim 69*4971266eSNamhyung Kim mutex_lock(&slab_mutex); 70*4971266eSNamhyung Kim 71*4971266eSNamhyung Kim /* Skip kmem_cache_destroy() for active entries */ 72*4971266eSNamhyung Kim if (ctx.s->refcount > 1) 73*4971266eSNamhyung Kim ctx.s->refcount--; 74*4971266eSNamhyung Kim else if (ctx.s->refcount == 1) 75*4971266eSNamhyung Kim destroy = true; 76*4971266eSNamhyung Kim 77*4971266eSNamhyung Kim mutex_unlock(&slab_mutex); 78*4971266eSNamhyung Kim 79*4971266eSNamhyung Kim if (destroy) 80*4971266eSNamhyung Kim kmem_cache_destroy(ctx.s); 81*4971266eSNamhyung Kim } 82*4971266eSNamhyung Kim 83*4971266eSNamhyung Kim static void *kmem_cache_iter_seq_next(struct seq_file *seq, void *v, loff_t *pos) 84*4971266eSNamhyung Kim { 85*4971266eSNamhyung Kim struct kmem_cache *s = v; 86*4971266eSNamhyung Kim struct kmem_cache *next = NULL; 87*4971266eSNamhyung Kim bool destroy = false; 88*4971266eSNamhyung Kim 89*4971266eSNamhyung Kim ++*pos; 90*4971266eSNamhyung Kim 91*4971266eSNamhyung Kim mutex_lock(&slab_mutex); 92*4971266eSNamhyung Kim 93*4971266eSNamhyung Kim if (list_last_entry(&slab_caches, struct kmem_cache, list) != s) { 94*4971266eSNamhyung Kim next = list_next_entry(s, list); 95*4971266eSNamhyung Kim 96*4971266eSNamhyung Kim WARN_ON_ONCE(next->refcount == 0); 97*4971266eSNamhyung Kim 98*4971266eSNamhyung Kim /* boot_caches have negative refcount, don't touch them */ 99*4971266eSNamhyung Kim if (next->refcount > 0) 100*4971266eSNamhyung Kim next->refcount++; 101*4971266eSNamhyung Kim } 102*4971266eSNamhyung Kim 103*4971266eSNamhyung Kim /* Skip kmem_cache_destroy() for active entries */ 104*4971266eSNamhyung Kim if (s->refcount > 1) 105*4971266eSNamhyung Kim s->refcount--; 106*4971266eSNamhyung Kim else if (s->refcount == 1) 107*4971266eSNamhyung Kim destroy = true; 108*4971266eSNamhyung Kim 109*4971266eSNamhyung Kim mutex_unlock(&slab_mutex); 110*4971266eSNamhyung Kim 111*4971266eSNamhyung Kim if (destroy) 112*4971266eSNamhyung Kim kmem_cache_destroy(s); 113*4971266eSNamhyung Kim 114*4971266eSNamhyung Kim return next; 115*4971266eSNamhyung Kim } 116*4971266eSNamhyung Kim 117*4971266eSNamhyung Kim static int kmem_cache_iter_seq_show(struct seq_file *seq, void *v) 118*4971266eSNamhyung Kim { 119*4971266eSNamhyung Kim struct bpf_iter_meta meta; 120*4971266eSNamhyung Kim struct bpf_iter__kmem_cache ctx = { 121*4971266eSNamhyung Kim .meta = &meta, 122*4971266eSNamhyung Kim .s = v, 123*4971266eSNamhyung Kim }; 124*4971266eSNamhyung Kim struct bpf_prog *prog; 125*4971266eSNamhyung Kim int ret = 0; 126*4971266eSNamhyung Kim 127*4971266eSNamhyung Kim meta.seq = seq; 128*4971266eSNamhyung Kim prog = bpf_iter_get_info(&meta, false); 129*4971266eSNamhyung Kim if (prog) 130*4971266eSNamhyung Kim ret = bpf_iter_run_prog(prog, &ctx); 131*4971266eSNamhyung Kim 132*4971266eSNamhyung Kim return ret; 133*4971266eSNamhyung Kim } 134*4971266eSNamhyung Kim 135*4971266eSNamhyung Kim static const struct seq_operations kmem_cache_iter_seq_ops = { 136*4971266eSNamhyung Kim .start = kmem_cache_iter_seq_start, 137*4971266eSNamhyung Kim .next = kmem_cache_iter_seq_next, 138*4971266eSNamhyung Kim .stop = kmem_cache_iter_seq_stop, 139*4971266eSNamhyung Kim .show = kmem_cache_iter_seq_show, 140*4971266eSNamhyung Kim }; 141*4971266eSNamhyung Kim 142*4971266eSNamhyung Kim BTF_ID_LIST_GLOBAL_SINGLE(bpf_kmem_cache_btf_id, struct, kmem_cache) 143*4971266eSNamhyung Kim 144*4971266eSNamhyung Kim static const struct bpf_iter_seq_info kmem_cache_iter_seq_info = { 145*4971266eSNamhyung Kim .seq_ops = &kmem_cache_iter_seq_ops, 146*4971266eSNamhyung Kim }; 147*4971266eSNamhyung Kim 148*4971266eSNamhyung Kim static void bpf_iter_kmem_cache_show_fdinfo(const struct bpf_iter_aux_info *aux, 149*4971266eSNamhyung Kim struct seq_file *seq) 150*4971266eSNamhyung Kim { 151*4971266eSNamhyung Kim seq_puts(seq, "kmem_cache iter\n"); 152*4971266eSNamhyung Kim } 153*4971266eSNamhyung Kim 154*4971266eSNamhyung Kim DEFINE_BPF_ITER_FUNC(kmem_cache, struct bpf_iter_meta *meta, 155*4971266eSNamhyung Kim struct kmem_cache *s) 156*4971266eSNamhyung Kim 157*4971266eSNamhyung Kim static struct bpf_iter_reg bpf_kmem_cache_reg_info = { 158*4971266eSNamhyung Kim .target = "kmem_cache", 159*4971266eSNamhyung Kim .feature = BPF_ITER_RESCHED, 160*4971266eSNamhyung Kim .show_fdinfo = bpf_iter_kmem_cache_show_fdinfo, 161*4971266eSNamhyung Kim .ctx_arg_info_size = 1, 162*4971266eSNamhyung Kim .ctx_arg_info = { 163*4971266eSNamhyung Kim { offsetof(struct bpf_iter__kmem_cache, s), 164*4971266eSNamhyung Kim PTR_TO_BTF_ID_OR_NULL | PTR_TRUSTED }, 165*4971266eSNamhyung Kim }, 166*4971266eSNamhyung Kim .seq_info = &kmem_cache_iter_seq_info, 167*4971266eSNamhyung Kim }; 168*4971266eSNamhyung Kim 169*4971266eSNamhyung Kim static int __init bpf_kmem_cache_iter_init(void) 170*4971266eSNamhyung Kim { 171*4971266eSNamhyung Kim bpf_kmem_cache_reg_info.ctx_arg_info[0].btf_id = bpf_kmem_cache_btf_id[0]; 172*4971266eSNamhyung Kim return bpf_iter_reg_target(&bpf_kmem_cache_reg_info); 173*4971266eSNamhyung Kim } 174*4971266eSNamhyung Kim 175*4971266eSNamhyung Kim late_initcall(bpf_kmem_cache_iter_init); 176