Tạo Image Bootable Hoàn chỉnh với Kernel Tùy chỉnh
Bước 1: Chuẩn bị thư mục rootfs và chèn Kernel Module AI
Bạn cần tạo một cây thư mục rootfs trống để làm nền tảng cho image. Sau đó, copy file module kernel (.ko) mà bạn đã compile ở Phần 2 và Phần 4 vào đúng vị trí module.
Đảm bảo thư mục /lib/modules trong rootfs có cùng version với kernel bạn đang build. Nếu không, command modprobe sẽ không nhận diện được module.
Tạo cấu trúc thư mục và copy module:
mkdir -p /rootfs/lib/modules/$(uname -r)/kernel/drivers/ai
cp ./drivers/ai/npu_driver.ko /rootfs/lib/modules/$(uname -r)/kernel/drivers/ai/
cp ./drivers/ai/tflite_accel.ko /rootfs/lib/modules/$(uname -r)/kernel/drivers/ai/
Kết quả mong đợi: Thư mục trong rootfs xuất hiện các file .ko tương ứng, không báo lỗi permission.
Bước 2: Tạo file config modprobe tự động load
Để module AI tự động load khi hệ thống khởi động, bạn cần tạo file cấu hình trong thư mục modprobe.d. Điều này giúp NPU sẵn sàng ngay khi boot, không cần lệnh thủ công.
Tạo file /rootfs/etc/modules-load.d/ai-edge.conf với nội dung sau:
cat > /rootfs/etc/modules-load.d/ai-edge.conf
Kết quả mong đợi: File config được tạo thành công, chứa danh sách các module cần load.
Bước 3: Gộp Image với Raspberry Pi CM5 Firmware
Sử dụng công cụ genimage hoặc thủ công để gộp rootfs, kernel image (Image) và device tree (dtb) vào một file .img bootable cho CM5. Với CM5, bạn cần đảm bảo partition boot chứa firmware và config.txt đã chỉnh sửa.
Thực hiện lệnh chroot để tạo module dependency và build image:
cd /rootfs
cp /boot/Image boot/Image
cp /boot/*.dtb boot/
cp /boot/firmware/config.txt boot/firmware/config.txt
cp /boot/firmware/cmdline.txt boot/firmware/cmdline.txt
debootstrap --arch arm64 bookworm /rootfs http://archive.raspberrypi.org/debian/
# Sau khi debootstrap xong, cập nhật module dependencies
chroot /rootfs depmod -a
Kết quả mong đợi: Hệ thống file /rootfs hoàn chỉnh, lệnh depmod chạy không lỗi, tạo file modules.dep.
Bước 4: Đóng gói Image cuối cùng
Sử dụng công cụ buildroot hoặc script tùy chỉnh để tạo file .img từ cây rootfs đã chuẩn bị. Đây là bước quan trọng để đưa hệ thống lên phần cứng thực tế.
Script đóng gói image cho CM5 (giả sử dùng buildroot hoặc script thủ công với dd):
dd if=/boot/Image of=/rootfs/boot/Image
dd if=/boot/bcm2711-rpi-cm5.dtb of=/rootfs/boot/firmware/bcm2711-rpi-cm5.dtb
mkfs.ext4 -F /dev/loop0
mount /dev/loop0 /mnt
cp -a /rootfs/* /mnt/
umount /mnt
Kết quả mong đợi: File image .img hoặc hệ thống file đã sẵn sàng để flash vào SD Card hoặc eMMC của CM5.
Verify kết quả phần 1
Flash image vào CM5, cấp nguồn và kiểm tra console:
lsmod | grep npu
dmesg | grep -i "npu driver"
Kết quả: Lệnh lsmod hiển thị module npu_driver và tflite_accel. dmesg không báo lỗi về module không load.
Xử lý lỗi Kernel Panic và Oops khi khởi động Module AI
Bước 1: Bật chế độ Debug Kernel và Kdump
Khi gặp Kernel Panic hoặc Oops, màn hình thường tắt hoặc in ra log không đầy đủ. Bạn cần bật debug trong cmdline.txt để ghi lại stack trace chi tiết vào file log.
Chỉnh sửa file /boot/firmware/cmdline.txt (chú ý: file này phải trên một dòng duy nhất, không xuống dòng):
echo "console=tty1 console=ttyAMA0,115200 panic=1 loglevel=8 earlycon=uart8250,mmio32,0x7ff89000" > /boot/firmware/cmdline.txt
Kết quả mong đợi: Khi hệ thống crash, log chi tiết sẽ hiện ra trên console serial hoặc được ghi vào dmesg trước khi hệ thống reboot.
Bước 2: Phân tích Oops và Stack Trace
Khi module AI gây Oops, lỗi thường nằm ở việc truy cập địa chỉ bộ nhớ không hợp lệ (NULL pointer dereference) hoặc xung đột driver. Sử dụng tool crash hoặc phân tích trực tiếp log dmesg.
Lệnh trích xuất thông tin lỗi từ log:
dmesg | grep -A 20 "Oops:" | tee /rootfs/ai_crash_log.txt
cat /proc/kallsyms | grep npu_driver
Kết quả mong đợi: File log chứa đầy đủ thông tin về địa chỉ lỗi (EIP/PC), module gây lỗi và hàm bị crash.
Bước 3: Chỉnh sửa Driver để xử lý lỗi Null Pointer
Nguyên nhân phổ biến nhất là driver cố gắng truy cập cấu trúc device trước khi nó được khởi tạo xong. Bạn cần thêm kiểm tra NULL trong file source driver trước khi gọi hàm.
Chỉnh sửa file source (ví dụ: npu_driver.c) - thêm đoạn kiểm tra bảo vệ:
// Trong hàm npu_probe hoặc npu_init
if (!dev) {
dev_err(&pdev->dev, "Device structure is NULL\n");
return -ENODEV;
}
if (!dev->platform_data) {
dev_err(&pdev->dev, "Platform data missing\n");
return -EINVAL;
}
Kết quả mong đợi: Code compile thành công, khi chạy lại nếu thiếu thiết bị sẽ báo lỗi rõ ràng thay vì gây Kernel Panic.
Bước 4: Cấu hình Kdump để ghi Core Dump tự động
Để phân tích sâu hơn khi hệ thống sập, cấu hình Kdump để ghi kernel dump vào file trên disk thay vì reboot ngay.
Tạo file /etc/kdump.conf:
cat > /etc/kdump.conf
Kết quả mong đợi: Dịch vụ kdump chạy, khi crash xảy ra, file dump sẽ được lưu vào /var/crash.
Verify kết quả phần 2
Tạo lỗi giả lập bằng cách load module bị lỗi (nếu có) và kiểm tra log:
modprobe -f npu_driver
dmesg | tail -n 50
Kết quả: Log hiển thị thông báo lỗi chi tiết từ driver thay vì hệ thống treo hoàn toàn.
Debug xung đột tài nguyên giữa GPU và NPU
Bước 1: Kiểm tra xung đột DMA và IOMMU
GPU và NPU trên CM5 đều cần truy cập bộ nhớ DMA. Nếu Device Tree không cấu hình đúng IOMMU, hai thiết bị có thể ghi đè lên vùng nhớ của nhau gây crash hoặc dữ liệu bị hỏng.
Trích xuất thông tin DMA từ Device Tree và log kernel:
dtc -I dtb -O dts /boot/firmware/bcm2711-rpi-cm5.dtb | grep -A 5 "dma"
dmesg | grep -i "dma" | grep -i "conflict\|fail"
Kết quả mong đợi: Xác định được vùng address mà GPU và NPU đang tranh chấp.
Bước 2: Điều chỉnh Device Tree (DTS) cho vùng nhớ riêng biệt
Bạn cần sửa file .dts để gán vùng nhớ riêng (reserved-memory) cho NPU, tránh vùng mà GPU đang dùng cho framebuffer hoặc CMA.
Chỉnh sửa file bcm2711-rpi-cm5.dts (hoặc file overlay .dts):
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target = ;
__overlay__ {
npu_memory: memory@40000000 {
reg = ; // 256MB cho NPU
no-map;
};
};
};
};
Kết quả mong đợi: File .dts được biên dịch thành .dtb, vùng nhớ 256MB được reserve riêng cho NPU.
Bước 3: Cấu hình IOMMU trong Driver và Kernel
Để đảm bảo cách ly, bật tính năng IOMMU trong kernel và cấu hình driver NPU để sử dụng IOVA (IO Virtual Address) thay vì physical address trực tiếp.
Thêm tham số vào cmdline.txt để bật IOMMU:
echo "iommu=pt" >> /boot/firmware/cmdline.txt
Và trong source driver (npu_driver.c), thay đổi cách cấp phát buffer:
// Sử dụng DMA mapping với IOMMU
dma_handle = dma_map_coherent(dev, GFP_DMA, buffer, size);
if (dma_mapping_error(dev, dma_handle)) {
dev_err(dev, "DMA mapping failed\n");
return -ENOMEM;
}
Kết quả mong đợi: Không còn báo lỗi DMA conflict trong dmesg, hệ thống ổn định khi GPU và NPU chạy cùng lúc.
Verify kết quả phần 3
Chạy benchmark song song GPU (GLES) và NPU (AI Inference) và kiểm tra log:
stress-ng --cpu 4 &
glmark2-es2 &
python ai_inference.py &
dmesg | grep -i "dma\|fault"
Kết quả: Không xuất hiện lỗi DMA fault hoặc Kernel Panic khi chạy song song.
Mẹo nâng cao: Tối ưu hóa bộ nhớ cho nhiều model chạy song song
Bước 1: Cấu hình CMA (Contiguous Memory Allocator)
Khi chạy nhiều model AI song song, kernel cần phân vùng bộ nhớ liên tục lớn để tránh phân mảnh. Bạn cần tăng kích thước CMA trong cmdline.txt.
Chỉnh sửa /boot/firmware/cmdline.txt:
echo "cma=512M" >> /boot/firmware/cmdline.txt
Kết quả mong đợi: Kiểm tra /proc/meminfo thấy dòng CmaTotal tăng lên 512MB.
Bước 2: Sử dụng Huge Pages cho Model AI
Giảm overhead của TLB (Translation Lookaside Buffer) bằng cách cấp phát Huge Pages (2MB hoặc 1GB) cho các tiến trình AI. Điều này đặc biệt quan trọng khi load model lớn lên NPU.
Lệnh cấp phát Huge Pages và cấu hình ứng dụng:
echo 256 > /proc/sys/vm/nr_hugepages
export MALLOC_HUGETLB=2
ulimit -l unlimited
Kết quả mong đợi: Ứng dụng AI sử dụng bộ nhớ hiệu quả hơn, giảm số lần context switch.
Bước 3: Điều chỉnh Governor CPU cho NPU
Để đảm bảo NPU luôn nhận đủ clock cycle khi xử lý song song, bạn cần cấu hình CPU governor để giữ tần số cao nhất cho các core xử lý AI.
Chỉnh sửa file /etc/rc.local hoặc systemd service để set governor:
for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
echo performance > $cpu
done
Kết quả mong đợi: CPU luôn chạy ở tần số tối đa, giảm độ trễ khi kích hoạt NPU.
Bước 4: Giới hạn bộ nhớ cho từng Model (cgroups)
Sử dụng cgroups v2 để giới hạn bộ nhớ cho từng tiến trình model, tránh việc một model chiếm hết RAM gây OOM Killer.
Tạo cấu hình cgroups cho 2 model chạy song song:
mkdir /sys/fs/cgroup/ai_model1
mkdir /sys/fs/cgroup/ai_model2
echo 512M > /sys/fs/cgroup/ai_model1/memory.max
echo 512M > /sys/fs/cgroup/ai_model2/memory.max
Kết quả mong đợi: Mỗi model chỉ được dùng tối đa 512MB RAM, hệ thống không bị sập do hết bộ nhớ.
Verify kết quả phần 4
Chạy 2 model song song và theo dõi sử dụng bộ nhớ:
cat /sys/fs/cgroup/ai_model1/memory.current
cat /sys/fs/cgroup/ai_model2/memory.current
free -h
Kết quả: Bộ nhớ của từng model nằm trong giới hạn đã đặt, tổng bộ nhớ hệ thống ổn định.
Điều hướng series:
Mục lục: Series: Tối ưu hóa Linux Kernel cho Raspberry Pi CM5 để chạy AI Edge
« Phần 5: Tối ưu hiệu năng và đo lường thông số thực tế