Work with DPDK
This page describes how to use the Data Plane Development Kit (DPDK) on U4 Compute Engine instances.
About DPDK over AF_XDP
The Data Plane Development Kit (DPDK) is a framework for performance-intensive applications that require fast packet processing, low latency, and consistent performance. DPDK bypasses the Linux kernel network stack and runs directly in the user space. You can run DPDK on U4 instances by using an AF_XDP architecture.
DPDK provides an AF_XDP Poll Mode Driver (PMD), which is a virtual device
(vdev) that lets DPDK applications run on top of AF_XDP in either copy mode or
zero-copy mode. For more information, see
AF_XDP Poll Mode Driver in the
DPDK documentation. Unlike
typical deployments on Compute Engine,
DPDK over AF_XDP on U4 instances doesn't require configuring VFIO, UIO, or
dpdk-devbind.py.
Using DPDK with ULL Solution includes support for flow steering. You can bypass default Receive Side Scaling (RSS) hashing by steering specific traffic flows directly to a designated receive queue (RX). ULL Solution supports 3-tuple flow steering (protocol, destination IP address, and destination port) for ULL unicast and multicast traffic.
Before you begin
Before you work with DPDK on U4 Compute Engine instances, you must meet the following requirements.
Create a U4 instance
If you haven't already, create a U4 Compute Engine instance by using one of the following procedures:
- To create a U4P or U4C bare metal instance, see Create ULL Compute Engine instances.
- To create a U4S virtual machine (VM) instance, see Create non-ULL Compute Engine instances for auxiliary workloads.
Connect to your instance by using SSH
If you haven't already, connect to your instance by using SSH.
Switch to the root user
The commands and scripts in the following procedures modify system-level
settings, kernel parameters, and network interfaces. To run them successfully,
you must execute them as the root user. You can switch to a root shell by
running sudo su, or add sudo before running commands as needed.
Install DPDK on your U4 instance
To install DPDK on your U4 instance, follow these steps:
Configure the dependencies for 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 mesonInstall DPDK.
wget https://fast.dpdk.org/rel/dpdk-VERSION.tar.xz tar xvf dpdk-VERSION.tar.xz cd dpdk-VERSION
Replace
VERSIONwith the DPDK version that you want to install, such as26.07. If needed, see the DPDK Download page.To build DPDK with the examples:
meson setup -Dexamples=all build ninja -C build install; ldconfig
Configure network interfaces for AF_XDP
To use AF_XDP on Google Virtual NIC (gVNIC), you must adjust default driver features to prepare the network interface.
You can perform these steps manually or use an automated configuration script. Select one of the following tabs:
Manual
Follow these steps for each network interface that you want to configure.
Reduce the RX and TX queue counts: gVNIC defaults to using the maximum supported number of RX and TX queues, but you must reduce this by half to ensure there are enough TX queues for normal kernel traffic.
ethtool -L NIC_NAME rx NUM_SOCKETS \ tx NUM_SOCKETS
Replace the following:
NIC_NAME: the OS name of the network interface, such aseth1.NUM_SOCKETS: the number of AF_XDP sockets to configure. Set this to a value no larger than half of the maximum queues for the interface. For U4P and U4C instances, this is typically8(half of the default 16 queues). For U4S instances, it is up to8depending on the machine size. You can verify the maximum queues by runningethtool -l NIC_NAME.
Disable hardware GRO and LRO: Because gVNIC doesn't support multi-buffer XDP, you must disable Large Receive Offload (LRO) and hardware Generic Receive Offload (GRO):
ethtool -K NIC_NAME rx-gro-hw off ethtool -K NIC_NAME lro off
Reduce the RX buffer length: By default, newer drivers post 4 KB (4,096-byte) buffers to the network interface for RX, but XDP requires a buffer length of
2048:ethtool -G NIC_NAME rx-buf-len 2048
Script
Alternatively, you can run the following Bash script for each network interface that you want to configure. The script automatically prepares a given network interface for XDP by reducing the queue counts, disabling offloads, and adjusting the RX buffer length:
#!/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
Run your DPDK application
To use the AF_XDP PMD, include the --vdev flag in the Environment
Abstraction Layer (EAL) arguments of your DPDK application.
The following example command includes several key parameters. For detailed information about setup and parameters, see AF_XDP Poll Mode Driver in the DPDK documentation.
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
Replace the following:
DPDK_APPLICATION: the DPDK application binary to run.PCIE_BDF: the PCI address of the network interface. You can find this value by runningethtool -i NIC_NAMEand checking thebus-infovalue, such as0000:00:04.0.NIC_NAME: the OS name of the network interface, such aseth1.NUM_SOCKETS: the number of AF_XDP sockets to open. Each socket attaches to a single queue pair. This value must match the queue count that you configured on the interface.START_QUEUE: the starting queue index for the AF_XDP sockets.XDP_PROG: a custom XDP program to run on received packets. If omitted, DPDK uses the default XDP program provided bylibxdp.APPLICATION_ARGS: arguments specific to your DPDK application.
Use driver features
This section provides usage information for flow steering and RX timestamping.
Flow steering
You can use flow steering with XDP to steer application packets to a specific subset of queues, leaving the remaining queues for kernel traffic. The following sections describe two approaches that you can use for flow rule programming.
Pre-program flow steering (recommended)
Because on-the-fly flow programming using ioctl calls isn't supported by
the AF_XDP PMD and requires modifying your DPDK application, we recommend
pre-programming your flow rules by using ethtool before starting your
application.
For example, to program a flow rule that steers IPv4 UDP traffic to queue 0:
ethtool -N NIC_NAME flow-type udp4 \ dst-ip DST_IP dst-port DST_PORT action 0 loc 0
Replace the following:
NIC_NAME: the OS name of the network interface, such aseth1.DST_IP: the destination IP address of the traffic to steer.DST_PORT: the destination port of the traffic to steer.
Program flow rules on the fly
The AF_XDP PMD doesn't support on-the-fly flow programming. To use this
method, you must modify your DPDK application to manually send ethtool ioctl calls.
We recommend avoiding this approach unless your application handles a large number of ephemeral connections. gVNIC supports up to 20,000 3-tuple flow steering rules; if you require fewer rules and know your destination IP addresses and ports in advance, pre-program your rules instead.
If you must program rules dynamically, refer to the following C code example:
Expand to view C code example
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 programming
When using flow steering, RSS can help provide better traffic isolation.
As with flow steering, you can configure RSS by using ethtool. We recommend
configuring RSS before running your application.
The following example shows how to configure RSS to work with 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
RX timestamping
Although RX timestamping isn't supported in the upstream DPDK repository for the AF_XDP PMD, you can use RX timestamping by doing the following:
Apply the required patches to the DPDK source tree and recompile the DPDK source and your application. These patches add support for
rte_eth_read_clockand RX timestamps in receivedmbufs.Use an XDP program that loads the timestamp into the metadata.
Provide the following additional AF_XDP
vdevparameters when starting your DPDK application:xdp_meta_rx_ts_offset: the byte offset from the start of the XDP metadata where the 64-bit RX timestamp value is located.xdp_meta_valid_hint_offset: (Optional) the byte offset covering a 1-byte flag field indicating whether the timestamp is valid.xdp_meta_rx_ts_valid_mask: (Optional) the bitmask used to extract the valid flag bits.If
ctxis the start of the metadata, then the value of(ctx->data_meta + xdp_meta_valid_hint_offset) & xdp_meta_rx_ts_valid_maskdescribes whether the timestamp is valid.
What's next
- To synchronize your instance clock to the physical NIC clock of its host server, see Configure accurate time.