// SPDX-License-Identifier: GPL-2.0-or-later /* * Memory Controller-related BPF kfuncs and auxiliary code * * Author: Roman Gushchin */ #include #include __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);