Commit 911c0359 authored by Martin KaFai Lau's avatar Martin KaFai Lau
Browse files

Merge branch 'allow-struct_ops-to-create-map-id-to'

Amery Hung says:

====================
This patchset allows struct_ops implementors to get map id from kdata in
reg(), unreg() and update() so that they create an id to struct_ops
instance mapping. This in turn allows struct_ops kfuncs to refer to the
calling instance without passing a pointer to the struct_ops. The selftest
provides an end-to-end example.

Some struct_ops users extend themselves with other bpf programs, which
also need to call struct_ops kfuncs. For example, scx_layered uses
syscall bpf programs as a scx_layered specific control plane and uses
tracing programs to get additional information for scheduling [0].
The kfuncs may need to refer to the struct_ops instance and perform
jobs accordingly. To allow calling struct_ops kfuncs referring to
specific instances from different program types and context (e.g.,
struct_ops, tracing, async callbacks), the traditional way is to pass
the struct_ops pointer to kfuncs.

This patchset provides an alternative way, through a combination of
bpf map id and global variable. First, a struct_ops implementor will
use the map id of the struct_ops map as the id of an instance. Then,
it needs to maintain an id to instance mapping: inserting a new mapping
during reg() and removing it during unreg(). The map id can be acquired
by calling bpf_struct_ops_id().

v1 -> v2
    Add bpf_struct_ops_id() instead of using bpf_struct_ops_get()

[0] https://github.com/sched-ext/scx/blob/main/scheds/rust/scx_layered/src/bpf/main.bpf.c
====================

Link: https://patch.msgid.link/20250806162540.681679-1-ameryhung@gmail.com


Signed-off-by: default avatarMartin KaFai Lau <martin.lau@kernel.org>
parents f3af62b6 ba7000f1
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1985,6 +1985,7 @@ static inline void bpf_module_put(const void *data, struct module *owner)
		module_put(owner);
}
int bpf_struct_ops_link_create(union bpf_attr *attr);
u32 bpf_struct_ops_id(const void *kdata);

#ifdef CONFIG_NET
/* Define it here to avoid the use of forward declaration */
+12 −0
Original line number Diff line number Diff line
@@ -1174,6 +1174,18 @@ void bpf_struct_ops_put(const void *kdata)
	bpf_map_put(&st_map->map);
}

u32 bpf_struct_ops_id(const void *kdata)
{
	struct bpf_struct_ops_value *kvalue;
	struct bpf_struct_ops_map *st_map;

	kvalue = container_of(kdata, struct bpf_struct_ops_value, data);
	st_map = container_of(kvalue, struct bpf_struct_ops_map, kvalue);

	return st_map->map.id;
}
EXPORT_SYMBOL_GPL(bpf_struct_ops_id);

static bool bpf_struct_ops_valid_to_reg(struct bpf_map *map)
{
	struct bpf_struct_ops_map *st_map = (struct bpf_struct_ops_map *)map;
+74 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0

#include <test_progs.h>
#include "struct_ops_id_ops_mapping1.skel.h"
#include "struct_ops_id_ops_mapping2.skel.h"

static void test_st_ops_id_ops_mapping(void)
{
	struct struct_ops_id_ops_mapping1 *skel1 = NULL;
	struct struct_ops_id_ops_mapping2 *skel2 = NULL;
	struct bpf_map_info info = {};
	__u32 len = sizeof(info);
	int err, pid, prog1_fd, prog2_fd;

	skel1 = struct_ops_id_ops_mapping1__open_and_load();
	if (!ASSERT_OK_PTR(skel1, "struct_ops_id_ops_mapping1__open"))
		goto out;

	skel2 = struct_ops_id_ops_mapping2__open_and_load();
	if (!ASSERT_OK_PTR(skel2, "struct_ops_id_ops_mapping2__open"))
		goto out;

	err = bpf_map_get_info_by_fd(bpf_map__fd(skel1->maps.st_ops_map),
				     &info, &len);
	if (!ASSERT_OK(err, "bpf_map_get_info_by_fd"))
		goto out;

	skel1->bss->st_ops_id = info.id;

	err = bpf_map_get_info_by_fd(bpf_map__fd(skel2->maps.st_ops_map),
				     &info, &len);
	if (!ASSERT_OK(err, "bpf_map_get_info_by_fd"))
		goto out;

	skel2->bss->st_ops_id = info.id;

	err = struct_ops_id_ops_mapping1__attach(skel1);
	if (!ASSERT_OK(err, "struct_ops_id_ops_mapping1__attach"))
		goto out;

	err = struct_ops_id_ops_mapping2__attach(skel2);
	if (!ASSERT_OK(err, "struct_ops_id_ops_mapping2__attach"))
		goto out;

	/* run tracing prog that calls .test_1 and checks return */
	pid = getpid();
	skel1->bss->test_pid = pid;
	skel2->bss->test_pid = pid;
	sys_gettid();
	skel1->bss->test_pid = 0;
	skel2->bss->test_pid = 0;

	/* run syscall_prog that calls .test_1 and checks return */
	prog1_fd = bpf_program__fd(skel1->progs.syscall_prog);
	err = bpf_prog_test_run_opts(prog1_fd, NULL);
	ASSERT_OK(err, "bpf_prog_test_run_opts");

	prog2_fd = bpf_program__fd(skel2->progs.syscall_prog);
	err = bpf_prog_test_run_opts(prog2_fd, NULL);
	ASSERT_OK(err, "bpf_prog_test_run_opts");

	ASSERT_EQ(skel1->bss->test_err, 0, "skel1->bss->test_err");
	ASSERT_EQ(skel2->bss->test_err, 0, "skel2->bss->test_err");

out:
	struct_ops_id_ops_mapping1__destroy(skel1);
	struct_ops_id_ops_mapping2__destroy(skel2);
}

void test_struct_ops_id_ops_mapping(void)
{
	if (test__start_subtest("st_ops_id_ops_mapping"))
		test_st_ops_id_ops_mapping();
}
+59 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0

#include <vmlinux.h>
#include <bpf/bpf_tracing.h>
#include "bpf_misc.h"
#include "../test_kmods/bpf_testmod.h"
#include "../test_kmods/bpf_testmod_kfunc.h"

char _license[] SEC("license") = "GPL";

#define bpf_kfunc_multi_st_ops_test_1(args) bpf_kfunc_multi_st_ops_test_1(args, st_ops_id)
int st_ops_id;

int test_pid;
int test_err;

#define MAP1_MAGIC 1234

SEC("struct_ops")
int BPF_PROG(test_1, struct st_ops_args *args)
{
	return MAP1_MAGIC;
}

SEC("tp_btf/sys_enter")
int BPF_PROG(sys_enter, struct pt_regs *regs, long id)
{
	struct st_ops_args args = {};
	struct task_struct *task;
	int ret;

	task = bpf_get_current_task_btf();
	if (!test_pid || task->pid != test_pid)
		return 0;

	ret = bpf_kfunc_multi_st_ops_test_1(&args);
	if (ret != MAP1_MAGIC)
		test_err++;

	return 0;
}

SEC("syscall")
int syscall_prog(void *ctx)
{
	struct st_ops_args args = {};
	int ret;

	ret = bpf_kfunc_multi_st_ops_test_1(&args);
	if (ret != MAP1_MAGIC)
		test_err++;

	return 0;
}

SEC(".struct_ops.link")
struct bpf_testmod_multi_st_ops st_ops_map = {
	.test_1 = (void *)test_1,
};
+59 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0

#include <vmlinux.h>
#include <bpf/bpf_tracing.h>
#include "bpf_misc.h"
#include "../test_kmods/bpf_testmod.h"
#include "../test_kmods/bpf_testmod_kfunc.h"

char _license[] SEC("license") = "GPL";

#define bpf_kfunc_multi_st_ops_test_1(args) bpf_kfunc_multi_st_ops_test_1(args, st_ops_id)
int st_ops_id;

int test_pid;
int test_err;

#define MAP2_MAGIC 4567

SEC("struct_ops")
int BPF_PROG(test_1, struct st_ops_args *args)
{
	return MAP2_MAGIC;
}

SEC("tp_btf/sys_enter")
int BPF_PROG(sys_enter, struct pt_regs *regs, long id)
{
	struct st_ops_args args = {};
	struct task_struct *task;
	int ret;

	task = bpf_get_current_task_btf();
	if (!test_pid || task->pid != test_pid)
		return 0;

	ret = bpf_kfunc_multi_st_ops_test_1(&args);
	if (ret != MAP2_MAGIC)
		test_err++;

	return 0;
}

SEC("syscall")
int syscall_prog(void *ctx)
{
	struct st_ops_args args = {};
	int ret;

	ret = bpf_kfunc_multi_st_ops_test_1(&args);
	if (ret != MAP2_MAGIC)
		test_err++;

	return 0;
}

SEC(".struct_ops.link")
struct bpf_testmod_multi_st_ops st_ops_map = {
	.test_1 = (void *)test_1,
};
Loading