|SUCCESS| [v5,2/2] test: add test for af_packet
dpdklab at iol.unh.edu
dpdklab at iol.unh.edu
Tue Apr 28 17:50:41 CEST 2026
Test-Label: ai-code-review
Test-Status: SUCCESS
https://dpdk.org/patch/163317
_AI Code Review_
=== Patch Review: series.patch (via Claude) ===
Review date: 2026-04-28
Reviewing the patch series for DPDK coding standards and guidelines compliance.
commit sha: (from patch headers)
Author: Stephen Hemminger <stephen at networkplumber.org>
Subject: [PATCH v5 1/2] net/af_packet: fix indentation
This patch fixes indentation issues by converting spaces to tabs and
changes 'unsigned' to 'unsigned int' in the af_packet driver.
> 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
[ ... ]
> @@ -854,17 +854,17 @@ get_fanout(const char *fanout_mode, int if_index)
>
> static int
> rte_pmd_init_internals(struct rte_vdev_device *dev,
> - const int sockfd,
> - const unsigned nb_queues,
> - unsigned int blocksize,
> - unsigned int blockcnt,
> - unsigned int framesize,
> - unsigned int framecnt,
> + const int sockfd,
> + const unsigned int nb_queues,
> + unsigned int blocksize,
> + unsigned int blockcnt,
> + unsigned int framesize,
> + unsigned int framecnt,
> unsigned int qdisc_bypass,
> const char *fanout_mode,
> - struct pmd_internals **internals,
> - struct rte_eth_dev **eth_dev,
> - struct rte_kvargs *kvlist)
> + struct pmd_internals **internals,
> + struct rte_eth_dev **eth_dev,
> + struct rte_kvargs *kvlist)
Does this code change qdisc_bypass from 'const unsigned int' to
'unsigned int'? The parameter qdisc_bypass appears to lose its const
qualifier compared to the other parameters, while nb_queues gains one.
Is this intentional or does qdisc_bypass need const added for
consistency?
[ ... ]
---
commit sha: (from patch headers)
Author: Stephen Hemminger <stephen at networkplumber.org>
Subject: [PATCH v5 2/2] test: add test for af_packet
This patch adds comprehensive unit tests for the af_packet PMD.
> diff --git a/app/test/test_pmd_af_packet.c b/app/test/test_pmd_af_packet.c
> new file mode 100644
> index 0000000000..8b36c3b539
> --- /dev/null
> +++ b/app/test/test_pmd_af_packet.c
[ ... ]
> +/*
> + * Create a TAP interface for testing.
> + * Returns fd on success, -1 on failure.
> + */
> +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;
> + }
> +
> + memset(&ifr, 0, sizeof(ifr));
> + ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
> + snprintf(ifr.ifr_name, IFNAMSIZ, "%s", name);
> +
> + 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 code have a resource leak if socket() succeeds but subsequent
operations fail? The socket needs to be closed on all paths.
> + 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);
> + }
> +
> + printf("Created TAP interface '%s'\n", name);
> + return fd;
> +}
[ ... ]
> +/*
> + * Helper: Allocate and prepare a burst of TX mbufs with minimal Ethernet frames.
> + * Returns the number of successfully allocated mbufs.
> + */
> +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);
Does this code potentially free uninitialized pointers? When
rte_pktmbuf_append() fails for bufs[i], the function calls
rte_pktmbuf_free_bulk(bufs, count) which will attempt to free bufs[i+1]
through bufs[count-1] that were never initialized by
rte_pktmbuf_alloc_bulk(). Should this be
rte_pktmbuf_free_bulk(bufs, i + 1) instead?
> + return 0;
> + }
> +
> + /* Set broadcast destination and zero source MAC */
> + memset(ð_hdr->dst_addr, 0xFF, RTE_ETHER_ADDR_LEN);
> + memset(ð_hdr->src_addr, 0x00, RTE_ETHER_ADDR_LEN);
> + eth_hdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
> + }
> +
> + return 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;
> + }
> +
> + /* 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 */
> + }
> +
> + /* Create and configure af_packet port */
> + snprintf(devargs, sizeof(devargs), "iface=%s", TAP_DEV_NAME);
> + 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;
> + }
Does this code leak resources on error? When
configure_af_packet_port() fails, the function returns -1 without
calling test_af_packet_teardown(), leaving tap_fd, port_id, and mp
allocated. Should there be cleanup here or does the test framework
call teardown on setup failure?
[ ... ]
> +/*
> + * Multi-threaded test context
> + */
> +struct mt_test_ctx {
> + uint16_t port_id;
> + RTE_ATOMIC(uint32_t) stop;
> + uint32_t rx_count;
> +};
Does this code have a potential race condition? The rx_count field is
accessed from multiple threads (incremented in mt_reader_thread() and
read in test_af_packet_multithread()) but is not declared as atomic.
Should rx_count be RTE_ATOMIC(uint32_t) or should the access be
protected?
> +
> +/*
> + * Reader thread function for multi-threaded test
> + */
> +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;
> + rte_pktmbuf_free_bulk(bufs, nb_rx);
> + }
> +
> + rte_delay_us_block(50);
> + }
> +
> + return NULL;
> +}
More information about the test-report
mailing list