1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3 * Copyright (c) 2024 Meta Platforms, Inc. and affiliates.
4 * Copyright (c) 2024 Tejun Heo <tj@kernel.org>
5 * Copyright (c) 2024 David Vernet <dvernet@meta.com>
6 */
7 #ifndef __SCX_COMPAT_H
8 #define __SCX_COMPAT_H
9
10 #include <bpf/btf.h>
11 #include <fcntl.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14
15 struct btf *__COMPAT_vmlinux_btf __attribute__((weak));
16
__COMPAT_load_vmlinux_btf(void)17 static inline void __COMPAT_load_vmlinux_btf(void)
18 {
19 if (!__COMPAT_vmlinux_btf) {
20 __COMPAT_vmlinux_btf = btf__load_vmlinux_btf();
21 SCX_BUG_ON(!__COMPAT_vmlinux_btf, "btf__load_vmlinux_btf()");
22 }
23 }
24
__COMPAT_read_enum(const char * type,const char * name,u64 * v)25 static inline bool __COMPAT_read_enum(const char *type, const char *name, u64 *v)
26 {
27 const struct btf_type *t;
28 const char *n;
29 s32 tid;
30 int i;
31
32 __COMPAT_load_vmlinux_btf();
33
34 tid = btf__find_by_name(__COMPAT_vmlinux_btf, type);
35 if (tid < 0)
36 return false;
37
38 t = btf__type_by_id(__COMPAT_vmlinux_btf, tid);
39 SCX_BUG_ON(!t, "btf__type_by_id(%d)", tid);
40
41 if (btf_is_enum(t)) {
42 struct btf_enum *e = btf_enum(t);
43
44 for (i = 0; i < BTF_INFO_VLEN(t->info); i++) {
45 n = btf__name_by_offset(__COMPAT_vmlinux_btf, e[i].name_off);
46 SCX_BUG_ON(!n, "btf__name_by_offset()");
47 if (!strcmp(n, name)) {
48 *v = e[i].val;
49 return true;
50 }
51 }
52 } else if (btf_is_enum64(t)) {
53 struct btf_enum64 *e = btf_enum64(t);
54
55 for (i = 0; i < BTF_INFO_VLEN(t->info); i++) {
56 n = btf__name_by_offset(__COMPAT_vmlinux_btf, e[i].name_off);
57 SCX_BUG_ON(!n, "btf__name_by_offset()");
58 if (!strcmp(n, name)) {
59 *v = btf_enum64_value(&e[i]);
60 return true;
61 }
62 }
63 }
64
65 return false;
66 }
67
68 #define __COMPAT_ENUM_OR_ZERO(__type, __ent) \
69 ({ \
70 u64 __val = 0; \
71 __COMPAT_read_enum(__type, __ent, &__val); \
72 __val; \
73 })
74
__COMPAT_has_ksym(const char * ksym)75 static inline bool __COMPAT_has_ksym(const char *ksym)
76 {
77 __COMPAT_load_vmlinux_btf();
78 return btf__find_by_name(__COMPAT_vmlinux_btf, ksym) >= 0;
79 }
80
__COMPAT_struct_has_field(const char * type,const char * field)81 static inline bool __COMPAT_struct_has_field(const char *type, const char *field)
82 {
83 const struct btf_type *t;
84 const struct btf_member *m;
85 const char *n;
86 s32 tid;
87 int i;
88
89 __COMPAT_load_vmlinux_btf();
90 tid = btf__find_by_name_kind(__COMPAT_vmlinux_btf, type, BTF_KIND_STRUCT);
91 if (tid < 0)
92 return false;
93
94 t = btf__type_by_id(__COMPAT_vmlinux_btf, tid);
95 SCX_BUG_ON(!t, "btf__type_by_id(%d)", tid);
96
97 m = btf_members(t);
98
99 for (i = 0; i < BTF_INFO_VLEN(t->info); i++) {
100 n = btf__name_by_offset(__COMPAT_vmlinux_btf, m[i].name_off);
101 SCX_BUG_ON(!n, "btf__name_by_offset()");
102 if (!strcmp(n, field))
103 return true;
104 }
105
106 return false;
107 }
108
109 #define SCX_OPS_SWITCH_PARTIAL \
110 __COMPAT_ENUM_OR_ZERO("scx_ops_flags", "SCX_OPS_SWITCH_PARTIAL")
111
scx_hotplug_seq(void)112 static inline long scx_hotplug_seq(void)
113 {
114 int fd;
115 char buf[32];
116 ssize_t len;
117 long val;
118
119 fd = open("/sys/kernel/sched_ext/hotplug_seq", O_RDONLY);
120 if (fd < 0)
121 return -ENOENT;
122
123 len = read(fd, buf, sizeof(buf) - 1);
124 SCX_BUG_ON(len <= 0, "read failed (%ld)", len);
125 buf[len] = 0;
126 close(fd);
127
128 val = strtoul(buf, NULL, 10);
129 SCX_BUG_ON(val < 0, "invalid num hotplug events: %lu", val);
130
131 return val;
132 }
133
134 /*
135 * struct sched_ext_ops can change over time. If compat.bpf.h::SCX_OPS_DEFINE()
136 * is used to define ops and compat.h::SCX_OPS_LOAD/ATTACH() are used to load
137 * and attach it, backward compatibility is automatically maintained where
138 * reasonable.
139 *
140 * ec7e3b0463e1 ("implement-ops") in https://github.com/sched-ext/sched_ext is
141 * the current minimum required kernel version.
142 */
143 #define SCX_OPS_OPEN(__ops_name, __scx_name) ({ \
144 struct __scx_name *__skel; \
145 \
146 SCX_BUG_ON(!__COMPAT_struct_has_field("sched_ext_ops", "dump"), \
147 "sched_ext_ops.dump() missing, kernel too old?"); \
148 \
149 __skel = __scx_name##__open(); \
150 SCX_BUG_ON(!__skel, "Could not open " #__scx_name); \
151 __skel->struct_ops.__ops_name->hotplug_seq = scx_hotplug_seq(); \
152 __skel; \
153 })
154
155 #define SCX_OPS_LOAD(__skel, __ops_name, __scx_name, __uei_name) ({ \
156 UEI_SET_SIZE(__skel, __ops_name, __uei_name); \
157 SCX_BUG_ON(__scx_name##__load((__skel)), "Failed to load skel"); \
158 })
159
160 /*
161 * New versions of bpftool now emit additional link placeholders for BPF maps,
162 * and set up BPF skeleton in such a way that libbpf will auto-attach BPF maps
163 * automatically, assumming libbpf is recent enough (v1.5+). Old libbpf will do
164 * nothing with those links and won't attempt to auto-attach maps.
165 *
166 * To maintain compatibility with older libbpf while avoiding trying to attach
167 * twice, disable the autoattach feature on newer libbpf.
168 */
169 #if LIBBPF_MAJOR_VERSION > 1 || \
170 (LIBBPF_MAJOR_VERSION == 1 && LIBBPF_MINOR_VERSION >= 5)
171 #define __SCX_OPS_DISABLE_AUTOATTACH(__skel, __ops_name) \
172 bpf_map__set_autoattach((__skel)->maps.__ops_name, false)
173 #else
174 #define __SCX_OPS_DISABLE_AUTOATTACH(__skel, __ops_name) do {} while (0)
175 #endif
176
177 #define SCX_OPS_ATTACH(__skel, __ops_name, __scx_name) ({ \
178 struct bpf_link *__link; \
179 __SCX_OPS_DISABLE_AUTOATTACH(__skel, __ops_name); \
180 SCX_BUG_ON(__scx_name##__attach((__skel)), "Failed to attach skel"); \
181 __link = bpf_map__attach_struct_ops((__skel)->maps.__ops_name); \
182 SCX_BUG_ON(!__link, "Failed to attach struct_ops"); \
183 __link; \
184 })
185
186 #endif /* __SCX_COMPAT_H */
187