From: Numan Siddique <[email protected]>

The netdev-offload-tc module uses a flow's recirc_id (a uint32_t)
as the TC flower chain ID during offloading.  However, the kernel's
TC implementation reserves the upper 4 bits of the chain index for
extended action opcodes.  If a recirc_id exceeds (2^28 - 1), the
kernel rejects the flow offload with EINVAL.  This patch caps the
maximum recirc_id to (2^28 - 1) to prevent these failures.

This limit is sufficiently high that it is unlikely to impact the
number of active datapath flows in practice.

Reported-at: 
https://mail.openvswitch.org/pipermail/ovs-dev/2025-November/427485.html
Suggested-by: Ilya Maximets <[email protected]>
Suggested-by: Eelco Chaudron <[email protected]>
Signed-off-by: Numan Siddique <[email protected]>
---

v2 -> v3
-------
  - Addressed review comments from v2.

 ofproto/ofproto-dpif-rid.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/ofproto/ofproto-dpif-rid.c b/ofproto/ofproto-dpif-rid.c
index f01468025..1f9746f93 100644
--- a/ofproto/ofproto-dpif-rid.c
+++ b/ofproto/ofproto-dpif-rid.c
@@ -38,6 +38,11 @@ static uint32_t next_id OVS_GUARDED_BY(mutex) = 1; /* 
Possible next free id. */
 
 #define RECIRC_POOL_STATIC_IDS 1024
 
+/* Limit recirc_id to 28 bits for TC compatibility.  recirc_id is used
+ * as chain id in TC offload and kernel's TC implementation reserves
+ * the upper 4 bits of the chain index for extended action opcodes. */
+#define RECIRC_ID_MAX_VALUE ((UINT32_C(1) << 28) - 1)
+
 static void recirc_id_node_free(struct recirc_id_node *);
 
 /* This should be called by the revalidator once at each round (every 500ms or
@@ -227,8 +232,8 @@ frozen_state_free(struct frozen_state *state)
 }
 
 /* Allocate a unique recirculation id for the given set of flow metadata.
- * The ID space is 2^^32, so there should never be a situation in which all
- * the IDs are used up.  We loop until we find a free one. */
+ * The ID space is limited to (2^28 - 1), so there should never be a situation
+ * in which all the IDs are used up.  We loop until we find a free one. */
 static struct recirc_id_node *
 recirc_alloc_id__(const struct frozen_state *state, uint32_t hash)
 {
@@ -247,7 +252,7 @@ recirc_alloc_id__(const struct frozen_state *state, 
uint32_t hash)
            RECIRC_POOL_STATIC_IDS IDs on the later rounds, though, as some of
            the initial allocations may be for long term uses (like bonds). */
         node->id = next_id++;
-        if (OVS_UNLIKELY(!node->id)) {
+        if (OVS_UNLIKELY(!node->id || node->id > RECIRC_ID_MAX_VALUE)) {
             next_id = RECIRC_POOL_STATIC_IDS + 1;
             node->id = next_id++;
         }
-- 
2.52.0

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to