DPDK を使用する

このページでは、U4C Compute Engine インスタンスでデータプレーン開発キット(DPDK)を使用する方法について説明します。

AF_XDP 上の DPDK について

データプレーン開発キット(DPDK)は、高速パケット処理、 低レイテンシ、一貫性のあるパフォーマンスを必要とする パフォーマンス重視のアプリケーションに対応したフレームワークです。DPDK は Linux カーネル ネットワーク スタックをバイパスし、ユーザー空間で直接実行されます。AF_XDP アーキテクチャを使用すると、U4C インスタンスで DPDK を実行できます。

DPDK には、AF_XDP Poll Mode Driver(PMD)が用意されています。これは、コピーモードまたはゼロコピー モードで AF_XDP 上で DPDK アプリケーションを実行できる仮想デバイス(vdev)です。詳細については、 DPDK ドキュメントの AF_XDP Poll Mode Driver をご覧ください。Compute Engine での 一般的なデプロイとは異なり、 U4C インスタンスの AF_XDP 上の DPDK では、VFIO、UIO、 dpdk-devbind.pyを構成する必要はありません。

ULL Solution で DPDK を使用すると、フロー ステアリングがサポートされます。特定のトラフィック フローを指定された受信キュー(RX)に直接転送することで、デフォルトの Receive Side Scaling(RSS)ハッシュをバイパスできます。ULL ソリューションは、ULL ユニキャスト トラフィックとマルチキャスト トラフィックの 3 タプル フロー ステアリング(プロトコル、宛先 IP アドレス、宛先ポート)をサポートしています。

始める前に

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

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

まだ作成していない場合は、U4C ベアメタル インスタンスを作成します。ULL Compute Engine インスタンスを作成するをご覧ください。

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

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

root ユーザーに切り替える

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

U4C インスタンスに DPDK をインストールする

U4C インスタンスに DPDK をインストールする手順は次のとおりです。

  1. DPDK インストールの依存関係を構成します。

    apt-get update && apt-get upgrade -yq
    apt-get install -yq build-essential ninja-build python3-pip \
        linux-headers-$(uname -r) pkg-config libnuma-dev
    pip install pyelftools meson
    
  2. DPDK をインストールします。

    wget https://fast.dpdk.org/rel/dpdk-VERSION.tar.xz
    tar xvf dpdk-VERSION.tar.xz
    cd dpdk-VERSION

    VERSION は、インストールする DPDK バージョン(26.07 など)に置き換えます。必要に応じて、 DPDK ダウンロード ページをご覧ください。

  3. サンプルを使用して DPDK をビルドするには:

    meson setup -Dexamples=all build
    ninja -C build install; ldconfig
    

AF_XDP のネットワーク インターフェースを構成する

Google Virtual NIC(gVNIC)で AF_XDP を使用するには、デフォルトのドライバ機能を調整してネットワーク インターフェースを準備する必要があります。

これらの手順は手動で実行することも、自動構成スクリプトを使用することもできます。次のいずれかのタブを選択します。

手動

構成するネットワーク インターフェースごとに、次の手順を行います。

  1. RX キューと TX キューの数を減らす: gVNIC はデフォルトで、サポートされている RX キューと TX キューの最大数を使用しますが、通常のカーネル トラフィックに十分な TX キューを確保するには、この数を半分に減らす必要があります。

    ethtool -L NIC_NAME rx NUM_SOCKETS \
    tx NUM_SOCKETS

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

    • NIC_NAME: ネットワーク インターフェースの OS 名(eth1 など)。
    • NUM_SOCKETS: 構成する AF_XDP ソケットの数。 インターフェースの最大キュー数の半分以下の値に設定します。 U4C インスタンスの場合、通常は 8(デフォルトの 16 キューの半分)です。 最大キュー数は、ethtool -l NIC_NAME を実行して確認できます。
  2. ハードウェア GRO と LRO を無効にする: gVNIC はマルチバッファ XDP をサポートしていないため、大規模な受信オフロード(LRO)とハードウェアの汎用受信オフロード(GRO)を無効にする必要があります。

    ethtool -K NIC_NAME rx-gro-hw off
    ethtool -K NIC_NAME lro off
  3. RX バッファの長さを短くする: デフォルトでは、新しいドライバは RX 用に 4 KB(4,096 バイト)のバッファをネットワーク インターフェースに送信しますが、XDP ではバッファ長が 2048 である必要があります。

    ethtool -G NIC_NAME rx-buf-len 2048

スクリプト

または、構成するネットワーク インターフェースごとに次の Bash スクリプトを実行することもできます。このスクリプトは、 キュー数の削減、 オフロードの無効化、RX バッファ長の調整により、指定されたネットワーク インターフェースを XDP 用に自動的に準備します。

#!/bin/bash
# Usage example: NUM_SOCKETS=8 prep_xdp.sh eth0

DEV=$1
NUM_SOCKETS=${NUM_SOCKETS=1}

# Reduce RX/TX queue counts to the number of AF_XDP sockets
ethtool -L $DEV rx $NUM_SOCKETS tx $NUM_SOCKETS

# Disable LRO/HW-gro
OFFLOAD=$(ethtool -k $DEV | \
grep "rx-gro-hw\|large-receive-offload" | \
grep -v fixed | cut -d ":" -f 1)
ethtool -K $DEV ${OFFLOAD} off

# Reduce RX buffer length to 2048
ethtool -G $DEV rx-buf-len 2048

DPDK アプリケーションを実行する

AF_XDP PMD を使用するには、DPDK アプリケーションの Environment Abstraction Layer(EAL)引数に --vdev フラグを含めます。

次のコマンド例には、いくつかの重要なパラメータが含まれています。設定とパラメータの詳細については、 DPDK ドキュメントの AF_XDP Poll Mode Driver をご覧ください。

DPDK_APPLICATION -a PCIE_BDF \
  --vdev=net_af_xdp,iface=NIC_NAME,queue_count=NUM_SOCKETS,start_queue=START_QUEUE,xdp_prog=XDP_PROG \
  -- APPLICATION_ARGS

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

  • DPDK_APPLICATION: 実行する DPDK アプリケーション バイナリ。
  • PCIE_BDF: ネットワーク インターフェースの PCI アドレス。この値は、ethtool -i NIC_NAME を実行してbus-info値(0000:00:04.0など)を確認することで確認できます。
  • NIC_NAME: ネットワーク インターフェースの OS 名(eth1 など)。
  • NUM_SOCKETS: 開く AF_XDP ソケットの数。各ソケットは 1 つのキューペアに接続されます。この値は、インターフェースで構成したキュー数と一致する必要があります。
  • START_QUEUE: AF_XDP ソケットの開始キュー インデックス。
  • XDP_PROG: 受信パケットで実行するカスタム XDP プログラム。省略すると、DPDK は libxdp で提供されるデフォルトの XDP プログラムを使用します。
  • APPLICATION_ARGS: DPDK アプリケーションに固有の引数。

ドライバ機能を使用する

このセクションでは、フロー ステアリングと RX タイムスタンプの使用方法について説明します。

フロー ステアリング

XDP でフロー ステアリングを使用すると、アプリケーション パケットを特定のキューのサブセットに転送し、残りのキューをカーネル トラフィック用に残すことができます。以降のセクションでは、フロー ルール プログラミングに使用できる 2 つの方法について説明します。

フロー ステアリングを事前プログラミングする(推奨)

ioctl 呼び出しを使用したオンザフライ フロー プログラミングは AF_XDP PMD でサポートされておらず、DPDK アプリケーションの変更が必要になるため、アプリケーションを起動する前に ethtool を使用してフロー ルールを事前プログラミングすることをおすすめします。

たとえば、IPv4 UDP トラフィックをキュー 0 に転送するフロー ルールをプログラミングするには:

ethtool -N NIC_NAME flow-type udp4 \
  dst-ip DST_IP dst-port DST_PORT action 0 loc 0

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

  • NIC_NAME: ネットワーク インターフェースの OS 名(eth1 など)。
  • DST_IP: 転送するトラフィックの宛先 IP アドレス。
  • DST_PORT: 転送するトラフィックの宛先ポート。

フロー ルールをオンザフライでプログラミングする

AF_XDP PMD は、オンザフライ フロー プログラミングをサポートしていません。この方法を使用するには、DPDK アプリケーションを変更して、ethtool ioctl 呼び出しを手動で送信する必要があります。

アプリケーションが大量の一時接続を処理する場合を除き、この方法は避けることをおすすめします。gVNIC は最大 20,000 個の 3 タプル フロー ステアリング ルールをサポートしています。必要なルールが少なく、宛先 IP アドレスとポートが事前にわかっている場合は、代わりにルールを事前プログラミングしてください。

ルールを動的にプログラミングする必要がある場合は、次の C コードの例をご覧ください。

開いて C コードの例を表示

struct flow_rule_info {
  uint32_t src_ip;
  uint32_t dst_ip;
  uint16_t src_port;
  uint16_t dst_port;
  uint32_t target_queue;
  uint32_t rule_id;
}

int add_flow_rule(const char *ifname,
                  struct flow_rule_info *rule_info,
                  bool is_5tuple) {
  struct ethtool_rxnfc cmd;
  struct ifreq ifr;
  int fd;

  fd = socket(AF_INET, SOCK_DGRAM, 0);
  if (fd < 0) {
    fprintf(stderr, "Failed to open socket: %s", strerror(fd));
    return -1;
  }

  memset(&cmd, 0, sizeof(cmd));
  memset(&ifr, 0, sizeof(ifr));

  cmd.cmd = ETHTOOL_SRXCLSRLINS;
  cmd.fs.flow_type = UDP_V4_FLOW;

  cmd.fs.h_u.udp_ip4_spec.ip4dst = rule_info->dst_ip;
  cmd.fs.h_u.udp_ip4_spec.pdst = htons(rule_info->dst_port);
  cmd.fs.m_u.udp_ip4_spec.ip4dst = 0xFFFFFFFF;
  cmd.fs.m_u.udp_ip4_spec.pdst = 0xFFFF;

  if (is_5tuple) {
    cmd.fs.h_u.udp_ip4_spec.ip4src = rule_info->src_ip;
    cmd.fs.h_u.udp_ip4_spec.psrc = htons(rule_info->src_port);
    cmd.fs.m_u.udp_ip4_spec.ip4src = 0xFFFFFFFF;
    cmd.fs.m_u.udp_ip4_spec.psrc = 0xFFFF;
  }

  cmd.fs.ring_cookie = rule_info->target_queue;
  cmd.fs.location = rule_info->rule_id;

  strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
  ifr.ifr_data = (void *)&cmd;

  int ret = ioctl(fd, SIOCETHTOOL, &ifr);
  if (ret)
    fprintf(stderr, "Failed to send ioctl: %s\n", strerror(errno));
  close(fd);

  return ret;
}

int try_add_xdp_flow_rule(int port, struct flow_rule_info *rule_info) {
  char dev_name[RTE_ETH_NAME_MAX_LEN];
  char *af_xdp_driver = "net_af_xdp";
  struct rte_eth_dev_info dev_info;
  int err;

  if (!rte_eth_dev_is_valid_port(port)) return -1;

  err = rte_eth_dev_info_get(port, &dev_info);
  if (err) {
    fprintf(stderr, "Error getting info for port %d: %s\n", port,
            strerror(err));
    return retval;
  }

  if (strncmp(dev_info.driver_name, af_xdp_driver,
              strlen(af_xdp_driver)) != 0) {
    fprintf(stderr, "Not an AF_XDP vdev!\n");
    return -EINVAL;
  }

  err = rte_eth_dev_get_name_by_port(port, dev_name);
  if (retval) {
    fprintf(stderr, "Failed to get dev_name: %s\n", strerror(err));
    return retval;
  }

  /* replace ens4 with correct ifname; must be passed in as application argument */
  err = add_flow_rule("ens4", rule_info));
  if (err)
    fprintf("Failed to add flow rule: %s\n", strerror(err));
  return err;
}

/* From somewhere in the application: */
int application_func(...) {
  ...

  struct flow_rule_info *rule_info {
    .src_ip = 0x0a000001,
    .dst_ip = 0x0a000002,
    .src_port = 0x1110,
    .uint16_t dst_port = 0x1011,
    .uint32_t target_queue = 0,
    .uint32_t rule_id = 1,
  };
  try_add_xdp_flow_rule(xdp_port_id, &rule_info, /*is_5tuple=*/false);
  ...
}

RSS プログラミング

フロー ステアリングを使用すると、RSS を使用してトラフィックの分離を強化できます。 フロー ステアリングと同様に、ethtool を使用して RSS を構成できます。アプリケーションを実行する前に RSS を構成することをおすすめします。

次の例は、AF_XDP と連携するように RSS を構成する方法を示しています。

# Example: Kernel queues 0-3, XDP queues 4-7
NUM_SOCKETS=4 bash prep_xdp.sh eth0

# Program flow rules
ethtool -N eth0 flow-type udp4 dst-ip DST_IP dst-port DST_PORT_0 action 4 loc 0
ethtool -N eth0 flow-type udp4 dst-ip DST_IP dst-port DST_PORT_1 action 5 loc 1
ethtool -N eth0 flow-type udp4 dst-ip DST_IP dst-port DST_PORT_2 action 6 loc 2
ethtool -N eth0 flow-type udp4 dst-ip DST_IP dst-port DST_PORT_3 action 7 loc 3
# If there are more ports that the application polls on, flow rules can be added in a round-robin fashion in a script.

# Program RSS
ethtool -X eth0 start 0 equal 4

# Run the application
./path/to/application -a 0000:00:03.0 --vdev net_af_xdp,iface=eth0,start_queue=4,queue_count=4 -- APPLICATION_ARGS

RX タイムスタンプ

RX タイムスタンプは AF_XDP PMD のアップストリーム DPDK リポジトリではサポートされていませんが、次の操作を行うことで RX タイムスタンプを使用できます。

  1. 必要なパッチを DPDK ソースツリーに適用し、DPDK ソースとアプリケーションを再コンパイルします。これらのパッチにより、受信した mbufsrte_eth_read_clock と RX タイムスタンプがサポートされます。

  2. タイムスタンプをメタデータに読み込む XDP プログラムを使用します。

  3. DPDK アプリケーションの起動時に、次の AF_XDP vdev パラメータを追加します。

    • xdp_meta_rx_ts_offset: 64 ビット RX タイムスタンプ値が配置されている XDP メタデータの先頭からのバイトオフセット。
    • xdp_meta_valid_hint_offset: (省略可)タイムスタンプが有効かどうかを示す 1 バイトのフラグ フィールドをカバーするバイトオフセット。
    • xdp_meta_rx_ts_valid_mask: (省略可)有効なフラグビットの抽出に使用するビットマスク。

      ctx がメタデータの先頭の場合、 (ctx->data_meta + xdp_meta_valid_hint_offset) & xdp_meta_rx_ts_valid_mask の値は、タイムスタンプが有効かどうかを示します。

次のステップ

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