BPF Subsystem Core Part 2
Note: Line numbers may vary based on the version of the Linux kernel source code.
In the previous part, we explored the initial sections of the core.c file in the BPF subsystem. In this part, we will continue our exploration, covering additional functions and structures that are crucial for BPF operations.
bpf_prog_jited_linfo (Line 175)
This allocats space for line number mappings after JIT compilation.
Line 177: Early exit if no line info or no JIT requested.
if (!prog->aux->nr_linfo || !prog->jit_requested)
return 0;
Line 180: Allocate array
prog->aux->jited_linfo = kvcalloc(prog->aux->nr_linfo,
sizeof(*prog->aux->jited_linfo),
...);
This maps BPF instruction offsets -> native code addresses so debuggers/profilers can show source lines.
bpf_prog_jit_attempt_done (Line 189)
This function is called after JIT compilation completes or fails.
Line 191: Free line info if JIT failed.
if (prog->aux->jited_linfo &&
(!prog->jited || !prog->aux->jited_linfo[0])) {
kvfree(prog->aux->jited_linfo);
prog->aux->jited_linfo = NULL;
}
Line 197: Free kernel function call table.
kvfree(prog->aux->kfunc_tab);
prog->aux->kfunc_tab = NULL;
The kfunc_tab holds addresses of kernel functions called by the BPF program.
bpf_prog_fill_jited_linfo (Line 225)
This is complex but crucial for debugging. It creates a mapping from source line numbers to JIT-compiled code addresses.
Key concept: When you debug a BPF program with bpftool prog dump jited, this mapping allows you to see which lines of the original BPF code correspond to the JIT-compiled instructions.
bpf_prog_realloc (Line 254)
This function lets you resize a BPF program's instruction array (used during compilation or optimization).
Line 262: Check if we even need to resize.
if (pages == fp_old->pages)
return fp_old;
Line 266: Allocate new space and copy old data.
fp = __vmalloc(size, gfp_flags);
if (fp) {
memcpy(fp, fp_old, fp_old->pages * PAGE_SIZE);
Line 275: Transfer ownership of aux, clear old pointers.
fp_old->aux = NULL;
fp_old->stats = NULL;
fp_old->active = NULL;
__bpf_prog_free(fp_old);
This prevents double-free issues.
__bpf_prog_free (Line 284)
This frees all memeory associated with a BPF program.
Line 286: Destroy mutexes and free aux data.
if (fp->aux) {
mutex_destroy(&fp->aux->used_maps_mutex);
mutex_destroy(&fp->aux->dst_mutex);
kfree(fp->aux->poke_tab);
kfree(fp->aux);
}
Line 292: Free per-CPU data and the program itself.
free_percpu(fp->stats);
free_percpu(fp->active);
vfree(fp);
bpf_prog_calc_tag (Line 297)
This creates a hash of the program for identification. Line 315-318: It zeroes out map file descriptiors before hashing.
if (dst[i].src_reg == BPF_PSEUDO_MAP_FD ||
dst[i].src_reg == BPF_PSEUDO_MAP_VALUE) {
was_ld_map = true;
dst[i].imm = 0;
}
Why? Map FDs change each time the program loads, but the program logic is the same. By zeroing them out, we get a stable hash that identifies the program logic, not the runtime instance.