使用 Onload
本頁說明如何搭配使用 Onload 與 U4 Compute Engine 執行個體。
關於 Onload
Onload 是高效能網路堆疊,適用於對延遲時間敏感的應用程式,可提供超低延遲、極小抖動和穩定效能。Onload 提供 TCP/IP 實作,可略過作業系統核心,直接在使用者空間中執行,同時讓應用程式使用標準 BSD Socket API。
搭配 ULL 解決方案使用 Onload 時,支援下列項目:
事前準備
在 U4 Compute Engine 執行個體上使用 Onload 之前,您必須符合下列條件。
建立 U4 執行個體
如果尚未建立 U4 Compute Engine 執行個體,請使用下列其中一個程序,其中包含 Onload 的必要設定:
- 如要建立 U4P 或 U4C 裸機執行個體,請參閱「建立 ULL Compute Engine 執行個體」。
- 如要建立 U4S 虛擬機器 (VM) 執行個體,請參閱「為輔助工作負載建立非 ULL Compute Engine 執行個體」。
使用 SSH 連線至執行個體
如果尚未連線至執行個體,請使用 SSH 連線。
切換為超級使用者
下列程序中的指令和指令碼會修改系統層級設定、核心參數和網路介面。如要順利執行這些指令,必須以超級使用者身分執行。您可以執行 sudo su 切換至根層級殼層,或視需要先新增 sudo 再執行指令。
設定 Onload
本節說明在 U4 執行個體上設定 Onload 的必要步驟。
安裝依附元件
如果您使用 Rocky Linux,請啟用 CodeReady Builder (CRB) 存放區。如果您使用的是 Red Hat Enterprise Linux (RHEL),請略過這個步驟。
dnf -y config-manager --enable crb
安裝 Onload 的必要依附元件:
dnf -y install git clang \ python3-setuptools \ linuxptp \ libcap-devel libbpf-devel libxdp-devel
提取 Onload 來源
如要提取 onload 存放區和所需變更,請執行下列指令:
umask 0022 mkdir -p /usr/src/ git clone https://github.com/Xilinx-CNS/onload /usr/src/onload # 9.2.0.43 / 9.2.1, origin/v9_2 as of May 18, 2026 git -C /usr/src/onload checkout origin/v9_2 # Pull Google-specific Onload changes not yet merged as of v9_2 curl -L https://github.com/Xilinx-CNS/onload/pull/279.patch | git -C /usr/src/onload am -3 curl -L https://github.com/Xilinx-CNS/onload/pull/282.patch | git -C /usr/src/onload am -3 curl -L https://github.com/Xilinx-CNS/onload/pull/325.patch | git -C /usr/src/onload am -3 curl -L https://github.com/Xilinx-CNS/onload/pull/327.patch | git -C /usr/src/onload am -3
建構 Onload
如要建構 Onload,請執行下列指令:
cd /usr/src/onload USEONLOADEXT=1 ./scripts/onload_install --no-sfc pushd ./src/tools/bpf_link_helper clang xdp_onload_prepare.c -lbpf -o xdp_onload_prepare clang -target bpf -O2 -g -c xdp_tstamp.c -o ./xdp_tstamp.o popd
停用間接分支追蹤 (IBT)
如要使用 Onload,必須停用 IBT,詳情請參閱「Indirect Branch Tracking (IBT) Incompatibility (間接分支追蹤 (IBT) 不相容) 」。
如要停用 IBT,請執行下列指令:
grubby --args="ibt=off" --update-kernel=ALL reboot
載入 Onload
本節說明如何在執行個體上載入 Onload。
在 U4P 或 U4C 執行個體上載入 Onload
如要在 U4P 或 U4C 裸機執行個體上載入 Onload,請使用下列指令碼。
IFNAMES=($( find /sys/class/net -type l -not -lname '*virtual*' -printf '%l %f\n' | sort | awk '{print $2}')) for IFNAME in "${IFNAMES[@]}"; do ethtool -L "${IFNAME}" rx 16 tx 16 ethtool -G "${IFNAME}" rx 1024 rx-buf-len 2048 ethtool -K "${IFNAME}" ntuple on echo 0 > "/sys/class/net/${IFNAME}/threaded" /usr/src/onload/src/tools/bpf_link_helper/xdp_onload_prepare \ "${IFNAME}" /usr/src/onload/src/tools/bpf_link_helper/xdp_tstamp.o done setenforce 0 numactl --cpunodebind=0,2 onload_tool reload --onload-only for IFNAME in "${IFNAMES[@]}"; do echo "${IFNAME}" 16 > /sys/module/sfc_resource/afxdp/register until [[ $(cat "/sys/class/net/${IFNAME}/carrier") == 1 ]]; do sleep 1 done hwstamp_ctl -i "${IFNAME}" -r 1 done echo 1 > /sys/module/sfc_resource/parameters/enable_af_xdp_flow_filters echo 256 > /sys/module/onload/parameters/xdp_headroom echo -1 > /sys/module/onload/parameters/inject_kernel_gid
在 U4S 執行個體上載入 Onload
如要在 U4S VM 執行個體上載入 Onload,請使用下列指令碼。
IFNAME=NIC_NAME ALLOCATED_QUEUES=ALLOCATED_QUEUES ethtool -L "$IFNAME" rx "${ALLOCATED_QUEUES}" tx "${ALLOCATED_QUEUES}" ethtool -G "$IFNAME" rx 1024 rx-buf-len 2048 ethtool -K "$IFNAME" ntuple on echo 0 > "/sys/class/net/${IFNAME}/threaded" /usr/src/onload/src/tools/bpf_link_helper/xdp_onload_prepare "$IFNAME" \ /usr/src/onload/src/tools/bpf_link_helper/xdp_tstamp.o setenforce 0 numactl --cpunodebind=0 onload_tool reload --onload-only echo "${IFNAME} ${ALLOCATED_QUEUES}" | tee /sys/module/sfc_resource/afxdp/register until [[ $(cat "/sys/class/net/${IFNAME}/carrier") == 1 ]]; do sleep 1 done hwstamp_ctl -i "$IFNAME" -r 1 echo 1 > /sys/module/sfc_resource/parameters/enable_af_xdp_flow_filters echo 256 > /sys/module/onload/parameters/xdp_headroom echo -1 > /sys/module/onload/parameters/inject_kernel_gid
更改下列內容:
NIC_NAME:網路介面的 OS 名稱,例如enp22s0f0。ALLOCATED_QUEUES:要為網路介面上的 Onload 分配的接收 (RX) 和傳輸 (TX) 佇列數量。將這個值設為指派給 vNIC 的 RX 或 TX 佇列總數的一半。對於 U4S 執行個體,佇列總數 (分別適用於 RX 或 TX 佇列) 等於
num_vcpus / num_vnics,每個 vNIC 最多16個佇列。舉例來說,如果 vNIC 共有4個 TX 佇列,請將這個值設為2。 如果 vNIC 有16個 TX 佇列,請將這個值設為8。如要進一步瞭解預設佇列分配,請參閱「接收和傳輸佇列」。
設定 Onload 旗標
如要提升效能並減少延遲時間,使用 Onload 執行應用程式時,可以採用下列環境變數和旗標。本節包含建議設定,您可以視應用程式需求調整。
您必須在應用程式指令前指定這些參數。舉例來說,如要使用這些設定執行應用程式,請使用下列格式:
env EF_NO_FAIL=0 \ EF_POLL_USEC=100000 \ EF_RX_TIMESTAMPING=3 \ EF_MAX_ENDPOINTS=1048576 \ EF_WODA_SINGLE_INTERFACE=1 \ EF_UL_EPOLL=3 \ EF_USE_HUGE_PAGES=0 \ EF_EPOLL_CTL_HANDOFF=0 \ EF_FDS_MT_SAFE=0 \ EF_NONAGLE_INFLIGHT_MAX=-1 \ EF_RXQ_SIZE=4096 \ EF_TCP_RCVBUF_ESTABLISHED_DEFAULT=65536 \ EF_MAX_PACKETS=65536 \ EF_PREFAULT_PACKETS=65536 \ EF_EVS_PER_POLL=256 \ onload -v --profile=latency APPLICATION_COMMAND
Unload Onload
如要卸載 Onload,請使用下列指令碼。
IFNAMES=($( find /sys/class/net -type l -not -lname '*virtual*' -printf '%l %f\n' | sort | awk '{print $2}')) for IFNAME in "${IFNAMES[@]}"; do rm -f "/sys/fs/bpf/onload_xdp_xsk_${IFNAME}" done onload_tool unload --onload-only for IFNAME in "${IFNAMES[@]}"; do # (optional) Disable threaded busypolling in case it's up. See busypolling # section echo 0 > "/sys/class/net/${IFNAME}/threaded" ip link set dev "${IFNAME}" xdp off done
設定 systemd 服務,在載入時自動啟動
如要在執行個體啟動時自動啟動 Onload,可以將其註冊為systemd服務。使用下列範本建立服務檔案:
[Unit] Description=ULL Solution -- Loading & instance tuning for Onload After=network-online.target After=google-guest-agent-manager.service google-guest-agent.service Before=multi-user.target Before=sshd.service [Service] Type=oneshot RemainAfterExit=yes ExecStart=START_SCRIPT_PATH ExecStartPost=OPTIMIZATION_SCRIPT_PATH ExecStop=STOP_SCRIPT_PATH [Install] WantedBy=multi-user.target
更改下列內容:
START_SCRIPT_PATH:啟動 Onload 的指令碼路徑,例如「載入 Onload」中的其中一個指令碼。OPTIMIZATION_SCRIPT_PATH:選用指令碼的路徑,可套用最佳化設定。如有需要,您可以建立包含效能最佳化的指令碼,並在此處加入。否則,您可以移除包含這個變數的行。STOP_SCRIPT_PATH:停止 Onload 的指令碼路徑,例如「Unload Onload」中的指令碼。
設定忙碌輪詢
本節提供範例,說明如何在執行個體上設定忙碌輪詢。
忙碌輪詢會持續檢查新的網路封包,而不是等待裝置中斷,有助於減少延遲和抖動。如要進一步瞭解忙碌輪詢,請參閱 Linux 核心說明文件中的「忙碌輪詢」。
取得 Onload 堆疊使用的 RX 佇列
如要取得 Onload 堆疊使用的 RX 佇列,請執行下列操作:
執行
onload_stackdump取得 Onload 堆疊 ID:onload_stackdump
由於 Onload 堆疊 ID 和 NAPI 佇列 ID 可能不一定相符,請使用下列指令碼從堆疊 ID 取得對應的介面名稱、索引和佇列 ID。
ONLOAD_STACK=ONLOAD_STACK_ID INTF_HWPORT_MAP=($(onload_stackdump "${ONLOAD_STACK}" netif_extra | grep -oP "intf_i_to_hwport=\K.*$" | tr ',' '\n')) HWPORT_IFINDEX_MAP=($(onload_stackdump "${ONLOAD_STACK}" hwport_to_base_ifindex | grep -oP "\d+$")) while read -r INTF_ID QUEUE_ID; do HW_PORT="${INTF_HWPORT_MAP[INTF_ID]}" IFINDEX="${HWPORT_IFINDEX_MAP[HW_PORT]}" IFNAME=$(ip -j link | jq -r ".[] | select(.ifindex == ${IFINDEX}) | .ifname") echo "ifname=${IFNAME} ifindex=${IFINDEX} queue_id=${QUEUE_ID}" done < <(onload_stackdump "${ONLOAD_STACK}" netif | grep -oP "((intf|vi)=)\K\d+" | xargs -n 2)
將
ONLOAD_STACK_ID替換為要啟用或停用忙碌輪詢的堆疊 ID。
在 RX 佇列上啟用忙碌輪詢
本節提供範例,說明如何針對 Onload 堆疊使用的特定 RX 佇列啟用忙碌輪詢。
在終端機中執行下列 bash 指令碼。
enable_single_queue函式會執行下列作業:- 使用 netlink (
ynl) 取得對應 RX 佇列的napi_id - 設定
napi_id的threaded: busy-poll屬性 - 取得忙於輪詢
napi_id的執行緒kthread_pid - 使用
taskset將kthread_pid繫結至特定 CPU
readonly NETDEV_YAML=${NETDEV_YAML:-"/usr/share/ynl/specs/netdev.yaml"} call_ynl() { ynl --spec "${NETDEV_YAML}" "$@" } enable_single_queue() { local -r interface="$1" local -r ifindex=$(cat "/sys/class/net/${interface}/ifindex") local -r q_id="$2" local -r cpu="$3" local napi_id napi_id=$(call_ynl --output-json --do queue-get \ --json "{\"ifindex\": ${ifindex}, \"id\": ${q_id}, \"type\": \"rx\"}" | \ jq -r '."napi-id"') if [[ -z "${napi_id}" || "${napi_id}" == "null" ]]; then echo "Error: No napi_id found for queue ${q_id} on interface ${interface}" >&2 exit 1 fi echo "Enabling busypolling for queue ${q_id} (NAPI ${napi_id}) on CPU ${cpu}" call_ynl --do napi-set --json "{\"id\": \"${napi_id}\", \"threaded\": \"busy-poll\"}" >/dev/null local napi_kthread_pid napi_kthread_pid=$(call_ynl --do napi-get --output-json \ --json "{\"id\": \"${napi_id}\"}" | jq -r '."pid" // empty') if [[ -z "${napi_kthread_pid}" ]]; then echo "Error: Could not get PID for NAPI ${napi_id}" >&2 exit 1 fi taskset -pc "${cpu}" "${napi_kthread_pid}" >/dev/null }
- 使用 netlink (
執行下列指令來叫用
enable_single_queue函式:enable_single_queue NIC_NAME QUEUE_ID CPU_ID
更改下列內容:
NIC_NAME:網路介面的 OS 名稱,例如ens8f0。QUEUE_ID:先前取得的佇列 ID。CPU_ID:要執行忙碌輪詢執行緒的 CPU ID,例如5。
規劃執行緒重新建立事件
當核心重新建立執行緒時,相關聯的執行緒設定不會保留,例如 CPU 親和性遮罩和排程政策。發生下列事件時,核心會重新建立忙於輪詢 NAPI 的執行緒:
- 連結擋板/重設
- XDP 程式附件 (例如執行指令碼載入 Onload 或附加自訂 XDP 程式時)
- 變更鈴聲參數 (
ethtool -G) - 佇列計數變更 (
ethtool -L)
為避免發生問題,請考慮在正常運作期間,避免執行會導致執行緒重新建立事件的作業。
如要在重新建立執行緒後維持忙碌輪詢設定,請務必取得執行緒的新程序 ID (PID),並重新繫結至 CPU。方法是再次執行 enable_single_queue 函式。
停用 RX 佇列的忙碌輪詢
本節提供範例,說明如何停用 Onload 堆疊使用的特定 RX 佇列上的忙碌輪詢。
執行
onload_stackdump,取得 Onload 堆疊使用的 RX 佇列:onload_stackdump
在終端機中執行下列 bash 指令碼。
disable_single_queue函式會執行下列作業:- 使用 netlink (
ynl) 取得對應 RX 佇列的napi_id - 將
napi_id的執行緒屬性設為disabled
disable_single_queue() { local -r interface="$1" local -r ifindex=$(cat "/sys/class/net/${interface}/ifindex") local -r q_id="$2" local napi_id napi_id=$(call_ynl --output-json --do queue-get \ --json "{\"ifindex\": ${ifindex}, \"id\": ${q_id}, \"type\": \"rx\"}" | \ jq -r '."napi-id"') if [[ -z "${napi_id}" || "${napi_id}" == "null" ]]; then echo "Error: No napi_id found for queue ${q_id} on interface ${interface}" >&2 exit 1 fi echo "Disabling busypolling for queue ${q_id} (NAPI ${napi_id})" call_ynl --do napi-set --json "{\"id\": \"${napi_id}\", \"threaded\": \"disabled\"}" >/dev/null }
- 使用 netlink (
執行下列指令來叫用
disable_single_queue函式:disable_single_queue NIC_NAME QUEUE_ID
更改下列內容:
NIC_NAME:網路介面的 OS 名稱,例如ens8f0。QUEUE_ID:先前取得的佇列 ID。
取得佇列的忙碌輪詢狀態
如要檢查佇列的 NAPI 狀態,看看是否忙於輪詢,可以使用下列指令:
IFNAME=NIC_NAME QUEUE_ID=QUEUE_ID QUEUE_TYPE=QUEUE_TYPE IFINDEX=$(cat "/sys/class/net/${IFNAME}/ifindex") NAPI_ID=$(ynl --spec /usr/share/ynl/specs/netdev.yaml \ --output-json --do queue-get \ --json '{"ifindex": '${IFINDEX}', "id": '${QUEUE_ID}', "type": "'${QUEUE_TYPE}'"}' | \ jq '."napi-id"') ynl --spec /usr/share/ynl/specs/netdev.yaml \ --output-json --do napi-get \ --json '{"id": '${NAPI_ID}'}' | jq -r '"status: \(.threaded)"'
更改下列內容:
NIC_NAME:網路介面的 OS 名稱,例如ens8f0。QUEUE_ID:要檢查的佇列 ID。QUEUE_TYPE:rx或tx。
發揮最大效能
本節提供一般指引,說明如何最佳化 U4 裸機執行個體 (U4P 和 U4C) 的效能。請視工作負載需求調整本指南中的範例。
查看 U4 裸機執行個體的 NUMA 拓撲
下表說明 U4 裸機執行個體使用哪些 NUMA 節點的網路介面:
| NIC (Google Cloud 名稱) | NIC (OS 名稱) | NUMA 節點 | PCIE BDF |
|---|---|---|---|
nic0 |
enp22s0f0 |
0 | 0000:16:00.0 |
nic1 |
ens8f0 |
0 | 0000:27:00.0 |
nic2 |
ens48f0 |
2 | 0000:b8:00.0 |
上表列出 RHEL 的一般 OS 指派網路介面名稱。實際名稱可能有所不同。
判斷 CPU 隔離配置
為獲得最佳效能,建議您隔離下列項目:
- 應用程式使用的 CPU
- 用於忙碌輪詢 Onload RX 佇列的 CPU
- 用於核心和驅動程式中斷的 CPU
下表提供範例,說明如何在 U4 裸機執行個體上隔離 CPU。視工作負載需求調整對應,例如您可能需要更多應用程式 CPU。
| 目的 | CPU |
|---|---|
| 一般核心中斷 | 0,1,30,31,60,61,90,91 |
nic0 driver interrupts for queues 0-11 |
2 |
nic1 driver interrupts for queues 0-11 |
3 |
nic0 和 nic1 驅動程式中斷,適用於佇列 12 至 15 |
4 |
nic1 忙碌輪詢 |
5-16 |
nic1 應用程式執行緒 (Onload) |
17-29 |
nic0 忙碌輪詢 |
32-43 |
nic0 應用程式執行緒 (Onload) |
44-59 |
nic2 driver interrupts for queues 0-11 |
62 |
nic2 駕駛人中斷佇列 12 至 15 |
63 |
nic2 忙碌輪詢 |
64-75 |
nic2 應用程式執行緒 (Onload) |
76-89 |
安裝依附元件,以最佳化效能
如要安裝成效最佳化所需的依附元件,請執行下列指令:
dnf -y install numactl tuna jq
設定核心開機參數
如要將 CPU 與核心排程隔離,請執行下列指令。這也會停用 Intel QuickAssist Technology (QAT),避免干擾隔離的核心。
以下範例指令會隔離 CPU 2-29、32-59 和 62-89,並將 0,1,30,31,60,61,90,91 指定為一般核心中斷。這些值對應於範例 CPU 隔離配置。視 CPU 隔離配置而定,視需要替換值。
grubby --args="isolcpus=domain,managed_irq,2-29,32-59,62-89 nohz=on nohz_full=2-29,32-59,62-89 rcu_nocbs=2-29,32-59,62-89 irqaffinity=0,1,30,31,60,61,90,91 rcu_nocb_poll modprobe.blacklist=intel_qat,qat_4xxx" --update-kernel=ALL reboot
設定開機後 CPU 隔離
如要在開機後隔離 CPU,請執行下列指令。這些值對應於範例 CPU 隔離配置。視 CPU 隔離配置而定,視需要取代值。
tuna isolate -c 2-29,32-59,62-89
將佇列中斷指派給特定 CPU
本節說明如何將 gve 佇列中斷要求 (IRQ) 移至特定 CPU。gve 驅動程式是由 Google Cloud中的 GVNIC 網路介面類型使用。
判斷特定網路介面和佇列範圍的 IRQ。請參閱以下 Bash 範例,瞭解如何定義
irq_list函式。irq_list() { local ifname=$1 local queue_begin=$2 local queue_end=$3 pci_name=$(basename $(readlink /sys/class/net/${ifname}/device)) rx_ntfy_blk_start=$(ethtool -l "${ifname}" | awk ' /Pre-set maximums:/ { in_preset = 1 } /Current hardware settings:/ { in_preset = 0 } in_preset && $1 == "RX:" { rx = $2 } in_preset && $1 == "TX:" { tx = $2 } END { print int((rx + tx) / 2) } ') for i in $(seq "${queue_begin}" "${queue_end}"); do irq_tx="gve-ntfy-blk${i}@pci:${pci_name}" irq_rx="gve-ntfy-blk$(($i + rx_ntfy_blk_start))@pci:${pci_name}" # gve IRQ names are stored in a char[IFNAMSIZ + 16] so capped to 31 characters. echo "${irq_tx:0:31}" echo "${irq_rx:0:31}" done | paste -sd ',' }
根據 CPU 隔離配置,將 IRQ 指派給適當的 CPU。下列指令碼範例使用上一個步驟中的
tuna和irq_list函式:tuna move -c 2 -q "$(irq_list enp22s0f0 0 11)" tuna move -c 4 -q "$(irq_list enp22s0f0 12 15)" tuna move -c 3 -q "$(irq_list ens8f0 0 11)" tuna move -c 4 -q "$(irq_list ens8f0 12 15)" tuna move -c 62 -q "$(irq_list ens48f0 0 11)" tuna move -c 63 -q "$(irq_list ens48f0 12 15)"
設定作業系統和裝置設定
執行下列指令碼,設定有助於縮短延遲時間的設定,並防止預設 OS 行為干擾 Onload 設定。
echo 0 > /proc/sys/net/core/busy_poll echo 0 > /proc/sys/net/core/busy_read echo 0 > /proc/sys/kernel/timer_migration echo 0 > /proc/sys/net/core/rps_sock_flow_entries echo -1 > /proc/sys/kernel/sched_rt_runtime_us for IFNAME in "${IFNAMES[@]}"; do ethtool -C "${IFNAME}" rx-usecs 0 tx-usecs 0 echo 0 > "/sys/class/net/${IFNAME}/napi_defer_hard_irqs" echo 15000 > "/sys/class/net/${IFNAME}/gro_flush_timeout" done
如要將流量導向專為 Onload 工作負載而設的佇列,請使用 RSS (
ethtool -X)。下列範例指令碼是以範例 CPU 隔離配置中的值為依據。由於 Onload 使用佇列0-11,因此指令碼會將所有其他流量導向佇列12-15。for IFNAME in "${IFNAMES[@]}"; do ethtool -X "${IFNAME}" weight 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 done
已知問題
在 U4 執行個體上使用 Onload 時,可能會發生下列已知問題:
- 開啟和關閉通訊端最多可能需要幾毫秒。
這是因為 AF_XDP 流量導向設定是緩慢的控制路徑程序:
- 管理接聽程式通訊端的交易所營運商,可以使用 Google 提交至上游 Onload 存放區的修補程式 (#335、#336),解決這個問題。請務必在提取 Onload 來源時加入這些修補程式。
- 建立外送連線的 Exchange 參與者不需要上述修補程式,而是可以使用 Onload 現有的
EF_TCP_SHARED_LOCAL_PORTS功能,協助縮短延遲時間。
後續步驟
- 如要將執行個體系統時鐘與主機伺服器的實體 NIC 時鐘同步,請參閱「設定準確時間」。