Onload を使用する

このページでは、U4 Compute Engine インスタンスで Onload を使用する方法について説明します。

Onload について

Onload は、超低レイテンシ、最小限の ジッター、一貫したパフォーマンスを必要とするレイテンシの影響を受けやすいアプリケーション向けの高性能ネットワーク スタックです。Onload は、オペレーティング システム カーネルをバイパスしてユーザー空間で直接実行する TCP/IP 実装を提供し、アプリケーションが標準の BSD ソケット API を使用できるようにします。

ULL Solution で Onload を使用すると、次のものがサポートされます。

始める前に

U4 Compute Engine インスタンスで Onload を使用する前に、次の要件を満たす必要があります。

U4 インスタンスを作成する

まだ作成していない場合は、Onload に必要な構成を含む次のいずれかの手順で U4 Compute Engine インスタンスを作成します。

SSH を使用してインスタンスに接続する

まだ接続していない場合は、SSH を使用してインスタンスに接続します

root ユーザーに切り替える

次の手順のコマンドとスクリプトは、システムレベルの設定、カーネル パラメータ、ネットワーク インターフェースを変更します。これらを正常に実行するには、root ユーザーとして実行する必要があります。sudo su を実行して root シェルに切り替えるか、必要に応じてコマンドの実行前に sudo を追加します。

Onload を設定する

このセクションでは、U4 インスタンスに Onload を設定するために必要な手順について説明します。

依存関係のインストール

  1. Rocky Linux を使用している場合は、CodeReady Builder(CRB)リポジトリを有効にします。Red Hat Enterprise Linux(RHEL)を使用している場合は、この手順をスキップします。

    dnf -y config-manager --enable crb
    
  2. Onload に必要な依存関係をインストールします。

    dnf -y install git clang \
       python3-setuptools \
       linuxptp \
       libcap-devel libbpf-devel libxdp-devel
    

Onload ソースを pull する

必要な変更を含む onload リポジトリ を pull するには、次のコマンドを実行します。

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
curl -L https://github.com/Xilinx-CNS/onload/pull/282.patch | git -C /usr/src/onload am
curl -L https://github.com/Xilinx-CNS/onload/pull/325.patch | git -C /usr/src/onload am
curl -L https://github.com/Xilinx-CNS/onload/pull/327.patch | git -C /usr/src/onload am

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)を無効にする

間接分岐トラッキング(IBT)の非互換性で説明されているように、Onload を使用するには 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

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

Onload を自動的に起動する 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

次のように置き換えます。

ビジーポーリングを構成する

このセクションでは、インスタンスでビジーポーリングを構成する方法の例を示します。

ビジーポーリングは、デバイス割り込みを待つのではなく、新しいネットワーク パケットを継続的にチェックするため、レイテンシとジッターを削減できます。ビジーポーリングの詳細については、 Linux カーネルのドキュメントの ビジーポーリング をご覧ください。

Onload スタックが使用している RX キューを取得する

Onload スタックが使用している RX キューを取得するには、次の操作を行います。

  1. `onload_stackdump` を実行して、Onload スタック ID を取得します onload_stackdump:

    onload_stackdump
    
  2. 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 に置き換えます。

  3. 次のセクションでビジーポーリングを有効または 無効にするときに使用する値を記録します。

RX キューでビジーポーリングを有効にする

このセクションでは、Onload スタックが使用している特定の RX キューでビジーポーリングを有効にする方法の例を示します。

  1. ターミナルで次の bash スクリプトを実行します。enable_single_queue 関数は次の処理を行います。

    • netlink(ynl)を使用して、RX キューに対応する napi_id を取得する
    • napi_idthreaded: 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
    }
  2. 次のコマンドを実行して、enable_single_queue 関数を呼び出します。

    enable_single_queue NIC_NAME QUEUE_ID CPU_ID
    

    次のように置き換えます。

    • NIC_NAME: ネットワーク インターフェースの OS 名(ens8f0 など)。
    • QUEUE_ID: 前の手順で取得したキュー ID。
    • CPU_ID: ビジーポーリング スレッドを実行する CPU の ID(5 など)。
  3. ビジーポーリング構成に影響する可能性のあるスレッドの再作成イベントを計画してください

スレッドの再作成イベントを計画する

カーネルがスレッドを再作成しても、関連するスレッド構成(CPU アフィニティ マスクやスケジューリング ポリシーなど)は保持されません。次のようなイベントが発生すると、カーネルは NAPI をビジーポーリングしているスレッドを再作成します。

  • リンクのフラップ/リセット
  • XDP プログラムのアタッチ(Onload を読み込むスクリプトの実行時や、カスタム XDP プログラムのアタッチ時など)
  • リング パラメータの変更(ethtool -G
  • キュー数の変更(ethtool -L

問題を回避するには、通常オペレーション中にスレッドの再作成イベントを引き起こすタスクを避けることを検討してください。

スレッドの再作成後にビジーポーリング構成を維持するには、スレッドの新しいプロセス ID(PID)を取得して、CPU に再バインドする必要があります。これを行うには、enable_single_queue 関数を再度実行します。

RX キューでビジーポーリングを無効にする

このセクションでは、Onload スタックが使用している特定の RX キューでビジーポーリングを無効にする方法の例を示します。

  1. `onload_stackdump` を実行して、Onload スタックが使用している RX キューを取得します。onload_stackdump

    onload_stackdump
    
  2. ターミナルで次の bash スクリプトを実行します。disable_single_queue 関数は次の処理を行います。

    • netlink(ynl)を使用して、RX キューに対応する napi_id を取得する
    • napi_id の threaded プロパティを 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
    }
  3. 次のコマンドを実行して、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
キュー 0 ~ 11 の nic0 ドライバ割り込み 2
キュー 0 ~ 11 の nic1 ドライバ割り込み 3
キュー 12 ~ 15 の nic0nic1 のドライバ割り込み 4
nic1 ビジーポーリング 5-16
nic1 アプリケーション スレッド(Onload) 17-29
nic0 ビジーポーリング 32-43
nic0 アプリケーション スレッド(Onload) 44-59
キュー 0 ~ 11 の nic2 ドライバ割り込み 62
キュー 12 ~ 15 の nic2 ドライバ割り込み 63
nic2 ビジーポーリング 64-75
nic2 アプリケーション スレッド(Onload) 76-89

パフォーマンスの最適化に必要な依存関係をインストールする

パフォーマンスの最適化に必要な依存関係をインストールするには、次のコマンドを実行します。

dnf -y install numactl tuna jq

カーネルの起動パラメータを構成する

CPU をカーネル スケジューリングから分離するには、次のコマンドを実行します。これにより、Intel QuickAssist Technology(QAT)も無効になり、分離されたコアに干渉しなくなります。

次のコマンド例では、CPU 2-2932-5962-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 ドライバは、GVNIC ネットワーク インターフェース タイプで Google Cloud使用されます。

  1. 特定のネットワーク インターフェースとキュー範囲の IRQ を決定します。irq_list 関数を定義する次の bash の例をご覧ください。

    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 ','
    }
  2. CPU 分離スキームに基づいて、IRQ を適切な CPU に割り当てます。 次のスクリプト例では、前の手順の tunairq_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 とデバイスの設定を構成する

  1. 次のスクリプトを実行して、レイテンシを最小限に抑え、デフォルトの 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
  2. 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

次のステップ

  • インスタンスのシステム クロックをホストサーバーの物理 NIC クロックと同期するには、正確な時刻を構成するをご覧ください。