SCX eBPF macro MEMBER_VPTR
The MEMBER_VPTR
macro obtains a verified pointer to a struct or array member
Definition
#define MEMBER_VPTR(base, member) (typeof((base) member) *) \
({ \
u64 __base = (u64)&(base); \
u64 __addr = (u64)&((base) member) - __base; \
_Static_assert(sizeof(base) >= sizeof((base) member), \
"@base is smaller than @member, is @base a pointer?"); \
asm volatile ( \
"if %0 <= %[max] goto +2\n" \
"%0 = 0\n" \
"goto +1\n" \
"%0 += %1\n" \
: "+r"(__addr) \
: "r"(__base), \
[max]"i"(sizeof(base) - sizeof((base) member))); \
__addr; \
})
Usage
The verifier often gets confused by the instruction sequence the compiler generates for indexing struct fields or arrays. This macro forces the compiler to generate a code sequence which first calculates the byte offset, checks it against the struct or array size and add that byte offset to generate the pointer to the member to help the verifier.
Ideally, we want to abort if the calculated offset is out-of-bounds. However, BPF currently doesn't support abort, so evaluate to NULL
instead. The caller must check for NULL
and take appropriate action to appease the verifier. To avoid confusing the verifier, it's best to check for NULL
and dereference immediately.
vptr = MEMBER_VPTR(my_array, [i][j]);
if (!vptr)
return error;
*vptr = new_value;
sizeof(base)
should encompass the memory area to be accessed and thus can't be a pointer to the area. Use MEMBER_VPTR(*ptr, .member)
instead of MEMBER_VPTR(ptr, ->member)
.
Parameters
- @base: struct or array to index
- @member: dereferenced member (e.g.
.field
,[idx0][idx1]
,.field[idx0]
...)
Example
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates.
* Copyright (c) 2022 Tejun Heo <tj@kernel.org>
* Copyright (c) 2022 David Vernet <dvernet@meta.com>
*/
/*
* Print out the online and possible CPU map using bpf_printk() as a
* demonstration of using the cpumask kfuncs and ops.cpu_on/offline().
*/
static void print_cpus(void)
{
const struct cpumask *possible, *online;
s32 cpu;
char buf[128] = "", *p;
int idx;
possible = scx_bpf_get_possible_cpumask();
online = scx_bpf_get_online_cpumask();
idx = 0;
bpf_for(cpu, 0, scx_bpf_nr_cpu_ids()) {
if (!(p = MEMBER_VPTR(buf, [idx++])))
break;
if (bpf_cpumask_test_cpu(cpu, online))
*p++ = 'O';
else if (bpf_cpumask_test_cpu(cpu, possible))
*p++ = 'X';
else
*p++ = ' ';
if ((cpu & 7) == 7) {
if (!(p = MEMBER_VPTR(buf, [idx++])))
break;
*p++ = '|';
}
}
buf[sizeof(buf) - 1] = '\0';
scx_bpf_put_cpumask(online);
scx_bpf_put_cpumask(possible);
bpf_printk("CPUS: |%s", buf);
}
void BPF_STRUCT_OPS(qmap_cpu_online, s32 cpu)
{
bpf_printk("CPU %d coming online", cpu);
/* @cpu is already online at this point */
print_cpus();
}