Profiling hiệu suất Kernel Module với ftrace và perf
Bước đầu tiên để tối ưu hóa module Shamir Secret Sharing là xác định điểm nghẽn (bottleneck) trong quá trình tính toán chia sẻ bí mật và thao tác I/O.
Chúng ta sẽ sử dụng ftrace để theo dõi luồng thực thi trong kernel và perf để đo lường chi tiết thời gian CPU tiêu tốn cho các hàm cụ thể.
Cấu hình và kích hoạt ftrace cho module
Trước tiên, cần bật tracing function để ghi lại thời điểm vào và ra của các hàm trong module của bạn.
Việc này giúp xác định hàm nào đang chiếm nhiều thời gian nhất trong thuật toán Shamir.
echo 1 > /sys/kernel/debug/tracing/events/ftrace/enable
echo "shamir_*" > /sys/kernel/debug/tracing/set_ftrace_filter
echo function_graph > /sys/kernel/debug/tracing/current_tracer
Kết quả mong đợi: File /sys/kernel/debug/tracing/trace sẽ bắt đầu ghi log các hàm có tên chứa "shamir" khi module hoạt động.
Chạy module và thu thập trace
Thực hiện một thao tác giả lập chia sẻ dữ liệu kích thước lớn (ví dụ: 1MB) để tạo tải cho module.
Đồng thời sử dụng lệnh cat để xem log trực tiếp hoặc ghi ra file để phân tích sau.
insmod /lib/modules/$(uname -r)/extra/shamir_kernel.ko
dd if=/dev/urandom of=/tmp/test_shamir.bin bs=1M count=1
cat /tmp/test_shamir.bin > /dev/shamir_share
cat /sys/kernel/debug/tracing/trace > /tmp/shamir_trace.log
Kết quả mong đợi: File /tmp/shamir_trace.log chứa danh sách các hàm được gọi kèm thời gian thực thi (ns/us). Bạn sẽ thấy các hàm như shamir_split hoặc shamir_combine có thời gian chạy dài bất thường.
Sử dụng perf để phân tích chi tiết CPU
ftrace cho biết thứ tự gọi hàm, nhưng perf cho biết chính xác hàm nào tiêu tốn bao nhiêu % CPU và có bị cache miss hay không.
Chúng ta sẽ dùng perf record để thu thập dữ liệu và perf report để hiển thị.
perf record -g -p $(pgrep -f shamir) -e cycles,instructions,cache-misses -a -- sleep 10
perf report --stdio --sort symbol
Kết quả mong đợi: Danh sách các hàm xếp theo % CPU tiêu thụ. Nếu thấy hàm shamir_poly_eval hoặc memcpy đứng đầu với cache-misses cao, đây là điểm cần tối ưu.
Verify kết quả: Kiểm tra file /tmp/shamir_trace.log và output của perf report. Nếu có các hàm module của bạn xuất hiện trong top 5, profiling thành công.
Phân tích log Kernel và xử lý lỗi Segmentation Fault
Khi module chạy trong không gian kernel, lỗi Segmentation Fault (dưới dạng Kernel Panic hoặc OOPS) sẽ gây sập hệ thống nếu không được xử lý đúng cách.
Chúng ta sẽ học cách đọc log OOPS và sử dụng gdb để debug module đang crash.
Đọc và phân tích log OOPS
Khi module gặp lỗi truy cập bộ nhớ không hợp lệ, kernel sẽ in stack trace ra dmesg.
Cần xác định địa chỉ bộ nhớ lỗi và hàm gây ra sự cố từ log này.
dmesg | tail -n 50
grep -A 20 "Call Trace" /var/log/kern.log
Kết quả mong đợi: Xuất hiện đoạn log bắt đầu bằng [] BUG: unable to handle kernel NULL pointer dereference hoặc general protection fault, kèm theo Call Trace liệt kê các hàm từ điểm crash ngược lên.
Cấu hình vmlinux và module symbols để debug
Để gdb hiểu được stack trace, file module .ko và file vmlinux phải chứa thông tin symbol (không bị strip).
Đảm bảo file debug symbol được đặt đúng vị trí hoặc chỉ định đường dẫn cho gdb.
ls -l /lib/modules/$(uname -r)/build/vmlinux
ls -l /lib/modules/$(uname -r)/extra/shamir_kernel.ko
Kết quả mong đợi: Các file này phải có kích thước lớn (chứa debug info). Nếu file quá nhỏ, cần compile lại với flag -g và tắt -s.
Debug lỗi với gdb và vmlinux
Sử dụng gdb để load kernel image và module, sau đó nạp file log OOPS để tái tạo stack trace và xem biến tại thời điểm crash.
Đây là bước quan trọng nhất để tìm ra biến nào bị null hoặc pointer sai lệch trong thuật toán Shamir.
gdb /lib/modules/$(uname -r)/build/vmlinux
(gdb) set kernelinfo vmlinux
(gdb) add-symbol-file /lib/modules/$(uname -r)/extra/shamir_kernel.ko
(gdb) source /path/to/oops.log
(gdb) backtrace
(gdb) frame 5
(gdb) print *pointer_variable
Kết quả mong đợi: GDB hiển thị stack trace chi tiết với tên hàm và dòng code. Lệnh print sẽ cho thấy giá trị thực tế của biến (ví dụ: (void *) 0x0) giúp xác nhận nguyên nhân lỗi.
Verify kết quả: Sau khi tìm ra lỗi (ví dụ: pointer null), sửa code, compile lại và chạy test. Lỗi OOPS phải biến mất và dmesg không còn thông báo lỗi mới.
Tối ưu hóa I/O và giảm độ trễ với dữ liệu lớn
Khi làm việc với dữ liệu lớn trong module Shamir, việc gọi I/O từng khối nhỏ (block-by-block) gây ra độ trễ lớn do hệ thống gọi interrupt liên tục.
Chúng ta sẽ áp dụng kỹ thuật Zero-Copy và tối ưu hóa buffer size để giảm số lần chuyển đổi ngữ cảnh (context switch).
Áp dụng Zero-Copy trong module
Thay vì copy dữ liệu từ User Space sang Kernel Space rồi mới xử lý, hãy sử dụng copy_from_user hiệu quả hoặc sử dụng get_user_pages để ánh xạ trực tiếp vào kernel address space nếu cần xử lý lâu.
Dưới đây là đoạn code mẫu trong file shamir_io.c để tối ưu hóa việc đọc dữ liệu.
Đường dẫn file: /lib/modules/$(uname -r)/extra/shamir_io.c
static ssize_t shamir_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) {
void *kernel_buf;
int ret;
// Tránh copy nhiều lần bằng cách phân bổ buffer lớn đủ cho chunk xử lý
// Tối ưu: Sử dụng kmalloc với GFP_KERNEL | __GFP_ZERO
kernel_buf = kmalloc(count, GFP_KERNEL | __GFP_ZERO);
if (!kernel_buf)
return -ENOMEM;
// Copy dữ liệu một lần duy nhất từ user space
ret = copy_from_user(kernel_buf, buf, count);
if (ret) {
kfree(kernel_buf);
return -EFAULT;
}
// Gọi hàm xử lý Shamir trực tiếp trên kernel_buf
// Hàm này thực hiện tính toán trong RAM, không ra disk ngay lập tức
shamir_process_shares(kernel_buf, count);
// Ghi vào disk chỉ khi đủ block (batching)
if (count >= SHAMIR_BLOCK_SIZE) {
write_to_disk_batch(kernel_buf, count);
}
kfree(kernel_buf);
return count;
}
Kết quả mong đợi: Giảm đáng kể số lần gọi hàm I/O hệ thống, tăng thông lượng (throughput) khi xử lý file lớn.
Tối ưu hóa Buffer Size và Batch Processing
Cấu hình module để xử lý dữ liệu theo batch thay vì từng byte.
Chỉnh sửa file cấu hình module hoặc biến module parameter để tăng kích thước buffer xử lý.
Đường dẫn file: /etc/modprobe.d/shamir.conf
options shamir_kernel block_size=65536 batch_threshold=4194304
Kết quả mong đợi: Module sẽ chỉ thực hiện thao tác ghi disk (I/O heavy) khi tích lũy đủ 4MB dữ liệu, giảm độ trễ và tăng tốc độ xử lý tổng thể.
Điều chỉnh Scheduler cho Thread xử lý
Nếu module sử dụng kworker hoặc kernel thread để xử lý thuật toán Shamir song song, cần ưu tiên lịch trình (scheduling) cho các thread này.
Sử dụng chrt hoặc cấu hình task_struct để đặt priority cao hơn cho thread xử lý tính toán.
chrt -f 90 -p $(cat /proc/modules | grep shamir | awk '{print $2}')
# Hoặc trong code module, thiết lập policy:
// current->policy = SCHED_FIFO;
// current->rt_priority = 90;
Kết quả mong đợi: Thread xử lý Shamir được CPU ưu tiên phục vụ, giảm độ trễ (latency) khi có tải hệ thống cao.
Verify kết quả: Chạy lại test với file 100MB. So sánh thời gian thực hiện (thông qua time) và thông số I/O (thông qua iostat -x 1). Bạn sẽ thấy util% của disk cao hơn nhưng await (thời gian chờ) thấp hơn, chứng tỏ I/O được tối ưu.
Điều hướng series:
Mục lục: Series: Triển khai Database phân tán với Shamir Secret Sharing và Linux Kernel Modules
« Phần 7: Đảm bảo độ tin cậy: Khôi phục dữ liệu và xử lý sự cố node