Skip to content

Commit 216eaa7

Browse files
committed
libbpf: Auto-bump RLIMIT_MEMLOCK if kernel needs it for BPF
The need to increase RLIMIT_MEMLOCK to do anything useful with BPF is one of the first extremely frustrating gotchas that all new BPF users go through and in some cases have to learn it a very hard way. Luckily, starting with upstream Linux kernel version 5.11, BPF subsystem dropped the dependency on memlock and uses memcg-based memory accounting instead. Unfortunately, detecting memcg-based BPF memory accounting is far from trivial (as can be evidenced by this patch), so in practice most BPF applications still do unconditional RLIMIT_MEMLOCK increase. As we move towards libbpf 1.0, it would be good to allow users to forget about RLIMIT_MEMLOCK vs memcg and let libbpf do the sensible adjustment automatically. This patch paves the way forward in this matter. Libbpf will do feature detection of memcg-based accounting, and if detected, will do nothing. But if the kernel is too old, just like BCC, libbpf will automatically increase RLIMIT_MEMLOCK on behalf of user application ([0]). As this is technically a breaking change, during the transition period applications have to opt into libbpf 1.0 mode by setting LIBBPF_STRICT_AUTO_RLIMIT_MEMLOCK bit when calling libbpf_set_strict_mode(). Libbpf allows to control the exact amount of set RLIMIT_MEMLOCK limit with libbpf_set_memlock_rlim_max() API. Passing 0 will make libbpf do nothing with RLIMIT_MEMLOCK. libbpf_set_memlock_rlim_max() has to be called before the first bpf_prog_load(), bpf_btf_load(), or bpf_object__load() call, otherwise it has no effect and will return -EBUSY. [0] Closes: #369 Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Link: https://lore.kernel.org/bpf/20211214195904.1785155-2-andrii@kernel.org
1 parent a4e725f commit 216eaa7

File tree

6 files changed

+143
-39
lines changed

6 files changed

+143
-39
lines changed

src/bpf.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828
#include <asm/unistd.h>
2929
#include <errno.h>
3030
#include <linux/bpf.h>
31+
#include <linux/filter.h>
3132
#include <limits.h>
33+
#include <sys/resource.h>
3234
#include "bpf.h"
3335
#include "libbpf.h"
3436
#include "libbpf_internal.h"
@@ -94,6 +96,77 @@ static inline int sys_bpf_prog_load(union bpf_attr *attr, unsigned int size, int
9496
return fd;
9597
}
9698

99+
/* Probe whether kernel switched from memlock-based (RLIMIT_MEMLOCK) to
100+
* memcg-based memory accounting for BPF maps and progs. This was done in [0].
101+
* We use the support for bpf_ktime_get_coarse_ns() helper, which was added in
102+
* the same 5.11 Linux release ([1]), to detect memcg-based accounting for BPF.
103+
*
104+
* [0] https://lore.kernel.org/bpf/20201201215900.3569844-1-guro@fb.com/
105+
* [1] d05512618056 ("bpf: Add bpf_ktime_get_coarse_ns helper")
106+
*/
107+
int probe_memcg_account(void)
108+
{
109+
const size_t prog_load_attr_sz = offsetofend(union bpf_attr, attach_btf_obj_fd);
110+
struct bpf_insn insns[] = {
111+
BPF_EMIT_CALL(BPF_FUNC_ktime_get_coarse_ns),
112+
BPF_EXIT_INSN(),
113+
};
114+
size_t insn_cnt = sizeof(insns) / sizeof(insns[0]);
115+
union bpf_attr attr;
116+
int prog_fd;
117+
118+
/* attempt loading freplace trying to use custom BTF */
119+
memset(&attr, 0, prog_load_attr_sz);
120+
attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER;
121+
attr.insns = ptr_to_u64(insns);
122+
attr.insn_cnt = insn_cnt;
123+
attr.license = ptr_to_u64("GPL");
124+
125+
prog_fd = sys_bpf_fd(BPF_PROG_LOAD, &attr, prog_load_attr_sz);
126+
if (prog_fd >= 0) {
127+
close(prog_fd);
128+
return 1;
129+
}
130+
return 0;
131+
}
132+
133+
static bool memlock_bumped;
134+
static rlim_t memlock_rlim = RLIM_INFINITY;
135+
136+
int libbpf_set_memlock_rlim(size_t memlock_bytes)
137+
{
138+
if (memlock_bumped)
139+
return libbpf_err(-EBUSY);
140+
141+
memlock_rlim = memlock_bytes;
142+
return 0;
143+
}
144+
145+
int bump_rlimit_memlock(void)
146+
{
147+
struct rlimit rlim;
148+
149+
/* this the default in libbpf 1.0, but for now user has to opt-in explicitly */
150+
if (!(libbpf_mode & LIBBPF_STRICT_AUTO_RLIMIT_MEMLOCK))
151+
return 0;
152+
153+
/* if kernel supports memcg-based accounting, skip bumping RLIMIT_MEMLOCK */
154+
if (memlock_bumped || kernel_supports(NULL, FEAT_MEMCG_ACCOUNT))
155+
return 0;
156+
157+
memlock_bumped = true;
158+
159+
/* zero memlock_rlim_max disables auto-bumping RLIMIT_MEMLOCK */
160+
if (memlock_rlim == 0)
161+
return 0;
162+
163+
rlim.rlim_cur = rlim.rlim_max = memlock_rlim;
164+
if (setrlimit(RLIMIT_MEMLOCK, &rlim))
165+
return -errno;
166+
167+
return 0;
168+
}
169+
97170
int bpf_map_create(enum bpf_map_type map_type,
98171
const char *map_name,
99172
__u32 key_size,
@@ -105,6 +178,8 @@ int bpf_map_create(enum bpf_map_type map_type,
105178
union bpf_attr attr;
106179
int fd;
107180

181+
bump_rlimit_memlock();
182+
108183
memset(&attr, 0, attr_sz);
109184

110185
if (!OPTS_VALID(opts, bpf_map_create_opts))
@@ -251,6 +326,8 @@ int bpf_prog_load_v0_6_0(enum bpf_prog_type prog_type,
251326
union bpf_attr attr;
252327
char *log_buf;
253328

329+
bump_rlimit_memlock();
330+
254331
if (!OPTS_VALID(opts, bpf_prog_load_opts))
255332
return libbpf_err(-EINVAL);
256333

@@ -456,6 +533,8 @@ int bpf_verify_program(enum bpf_prog_type type, const struct bpf_insn *insns,
456533
union bpf_attr attr;
457534
int fd;
458535

536+
bump_rlimit_memlock();
537+
459538
memset(&attr, 0, sizeof(attr));
460539
attr.prog_type = type;
461540
attr.insn_cnt = (__u32)insns_cnt;
@@ -1056,6 +1135,8 @@ int bpf_btf_load(const void *btf_data, size_t btf_size, const struct bpf_btf_loa
10561135
__u32 log_level;
10571136
int fd;
10581137

1138+
bump_rlimit_memlock();
1139+
10591140
memset(&attr, 0, attr_sz);
10601141

10611142
if (!OPTS_VALID(opts, bpf_btf_load_opts))

src/bpf.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
extern "C" {
3636
#endif
3737

38+
int libbpf_set_memlock_rlim(size_t memlock_bytes);
39+
3840
struct bpf_map_create_opts {
3941
size_t sz; /* size of this struct for forward/backward compatibility */
4042

src/libbpf.c

Lines changed: 9 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -187,42 +187,6 @@ const char *libbpf_version_string(void)
187187
#undef __S
188188
}
189189

190-
enum kern_feature_id {
191-
/* v4.14: kernel support for program & map names. */
192-
FEAT_PROG_NAME,
193-
/* v5.2: kernel support for global data sections. */
194-
FEAT_GLOBAL_DATA,
195-
/* BTF support */
196-
FEAT_BTF,
197-
/* BTF_KIND_FUNC and BTF_KIND_FUNC_PROTO support */
198-
FEAT_BTF_FUNC,
199-
/* BTF_KIND_VAR and BTF_KIND_DATASEC support */
200-
FEAT_BTF_DATASEC,
201-
/* BTF_FUNC_GLOBAL is supported */
202-
FEAT_BTF_GLOBAL_FUNC,
203-
/* BPF_F_MMAPABLE is supported for arrays */
204-
FEAT_ARRAY_MMAP,
205-
/* kernel support for expected_attach_type in BPF_PROG_LOAD */
206-
FEAT_EXP_ATTACH_TYPE,
207-
/* bpf_probe_read_{kernel,user}[_str] helpers */
208-
FEAT_PROBE_READ_KERN,
209-
/* BPF_PROG_BIND_MAP is supported */
210-
FEAT_PROG_BIND_MAP,
211-
/* Kernel support for module BTFs */
212-
FEAT_MODULE_BTF,
213-
/* BTF_KIND_FLOAT support */
214-
FEAT_BTF_FLOAT,
215-
/* BPF perf link support */
216-
FEAT_PERF_LINK,
217-
/* BTF_KIND_DECL_TAG support */
218-
FEAT_BTF_DECL_TAG,
219-
/* BTF_KIND_TYPE_TAG support */
220-
FEAT_BTF_TYPE_TAG,
221-
__FEAT_CNT,
222-
};
223-
224-
static bool kernel_supports(const struct bpf_object *obj, enum kern_feature_id feat_id);
225-
226190
enum reloc_type {
227191
RELO_LD64,
228192
RELO_CALL,
@@ -4352,6 +4316,10 @@ bpf_object__probe_loading(struct bpf_object *obj)
43524316
if (obj->gen_loader)
43534317
return 0;
43544318

4319+
ret = bump_rlimit_memlock();
4320+
if (ret)
4321+
pr_warn("Failed to bump RLIMIT_MEMLOCK (err = %d), you might need to do it explicitly!\n", ret);
4322+
43554323
/* make sure basic loading works */
43564324
ret = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL", insns, insn_cnt, NULL);
43574325
if (ret < 0)
@@ -4718,14 +4686,17 @@ static struct kern_feature_desc {
47184686
[FEAT_BTF_TYPE_TAG] = {
47194687
"BTF_KIND_TYPE_TAG support", probe_kern_btf_type_tag,
47204688
},
4689+
[FEAT_MEMCG_ACCOUNT] = {
4690+
"memcg-based memory accounting", probe_memcg_account,
4691+
},
47214692
};
47224693

4723-
static bool kernel_supports(const struct bpf_object *obj, enum kern_feature_id feat_id)
4694+
bool kernel_supports(const struct bpf_object *obj, enum kern_feature_id feat_id)
47244695
{
47254696
struct kern_feature_desc *feat = &feature_probes[feat_id];
47264697
int ret;
47274698

4728-
if (obj->gen_loader)
4699+
if (obj && obj->gen_loader)
47294700
/* To generate loader program assume the latest kernel
47304701
* to avoid doing extra prog_load, map_create syscalls.
47314702
*/

src/libbpf.map

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,4 +427,5 @@ LIBBPF_0.7.0 {
427427
bpf_program__log_level;
428428
bpf_program__set_log_buf;
429429
bpf_program__set_log_level;
430+
libbpf_set_memlock_rlim_max;
430431
};

src/libbpf_internal.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,45 @@ static inline bool libbpf_validate_opts(const char *opts,
291291
(opts)->sz - __off); \
292292
})
293293

294+
enum kern_feature_id {
295+
/* v4.14: kernel support for program & map names. */
296+
FEAT_PROG_NAME,
297+
/* v5.2: kernel support for global data sections. */
298+
FEAT_GLOBAL_DATA,
299+
/* BTF support */
300+
FEAT_BTF,
301+
/* BTF_KIND_FUNC and BTF_KIND_FUNC_PROTO support */
302+
FEAT_BTF_FUNC,
303+
/* BTF_KIND_VAR and BTF_KIND_DATASEC support */
304+
FEAT_BTF_DATASEC,
305+
/* BTF_FUNC_GLOBAL is supported */
306+
FEAT_BTF_GLOBAL_FUNC,
307+
/* BPF_F_MMAPABLE is supported for arrays */
308+
FEAT_ARRAY_MMAP,
309+
/* kernel support for expected_attach_type in BPF_PROG_LOAD */
310+
FEAT_EXP_ATTACH_TYPE,
311+
/* bpf_probe_read_{kernel,user}[_str] helpers */
312+
FEAT_PROBE_READ_KERN,
313+
/* BPF_PROG_BIND_MAP is supported */
314+
FEAT_PROG_BIND_MAP,
315+
/* Kernel support for module BTFs */
316+
FEAT_MODULE_BTF,
317+
/* BTF_KIND_FLOAT support */
318+
FEAT_BTF_FLOAT,
319+
/* BPF perf link support */
320+
FEAT_PERF_LINK,
321+
/* BTF_KIND_DECL_TAG support */
322+
FEAT_BTF_DECL_TAG,
323+
/* BTF_KIND_TYPE_TAG support */
324+
FEAT_BTF_TYPE_TAG,
325+
/* memcg-based accounting for BPF maps and progs */
326+
FEAT_MEMCG_ACCOUNT,
327+
__FEAT_CNT,
328+
};
329+
330+
int probe_memcg_account(void);
331+
bool kernel_supports(const struct bpf_object *obj, enum kern_feature_id feat_id);
332+
int bump_rlimit_memlock(void);
294333

295334
int parse_cpu_mask_str(const char *s, bool **mask, int *mask_sz);
296335
int parse_cpu_mask_file(const char *fcpu, bool **mask, int *mask_sz);

src/libbpf_legacy.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ enum libbpf_strict_mode {
4545
* (positive) error code.
4646
*/
4747
LIBBPF_STRICT_DIRECT_ERRS = 0x02,
48-
4948
/*
5049
* Enforce strict BPF program section (SEC()) names.
5150
* E.g., while prefiously SEC("xdp_whatever") or SEC("perf_event_blah") were
@@ -63,6 +62,17 @@ enum libbpf_strict_mode {
6362
* Clients can maintain it on their own if it is valuable for them.
6463
*/
6564
LIBBPF_STRICT_NO_OBJECT_LIST = 0x08,
65+
/*
66+
* Automatically bump RLIMIT_MEMLOCK using setrlimit() before the
67+
* first BPF program or map creation operation. This is done only if
68+
* kernel is too old to support memcg-based memory accounting for BPF
69+
* subsystem. By default, RLIMIT_MEMLOCK limit is set to RLIM_INFINITY,
70+
* but it can be overriden with libbpf_set_memlock_rlim_max() API.
71+
* Note that libbpf_set_memlock_rlim_max() needs to be called before
72+
* the very first bpf_prog_load(), bpf_map_create() or bpf_object__load()
73+
* operation.
74+
*/
75+
LIBBPF_STRICT_AUTO_RLIMIT_MEMLOCK = 0x10,
6676

6777
__LIBBPF_STRICT_LAST,
6878
};

0 commit comments

Comments
 (0)