Add an "eof" devarg for rx_pcap mode that signals end-of-file by
setting link down and generating an LSC event. This allows
applications to detect when a pcap file has been fully consumed
using the standard ethdev callback mechanism.

The eof and infinite_rx options are mutually exclusive. On device
restart, the EOF state is reset so the file can be replayed.

Signed-off-by: Stephen Hemminger <[email protected]>
---
 doc/guides/nics/pcap.rst               | 12 +++++
 doc/guides/rel_notes/release_26_03.rst |  2 +
 drivers/net/pcap/pcap_ethdev.c         | 75 ++++++++++++++++++++++++--
 3 files changed, 86 insertions(+), 3 deletions(-)

diff --git a/doc/guides/nics/pcap.rst b/doc/guides/nics/pcap.rst
index f241069ebb..6f3a4fc887 100644
--- a/doc/guides/nics/pcap.rst
+++ b/doc/guides/nics/pcap.rst
@@ -144,6 +144,18 @@ Runtime Config Options
   so all queues on a device will either have this enabled or disabled.
   This option should only be provided once per device.
 
+* Signal end-of-file via link status change
+
+  In case ``rx_pcap=`` configuration is set, the user may want to be notified 
when
+  all packets in the pcap file have been read.  This can be done with the 
``eof``
+  devarg, for example::
+
+     --vdev 'net_pcap0,rx_pcap=file_rx.pcap,eof=1'
+
+  When enabled, the driver sets link down and generates an LSC event at end of 
file.
+  If the device is stopped and restarted, the EOF state is reset.
+  This option cannot be combined with ``infinite_rx``.
+
 * Drop all packets on transmit
 
   To drop all packets on transmit for a device,
diff --git a/doc/guides/rel_notes/release_26_03.rst 
b/doc/guides/rel_notes/release_26_03.rst
index e614d8a8ac..acd6450a59 100644
--- a/doc/guides/rel_notes/release_26_03.rst
+++ b/doc/guides/rel_notes/release_26_03.rst
@@ -90,6 +90,8 @@ New Features
   * Receive timestamps support nanosecond precision.
   * Added ``snaplen`` devarg to configure packet capture snapshot length.
   * Added support for Link State interrupt in ``iface`` mode.
+  * Added ``eof`` devarg to use link state to signal end of receive
+    file input.
 
 
 Removed Items
diff --git a/drivers/net/pcap/pcap_ethdev.c b/drivers/net/pcap/pcap_ethdev.c
index afb68998e7..8b091af6ee 100644
--- a/drivers/net/pcap/pcap_ethdev.c
+++ b/drivers/net/pcap/pcap_ethdev.c
@@ -42,6 +42,7 @@
 #define ETH_PCAP_IFACE_ARG    "iface"
 #define ETH_PCAP_PHY_MAC_ARG  "phy_mac"
 #define ETH_PCAP_INFINITE_RX_ARG  "infinite_rx"
+#define ETH_PCAP_EOF_ARG          "eof"
 #define ETH_PCAP_SNAPSHOT_LEN_ARG "snaplen"
 
 #define ETH_PCAP_SNAPSHOT_LEN_DEFAULT 65535
@@ -114,6 +115,8 @@ struct pmd_internals {
        bool single_iface;
        bool phy_mac;
        bool infinite_rx;
+       bool eof;
+       bool eof_signaled;
        bool vlan_strip;
        bool timestamp_offloading;
        bool lsc_active;
@@ -146,6 +149,7 @@ struct pmd_devargs_all {
        bool is_rx_pcap;
        bool is_rx_iface;
        bool infinite_rx;
+       bool eof;
 };
 
 static const char *valid_arguments[] = {
@@ -157,6 +161,7 @@ static const char *valid_arguments[] = {
        ETH_PCAP_IFACE_ARG,
        ETH_PCAP_PHY_MAC_ARG,
        ETH_PCAP_INFINITE_RX_ARG,
+       ETH_PCAP_EOF_ARG,
        ETH_PCAP_SNAPSHOT_LEN_ARG,
        NULL
 };
@@ -306,15 +311,33 @@ eth_pcap_rx_infinite(void *queue, struct rte_mbuf **bufs, 
uint16_t nb_pkts)
        return i;
 }
 
+/*
+ * Deferred EOF alarm callback.
+ *
+ * Scheduled from the RX burst path when end-of-file is reached,
+ * so that rte_eth_dev_callback_process() runs outside the datapath.
+ * This avoids holding any locks that the application callback
+ * might also need, preventing potential deadlocks.
+ */
+static void
+eth_pcap_eof_alarm(void *arg)
+{
+       struct rte_eth_dev *dev = arg;
+
+       rte_eth_dev_callback_process(dev, RTE_ETH_EVENT_INTR_LSC, NULL);
+}
+
 static uint16_t
 eth_pcap_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
 {
+       struct pcap_rx_queue *pcap_q = queue;
+       struct rte_eth_dev *dev = &rte_eth_devices[pcap_q->port_id];
+       struct pmd_internals *internals = dev->data->dev_private;
        unsigned int i;
        struct pcap_pkthdr *header;
        struct pmd_process_private *pp;
        const u_char *packet;
        struct rte_mbuf *mbuf;
-       struct pcap_rx_queue *pcap_q = queue;
        uint16_t num_rx = 0;
        uint32_t rx_bytes = 0;
        pcap_t *pcap;
@@ -335,6 +358,19 @@ eth_pcap_rx(void *queue, struct rte_mbuf **bufs, uint16_t 
nb_pkts)
                        if (ret == PCAP_ERROR)
                                pcap_q->rx_stat.err_pkts++;
 
+                       /*
+                        * EOF: if eof mode is enabled, set link down and
+                        * defer notification via alarm to avoid calling
+                        * rte_eth_dev_callback_process() from the datapath.
+                        */
+                       else if (ret == PCAP_ERROR_BREAK) {
+                               if (internals->eof && !internals->eof_signaled) 
{
+                                       internals->eof_signaled = true;
+                                       dev->data->dev_link.link_status = 
RTE_ETH_LINK_DOWN;
+                                       rte_eal_alarm_set(1, 
eth_pcap_eof_alarm, dev);
+                               }
+                       }
+
                        break;
                }
 
@@ -875,6 +911,7 @@ eth_dev_start(struct rte_eth_dev *dev)
        for (i = 0; i < dev->data->nb_tx_queues; i++)
                dev->data->tx_queue_state[i] = RTE_ETH_QUEUE_STATE_STARTED;
 
+       internals->eof_signaled = false;
        dev->data->dev_link.link_status = RTE_ETH_LINK_UP;
 
        /* Start LSC polling for iface mode if application requested it */
@@ -936,6 +973,10 @@ eth_dev_stop(struct rte_eth_dev *dev)
        }
 
 status_down:
+       /* Cancel any pending EOF alarm */
+       if (internals->eof_signaled)
+               rte_eal_alarm_cancel(eth_pcap_eof_alarm, dev);
+
        for (i = 0; i < dev->data->nb_rx_queues; i++)
                dev->data->rx_queue_state[i] = RTE_ETH_QUEUE_STATE_STOPPED;
 
@@ -1173,9 +1214,13 @@ eth_link_update(struct rte_eth_dev *dev, int 
wait_to_complete __rte_unused)
                 */
                link.link_speed = RTE_ETH_SPEED_NUM_10G;
                link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
-               link.link_status = dev->data->dev_started ?
-                       RTE_ETH_LINK_UP : RTE_ETH_LINK_DOWN;
                link.link_autoneg = RTE_ETH_LINK_FIXED;
+
+               if (internals->eof_signaled)
+                       link.link_status = RTE_ETH_LINK_DOWN;
+               else
+                       link.link_status = dev->data->dev_started ?
+                               RTE_ETH_LINK_UP : RTE_ETH_LINK_DOWN;
        }
 
        return rte_eth_linkstatus_set(dev, &link);
@@ -1755,8 +1800,13 @@ eth_from_pcaps(struct rte_vdev_device *vdev,
        }
 
        internals->infinite_rx = infinite_rx;
+       internals->eof = devargs_all->eof;
        internals->snapshot_len = devargs_all->snapshot_len;
 
+       /* Enable LSC for eof mode (already set above for single_iface) */
+       if (internals->eof)
+               eth_dev->data->dev_flags |= RTE_ETH_DEV_INTR_LSC;
+
        /* Assign rx ops. */
        if (infinite_rx)
                eth_dev->rx_pkt_burst = eth_pcap_rx_infinite;
@@ -1943,6 +1993,24 @@ pmd_pcap_probe(struct rte_vdev_device *dev)
                                        "for %s", name);
                }
 
+               /*
+                * Check whether to signal EOF via link status change.
+                */
+               if (rte_kvargs_count(kvlist, ETH_PCAP_EOF_ARG) == 1) {
+                       ret = rte_kvargs_process(kvlist, ETH_PCAP_EOF_ARG,
+                                                &process_bool_flag,
+                                                &devargs_all.eof);
+                       if (ret < 0)
+                               goto free_kvlist;
+               }
+
+               if (devargs_all.infinite_rx && devargs_all.eof) {
+                       PMD_LOG(ERR, "Cannot use both infinite_rx and eof for 
%s",
+                               name);
+                       ret = -EINVAL;
+                       goto free_kvlist;
+               }
+
                ret = rte_kvargs_process(kvlist, ETH_PCAP_RX_PCAP_ARG,
                                &open_rx_pcap, &pcaps);
        } else if (devargs_all.is_rx_iface) {
@@ -2082,4 +2150,5 @@ RTE_PMD_REGISTER_PARAM_STRING(net_pcap,
        ETH_PCAP_IFACE_ARG "=<ifc> "
        ETH_PCAP_PHY_MAC_ARG "=<0|1> "
        ETH_PCAP_INFINITE_RX_ARG "=<0|1> "
+       ETH_PCAP_EOF_ARG "=<0|1> "
        ETH_PCAP_SNAPSHOT_LEN_ARG "=<int>");
-- 
2.51.0

Reply via email to