* [PATCH net v1 1/2] net: openvswitch: do not record user features when the change fails
[not found] <20261107113403.797779-1-houminxi@gmail.com>
@ 2026-11-07 11:34 ` Minxi Hou
0 siblings, 0 replies; only message in thread
From: Minxi Hou @ 2026-11-07 11:34 UTC (permalink / raw)
To: netdev
Cc: Aaron Conole, Eelco Chaudron, Ilya Maximets, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Shuah Khan, Jamal Hadi Salim, Paul Blakey, dev, linux-kselftest,
stable
ovs_dp_change() stores the new features in dp->user_features before it
updates the per-cpu upcall port IDs, and that update fails when the
per-cpu-pids attribute is empty or its length is not a multiple of four.
The request returns -EINVAL with the new features already recorded.
The features decide whether the tc skb extension static key is enabled,
but the code that enables or disables the key runs after the failing
call, so it does not run for the failed request. dp->user_features then
claims TC_RECIRC_SHARING is set while tc_skb_ext_tc_enable() was not
called, and a later request that clears the bit calls
tc_skb_ext_tc_disable() for an enable that never happened. That key is a
reference count shared with cls_flower, so the count ends up one short
and reaches zero while other users still hold a reference. Recirc
sharing in every datapath goes off with it, as does the skb extension
allocation in tc, in the flow dissector and in the bridge. The opposite
ordering is possible as well: a failing request that drops the bit
leaves the key enabled for good.
Keep the new features in the local variable until the request has gone
through, and use that variable for the enable and disable decisions. A
failed request then leaves both the features and the key alone.
Two other paths touch the bit without keeping the key in step:
ovs_dp_reset_user_features() clears it without disabling the key, and a
new datapath that fails after ovs_dp_change() succeeded is freed
without disabling it. Both predate this change and are left alone here.
The selftest in the next patch sends a set request that fails on an
empty per-cpu-pids and reads the features back; it fails without this
change and passes with it. The openvswitch selftest suite passes with
the change applied.
Fixes: 35d39fecbc24 ("net/sched: Enable tc skb ext allocation on chain miss only when needed")
Cc: stable@vger.kernel.org
Signed-off-by: Minxi Hou <houminxi@gmail.com>
---
net/openvswitch/datapath.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index b2a0b09fd4a5..00043aef2d03 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -1801,9 +1801,7 @@ static int ovs_dp_change(struct datapath *dp, struct nlattr *a[])
return err;
}
- dp->user_features = user_features;
-
- if (dp->user_features & OVS_DP_F_DISPATCH_UPCALL_PER_CPU &&
+ if (user_features & OVS_DP_F_DISPATCH_UPCALL_PER_CPU &&
a[OVS_DP_ATTR_PER_CPU_PIDS]) {
/* Upcall Netlink Port IDs have been updated */
err = ovs_dp_set_upcall_portids(dp,
@@ -1812,10 +1810,12 @@ static int ovs_dp_change(struct datapath *dp, struct nlattr *a[])
return err;
}
- if ((dp->user_features & OVS_DP_F_TC_RECIRC_SHARING) &&
+ dp->user_features = user_features;
+
+ if ((user_features & OVS_DP_F_TC_RECIRC_SHARING) &&
!(old_features & OVS_DP_F_TC_RECIRC_SHARING))
tc_skb_ext_tc_enable();
- else if (!(dp->user_features & OVS_DP_F_TC_RECIRC_SHARING) &&
+ else if (!(user_features & OVS_DP_F_TC_RECIRC_SHARING) &&
(old_features & OVS_DP_F_TC_RECIRC_SHARING))
tc_skb_ext_tc_disable();
--
2.56.0
^ permalink raw reply related [flat|nested] only message in thread