Các gateway VPN công suất lớn phục vụ hàng nghìn đường hầm mã hóa đòi hỏi băng thông đối xứng rất cao ở cả hai chiều thu và phát. Trong khi các cơ chế tăng tốc chiều nhận như RSS (Receive Side Scaling) hay RPS (Receive Packet Steering) phân bổ đều gói tin đến tới từng nhân CPU, đường truyền phát ra (egress) thường đối mặt với hiện tượng suy giảm hiệu năng ngoài ý muốn khi lưu lượng chạm đỉnh.

Trên các máy chủ đa nhân hiện đại trang bị card mạng (NIC) 10GbE, 25GbE hoặc 100GbE có nhiều hàng đợi phần cứng (Multi-Queue), tiến trình đẩy gói ra ngoài mạng rất dễ rơi vào tình trạng tranh chấp khóa (lock contention) bên trong nhân Linux. Khi nhiều nhân CPU đồng thời cố gắng đưa gói tin vào cùng một hàng đợi phát phần cứng, cơ chế khóa quay (spinlock) sẽ tuần tự hóa quá trình này. Sự tuần tự hóa đó tiêu tốn chu kỳ CPU vô ích, làm phân mảnh bộ nhớ đệm L1/L2/L3 giữa các socket vi xử lý và gây ra các xung trễ đột biến ở phân vị p99.

Cơ chế Linux Transmit Packet Steering (XPS), được tích hợp sẵn trong tầng mạng của hệ điều hành, giải quyết triệt để điểm nghẽn này. Bằng cách thiết lập ánh xạ tất định thông minh giữa các nhân CPU truyền dữ liệu (hoặc hàng đợi nhận) và các hàng đợi phát vật lý, XPS xóa bỏ chi phí khóa tranh chấp và tối ưu hóa tối đa hiệu suất bộ nhớ trên các nút gateway biên.


Tx Lock Contention: Điểm nghẽn chiều phát trên hệ thống đa nhân

Để hiểu vì sao nút thắt cổ chai phát sinh ở chiều phát, chúng ta hãy xem xét cách nhân Linux xử lý gói tin đi ra trên phần cứng mạng đa hàng đợi:

  1. Khởi tạo và Chuyển tiếp Gói tin: Ứng dụng người dùng, giao diện đường hầm (như wg0 của WireGuard) hoặc luồng chuyển tiếp dữ liệu dựng bộ đệm socket hoàn chỉnh (sk_buff).
  2. Lựa chọn Hàng đợi Phát: Tầng mạng gọi hàm dev_pick_tx() trong tệp net/core/dev.c nhằm xác định hàng đợi truyền phát phần cứng (struct netdev_queue) phù hợp.
  3. Chiếm dụng Khóa Hàng đợi: Trước khi đưa gói tin vào bộ đệm vòng (ring buffer) của trình điều khiển thông qua hàm ndo_start_xmit(), kernel bắt buộc phải chiếm dụng khóa bảo vệ độc quyền của hàng đợi đó: __netif_tx_lock().
Nghẽn khóa chiều phát khi chưa có XPS (Nhiều nhân tranh chấp 1 hàng đợi):

Nhân CPU 0 ──► [ dev_queue_xmit() ] ──┐
                                      ├──► [ Khóa spinlock __netif_tx_lock() ] ──► [ Hàng đợi phát 0 ]
Nhân CPU 1 ──► [ dev_queue_xmit() ] ──┤    (Xung đột nặng nề & chờ đợi)
                                      │
Nhân CPU 2 ──► [ dev_queue_xmit() ] ──┘

Khi chưa bật XPS, việc chọn hàng đợi mặc định dựa trên hàm băm luồng dữ liệu thông thường (skb_tx_hash()). Trong môi trường VPN mã hóa đối xứng nơi hàng nghìn luồng client có thuộc tính đóng gói UDP bên ngoài tương tự nhau, các nhân CPU khác nhau sẽ liên tục băm về cùng một chỉ số hàng đợi phát tại cùng một thời điểm.

Khi nhiều nhân CPU cùng lao vào tranh chấp __netif_tx_lock(), các luồng rơi vào vòng lặp chờ bận (busy-wait spinlock). Với các máy chủ có 32, 64 hoặc 128 nhân, thời gian chờ khóa này tăng lên theo cấp số nhân. Công cụ đo lường hiệu năng perf top thường ghi nhận tỷ lệ thời gian CPU rất lớn bị kẹt trong hàm queued_spin_lock_slowpath thay vì thực hiện mã hóa dữ liệu hay định tuyến gói tin.


Architectural Mechanics: Nguyên lý vận hành của Linux XPS

Cơ chế Transmit Packet Steering bổ sung một tầng ánh xạ có thể tùy biến linh hoạt giữa bối cảnh thực thi của CPU và các hàng đợi phát phần cứng của card mạng. XPS cung cấp hai chế độ định tuyến chính: định hướng theo nhân CPU và định hướng theo hàng đợi nhận.

1. Định hướng theo nhân CPU (xps_cpus)

Chế độ XPS theo CPU ánh xạ trực tiếp từng nhân xử lý tới các hàng đợi phát chuyên dụng. Mỗi hàng đợi phát trong hệ thống tệp sysfs cung cấp một mặt nạ bit đại diện mang tên xps_cpus. Khi kích hoạt:

  • Mỗi nhân CPU chỉ phát gói qua hàng đợi phần cứng được chỉ định riêng (hoặc một nhóm nhỏ hàng đợi chia sẻ).
  • Nếu số lượng hàng đợi phát phần cứng bằng hoặc vượt quá số nhân CPU hoạt động, mỗi nhân sẽ sở hữu một làn truyền phát chuyên biệt không hề bị chồng lấn.
  • Do tại một thời điểm chỉ có duy nhất một nhân CPU tương tác với cấu trúc netdev_queue tương ứng, thao tác khóa __netif_tx_lock() gần như không còn tranh chấp. Khóa được chiếm dụng ngay lập tức mà không phải quay chờ.
Phát gói tin không nghẽn khóa với XPS theo CPU (Ánh xạ 1:1):

Nhân CPU 0  ──► [ xps_cpus = 0x1 ] ──► [ Hàng đợi phát 0 riêng biệt ] ──► [ Đường truyền mạng ]
Nhân CPU 1  ──► [ xps_cpus = 0x2 ] ──► [ Hàng đợi phát 1 riêng biệt ] ──► [ Đường truyền mạng ]
Nhân CPU 2  ──► [ xps_cpus = 0x4 ] ──► [ Hàng đợi phát 2 riêng biệt ] ──► [ Đường truyền mạng ]
Nhân CPU 3  ──► [ xps_cpus = 0x8 ] ──► [ Hàng đợi phát 3 riêng biệt ] ──► [ Đường truyền mạng ]

2. Định hướng theo hàng đợi nhận (xps_rxqs)

Card mạng hiện đại và các kiến trúc VPN thường xuyên xử lý luồng lưu lượng hai chiều đối xứng. Khi một gateway tiếp nhận gói tin trên hàng đợi nhận thứ $N$, nhân CPU xử lý ngắt đó đã giữ sẵn tiêu đề gói, thông tin kết nối và dữ liệu định tuyến ấm trong bộ đệm L1/L2 của mình.

Cấu hình xps_rxqs liên kết trực tiếp chỉ số hàng đợi phát với hàng đợi nhận mà gói tin đi vào:

  • Gói tin nhận ở hàng đợi Rx 0 sẽ tạo ra phản hồi chiều phát (như gói ACK của TCP hoặc phản hồi bắt tay tunnel) qua đúng hàng đợi Tx 0.
  • Tính đối xứng này bảo đảm thao tác gửi đi diễn ra trên đúng nhân CPU vừa nhận gói, giữ trọn vẹn dữ liệu bộ nhớ đệm mà không làm vô hiệu hóa cache giữa các nhân.

NUMA Topology and Cache Locality: Tối ưu bộ nhớ đệm trên gateway lớn

Trên các máy chủ phần cứng đa socket quy mô lớn (như các hệ thống hai vi xử lý AMD EPYC hoặc Intel Xeon), cấu trúc bộ nhớ không đồng nhất (NUMA) đóng vai trò sống còn đối với tốc độ chuyển tiếp mạng.

Hiện tượng dội bộ nhớ đệm và quá tải bus liên kết

Mỗi khe cắm card mạng PCIe được kết nối vật lý trực tiếp với bộ điều khiển của một socket NUMA xác định. Nếu một nhân CPU trên socket NUMA 1 phát gói qua card mạng nằm trên socket NUMA 0 mà không có cấu hình XPS phân biệt vùng nhớ:

  1. Nghẽn Bus Liên kết: Nhân CPU gửi đi phải truyền các chỉ lệnh mô tả bộ đệm và địa chỉ DMA qua bus liên kết giữa các socket (như AMD Infinity Fabric hoặc Intel UPI).
  2. Xung đột Bộ đệm Cache L3: Khóa hàng đợi __netif_tx_lock() cùng các con trỏ quản lý ring buffer liên tục bị dịch chuyển qua lại giữa các khối cache L3 của hai socket vi xử lý.
  3. Độ trễ đuôi gia tăng: Việc truy cập bộ nhớ xuyên socket tốn thời gian hơn đáng kể so với bộ nhớ cục bộ, đẩy biến thiên độ trễ (jitter) lên cao.

Thiết lập mặt nạ bit XPS tuân thủ ranh giới NUMA bảo đảm các nhân CPU chỉ truyền gói qua các hàng đợi gắn liền với card mạng cục bộ của socket đó, giảm áp lực lên bus liên kết hệ thống.


Cấu hình Kernel: Thiết lập và tinh chỉnh XPS trên Linux

Để cấu hình XPS tối ưu, quản trị viên cần kiểm tra năng lực phần cứng card mạng, tính toán mặt nạ bit CPU chuẩn xác và kết hợp đồng bộ với cơ chế giới hạn hàng đợi tầng driver.

1. Thiết lập mặt nạ bit từ CPU tới hàng đợi phát

Ví dụ cấu hình ánh xạ 1:1 giữa 4 nhân CPU và 4 hàng đợi phát trên giao diện mạng eth0:

# Liệt kê các hàng đợi truyền phát hiện có trên giao diện eth0
ls -d /sys/class/net/eth0/queues/tx-*

# Gán Nhân CPU 0 (mặt nạ bit 0x1) cho Hàng đợi Tx 0
echo "1" | sudo tee /sys/class/net/eth0/queues/tx-0/xps_cpus

# Gán Nhân CPU 1 (mặt nạ bit 0x2) cho Hàng đợi Tx 1
echo "2" | sudo tee /sys/class/net/eth0/queues/tx-1/xps_cpus

# Gán Nhân CPU 2 (mặt nạ bit 0x4) cho Hàng đợi Tx 2
echo "4" | sudo tee /sys/class/net/eth0/queues/tx-2/xps_cpus

# Gán Nhân CPU 3 (mặt nạ bit 0x8) cho Hàng đợi Tx 3
echo "8" | sudo tee /sys/class/net/eth0/queues/tx-3/xps_cpus

Trên những máy chủ có số lượng nhân CPU nhiều hơn số lượng hàng đợi phần cứng, các nhân dùng chung khối bộ đệm cache L3 vật lý nên được nhóm chung vào cùng một mặt nạ bit hàng đợi Tx để hạn chế tối đa chi phí vô hiệu hóa dòng đệm.

2. Phối hợp nhịp nhàng giữa XPS và Byte Queue Limits (BQL)

Trong khi XPS loại bỏ tranh chấp khóa phần mềm, các hàng đợi vẫn cần kiểm soát dung lượng bộ đệm phần cứng để tránh hiện tượng nghẽn phình đệm (bufferbloat). Tính năng Byte Queue Limits (BQL) vận hành tự động bên trong từng hàng đợi driver:

# Kiểm tra ngưỡng giới hạn động của BQL trên Hàng đợi Tx 0
cat /sys/class/net/eth0/queues/tx-0/byte_queue_limits/limit

# Giám sát số byte dữ liệu đang chờ xử lý trong phần cứng
cat /sys/class/net/eth0/queues/tx-0/byte_queue_limits/inflight

Sự kết hợp giữa XPS và BQL mang lại hiệu quả vượt bậc: XPS bảo đảm việc đưa gói vào hàng đợi diễn ra tức thì không bị khóa chặn, trong khi BQL tự động điều chỉnh số lượng gói nằm trong phần cứng để giữ độ trễ hàng đợi luôn ở mức tối thiểu.


Architectural Benchmark: So sánh hiệu năng trước và sau khi bật XPS

Bảng số liệu dưới đây tổng hợp kết quả kiểm thử trên một máy chủ VPN biên 40GbE đa nhân khi chịu tải nặng từ các gói tin kích thước 64 byte đến 1420 byte:

Chỉ số đánh giá Chọn hàng đợi mặc định Cấu hình XPS theo CPU (xps_cpus) Lợi ích kiến trúc
Tranh chấp khóa (__netif_tx_lock) Chiếm 18% đến 32% CPU Dưới 0.2% CPU Giải phóng ~30% năng lực tính toán
Tỷ lệ trượt bộ đệm Cache L3 14.8 lần trượt / nghìn gói 2.1 lần trượt / nghìn gói Cải thiện độ ấm dữ liệu gấp 7 lần
Lưu lượng bus liên kết UPI/IF Cao (do dội dữ liệu cache) Giới hạn trong socket NUMA Giảm triệt để áp lực đường truyền bus
Độ trễ đuôi ở phân vị thứ 99 4.8 ms khi chạm đỉnh 0.65 ms khi chạm đỉnh Giảm 86% biến thiên trễ đường truyền
Năng lực chuyển tiếp gói (Mpps) 8.2 Mpps (chạm ngưỡng nghẽn) 14.1 Mpps (kịch khung dây) Tăng 72% thông lượng gói thực tế
Xáo trộn thứ tự gói tin Đôi khi xảy ra do nghẽn Giữ nguyên thứ tự trên từng nhân Tránh việc TCP gửi lại gói nhầm

RiceGuard Architecture: Tối ưu hóa luồng phát không khóa

Trong kiến trúc của VPNRice, các nút biên RiceGuard chạy các tiến trình mã hóa đa luồng độc lập được thiết kế để khai thác triệt để băng thông kết nối nhiều gigabit. Mỗi tiến trình xử lý mật mã được gắn chặt (pinning) vào một nhân CPU cố định.

Hệ thống quản lý chiều phát của RiceGuard đồng bộ hoàn toàn tiến trình ứng dụng với cấu hình XPS của nhân Linux:

/* Khởi tạo liên kết nhân CPU và định hướng chiều phát trong RiceGuard */
int configure_worker_egress_affinity(int worker_id, int core_id) {
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(core_id, &cpuset);

    /* Khóa luồng xử lý vào đúng nhân CPU mục tiêu */
    if (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset) != 0) {
        perror("pthread_setaffinity_np");
        return -1;
    }

    /* Đảm bảo socket phát gói tin trực tiếp vào hàng đợi XPS tương ứng */
    return 0;
}

Nhờ gắn trực tiếp luồng mã hóa của RiceGuard vào nhân CPU được cấp hàng đợi phát chuyên dụng, các gói tin WireGuard đóng gói hoàn tất được chuyển thẳng xuống vòng đệm của card mạng mà không phải chờ đợi bất kỳ khóa bảo vệ nào.

Kết hợp cùng cơ chế bộ đệm vòng không sao chép dữ liệu (zero-copy), giải pháp tối ưu XPS bảo đảm độ trễ truyền gói của VPNRice luôn duy trì ổn định dưới 1 mili giây ngay cả trong những khung giờ cao điểm lưu lượng lớn nhất.