Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'mm-bpf-kfuncs-to-access-memcg-data'

Roman Gushchin says:

====================
mm: bpf kfuncs to access memcg data

Introduce kfuncs to simplify the access to the memcg data.
These kfuncs can be used to accelerate monitoring use cases and
for implementing custom OOM policies once BPF OOM is landed.

This patchset was separated out from the BPF OOM patchset to simplify
the logistics and accelerate the landing of the part which is useful
by itself. No functional changes since BPF OOM v2.

v4:
- refactored memcg vm event and stat item idx checks (by Alexei)

v3:
- dropped redundant kfuncs flags (by Alexei)
- fixed kdocs warnings (by Alexei)
- merged memcg stats access patches into one (by Alexei)
- restored root memcg usage reporting, added a comment
- added checks for enum boundaries
- added Shakeel and JP as co-maintainers (by Shakeel)

v2:
- added mem_cgroup_disabled() checks (by Shakeel B.)
- added special handling of the root memcg in bpf_mem_cgroup_usage()
(by Shakeel B.)
- minor fixes in the kselftest (by Shakeel B.)
- added a MAINTAINERS entry (by Shakeel B.)

v1:
https://lore.kernel.org/bpf/87ike29s5r.fsf@linux.dev/T/#t
====================

Link: https://patch.msgid.link/20251223044156.208250-1-roman.gushchin@linux.dev
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

+521 -1
+9
MAINTAINERS
··· 4799 4799 S: Maintained 4800 4800 F: tools/lib/bpf/ 4801 4801 4802 + BPF [MEMORY MANAGEMENT EXTENSIONS] 4803 + M: Roman Gushchin <roman.gushchin@linux.dev> 4804 + M: JP Kobryn <inwardvessel@gmail.com> 4805 + M: Shakeel Butt <shakeel.butt@linux.dev> 4806 + L: bpf@vger.kernel.org 4807 + L: linux-mm@kvack.org 4808 + S: Maintained 4809 + F: mm/bpf_memcontrol.c 4810 + 4802 4811 BPF [MISC] 4803 4812 L: bpf@vger.kernel.org 4804 4813 S: Odd Fixes
+20
include/linux/memcontrol.h
··· 949 949 rcu_read_unlock(); 950 950 } 951 951 952 + unsigned long memcg_events(struct mem_cgroup *memcg, int event); 953 + unsigned long mem_cgroup_usage(struct mem_cgroup *memcg, bool swap); 952 954 unsigned long memcg_page_state(struct mem_cgroup *memcg, int idx); 955 + unsigned long memcg_page_state_output(struct mem_cgroup *memcg, int item); 956 + bool memcg_stat_item_valid(int idx); 957 + bool memcg_vm_event_item_valid(enum vm_event_item idx); 953 958 unsigned long lruvec_page_state(struct lruvec *lruvec, enum node_stat_item idx); 954 959 unsigned long lruvec_page_state_local(struct lruvec *lruvec, 955 960 enum node_stat_item idx); ··· 1376 1371 static inline unsigned long memcg_page_state(struct mem_cgroup *memcg, int idx) 1377 1372 { 1378 1373 return 0; 1374 + } 1375 + 1376 + static inline unsigned long memcg_page_state_output(struct mem_cgroup *memcg, int item) 1377 + { 1378 + return 0; 1379 + } 1380 + 1381 + static inline bool memcg_stat_item_valid(int idx) 1382 + { 1383 + return false; 1384 + } 1385 + 1386 + static inline bool memcg_vm_event_item_valid(enum vm_event_item idx) 1387 + { 1388 + return false; 1379 1389 } 1380 1390 1381 1391 static inline unsigned long lruvec_page_state(struct lruvec *lruvec,
+3
mm/Makefile
··· 106 106 ifdef CONFIG_SWAP 107 107 obj-$(CONFIG_MEMCG) += swap_cgroup.o 108 108 endif 109 + ifdef CONFIG_BPF_SYSCALL 110 + obj-$(CONFIG_MEMCG) += bpf_memcontrol.o 111 + endif 109 112 obj-$(CONFIG_CGROUP_HUGETLB) += hugetlb_cgroup.o 110 113 obj-$(CONFIG_GUP_TEST) += gup_test.o 111 114 obj-$(CONFIG_DMAPOOL_TEST) += dmapool_test.o
+193
mm/bpf_memcontrol.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Memory Controller-related BPF kfuncs and auxiliary code 4 + * 5 + * Author: Roman Gushchin <roman.gushchin@linux.dev> 6 + */ 7 + 8 + #include <linux/memcontrol.h> 9 + #include <linux/bpf.h> 10 + 11 + __bpf_kfunc_start_defs(); 12 + 13 + /** 14 + * bpf_get_root_mem_cgroup - Returns a pointer to the root memory cgroup 15 + * 16 + * The function has KF_ACQUIRE semantics, even though the root memory 17 + * cgroup is never destroyed after being created and doesn't require 18 + * reference counting. And it's perfectly safe to pass it to 19 + * bpf_put_mem_cgroup() 20 + * 21 + * Return: A pointer to the root memory cgroup. 22 + */ 23 + __bpf_kfunc struct mem_cgroup *bpf_get_root_mem_cgroup(void) 24 + { 25 + if (mem_cgroup_disabled()) 26 + return NULL; 27 + 28 + /* css_get() is not needed */ 29 + return root_mem_cgroup; 30 + } 31 + 32 + /** 33 + * bpf_get_mem_cgroup - Get a reference to a memory cgroup 34 + * @css: pointer to the css structure 35 + * 36 + * It's fine to pass a css which belongs to any cgroup controller, 37 + * e.g. unified hierarchy's main css. 38 + * 39 + * Implements KF_ACQUIRE semantics. 40 + * 41 + * Return: A pointer to a mem_cgroup structure after bumping 42 + * the corresponding css's reference counter. 43 + */ 44 + __bpf_kfunc struct mem_cgroup * 45 + bpf_get_mem_cgroup(struct cgroup_subsys_state *css) 46 + { 47 + struct mem_cgroup *memcg = NULL; 48 + bool rcu_unlock = false; 49 + 50 + if (mem_cgroup_disabled() || !root_mem_cgroup) 51 + return NULL; 52 + 53 + if (root_mem_cgroup->css.ss != css->ss) { 54 + struct cgroup *cgroup = css->cgroup; 55 + int ssid = root_mem_cgroup->css.ss->id; 56 + 57 + rcu_read_lock(); 58 + rcu_unlock = true; 59 + css = rcu_dereference_raw(cgroup->subsys[ssid]); 60 + } 61 + 62 + if (css && css_tryget(css)) 63 + memcg = container_of(css, struct mem_cgroup, css); 64 + 65 + if (rcu_unlock) 66 + rcu_read_unlock(); 67 + 68 + return memcg; 69 + } 70 + 71 + /** 72 + * bpf_put_mem_cgroup - Put a reference to a memory cgroup 73 + * @memcg: memory cgroup to release 74 + * 75 + * Releases a previously acquired memcg reference. 76 + * Implements KF_RELEASE semantics. 77 + */ 78 + __bpf_kfunc void bpf_put_mem_cgroup(struct mem_cgroup *memcg) 79 + { 80 + css_put(&memcg->css); 81 + } 82 + 83 + /** 84 + * bpf_mem_cgroup_vm_events - Read memory cgroup's vm event counter 85 + * @memcg: memory cgroup 86 + * @event: event id 87 + * 88 + * Allows to read memory cgroup event counters. 89 + * 90 + * Return: The current value of the corresponding events counter. 91 + */ 92 + __bpf_kfunc unsigned long bpf_mem_cgroup_vm_events(struct mem_cgroup *memcg, 93 + enum vm_event_item event) 94 + { 95 + if (unlikely(!memcg_vm_event_item_valid(event))) 96 + return (unsigned long)-1; 97 + 98 + return memcg_events(memcg, event); 99 + } 100 + 101 + /** 102 + * bpf_mem_cgroup_usage - Read memory cgroup's usage 103 + * @memcg: memory cgroup 104 + * 105 + * Please, note that the root memory cgroup it special and is exempt 106 + * from the memory accounting. The returned value is a sum of sub-cgroup's 107 + * usages and it not reflecting the size of the root memory cgroup itself. 108 + * If you need to get an approximation, you can use root level statistics: 109 + * e.g. NR_FILE_PAGES + NR_ANON_MAPPED. 110 + * 111 + * Return: The current memory cgroup size in bytes. 112 + */ 113 + __bpf_kfunc unsigned long bpf_mem_cgroup_usage(struct mem_cgroup *memcg) 114 + { 115 + return page_counter_read(&memcg->memory) * PAGE_SIZE; 116 + } 117 + 118 + /** 119 + * bpf_mem_cgroup_memory_events - Read memory cgroup's memory event value 120 + * @memcg: memory cgroup 121 + * @event: memory event id 122 + * 123 + * Return: The current value of the memory event counter. 124 + */ 125 + __bpf_kfunc unsigned long bpf_mem_cgroup_memory_events(struct mem_cgroup *memcg, 126 + enum memcg_memory_event event) 127 + { 128 + if (unlikely(event >= MEMCG_NR_MEMORY_EVENTS)) 129 + return (unsigned long)-1; 130 + 131 + return atomic_long_read(&memcg->memory_events[event]); 132 + } 133 + 134 + /** 135 + * bpf_mem_cgroup_page_state - Read memory cgroup's page state counter 136 + * @memcg: memory cgroup 137 + * @idx: counter idx 138 + * 139 + * Allows to read memory cgroup statistics. The output is in bytes. 140 + * 141 + * Return: The value of the page state counter in bytes. 142 + */ 143 + __bpf_kfunc unsigned long bpf_mem_cgroup_page_state(struct mem_cgroup *memcg, int idx) 144 + { 145 + if (unlikely(!memcg_stat_item_valid(idx))) 146 + return (unsigned long)-1; 147 + 148 + return memcg_page_state_output(memcg, idx); 149 + } 150 + 151 + /** 152 + * bpf_mem_cgroup_flush_stats - Flush memory cgroup's statistics 153 + * @memcg: memory cgroup 154 + * 155 + * Propagate memory cgroup's statistics up the cgroup tree. 156 + */ 157 + __bpf_kfunc void bpf_mem_cgroup_flush_stats(struct mem_cgroup *memcg) 158 + { 159 + mem_cgroup_flush_stats(memcg); 160 + } 161 + 162 + __bpf_kfunc_end_defs(); 163 + 164 + BTF_KFUNCS_START(bpf_memcontrol_kfuncs) 165 + BTF_ID_FLAGS(func, bpf_get_root_mem_cgroup, KF_ACQUIRE | KF_RET_NULL) 166 + BTF_ID_FLAGS(func, bpf_get_mem_cgroup, KF_ACQUIRE | KF_RET_NULL | KF_RCU) 167 + BTF_ID_FLAGS(func, bpf_put_mem_cgroup, KF_RELEASE) 168 + 169 + BTF_ID_FLAGS(func, bpf_mem_cgroup_vm_events, KF_TRUSTED_ARGS) 170 + BTF_ID_FLAGS(func, bpf_mem_cgroup_memory_events, KF_TRUSTED_ARGS) 171 + BTF_ID_FLAGS(func, bpf_mem_cgroup_usage, KF_TRUSTED_ARGS) 172 + BTF_ID_FLAGS(func, bpf_mem_cgroup_page_state, KF_TRUSTED_ARGS) 173 + BTF_ID_FLAGS(func, bpf_mem_cgroup_flush_stats, KF_TRUSTED_ARGS | KF_SLEEPABLE) 174 + 175 + BTF_KFUNCS_END(bpf_memcontrol_kfuncs) 176 + 177 + static const struct btf_kfunc_id_set bpf_memcontrol_kfunc_set = { 178 + .owner = THIS_MODULE, 179 + .set = &bpf_memcontrol_kfuncs, 180 + }; 181 + 182 + static int __init bpf_memcontrol_init(void) 183 + { 184 + int err; 185 + 186 + err = register_btf_kfunc_id_set(BPF_PROG_TYPE_UNSPEC, 187 + &bpf_memcontrol_kfunc_set); 188 + if (err) 189 + pr_warn("error while registering bpf memcontrol kfuncs: %d", err); 190 + 191 + return err; 192 + } 193 + late_initcall(bpf_memcontrol_init);
-1
mm/memcontrol-v1.h
··· 27 27 void drain_all_stock(struct mem_cgroup *root_memcg); 28 28 29 29 unsigned long memcg_events(struct mem_cgroup *memcg, int event); 30 - unsigned long memcg_page_state_output(struct mem_cgroup *memcg, int item); 31 30 int memory_stat_show(struct seq_file *m, void *v); 32 31 33 32 void mem_cgroup_id_get_many(struct mem_cgroup *memcg, unsigned int n);
+16
mm/memcontrol.c
··· 663 663 return x; 664 664 } 665 665 666 + bool memcg_stat_item_valid(int idx) 667 + { 668 + if ((u32)idx >= MEMCG_NR_STAT) 669 + return false; 670 + 671 + return !BAD_STAT_IDX(memcg_stats_index(idx)); 672 + } 673 + 666 674 static int memcg_page_state_unit(int item); 667 675 668 676 /* ··· 866 858 return 0; 867 859 868 860 return READ_ONCE(memcg->vmstats->events[i]); 861 + } 862 + 863 + bool memcg_vm_event_item_valid(enum vm_event_item idx) 864 + { 865 + if (idx >= NR_VM_EVENT_ITEMS) 866 + return false; 867 + 868 + return !BAD_STAT_IDX(memcg_events_index(idx)); 869 869 } 870 870 871 871 #ifdef CONFIG_MEMCG_V1
+18
tools/testing/selftests/bpf/cgroup_iter_memcg.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ 3 + #ifndef __CGROUP_ITER_MEMCG_H 4 + #define __CGROUP_ITER_MEMCG_H 5 + 6 + struct memcg_query { 7 + /* some node_stat_item's */ 8 + unsigned long nr_anon_mapped; 9 + unsigned long nr_shmem; 10 + unsigned long nr_file_pages; 11 + unsigned long nr_file_mapped; 12 + /* some memcg_stat_item */ 13 + unsigned long memcg_kmem; 14 + /* some vm_event_item */ 15 + unsigned long pgfault; 16 + }; 17 + 18 + #endif /* __CGROUP_ITER_MEMCG_H */
+223
tools/testing/selftests/bpf/prog_tests/cgroup_iter_memcg.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ 3 + #include <test_progs.h> 4 + #include <bpf/libbpf.h> 5 + #include <bpf/btf.h> 6 + #include <fcntl.h> 7 + #include <sys/mman.h> 8 + #include <unistd.h> 9 + #include "cgroup_helpers.h" 10 + #include "cgroup_iter_memcg.h" 11 + #include "cgroup_iter_memcg.skel.h" 12 + 13 + static int read_stats(struct bpf_link *link) 14 + { 15 + int fd, ret = 0; 16 + ssize_t bytes; 17 + 18 + fd = bpf_iter_create(bpf_link__fd(link)); 19 + if (!ASSERT_OK_FD(fd, "bpf_iter_create")) 20 + return 1; 21 + 22 + /* 23 + * Invoke iter program by reading from its fd. We're not expecting any 24 + * data to be written by the bpf program so the result should be zero. 25 + * Results will be read directly through the custom data section 26 + * accessible through skel->data_query.memcg_query. 27 + */ 28 + bytes = read(fd, NULL, 0); 29 + if (!ASSERT_EQ(bytes, 0, "read fd")) 30 + ret = 1; 31 + 32 + close(fd); 33 + return ret; 34 + } 35 + 36 + static void test_anon(struct bpf_link *link, struct memcg_query *memcg_query) 37 + { 38 + void *map; 39 + size_t len; 40 + 41 + len = sysconf(_SC_PAGESIZE) * 1024; 42 + 43 + /* 44 + * Increase memcg anon usage by mapping and writing 45 + * to a new anon region. 46 + */ 47 + map = mmap(NULL, len, PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 48 + if (!ASSERT_NEQ(map, MAP_FAILED, "mmap anon")) 49 + return; 50 + 51 + memset(map, 1, len); 52 + 53 + if (!ASSERT_OK(read_stats(link), "read stats")) 54 + goto cleanup; 55 + 56 + ASSERT_GT(memcg_query->nr_anon_mapped, 0, "final anon mapped val"); 57 + 58 + cleanup: 59 + munmap(map, len); 60 + } 61 + 62 + static void test_file(struct bpf_link *link, struct memcg_query *memcg_query) 63 + { 64 + void *map; 65 + size_t len; 66 + char *path; 67 + int fd; 68 + 69 + len = sysconf(_SC_PAGESIZE) * 1024; 70 + path = "/tmp/test_cgroup_iter_memcg"; 71 + 72 + /* 73 + * Increase memcg file usage by creating and writing 74 + * to a mapped file. 75 + */ 76 + fd = open(path, O_CREAT | O_RDWR, 0644); 77 + if (!ASSERT_OK_FD(fd, "open fd")) 78 + return; 79 + if (!ASSERT_OK(ftruncate(fd, len), "ftruncate")) 80 + goto cleanup_fd; 81 + 82 + map = mmap(NULL, len, PROT_WRITE, MAP_SHARED, fd, 0); 83 + if (!ASSERT_NEQ(map, MAP_FAILED, "mmap file")) 84 + goto cleanup_fd; 85 + 86 + memset(map, 1, len); 87 + 88 + if (!ASSERT_OK(read_stats(link), "read stats")) 89 + goto cleanup_map; 90 + 91 + ASSERT_GT(memcg_query->nr_file_pages, 0, "final file value"); 92 + ASSERT_GT(memcg_query->nr_file_mapped, 0, "final file mapped value"); 93 + 94 + cleanup_map: 95 + munmap(map, len); 96 + cleanup_fd: 97 + close(fd); 98 + unlink(path); 99 + } 100 + 101 + static void test_shmem(struct bpf_link *link, struct memcg_query *memcg_query) 102 + { 103 + size_t len; 104 + int fd; 105 + 106 + len = sysconf(_SC_PAGESIZE) * 1024; 107 + 108 + /* 109 + * Increase memcg shmem usage by creating and writing 110 + * to a shmem object. 111 + */ 112 + fd = shm_open("/tmp_shmem", O_CREAT | O_RDWR, 0644); 113 + if (!ASSERT_OK_FD(fd, "shm_open")) 114 + return; 115 + 116 + if (!ASSERT_OK(fallocate(fd, 0, 0, len), "fallocate")) 117 + goto cleanup; 118 + 119 + if (!ASSERT_OK(read_stats(link), "read stats")) 120 + goto cleanup; 121 + 122 + ASSERT_GT(memcg_query->nr_shmem, 0, "final shmem value"); 123 + 124 + cleanup: 125 + close(fd); 126 + shm_unlink("/tmp_shmem"); 127 + } 128 + 129 + #define NR_PIPES 64 130 + static void test_kmem(struct bpf_link *link, struct memcg_query *memcg_query) 131 + { 132 + int fds[NR_PIPES][2], i; 133 + 134 + /* 135 + * Increase kmem value by creating pipes which will allocate some 136 + * kernel buffers. 137 + */ 138 + for (i = 0; i < NR_PIPES; i++) { 139 + if (!ASSERT_OK(pipe(fds[i]), "pipe")) 140 + goto cleanup; 141 + } 142 + 143 + if (!ASSERT_OK(read_stats(link), "read stats")) 144 + goto cleanup; 145 + 146 + ASSERT_GT(memcg_query->memcg_kmem, 0, "kmem value"); 147 + 148 + cleanup: 149 + for (i = i - 1; i >= 0; i--) { 150 + close(fds[i][0]); 151 + close(fds[i][1]); 152 + } 153 + } 154 + 155 + static void test_pgfault(struct bpf_link *link, struct memcg_query *memcg_query) 156 + { 157 + void *map; 158 + size_t len; 159 + 160 + len = sysconf(_SC_PAGESIZE) * 1024; 161 + 162 + /* Create region to use for triggering a page fault. */ 163 + map = mmap(NULL, len, PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 164 + if (!ASSERT_NEQ(map, MAP_FAILED, "mmap anon")) 165 + return; 166 + 167 + /* Trigger page fault. */ 168 + memset(map, 1, len); 169 + 170 + if (!ASSERT_OK(read_stats(link), "read stats")) 171 + goto cleanup; 172 + 173 + ASSERT_GT(memcg_query->pgfault, 0, "final pgfault val"); 174 + 175 + cleanup: 176 + munmap(map, len); 177 + } 178 + 179 + void test_cgroup_iter_memcg(void) 180 + { 181 + char *cgroup_rel_path = "/cgroup_iter_memcg_test"; 182 + struct cgroup_iter_memcg *skel; 183 + struct bpf_link *link; 184 + int cgroup_fd; 185 + 186 + cgroup_fd = cgroup_setup_and_join(cgroup_rel_path); 187 + if (!ASSERT_OK_FD(cgroup_fd, "cgroup_setup_and_join")) 188 + return; 189 + 190 + skel = cgroup_iter_memcg__open_and_load(); 191 + if (!ASSERT_OK_PTR(skel, "cgroup_iter_memcg__open_and_load")) 192 + goto cleanup_cgroup_fd; 193 + 194 + DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts); 195 + union bpf_iter_link_info linfo = { 196 + .cgroup.cgroup_fd = cgroup_fd, 197 + .cgroup.order = BPF_CGROUP_ITER_SELF_ONLY, 198 + }; 199 + opts.link_info = &linfo; 200 + opts.link_info_len = sizeof(linfo); 201 + 202 + link = bpf_program__attach_iter(skel->progs.cgroup_memcg_query, &opts); 203 + if (!ASSERT_OK_PTR(link, "bpf_program__attach_iter")) 204 + goto cleanup_skel; 205 + 206 + if (test__start_subtest("cgroup_iter_memcg__anon")) 207 + test_anon(link, &skel->data_query->memcg_query); 208 + if (test__start_subtest("cgroup_iter_memcg__shmem")) 209 + test_shmem(link, &skel->data_query->memcg_query); 210 + if (test__start_subtest("cgroup_iter_memcg__file")) 211 + test_file(link, &skel->data_query->memcg_query); 212 + if (test__start_subtest("cgroup_iter_memcg__kmem")) 213 + test_kmem(link, &skel->data_query->memcg_query); 214 + if (test__start_subtest("cgroup_iter_memcg__pgfault")) 215 + test_pgfault(link, &skel->data_query->memcg_query); 216 + 217 + bpf_link__destroy(link); 218 + cleanup_skel: 219 + cgroup_iter_memcg__destroy(skel); 220 + cleanup_cgroup_fd: 221 + close(cgroup_fd); 222 + cleanup_cgroup_environment(); 223 + }
+39
tools/testing/selftests/bpf/progs/cgroup_iter_memcg.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ 3 + #include <vmlinux.h> 4 + #include <bpf/bpf_core_read.h> 5 + #include "cgroup_iter_memcg.h" 6 + 7 + char _license[] SEC("license") = "GPL"; 8 + 9 + /* The latest values read are stored here. */ 10 + struct memcg_query memcg_query SEC(".data.query"); 11 + 12 + SEC("iter.s/cgroup") 13 + int cgroup_memcg_query(struct bpf_iter__cgroup *ctx) 14 + { 15 + struct cgroup *cgrp = ctx->cgroup; 16 + struct cgroup_subsys_state *css; 17 + struct mem_cgroup *memcg; 18 + 19 + if (!cgrp) 20 + return 1; 21 + 22 + css = &cgrp->self; 23 + memcg = bpf_get_mem_cgroup(css); 24 + if (!memcg) 25 + return 1; 26 + 27 + bpf_mem_cgroup_flush_stats(memcg); 28 + 29 + memcg_query.nr_anon_mapped = bpf_mem_cgroup_page_state(memcg, NR_ANON_MAPPED); 30 + memcg_query.nr_shmem = bpf_mem_cgroup_page_state(memcg, NR_SHMEM); 31 + memcg_query.nr_file_pages = bpf_mem_cgroup_page_state(memcg, NR_FILE_PAGES); 32 + memcg_query.nr_file_mapped = bpf_mem_cgroup_page_state(memcg, NR_FILE_MAPPED); 33 + memcg_query.memcg_kmem = bpf_mem_cgroup_page_state(memcg, MEMCG_KMEM); 34 + memcg_query.pgfault = bpf_mem_cgroup_vm_events(memcg, PGFAULT); 35 + 36 + bpf_put_mem_cgroup(memcg); 37 + 38 + return 0; 39 + }