5 điểm bởi GN⁺ 2024-07-29 | 1 bình luận | Chia sẻ qua WhatsApp
  • Tối ưu hiệu năng mạng Linux là công việc diễn giải luồng gói tin đi qua NIC ring buffer, IRQ, NAPI, softIRQ, qdisc, TCP buffer đến socket của ứng dụng theo góc nhìn các điểm nghẽn
  • Trên đường nhận, NIC ghi gói tin vào RAM bằng DMA và phát sinh HardIRQ, trình điều khiển lên lịch NAPI để làm trống ring buffer trong NET_RX_SOFTIRQ rồi chuyển tiếp qua các tầng IP/TCP và vào bộ đệm nhận của socket
  • ethtool, /proc/net/softnet_stat, ss, netstat, sysctl là điểm khởi đầu cho việc quan sát và điều chỉnh; các trục chính gồm interrupt coalescing, IRQ affinity, RSS/RPS/RFS/aRFS, netdev_budget, netdev_max_backlog, txqueuelen, và TCP read/write buffer
  • Không có một cấu hình duy nhất áp dụng cho mọi hệ thống; tăng ring buffer có thể giảm drop nhưng làm tăng độ trễ, còn interrupt coalescing có thể giảm mức sử dụng CPU và HardIRQ nhưng phải đánh đổi bằng độ trễ
  • Xử lý gói tin hiệu năng cao có thể mở rộng với các lựa chọn như PACKET_MMAP, DPDK, PF_RING, XDP/AF_XDP, nhưng mỗi cơ chế bypass kernel, zero-copy hay đường đi nhanh trong kernel đều khác nhau về phụ thuộc phần cứng, mức chiếm dụng CPU và yêu cầu phiên bản kernel

Đường nhận Linux: từ NIC đến socket

  • Thiết bị mạng phát sinh IRQ để báo gói tin đến, và ánh xạ IRQ của Linux được lưu trong /proc/interrupts
  • Trình xử lý IRQ chạy ở mức ưu tiên rất cao và trong một số trường hợp chặn việc phát sinh thêm IRQ, vì vậy trình điều khiển sẽ dời các tác vụ dài ra ngoài ngữ cảnh IRQ
  • Việc xử lý trì hoãn này dùng softIRQ; trong xử lý nhận mạng, kernel tạo các luồng ksoftirqd/<cpu-number>, softnet_data, và poll_list cho từng CPU
  • net_dev_init đăng ký NET_RX_SOFTIRQ vào hệ thống softIRQ, và trình xử lý tương ứng là net_rx_action
  • Gói tin đến và xử lý NAPI

    • NIC ghi dữ liệu nhận từ mạng vào ring buffer trong RAM bằng DMA
    • Một số NIC là NIC multiqueue với nhiều ring buffer
    • Khi NIC phát sinh HardIRQ, trình xử lý IRQ của trình điều khiển sẽ chạy
    • Trình điều khiển xóa IRQ của NIC và gọi napi_schedule để khởi động vòng poll softIRQ của NAPI
    • napi_schedule thêm cấu trúc NAPI poll của trình điều khiển vào poll_list của CPU hiện tại và đặt pending bit cho softIRQ
    • Khi ksoftirqd gọi __do_softirq, trình xử lý net_rx_action của NET_RX_SOFTIRQ đang ở trạng thái pending sẽ được thực thi
  • GRO và đi vào protocol stack

    • net_rx_action kiểm tra NAPI poll list, budget và thời gian đã trôi qua để softIRQ không độc chiếm CPU
    • Hàm poll của trình điều khiển thu hoạch gói tin từ ring buffer trong RAM
    • Gói tin được chuyển cho napi_gro_receive
    • GRO(Generic Receive Offloading) là kỹ thuật offload dựa trên phần mềm, tái lắp ráp các gói nhỏ thành gói lớn để giảm số lượng gói mà ứng dụng phải xử lý
    • Nếu GRO không giữ lại gói tin, nó sẽ đi lên trên protocol stack qua netif_receive_skb
  • Phân nhánh theo việc RPS có được bật hay không

    • Khi RPS bị tắt:
      • netif_receive_skb chuyển dữ liệu tới __netif_receive_core
      • __netif_receive_core chuyển dữ liệu đến tap và các trình xử lý tầng giao thức đã đăng ký
    • Khi RPS được bật:
      • netif_receive_skb chuyển dữ liệu đến enqueue_to_backlog
      • Gói tin được đưa vào input queue theo từng CPU
      • Cấu trúc NAPI của CPU từ xa được thêm vào poll_list của CPU đó, và một IPI được xếp hàng để đánh thức luồng softIRQ của CPU từ xa
      • ksoftirqd của CPU từ xa sẽ thu hoạch gói tin từ CPU input queue bằng hàm poll process_backlog
  • IP, TCP, bộ đệm nhận của socket

    • Gói tin được nhận ở tầng IPv4 qua ip_rcv và đi qua netfilter cùng tối ưu hóa định tuyến
    • Dữ liệu hướng tới hệ thống hiện tại được chuyển tiếp lên các tầng giao thức cao hơn như UDP hoặc TCP
    • Trên đường nhận TCP, gói tin đi qua tcp_v4_rcv, máy trạng thái hữu hạn TCP và bước tra cứu socket trước khi vào bộ đệm nhận
    • Kích thước bộ đệm nhận tuân theo quy tắc tcp_rmem
    • Nếu tcp_moderate_rcvbuf được bật, kernel sẽ tự động điều chỉnh bộ đệm nhận
    • tcp_rmem chứa giá trị tối thiểu, mặc định và tối đa của bộ đệm nhận cho socket TCP
    • Dùng SO_RCVBUF sẽ tắt tự động điều chỉnh bộ đệm nhận của socket đó
    • net.core.rmem_max là giới hạn trên của kích thước bộ đệm nhận TCP; cửa sổ lớn hơn cho phép gửi nhiều dữ liệu hơn trước khi truyền ACK, nhờ đó giảm độ trễ và tăng thông lượng

Đường gửi Linux: từ ứng dụng đến NIC

  • Đường gửi đơn giản hơn đường nhận nhưng có sự tham gia của qdisc, TCP write buffer, DMA và IRQ
  • Khi ứng dụng gửi thông điệp bằng lời gọi như sendmsg, đường gửi TCP sẽ cấp phát skb_buff
  • Gói tin được đưa vào socket write buffer có kích thước tcp_wmem
    • tcp_wmem chứa giá trị tối thiểu, mặc định và tối đa của bộ đệm gửi cho socket TCP
    • Kernel điều chỉnh động kích thước bộ đệm gửi TCP trong khoảng từ giá trị tối thiểu đến tối đa
    • Dùng SO_SNDBUF sẽ tắt tự động điều chỉnh bộ đệm gửi của socket đó
    • net.core.wmem_max là giới hạn trên của kích thước bộ đệm gửi TCP
  • TCP header và IP header được tạo ra, sau đó đi qua LOCAL_OUT, định tuyến, POST_ROUTING, fragmentation rồi hàm gửi L2 được gọi qua dev_queue_xmit
  • qdisc đầu ra sử dụng độ dài txqueuelen và thuật toán default_qdisc
  • Trình điều khiển đưa gói tin vào TX ring buffer và thực hiện NET_TX_SOFTIRQ sau timeout tx-usecs hoặc sau tx-frames
  • NIC lấy gói tin từ RAM bằng DMA để truyền đi, rồi phát sinh HardIRQ sau khi truyền xong
  • Trình điều khiển xử lý IRQ này và lên lịch hệ thống NAPI poll để giải phóng RAM

Công cụ quan sát và các điểm kiểm tra cơ bản

  • /proc/net/softnet_stat

    • Mỗi dòng trong /proc/net/softnet_stat đại diện cho một lõi CPU, bắt đầu từ CPU0
    • Thống kê của từng cột được cung cấp ở dạng hexadecimal
    • Cột thứ 1 là số frame mà trình xử lý ngắt đã nhận
    • Cột thứ 2 là số frame bị drop do vượt quá netdev_max_backlog
    • Cột thứ 3 là số lần ksoftirqd dùng hết netdev_budget hoặc thời gian CPU dù vẫn còn việc phải xử lý
    • Các cột còn lại có thể khác nhau tùy theo phiên bản Linux
  • /proc/net/sockstatss

    • Trong /proc/net/sockstat, hãy kiểm tra trường mem
    • Giá trị này được tính bằng cách cộng tổng sk_buff->truesize của tất cả socket
    • ss là công cụ dump thống kê socket, có thể hiển thị thông tin tương tự netstat cùng với nhiều thông tin TCP và trạng thái hơn
    • ss -tm dùng để kiểm tra mức sử dụng bộ nhớ của socket TCP
    • rmem_alloc: bộ nhớ được cấp phát cho packet nhận
    • rcv_buf: tổng bộ nhớ có thể cấp phát cho packet nhận
    • wmem_alloc: bộ nhớ dùng cho packet gửi đã được chuyển xuống layer 3
    • snd_buf: tổng bộ nhớ có thể cấp phát cho packet gửi
    • wmem_queued: bộ nhớ được cấp phát cho packet gửi chưa được chuyển xuống layer 3
    • sock_drop: số packet bị drop trước khi được demultiplex tới socket
  • netstatsysctl

    • netstat là công cụ dòng lệnh xuất các kết nối mạng đang mở và thống kê protocol stack, đồng thời lấy thông tin từ hệ thống tệp /proc/net/
    • /proc/net/dev: thông tin thiết bị
    • /proc/net/tcp: thông tin socket TCP
    • /proc/net/unix: thông tin Unix domain socket
    • sysctl là lệnh thay đổi thiết lập hệ thống và mạng thay vì ghi trực tiếp giá trị vào hệ thống tệp /proc
    • sysctl -w variable=value dùng cho thay đổi tạm thời; với thay đổi vĩnh viễn, chỉnh sửa /etc/sysctl.conf rồi áp dụng bằng sysctl -p

Điều chỉnh ring buffer NIC và interrupt

  • Ring buffer NIC

    • RX ring buffer là một circular buffer FIFO kích thước cố định nằm trong RAM
    • Bản thân ring buffer không chứa dữ liệu packet mà chứa các descriptor trỏ tới skb đã được đưa vào RAM bằng DMA
    • Nếu thấy drop hoặc overrun, có thể tăng kích thước queue, nhưng tác dụng phụ là độ trễ có thể tăng
    • Trong nhiều trường hợp, chỉ cần tăng kích thước buffer nhận là có thể ngăn packet drop, đồng thời cho kernel thêm chút thời gian để làm trống buffer
    • Việc kiểm tra và thay đổi được thực hiện bằng ethtool
    • ethtool -g eth3: kiểm tra kích thước ring RX/TX hiện tại và giá trị tối đa
    • ethtool -G eth3 rx 8192 tx 8192: tăng buffer RX/TX lên giá trị tối đa
    • Theo dõi bằng ethtool -S eth3 và các bộ đếm như err, drop, over, miss, timeout, reset, collis
  • Gộp ngắt phần cứng

    • NIC có thể tích lũy các reference packet trong RX ring buffer cho đến khi đạt điều kiện timeout rx-usecs hoặc rx-frames rồi mới phát sinh HardIRQ; điều này được gọi là Interrupt coalescence
    • Nếu ngắt quá sớm, kernel sẽ thường xuyên bị gián đoạn khỏi công việc đang chạy, làm giảm hiệu năng hệ thống
    • Nếu ngắt quá muộn, có thể không rút traffic khỏi NIC đủ nhanh, dẫn đến ghi đè và mất traffic
    • Việc điều chỉnh gộp ngắt có thể giảm mức sử dụng CPU và HardIRQ, đồng thời tăng throughput, nhưng có thể phải đánh đổi bằng độ trễ
    • Có thể kiểm tra tham số coalesce bằng ethtool -c eth3, và thay đổi như ethtool -C eth3 adaptive-rx off rx-usecs 0 rx-frames 0
    • Chế độ adaptive cho phép card động ước lượng thiết lập coalescing dựa trên mẫu traffic và mẫu nhận của kernel

IRQ affinity và phân tán tải giữa các CPU

  • IRQ affinity

    • IRQ có thuộc tính smp_affinity xác định các lõi CPU có thể chạy ISR của IRQ đó
    • Căn chỉnh interrupt affinity và application thread affinity theo các lõi CPU cụ thể có thể cải thiện hiệu năng ứng dụng nhờ chia sẻ cache line
    • Mặc định, việc này do daemon irqbalance điều khiển
    • Trước khi điều chỉnh thủ công, cần dừng irqbalance
    • /proc/irq/<IRQ_NUMBER>/smp_affinity lưu bitmask hexadecimal biểu thị các lõi CPU
    • Trên máy chủ 4 lõi, giá trị mặc định f nghĩa là IRQ có thể được xử lý trên mọi CPU
    • echo 1 > /proc/irq/32/smp_affinity sẽ buộc chỉ dùng CPU0
    • Trên hệ thống có hơn 32 lõi, các nhóm 32-bit được phân tách bằng dấu phẩy
    • IRQ affinity chỉ có thể tăng hiệu năng trong những cấu hình rất cụ thể và workload được xác định trước, nên có thể là con dao hai lưỡi
  • RSS

    • Trên NIC tốc độ cao, nếu chỉ dùng một queue và một CPU để nhận packet, một lõi có thể phải gánh toàn bộ trách nhiệm xử lý dữ liệu trong khi các lõi khác nhàn rỗi
    • RSS(Receive-side scaling) là công nghệ NIC cho phép phân tán traffic qua nhiều queue gửi/nhận
    • NIC tính hash dựa trên source/destination IP và source/destination port TCP/UDP, gán packet của cùng một flow vào một queue duy nhất, đồng thời phân bố các flow tương đối đồng đều giữa các queue
    • RSS mang lại lợi ích xử lý nhận song song trong môi trường multiprocessing
    • Theo tài liệu Linux kernel, RSS nên được bật khi độ trễ là yếu tố quan trọng hoặc khi xử lý ngắt nhận là nút thắt cổ chai; trong mạng độ trễ thấp, cấu hình tối ưu là cấp phát số queue bằng với số CPU của hệ thống
  • RPS, RFS, aRFS

    • RPS(Receive Packet Steering) gần như là một triển khai phần mềm của RSS
    • Trong khi RSS chọn queue và CPU sẽ chạy trình xử lý ngắt phần cứng, RPS chọn CPU sẽ thực hiện xử lý giao thức ở phía trên trình xử lý ngắt
    • Cần CONFIG_RPS và mặc định được bật trên SMP
    • Việc cấu hình được thực hiện bằng bitmap CPU trong /sys/class/net/<dev>/queues/rx-<n>/rps_cpus
    • Nếu đã có RSS thì có thể không cần, nhưng vẫn hữu ích khi số CPU nhiều hơn số queue
    • RFS(Receive Flow Steering) mở rộng RPS đến mức locality của ứng dụng
    • RPS phân tán packet theo flow nhưng không xét đến việc ứng dụng không gian người dùng đang chạy trên CPU nào
    • RFS duy trì rps_sock_flow_table, là bảng flow-to-CPU toàn cục
    • Có thể điều chỉnh kích thước bảng bằng net.core.rps_sock_flow_entries
    • rps_dev_flow_table theo từng queue được dùng để giảm vấn đề sai thứ tự do packet còn tồn khi scheduler chuyển ứng dụng sang CPU mới
    • aRFS(Accelerated RFS) là cơ chế cân bằng tải tăng tốc bằng phần cứng cho RFS
    • Nó gửi packet trực tiếp tới CPU gần với thread đang tiêu thụ dữ liệu, nên có thể cho hiệu năng tốt hơn RFS
    • Cần ndo_rx_flow_steer, ntuple filtering của NIC và CONFIG_RFS_ACCEL
    • Ánh xạ giữa CPU và queue được tự động suy ra từ IRQ affinity của từng queue nhận, nên không cần cấu hình thêm

softIRQ, tinh chỉnh qdisc và bộ đệm TCP

  • ngân sách softIRQ

    • Routine polling NAPI bị giới hạn bởi netdev_budget_usecs, netdev_budget, dev_weight để softIRQ không chiếm dụng CPU
    • Giá trị mặc định của net.core.netdev_budget là 300, nghĩa là tiến trình softIRQ sẽ làm trống 300 thông điệp từ NIC trước khi rời CPU
    • net.core.netdev_budget_usecs là số micro giây tối đa của một chu kỳ polling NAPI
    • net.core.dev_weight là số lượng gói tối đa trên mỗi CPU mà kernel có thể xử lý trong ngắt NAPI
    • Nếu các cột khác ngoài cột thứ 1 trong /proc/net/softnet_stat tăng lên thì có thể cần thay đổi budget, dù mức tăng nhỏ có thể là bình thường
  • qdisc ingress và netdev_max_backlog

    • netdev_max_backlog là hàng đợi nội bộ trong kernel, nơi lưu lượng được lưu sau khi nhận từ NIC nhưng trước khi được xử lý bởi stack giao thức như IP/TCP
    • Mỗi lõi CPU có một backlog queue riêng
    • Nếu interface nhận gói nhanh hơn tốc độ kernel có thể xử lý, hàng đợi phía INPUT sẽ đầy đến netdev_max_backlog, và các gói vượt quá sẽ bị drop
    • Giá trị mặc định là 1000, có thể không đủ cho nhiều interface 1Gbps hoặc một interface 10Gbps
    • Cột thứ 2 của /proc/net/softnet_stat là bộ đếm tăng lên khi backlog queue bị tràn
    • Thay đổi giá trị bằng sysctl -w net.core.netdev_max_backlog <value>
  • qdisc egress, txqueuelen, default_qdisc

    • txqueuelen thiết lập số gói được phép trong hàng đợi truyền của kernel trên thiết bị giao diện mạng
    • Giá trị mặc định có thể là 1000 tùy theo driver của interface
    • Thay đổi bằng ifconfig <interface> txqueuelen value, và kiểm tra RX/TX dropped trong ip -s link
    • default_qdisc là queuing discipline mặc định sẽ dùng cho thiết bị mạng
    • Có thể chỉ định các lựa chọn thay thế như sfq, codel, fq_codel thay cho pfifo_fast
    • Kiểm tra các chỉ số như dropped, overlimits, requeues trong tc -s qdisc ls dev <interface>
  • bộ đệm đọc/ghi TCP và hàng đợi kết nối

    • tcp_rmemtcp_wmem lần lượt định nghĩa giá trị tối thiểu, mặc định và tối đa của bộ đệm nhận/gửi TCP
    • Thay đổi như sau
      • sysctl -w net.ipv4.tcp_rmem="min default max"
      • sysctl -w net.ipv4.tcp_wmem="min default max"
    • Kiểm tra trạng thái sử dụng bộ đệm bằng /proc/net/sockstat
    • Accept queue và SYN queue chịu ảnh hưởng của net.core.somaxconnnet.ipv4.tcp_max_syn_backlog
    • net.core.somaxconn là giới hạn trên của tham số backlog trong listen(), và nếu thay đổi giá trị này thì ứng dụng cũng phải đổi sang giá trị tương thích
    • net.ipv4.tcp_syncookies bật hoặc tắt SYN cookie, hữu ích để bảo vệ trước tấn công SYN flood
    • net.ipv4.tcp_congestion_control thiết lập thuật toán congestion control sẽ dùng cho các kết nối mới

NUMA và hiệu năng mạng

  • NUMA (Non-uniform memory access) là kiến trúc bộ nhớ trong đó bộ xử lý có thể truy cập local memory nhanh hơn non-local memory
  • Trong xử lý mạng, CPU phải truy cập bộ nhớ ring buffer, nên NUMA locality có thể ảnh hưởng đến hiệu năng mạng
  • NUMA chia CPU, bộ nhớ và thiết bị thành nhiều node, và hoạt động như nhiều máy tính nhỏ có interconnect tốc độ cao và cùng một OS
  • Trên hệ thống NUMA, mục tiêu tinh chỉnh là gom ngắt của thiết bị vào các lõi CPU thuộc cùng node với thiết bị đó
  • Tuy vậy, hệ thống NUMA có thể tương tác không tốt với ứng dụng thời gian thực và gây ra độ trễ sự kiện không mong muốn
  • Có thể kiểm tra NUMA node tại /sys/devices/system/node/node*
  • Có thể kiểm tra locality của thiết bị tại /sys/class/net/<interface>/device/numa_node
    • -1 nghĩa là nền tảng phần cứng thực tế không phải NUMA, hoặc kernel đang mô phỏng NUMA, hoặc thiết bị không có NUMA locality
  • Linux kernel hỗ trợ NUMA từ phiên bản 2.5, và các bản phân phối dựa trên RedHat và Debian cung cấp numactl, numad
  • numad giám sát topology hệ thống và mức sử dụng tài nguyên, rồi cố gắng đặt các tiến trình có tải CPU và bộ nhớ đủ lớn vào NUMA locality hiệu quả

Các lựa chọn xử lý gói tin nhanh hơn

  • AF_PACKET v4 và PACKET_MMAP

    • AF_PACKET v4 là giao diện gói tin tốc độ cao của Linux, loại bỏ system call khỏi đường đi dữ liệu và mặc định sử dụng chế độ copy-mode
    • Khi dùng PACKET_ZEROCOPY, có thể sử dụng chế độ zero-copy thực sự bằng cách ánh xạ DMA packet buffer vào không gian người dùng
    • Đường đi thông thường từ file read đến socket send cần context switch giữa user mode và kernel mode cùng nhiều lần sao chép dữ liệu
    • zero-copy loại bỏ các lần sao chép dữ liệu dư thừa để tăng hiệu năng
    • PACKET_MMAP là API Linux dành cho packet sniffing tốc độ cao
    • Cung cấp ring buffer được mmap dùng chung giữa không gian người dùng và kernel
    • Giảm system call và việc sao chép giữa không gian người dùng với kernel khi gửi/nhận gói tin
  • DPDK

    • DPDK(Data Plane Development Kit) là thư viện không gian người dùng và framework driver cho xử lý gói tin tốc độ cao
    • Mục tiêu là trao đổi gói mạng ở tốc độ native giữa NIC và ứng dụng người dùng
    • Nhắm đến các NIC 10Gb hoặc 40Gb, với tốc độ là tiêu chí quan trọng nhất
    • Tập trung vào packet forwarding chứ không phải network stack
    • Khi DPDK điều khiển NIC, toàn bộ lưu lượng sẽ đi vòng qua kernel, và NIC đó sẽ không còn hiển thị với kernel
    • Cổng sẽ được unbind khỏi driver của Linux kernel và được quản lý bởi các driver như vfio_pci, igb_uio, uio_pci_generic
    • Sau đó, DPDK PMD sẽ đảm nhiệm việc giao tiếp giữa ứng dụng và NIC
    • DPDK cần cấu hình hugepages để cấp phát các chunk bộ nhớ lớn
    • Thành phần chính:
      • EAL: generic interface che giấu khác biệt môi trường
      • librte_ring: API FIFO lockless multi-producer, multi-consumer
      • librte_mempool: cấp phát pool đối tượng bộ nhớ
      • librte_mbuf: tạo và thao tác buffer chứa gói mạng
      • librte_timer: dịch vụ timer để thực thi hàm bất đồng bộ
      • PMD: driver để CPU liên tục poll NIC thay vì dùng interrupt
    • Hạn chế:
      • Phụ thuộc phần cứng rất lớn
      • Phải dành riêng CPU core để chạy PMD và sẽ sử dụng 100% CPU
  • PF_RING

    • PF_RING là Linux kernel module và framework user-space để xử lý gói tin ở tốc độ cao và cung cấp API nhất quán cho các ứng dụng xử lý gói tin
    • PF_RING poll gói tin từ NIC bằng Linux NAPI
    • NAPI sao chép gói từ NIC vào PF_RING circular buffer, sau đó ứng dụng không gian người dùng đọc gói từ ring
    • Cấu trúc này có hai poller là ứng dụng và NAPI, nên CPU cycle sẽ được dùng cho polling
    • Ưu điểm là có thể phân phối đồng thời incoming packet vào nhiều ring
    • Với cấu trúc mô-đun, có thể dùng thêm các thành phần như ZC module, FPGA-based card module, Stack module, Timeline module, Sysdig module
    • PF_RING đã giảm chi phí packet capture và userland forwarding, nhưng do cấu trúc hai actor gồm kernel sao chép từ NIC vào ring và userland đọc từ ring để xử lý nên hiệu năng tối ưu vẫn có giới hạn
    • PF_RING từ phiên bản 7.5 đã bao gồm hỗ trợ adapter AF_XDP
  • XDP và AF_XDP

    • XDP(eXpress Data Path) là một triển khai eBPF chặn gói tin ở giai đoạn sớm trong đường đi dữ liệu mạng của Linux
    • XDP xử lý trực tiếp RX packet page bên trong hàm RX của device driver, trước khi SKB được cấp phát
    • eBPF là bytecode sandboxed tùy biến chạy trong kernel
    • Sử dụng 11 thanh ghi 64-bit và stack 512-byte
    • Có thể biên dịch sang eBPF từ C, Lua, Go, P4, Rust... bằng LLVM backend
    • Cung cấp in-kernel verifier và JIT compiler, đồng thời hỗ trợ các tính năng như map, tail call và helper
    • Trường hợp sử dụng của XDP:
      • pre-stack filtering để giảm thiểu DoS
      • forwarding và load balancing
      • kỹ thuật batching như GRO
      • flow sampling và monitoring
      • xử lý ULP
    • XDP không phải là kernel bypass mà là fast path bên trong kernel stack, và không thay thế TCP/IP stack
    • Không cần phần cứng chuyên dụng, nhưng có các yêu cầu như NIC multi-queue, TX/RX checksum offload, RSS, TSO
    • Ưu điểm của XDP so với DPDK:
      • có thể chọn busy polling hoặc interrupt driven networking
      • không cần huge pages
      • không có yêu cầu phần cứng đặc biệt
      • không bắt buộc phải có CPU chuyên dụng
      • không cần bơm lại gói tin từ ứng dụng userspace của bên thứ ba vào kernel
      • không cần security model mới để truy cập phần cứng mạng
      • không cần code/licensing của bên thứ ba
    • AF_XDP là kiểu socket mới được giới thiệu trong Linux 4.18
    • Có thể tạo cấu trúc tương tự DPDK hoặc AF_PACKET bằng cách tận dụng các chức năng của kernel mà không cần đi vòng qua kernel hoàn toàn
    • XDP program có thể dùng eBPF để redirect frame vào memory buffer của không gian người dùng
    • DMA transfer hỗ trợ zero-copy bằng cách sử dụng bộ nhớ không gian người dùng
    • Có thể đạt mức cải thiện hiệu năng gấp 3~20 lần so với AF_PACKET
    • Hạn chế:
      • Là dự án còn khá mới
      • Để được hỗ trợ đầy đủ cần Linux kernel 5.4 trở lên

1 bình luận

 
GN⁺ 2024-07-29
Ý kiến trên Hacker News
  • Nếu tôi xem được tài liệu này chỉ vài tuần trước thì hẳn đã rất hữu ích
    Tôi đã lấy báo giá appliance phần cứng từ nhiều nhà cung cấp để triển khai mã hóa liên kết L2 giữa các trung tâm dữ liệu, nhưng thấy chi phí quá đắt nên đã tự làm thử
    Tôi cấu hình để chở các frame Ethernet trên mạng overlay WireGuard bằng phần cứng phổ thông và đạt 10Gbps; sau khoảng mười ngày làm việc, tôi triển khai được với chi phí rẻ hơn khoảng 70% so với đề xuất rẻ nhất và khoảng 95% so với đề xuất đắt nhất, nhưng phải đọc tài liệu chi tiết và thử nghiệm rất nhiều
    Tôi muốn dùng nội dung bài này để kiểm chứng xem hiểu biết của mình có đúng không; nhìn qua thì có vẻ đầy hứa hẹn và khá toàn diện

    • Tôi tò mò: trường hợp sử dụng là gì mà tunnel L3 lại không đủ?
    • Nếu có thể chia sẻ mã nguồn thì tôi muốn xem. Tôi rất tò mò bạn đã triển khai như thế nào
  • Khi có nhiều giá trị có thể điều chỉnh đến mức này, tôi nghĩ có lẽ đáng để tạo phần mềm tự động tinh chỉnh
    Có vẻ có thể dùng một cách tiếp cận giống gradient descent: chọn ngẫu nhiên một tham số trong whitelist, tăng hoặc giảm nhẹ trong phạm vi cho phép, đo hiệu năng trong một thời gian; nếu tệ đi thì hoàn nguyên, nếu tốt lên thì tiếp tục điều chỉnh

  • Bài viết thú vị, nhưng với tư cách kỹ sư phần mềm, tôi hiếm khi có cơ hội thực sự chạy các lệnh trong bài
    Phần lớn hệ thống chạy trong các container là bản rút gọn của một Linux nào đó, không có quyền truy cập shell vào hệ thống vận hành, còn môi trường dev hay QA thì quá khác production về tải và nhiều mặt khác nên việc tái hiện lỗi thường không giúp được mấy
    Rốt cuộc, cơ hội chạy thử các lệnh trong bài chỉ là khi nghịch hệ thống cá nhân; nếu làm kỹ sư nền tảng thì chắc sẽ hữu ích

    • Phần lớn các tính năng cấp thấp có lẽ dù sao cũng không hoạt động hoặc không hữu ích. Việc triển khai giao diện mạng của container thường khiến bạn phải xử lý cặp veth, và đủ loại xử lý kỳ quặc trong user space
      Một trong những điểm tôi không thích lắm ở Kubernetes là mô hình mạng. Nó giả định chỉ có một card mạng, và ứng dụng đủ đơn giản để không cần biết gì về các tầng bên dưới
      Toàn bộ mô hình mạng có vẻ còn nhiều chỗ để đại tu theo kiểu thập niên 2020 nhằm đơn giản hóa và cải thiện
    • Nếu có môi trường staging giống production nhất có thể, bạn có thể thử nghiệm và phân tích trong một môi trường giống production mà mình có quyền truy cập, nên tùy tình huống việc đó có thể hữu ích
  • net.core.wmem_max được mô tả là giới hạn trên của kích thước bộ đệm gửi TCP, và cũng có net.ipv4.tcp_wmem, nên tôi có hai thắc mắc

    1. Vì sao không có giá trị tương ứng cho IPv6, 2. Nó khác gì với net.core.wmem_max?
    • net.core.wmem_max đúng như tên gọi là giá trị tối đa
      net.ipv4.tcp_wmem là một bộ ba giá trị gồm tối thiểu, mặc định, tối đa; giá trị tối đa được chỉ định ở đây không thể vượt quá net.core.wmem_max nói trên
      TCP phải là cùng một giao thức, dù được chở trên IPv4 hay IPv6
      Ví dụ: https://docs.redhat.com/en/documentation/red_hat_data_grid/7...
  • Phần hơi thiếu ở đây là gỡ lỗi và tinh chỉnh cho thông lượng trên 100Gbps
    Khi phục vụ HTTP ở quy mô đó, nút thắt đầu tiên thường là băng thông bộ nhớ, nên nhiều trường hợp sẽ cần kTLS
    Các công cụ như AMD μProf rất hữu ích cho việc gỡ lỗi, và profiling liên tục dựa trên eBPF cũng giúp hiểu chính xác chuyện gì đang xảy ra trong kernel và user space

  • Trông khá hay. Trong sự nghiệp của tôi đến nay, mỗi khi cần hiệu năng, thường tôi bắt đầu bằng bypass kernel