From: Hui Zhu Add bpf_proactive_reclaim(), a sleepable kfunc which performs one proactive reclaim pass on a given memory cgroup, similar to a write to memory.reclaim but without retrying until the target is reached. The kfunc refuses to reclaim if the calling task is already in a reclaim context, as a nested reclaim would corrupt the outer reclaim state. Signed-off-by: Hui Zhu --- mm/bpf_memcontrol.c | 46 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/mm/bpf_memcontrol.c b/mm/bpf_memcontrol.c index 716df49d7647..297ff7f05042 100644 --- a/mm/bpf_memcontrol.c +++ b/mm/bpf_memcontrol.c @@ -6,6 +6,7 @@ */ #include +#include #include __bpf_kfunc_start_defs(); @@ -159,6 +160,49 @@ __bpf_kfunc void bpf_mem_cgroup_flush_stats(struct mem_cgroup *memcg) mem_cgroup_flush_stats(memcg); } +/* + * Reclaim must not recurse: try_to_free_mem_cgroup_pages() overwrites + * current->reclaim_state, so a nested call would corrupt the outer + * reclaim state. Reclaim windows are marked with PF_MEMALLOC; + * reclaim_state is also checked because it is installed slightly + * before PF_MEMALLOC. + */ +static bool bpf_in_reclaim_context(void) +{ + return (current->flags & PF_MEMALLOC) || current->reclaim_state; +} + +/** + * bpf_proactive_reclaim - proactively reclaim memory from a memory + * cgroup + * @memcg: the target memory cgroup to reclaim from + * @size: the amount of memory to reclaim, in bytes + * + * Trigger one proactive reclaim pass on @memcg, similar to a write to + * memory.reclaim, but without retrying until @size is reached. + * Must not be called with a filesystem lock held: the reclaim path + * may deadlock on it via filesystem shrinkers. + * + * Return: The amount of memory reclaimed, in bytes, or 0 if @size is + * smaller than a page or the task is already in a reclaim context. + */ +__bpf_kfunc unsigned long bpf_proactive_reclaim(struct mem_cgroup *memcg, + unsigned long size) +{ + unsigned long nr_reclaimed; + + if (size < PAGE_SIZE || unlikely(bpf_in_reclaim_context())) + return 0; + + nr_reclaimed = try_to_free_mem_cgroup_pages(memcg, size / PAGE_SIZE, + GFP_KERNEL, + MEMCG_RECLAIM_MAY_SWAP | + MEMCG_RECLAIM_PROACTIVE, + NULL); + + return nr_reclaimed * PAGE_SIZE; +} + __bpf_kfunc_end_defs(); BTF_KFUNCS_START(bpf_memcontrol_kfuncs) @@ -172,6 +216,8 @@ 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_ID_FLAGS(func, bpf_proactive_reclaim, KF_SLEEPABLE) + BTF_KFUNCS_END(bpf_memcontrol_kfuncs) static const struct btf_kfunc_id_set bpf_memcontrol_kfunc_set = { -- 2.53.0