Skip to content

Commit f6e9e36

Browse files
committed
Add support for eBPF linked lists in KernelScript. Introduce list declaration syntax, operations (push_front, push_back, pop_front, pop_back), and automatic struct modifications for list elements.
1 parent 83f2cb4 commit f6e9e36

File tree

16 files changed

+975
-14
lines changed

16 files changed

+975
-14
lines changed

SPEC.md

Lines changed: 182 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2297,6 +2297,174 @@ fn simple_monitor(ctx: *xdp_md) -> xdp_action {
22972297
}
22982298
```
22992299

2300+
2301+
### 5.6 eBPF Linked Lists
2302+
2303+
KernelScript provides seamless support for eBPF linked lists with Python-like syntax. Lists automatically handle the complex BPF linked list infrastructure while providing a simple, type-safe interface.
2304+
2305+
#### 5.6.1 List Declaration Syntax
2306+
2307+
Lists use a simplified syntax compared to maps - no flags or pinning allowed:
2308+
2309+
```kernelscript
2310+
// Basic list declaration
2311+
var my_list : list<StructType>
2312+
2313+
// Lists can only contain struct types
2314+
struct PacketInfo {
2315+
src_ip: u32,
2316+
dst_ip: u32,
2317+
size: u16,
2318+
}
2319+
2320+
var packet_queue : list<PacketInfo>
2321+
```
2322+
2323+
**List Constraints:**
2324+
- ✅ Only struct types are allowed as list elements
2325+
- ❌ Lists cannot be pinned (no `pin` keyword)
2326+
- ❌ Lists cannot have flags (no `@flags()`)
2327+
- ❌ Primitive types like `u32`, `str`, etc. are not allowed
2328+
2329+
#### 5.6.2 List Operations
2330+
2331+
KernelScript provides four core list operations that map directly to eBPF linked list functions:
2332+
2333+
```kernelscript
2334+
struct EventData {
2335+
timestamp: u64,
2336+
event_type: u32,
2337+
}
2338+
2339+
var event_list : list<EventData>
2340+
2341+
@helper
2342+
fn process_events() {
2343+
var event = EventData {
2344+
timestamp: bpf_ktime_get_ns(),
2345+
event_type: 1,
2346+
}
2347+
2348+
// Add elements
2349+
event_list.push_back(event) // Add to end of list
2350+
event_list.push_front(event) // Add to beginning of list
2351+
2352+
// Remove and return elements
2353+
var latest = event_list.pop_front() // Remove from beginning
2354+
var oldest = event_list.pop_back() // Remove from end
2355+
2356+
// Check for null (empty list)
2357+
if (latest != null) {
2358+
// Process the event
2359+
if (latest->event_type == 1) {
2360+
// Handle event
2361+
}
2362+
}
2363+
}
2364+
```
2365+
2366+
#### 5.6.3 Generated eBPF Code
2367+
2368+
The compiler automatically generates the necessary BPF linked list infrastructure:
2369+
2370+
##### 1. Helper Function Declarations
2371+
```c
2372+
/* BPF list helper functions are automatically declared */
2373+
extern int bpf_list_push_front(struct bpf_list_head *head, struct bpf_list_node *node) __ksym;
2374+
extern int bpf_list_push_back(struct bpf_list_head *head, struct bpf_list_node *node) __ksym;
2375+
extern struct bpf_list_node *bpf_list_pop_front(struct bpf_list_head *head) __ksym;
2376+
extern struct bpf_list_node *bpf_list_pop_back(struct bpf_list_head *head) __ksym;
2377+
```
2378+
2379+
##### 2. Automatic Struct Modification
2380+
```c
2381+
/* Original KernelScript struct */
2382+
// struct EventData { timestamp: u64, event_type: u32 }
2383+
2384+
/* Generated eBPF struct with injected list node */
2385+
struct EventData {
2386+
__u64 timestamp;
2387+
__u32 event_type;
2388+
struct bpf_list_node __list_node; // Automatically injected
2389+
};
2390+
```
2391+
2392+
##### 3. List Variable Declaration
2393+
```c
2394+
/* KernelScript: var event_list : list<EventData> */
2395+
struct bpf_list_head event_list;
2396+
```
2397+
2398+
##### 4. Operation Translation
2399+
```c
2400+
// KernelScript: event_list.push_back(event)
2401+
// Generated eBPF C:
2402+
result = bpf_list_push_back(&event_list, &event.__list_node);
2403+
```
2404+
2405+
**Complete Operation Mapping:**
2406+
- `list.push_front(item)``bpf_list_push_front(&list, &item.__list_node)`
2407+
- `list.push_back(item)``bpf_list_push_back(&list, &item.__list_node)`
2408+
- `list.pop_front()``bpf_list_pop_front(&list)`
2409+
- `list.pop_back()``bpf_list_pop_back(&list)`
2410+
2411+
#### 5.6.4 Type Safety
2412+
2413+
The compiler enforces strict type safety for list operations:
2414+
2415+
```kernelscript
2416+
struct PacketInfo { src_ip: u32 }
2417+
struct EventData { timestamp: u64 }
2418+
2419+
var packets : list<PacketInfo>
2420+
var events : list<EventData>
2421+
2422+
@helper
2423+
fn type_safety_example() {
2424+
var packet = PacketInfo { src_ip: 1234 }
2425+
var event = EventData { timestamp: 5678 }
2426+
2427+
packets.push_back(packet) // ✅ Correct type
2428+
packets.push_back(event) // ❌ Compile error: type mismatch
2429+
2430+
var retrieved = packets.pop_front() // Returns PacketInfo* or null
2431+
}
2432+
```
2433+
2434+
#### 5.6.5 Integration with eBPF Programs
2435+
2436+
Lists work seamlessly in all eBPF program types:
2437+
2438+
```kernelscript
2439+
struct NetworkEvent {
2440+
src_ip: u32,
2441+
dst_ip: u32,
2442+
action: u32,
2443+
}
2444+
2445+
var network_log : list<NetworkEvent>
2446+
2447+
@xdp
2448+
fn packet_filter(ctx: *xdp_md) -> xdp_action {
2449+
var event = NetworkEvent {
2450+
src_ip: ctx->remote_ip4,
2451+
dst_ip: ctx->local_ip4,
2452+
action: XDP_PASS,
2453+
}
2454+
2455+
network_log.push_front(event)
2456+
return XDP_PASS
2457+
}
2458+
```
2459+
2460+
#### 5.6.6 Memory Management
2461+
2462+
List elements are automatically managed by the eBPF kernel infrastructure:
2463+
- Elements are allocated using `bpf_obj_new()` when created
2464+
- Elements are freed automatically when removed from lists
2465+
- No manual memory management required
2466+
- Built-in protection against memory leaks and use-after-free
2467+
23002468
## 6. Assignment Operators
23012469

23022470
### 6.1 Simple Assignment
@@ -4304,7 +4472,7 @@ fn print_summary_stats() {
43044472
(* Top-level structure *)
43054473
kernelscript_file = { global_declaration }
43064474
4307-
global_declaration = config_declaration | map_declaration | type_declaration |
4475+
global_declaration = config_declaration | map_declaration | list_declaration | type_declaration |
43084476
function_declaration | struct_declaration | impl_declaration |
43094477
global_variable_declaration | bindings_declaration | import_declaration
43104478
@@ -4316,7 +4484,12 @@ map_type = "hash" | "array" | "percpu_hash" | "percpu_array" | "lru_hash"
43164484
map_config = integer_literal [ "," map_config_item { "," map_config_item } ]
43174485
map_config_item = identifier "=" literal
43184486
4319-
flag_expression = identifier | ( identifier { "|" identifier } )
4487+
flag_expression = identifier | ( identifier { "|" identifier } )
4488+
4489+
(* List declarations - global scope *)
4490+
list_declaration = "var" identifier ":" "list" "<" struct_type ">"
4491+
4492+
struct_type = identifier (* Must resolve to a struct type, not primitive *)
43204493
43214494
(* eBPF program function attributes *)
43224495
attribute_list = attribute { attribute }
@@ -4442,7 +4615,7 @@ unary_operator = "!" | "-" | "*" | "&"
44424615
*)
44434616
44444617
primary_expression = config_access | identifier | literal | function_call | field_access |
4445-
array_access | parenthesized_expression | struct_literal | match_expression
4618+
array_access | parenthesized_expression | struct_literal | match_expression | list_operation
44464619
44474620
config_access = identifier "." identifier
44484621
@@ -4460,19 +4633,23 @@ match_expression = "match" "(" expression ")" "{" match_arm { "," match_arm } [
44604633
match_arm = match_pattern ":" expression
44614634
match_pattern = integer_literal | identifier | "default"
44624635
4636+
list_operation = primary_expression "." list_method "(" [ argument_list ] ")"
4637+
list_method = "push_front" | "push_back" | "pop_front" | "pop_back"
4638+
44634639
(* Type annotations *)
44644640
type_annotation = primitive_type | compound_type | identifier
44654641
44664642
primitive_type = "u8" | "u16" | "u32" | "u64" | "i8" | "i16" | "i32" | "i64" |
44674643
"bool" | "char" | "void" | "ProgramRef" | string_type
44684644
4469-
compound_type = array_type | pointer_type | function_type
4645+
compound_type = array_type | pointer_type | function_type | list_type
44704646
44714647
string_type = "str" "(" integer_literal ")"
44724648
44734649
array_type = "[" type_annotation "" integer_literal "]"
44744650
pointer_type = "*" type_annotation
4475-
function_type = "fn" "(" [ type_annotation { "," type_annotation } ] ")" [ return_type_spec ]
4651+
function_type = "fn" "(" [ type_annotation { "," type_annotation } ] ")" [ return_type_spec ]
4652+
list_type = "list" "<" type_annotation ">"
44764653
44774654
(* Literals *)
44784655
literal = integer_literal | string_literal | char_literal | boolean_literal |

examples/simple_list_demo.ks

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Simple List Demo - demonstrates KernelScript list functionality
2+
3+
struct SimpleData {
4+
id: u32,
5+
value: u64,
6+
}
7+
8+
// List declaration - no flags or pinning allowed
9+
var data_list : list<SimpleData>
10+
11+
@xdp
12+
fn simple_processor(ctx: *xdp_md) -> xdp_action {
13+
// Create some data
14+
var data1 = SimpleData {
15+
id : 1,
16+
value: 100
17+
}
18+
var data2 = SimpleData {
19+
id : 2,
20+
value : 200
21+
}
22+
23+
// Add data to list using eBPF list operations
24+
data_list.push_back(data1)
25+
data_list.push_front(data2)
26+
27+
// Pop data from list
28+
var front_item = data_list.pop_front()
29+
if (front_item != none) {
30+
// Process the item
31+
if (front_item.id > 0) {
32+
return XDP_PASS
33+
}
34+
}
35+
36+
var back_item = data_list.pop_back()
37+
if (back_item != none) {
38+
// Process the back item
39+
return XDP_PASS
40+
}
41+
42+
return XDP_DROP
43+
}

src/ast.ml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ and bpf_type =
7272
| Result of bpf_type * bpf_type
7373
| Function of bpf_type list * bpf_type
7474
| Map of bpf_type * bpf_type * map_type
75+
| List of bpf_type (* eBPF linked lists - only struct types allowed *)
7576
(* Built-in context types *)
7677
| Xdp_md | UprobeContext
7778
| TracepointContext | LsmContext | CgroupSkbContext
@@ -103,6 +104,14 @@ type map_declaration = {
103104
map_pos: position;
104105
}
105106

107+
(** List declarations - simpler than maps, no flags or pinning *)
108+
type list_declaration = {
109+
list_name: string;
110+
element_type: bpf_type; (* Must be a struct type *)
111+
is_global: bool;
112+
list_pos: position;
113+
}
114+
106115
(** Literal values *)
107116
type literal =
108117
| IntLit of int * string option (* value * original_representation *)
@@ -171,6 +180,20 @@ and expr_desc =
171180
| Match of expr * match_arm list (* match (expr) { arms } *)
172181
| New of bpf_type (* new Type() - object allocation *)
173182
| NewWithFlag of bpf_type * expr (* new Type(gfp_flag) - object allocation with flag *)
183+
| ListOperation of list_operation (* List operations: push_front, pop_back, etc. *)
184+
185+
(** List operations for eBPF linked lists *)
186+
and list_operation = {
187+
list_expr: expr; (* The list variable being operated on *)
188+
operation: list_op_type;
189+
op_pos: position;
190+
}
191+
192+
and list_op_type =
193+
| PushFront of expr (* list.push_front(element) *)
194+
| PushBack of expr (* list.push_back(element) *)
195+
| PopFront (* list.pop_front() - returns element or null *)
196+
| PopBack (* list.pop_back() - returns element or null *)
174197

175198
(** Module function call *)
176199
and module_call = {
@@ -341,6 +364,7 @@ type declaration =
341364
| GlobalFunction of function_def
342365
| TypeDef of type_def
343366
| MapDecl of map_declaration
367+
| ListDecl of list_declaration
344368
| ConfigDecl of config_declaration
345369
| StructDecl of struct_def
346370
| GlobalVarDecl of global_variable_declaration
@@ -458,6 +482,13 @@ let make_map_declaration name key_type value_type map_type config is_global ~is_
458482
map_pos = pos;
459483
}
460484

485+
let make_list_declaration name element_type is_global pos = {
486+
list_name = name;
487+
element_type = element_type;
488+
is_global = is_global;
489+
list_pos = pos;
490+
}
491+
461492
let make_struct_def ?(attributes=[]) name fields pos = {
462493
struct_name = name;
463494
struct_fields = fields;
@@ -599,6 +630,7 @@ let rec string_of_bpf_type = function
599630
(string_of_bpf_type key_type)
600631
(string_of_bpf_type value_type)
601632
(string_of_map_type map_type)
633+
| List t -> Printf.sprintf "list<%s>" (string_of_bpf_type t)
602634
| Xdp_md -> "xdp_md"
603635
| UprobeContext -> "UprobeContext"
604636
| TracepointContext -> "TracepointContext"
@@ -684,6 +716,14 @@ let rec string_of_expr expr =
684716
| New typ -> Printf.sprintf "new %s()" (string_of_bpf_type typ)
685717
| NewWithFlag (typ, flag_expr) ->
686718
Printf.sprintf "new %s(%s)" (string_of_bpf_type typ) (string_of_expr flag_expr)
719+
| ListOperation op ->
720+
let op_str = match op.operation with
721+
| PushFront arg -> Printf.sprintf "push_front(%s)" (string_of_expr arg)
722+
| PushBack arg -> Printf.sprintf "push_back(%s)" (string_of_expr arg)
723+
| PopFront -> "pop_front()"
724+
| PopBack -> "pop_back()"
725+
in
726+
Printf.sprintf "%s.%s" (string_of_expr op.list_expr) op_str
687727

688728
and string_of_match_pattern = function
689729
| ConstantPattern lit -> string_of_literal lit
@@ -827,6 +867,10 @@ let string_of_declaration = function
827867
md.name
828868
(string_of_map_type md.map_type)
829869
(string_of_int md.config.max_entries)
870+
| ListDecl ld ->
871+
Printf.sprintf "var %s : list<%s>"
872+
ld.list_name
873+
(string_of_bpf_type ld.element_type)
830874
| ConfigDecl config_decl ->
831875
let fields_str = String.concat ",\n " (List.map (fun field ->
832876
let default_str = match field.field_default with

0 commit comments

Comments
 (0)