Các cổng VPN hiệu năng cao khi xử lý hàng triệu gói tin mỗi giây (Mpps) thường chạm phải giới hạn vật lý ngay trong hệ thống quản lý bộ nhớ của nhân hệ điều hành. Khi các đường truyền dữ liệu 10 Gbps, 25 Gbps hoặc 40 Gbps liên tục đưa lượng lớn datagram UDP mã hóa vào bộ tập trung VPN, các đường dẫn cấp phát bộ nhớ truyền thống bị nghẽn nghiêm trọng. Phân tích tài nguyên nhân cho thấy phần lớn chu kỳ CPU bị tiêu tốn vào các hàm cấp phát trang (alloc_pages_node), hiện tượng tranh chấp khóa vùng bộ nhớ và các thao tác ánh xạ Direct Memory Access (DMA) lặp đi lặp lại.

Trình điều khiển mạng Linux truyền thống phải yêu cầu các trang bộ nhớ vật lý mới từ trình phân bổ buddy allocator của nhân cho mỗi gói tin đi vào hàng đợi nhận. Khi gói tin đi qua các bộ đệm socket (sk_buff) và thoát khỏi hàng đợi không gian người dùng hoặc phần cứng, trang nhớ đó bị giải phóng và trả ngược về RAM hệ thống. Ở tốc độ vượt quá 10 triệu gói tin mỗi giây, chu trình cấp phát và thu hồi liên tục này chiếm tới 30% tổng thời gian xử lý ngắt mềm (softirq).

Để triệt tiêu hao phí này, nhân Linux đã bổ sung giao diện lập trình Page Pool API (net/core/page_pool.c). Bằng cách duy trì các vùng đệm bộ nhớ chuyên dụng, phi khóa ngay tại tầng trình điều khiển mạng và bảo lưu liên tục ánh xạ DMA qua suốt vòng đời gói tin, Page Pool cho phép các cổng VPN duy trì đường truyền nhận gói tin không cần cấp phát mới.

Vấn đề hao phí bộ nhớ khi tiếp nhận gói tin tốc độ cao

Để hiểu rõ nguyên nhân khiến việc cấp phát bộ nhớ truyền thống làm suy giảm thông lượng VPN, hãy xem xét vòng đời của một gói tin khi đến card giao tiếp mạng (NIC):

Chu kỳ cấp phát gói tin truyền thống (Độ trễ bộ nhớ cao):
[ Vòng nhận NIC ] ---> Cần bộ đệm? ---> Gọi alloc_pages()
                                              |
                                    [ Trình phân bổ Buddy ] (Tranh chấp khóa!)
                                              |
                                              v
[ Trang vật lý được cấp ] <--- dma_map_page() (Độ trễ IOMMU / Bộ đệm phụ)
         |
         v
[ Tiếp nhận gói -> Ngắt mềm -> Giải mã mật mã -> Chuyển giao socket ]
         |
         v
[ Giải phóng gói ] ---------> dma_unmap_page() -> free_pages() -> [ Trả về RAM ]

Mô hình truyền thống này bộc lộ bốn điểm nghẽn nghiêm trọng dưới tải trọng hàng chục gigabit:

  1. Tranh chấp khóa phân bổ Buddy: Các lệnh gọi alloc_pages() chuẩn phải chiếm giữ khóa quay theo từng vùng (zone->lock). Khi nhiều lõi CPU đồng thời xử lý các đợt bùng nổ gói tin từ card mạng đa hàng đợi, tranh chấp khóa sẽ làm tuần tự hóa các luồng xử lý.
  2. Chi phí ánh xạ DMA liên tục: Bộ điều hợp mạng tốc độ cao cần địa chỉ DMA để ghi khung dữ liệu vào RAM máy chủ. Việc gọi dma_map_page() và dma_unmap_page() cho từng gói tin đơn lẻ buộc hệ điều hành phải liên tục cập nhật bảng dịch IOMMU và xóa bộ nhớ đệm TLB.
  3. Mất định vị bộ nhớ đệm: Các trang bộ nhớ mới cấp phát thường chưa có sẵn trong bộ nhớ đệm vi xử lý L1/L2. Ghi tiêu đề gói tin vào vùng nhớ nguội gây ra hiện tượng trượt cache tức thì.
  4. Phân mảnh bộ nhớ vật lý: Lưu lượng mạng đảo chiều liên tục làm xáo trộn các vùng cấp phát gói tin với bộ nhớ heap của các ứng dụng khác, dẫn đến tình trạng phân mảnh RAM theo thời gian.

Kiến trúc phân tầng của Linux Page Pool

Giao diện Page Pool giải quyết triệt để các vấn đề trên bằng cách thiết lập một đường ống tái chế bộ đệm vận hành nhịp nhàng với vòng lặp thăm dò NAPI của trình điều khiển.

Kiến trúc tái chế phân tầng của Linux Page Pool:
[ Trình điều khiển mạng: Vòng lặp thăm dò NAPI ]
                       |
                       v
            Có trang trong alloc_cache?
             /                     \
         [ CÓ ]                  [ KHÔNG ]
           |                         |
   Lấy trang (0 ns)       Nạp lại từ ptr_ring (Hàng đợi phi khóa an toàn)
           |                         |
           |             Vẫn trống? ---> Cấp phát hàng loạt từ Buddy Allocator
           |                         |
           +------------+------------+
                        |
                        v
          [ Tái sử dụng ánh xạ DMA sẵn có ] (Triệt tiêu phụ tải IOMMU!)
                        |
                        v
          [ Gắn vào sk_buff nhận dữ liệu ]

Kiến trúc Page Pool tổ chức bộ nhớ thành hai tầng đệm nội bộ:

1. Vùng nhớ đệm cấp phát nhanh theo CPU (alloc_cache)

Tầng cấp phát chính là một mảng các con trỏ trang vật lý (struct page *) được truy cập duy nhất trong ngữ cảnh ngắt mềm thăm dò NAPI. Do cơ chế NAPI bảo đảm rằng chỉ có đúng một lõi CPU thực thi chu trình thăm dò cho một hàng đợi phần cứng nhất định tại một thời điểm, vùng đệm này hoạt động hoàn toàn không cần khóa. Việc lấy một trang tái chế chỉ tốn một thao tác lấy phần tử khỏi mảng và giải tham chiếu con trỏ, tiêu tốn chưa đầy 10 chu kỳ xung nhịp CPU.

2. Vòng con trỏ phi khóa (ptr_ring)

Khi một gói tin được chuyển tiếp, giải mã hoặc gửi đến một socket trên một lõi CPU khác, lõi ở xa đó không thể hoàn trả trực tiếp trang nhớ về alloc_cache của hàng đợi ban đầu nếu không có cơ chế đồng bộ luồng. Thay vào đó, các lõi từ xa đẩy các trang đã sử dụng xong vào một hàng đợi vòng nhiều nguồn gửi an toàn (ptr_ring). Trong các chu kỳ thăm dò NAPI kế tiếp, hàng đợi tiếp nhận sẽ rút từng lô trang từ vòng này để bổ sung cho alloc_cache cục bộ.

3. Tái sử dụng liên tục ánh xạ DMA

Page Pool mặc định duy trì trạng thái ánh xạ DMA thông qua cờ PAGE_POOL_F_DMA_SYNC_DEV. Khi một trang nhớ quay trở lại vùng đệm, trình điều khiển chỉ cần thực thi lệnh đồng bộ bộ đệm nhẹ nhàng dma_sync_single_for_cpu() hoặc dma_sync_single_for_device(). Cấu trúc ánh xạ IOMMU đắt đỏ được giữ nguyên vẹn trong thanh ghi phần cứng, loại bỏ hoàn toàn chi phí hủy ánh xạ DMA.

Triển khai Page Pool trong tầng trình điều khiển

Các trình điều khiển mạng hiện đại (như mlx5, ixgbe, virtio_net và các giao diện ảo hóa tiên tiến) khởi tạo một Page Pool riêng cho từng hàng đợi nhận (RX):

#include <net/page_pool.h>

struct page_pool *init_vpn_rx_page_pool(struct net_device *netdev, int queue_idx) {
    struct page_pool_params params = {
        .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
        .order = 0,                         // Trang nhớ đơn 4KB
        .pool_size = 2048,                   // Độ sâu bộ đệm vòng
        .nid = dev_to_node(&netdev->dev),   // Nút bộ nhớ NUMA cục bộ
        .dev = &netdev->dev,                // Thiết bị đích cho ánh xạ DMA
        .dma_dir = DMA_FROM_DEVICE,         // Hướng nhận dữ liệu mạng
        .max_len = PAGE_SIZE,
        .offset = 0,
    };

    return page_pool_create(&params);
}

Trong chu trình nhận gói NAPI, trình điều khiển lấy trang nhớ tái chế trực tiếp từ hồ đệm:

// Cấp phát phi khóa siêu tốc bên trong NAPI poll của trình điều khiển
struct page *page = page_pool_dev_alloc_pages(pool);
if (!page) {
    // Dự phòng: Hủy khung dữ liệu hoặc ghi nhận cạn kiệt hàng đợi
    return -ENOMEM;
}

// Gắn trang tái chế trực tiếp vào bộ đệm socket
skb_add_rx_frag(skb, 0, page, 0, packet_len, PAGE_SIZE);

// Bàn giao theo dõi quyền sở hữu trang cho ngăn xếp mạng
page_pool_release_page(pool, page);

Khi ngăn xếp mạng hoàn tất việc xử lý dữ liệu gói tin, hệ thống tự động giảm bộ đếm tham chiếu trang. Khi số đếm về 0, trang nhớ tự động quay trở lại hàng đợi tái chế của Page Pool thay vì bị hủy về trình quản lý bộ nhớ toàn cục.

Giám sát chỉ số Page Pool trên cổng VPN trực tiếp

Linux hiển thị hiệu quả tái chế của Page Pool thông qua giao diện thống kê ethtool và hệ thống phụ Netlink page_pool. Kỹ sư mạng có thể giám sát hiệu quả tái chế theo thời gian thực:

# Kiểm tra các bộ đếm tái chế Page Pool tầng trình điều khiển qua ethtool
ethtool -S eth0 | grep -E "page_pool|recycle"

Các chỉ số đầu ra điển hình phản ánh trạng thái tối ưu của bộ đệm:

rx_pp_alloc_fast: 489210452      # Cấp phát tức thì từ bộ đệm phi khóa theo CPU
rx_pp_alloc_slow: 12045          # Cần nạp lại từ vòng con trỏ
rx_pp_alloc_slow_high_order: 0   # Phân bổ cấp cao dự phòng (bằng 0 là tối ưu)
rx_pp_alloc_empty: 0             # Sự cố cạn bộ nhớ (phải luôn bằng 0)
rx_pp_recycle_cached: 421094820  # Trang tái chế trực tiếp không qua bộ điều khiển RAM
rx_pp_recycle_ring: 68103587     # Trang hoàn trả an toàn từ các lõi CPU từ xa
rx_pp_recycle_freed: 12045       # Trang trả về RAM hệ thống khi thu hẹp hàng đợi

Tỷ lệ tái chế đạt trên 98% chứng minh trình điều khiển mạng đang luân chuyển hàng triệu datagram với chi phí cấp phát bộ nhớ thực tế gần bằng 0.

Kiến trúc tương thích trong RiceGuard VPN

RiceGuard, công nghệ lõi định tuyến tầng 3 phát triển bởi VPNRice, tích hợp trực tiếp với cơ chế Page Pool của Linux trên toàn bộ các cụm máy chủ trung chuyển:

  1. Đường truyền giải mã không cần cấp phát: Khi RiceGuard tiếp nhận các gói tin đường hầm mã hóa WireGuard hoặc RiceGuard ở mức nhiều gigabit, bộ đệm dữ liệu được giữ cố định trong các vòng trang nhớ đã được cấp phát trước. Các khung IP nội bộ sau giải mã được đưa thẳng vào giao diện ảo mà không cần sao chép trang nhớ.
  2. Cách ly vùng nhớ theo kiến trúc NUMA: RiceGuard ghim các vùng đệm Page Pool chính xác vào nút NUMA gắn trực tiếp với card mạng vật lý. Điều này triệt tiêu hoàn toàn lưu lượng trung chuyển qua bus liên kết QuickPath/UltraPath, dành trọn băng thông cho các thuật toán mã hóa ChaCha20-Poly1305.
  3. Hiệu quả thông lượng thực tế: Trong các thử nghiệm tải nặng xử lý 35 Gbps lưu lượng trung chuyển qua 40.000 đường hầm đồng thời, việc chuyển sang cơ chế tái chế Page Pool tầng trình điều khiển đã giảm 78% độ trễ cấp phát bộ nhớ nhân, triệt tiêu các đợt tăng vọt CPU do ngắt mềm và duy trì tốc độ truyền dẫn dây dẫn tối đa.

Câu hỏi thường gặp

Sự khác biệt cơ bản giữa trình cấp phát Linux Slab và Page Pool là gì?

Trình phân bổ Slab (kmalloc/SLUB) quản lý các đối tượng bộ nhớ nhân có kích thước nhỏ và thay đổi linh hoạt, đòi hỏi chi phí khóa quay khi cấp phát giữa các lõi. Ngược lại, Page Pool quản lý toàn bộ các trang nhớ vật lý kích thước cố định (thường là 4KB) chuyên biệt cho bộ đệm gói tin, cung cấp cơ chế lưu đệm phi khóa và duy trì ánh xạ DMA liên tục cho thông lượng cao.

Page Pool có làm giảm dung lượng RAM khả dụng của máy chủ VPN không?

Page Pool dành ra một lượng RAM cố định rất nhỏ được cấp phát trước (thường từ 8 MB đến 32 MB cho mỗi hàng đợi mạng) để tạo các vòng đệm gói tin. Bằng cách tái sử dụng liên tục lượng trang nhớ này, nó ngăn chặn triệt để hiện tượng phân mảnh bộ nhớ và ổn định áp lực RAM của toàn hệ thống.

Có thể kích hoạt Page Pool trên các giao diện ảo như tun hoặc tap không?

Có. Các phiên bản nhân Linux hiện đại (từ 5.15 trở lên) ngày càng hỗ trợ sâu rộng Page Pool cho các giao diện ảo hóa và trình điều khiển virtio_net. Điều này giúp các cổng VPN triển khai trên hạ tầng đám mây đạt được hiệu quả tái chế bộ nhớ tương đương với các máy chủ vật lý chuyên dụng.