[PATCH iwl-net 2/6] ice: take rtnl before tc_mutex in DCB MIB change and rebuild

From
Przemek Kitszel <przemyslaw.kitszel@intel.com>
Date
2026-11-02 10:46:53
Also in
intel-wired-lan
Reply to
Przemek Kitszel, 2026-11-02
ice_setup_tc() and dcbnl ops are called with rtnl held and then take
pf->tc_mutex. LLDP MIB change handler and ice_dcb_rebuild() (the latter
only on its error path, via ice_pf_dcb_cfg(..., false)) do the opposite:
take pf->tc_mutex and then rtnl. The MIB change handler runs from the
service task. ice_dcb_rebuild() runs from ice_rebuild() (reset, also PCI
AER resume), and from ice_link_event() in the service task, on a link
change while DCB is enabled. So a MIB change from the link partner, a reset
or a link change, concurrent with "tc qdisc add ... mqprio" or "dcb ets
set" may deadlock.

Take rtnl first in both functions. ice_dcb_rebuild() now holds rtnl also
on its success path, which only sends a few AQ commands. None of its
callers hold rtnl.

Lockdep splat, on the first MIB change that needs a DCB reconfiguration
after "dcb ets set" was used:

        CPU0                    CPU1
        ----                    ----
   lock(&pf->tc_mutex);
                                lock(rtnl_mutex);
                                lock(&pf->tc_mutex);
   lock(rtnl_mutex);

Reproducer, with the FW LLDP agent in its default (willing) mode:
  PF=eth2
  ethtool --set-priv-flags $PF fw-lldp-agent off
  dcb ets set dev $PF tc-tsa all:ets tc-bw all:0 0:100 prio-tc all:0
  ethtool --set-priv-flags $PF fw-lldp-agent on
and then, on the link partner, send an ETS recommendation with a different
UP2TC mapping (python3 lldp-ets.py <partner-ifname>):
  import socket, struct, sys
  ifc = sys.argv[1]
  mac = open(f"/sys/class/net/{ifc}/address").read().strip()
  mac = bytes.fromhex(mac.replace(":", ""))
  tlv = lambda t, v: struct.pack("!H", t << 9 | len(v)) + v
  # not willing, prio 4 -> TC1, TC0/TC1 ETS 50/50
  ets = bytes([0, 0, 0, 0x10, 0, 50, 50] + [0] * 6 + [2, 2] + [0] * 6)
  lldp = (tlv(1, b"\x04" + mac) + tlv(2, b"\x03" + mac) +
          tlv(3, b"\x00\x78") +
          tlv(127, b"\x00\x80\xc2\x09" + ets) +  # ETS Configuration
          tlv(127, b"\x00\x80\xc2\x0a" + ets) +  # ETS Recommendation
          tlv(0, b""))
  s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW)
  s.bind((ifc, 0))
  s.send(bytes.fromhex("0180c200000e") + mac + b"\x88\xcc" + lldp)

Fixes: 242b5e068b25 ("ice: Fix DCB rebuild after reset")
Signed-off-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
---
 drivers/net/ethernet/intel/ice/ice_dcb_lib.c | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c
index 0bc6dd375687..3034f461f513 100644
--- a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c
@@ -537,6 +537,7 @@ void ice_dcb_rebuild(struct ice_pf *pf)
 	struct ice_dcbx_cfg *err_cfg;
 	int ret;
 
+	rtnl_lock();
 	mutex_lock(&pf->tc_mutex);
 
 	ret = ice_query_port_ets(pf->hw.port_info, &buf, sizeof(buf), NULL);
@@ -570,14 +571,16 @@ void ice_dcb_rebuild(struct ice_pf *pf)
 	}
 
 	mutex_unlock(&pf->tc_mutex);
+	rtnl_unlock();
 
 	return;
 
 dcb_error:
 	dev_err(dev, "Disabling DCB until new settings occur\n");
 	err_cfg = kzalloc_obj(*err_cfg);
 	if (!err_cfg) {
 		mutex_unlock(&pf->tc_mutex);
+		rtnl_unlock();
 		return;
 	}
 
@@ -591,10 +594,11 @@ void ice_dcb_rebuild(struct ice_pf *pf)
 	 * Suppress the Coverity warning with the following comment...
 	 */
 	/* coverity[check_return] */
-	ice_pf_dcb_cfg(pf, err_cfg, false);
+	ice_pf_dcb_cfg(pf, err_cfg, true);
 	kfree(err_cfg);
 
 	mutex_unlock(&pf->tc_mutex);
+	rtnl_unlock();
 }
 
 /**
@@ -1055,6 +1059,7 @@ ice_dcb_process_lldp_set_mib_change(struct ice_pf *pf,
 	}
 
 	/* That a DCB change has happened is now determined */
+	rtnl_lock();
 	mutex_lock(&pf->tc_mutex);
 
 	/* store the old configuration */
@@ -1105,25 +1110,23 @@ ice_dcb_process_lldp_set_mib_change(struct ice_pf *pf,
 		pending_handled = true;
 	}
 
-	rtnl_lock();
 	/* disable VSIs affected by DCB changes */
 	ice_dcb_ena_dis_vsi(pf, false, true);
 
 	ret = ice_query_port_ets(pi, &buf, sizeof(buf), NULL);
 	if (ret) {
 		dev_err(dev, "Query Port ETS failed\n");
-		goto unlock_rtnl;
+		goto out;
 	}
 
 	/* changes in configuration update VSI */
 	ice_pf_dcb_recfg(pf, false);
 
 	/* enable previously downed VSIs */
 	ice_dcb_ena_dis_vsi(pf, true, true);
-unlock_rtnl:
-	rtnl_unlock();
 out:
 	mutex_unlock(&pf->tc_mutex);
+	rtnl_unlock();
 
 	/* Send Execute Pending MIB Change event if it is a Pending event */
 	if (!pending_handled)
-- 
2.54.0
Thread (10 messages) 10 messages, 3 authors, last reply 2d ago
Keyboard shortcuts
hback out one level
jnext message in thread / next series-diff position
kprevious message in thread / previous series-diff position
ldrill in
Escclose help / fold thread tree
?toggle this help