When the PF negotiates TX PAUSE on the port it signals this to the VF
via BIT(1) of the PF-to-VF link status mailbox message. The VF PMD must
respond by setting RBMR_CM (BIT(4)) on all active RX rings so the MAC
emits PAUSE frames on ingress pressure.

Add ENETC_RBMR_CM register definition, ENETC_LINK_TX_PAUSE bitmask,
and tx_pause_active state flag. Add enetc4_vf_set_congestion_mode() to
update all active RX rings and persist the state for rings started
later. Hook it into both the interrupt and poll link-update paths, and
apply the saved state in rx_queue_setup() and rx_queue_start().

RX PAUSE (honoring received PAUSE frames) is handled at the MAC level
by the PF and requires no VF PMD changes.

Signed-off-by: Gagandeep Singh <[email protected]>
---
 doc/guides/nics/features/enetc4.ini    |  1 +
 doc/guides/rel_notes/release_26_11.rst |  1 +
 drivers/net/enetc/base/enetc_hw.h      |  1 +
 drivers/net/enetc/enetc.h              |  9 +++-
 drivers/net/enetc/enetc4_ethdev.c      | 13 +++++-
 drivers/net/enetc/enetc4_vf.c          | 59 ++++++++++++++++++++++++--
 6 files changed, 78 insertions(+), 6 deletions(-)

diff --git a/doc/guides/nics/features/enetc4.ini 
b/doc/guides/nics/features/enetc4.ini
index 01b0dc5b80..1f599dace7 100644
--- a/doc/guides/nics/features/enetc4.ini
+++ b/doc/guides/nics/features/enetc4.ini
@@ -14,6 +14,7 @@ Promiscuous mode     = Y
 Allmulticast mode    = Y
 Unicast MAC filter   = Y
 VLAN filter          = Y
+Flow control         = Y
 VLAN offload         = Y
 RSS hash             = Y
 Packet type parsing  = Y
diff --git a/doc/guides/rel_notes/release_26_11.rst 
b/doc/guides/rel_notes/release_26_11.rst
index 5323f4fea3..d69888aafb 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -72,6 +72,7 @@ New Features
   * Added per-queue MSI-X Rx interrupt support for the ENETC4 VF.
   * Added SI-based port VLAN insertion (Tx) and removal (Rx) for ENETC4 PF and 
VF.
   * Updated ENETC4 VF link status reporting to use bitmask encoding.
+  * Added TX PAUSE support for the ENETC4 VF via RX congestion mode.
 
 Removed Items
 -------------
diff --git a/drivers/net/enetc/base/enetc_hw.h 
b/drivers/net/enetc/base/enetc_hw.h
index 6e96562850..33d075fe59 100644
--- a/drivers/net/enetc/base/enetc_hw.h
+++ b/drivers/net/enetc/base/enetc_hw.h
@@ -51,6 +51,7 @@ enum enetc_bdr_type {TX, RX};
                                                        + (off))
 /* RX BDR reg offsets */
 #define ENETC_RBMR             0x0 /* RX BDR mode register*/
+#define ENETC_RBMR_CM          BIT(4)  /* congestion mode: assert congestion 
to emit TX PAUSE */
 #define ENETC_RBMR_EN          BIT(31)
 
 #define ENETC_BMR_RESET                0x0 /* BDR reset*/
diff --git a/drivers/net/enetc/enetc.h b/drivers/net/enetc/enetc.h
index 40bad56341..8aaf8a7eb1 100644
--- a/drivers/net/enetc/enetc.h
+++ b/drivers/net/enetc/enetc.h
@@ -140,6 +140,10 @@ struct enetc_eth_hw {
         * for PF kernel versions before 6.18.37. Set via vf_link_legacy devarg.
         */
        uint8_t vf_link_legacy;
+       /* 1 = TX PAUSE negotiated on port; VF RX rings must have RBMR_CM set.
+        * Updated from the PF-to-VF link status mailbox message (BIT(1)).
+        */
+       uint8_t tx_pause_active;
        /* Baseline snapshot for VF stats reset (software delta approach). */
        struct enetc4_vf_stats_saved vf_stats_saved;
 };
@@ -238,8 +242,11 @@ enum vlan_status {
 
 /* Link status bitmask in PF-to-VF mailbox notification.
  * Link up is encoded as the DOWN bit being clear.
+ * TX_PAUSE is set when the port has negotiated TX PAUSE; VF must enable
+ * congestion mode (ENETC_RBMR_CM) on its RX rings accordingly.
  */
-#define ENETC_LINK_DOWN  (1u << 0)
+#define ENETC_LINK_DOWN      (1u << 0)
+#define ENETC_LINK_TX_PAUSE  (1u << 1)
 
 enum speed {
        ENETC_SPEED_UNKNOWN = 0x0,
diff --git a/drivers/net/enetc/enetc4_ethdev.c 
b/drivers/net/enetc/enetc4_ethdev.c
index ae40cc69e0..2355522515 100644
--- a/drivers/net/enetc/enetc4_ethdev.c
+++ b/drivers/net/enetc/enetc4_ethdev.c
@@ -712,8 +712,12 @@ enetc4_rx_queue_setup(struct rte_eth_dev *dev,
        }
 
        if (!rx_conf->rx_deferred_start) {
-               /* enable ring */
+               /* Enable ring; apply congestion mode if TX PAUSE is already 
active. */
                rx_enable |= ENETC_RBMR_EN;
+               if (adapter->hw.tx_pause_active)
+                       rx_enable |= ENETC_RBMR_CM;
+               else
+                       rx_enable &= ~(uint32_t)ENETC_RBMR_CM;
                enetc4_rxbdr_wr(&adapter->hw.hw, rx_ring->index, ENETC_RBMR,
                               rx_enable);
                dev->data->rx_queue_state[rx_ring->index] =
@@ -1089,7 +1093,12 @@ enetc4_rx_queue_start(struct rte_eth_dev *dev, uint16_t 
qidx)
        if (dev->data->rx_queue_state[qidx] == RTE_ETH_QUEUE_STATE_STOPPED) {
                rx_data = enetc4_rxbdr_rd(&priv->hw.hw, rx_ring->index,
                                         ENETC_RBMR);
-               rx_data = rx_data | ENETC_RBMR_EN;
+               rx_data |= ENETC_RBMR_EN;
+               /* Restore congestion mode if TX PAUSE is active. */
+               if (priv->hw.tx_pause_active)
+                       rx_data |= ENETC_RBMR_CM;
+               else
+                       rx_data &= ~(uint32_t)ENETC_RBMR_CM;
                enetc4_rxbdr_wr(&priv->hw.hw, rx_ring->index, ENETC_RBMR,
                               rx_data);
                dev->data->rx_queue_state[qidx] = RTE_ETH_QUEUE_STATE_STARTED;
diff --git a/drivers/net/enetc/enetc4_vf.c b/drivers/net/enetc/enetc4_vf.c
index 1b93835dd7..c68bc8521b 100644
--- a/drivers/net/enetc/enetc4_vf.c
+++ b/drivers/net/enetc/enetc4_vf.c
@@ -461,6 +461,37 @@ enetc4_decode_link_speed(uint8_t status, bool 
vf_link_legacy,
        }
 }
 
+/*
+ * Set or clear ENETC_RBMR_CM (congestion mode) on all active VF RX rings.
+ * When set, the ring signals congestion to the MAC, causing it to emit TX
+ * PAUSE frames on ingress pressure. hw->tx_pause_active is updated so rings
+ * started later inherit the correct state.
+ */
+static void
+enetc4_vf_set_congestion_mode(struct rte_eth_dev *eth_dev, bool enable)
+{
+       struct enetc_eth_hw *hw =
+               ENETC_DEV_PRIVATE_TO_HW(eth_dev->data->dev_private);
+       struct enetc_hw *enetc_hw = &hw->hw;
+       uint16_t nb_rx = eth_dev->data->nb_rx_queues;
+       uint16_t i;
+       uint32_t rbmr;
+
+       hw->tx_pause_active = enable ? 1 : 0;
+
+       for (i = 0; i < nb_rx; i++) {
+               rbmr = enetc4_rxbdr_rd(enetc_hw, i, ENETC_RBMR);
+               if (enable)
+                       rbmr |= ENETC_RBMR_CM;
+               else
+                       rbmr &= ~(uint32_t)ENETC_RBMR_CM;
+               enetc4_rxbdr_wr(enetc_hw, i, ENETC_RBMR, rbmr);
+       }
+
+       ENETC_PMD_DEBUG("VF congestion mode %s on %u RX rings",
+                       enable ? "enabled" : "disabled", nb_rx);
+}
+
 static void
 enetc4_process_psi_msg(struct rte_eth_dev *eth_dev, struct enetc_hw *enetc_hw)
 {
@@ -468,6 +499,7 @@ enetc4_process_psi_msg(struct rte_eth_dev *eth_dev, struct 
enetc_hw *enetc_hw)
                ENETC_DEV_PRIVATE_TO_HW(eth_dev->data->dev_private);
        struct enetc_psi_reply_msg *msg;
        struct rte_eth_link link;
+       bool tx_pause;
        int ret = 0;
 
        msg = rte_zmalloc(NULL, sizeof(*msg), RTE_CACHE_LINE_SIZE);
@@ -483,9 +515,24 @@ enetc4_process_psi_msg(struct rte_eth_dev *eth_dev, struct 
enetc_hw *enetc_hw)
                if (msg->status & ENETC_LINK_DOWN) {
                        ENETC_PMD_DEBUG("Link is down");
                        link.link_status = RTE_ETH_LINK_DOWN;
+                       /* Clear congestion mode on link-down so VF rings do not
+                        * assert congestion while the port is offline.
+                        */
+                       enetc4_vf_set_congestion_mode(eth_dev, false);
                } else {
-                       ENETC_PMD_DEBUG("Link is up");
+                       /* BIT(1) is set when the port has negotiated TX PAUSE.
+                        * Legacy PF does not set this bit so tx_pause stays 
false.
+                        */
+                       tx_pause = !!(msg->status & ENETC_LINK_TX_PAUSE);
+                       ENETC_PMD_DEBUG("Link is up, tx_pause=%d", tx_pause);
                        link.link_status = RTE_ETH_LINK_UP;
+
+                       /* Apply congestion mode before raising the carrier so
+                        * the VF rings are ready to emit PAUSE before traffic
+                        * starts flowing.
+                        */
+                       enetc4_vf_set_congestion_mode(eth_dev, tx_pause);
+
                        /* Re-query speed from PF so the cached value reflects
                         * the current negotiated speed after link-up.
                         */
@@ -1174,10 +1221,16 @@ enetc4_vf_link_update(struct rte_eth_dev *dev, int 
wait_to_complete __rte_unused
        }
 
        if (reply_msg->class_id == ENETC_CLASS_ID_LINK_STATUS) {
-               if (reply_msg->status & ENETC_LINK_DOWN)
+               if (reply_msg->status & ENETC_LINK_DOWN) {
                        link.link_status = RTE_ETH_LINK_DOWN;
-               else
+                       /* Link is down: disable congestion mode on all RX 
rings. */
+                       enetc4_vf_set_congestion_mode(dev, false);
+               } else {
                        link.link_status = RTE_ETH_LINK_UP;
+                       /* Restore congestion mode from the TX PAUSE bit. */
+                       enetc4_vf_set_congestion_mode(dev,
+                               !!(reply_msg->status & ENETC_LINK_TX_PAUSE));
+               }
        } else {
                ENETC_PMD_ERR("Wrong reply message");
                return -1;
-- 
2.25.1

Reply via email to