|SUCCESS| [v4,2/2] test: add test for af_packet

dpdklab at iol.unh.edu dpdklab at iol.unh.edu
Fri Apr 17 01:38:43 CEST 2026


Test-Label: ai-code-review
Test-Status: SUCCESS
https://dpdk.org/patch/163125

_AI Code Review_

=== Patch Review: series.patch (via Claude) ===
Review date: 2026-04-16

Reviewing the patch series for DPDK coding standards compliance and potential issues.

commit 5a478bee46e88-2ba9c484e5aso8641865eec
Author: Stephen Hemminger <stephen at networkplumber.org>

net/af_packet: fix indentation

DPDK code is supposed to be indented with TABS not spaces.
Also, used "unsigned int" to keep checkpatch happy.

> diff --git a/drivers/net/af_packet/rte_eth_af_packet.c b/drivers/net/af_packet/rte_eth_af_packet.c
> index 0ee94e71ea..8303ff5ca9 100644
> --- a/drivers/net/af_packet/rte_eth_af_packet.c
> +++ b/drivers/net/af_packet/rte_eth_af_packet.c

[ ... ]

> @@ -890,7 +890,7 @@ rte_pmd_init_internals(struct rte_vdev_device *dev,
>  	if (pair == NULL) {
>  		PMD_LOG(ERR,
>  			"%s: no interface specified for AF_PACKET ethdev",
> -		        name);
> +			name);

Does this line still have a mix of tabs and spaces, or is it now purely
tabs? The coding standard requires pure tabs for indentation.

> @@ -1226,7 +1226,7 @@ rte_eth_from_packet(struct rte_vdev_device *dev,
>  	if (framesize > blocksize) {
>  		PMD_LOG(ERR,
>  			"%s: AF_PACKET MMAP frame size exceeds block size!",
> -		        name);
> +			name);

Same concern - is the indentation now pure tabs?


commit 5a478bee46e88-2e2e4f21fa8mr213470eec
Author: Stephen Hemminger <stephen at networkplumber.org>

test: add test for af_packet

> diff --git a/app/test/test_pmd_af_packet.c b/app/test/test_pmd_af_packet.c
> new file mode 100644
> index 0000000000..abd60fe18c
> --- /dev/null
> +++ b/app/test/test_pmd_af_packet.c
> @@ -0,0 +1,1122 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2026 Stephen Hemminger
> + */
> +
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <inttypes.h>
> +#include <linux/if.h>
> +#include <linux/if_tun.h>
> +#include <linux/sockios.h>
> +#include <pthread.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <sys/ioctl.h>
> +#include <sys/socket.h>
> +#include <time.h>
> +#include <unistd.h>

The coding style guide states system headers should be included before
RTE headers. Can the ordering be verified for compliance?

> +static int
> +create_tap_interface(const char *name)
> +{
> +	struct ifreq ifr;
> +	int fd, ret;
> +
> +	fd = open("/dev/net/tun", O_RDWR);
> +	if (fd < 0) {
> +		printf("Cannot open /dev/net/tun: %s\n", strerror(errno));
> +		printf("(Are you running as root?)\n");
> +		return -1;
> +	}

Does this code properly handle the case where open() succeeds but later
operations fail? If ioctl() or socket() fails, does fd leak?

> +	ret = ioctl(fd, TUNSETIFF, &ifr);
> +	if (ret < 0) {
> +		printf("Cannot create TAP interface '%s': %s\n",
> +		       name, strerror(errno));
> +		close(fd);
> +		return -1;
> +	}
> +
> +	/* Bring the interface up */
> +	int sock = socket(AF_INET, SOCK_DGRAM, 0);

Does this follow C99 style requirements? The coding standard requires
all declarations at the start of the block. Does sock need to be
declared at the function entry?

> +	if (sock >= 0) {
> +		memset(&ifr, 0, sizeof(ifr));
> +		snprintf(ifr.ifr_name, IFNAMSIZ, "%s", name);
> +
> +		/* Get current flags */
> +		if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) {
> +			ifr.ifr_flags |= IFF_UP;
> +			ioctl(sock, SIOCSIFFLAGS, &ifr);
> +		}
> +		close(sock);
> +	}

If socket() fails (sock < 0), does fd leak?

> +static unsigned int
> +alloc_tx_mbufs(struct rte_mbuf **bufs, unsigned int count)
> +{
> +	unsigned int i;
> +	int ret;
> +
> +	ret = rte_pktmbuf_alloc_bulk(mp, bufs, count);
> +	if (ret != 0)
> +		return 0;
> +
> +	for (i = 0; i < count; i++) {
> +		struct rte_ether_hdr *eth_hdr;
> +		eth_hdr = (struct rte_ether_hdr *)rte_pktmbuf_append(bufs[i],
> +				sizeof(struct rte_ether_hdr) + 46);
> +		if (eth_hdr == NULL) {
> +			/* Free all allocated mbufs on failure */
> +			rte_pktmbuf_free_bulk(bufs, count);
> +			return 0;
> +		}

If rte_pktmbuf_append() fails when i > 0, does this code correctly free
only the mbufs that were allocated? Should it free i mbufs instead of
count?

> +static int
> +test_af_packet_setup(void)
> +{
> +	char devargs[256];
> +	int ret;
> +
> +	/* Create mempool for mbufs */
> +	mp = rte_pktmbuf_pool_create("af_packet_test_pool", NUM_MBUFS,
> +				     MBUF_CACHE_SIZE, 0,
> +				     RTE_MBUF_DEFAULT_BUF_SIZE,
> +				     rte_socket_id());
> +	if (mp == NULL) {
> +		printf("Failed to create mempool\n");
> +		return -1;
> +	}

Does this function properly clean up mp if later operations fail? What
happens if create_tap_interface() fails - does mp leak?

> +	/* Create TAP interface for testing */
> +	tap_fd = create_tap_interface(TAP_DEV_NAME);
> +	if (tap_fd >= 0)
> +		tap_created = true;
> +	else {
> +		printf("TAP interface creation failed - tests will be skipped\n");
> +		return 0; /* Return success to allow skipped tests */
> +	}

When returning 0 here, is mp properly freed or does it leak until
teardown?

> +	ret = create_af_packet_port(AF_PACKET_DEV_NAME, devargs, &port_id);
> +	if (ret != 0) {
> +		printf("Failed to create af_packet port\n");
> +		return -1;
> +	}
> +	port_created = true;
> +
> +	ret = configure_af_packet_port(port_id, 1, 1);
> +	if (ret != 0) {
> +		printf("Failed to configure af_packet port\n");
> +		return -1;
> +	}
> +	port_started = true;

When these failures occur, does the cleanup path properly handle all the
resources allocated so far (mp, tap_fd, port)?

> +static int
> +test_af_packet_loopback(void)
> +{
> +	struct rte_mbuf *tx_bufs[BURST_SIZE];
> +	struct rte_mbuf *rx_bufs[BURST_SIZE];
> +	uint16_t nb_tx, total_rx = 0;
> +	uint64_t elapsed = 0;
> +	unsigned int allocated;
> +	char tap_buf[2048];

Does this 2048-byte stack allocation create any portability concerns?

> +	/* Set TAP fd to non-blocking for reading */
> +	int flags = fcntl(tap_fd, F_GETFL, 0);

Does this follow C99 style? Should flags be declared at function entry?

> +	fcntl(tap_fd, F_SETFL, flags | O_NONBLOCK);

If fcntl(F_GETFL) fails and returns -1, does this code properly handle
the error case?

> +	/* Read from TAP (what af_packet sent) */
> +		bytes_read = read(tap_fd, tap_buf, sizeof(tap_buf));
> +		if (bytes_read > 0) {
> +			/* Write back to TAP for af_packet to receive */
> +			bytes_written = write(tap_fd, tap_buf, bytes_read);
> +			TEST_ASSERT(bytes_read == bytes_written, "TAP write %zd != %zd",
> +				    bytes_read, bytes_written);

What happens if write() returns an error? Does this properly handle
short writes or errors?

> +static int
> +test_af_packet_multi_queue(void)
> +{

[ ... ]

> +	allocated = alloc_tx_mbufs(bufs, BURST_SIZE / 2);
> +		if (allocated > 0)
> +			do_tx_burst(test_port, q, bufs, allocated);

Does the indentation look correct here? Should the if statement be
aligned with the for loop?

> +static void *
> +mt_reader_thread(void *arg)
> +{
> +	struct mt_test_ctx *ctx = arg;
> +	struct rte_mbuf *bufs[BURST_SIZE];
> +	uint16_t nb_rx;
> +
> +	while (!rte_atomic_load_explicit(&ctx->stop, rte_memory_order_relaxed)) {
> +		nb_rx = rte_eth_rx_burst(ctx->port_id, 0, bufs, BURST_SIZE);
> +		if (nb_rx > 0) {
> +			ctx->rx_count += nb_rx;

Is ctx->rx_count properly protected for concurrent access? The reader
thread writes it and the main thread reads it - does this need atomic
operations?


More information about the test-report mailing list