Hello eBPF: Global Variables (10)

Welcome back to my series on ebpf; in the last post, we learned how to write a simple XDP-based packet filter. In this post, we’ll continue the work on eBPF to make it easier to write more complex programs. Yes, I promised to write a load balancer but instead opted to add support for global variables to hello-ebpf, documenting it in this short post.

When we want to configure our eBPF program, say to set a simple logLevel setting, we currently have only one option: We could create an array map with one entry, our configuration value, and then use the usual methods to set its value and retrieve it. In Java, this would look like:

@BPFMapDefinition(maxEntries = 1)
BPFArray<Integer> logLevel;

void setLogLevel(int level) {
    logLevel.set(0, level);
}

In the ebpf program itself, see Hello eBPF: Recording data in basic eBPF maps (2) for more information; the value would be used as:

struct { // this is auto-generated by hello-ebpf
    __uint (type, BPF_MAP_TYPE_ARRAY);
    __type (key, u32);                
    __type (value, s32);              
    __uint (max_entries, 1);
} logLevel SEC(".maps");                           

s32 getLogLevel() {        
    u32 zero = 0;                            
    return *bpf_map_lookup_elem(&map, &zero);
}                

Memory Segmentation

This is quite cumbersome, especially as C already has a concept of global variables. Why couldn’t we just use these:

s32 logLevel;                           

s32 getLogLevel() {                          
    return logLevel;
}                                            

A program’s memory at runtime is split into multiple segments:

Segments as BPF Maps

Starting with Linux 5.2, d8eca5bbb2be (“bpf: implement lookup-free direct value access for maps”), we can directly access segments from the user-land as if they are a single-valued array map and can use the BPF Type Format information for every segment to see where each global variable is placed.

But how can we expose this to the user in user-land in a usable manner? We can extend the preprocessor to do its magic:

final GlobalVariable<Integer> logLevel =
    new GlobalVariable(/* initial value */ 42);

// later
program.logLevel.set(...);
// or
program.logLevel.get();

It is essential to state that the eBPF program can change the global variables, too, allowing us to have a simple communication channel between user-land and kernel-land.

This mechanism isn’t limited to scalar values; you can also store more complex values:

@Type
record Server(int ip, @Size(10) int[] ports, int portsCount) {}

final GlobalVariable<Server> server =
    new GlobalVariable<>(new Server(..., 
        new int[]{22, 23, 0, 0, 0, 0, 0, 0, 0, 0}, 2));

Conclusion

Using global variables, we can easily configure our eBPF and communicate between user-land and kernel-land. Add some preprocessor magic, and we have a powerful new feature in hello-ebpf. With this at hand, we can finally start writing a load balancer.

Thanks for joining us on the journey to create an eBPF library for Java. I’ll see you in two weeks for the next installment.

This article is part of my work in the SapMachine team at SAP, making profiling and debugging easier for everyone. Thanks to Dylan Reimerink for answering all my questions and sharing all his knowledge on eBPF; this blog post is based on one of his answers on StackOverflow.

Author

  • Johannes Bechberger

    Johannes Bechberger is a JVM developer working on profilers and their underlying technology in the SapMachine team at SAP. This includes improvements to async-profiler and its ecosystem, a website to view the different JFR event types, and improvements to the FirefoxProfiler, making it usable in the Java world. He started at SAP in 2022 after two years of research studies at the KIT in the field of Java security analyses. His work today is comprised of many open-source contributions and his blog, where he writes regularly on in-depth profiling and debugging topics, and of working on his JEP Candidate 435 to add a new profiling API to the OpenJDK.

    View all posts

New posts like these come out at least every two weeks, to get notified about new posts, follow me on Twitter, Mastodon, or LinkedIn, or join the newsletter: