Utilizzare DPDK
Questa pagina descrive come utilizzare Data Plane Development Kit (DPDK) sulle istanze Compute Engine U4C.
Informazioni su DPDK su AF_XDP
Il Data Plane Development Kit (DPDK) è un framework per applicazioni con esigenze di prestazioni elevate che richiedono un'elaborazione rapida dei pacchetti, bassa latenza e prestazioni costanti. DPDK ignora lo stack di rete del kernel Linux e viene eseguito direttamente nello spazio utente. Puoi eseguire DPDK sulle istanze U4C utilizzando un'architettura AF_XDP.
DPDK fornisce un driver in modalità polling (PMD) AF_XDP, che è un dispositivo virtuale (vdev) che consente alle applicazioni DPDK di essere eseguite su AF_XDP in modalità di copia o zero-copy. Per saperne di più, consulta
Driver in modalità polling AF_XDP nella
documentazione di DPDK. A differenza delle implementazioni
tipiche su Compute Engine,
DPDK su AF_XDP sulle istanze U4C non richiede la configurazione di VFIO, UIO o
dpdk-devbind.py.
L'utilizzo di DPDK con la soluzione ULL include il supporto per lo steering del flusso. Puoi ignorare l'hashing RSS (Receive Side Scaling) predefinito indirizzando flussi di traffico specifici direttamente a una coda di ricezione (RX) designata. La soluzione ULL supporta lo steering del flusso a 3 tuple (protocollo, indirizzo IP di destinazione e porta di destinazione) per il traffico unicast e multicast ULL.
Prima di iniziare
Prima di utilizzare DPDK sulle istanze Compute Engine U4C, devi soddisfare i seguenti requisiti.
Creare un'istanza U4C
Se non l'hai ancora fatto, crea un'istanza bare metal U4C. Consulta Creare ULL istanze Compute Engine.
Connettersi all'istanza utilizzando SSH
Se non l'hai ancora fatto, connettiti all' istanza utilizzando SSH.
Passare all'utente root
I comandi e gli script nelle procedure seguenti modificano le impostazioni a livello di sistema, i parametri del kernel e le interfacce di rete. Per eseguirli correttamente, devi eseguirli come utente root. Puoi passare a una shell root eseguendo
sudo su o aggiungere sudo prima di eseguire i comandi, se necessario.
Installare DPDK sull'istanza U4C
Per installare DPDK sull'istanza U4C:
Configura le dipendenze per l'installazione di 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 mesonInstalla DPDK.
wget https://fast.dpdk.org/rel/dpdk-VERSION.tar.xz tar xvf dpdk-VERSION.tar.xz cd dpdk-VERSION
Sostituisci
VERSIONcon la versione di DPDK che vuoi installare, ad esempio26.07. Se necessario, consulta la pagina di download di DPDK.Per compilare DPDK con gli esempi:
meson setup -Dexamples=all build ninja -C build install; ldconfig
Configurare le interfacce di rete per AF_XDP
Per utilizzare AF_XDP su Google Virtual NIC (gVNIC), devi modificare le funzionalità predefinite del driver per preparare l'interfaccia di rete.
Puoi eseguire questi passaggi manualmente o utilizzare uno script di configurazione automatizzato. Seleziona una delle seguenti schede:
Manuale
Segui questi passaggi per ogni interfaccia di rete che vuoi configurare.
Riduci il numero di code RX e TX:per impostazione predefinita, gVNIC utilizza il numero massimo di code RX e TX supportate, ma devi ridurlo della metà per assicurarti che ci siano code TX sufficienti per il traffico del kernel normale.
ethtool -L NIC_NAME rx NUM_SOCKETS \ tx NUM_SOCKETS
Sostituisci quanto segue:
NIC_NAME: il nome del sistema operativo dell'interfaccia di rete, ad esempioeth1.NUM_SOCKETS: il numero di socket AF_XDP da configurare. Imposta questo valore su un valore non superiore alla metà delle code massime per l'interfaccia. Per le istanze U4C, in genere è8(la metà delle 16 code predefinite). Puoi verificare le code massime eseguendoethtool -l NIC_NAME.
Disattiva GRO e LRO hardware:poiché gVNIC non supporta XDP multi-buffer, devi disattivare Large Receive Offload (LRO) e Generic Receive Offload (GRO) hardware:
ethtool -K NIC_NAME rx-gro-hw off ethtool -K NIC_NAME lro off
Riduci la lunghezza del buffer RX:per impostazione predefinita, i driver più recenti pubblicano buffer di 4 KB (4096 byte) sull'interfaccia di rete per RX, ma XDP richiede una lunghezza del buffer di
2048:ethtool -G NIC_NAME rx-buf-len 2048
Script
In alternativa, puoi eseguire il seguente script Bash per ogni interfaccia di rete che vuoi configurare. Lo script prepara automaticamente una determinata interfaccia di rete per XDP riducendo il numero di code, disattivando gli offload e regolando la lunghezza del buffer 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
Eseguire l'applicazione DPDK
Per utilizzare il PMD AF_XDP, includi il flag --vdev negli argomenti EAL (Environment Abstraction Layer) della tua applicazione DPDK.
Il seguente comando di esempio include diversi parametri chiave. Per informazioni dettagliate sulla configurazione e sui parametri, consulta Driver in modalità polling AF_XDP nella documentazione di 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
Sostituisci quanto segue:
DPDK_APPLICATION: il file binario dell'applicazione DPDK da eseguire.PCIE_BDF: l'indirizzo PCI dell'interfaccia di rete. Puoi trovare questo valore eseguendoethtool -i NIC_NAMEe controllando ilbus-infovalore, ad esempio0000:00:04.0.NIC_NAME: il nome del sistema operativo dell'interfaccia di rete, ad esempioeth1.NUM_SOCKETS: il numero di socket AF_XDP da aprire. Ogni socket è collegato a una singola coppia di code. Questo valore deve corrispondere al numero di code configurato sull'interfaccia.START_QUEUE: l'indice della coda iniziale per i socket AF_XDP.XDP_PROG: un programma XDP personalizzato da eseguire sui pacchetti ricevuti. Se omesso, DPDK utilizza il programma XDP predefinito fornito dalibxdp.APPLICATION_ARGS: argomenti specifici per la tua applicazione DPDK.
Utilizzare le funzionalità del driver
Questa sezione fornisce informazioni sull'utilizzo dello steering del flusso e del timestamp RX.
Steering del flusso
Puoi utilizzare lo steering del flusso con XDP per indirizzare i pacchetti dell'applicazione a un sottoinsieme specifico di code, lasciando le code rimanenti per il traffico del kernel. Le sezioni seguenti descrivono due approcci che puoi utilizzare per la programmazione delle regole di flusso.
Programmare in anticipo lo steering del flusso (consigliato)
Poiché la programmazione del flusso on-the-fly utilizzando le chiamate ioctl non è supportata dal PMD AF_XDP e richiede la modifica dell'applicazione DPDK, ti consigliamo di programmare in anticipo le regole di flusso utilizzando ethtool prima di avviare l'applicazione.
Ad esempio, per programmare una regola di flusso che indirizza il traffico UDP IPv4 alla coda 0:
ethtool -N NIC_NAME flow-type udp4 \ dst-ip DST_IP dst-port DST_PORT action 0 loc 0
Sostituisci quanto segue:
NIC_NAME: il nome del sistema operativo dell'interfaccia di rete, ad esempioeth1.DST_IP: l'indirizzo IP di destinazione del traffico da indirizzare.DST_PORT: la porta di destinazione del traffico da indirizzare.
Programmare le regole di flusso on-the-fly
Il PMD AF_XDP non supporta la programmazione del flusso on-the-fly. Per utilizzare questo metodo, devi modificare l'applicazione DPDK per inviare manualmente le chiamate ioctl ethtool.
Ti consigliamo di evitare questo approccio a meno che la tua applicazione non gestisca un numero elevato di connessioni effimere. gVNIC supporta fino a 20.000 regole di steering del flusso a 3 tuple; se hai bisogno di meno regole e conosci in anticipo gli indirizzi IP e le porte di destinazione, programma le regole in anticipo.
Se devi programmare le regole in modo dinamico, consulta il seguente esempio di codice C:
Espandi per visualizzare l'esempio di codice 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); ... }
Programmazione RSS
Quando utilizzi lo steering del flusso, RSS può contribuire a fornire un migliore isolamento del traffico.
Come per lo steering del flusso, puoi configurare RSS utilizzando ethtool. Ti consigliamo di configurare RSS prima di eseguire l'applicazione.
Il seguente esempio mostra come configurare RSS per funzionare con 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
Timestamp RX
Sebbene il timestamp RX non sia supportato nel repository DPDK upstream per il PMD AF_XDP, puoi utilizzare il timestamp RX procedendo nel seguente modo:
Applica le patch richieste all'albero di origine DPDK e ricompila l'origine DPDK e l'applicazione. Queste patch aggiungono il supporto per
rte_eth_read_clocke i timestamp RX inmbufsricevuti.Utilizza un programma XDP che carica il timestamp nei metadati.
Fornisci i seguenti parametri
vdevAF_XDP aggiuntivi all'avvio dell'applicazione DPDK:xdp_meta_rx_ts_offset: l'offset di byte dall'inizio dei metadati XDP in cui si trova il valore del timestamp RX a 64 bit.xdp_meta_valid_hint_offset: (facoltativo) l'offset di byte che copre un campo di flag di 1 byte che indica se il timestamp è valido.xdp_meta_rx_ts_valid_mask: (facoltativo) la maschera di bit utilizzata per estrarre i bit di flag validi.Se
ctxè l'inizio dei metadati, il valore di(ctx->data_meta + xdp_meta_valid_hint_offset) & xdp_meta_rx_ts_valid_maskdescrive se il timestamp è valido.
Passaggi successivi
- Per sincronizzare l'orologio dell'istanza con l'orologio della NIC fisica del server host, consulta Configurare l'ora precisa.