Kiểm tra và bật các tùy chọn Kernel cần thiết cho eBPF
Để eBPF hoạt động trên Raspberry Pi CM5 (ARM64), kernel phải được biên dịch với các tùy chọn hỗ trợ cụ thể. Nếu bạn đang dùng image Raspberry Pi OS mặc định, các tùy chọn này thường đã được bật, nhưng cần xác minh để đảm bảo môi trường sẵn sàng.
Các tùy chọn bắt buộc là CONFIG_BPF (hỗ trợ cơ bản) và CONFIG_BPF_SYSCALL (cho phép gọi syscall để load chương trình). Thiếu chúng sẽ gây lỗi Operation not supported khi chạy bpftool.
Thực hiện lệnh sau để kiểm tra trạng thái các tùy chọn trong kernel đang chạy:
grep -E "^CONFIG_BPF|^CONFIG_BPF_SYSCALL" /boot/config-$(uname -r)
Kết quả mong đợi: Bạn phải thấy hai dòng kết quả với giá trị y (biên dịch vào kernel) hoặc m (module động).
Nếu thấy # CONFIG_BPF is not set, bạn cần biên dịch lại kernel. Với Raspberry Pi CM5 chuẩn, thường không cần biên dịch lại, chỉ cần cập nhật kernel.
Để bật tính năng CONFIG_BPF_JIT (Just-In-Time compilation), giúp eBPF biên dịch bytecode ARM64 thành mã máy thực thi ngay lập tức, giảm overhead đáng kể trên CPU ARM. Kiểm tra bằng lệnh:
grep "CONFIG_BPF_JIT" /boot/config-$(uname -r)
Kết quả mong đợi: Dòng kết quả phải là CONFIG_BPF_JIT=y. Nếu là n, chương trình eBPF sẽ không chạy được hoặc bị từ chối bởi verifier.
Verify kết quả kiểm tra Kernel
Cách kiểm tra nhanh nhất là xem trạng thái eBPF trong sysfs:
cat /proc/sys/net/core/bpf_jit_enable
Kết quả mong đợi: Xuất ra số 1. Nếu là 0, JIT bị tắt. Để bật tạm thời (không cần reboot), chạy:
echo 1 | sudo tee /proc/sys/net/core/bpf_jit_enable
Cài đặt trình biên dịch LLVM và Clang hỗ trợ backend BPF
Khác với kernel, eBPF source code được viết bằng C và biên dịch bởi LLVM/Clang thành bytecode riêng biệt (không phải mã máy ARM64 trực tiếp). Clang phải có backend --target=bpf mới hỗ trợ.
Trên Raspberry Pi OS (dựa trên Debian), package mặc định thường là LLVM 12-14. Để đảm bảo tính tương thích tốt nhất với libbpf hiện đại, chúng ta cần cài đặt phiên bản mới nhất hoặc version stable hỗ trợ full BPF features.
Cập nhật repository và cài đặt các package cần thiết: clang, llvm, llvm-dev và libclang-rt-dev (cung cấp runtime headers cho eBPF).
sudo apt update && sudo apt install -y clang llvm libclang-rt-dev
Kết quả mong đợi: Quá trình cài đặt hoàn tất, không có lỗi missing dependencies. Thời gian cài đặt trên CM5 có thể mất 2-3 phút tùy tốc độ mạng.
Để xác minh Clang đã có khả năng biên dịch cho BPF, chạy lệnh test sau. Lệnh này yêu cầu Clang target là bpf.
clang --target=bpf --version
Kết quả mong đợi: Xuất ra thông tin phiên bản Clang kèm dòng Target: bpf. Nếu không thấy Target: bpf, backend BPF chưa được tích hợp đúng cách.
Để test biên dịch một file mẫu đơn giản (chưa cần run), tạo file test_bpf.c và biên dịch:
echo "int main() { return 0; }" > /tmp/test_bpf.c && clang -O2 -target bpf -c /tmp/test_bpf.c -o /tmp/test_bpf.o
Kết quả mong đợi: Lệnh chạy thành công, không có lỗi, và tạo ra file /tmp/test_bpf.o có kích thước khoảng 2-4KB. Đây là file object chứa eBPF bytecode.
Verify kết quả cài đặt LLVM/Clang
Sử dụng lệnh objdump để kiểm tra kiến trúc của file object vừa tạo. Nó phải hiển thị bpf thay vì aarch64.
objdump -f /tmp/test_bpf.o | grep "architecture:"
Kết quả mong đợi: Dòng đầu tiên của output phải là architecture: bpf. Nếu thấy aarch64, Clang đã biên dịch sai target.
Cài đặt thư viện libbpf và công cụ bpftool
libbpf là thư viện C giúp ứng dụng giao tiếp với kernel eBPF, trong khi bpftool là công cụ dòng lệnh (CLI) để quản lý map, program, link và inspect kernel eBPF runtime.
Trên Raspberry Pi OS, các package này thường nằm trong repo chính thức. Tuy nhiên, để đảm bảo version mới nhất (hỗ trợ các tính năng mới như BPF_CGROUP_SOCK_OPS), nên ưu tiên cài đặt từ repo hoặc build từ source nếu package mặc định quá cũ.
Trong bước này, chúng ta sẽ cài đặt từ repository chính thức của Debian/Raspberry Pi OS trước. Package cần thiết là libbpf-dev (headers để compile user-space app) và bpftool (CLI tool).
sudo apt install -y libbpf-dev bpftool
Kết quả mong đợi: Package được tải và cài đặt thành công. Nếu hệ thống báo lỗi "unable to locate package", có thể cần thêm repo hoặc build từ source (bài viết sẽ hướng dẫn build nếu apt thất bại ở bước verify).
Kiểm tra phiên bản của bpftool để đảm bảo nó tương thích với kernel đang chạy. bpftool cần biết version kernel để load map đúng định dạng.
bpftool version
Kết quả mong đợi: Xuất ra phiên bản bpftool (ví dụ: bpftool v5.10.0) và thông tin kernel version đang chạy. Không có lỗi runtime.
Để test khả năng tương tác với kernel, thực hiện lệnh liệt kê tất cả các eBPF programs đang chạy (dù hiện tại có thể chưa có gì).
sudo bpftool prog list
Kết quả mong đợi: Output trả về là error: no programs found hoặc danh sách trống. Quan trọng là lệnh chạy thành công, không báo lỗi permission denied hay command not found.
Nếu cần thư viện libbpf cho việc compile ứng dụng C sau này, kiểm tra file header:
ls /usr/include/bpf/libbpf.h
Kết quả mong đợi: File libbpf.h tồn tại. Nếu không thấy, cần cài lại package libbpf-dev.
Verify kết quả cài đặt libbpf và bpftool
Để chắc chắn toàn bộ stack eBPF đã hoạt động, hãy load một program mẫu đơn giản (Hello World) bằng bpftool. Tạo file C mẫu:
cat > /tmp/hello_bpf.c
Biên dịch file này thành object file BPF:
clang -O2 -target bpf -c /tmp/hello_bpf.c -o /tmp/hello_bpf.o
Load program vào kernel bằng bpftool:
sudo bpftool prog load /tmp/hello_bpf.o /sys/fs/bpf/hello_bpf type kprobe
Kết quả mong đợi: Lệnh chạy thành công và xuất ra thông tin về program ID, map ID (nếu có), và dòng loaded successfully.
Xác nhận program đã tồn tại trong kernel:
sudo bpftool prog list
Kết quả mong đợi: Danh sách hiển thị program hello_world với type kprobe và status running.
Để dọn dẹp sau khi test (giải phóng resource), chạy lệnh unload:
sudo bpftool prog delete id $(sudo bpftool prog list | grep hello_world | awk '{print $2}')
Kết quả mong đợi: Program bị xóa, danh sách prog list trở lại trạng thái ban đầu (rỗng).
Điều hướng series:
Mục lục: Series: Tối ưu hóa Linux Kernel cho Raspberry Pi CM5 với BPF và eBPF
« Phần 1: Chuẩn bị môi trường phần cứng và phần mềm cho Raspberry Pi CM5
Phần 3: Giới thiệu kiến trúc eBPF và cơ chế hoạt động trên ARM64 »