Mit DPDK arbeiten

Auf dieser Seite wird beschrieben, wie Sie das Data Plane Development Kit (DPDK) auf U4C Compute Engine-Instanzen verwenden.

Informationen zu DPDK über AF_XDP

Das Data Plane Development Kit (DPDK) ist ein Framework für leistungsintensive Anwendungen, die eine schnelle Paketverarbeitung, eine geringe Latenz und eine gleichbleibende Leistung erfordern. DPDK umgeht den Linux-Kernel-Netzwerkstack und wird direkt im Userspace ausgeführt. Sie können DPDK auf U4C-Instanzen mit einer AF_XDP-Architektur ausführen.

DPDK bietet einen AF_XDP-Poll-Mode-Treiber (PMD), ein virtuelles Gerät (vdev), mit dem DPDK-Anwendungen im Kopier- oder Zero-Copy-Modus auf AF_XDP ausgeführt werden können. Weitere Informationen finden Sie in der DPDK-Dokumentation unter AF_XDP Poll Mode Driver. Im Gegensatz zu typischen Bereitstellungen auf Compute Engine, ist für DPDK über AF_XDP auf U4C-Instanzen keine Konfiguration von VFIO, UIO oder dpdk-devbind.py erforderlich.

Die Verwendung von DPDK mit der ULL-Lösung umfasst die Unterstützung für die Flusssteuerung. Sie können das standardmäßige Hashing für die Receive Side Scaling (RSS) umgehen, indem Sie bestimmte Trafficflüsse direkt an eine bestimmte Empfangswarteschlange (RX) weiterleiten. Die ULL-Lösung unterstützt die 3-Tupel-Flusssteuerung (Protokoll, Ziel-IP-Adresse und Zielport) für ULL-Unicast- und Multicast-Traffic.

Hinweis

Bevor Sie mit DPDK auf U4C Compute Engine-Instanzen arbeiten, müssen Sie die folgenden Anforderungen erfüllen.

U4C-Instanz erstellen

Erstellen Sie eine U4C-Bare-Metal-Instanz, falls noch nicht geschehen. Weitere Informationen finden Sie unter ULL Compute Engine-Instanzen erstellen.

Mit SSH eine Verbindung zur Instanz herstellen

Stellen Sie eine SSH-Verbindung zur Instanz her, falls noch nicht geschehen.

Zum Root-Nutzer wechseln

Mit den Befehlen und Skripts in den folgenden Verfahren werden Einstellungen auf Systemebene, Kernelparameter und Netzwerkschnittstellen geändert. Damit sie erfolgreich ausgeführt werden können, müssen Sie sie als Root-Nutzer ausführen. Sie können zu einer Root-Shell wechseln, indem Sie sudo suausführen, oder bei Bedarf sudovor die Befehle setzen.

DPDK auf der U4C-Instanz installieren

So installieren Sie DPDK auf der U4C-Instanz:

  1. Konfigurieren Sie die Abhängigkeiten für die DPDK-Installation:

    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. Installieren Sie DPDK.

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

    Ersetzen Sie VERSION durch die DPDK-Version, die Sie installieren möchten, z. B. 26.07. Bei Bedarf finden Sie weitere Informationen auf der DPDK-Downloadseite.

  3. So erstellen Sie DPDK mit den Beispielen:

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

Netzwerkschnittstellen für AF_XDP konfigurieren

Wenn Sie AF_XDP auf der virtuellen Netzwerkschnittstelle von Google (gVNIC) verwenden möchten, müssen Sie die Standardtreiberfunktionen anpassen, um die Netzwerkschnittstelle vorzubereiten.

Sie können diese Schritte manuell ausführen oder ein automatisiertes Konfigurationsskript verwenden. Wählen Sie einen der folgenden Tabs aus:

Manuell

Führen Sie diese Schritte für jede Netzwerkschnittstelle aus, die Sie konfigurieren möchten.

  1. Anzahl der RX- und TX-Warteschlangen reduzieren:gVNIC verwendet standardmäßig die maximal unterstützte Anzahl von RX- und TX-Warteschlangen. Sie müssen diese jedoch um die Hälfte reduzieren, damit genügend TX-Warteschlangen für den normalen Kerneltraffic vorhanden sind.

    ethtool -L NIC_NAME rx NUM_SOCKETS \
    tx NUM_SOCKETS

    Ersetzen Sie Folgendes:

    • NIC_NAME: der Name der Netzwerkschnittstelle im Betriebssystem, z. B. eth1.
    • NUM_SOCKETS: die Anzahl der zu konfigurierenden AF_XDP-Sockets. Legen Sie für diesen Wert maximal die Hälfte der maximalen Warteschlangen für die Schnittstelle fest. Bei U4C-Instanzen ist dies in der Regel 8 (die Hälfte der standardmäßigen 16 Warteschlangen). Sie können die maximale Anzahl der Warteschlangen mit ethtool -l NIC_NAME prüfen.
  2. Hardware-GRO und -LRO deaktivieren:Da gVNIC Multi-Buffer-XDP nicht unterstützt, müssen Sie Large Receive Offload (LRO) und Hardware-Generic Receive Offload (GRO) deaktivieren:

    ethtool -K NIC_NAME rx-gro-hw off
    ethtool -K NIC_NAME lro off
  3. Länge des RX-Puffers reduzieren:Standardmäßig stellen neuere Treiber 4-KB-Puffer (4.096 Byte) für RX an die Netzwerkschnittstelle. XDP erfordert jedoch eine Pufferlänge von 2048:

    ethtool -G NIC_NAME rx-buf-len 2048

Skript

Alternativ können Sie das folgende Bash-Skript für jede Netzwerkschnittstelle ausführen, die Sie konfigurieren möchten. Das Skript bereitet eine bestimmte Netzwerkschnittstelle automatisch für XDP vor, indem die Anzahl der Warteschlangen reduziert, Offloads deaktiviert und die Länge des RX-Puffers angepasst wird:

#!/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-Anwendung ausführen

Wenn Sie den AF_XDP-PMD verwenden möchten, fügen Sie das Flag --vdev in die EAL-Argumente (Environment Abstraction Layer) Ihrer DPDK-Anwendung ein.

Der folgende Beispielbefehl enthält mehrere wichtige Parameter. Detaillierte Informationen zur Einrichtung und zu Parametern finden Sie in der DPDK-Dokumentation unter 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

Ersetzen Sie Folgendes:

  • DPDK_APPLICATION: die DPDK-Anwendungsbinärdatei, die ausgeführt werden soll.
  • PCIE_BDF: die PCI-Adresse der Netzwerkschnittstelle. Sie finden diesen Wert, indem Sie ethtool -i NIC_NAME ausführen und den Wert bus-info prüfen, z. B. 0000:00:04.0.
  • NIC_NAME: der Name der Netzwerkschnittstelle im Betriebssystem, z. B. eth1.
  • NUM_SOCKETS: die Anzahl der zu öffnenden AF_XDP-Sockets. Jeder Socket ist mit einem einzelnen Warteschlangenpaar verbunden. Dieser Wert muss mit der Anzahl der Warteschlangen übereinstimmen, die Sie auf der Schnittstelle konfiguriert haben.
  • START_QUEUE: der Startindex der Warteschlange für die AF_XDP-Sockets.
  • XDP_PROG: ein benutzerdefiniertes XDP-Programm, das für empfangene Pakete ausgeführt werden soll. Wenn nichts angegeben ist, verwendet DPDK das Standard-XDP-Programm von libxdp.
  • APPLICATION_ARGS: Argumente, die für Ihre DPDK-Anwendung spezifisch sind.

Treiberfunktionen verwenden

In diesem Abschnitt finden Sie Informationen zur Verwendung der Flusssteuerung und des RX-Zeitstempels.

Flusssteuerung

Sie können die Flusssteuerung mit XDP verwenden, um Anwendungspakete an eine bestimmte Teilmenge von Warteschlangen weiterzuleiten. Die übrigen Warteschlangen sind für den Kerneltraffic reserviert. In den folgenden Abschnitten werden zwei Ansätze beschrieben, die Sie für die Programmierung von Flussregeln verwenden können.

Flusssteuerung vorprogrammieren (empfohlen)

Da die On-the-Fly-Flussprogrammierung mit ioctl-Aufrufen vom AF_XDP-PMD nicht unterstützt wird und eine Änderung Ihrer DPDK-Anwendung erfordert, empfehlen wir, Ihre Flussregeln vor dem Starten der Anwendung mit ethtool vorzuprogrammieren.

So programmieren Sie beispielsweise eine Flussregel, die IPv4-UDP-Traffic an die Warteschlange 0 weiterleitet:

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

Ersetzen Sie Folgendes:

  • NIC_NAME: der Name der Netzwerkschnittstelle im Betriebssystem, z. B. eth1.
  • DST_IP: die Ziel-IP-Adresse des weiterzuleitenden Traffics.
  • DST_PORT: der Zielport des weiterzuleitenden Traffics.

Flussregeln spontan programmieren

Der AF_XDP-PMD unterstützt keine On-the-Fly-Flussprogrammierung. Wenn Sie diese Methode verwenden möchten, müssen Sie Ihre DPDK-Anwendung so ändern, dass ethtool ioctl-Aufrufe manuell gesendet werden.

Wir empfehlen, diesen Ansatz nur zu verwenden, wenn Ihre Anwendung eine große Anzahl kurzlebiger Verbindungen verarbeitet. gVNIC unterstützt bis zu 20.000 3-Tupel-Fluss steuerungsregeln. Wenn Sie weniger Regeln benötigen und die Ziel-IP-Adressen und Ports im Voraus kennen, programmieren Sie Ihre Regeln stattdessen vor.

Wenn Sie Regeln dynamisch programmieren müssen, sehen Sie sich das folgende C-Codebeispiel an:

Maximieren, um das C-Codebeispiel anzusehen

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-Programmierung

Bei Verwendung der Flusssteuerung kann RSS zu einer besseren Traffic-Isolation beitragen. Wie bei der Flusssteuerung können Sie RSS mit ethtool konfigurieren. Wir empfehlen, RSS zu konfigurieren, bevor Sie Ihre Anwendung ausführen.

Das folgende Beispiel zeigt, wie Sie RSS für die Verwendung mit AF_XDP konfigurieren:

# 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-Zeitstempel

Obwohl RX-Zeitstempel im Upstream-DPDK-Repository für den AF_XDP-PMD nicht unterstützt werden, können Sie sie verwenden. Gehen Sie dazu so vor:

  1. Wenden Sie die erforderlichen Patches auf die DPDK-Quellstruktur an und kompilieren Sie die DPDK-Quelle und Ihre Anwendung neu. Diese Patches fügen Unterstützung für rte_eth_read_clock und RX-Zeitstempel in empfangenen mbufs hinzu.

  2. Verwenden Sie ein XDP-Programm, das den Zeitstempel in die Metadaten lädt.

  3. Geben Sie beim Starten Ihrer DPDK-Anwendung die folgenden zusätzlichen AF_XDP-vdev-Parameter an:

    • xdp_meta_rx_ts_offset: der Byte-Offset vom Anfang der XDP-Metadaten, an dem sich der 64-Bit-RX-Zeitstempelwert befindet.
    • xdp_meta_valid_hint_offset: (Optional) der Byte-Offset, der ein 1-Byte-Flagfeld abdeckt, das angibt, ob der Zeitstempel gültig ist.
    • xdp_meta_rx_ts_valid_mask: (Optional) die Bitmaske, die zum Extrahieren der gültigen Flagbits verwendet wird.

      Wenn ctx der Anfang der Metadaten ist, beschreibt der Wert von (ctx->data_meta + xdp_meta_valid_hint_offset) & xdp_meta_rx_ts_valid_mask , ob der Zeitstempel gültig ist.

Nächste Schritte

  • Informationen zum Synchronisieren der Instanzuhr mit der physischen NIC-Uhr des Host servers finden Sie unter Genaue Zeit konfigurieren.