Skip to content

Commit 151de26

Browse files
authored
Merge pull request #21 from multikernel/fix-extern-kfunc-codegen
Fix kfunc/extern codegen so sched_ext_simple compiles
2 parents 81f65e8 + 4a30dea commit 151de26

12 files changed

Lines changed: 540 additions & 88 deletions

examples/sched_ext_simple.ks

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@
33

44
include "sched_ext_ops.kh"
55

6-
// kfuncs declarations (extracted from BTF)
7-
extern scx_bpf_select_cpu_dfl(p: *u8, prev_cpu: i32, wake_flags: u64, direct: *bool) -> i32
8-
extern scx_bpf_dsq_insert(p: *u8, dsq_id: u64, slice: u64, enq_flags: u64) -> void
9-
extern scx_bpf_consume(dsq_id: u64, cpu: i32, flags: u64) -> i32
6+
// kfuncs declarations (signatures match the kernel BTF for sched_ext)
7+
extern scx_bpf_select_cpu_dfl(p: *task_struct, prev_cpu: i32, wake_flags: u64, is_idle: *bool) -> i32
8+
extern scx_bpf_dsq_insert(p: *task_struct, dsq_id: u64, slice: u64, enq_flags: u64) -> void
9+
extern scx_bpf_dsq_move_to_local(dsq_id: u64) -> bool
1010

1111
// Simple FIFO scheduler implementation
1212
@struct_ops("sched_ext_ops")
1313
impl simple_fifo_scheduler {
1414

1515
// Select CPU for a waking task
16-
fn select_cpu(p: *u8, prev_cpu: i32, wake_flags: u64) -> i32 {
16+
fn select_cpu(p: *task_struct, prev_cpu: i32, wake_flags: u64) -> i32 {
1717
// Use default CPU selection with direct dispatch if idle core found
1818
var direct: bool = false
1919
var cpu = scx_bpf_select_cpu_dfl(p, prev_cpu, wake_flags, &direct)
@@ -27,56 +27,56 @@ impl simple_fifo_scheduler {
2727
}
2828

2929
// Enqueue task into global FIFO queue
30-
fn enqueue(p: *u8, enq_flags: u64) -> void {
30+
fn enqueue(p: *task_struct, enq_flags: u64) -> void {
3131
// Simple FIFO: insert all tasks into global DSQ
3232
scx_bpf_dsq_insert(p, SCX_DSQ_GLOBAL, SCX_SLICE_DFL, enq_flags)
3333
}
3434

3535
// Dispatch tasks from global queue to local CPU
36-
fn dispatch(cpu: i32, prev: *u8) -> void {
37-
// Try to consume a task from the global DSQ
38-
if (scx_bpf_consume(SCX_DSQ_GLOBAL, cpu, 0) == 0) {
36+
fn dispatch(cpu: i32, prev: *task_struct) -> void {
37+
// Try to move a task from the global DSQ to this CPU's local DSQ
38+
if (!scx_bpf_dsq_move_to_local(SCX_DSQ_GLOBAL)) {
3939
// No tasks available, CPU will go idle
4040
}
4141
}
4242

4343
// Task becomes runnable
44-
fn runnable(p: *u8, enq_flags: u64) -> void {
44+
fn runnable(p: *task_struct, enq_flags: u64) -> void {
4545
// Optional: track runnable tasks
4646
// For simple FIFO, we don't need special handling
4747
}
48-
48+
4949
// Task starts running
50-
fn running(p: *u8) -> void {
50+
fn running(p: *task_struct) -> void {
5151
// Optional: track running tasks
5252
// For simple FIFO, we don't need special handling
5353
}
54-
54+
5555
// Task stops running
56-
fn stopping(p: *u8, runnable: bool) -> void {
56+
fn stopping(p: *task_struct, runnable: bool) -> void {
5757
// Optional: handle task stopping
5858
// For simple FIFO, we don't need special handling
5959
}
60-
60+
6161
// Task becomes quiescent
62-
fn quiescent(p: *u8, deq_flags: u64) -> void {
62+
fn quiescent(p: *task_struct, deq_flags: u64) -> void {
6363
// Optional: handle quiescent tasks
6464
// For simple FIFO, we don't need special handling
6565
}
66-
66+
6767
// Initialize new task
68-
fn init_task(p: *u8, args: *u8) -> i32 {
68+
fn init_task(p: *task_struct, args: *u8) -> i32 {
6969
// Return 0 for success
7070
return 0
7171
}
72-
72+
7373
// Clean up exiting task
74-
fn exit_task(p: *u8, args: *u8) -> void {
74+
fn exit_task(p: *task_struct, args: *u8) -> void {
7575
// Optional cleanup for exiting tasks
7676
}
77-
77+
7878
// Enable scheduler
79-
fn enable(p: *u8) -> void {
79+
fn enable(p: *task_struct) -> void {
8080
// Optional: scheduler enable logic
8181
}
8282

src/bpf_helpers.ml

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
(*
2+
* Copyright 2026 Multikernel Technologies, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*)
16+
17+
(** Standard BPF helper functions.
18+
19+
These are the legacy BPF helpers invoked through a fixed helper ID. libbpf's
20+
[<bpf/bpf_helpers.h>] (via the auto-generated [bpf_helper_defs.h]) already
21+
declares every one of them as a function pointer, so the eBPF C backend must
22+
NOT emit its own [extern ... __ksym;] declaration for them - doing so clashes
23+
with the libbpf declaration ("redefinition as different kind of symbol").
24+
25+
KernelScript uses the [extern] keyword for both kfuncs and helpers, so the
26+
backend consults this set to tell them apart: a name here is a helper (skip
27+
the __ksym declaration); anything else is a real kfunc (needs __ksym).
28+
29+
The list mirrors libbpf's [bpf_helper_defs.h], which is generated from the
30+
kernel's [__BPF_FUNC_MAPPER] and only ever grows. *)
31+
32+
let helper_names = [
33+
"bpf_bind";
34+
"bpf_bprm_opts_set";
35+
"bpf_btf_find_by_name_kind";
36+
"bpf_cgrp_storage_delete";
37+
"bpf_cgrp_storage_get";
38+
"bpf_check_mtu";
39+
"bpf_clone_redirect";
40+
"bpf_copy_from_user";
41+
"bpf_copy_from_user_task";
42+
"bpf_csum_diff";
43+
"bpf_csum_level";
44+
"bpf_csum_update";
45+
"bpf_current_task_under_cgroup";
46+
"bpf_d_path";
47+
"bpf_dynptr_data";
48+
"bpf_dynptr_from_mem";
49+
"bpf_dynptr_read";
50+
"bpf_dynptr_write";
51+
"bpf_fib_lookup";
52+
"bpf_find_vma";
53+
"bpf_for_each_map_elem";
54+
"bpf_get_attach_cookie";
55+
"bpf_get_branch_snapshot";
56+
"bpf_get_cgroup_classid";
57+
"bpf_get_current_ancestor_cgroup_id";
58+
"bpf_get_current_cgroup_id";
59+
"bpf_get_current_comm";
60+
"bpf_get_current_pid_tgid";
61+
"bpf_get_current_task";
62+
"bpf_get_current_task_btf";
63+
"bpf_get_current_uid_gid";
64+
"bpf_get_func_arg";
65+
"bpf_get_func_arg_cnt";
66+
"bpf_get_func_ip";
67+
"bpf_get_func_ret";
68+
"bpf_get_hash_recalc";
69+
"bpf_get_listener_sock";
70+
"bpf_get_local_storage";
71+
"bpf_get_netns_cookie";
72+
"bpf_get_ns_current_pid_tgid";
73+
"bpf_get_numa_node_id";
74+
"bpf_get_prandom_u32";
75+
"bpf_get_retval";
76+
"bpf_get_route_realm";
77+
"bpf_get_smp_processor_id";
78+
"bpf_get_socket_cookie";
79+
"bpf_get_socket_uid";
80+
"bpf_getsockopt";
81+
"bpf_get_stack";
82+
"bpf_get_stackid";
83+
"bpf_get_task_stack";
84+
"bpf_ima_file_hash";
85+
"bpf_ima_inode_hash";
86+
"bpf_inode_storage_delete";
87+
"bpf_inode_storage_get";
88+
"bpf_jiffies64";
89+
"bpf_kallsyms_lookup_name";
90+
"bpf_kptr_xchg";
91+
"bpf_ktime_get_boot_ns";
92+
"bpf_ktime_get_coarse_ns";
93+
"bpf_ktime_get_ns";
94+
"bpf_ktime_get_tai_ns";
95+
"bpf_l3_csum_replace";
96+
"bpf_l4_csum_replace";
97+
"bpf_load_hdr_opt";
98+
"bpf_loop";
99+
"bpf_lwt_push_encap";
100+
"bpf_lwt_seg6_action";
101+
"bpf_lwt_seg6_adjust_srh";
102+
"bpf_lwt_seg6_store_bytes";
103+
"bpf_map_delete_elem";
104+
"bpf_map_lookup_elem";
105+
"bpf_map_lookup_percpu_elem";
106+
"bpf_map_peek_elem";
107+
"bpf_map_pop_elem";
108+
"bpf_map_push_elem";
109+
"bpf_map_update_elem";
110+
"bpf_msg_apply_bytes";
111+
"bpf_msg_cork_bytes";
112+
"bpf_msg_pop_data";
113+
"bpf_msg_pull_data";
114+
"bpf_msg_push_data";
115+
"bpf_msg_redirect_hash";
116+
"bpf_msg_redirect_map";
117+
"bpf_override_return";
118+
"bpf_per_cpu_ptr";
119+
"bpf_perf_event_output";
120+
"bpf_perf_event_read";
121+
"bpf_perf_event_read_value";
122+
"bpf_perf_prog_read_value";
123+
"bpf_probe_read";
124+
"bpf_probe_read_kernel";
125+
"bpf_probe_read_kernel_str";
126+
"bpf_probe_read_str";
127+
"bpf_probe_read_user";
128+
"bpf_probe_read_user_str";
129+
"bpf_probe_write_user";
130+
"bpf_rc_keydown";
131+
"bpf_rc_pointer_rel";
132+
"bpf_rc_repeat";
133+
"bpf_read_branch_records";
134+
"bpf_redirect";
135+
"bpf_redirect_map";
136+
"bpf_redirect_neigh";
137+
"bpf_redirect_peer";
138+
"bpf_reserve_hdr_opt";
139+
"bpf_ringbuf_discard";
140+
"bpf_ringbuf_discard_dynptr";
141+
"bpf_ringbuf_output";
142+
"bpf_ringbuf_query";
143+
"bpf_ringbuf_reserve";
144+
"bpf_ringbuf_reserve_dynptr";
145+
"bpf_ringbuf_submit";
146+
"bpf_ringbuf_submit_dynptr";
147+
"bpf_send_signal";
148+
"bpf_send_signal_thread";
149+
"bpf_seq_printf";
150+
"bpf_seq_printf_btf";
151+
"bpf_seq_write";
152+
"bpf_set_hash";
153+
"bpf_set_hash_invalid";
154+
"bpf_set_retval";
155+
"bpf_setsockopt";
156+
"bpf_sk_ancestor_cgroup_id";
157+
"bpf_sk_assign";
158+
"bpf_skb_adjust_room";
159+
"bpf_skb_ancestor_cgroup_id";
160+
"bpf_skb_cgroup_classid";
161+
"bpf_skb_cgroup_id";
162+
"bpf_skb_change_head";
163+
"bpf_skb_change_proto";
164+
"bpf_skb_change_tail";
165+
"bpf_skb_change_type";
166+
"bpf_skb_ecn_set_ce";
167+
"bpf_skb_get_tunnel_key";
168+
"bpf_skb_get_tunnel_opt";
169+
"bpf_skb_get_xfrm_state";
170+
"bpf_skb_load_bytes";
171+
"bpf_skb_load_bytes_relative";
172+
"bpf_skb_output";
173+
"bpf_skb_pull_data";
174+
"bpf_skb_set_tstamp";
175+
"bpf_skb_set_tunnel_key";
176+
"bpf_skb_set_tunnel_opt";
177+
"bpf_skb_store_bytes";
178+
"bpf_skb_under_cgroup";
179+
"bpf_skb_vlan_pop";
180+
"bpf_skb_vlan_push";
181+
"bpf_sk_cgroup_id";
182+
"bpf_skc_lookup_tcp";
183+
"bpf_skc_to_mptcp_sock";
184+
"bpf_skc_to_tcp6_sock";
185+
"bpf_skc_to_tcp_request_sock";
186+
"bpf_skc_to_tcp_sock";
187+
"bpf_skc_to_tcp_timewait_sock";
188+
"bpf_skc_to_udp6_sock";
189+
"bpf_skc_to_unix_sock";
190+
"bpf_sk_fullsock";
191+
"bpf_sk_lookup_tcp";
192+
"bpf_sk_lookup_udp";
193+
"bpf_sk_redirect_hash";
194+
"bpf_sk_redirect_map";
195+
"bpf_sk_release";
196+
"bpf_sk_select_reuseport";
197+
"bpf_sk_storage_delete";
198+
"bpf_sk_storage_get";
199+
"bpf_snprintf";
200+
"bpf_snprintf_btf";
201+
"bpf_sock_from_file";
202+
"bpf_sock_hash_update";
203+
"bpf_sock_map_update";
204+
"bpf_sock_ops_cb_flags_set";
205+
"bpf_spin_lock";
206+
"bpf_spin_unlock";
207+
"bpf_store_hdr_opt";
208+
"bpf_strncmp";
209+
"bpf_strtol";
210+
"bpf_strtoul";
211+
"bpf_sys_bpf";
212+
"bpf_sys_close";
213+
"bpf_sysctl_get_current_value";
214+
"bpf_sysctl_get_name";
215+
"bpf_sysctl_get_new_value";
216+
"bpf_sysctl_set_new_value";
217+
"bpf_tail_call";
218+
"bpf_task_pt_regs";
219+
"bpf_task_storage_delete";
220+
"bpf_task_storage_get";
221+
"bpf_tcp_check_syncookie";
222+
"bpf_tcp_gen_syncookie";
223+
"bpf_tcp_raw_check_syncookie_ipv4";
224+
"bpf_tcp_raw_check_syncookie_ipv6";
225+
"bpf_tcp_raw_gen_syncookie_ipv4";
226+
"bpf_tcp_raw_gen_syncookie_ipv6";
227+
"bpf_tcp_send_ack";
228+
"bpf_tcp_sock";
229+
"bpf_this_cpu_ptr";
230+
"bpf_timer_cancel";
231+
"bpf_timer_init";
232+
"bpf_timer_set_callback";
233+
"bpf_timer_start";
234+
"bpf_trace_printk";
235+
"bpf_trace_vprintk";
236+
"bpf_user_ringbuf_drain";
237+
"bpf_xdp_adjust_head";
238+
"bpf_xdp_adjust_meta";
239+
"bpf_xdp_adjust_tail";
240+
"bpf_xdp_get_buff_len";
241+
"bpf_xdp_load_bytes";
242+
"bpf_xdp_output";
243+
"bpf_xdp_store_bytes";
244+
]
245+
246+
let helper_set =
247+
let tbl = Hashtbl.create 256 in
248+
List.iter (fun name -> Hashtbl.replace tbl name ()) helper_names;
249+
tbl
250+
251+
(** [is_bpf_helper name] is true if [name] is a standard BPF helper already
252+
declared by libbpf's bpf_helpers.h, and therefore must not be re-declared
253+
as a [__ksym] extern by the eBPF C backend. *)
254+
let is_bpf_helper name = Hashtbl.mem helper_set name

src/dune

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
(name kernelscript)
33
(modules ast parser lexer parse type_checker symbol_table maps map_assignment
44
map_operations ir ir_generator ir_analysis loop_analysis ir_function_system codegen_common
5+
bpf_helpers
56
multi_program_analyzer multi_program_ir_optimizer ebpf_c_codegen
67
userspace_codegen evaluator safety_checker stdlib test_codegen
78
tail_call_analyzer kernel_module_codegen dynptr_bridge

0 commit comments

Comments
 (0)