Utiliser DPDK

Cette page explique comment utiliser le kit de développement de plan de données (DPDK) sur les instances Compute Engine U4C.

À propos de DPDK sur AF_XDP

Le kit de développement de plan de données (DPDK) est un framework pour les applications exigeantes qui requièrent un traitement rapide des paquets, une faible latence et des performances constantes. DPDK contourne la pile réseau du noyau Linux et s'exécute directement dans l'espace utilisateur. Vous pouvez exécuter DPDK sur des instances U4C à l'aide d'une architecture AF_XDP.

DPDK fournit un pilote de mode d'interrogation AF_XDP (PMD), qui est un appareil virtuel (vdev) permettant aux applications DPDK de s'exécuter sur AF_XDP en mode copie ou en mode zéro copie. Pour en savoir plus, consultez Pilote de mode d'interrogation AF_XDP dans la documentation DPDK. Contrairement aux déploiements classiques sur Compute Engine, DPDK sur AF_XDP sur les instances U4C ne nécessite pas la configuration de VFIO, UIO ni dpdk-devbind.py.

L'utilisation de DPDK avec la solution ULL inclut la prise en charge de l'orientation du flux. Vous pouvez contourner le hachage RSS (Receive Side Scaling) par défaut en orientant des flux de trafic spécifiques directement vers une file d'attente de réception (RX) désignée. La solution ULL est compatible avec l'orientation du flux à 3 tuples (protocole, adresse IP de destination et port de destination) pour le trafic unicast et multicast ULL.

Avant de commencer

Avant d'utiliser DPDK sur des instances Compute Engine U4C, vous devez répondre aux exigences suivantes.

Créer une instance U4C

Si ce n'est pas déjà fait, créez une instance bare metal U4C. Consultez Créer des instances Compute Engine ULL.

Se connecter à votre instance à l'aide de SSH

Si ce n'est pas déjà fait, connectez-vous à votre instance à l'aide de SSH.

Passer à l'utilisateur racine

Les commandes et les scripts des procédures suivantes modifient les paramètres au niveau du système, les paramètres du noyau et les interfaces réseau. Pour les exécuter correctement, vous devez les exécuter en tant qu'utilisateur racine. Vous pouvez passer à un shell racine en exécutant sudo su, ou ajouter sudo avant d'exécuter des commandes, si nécessaire.

Installer DPDK sur votre instance U4C

Pour installer DPDK sur votre instance U4C, procédez comme suit :

  1. Configurez les dépendances pour l'installation de 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. Installez DPDK.

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

    Remplacez VERSION par la version de DPDK que vous souhaitez installer, par exemple 26.07. Si nécessaire, consultez la page de téléchargement de DPDK.

  3. Compilez DPDK avec les exemples :

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

Configurer les interfaces réseau pour AF_XDP

Pour utiliser AF_XDP sur une carte d'interface réseau virtuelle Google (gVNIC), vous devez ajuster les fonctionnalités du pilote par défaut afin de préparer l'interface réseau.

Vous pouvez effectuer ces étapes manuellement ou utiliser un script de configuration automatisé. Sélectionnez l'un des onglets suivants :

Manuel

Suivez ces étapes pour chaque interface réseau que vous souhaitez configurer.

  1. Réduisez le nombre de files d'attente RX et TX : par défaut, gVNIC utilise le nombre maximal de files d'attente RX et TX compatibles, mais vous devez le réduire de moitié pour vous assurer qu'il y a suffisamment de files d'attente TX pour le trafic normal du noyau.

    ethtool -L NIC_NAME rx NUM_SOCKETS \
    tx NUM_SOCKETS

    Remplacez les éléments suivants :

    • NIC_NAME: nom de l'interface réseau dans l'OS, par exemple eth1.
    • NUM_SOCKETS : nombre de sockets AF_XDP à configurer. Définissez une valeur inférieure ou égale à la moitié du nombre maximal de files d'attente pour l'interface. Pour les instances U4C, il s'agit généralement de 8 (la moitié des 16 files d'attente par défaut). Vous pouvez vérifier le nombre maximal de files d'attente en exécutant ethtool -l NIC_NAME.
  2. Désactivez GRO et LRO matériels : comme gVNIC n'est pas compatible avec XDP multi-tampon, vous devez désactiver le déchargement de réception volumineux (LRO) et le déchargement de réception générique (GRO) matériel :

    ethtool -K NIC_NAME rx-gro-hw off
    ethtool -K NIC_NAME lro off
  3. Réduisez la longueur du tampon RX : par défaut, les pilotes plus récents publient des tampons de 4 Ko (4 096 octets) sur l'interface réseau pour RX, mais XDP nécessite une longueur de tampon de 2048 :

    ethtool -G NIC_NAME rx-buf-len 2048

Script

Vous pouvez également exécuter le script Bash suivant pour chaque interface réseau que vous souhaitez configurer. Le script prépare automatiquement une interface réseau donnée pour XDP en réduisant le nombre de files d'attente, en désactivant les déchargements et en ajustant la longueur du tampon RX :

#!/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

Exécuter votre application DPDK

Pour utiliser le PMD AF_XDP, incluez l'option --vdev dans les arguments de la couche d'abstraction de l'environnement (EAL) de votre application DPDK.

L'exemple de commande suivant inclut plusieurs paramètres clés. Pour obtenir des informations détaillées sur la configuration et les paramètres, consultez Pilote de mode d'interrogation AF_XDP dans la documentation DPDK.

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

Remplacez les éléments suivants :

  • DPDK_APPLICATION : binaire de l'application DPDK à exécuter.
  • PCIE_BDF: adresse PCI de l'interface réseau. Vous pouvez trouver cette valeur en exécutant ethtool -i NIC_NAME et en vérifiant la valeur bus-info, par exemple 0000:00:04.0.
  • NIC_NAME : nom de l'interface réseau dans l'OS, par exemple eth1.
  • NUM_SOCKETS : nombre de sockets AF_XDP à ouvrir. Chaque socket est associé à une seule paire de files d'attente. Cette valeur doit correspondre au nombre de files d'attente que vous avez configuré sur l'interface.
  • START_QUEUE : index de la file d'attente de départ pour les sockets AF_XDP.
  • XDP_PROG: programme XDP personnalisé à exécuter sur les paquets reçus. S'il est omis, DPDK utilise le programme XDP par défaut fourni par libxdp.
  • APPLICATION_ARGS : arguments spécifiques à votre application DPDK.

Utiliser les fonctionnalités du pilote

Cette section fournit des informations sur l'utilisation de l'orientation du flux et de l'horodatage RX.

Orientation du flux

Vous pouvez utiliser l'orientation du flux avec XDP pour orienter les paquets d'application vers un sous-ensemble spécifique de files d'attente, en laissant les files d'attente restantes pour le trafic du noyau. Les sections suivantes décrivent deux approches que vous pouvez utiliser pour la programmation des règles de flux.

Préprogrammer l'orientation du flux (recommandé)

Étant donné que la programmation de flux à la volée à l'aide d'appels ioctl n'est pas compatible avec le PMD AF_XDP et nécessite la modification de votre application DPDK, nous vous recommandons de préprogrammer vos règles de flux à l'aide de ethtool avant de démarrer votre application.

Par exemple, pour programmer une règle de flux qui oriente le trafic UDP IPv4 vers la file d'attente 0 :

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

Remplacez les éléments suivants :

  • NIC_NAME : nom de l'interface réseau dans l'OS, par exemple eth1.
  • DST_IP : adresse IP de destination du trafic à orienter.
  • DST_PORT : port de destination du trafic à orienter.

Programmer des règles de flux à la volée

Le PMD AF_XDP n'est pas compatible avec la programmation de flux à la volée. Pour utiliser cette méthode, vous devez modifier votre application DPDK afin d'envoyer manuellement des appels ioctl ethtool.

Nous vous recommandons d'éviter cette approche,sauf si votre application gère un grand nombre de connexions éphémères. gVNIC est compatible avec un maximum de 20 000 règles d'orientation de flux à 3 tuples. Si vous avez besoin de moins de règles et que vous connaissez à l'avance vos adresses IP et ports de destination, préprogrammez plutôt vos règles.

Si vous devez programmer des règles de manière dynamique, reportez-vous à l'exemple de code C suivant :

Développer pour afficher l'exemple de code 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);
  ...
}

Programmation RSS

Lorsque vous utilisez l'orientation du flux, RSS peut vous aider à mieux isoler le trafic. Comme pour l'orientation du flux, vous pouvez configurer RSS à l'aide de ethtool. Nous vous recommandons de configurer RSS avant d'exécuter votre application.

L'exemple suivant montre comment configurer RSS pour qu'il fonctionne avec AF_XDP :

# 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

Horodatage RX

Bien que l'horodatage RX ne soit pas compatible avec le dépôt DPDK en amont pour le PMD AF_XDP, vous pouvez l'utiliser en procédant comme suit :

  1. Appliquez les correctifs requis à l'arborescence source DPDK, puis recompilez la source DPDK et votre application. Ces correctifs ajoutent la prise en charge de rte_eth_read_clock et des codes temporels RX dans les mbufs reçus.

  2. Utilisez un programme XDP qui charge le code temporel dans les métadonnées.

  3. Fournissez les paramètres vdev AF_XDP supplémentaires suivants lorsque vous démarrez votre application DPDK :

    • xdp_meta_rx_ts_offset: décalage d'octets depuis le début des métadonnées XDP où se trouve la valeur du code temporel RX 64 bits.
    • xdp_meta_valid_hint_offset: (facultatif) décalage d'octets couvrant un champ d'indicateur de 1 octet indiquant si le code temporel est valide.
    • xdp_meta_rx_ts_valid_mask: (facultatif) masque de bits utilisé pour extraire les bits d'indicateur valides.

      Si ctx est le début des métadonnées, la valeur de (ctx->data_meta + xdp_meta_valid_hint_offset) & xdp_meta_rx_ts_valid_mask décrit si le code temporel est valide.

Étape suivante

  • Pour synchroniser l'horloge de votre instance avec l'horloge de la carte d'interface réseau physique de son serveur hôte, consultez Configurer l'heure exacte.