LCOV - code coverage report
Current view: top level - app/test - test_pmd_af_packet.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 298 349 85.4 %
Date: 2026-06-01 18:36:17 Functions: 28 28 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 150 290 51.7 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  * Copyright(c) 2026 Stephen Hemminger
       3                 :            :  */
       4                 :            : 
       5                 :            : #include <errno.h>
       6                 :            : #include <fcntl.h>
       7                 :            : #include <inttypes.h>
       8                 :            : #include <sys/ioctl.h>
       9                 :            : #include <sys/socket.h>
      10                 :            : #include <linux/if.h>
      11                 :            : #include <linux/if_tun.h>
      12                 :            : #include <linux/sockios.h>
      13                 :            : #include <pthread.h>
      14                 :            : #include <stdbool.h>
      15                 :            : #include <stdio.h>
      16                 :            : #include <string.h>
      17                 :            : #include <time.h>
      18                 :            : #include <unistd.h>
      19                 :            : 
      20                 :            : #include <rte_bus_vdev.h>
      21                 :            : #include <rte_config.h>
      22                 :            : #include <rte_cycles.h>
      23                 :            : #include <rte_ethdev.h>
      24                 :            : #include <rte_ether.h>
      25                 :            : #include <rte_lcore.h>
      26                 :            : #include <rte_mbuf.h>
      27                 :            : #include <rte_mempool.h>
      28                 :            : #include <rte_stdatomic.h>
      29                 :            : 
      30                 :            : #include "test.h"
      31                 :            : 
      32                 :            : #ifndef RTE_EXEC_ENV_LINUX
      33                 :            : static int
      34                 :            : test_pmd_af_packet(void)
      35                 :            : {
      36                 :            :         printf("af_packet only supported on Linux, skipping test\n");
      37                 :            :         return TEST_SKIPPED;
      38                 :            : }
      39                 :            : 
      40                 :            : #else
      41                 :            : 
      42                 :            : #define NUM_MBUFS 512
      43                 :            : #define MBUF_CACHE_SIZE 32
      44                 :            : #define BURST_SIZE 32
      45                 :            : #define RING_SIZE 256
      46                 :            : 
      47                 :            : /* Polling and timeout constants */
      48                 :            : #define STATS_POLL_INTERVAL_US 100
      49                 :            : #define STATS_POLL_TIMEOUT_US 100000  /* 100ms overall timeout */
      50                 :            : #define LOOPBACK_TIMEOUT_US 500000    /* 500ms for loopback test */
      51                 :            : 
      52                 :            : /* Multi-threaded test constants */
      53                 :            : #define MT_NUM_PACKETS 100
      54                 :            : #define MT_TEST_DURATION_SEC 2
      55                 :            : 
      56                 :            : /* Test device names */
      57                 :            : #define AF_PACKET_DEV_NAME "net_af_packet_test"
      58                 :            : #define TAP_DEV_NAME "dpdkafptest"
      59                 :            : 
      60                 :            : static struct rte_mempool *mp;
      61                 :            : static uint16_t port_id = RTE_MAX_ETHPORTS;
      62                 :            : static int tap_fd = -1;
      63                 :            : static bool tap_created;
      64                 :            : static bool port_created;
      65                 :            : static bool port_started;
      66                 :            : 
      67                 :            : /*
      68                 :            :  * Create a TAP interface for testing.
      69                 :            :  * Returns fd on success, -1 on failure.
      70                 :            :  */
      71                 :            : static int
      72                 :          1 : create_tap_interface(const char *name)
      73                 :            : {
      74                 :            :         struct ifreq ifr;
      75                 :            :         int fd, ret;
      76                 :            : 
      77                 :            :         fd = open("/dev/net/tun", O_RDWR);
      78         [ -  + ]:          1 :         if (fd < 0) {
      79                 :          0 :                 printf("Cannot open /dev/net/tun: %s\n", strerror(errno));
      80                 :            :                 printf("(Are you running as root?)\n");
      81                 :          0 :                 return -1;
      82                 :            :         }
      83                 :            : 
      84                 :            :         memset(&ifr, 0, sizeof(ifr));
      85                 :          1 :         ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
      86                 :            :         snprintf(ifr.ifr_name, IFNAMSIZ, "%s", name);
      87                 :            : 
      88                 :          1 :         ret = ioctl(fd, TUNSETIFF, &ifr);
      89         [ -  + ]:          1 :         if (ret < 0) {
      90                 :          0 :                 printf("Cannot create TAP interface '%s': %s\n",
      91                 :          0 :                        name, strerror(errno));
      92                 :          0 :                 close(fd);
      93                 :          0 :                 return -1;
      94                 :            :         }
      95                 :            : 
      96                 :            :         /* Bring the interface up */
      97                 :          1 :         int sock = socket(AF_INET, SOCK_DGRAM, 0);
      98         [ +  - ]:          1 :         if (sock >= 0) {
      99                 :            :                 memset(&ifr, 0, sizeof(ifr));
     100                 :            :                 snprintf(ifr.ifr_name, IFNAMSIZ, "%s", name);
     101                 :            : 
     102                 :            :                 /* Get current flags */
     103         [ +  - ]:          1 :                 if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) {
     104                 :          1 :                         ifr.ifr_flags |= IFF_UP;
     105                 :          1 :                         ioctl(sock, SIOCSIFFLAGS, &ifr);
     106                 :            :                 }
     107                 :          1 :                 close(sock);
     108                 :            :         }
     109                 :            : 
     110                 :            :         printf("Created TAP interface '%s'\n", name);
     111                 :          1 :         return fd;
     112                 :            : }
     113                 :            : 
     114                 :            : static void
     115                 :            : destroy_tap_interface(int fd)
     116                 :            : {
     117                 :          1 :         if (fd >= 0)
     118                 :          1 :                 close(fd);
     119                 :            : }
     120                 :            : 
     121                 :            : static int
     122                 :          1 : create_af_packet_port(const char *name, const char *args, uint16_t *out_port_id)
     123                 :            : {
     124                 :            :         int ret;
     125                 :            : 
     126                 :          1 :         ret = rte_vdev_init(name, args);
     127         [ -  + ]:          1 :         if (ret != 0) {
     128                 :            :                 printf("Failed to create af_packet device '%s': %d\n", name, ret);
     129                 :          0 :                 return ret;
     130                 :            :         }
     131                 :            : 
     132                 :          1 :         ret = rte_eth_dev_get_port_by_name(name, out_port_id);
     133         [ -  + ]:          1 :         if (ret != 0) {
     134                 :            :                 printf("Failed to get port id for '%s': %d\n", name, ret);
     135                 :          0 :                 rte_vdev_uninit(name);
     136                 :          0 :                 return ret;
     137                 :            :         }
     138                 :            : 
     139                 :            :         return 0;
     140                 :            : }
     141                 :            : 
     142                 :            : static int
     143                 :          1 : configure_af_packet_port(uint16_t pid, uint16_t nb_rx_q, uint16_t nb_tx_q)
     144                 :            : {
     145                 :          1 :         struct rte_eth_conf port_conf = {0};
     146                 :            :         struct rte_eth_dev_info dev_info;
     147                 :            :         int ret;
     148                 :            :         uint16_t q;
     149                 :            : 
     150                 :          1 :         ret = rte_eth_dev_info_get(pid, &dev_info);
     151         [ -  + ]:          1 :         if (ret != 0) {
     152                 :            :                 printf("Failed to get device info for port %u: %d\n", pid, ret);
     153                 :          0 :                 return ret;
     154                 :            :         }
     155                 :            : 
     156                 :          1 :         ret = rte_eth_dev_configure(pid, nb_rx_q, nb_tx_q, &port_conf);
     157         [ -  + ]:          1 :         if (ret != 0) {
     158                 :            :                 printf("Failed to configure port %u: %d\n", pid, ret);
     159                 :          0 :                 return ret;
     160                 :            :         }
     161                 :            : 
     162         [ +  + ]:          2 :         for (q = 0; q < nb_rx_q; q++) {
     163                 :          1 :                 ret = rte_eth_rx_queue_setup(pid, q, RING_SIZE,
     164                 :          1 :                                              rte_eth_dev_socket_id(pid),
     165                 :            :                                              NULL, mp);
     166         [ -  + ]:          1 :                 if (ret != 0) {
     167                 :            :                         printf("Failed to setup RX queue %u for port %u: %d\n",
     168                 :            :                                q, pid, ret);
     169                 :          0 :                         return ret;
     170                 :            :                 }
     171                 :            :         }
     172                 :            : 
     173         [ +  + ]:          2 :         for (q = 0; q < nb_tx_q; q++) {
     174                 :          1 :                 ret = rte_eth_tx_queue_setup(pid, q, RING_SIZE,
     175                 :          1 :                                              rte_eth_dev_socket_id(pid),
     176                 :            :                                              NULL);
     177         [ -  + ]:          1 :                 if (ret != 0) {
     178                 :            :                         printf("Failed to setup TX queue %u for port %u: %d\n",
     179                 :            :                                q, pid, ret);
     180                 :          0 :                         return ret;
     181                 :            :                 }
     182                 :            :         }
     183                 :            : 
     184                 :          1 :         ret = rte_eth_dev_start(pid);
     185         [ -  + ]:          1 :         if (ret != 0) {
     186                 :            :                 printf("Failed to start port %u: %d\n", pid, ret);
     187                 :          0 :                 return ret;
     188                 :            :         }
     189                 :            : 
     190                 :            :         return 0;
     191                 :            : }
     192                 :            : 
     193                 :            : /*
     194                 :            :  * Helper: Allocate and prepare a burst of TX mbufs with minimal Ethernet frames.
     195                 :            :  * Returns the number of successfully allocated mbufs.
     196                 :            :  */
     197                 :            : static unsigned int
     198                 :         18 : alloc_tx_mbufs(struct rte_mbuf **bufs, unsigned int count)
     199                 :            : {
     200                 :            :         unsigned int i;
     201                 :            :         int ret;
     202                 :            : 
     203                 :         18 :         ret = rte_pktmbuf_alloc_bulk(mp, bufs, count);
     204         [ +  - ]:         18 :         if (ret != 0)
     205                 :            :                 return 0;
     206                 :            : 
     207         [ +  + ]:        234 :         for (i = 0; i < count; i++) {
     208                 :            :                 struct rte_ether_hdr *eth_hdr;
     209                 :        216 :                 eth_hdr = (struct rte_ether_hdr *)rte_pktmbuf_append(bufs[i],
     210                 :            :                                 sizeof(struct rte_ether_hdr) + 46);
     211         [ -  + ]:        216 :                 if (eth_hdr == NULL) {
     212                 :            :                         /* Free all allocated mbufs on failure */
     213                 :          0 :                         rte_pktmbuf_free_bulk(bufs, count);
     214                 :          0 :                         return 0;
     215                 :            :                 }
     216                 :            : 
     217                 :            :                 /* Set broadcast destination and zero source MAC */
     218                 :        216 :                 memset(&eth_hdr->dst_addr, 0xFF, RTE_ETHER_ADDR_LEN);
     219                 :        216 :                 memset(&eth_hdr->src_addr, 0x00, RTE_ETHER_ADDR_LEN);
     220                 :        216 :                 eth_hdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
     221                 :            :         }
     222                 :            : 
     223                 :            :         return count;
     224                 :            : }
     225                 :            : 
     226                 :            : /*
     227                 :            :  * Helper: Transmit packets and free unsent mbufs.
     228                 :            :  * Returns the number of packets successfully transmitted.
     229                 :            :  */
     230                 :            : static uint16_t
     231                 :         18 : do_tx_burst(uint16_t pid, uint16_t queue_id, struct rte_mbuf **bufs,
     232                 :            :             unsigned int count)
     233                 :            : {
     234                 :            :         uint16_t nb_tx;
     235                 :            : 
     236                 :         18 :         nb_tx = rte_eth_tx_burst(pid, queue_id, bufs, count);
     237                 :            : 
     238                 :            :         /* Free any unsent mbufs */
     239                 :         18 :         rte_pktmbuf_free_bulk(&bufs[nb_tx], count - nb_tx);
     240                 :            : 
     241                 :         18 :         return nb_tx;
     242                 :            : }
     243                 :            : 
     244                 :            : /*
     245                 :            :  * Helper: Receive packets and free received mbufs.
     246                 :            :  * Returns the number of packets received.
     247                 :            :  */
     248                 :            : static uint16_t
     249                 :            : do_rx_burst(uint16_t pid, uint16_t queue_id, struct rte_mbuf **bufs,
     250                 :            :             unsigned int count)
     251                 :            : {
     252                 :            :         uint16_t nb_rx;
     253                 :            : 
     254                 :          3 :         nb_rx = rte_eth_rx_burst(pid, queue_id, bufs, count);
     255                 :          3 :         rte_pktmbuf_free_bulk(bufs, nb_rx);
     256                 :            : 
     257                 :            :         return nb_rx;
     258                 :            : }
     259                 :            : 
     260                 :            : /*
     261                 :            :  * Helper: Wait for stats to update by polling at intervals.
     262                 :            :  * Returns 0 on success (stats updated), -1 on timeout.
     263                 :            :  */
     264                 :            : static int
     265                 :          2 : wait_for_stats_update(uint16_t pid, uint64_t expected_opackets,
     266                 :            :                       uint64_t timeout_us)
     267                 :            : {
     268                 :            :         struct rte_eth_stats stats;
     269                 :            :         uint64_t elapsed = 0;
     270                 :            : 
     271         [ +  - ]:          2 :         while (elapsed < timeout_us) {
     272         [ +  - ]:          2 :                 if (rte_eth_stats_get(pid, &stats) == 0) {
     273         [ -  + ]:          2 :                         if (stats.opackets >= expected_opackets)
     274                 :            :                                 return 0;
     275                 :            :                 }
     276                 :          0 :                 rte_delay_us_block(STATS_POLL_INTERVAL_US);
     277                 :          0 :                 elapsed += STATS_POLL_INTERVAL_US;
     278                 :            :         }
     279                 :            : 
     280                 :            :         return -1;
     281                 :            : }
     282                 :            : 
     283                 :            : static int
     284                 :          1 : test_af_packet_setup(void)
     285                 :            : {
     286                 :            :         char devargs[256];
     287                 :            :         int ret;
     288                 :            : 
     289                 :            :         /* Create mempool for mbufs */
     290                 :          1 :         mp = rte_pktmbuf_pool_create("af_packet_test_pool", NUM_MBUFS,
     291                 :            :                                      MBUF_CACHE_SIZE, 0,
     292                 :            :                                      RTE_MBUF_DEFAULT_BUF_SIZE,
     293                 :          1 :                                      rte_socket_id());
     294         [ -  + ]:          1 :         if (mp == NULL) {
     295                 :            :                 printf("Failed to create mempool\n");
     296                 :          0 :                 return -1;
     297                 :            :         }
     298                 :            : 
     299                 :            :         /* Create TAP interface for testing */
     300                 :          1 :         tap_fd = create_tap_interface(TAP_DEV_NAME);
     301         [ +  - ]:          1 :         if (tap_fd >= 0)
     302                 :          1 :                 tap_created = true;
     303                 :            :         else {
     304                 :            :                 printf("TAP interface creation failed - tests will be skipped\n");
     305                 :          0 :                 return 0; /* Return success to allow skipped tests */
     306                 :            :         }
     307                 :            : 
     308                 :            :         /* Create and configure af_packet port */
     309                 :            :         snprintf(devargs, sizeof(devargs), "iface=%s", TAP_DEV_NAME);
     310                 :          1 :         ret = create_af_packet_port(AF_PACKET_DEV_NAME, devargs, &port_id);
     311         [ -  + ]:          1 :         if (ret != 0) {
     312                 :            :                 printf("Failed to create af_packet port\n");
     313                 :          0 :                 return -1;
     314                 :            :         }
     315                 :          1 :         port_created = true;
     316                 :            : 
     317                 :          1 :         ret = configure_af_packet_port(port_id, 1, 1);
     318         [ -  + ]:          1 :         if (ret != 0) {
     319                 :            :                 printf("Failed to configure af_packet port\n");
     320                 :          0 :                 return -1;
     321                 :            :         }
     322                 :          1 :         port_started = true;
     323                 :            : 
     324                 :          1 :         return 0;
     325                 :            : }
     326                 :            : 
     327                 :            : static void
     328                 :          1 : test_af_packet_teardown(void)
     329                 :            : {
     330                 :            :         /* Stop and close test port */
     331         [ +  - ]:          1 :         if (port_started) {
     332                 :          1 :                 rte_eth_dev_stop(port_id);
     333                 :          1 :                 port_started = false;
     334                 :            :         }
     335                 :            : 
     336         [ +  - ]:          1 :         if (port_created) {
     337                 :          1 :                 rte_eth_dev_close(port_id);
     338                 :          1 :                 rte_vdev_uninit(AF_PACKET_DEV_NAME);
     339                 :          1 :                 port_id = RTE_MAX_ETHPORTS;
     340                 :          1 :                 port_created = false;
     341                 :            :         }
     342                 :            : 
     343                 :            :         /* Destroy TAP interface */
     344         [ +  - ]:          1 :         if (tap_created) {
     345         [ +  - ]:          1 :                 destroy_tap_interface(tap_fd);
     346                 :          1 :                 tap_fd = -1;
     347                 :          1 :                 tap_created = false;
     348                 :            :         }
     349                 :            : 
     350         [ +  - ]:          1 :         if (mp != NULL) {
     351                 :          1 :                 rte_mempool_free(mp);
     352                 :          1 :                 mp = NULL;
     353                 :            :         }
     354                 :          1 : }
     355                 :            : 
     356                 :            : /*
     357                 :            :  * Test: Device info verification
     358                 :            :  */
     359                 :            : static int
     360                 :          1 : test_af_packet_dev_info(void)
     361                 :            : {
     362                 :            :         struct rte_eth_dev_info dev_info;
     363                 :            :         int ret;
     364                 :            : 
     365   [ +  -  -  + ]:          1 :         if (!tap_created || port_id >= RTE_MAX_ETHPORTS) {
     366                 :            :                 printf("SKIPPED: Port not available (need root)\n");
     367                 :          0 :                 return TEST_SKIPPED;
     368                 :            :         }
     369                 :            : 
     370                 :          1 :         ret = rte_eth_dev_info_get(port_id, &dev_info);
     371         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get device info");
     372                 :            : 
     373                 :            :         /* Verify expected device info values */
     374         [ -  + ]:          1 :         TEST_ASSERT(dev_info.max_mac_addrs == 1,
     375                 :            :                     "Expected max_mac_addrs=1, got %u", dev_info.max_mac_addrs);
     376                 :            : 
     377                 :            :         /* The driver has default internal frame size of 2K */
     378         [ -  + ]:          1 :         TEST_ASSERT(dev_info.max_rx_pktlen >= RTE_ETHER_MAX_LEN,
     379                 :            :                     "max_rx_pktlen %u < %u",
     380                 :            :                     dev_info.max_rx_pktlen, RTE_ETHER_MAX_LEN);
     381         [ -  + ]:          1 :         TEST_ASSERT(dev_info.max_mtu >= RTE_ETHER_MTU,
     382                 :            :                     "max_mtu %u < %u", dev_info.max_mtu, RTE_ETHER_MTU);
     383         [ -  + ]:          1 :         TEST_ASSERT(dev_info.min_rx_bufsize == 0,
     384                 :            :                     "Expected min_rx_bufsize=0, got %u", dev_info.min_rx_bufsize);
     385         [ -  + ]:          1 :         TEST_ASSERT(dev_info.max_rx_queues >= 1, "No RX queues available");
     386         [ -  + ]:          1 :         TEST_ASSERT(dev_info.max_tx_queues >= 1, "No TX queues available");
     387         [ -  + ]:          1 :         TEST_ASSERT(dev_info.if_index > 0, "Invalid interface index");
     388                 :            : 
     389                 :            :         /* Check TX offload capabilities */
     390         [ -  + ]:          1 :         TEST_ASSERT(dev_info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_MULTI_SEGS,
     391                 :            :                     "Expected MULTI_SEGS TX offload capability");
     392         [ -  + ]:          1 :         TEST_ASSERT(dev_info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_VLAN_INSERT,
     393                 :            :                     "Expected VLAN_INSERT TX offload capability");
     394                 :            : 
     395                 :            :         /* Check RX offload capabilities */
     396         [ -  + ]:          1 :         TEST_ASSERT(dev_info.rx_offload_capa & RTE_ETH_RX_OFFLOAD_VLAN_STRIP,
     397                 :            :                     "Expected VLAN_STRIP RX offload capability");
     398         [ -  + ]:          1 :         TEST_ASSERT(dev_info.rx_offload_capa & RTE_ETH_RX_OFFLOAD_TIMESTAMP,
     399                 :            :                     "Expected TIMESTAMP RX offload capability");
     400                 :            : 
     401                 :            :         return TEST_SUCCESS;
     402                 :            : }
     403                 :            : 
     404                 :            : /*
     405                 :            :  * Test: Link status
     406                 :            :  * Note: af_packet PMD link status reflects the underlying interface state,
     407                 :            :  * not the DPDK device start/stop state.
     408                 :            :  */
     409                 :            : static int
     410                 :          1 : test_af_packet_link_status(void)
     411                 :            : {
     412                 :            :         struct rte_eth_link link;
     413                 :            :         int ret;
     414                 :            : 
     415   [ +  -  -  + ]:          1 :         if (!tap_created || port_id >= RTE_MAX_ETHPORTS) {
     416                 :            :                 printf("SKIPPED: Port not available (need root)\n");
     417                 :          0 :                 return TEST_SKIPPED;
     418                 :            :         }
     419                 :            : 
     420                 :          1 :         ret = rte_eth_link_get_nowait(port_id, &link);
     421         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get link status");
     422                 :            : 
     423                 :            :         /* TAP interface was brought up during setup, so link should be UP */
     424         [ -  + ]:          1 :         TEST_ASSERT(link.link_status == RTE_ETH_LINK_UP,
     425                 :            :                     "Expected link UP (TAP interface is up)");
     426         [ -  + ]:          1 :         TEST_ASSERT(link.link_speed == RTE_ETH_SPEED_NUM_10G,
     427                 :            :                     "Expected 10G link speed");
     428         [ -  + ]:          1 :         TEST_ASSERT(link.link_duplex == RTE_ETH_LINK_FULL_DUPLEX,
     429                 :            :                     "Expected full duplex");
     430                 :            : 
     431                 :            :         return TEST_SUCCESS;
     432                 :            : }
     433                 :            : 
     434                 :            : /*
     435                 :            :  * Test: Statistics initial state
     436                 :            :  */
     437                 :            : static int
     438                 :          1 : test_af_packet_stats_init(void)
     439                 :            : {
     440                 :            :         struct rte_eth_stats stats;
     441                 :            :         int ret;
     442                 :            : 
     443   [ +  -  -  + ]:          1 :         if (!tap_created || port_id >= RTE_MAX_ETHPORTS) {
     444                 :            :                 printf("SKIPPED: Port not available (need root)\n");
     445                 :          0 :                 return TEST_SKIPPED;
     446                 :            :         }
     447                 :            : 
     448                 :            :         /* Reset stats */
     449                 :          1 :         ret = rte_eth_stats_reset(port_id);
     450         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to reset stats");
     451                 :            : 
     452                 :            :         /* Get initial stats */
     453                 :          1 :         ret = rte_eth_stats_get(port_id, &stats);
     454         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get stats");
     455                 :            : 
     456                 :            :         /* After reset, stats should be zero */
     457         [ -  + ]:          1 :         TEST_ASSERT(stats.ipackets == 0,
     458                 :            :                     "Expected ipackets=0 after reset, got %"PRIu64,
     459                 :            :                     stats.ipackets);
     460         [ -  + ]:          1 :         TEST_ASSERT(stats.opackets == 0,
     461                 :            :                     "Expected opackets=0 after reset, got %"PRIu64,
     462                 :            :                     stats.opackets);
     463         [ -  + ]:          1 :         TEST_ASSERT(stats.ibytes == 0,
     464                 :            :                     "Expected ibytes=0 after reset, got %"PRIu64,
     465                 :            :                     stats.ibytes);
     466         [ -  + ]:          1 :         TEST_ASSERT(stats.obytes == 0,
     467                 :            :                     "Expected obytes=0 after reset, got %"PRIu64,
     468                 :            :                     stats.obytes);
     469                 :            : 
     470                 :            :         return TEST_SUCCESS;
     471                 :            : }
     472                 :            : 
     473                 :            : /*
     474                 :            :  * Test: TX packets (packets will be sent to TAP interface)
     475                 :            :  */
     476                 :            : static int
     477                 :          1 : test_af_packet_tx(void)
     478                 :            : {
     479                 :            :         struct rte_mbuf *bufs[BURST_SIZE];
     480                 :            :         struct rte_eth_stats stats;
     481                 :            :         uint16_t nb_tx;
     482                 :            :         unsigned int allocated;
     483                 :            :         int ret;
     484                 :            : 
     485   [ +  -  -  + ]:          1 :         if (!tap_created || port_id >= RTE_MAX_ETHPORTS) {
     486                 :            :                 printf("SKIPPED: Port not available (need root)\n");
     487                 :          0 :                 return TEST_SKIPPED;
     488                 :            :         }
     489                 :            : 
     490                 :            :         /* Reset stats */
     491                 :          1 :         ret = rte_eth_stats_reset(port_id);
     492         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to reset stats");
     493                 :            : 
     494                 :            :         /* Allocate and prepare mbufs for TX using helper */
     495                 :          1 :         allocated = alloc_tx_mbufs(bufs, BURST_SIZE);
     496         [ -  + ]:          1 :         TEST_ASSERT(allocated == BURST_SIZE, "Failed to allocate all mbufs");
     497                 :            : 
     498                 :            :         /* TX burst using helper */
     499                 :          1 :         nb_tx = do_tx_burst(port_id, 0, bufs, BURST_SIZE);
     500                 :            : 
     501                 :            :         /* Poll for stats update instead of fixed delay */
     502         [ +  - ]:          1 :         if (nb_tx > 0) {
     503                 :          1 :                 ret = wait_for_stats_update(port_id, nb_tx, STATS_POLL_TIMEOUT_US);
     504         [ +  - ]:          1 :                 if (ret == 0) {
     505                 :          1 :                         ret = rte_eth_stats_get(port_id, &stats);
     506         [ -  + ]:          1 :                         TEST_ASSERT(ret == 0, "Failed to get stats");
     507         [ -  + ]:          1 :                         TEST_ASSERT(stats.opackets > 0,
     508                 :            :                                     "Expected opackets > 0 after TX");
     509                 :            :                 }
     510                 :            :                 /* Timeout is acceptable - stats may not update immediately */
     511                 :            :         }
     512                 :            : 
     513                 :            :         return TEST_SUCCESS;
     514                 :            : }
     515                 :            : 
     516                 :            : /*
     517                 :            :  * Test: RX packets (non-blocking, may not receive anything)
     518                 :            :  */
     519                 :            : static int
     520                 :          1 : test_af_packet_rx(void)
     521                 :            : {
     522                 :            :         struct rte_mbuf *bufs[BURST_SIZE];
     523                 :            : 
     524   [ +  -  -  + ]:          1 :         if (!tap_created || port_id >= RTE_MAX_ETHPORTS) {
     525                 :            :                 printf("SKIPPED: Port not available (need root)\n");
     526                 :          0 :                 return TEST_SKIPPED;
     527                 :            :         }
     528                 :            : 
     529                 :            :         /* Try to receive packets (non-blocking) using helper */
     530                 :          1 :         do_rx_burst(port_id, 0, bufs, BURST_SIZE);
     531                 :            : 
     532                 :            :         /* RX from tap interface without external traffic will return 0 */
     533                 :            :         /* This test just verifies the RX path doesn't crash */
     534                 :            : 
     535                 :          1 :         return TEST_SUCCESS;
     536                 :            : }
     537                 :            : 
     538                 :            : /*
     539                 :            :  * Test: Loopback - TX packets and receive them back via TAP interface.
     540                 :            :  * This exercises both the TX and RX paths in a round-trip.
     541                 :            :  */
     542                 :            : static int
     543                 :          1 : test_af_packet_loopback(void)
     544                 :            : {
     545                 :            :         struct rte_mbuf *tx_bufs[BURST_SIZE];
     546                 :            :         struct rte_mbuf *rx_bufs[BURST_SIZE];
     547                 :            :         uint16_t nb_tx, total_rx = 0;
     548                 :            :         uint64_t elapsed = 0;
     549                 :            :         unsigned int allocated;
     550                 :            :         char tap_buf[2048];
     551                 :            : 
     552   [ +  -  -  + ]:          1 :         if (!tap_created || port_id >= RTE_MAX_ETHPORTS) {
     553                 :            :                 printf("SKIPPED: Port not available (need root)\n");
     554                 :          0 :                 return TEST_SKIPPED;
     555                 :            :         }
     556                 :            : 
     557                 :            :         /* Set TAP fd to non-blocking for reading */
     558                 :          1 :         int flags = fcntl(tap_fd, F_GETFL, 0);
     559                 :          1 :         fcntl(tap_fd, F_SETFL, flags | O_NONBLOCK);
     560                 :            : 
     561                 :            :         /* Allocate and send packets */
     562                 :          1 :         allocated = alloc_tx_mbufs(tx_bufs, BURST_SIZE);
     563         [ -  + ]:          1 :         if (allocated == 0) {
     564                 :            :                 printf("SKIPPED: Could not allocate mbufs\n");
     565                 :          0 :                 return TEST_SKIPPED;
     566                 :            :         }
     567                 :            : 
     568                 :          1 :         nb_tx = do_tx_burst(port_id, 0, tx_bufs, allocated);
     569         [ -  + ]:          1 :         if (nb_tx == 0) {
     570                 :            :                 printf("SKIPPED: No packets transmitted\n");
     571                 :          0 :                 return TEST_SKIPPED;
     572                 :            :         }
     573                 :            : 
     574                 :            :         /*
     575                 :            :          * Read packets from TAP interface (they were sent there by af_packet).
     576                 :            :          * Then write them back to TAP so af_packet can receive them.
     577                 :            :          */
     578         [ +  - ]:         32 :         while (elapsed < LOOPBACK_TIMEOUT_US) {
     579                 :            :                 ssize_t bytes_read, bytes_written;
     580                 :            : 
     581                 :            :                 /* Read from TAP (what af_packet sent) */
     582                 :         32 :                 bytes_read = read(tap_fd, tap_buf, sizeof(tap_buf));
     583         [ +  - ]:         32 :                 if (bytes_read > 0) {
     584                 :            :                         /* Write back to TAP for af_packet to receive */
     585                 :         32 :                         bytes_written = write(tap_fd, tap_buf, bytes_read);
     586         [ -  + ]:         32 :                         TEST_ASSERT(bytes_read == bytes_written, "TAP write %zd != %zd",
     587                 :            :                                     bytes_read, bytes_written);
     588                 :            :                 }
     589                 :            : 
     590                 :            :                 /* Try to receive via af_packet RX */
     591                 :         32 :                 uint16_t nb_rx = rte_eth_rx_burst(port_id, 0, rx_bufs, BURST_SIZE);
     592         [ +  - ]:         32 :                 if (nb_rx > 0) {
     593                 :         32 :                         total_rx += nb_rx;
     594                 :         32 :                         rte_pktmbuf_free_bulk(rx_bufs, nb_rx);
     595                 :            :                 }
     596                 :            : 
     597                 :            :                 /* Check if we've received enough */
     598         [ +  + ]:         32 :                 if (total_rx >= nb_tx)
     599                 :            :                         break;
     600                 :            : 
     601                 :         31 :                 rte_delay_us_block(STATS_POLL_INTERVAL_US);
     602                 :         31 :                 elapsed += STATS_POLL_INTERVAL_US;
     603                 :            :         }
     604                 :            : 
     605                 :            :         /* Restore TAP fd flags */
     606                 :          1 :         fcntl(tap_fd, F_SETFL, flags);
     607                 :            : 
     608                 :            :         /*
     609                 :            :          * Note: We may not receive all packets due to timing, but receiving
     610                 :            :          * any packets proves the loopback path works.
     611                 :            :          */
     612                 :          1 :         printf("Loopback: TX=%u, RX=%u\n", nb_tx, total_rx);
     613                 :            : 
     614                 :          1 :         return TEST_SUCCESS;
     615                 :            : }
     616                 :            : 
     617                 :            : /*
     618                 :            :  * Test: Promiscuous mode
     619                 :            :  */
     620                 :            : static int
     621                 :          1 : test_af_packet_promiscuous(void)
     622                 :            : {
     623                 :            :         int ret;
     624                 :            : 
     625   [ +  -  -  + ]:          1 :         if (!tap_created || port_id >= RTE_MAX_ETHPORTS) {
     626                 :            :                 printf("SKIPPED: Port not available (need root)\n");
     627                 :          0 :                 return TEST_SKIPPED;
     628                 :            :         }
     629                 :            : 
     630                 :            :         /* Enable promiscuous mode */
     631                 :          1 :         ret = rte_eth_promiscuous_enable(port_id);
     632         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to enable promiscuous mode");
     633                 :            : 
     634                 :          1 :         ret = rte_eth_promiscuous_get(port_id);
     635         [ -  + ]:          1 :         TEST_ASSERT(ret == 1, "Expected promiscuous mode enabled");
     636                 :            : 
     637                 :            :         /* Disable promiscuous mode */
     638                 :          1 :         ret = rte_eth_promiscuous_disable(port_id);
     639         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to disable promiscuous mode");
     640                 :            : 
     641                 :          1 :         ret = rte_eth_promiscuous_get(port_id);
     642         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Expected promiscuous mode disabled");
     643                 :            : 
     644                 :            :         return TEST_SUCCESS;
     645                 :            : }
     646                 :            : 
     647                 :            : /*
     648                 :            :  * Test: MAC address operations
     649                 :            :  */
     650                 :            : static int
     651                 :          1 : test_af_packet_mac_addr(void)
     652                 :            : {
     653                 :            :         struct rte_ether_addr mac_addr;
     654                 :          1 :         struct rte_ether_addr new_mac = {
     655                 :            :                 .addr_bytes = {0x02, 0x11, 0x22, 0x33, 0x44, 0x55}
     656                 :            :         };
     657                 :            :         int ret;
     658                 :            : 
     659   [ +  -  -  + ]:          1 :         if (!tap_created || port_id >= RTE_MAX_ETHPORTS) {
     660                 :            :                 printf("SKIPPED: Port not available (need root)\n");
     661                 :          0 :                 return TEST_SKIPPED;
     662                 :            :         }
     663                 :            : 
     664                 :            :         /* Get current MAC address */
     665                 :          1 :         ret = rte_eth_macaddr_get(port_id, &mac_addr);
     666         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get MAC address");
     667                 :            : 
     668                 :            :         /* Set new MAC address (use locally administered address) */
     669                 :          1 :         ret = rte_eth_dev_default_mac_addr_set(port_id, &new_mac);
     670         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to set MAC address");
     671                 :            : 
     672                 :            :         /* Verify MAC was set */
     673                 :          1 :         ret = rte_eth_macaddr_get(port_id, &mac_addr);
     674         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get MAC address after set");
     675                 :            : 
     676         [ -  + ]:          1 :         TEST_ASSERT(memcmp(&mac_addr, &new_mac, sizeof(mac_addr)) == 0,
     677                 :            :                     "MAC address mismatch after set");
     678                 :            : 
     679                 :            :         return TEST_SUCCESS;
     680                 :            : }
     681                 :            : 
     682                 :            : /*
     683                 :            :  * Test: MTU operations
     684                 :            :  * Get min/max supported MTU from device info and verify setting works.
     685                 :            :  */
     686                 :            : static int
     687                 :          1 : test_af_packet_mtu(void)
     688                 :            : {
     689                 :            :         struct rte_eth_dev_info dev_info;
     690                 :            :         uint16_t mtu, original_mtu;
     691                 :            :         int ret;
     692                 :            : 
     693   [ +  -  -  + ]:          1 :         if (!tap_created || port_id >= RTE_MAX_ETHPORTS) {
     694                 :            :                 printf("SKIPPED: Port not available (need root)\n");
     695                 :          0 :                 return TEST_SKIPPED;
     696                 :            :         }
     697                 :            : 
     698                 :            :         /* Get device info for min/max MTU */
     699                 :          1 :         ret = rte_eth_dev_info_get(port_id, &dev_info);
     700         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get device info");
     701                 :            : 
     702                 :            :         /* Get current MTU */
     703                 :          1 :         ret = rte_eth_dev_get_mtu(port_id, &original_mtu);
     704         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get MTU");
     705                 :            : 
     706                 :          1 :         printf("MTU: current=%u, min=%u, max=%u\n",
     707                 :          1 :                original_mtu, dev_info.min_mtu, dev_info.max_mtu);
     708                 :            : 
     709                 :            :         /* Test setting minimum MTU if supported and different from current */
     710   [ +  -  +  - ]:          1 :         if (dev_info.min_mtu > 0 && dev_info.min_mtu != original_mtu) {
     711                 :          1 :                 ret = rte_eth_dev_set_mtu(port_id, dev_info.min_mtu);
     712         [ -  + ]:          1 :                 if (ret == 0) {
     713                 :          0 :                         ret = rte_eth_dev_get_mtu(port_id, &mtu);
     714         [ #  # ]:          0 :                         TEST_ASSERT(ret == 0, "Failed to get MTU after set to min");
     715         [ #  # ]:          0 :                         TEST_ASSERT(mtu == dev_info.min_mtu,
     716                 :            :                                     "MTU not set to min: expected %u, got %u",
     717                 :            :                                     dev_info.min_mtu, mtu);
     718                 :            :                 }
     719                 :            :                 /* MTU set may fail depending on interface, that's acceptable */
     720                 :            :         }
     721                 :            : 
     722                 :            :         /* Test setting maximum MTU if supported and different from current */
     723   [ +  -  +  - ]:          1 :         if (dev_info.max_mtu > 0 && dev_info.max_mtu != original_mtu &&
     724         [ +  - ]:          1 :             dev_info.max_mtu != dev_info.min_mtu) {
     725                 :          1 :                 ret = rte_eth_dev_set_mtu(port_id, dev_info.max_mtu);
     726         [ +  - ]:          1 :                 if (ret == 0) {
     727                 :          1 :                         ret = rte_eth_dev_get_mtu(port_id, &mtu);
     728         [ -  + ]:          1 :                         TEST_ASSERT(ret == 0, "Failed to get MTU after set to max");
     729         [ -  + ]:          1 :                         TEST_ASSERT(mtu == dev_info.max_mtu,
     730                 :            :                                     "MTU not set to max: expected %u, got %u",
     731                 :            :                                     dev_info.max_mtu, mtu);
     732                 :            :                 }
     733                 :            :                 /* MTU set may fail depending on interface capabilities */
     734                 :            :         }
     735                 :            : 
     736                 :            :         /* Restore original MTU */
     737                 :          1 :         rte_eth_dev_set_mtu(port_id, original_mtu);
     738                 :            : 
     739                 :          1 :         return TEST_SUCCESS;
     740                 :            : }
     741                 :            : 
     742                 :            : /*
     743                 :            :  * Test: Stats reset verification
     744                 :            :  */
     745                 :            : static int
     746                 :          1 : test_af_packet_stats_reset(void)
     747                 :            : {
     748                 :            :         struct rte_eth_stats stats;
     749                 :            :         struct rte_mbuf *bufs[BURST_SIZE / 2];
     750                 :            :         uint16_t nb_tx;
     751                 :            :         unsigned int allocated;
     752                 :            :         int ret;
     753                 :            : 
     754   [ +  -  -  + ]:          1 :         if (!tap_created || port_id >= RTE_MAX_ETHPORTS) {
     755                 :            :                 printf("SKIPPED: Port not available (need root)\n");
     756                 :          0 :                 return TEST_SKIPPED;
     757                 :            :         }
     758                 :            : 
     759                 :            :         /* Generate some TX traffic using helpers */
     760                 :          1 :         allocated = alloc_tx_mbufs(bufs, BURST_SIZE / 2);
     761                 :          1 :         nb_tx = do_tx_burst(port_id, 0, bufs, allocated);
     762                 :            : 
     763                 :            :         /* Poll for stats to update */
     764         [ +  - ]:          1 :         if (nb_tx > 0)
     765                 :          1 :                 wait_for_stats_update(port_id, nb_tx, STATS_POLL_TIMEOUT_US);
     766                 :            : 
     767                 :            :         /* Reset stats */
     768                 :          1 :         ret = rte_eth_stats_reset(port_id);
     769         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to reset stats");
     770                 :            : 
     771                 :            :         /* Verify stats are zero */
     772                 :          1 :         ret = rte_eth_stats_get(port_id, &stats);
     773         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get stats after reset");
     774                 :            : 
     775         [ -  + ]:          1 :         TEST_ASSERT(stats.ipackets == 0,
     776                 :            :                     "Expected ipackets=0, got %"PRIu64, stats.ipackets);
     777         [ -  + ]:          1 :         TEST_ASSERT(stats.opackets == 0,
     778                 :            :                     "Expected opackets=0, got %"PRIu64, stats.opackets);
     779         [ -  + ]:          1 :         TEST_ASSERT(stats.ibytes == 0,
     780                 :            :                     "Expected ibytes=0, got %"PRIu64, stats.ibytes);
     781         [ -  + ]:          1 :         TEST_ASSERT(stats.obytes == 0,
     782                 :            :                     "Expected obytes=0, got %"PRIu64, stats.obytes);
     783                 :            : 
     784                 :            :         return TEST_SUCCESS;
     785                 :            : }
     786                 :            : 
     787                 :            : /*
     788                 :            :  * Test: Invalid configuration handling - missing iface
     789                 :            :  */
     790                 :            : static int
     791                 :          1 : test_af_packet_invalid_no_iface(void)
     792                 :            : {
     793                 :            :         int ret;
     794                 :            : 
     795                 :            :         /* Test without iface argument (should fail) */
     796                 :          1 :         ret = rte_vdev_init("net_af_packet_invalid1", "");
     797         [ -  + ]:          1 :         TEST_ASSERT(ret != 0, "Expected failure without iface argument");
     798                 :            : 
     799                 :            :         return TEST_SUCCESS;
     800                 :            : }
     801                 :            : 
     802                 :            : /*
     803                 :            :  * Test: Invalid configuration handling - non-existent interface
     804                 :            :  */
     805                 :            : static int
     806                 :          1 : test_af_packet_invalid_bad_iface(void)
     807                 :            : {
     808                 :            :         int ret;
     809                 :            : 
     810                 :            :         /* Test with non-existent interface (should fail) */
     811                 :          1 :         ret = rte_vdev_init("net_af_packet_invalid2",
     812                 :            :                             "iface=nonexistent_iface_12345");
     813         [ -  + ]:          1 :         TEST_ASSERT(ret != 0, "Expected failure with non-existent interface");
     814                 :            : 
     815                 :            :         return TEST_SUCCESS;
     816                 :            : }
     817                 :            : 
     818                 :            : /*
     819                 :            :  * Test: Invalid configuration handling - invalid qpairs
     820                 :            :  */
     821                 :            : static int
     822                 :          1 : test_af_packet_invalid_qpairs(void)
     823                 :            : {
     824                 :            :         int ret;
     825                 :            : 
     826         [ -  + ]:          1 :         if (!tap_created) {
     827                 :            :                 printf("SKIPPED: TAP interface not available (need root)\n");
     828                 :          0 :                 return TEST_SKIPPED;
     829                 :            :         }
     830                 :            : 
     831                 :            :         /* Test with invalid qpairs (should fail) */
     832                 :          1 :         ret = rte_vdev_init("net_af_packet_invalid3",
     833                 :            :                             "iface=" TAP_DEV_NAME ",qpairs=0");
     834         [ -  + ]:          1 :         TEST_ASSERT(ret != 0, "Expected failure with qpairs=0");
     835                 :            : 
     836                 :            :         return TEST_SUCCESS;
     837                 :            : }
     838                 :            : 
     839                 :            : /*
     840                 :            :  * Test: Custom frame size configuration
     841                 :            :  */
     842                 :            : static int
     843                 :          1 : test_af_packet_frame_config(void)
     844                 :            : {
     845                 :            :         struct rte_eth_dev_info dev_info;
     846                 :            :         uint16_t test_port;
     847                 :            :         char devargs[256];
     848                 :            :         int ret;
     849                 :            : 
     850         [ -  + ]:          1 :         if (!tap_created) {
     851                 :            :                 printf("SKIPPED: TAP interface not available (need root)\n");
     852                 :          0 :                 return TEST_SKIPPED;
     853                 :            :         }
     854                 :            : 
     855                 :            :         /* Create with custom frame parameters */
     856                 :            :         snprintf(devargs, sizeof(devargs),
     857                 :            :                  "iface=%s,blocksz=4096,framesz=2048,framecnt=256",
     858                 :            :                  TAP_DEV_NAME);
     859                 :            : 
     860                 :          1 :         ret = rte_vdev_init("net_af_packet_frame_test", devargs);
     861         [ -  + ]:          1 :         if (ret != 0) {
     862                 :            :                 printf("SKIPPED: Could not create port with custom frame config\n");
     863                 :          0 :                 return TEST_SKIPPED;
     864                 :            :         }
     865                 :            : 
     866                 :          1 :         ret = rte_eth_dev_get_port_by_name("net_af_packet_frame_test",
     867                 :            :                                            &test_port);
     868         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get port id");
     869                 :            : 
     870                 :          1 :         ret = rte_eth_dev_info_get(test_port, &dev_info);
     871         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get device info");
     872                 :            : 
     873                 :            :         /* Cleanup */
     874                 :          1 :         rte_eth_dev_close(test_port);
     875                 :          1 :         rte_vdev_uninit("net_af_packet_frame_test");
     876                 :            : 
     877                 :          1 :         return TEST_SUCCESS;
     878                 :            : }
     879                 :            : 
     880                 :            : /*
     881                 :            :  * Test: Qdisc bypass configuration
     882                 :            :  */
     883                 :            : static int
     884                 :          1 : test_af_packet_qdisc_bypass(void)
     885                 :            : {
     886                 :            :         uint16_t test_port;
     887                 :            :         char devargs[256];
     888                 :            :         int ret;
     889                 :            : 
     890         [ -  + ]:          1 :         if (!tap_created) {
     891                 :            :                 printf("SKIPPED: TAP interface not available (need root)\n");
     892                 :          0 :                 return TEST_SKIPPED;
     893                 :            :         }
     894                 :            : 
     895                 :            :         /* Create with qdisc_bypass enabled */
     896                 :            :         snprintf(devargs, sizeof(devargs), "iface=%s,qdisc_bypass=1",
     897                 :            :                  TAP_DEV_NAME);
     898                 :            : 
     899                 :          1 :         ret = rte_vdev_init("net_af_packet_qdisc_test", devargs);
     900         [ -  + ]:          1 :         if (ret != 0) {
     901                 :            :                 printf("SKIPPED: qdisc_bypass may not be supported\n");
     902                 :          0 :                 return TEST_SKIPPED;
     903                 :            :         }
     904                 :            : 
     905                 :          1 :         ret = rte_eth_dev_get_port_by_name("net_af_packet_qdisc_test",
     906                 :            :                                            &test_port);
     907         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get port id");
     908                 :            : 
     909                 :            :         /* Cleanup */
     910                 :          1 :         rte_eth_dev_close(test_port);
     911                 :          1 :         rte_vdev_uninit("net_af_packet_qdisc_test");
     912                 :            : 
     913                 :          1 :         return TEST_SUCCESS;
     914                 :            : }
     915                 :            : 
     916                 :            : /*
     917                 :            :  * Test: Multiple queue pairs
     918                 :            :  */
     919                 :            : static int
     920                 :          1 : test_af_packet_multi_queue(void)
     921                 :            : {
     922                 :            :         struct rte_eth_dev_info dev_info;
     923                 :          1 :         struct rte_eth_conf port_conf = {0};
     924                 :            :         struct rte_mbuf *bufs[BURST_SIZE];
     925                 :            :         uint16_t test_port;
     926                 :            :         char devargs[256];
     927                 :            :         const uint16_t num_queues = 2;
     928                 :            :         unsigned int allocated, q;
     929                 :            :         int ret;
     930                 :            : 
     931         [ -  + ]:          1 :         if (!tap_created) {
     932                 :            :                 printf("SKIPPED: TAP interface not available (need root)\n");
     933                 :          0 :                 return TEST_SKIPPED;
     934                 :            :         }
     935                 :            : 
     936                 :            :         snprintf(devargs, sizeof(devargs), "iface=%s,qpairs=%u",
     937                 :            :                  TAP_DEV_NAME, num_queues);
     938                 :          1 :         ret = rte_vdev_init("net_af_packet_multi_q", devargs);
     939         [ -  + ]:          1 :         if (ret != 0) {
     940                 :            :                 printf("SKIPPED: Could not create multi-queue port\n");
     941                 :          0 :                 return TEST_SKIPPED;
     942                 :            :         }
     943                 :            : 
     944                 :          1 :         ret = rte_eth_dev_get_port_by_name("net_af_packet_multi_q", &test_port);
     945         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get port id for multi-queue port");
     946                 :            : 
     947                 :          1 :         ret = rte_eth_dev_info_get(test_port, &dev_info);
     948         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get device info");
     949                 :            : 
     950         [ -  + ]:          1 :         TEST_ASSERT(dev_info.max_rx_queues >= num_queues,
     951                 :            :                     "Expected at least %u RX queues, got %u",
     952                 :            :                     num_queues, dev_info.max_rx_queues);
     953         [ -  + ]:          1 :         TEST_ASSERT(dev_info.max_tx_queues >= num_queues,
     954                 :            :                     "Expected at least %u TX queues, got %u",
     955                 :            :                     num_queues, dev_info.max_tx_queues);
     956                 :            : 
     957                 :            :         /* Configure the port */
     958                 :          1 :         ret = rte_eth_dev_configure(test_port, num_queues, num_queues, &port_conf);
     959         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to configure multi-queue port");
     960                 :            : 
     961         [ +  + ]:          3 :         for (q = 0; q < num_queues; q++) {
     962                 :          2 :                 ret = rte_eth_rx_queue_setup(test_port, q, RING_SIZE,
     963                 :          2 :                                              rte_eth_dev_socket_id(test_port),
     964                 :            :                                              NULL, mp);
     965         [ -  + ]:          2 :                 TEST_ASSERT(ret == 0, "Failed to setup RX queue %u", q);
     966                 :            : 
     967                 :          2 :                 ret = rte_eth_tx_queue_setup(test_port, q, RING_SIZE,
     968                 :          2 :                                              rte_eth_dev_socket_id(test_port),
     969                 :            :                                              NULL);
     970         [ -  + ]:          2 :                 TEST_ASSERT(ret == 0, "Failed to setup TX queue %u", q);
     971                 :            :         }
     972                 :            : 
     973                 :          1 :         ret = rte_eth_dev_start(test_port);
     974         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to start multi-queue port");
     975                 :            : 
     976                 :            :         /* Test TX on different queues using shared helpers */
     977         [ +  + ]:          3 :         for (q = 0; q < num_queues; q++) {
     978                 :          2 :                 allocated = alloc_tx_mbufs(bufs, BURST_SIZE / 2);
     979         [ +  - ]:          2 :                 if (allocated > 0)
     980                 :          2 :                         do_tx_burst(test_port, q, bufs, allocated);
     981                 :            :         }
     982                 :            : 
     983                 :            :         /* Test RX on different queues */
     984         [ +  + ]:          3 :         for (q = 0; q < num_queues; q++)
     985                 :          2 :                 do_rx_burst(test_port, q, bufs, BURST_SIZE);
     986                 :            : 
     987                 :            :         /* Cleanup */
     988                 :          1 :         rte_eth_dev_stop(test_port);
     989                 :          1 :         rte_eth_dev_close(test_port);
     990                 :          1 :         rte_vdev_uninit("net_af_packet_multi_q");
     991                 :            : 
     992                 :          1 :         return TEST_SUCCESS;
     993                 :            : }
     994                 :            : 
     995                 :            : /*
     996                 :            :  * Multi-threaded test context
     997                 :            :  */
     998                 :            : struct mt_test_ctx {
     999                 :            :         uint16_t port_id;
    1000                 :            :         RTE_ATOMIC(uint32_t) stop;
    1001                 :            :         uint32_t rx_count;
    1002                 :            : };
    1003                 :            : 
    1004                 :            : /*
    1005                 :            :  * Reader thread function for multi-threaded test
    1006                 :            :  */
    1007                 :            : static void *
    1008                 :          1 : mt_reader_thread(void *arg)
    1009                 :            : {
    1010                 :            :         struct mt_test_ctx *ctx = arg;
    1011                 :            :         struct rte_mbuf *bufs[BURST_SIZE];
    1012                 :            :         uint16_t nb_rx;
    1013                 :            : 
    1014         [ -  + ]:          1 :         while (!rte_atomic_load_explicit(&ctx->stop, rte_memory_order_relaxed)) {
    1015                 :          0 :                 nb_rx = rte_eth_rx_burst(ctx->port_id, 0, bufs, BURST_SIZE);
    1016         [ #  # ]:          0 :                 if (nb_rx > 0) {
    1017                 :          0 :                         ctx->rx_count += nb_rx;
    1018                 :          0 :                         rte_pktmbuf_free_bulk(bufs, nb_rx);
    1019                 :            :                 }
    1020                 :            : 
    1021                 :          0 :                 rte_delay_us_block(50);
    1022                 :            :         }
    1023                 :            : 
    1024                 :          1 :         return NULL;
    1025                 :            : }
    1026                 :            : 
    1027                 :            : /*
    1028                 :            :  * Test: Multi-threaded concurrent TX and RX
    1029                 :            :  * Main thread sends packets while a reader thread receives,
    1030                 :            :  * exercising concurrent access to the af_packet PMD.
    1031                 :            :  */
    1032                 :            : static int
    1033                 :          1 : test_af_packet_multithread(void)
    1034                 :            : {
    1035                 :          1 :         struct mt_test_ctx ctx = {0};
    1036                 :            :         struct rte_mbuf *bufs[BURST_SIZE];
    1037                 :            :         pthread_t reader_tid;
    1038                 :            :         struct timespec start, now;
    1039                 :            :         uint32_t tx_count = 0;
    1040                 :            :         unsigned int allocated;
    1041                 :            :         uint16_t nb_tx;
    1042                 :            :         int ret;
    1043                 :            : 
    1044   [ +  -  -  + ]:          1 :         if (!tap_created || port_id >= RTE_MAX_ETHPORTS) {
    1045                 :            :                 printf("SKIPPED: Port not available (need root)\n");
    1046                 :          0 :                 return TEST_SKIPPED;
    1047                 :            :         }
    1048                 :            : 
    1049                 :          1 :         ctx.port_id = port_id;
    1050                 :            : 
    1051                 :            :         /* Start reader thread */
    1052                 :          1 :         ret = pthread_create(&reader_tid, NULL, mt_reader_thread, &ctx);
    1053         [ -  + ]:          1 :         if (ret != 0) {
    1054                 :            :                 printf("SKIPPED: Failed to create reader thread: %d\n", ret);
    1055                 :          0 :                 return TEST_SKIPPED;
    1056                 :            :         }
    1057                 :            : 
    1058                 :            :         /* Main thread sends packets for the configured duration */
    1059                 :          1 :         clock_gettime(CLOCK_MONOTONIC, &start);
    1060                 :            :         do {
    1061                 :         13 :                 allocated = alloc_tx_mbufs(bufs, BURST_SIZE / 4);
    1062         [ +  - ]:         13 :                 if (allocated == 0)
    1063                 :            :                         break;
    1064                 :            : 
    1065                 :         13 :                 nb_tx = do_tx_burst(port_id, 0, bufs, allocated);
    1066                 :         13 :                 tx_count += nb_tx;
    1067                 :            : 
    1068                 :         13 :                 rte_delay_us_block(100);
    1069                 :         13 :                 clock_gettime(CLOCK_MONOTONIC, &now);
    1070         [ +  + ]:         13 :         } while (tx_count < MT_NUM_PACKETS &&
    1071         [ +  - ]:         12 :                 (now.tv_sec - start.tv_sec) < MT_TEST_DURATION_SEC);
    1072                 :            : 
    1073                 :            :         /* Signal reader to stop */
    1074                 :          1 :         rte_atomic_store_explicit(&ctx.stop, 1, rte_memory_order_relaxed);
    1075                 :          1 :         pthread_join(reader_tid, NULL);
    1076                 :            : 
    1077                 :          1 :         printf("Multi-threaded test: TX=%u, RX=%u (duration=%ds)\n",
    1078                 :            :                tx_count, ctx.rx_count, MT_TEST_DURATION_SEC);
    1079                 :            : 
    1080         [ -  + ]:          1 :         TEST_ASSERT(tx_count > 0,
    1081                 :            :                     "Expected some packets to be transmitted");
    1082                 :            : 
    1083                 :            :         return TEST_SUCCESS;
    1084                 :            : }
    1085                 :            : 
    1086                 :            : static struct unit_test_suite af_packet_test_suite = {
    1087                 :            :         .suite_name = "AF_PACKET PMD Unit Test Suite",
    1088                 :            :         .setup = test_af_packet_setup,
    1089                 :            :         .teardown = test_af_packet_teardown,
    1090                 :            :         .unit_test_cases = {
    1091                 :            :                 /* Tests that don't modify device state */
    1092                 :            :                 TEST_CASE(test_af_packet_dev_info),
    1093                 :            :                 TEST_CASE(test_af_packet_link_status),
    1094                 :            :                 TEST_CASE(test_af_packet_stats_init),
    1095                 :            :                 TEST_CASE(test_af_packet_tx),
    1096                 :            :                 TEST_CASE(test_af_packet_rx),
    1097                 :            :                 TEST_CASE(test_af_packet_loopback),
    1098                 :            :                 TEST_CASE(test_af_packet_promiscuous),
    1099                 :            :                 TEST_CASE(test_af_packet_mac_addr),
    1100                 :            :                 TEST_CASE(test_af_packet_mtu),
    1101                 :            :                 TEST_CASE(test_af_packet_stats_reset),
    1102                 :            :                 TEST_CASE(test_af_packet_multithread),
    1103                 :            : 
    1104                 :            :                 /* Tests that create their own devices */
    1105                 :            :                 TEST_CASE(test_af_packet_invalid_no_iface),
    1106                 :            :                 TEST_CASE(test_af_packet_invalid_bad_iface),
    1107                 :            :                 TEST_CASE(test_af_packet_invalid_qpairs),
    1108                 :            :                 TEST_CASE(test_af_packet_frame_config),
    1109                 :            :                 TEST_CASE(test_af_packet_qdisc_bypass),
    1110                 :            :                 TEST_CASE(test_af_packet_multi_queue),
    1111                 :            : 
    1112                 :            :                 TEST_CASES_END() /**< NULL terminate unit test array */
    1113                 :            :         }
    1114                 :            : };
    1115                 :            : 
    1116                 :            : static int
    1117                 :          1 : test_pmd_af_packet(void)
    1118                 :            : {
    1119                 :          1 :         return unit_test_suite_runner(&af_packet_test_suite);
    1120                 :            : }
    1121                 :            : #endif
    1122                 :            : 
    1123                 :        286 : REGISTER_FAST_TEST(af_packet_pmd_autotest, NOHUGE_OK, ASAN_OK, test_pmd_af_packet);

Generated by: LCOV version 1.14