| // SPDX-License-Identifier: GPL-2.0-or-later |
| /* |
| * Memory Controller-related BPF kfuncs and auxiliary code |
| * |
| * Author: Roman Gushchin <roman.gushchin@linux.dev> |
| */ |
| |
| #include <linux/memcontrol.h> |
| #include <linux/bpf.h> |
| |
| __bpf_kfunc_start_defs(); |
| |
| /** |
| * bpf_get_root_mem_cgroup - Returns a pointer to the root memory cgroup |
| * |
| * The function has KF_ACQUIRE semantics, even though the root memory |
| * cgroup is never destroyed after being created and doesn't require |
| * reference counting. And it's perfectly safe to pass it to |
| * bpf_put_mem_cgroup() |
| * |
| * Return: A pointer to the root memory cgroup. |
| */ |
| __bpf_kfunc struct mem_cgroup *bpf_get_root_mem_cgroup(void) |
| { |
| if (mem_cgroup_disabled()) |
| return NULL; |
| |
| /* css_get() is not needed */ |
| return root_mem_cgroup; |
| } |
| |
| /** |
| * bpf_get_mem_cgroup - Get a reference to a memory cgroup |
| * @css: pointer to the css structure |
| * |
| * It's fine to pass a css which belongs to any cgroup controller, |
| * e.g. unified hierarchy's main css. |
| * |
| * Implements KF_ACQUIRE semantics. |
| * |
| * Return: A pointer to a mem_cgroup structure after bumping |
| * the corresponding css's reference counter. |
| */ |
| __bpf_kfunc struct mem_cgroup * |
| bpf_get_mem_cgroup(struct cgroup_subsys_state *css) |
| { |
| struct mem_cgroup *memcg = NULL; |
| bool rcu_unlock = false; |
| |
| if (mem_cgroup_disabled() || !root_mem_cgroup) |
| return NULL; |
| |
| if (root_mem_cgroup->css.ss != css->ss) { |
| struct cgroup *cgroup = css->cgroup; |
| int ssid = root_mem_cgroup->css.ss->id; |
| |
| rcu_read_lock(); |
| rcu_unlock = true; |
| css = rcu_dereference_raw(cgroup->subsys[ssid]); |
| } |
| |
| if (css && css_tryget(css)) |
| memcg = container_of(css, struct mem_cgroup, css); |
| |
| if (rcu_unlock) |
| rcu_read_unlock(); |
| |
| return memcg; |
| } |
| |
| /** |
| * bpf_put_mem_cgroup - Put a reference to a memory cgroup |
| * @memcg: memory cgroup to release |
| * |
| * Releases a previously acquired memcg reference. |
| * Implements KF_RELEASE semantics. |
| */ |
| __bpf_kfunc void bpf_put_mem_cgroup(struct mem_cgroup *memcg) |
| { |
| css_put(&memcg->css); |
| } |
| |
| /** |
| * bpf_mem_cgroup_vm_events - Read memory cgroup's vm event counter |
| * @memcg: memory cgroup |
| * @event: event id |
| * |
| * Allows to read memory cgroup event counters. |
| * |
| * Return: The current value of the corresponding events counter. |
| */ |
| __bpf_kfunc unsigned long bpf_mem_cgroup_vm_events(struct mem_cgroup *memcg, |
| enum vm_event_item event) |
| { |
| if (unlikely(!memcg_vm_event_item_valid(event))) |
| return (unsigned long)-1; |
| |
| return memcg_events(memcg, event); |
| } |
| |
| /** |
| * bpf_mem_cgroup_usage - Read memory cgroup's usage |
| * @memcg: memory cgroup |
| * |
| * Please, note that the root memory cgroup it special and is exempt |
| * from the memory accounting. The returned value is a sum of sub-cgroup's |
| * usages and it not reflecting the size of the root memory cgroup itself. |
| * If you need to get an approximation, you can use root level statistics: |
| * e.g. NR_FILE_PAGES + NR_ANON_MAPPED. |
| * |
| * Return: The current memory cgroup size in bytes. |
| */ |
| __bpf_kfunc unsigned long bpf_mem_cgroup_usage(struct mem_cgroup *memcg) |
| { |
| return page_counter_read(&memcg->memory) * PAGE_SIZE; |
| } |
| |
| /** |
| * bpf_mem_cgroup_memory_events - Read memory cgroup's memory event value |
| * @memcg: memory cgroup |
| * @event: memory event id |
| * |
| * Return: The current value of the memory event counter. |
| */ |
| __bpf_kfunc unsigned long bpf_mem_cgroup_memory_events(struct mem_cgroup *memcg, |
| enum memcg_memory_event event) |
| { |
| if (unlikely(event >= MEMCG_NR_MEMORY_EVENTS)) |
| return (unsigned long)-1; |
| |
| return atomic_long_read(&memcg->memory_events[event]); |
| } |
| |
| /** |
| * bpf_mem_cgroup_page_state - Read memory cgroup's page state counter |
| * @memcg: memory cgroup |
| * @idx: counter idx |
| * |
| * Allows to read memory cgroup statistics. The output is in bytes. |
| * |
| * Return: The value of the page state counter in bytes. |
| */ |
| __bpf_kfunc unsigned long bpf_mem_cgroup_page_state(struct mem_cgroup *memcg, int idx) |
| { |
| if (unlikely(!memcg_stat_item_valid(idx))) |
| return (unsigned long)-1; |
| |
| return memcg_page_state_output(memcg, idx); |
| } |
| |
| /** |
| * bpf_mem_cgroup_flush_stats - Flush memory cgroup's statistics |
| * @memcg: memory cgroup |
| * |
| * Propagate memory cgroup's statistics up the cgroup tree. |
| */ |
| __bpf_kfunc void bpf_mem_cgroup_flush_stats(struct mem_cgroup *memcg) |
| { |
| mem_cgroup_flush_stats(memcg); |
| } |
| |
| __bpf_kfunc_end_defs(); |
| |
| BTF_KFUNCS_START(bpf_memcontrol_kfuncs) |
| BTF_ID_FLAGS(func, bpf_get_root_mem_cgroup, KF_ACQUIRE | KF_RET_NULL) |
| BTF_ID_FLAGS(func, bpf_get_mem_cgroup, KF_ACQUIRE | KF_RET_NULL | KF_RCU) |
| BTF_ID_FLAGS(func, bpf_put_mem_cgroup, KF_RELEASE) |
| |
| BTF_ID_FLAGS(func, bpf_mem_cgroup_vm_events) |
| BTF_ID_FLAGS(func, bpf_mem_cgroup_memory_events) |
| BTF_ID_FLAGS(func, bpf_mem_cgroup_usage) |
| BTF_ID_FLAGS(func, bpf_mem_cgroup_page_state) |
| BTF_ID_FLAGS(func, bpf_mem_cgroup_flush_stats, KF_SLEEPABLE) |
| |
| BTF_KFUNCS_END(bpf_memcontrol_kfuncs) |
| |
| static const struct btf_kfunc_id_set bpf_memcontrol_kfunc_set = { |
| .owner = THIS_MODULE, |
| .set = &bpf_memcontrol_kfuncs, |
| }; |
| |
| static int __init bpf_memcontrol_init(void) |
| { |
| int err; |
| |
| err = register_btf_kfunc_id_set(BPF_PROG_TYPE_UNSPEC, |
| &bpf_memcontrol_kfunc_set); |
| if (err) |
| pr_warn("error while registering bpf memcontrol kfuncs: %d", err); |
| |
| return err; |
| } |
| late_initcall(bpf_memcontrol_init); |