Thread (1 message) flat view 1 message, 1 author, 6h ago
HOTtoday REVIEWED: 4 (4M)

2 review trailers.

[PATCH AUTOSEL 6.18] net: ethtool: cmis_cdb: hold instance lock for ops locked devices

From: Sasha Levin <sashal@kernel.org>
Date: 2026-08-31 13:44:02
Also in: linux-patches, lkml, stable
Subsystem: networking [ethtool], networking [general], the rest · Maintainers: Andrew Lunn, Jakub Kicinski, "David S. Miller", Eric Dumazet, Paolo Abeni, Linus Torvalds

From: Jakub Kicinski <kuba@kernel.org>

[ Upstream commit 1d3964e29e53b02a7c61ed6d6e4aeccba3275ebc ]

FW module flashing was written so that the flashing happens
without holding rtnl_lock. This allows flashing multiple modules
at once. Current drivers can handle that well, but we should
let drivers depend on the netdev instance lock. Instance lock
is per netdev, and so is the module so we won't break parallel
updates.

Reviewed-by: Jakub Sitnicki <jakub@cloudflare.com>
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
Link: https://patch.msgid.link/20260603012840.2254293-3-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---

LLM Generated explanations, may be completely bogus:

# Stable Backport Analysis: `net: ethtool: cmis_cdb: hold instance lock
for ops locked devices`

**Local tree:** `v6.18.44` (Linux 6.18.44)

---

## PHASE 1: COMMIT MESSAGE FORENSICS

### Step 1.1: Subject Line
**Record:** `[net: ethtool: cmis_cdb]` — no explicit "fix" verb, but
intent is locking correctness — `hold instance lock for ops locked
devices` during CMIS module firmware flashing.

### Step 1.2: Commit Tags
**Record:**
| Tag | Value |
|-----|-------|
| Reviewed-by | Jakub Sitnicki \<jakub@cloudflare.com\> |
| Acked-by | Stanislav Fomichev \<sdf@fomichev.me\> (ethtool/netdev
locking maintainer) |
| Link |
https://patch.msgid.link/20260603012840.2254293-3-kuba@kernel.org |
| Signed-off-by | Jakub Kicinski \<kuba@kernel.org\> |
| Fixes: | **Absent** (expected for candidate review) |
| Cc: stable | **Absent** (expected) |
| Reported-by / Tested-by | **Absent** |

Notable: subsystem maintainer Acked-by; no user/fuzzer reports.

### Step 1.3: Body Analysis
**Record:**
- **Bug described:** Module FW flashing runs without `rtnl_lock` (by
  design, for parallel flashing on different netdevs), but ops-locked
  drivers expect the per-netdev instance lock (`netdev_lock_ops`) during
  ethtool callbacks.
- **Symptom/failure mode:** Unsynchronized ethtool driver callbacks on
  ops-locked netdevs during firmware flashing.
- **Root cause:** `module_flash_fw_work()` calls
  `ethtool_cmis_fw_update()` without holding `netdev_lock_ops()`, while
  most other ethtool paths (added in commit `2bcf4772e45ad`) do hold it.
- **Version info:** None explicit in message.

### Step 1.4: Hidden Bug Fix Detection
**Record:** Yes — described as locking improvement, but it closes a real
race: concurrent ethtool ops vs. module-flash work on the same ops-
locked netdev.

---

## PHASE 2: DIFF ANALYSIS

### Step 2.1: Change Inventory
**Record:**
| File | Changes |
|------|---------|
| `include/net/netdev_lock.h` | +6 lines — new
`netdev_assert_locked_ops()` helper |
| `net/ethtool/cmis_cdb.c` | +3 lines — include + 2 lockdep assertions |
| `net/ethtool/cmis_fw_update.c` | +2 / -6 — reset path: lock acquire →
assert |
| `net/ethtool/module.c` | +2 lines — lock/unlock around fw update work
|

**Functions modified:** `netdev_assert_locked_ops()` (new),
`cmis_cdb_validate_password()`, `__ethtool_cmis_cdb_execute_cmd()`,
`cmis_fw_update_reset()`, `module_flash_fw_work()`

**Scope:** Single-subsystem, surgical (net +13 / -6).

### Step 2.2: Code Flow Changes
**Record:**
| Hunk | Before → After |
|------|----------------|
| `module_flash_fw_work()` | Calls `ethtool_cmis_fw_update()` unlocked →
wrapped in `netdev_lock_ops()` / `netdev_unlock_ops()` |
| `cmis_fw_update_reset()` | Acquires/releases lock internally → asserts
lock already held by caller |
| `__ethtool_cmis_cdb_execute_cmd()` / `cmis_cdb_validate_password()` |
No lock check → `netdev_assert_locked_ops(dev)` before
`set_module_eeprom_by_page()` |
| `netdev_lock.h` | No ops-only assert helper → adds
`netdev_assert_locked_ops()` (lockdep only when
`netdev_need_ops_lock()`) |

**Path affected:** Workqueue path for module firmware flashing (normal
operation path, not error-only).

### Step 2.3: Bug Mechanism
**Record:**
- **Category:** Synchronization / race condition (missing lock)
- **Mechanism:** After `2bcf4772e45ad` ("try to protect all callback
  with netdev instance lock"), most ethtool entry points take
  `netdev_lock_ops()` for drivers with `request_ops_lock=true`. The
  deferred work path (`module_flash_fw_work`) was missed. It calls
  `set_module_eeprom_by_page()` and `reset()` without the instance lock,
  while other ethtool ops on the same netdev can run concurrently with
  the lock held — two threads can enter driver ethtool callbacks
  simultaneously on ops-locked drivers.

### Step 2.4: Fix Quality
**Record:**
- **Quality:** Obviously correct — holds lock for entire fw-update
  duration; moves redundant nested lock in `cmis_fw_update_reset()` to
  assertion; adds lockdep checks at driver callback boundaries.
- **Regression risk:** Very low. `netdev_lock_ops()` is a no-op when
  `netdev_need_ops_lock()` is false. Per-netdev lock preserves parallel
  flashing across different netdevs.
- **Red flags:** None. No API changes, no refactoring.

---

## PHASE 3: GIT HISTORY INVESTIGATION

### Step 3.1: Blame
**Record:**
- `module_flash_fw_work()` without outer lock: introduced in
  `32b4c8b53ee77` (2024-06-27, "Add ability to flash transceiver
  modules' firmware")
- `cmis_fw_update_reset()` per-call locking: added in `2bcf4772e45ad`
  (2025-03-05)
- Recent related fixes already in 6.18.y: `9f5108f5ee273` (bitfield race
  on `module_fw_flash_in_progress`), `9e70c8efb0caf` (validation under
  rtnl)

### Step 3.2: Fixes: Tag
**Record:** N/A — no Fixes: tag in commit message.

### Step 3.3: Related File History
**Record:**
- Module FW flash feature: `32b4c8b53ee77` (present in tree)
- Netdev instance lock for ethtool: `2bcf4772e45ad` (present in tree)
- Patch is part of net-next v2 02/11 series preparing ethtool to run
  without rtnl, but **this patch is self-contained** — it does not
  require other series patches to function.

### Step 3.4: Author Context
**Record:** Jakub Kicinski is networking maintainer; authored related
module-flash fixes (`9f5108f5ee273`, `9e70c8efb0caf`) already backported
to this tree.

### Step 3.5: Dependencies
**Record:** Requires `netdev_lock_ops()` infrastructure and module FW
flash code — both present. Standalone; no prerequisite commits from the
broader rtnl-unlock series needed.

---

## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH

### Step 4.1: Original Discussion
**Record:**
- **URL:** https://lists.openwall.net/netdev/2026/06/03/31
- **Series:** `[PATCH net-next v2 02/11]` — part of "make sure
  __ethtool_get_link_ksettings() is ops-locked" prep series
- **Revisions:** v1 (01/14) → v2 (02/11); committed version matches v2
- **Reviewer feedback:** Naming discussion (netdev_assert_locked_ops vs
  netdev_ops_assert_locked); no functional objections
- **Stable nominations:** None found in thread
- **NAKs:** None

### Step 4.2: Reviewers
**Record:** CC'd to netdev@, davem, edumazet, pabeni, andrew+netdev,
driver maintainers. Reviewed-by Jakub Sitnicki; Acked-by Stanislav
Fomichev.

### Step 4.3: Bug Reports
**Record:** No syzbot, bugzilla, or user crash reports. Bug identified
through code review as part of ethtool locking hardening.

### Step 4.4: Series Context
**Record:** Patch 2/11 of rtnl-unlock prep series. This specific change
is independently valuable — fixes fw-flash locking regardless of whether
rtnl is dropped elsewhere.

### Step 4.5: Stable List History
**Record:** No stable@ discussion found for this specific patch. Related
module-flash fixes were already backported to 6.18.y.

---

## PHASE 5: CODE SEMANTIC ANALYSIS

### Step 5.1: Key Functions
**Record:** `module_flash_fw_work()`, `ethtool_cmis_fw_update()`,
`__ethtool_cmis_cdb_execute_cmd()`, `cmis_cdb_validate_password()`,
`cmis_fw_update_reset()`

### Step 5.2: Callers
**Record:**
- `module_flash_fw_work()` ← `schedule_work()` from
  `module_flash_fw_schedule()` ← `ethnl_act_module_fw_flash()` (netlink
  from userspace `ethtool --flash-module-firmware`)
- `set_module_eeprom_by_page` called on drivers including **bnxt**
  (`request_ops_lock=true`) and **mlxsw** (no ops lock — unaffected)

### Step 5.3: Callees
**Record:** Driver ethtool ops (`set_module_eeprom_by_page`, `reset`),
CMIS CDB command execution, sleep/polling during FW transfer.

### Step 5.4: Reachability
**Record:**
- Triggered by privileged admin via netlink ethtool
- Uncommon but real on datacenter NICs/switches with CMIS transceivers
- Race window: entire FW flash duration (seconds to minutes) if
  concurrent ethtool ops occur on same netdev

### Step 5.5: Similar Patterns
**Record:** All other ethtool paths in this tree use `netdev_lock_ops()`
/ `netdev_ops_assert_locked()` — fw-flash work path is the outlier.

---

## PHASE 6: CROSS-REFERENCE WITH LOCAL TREE (6.18.44)

### Step 6.1: Buggy Code Present?
**Record:** **YES.** Current `module_flash_fw_work()` at lines 229 calls
`ethtool_cmis_fw_update()` without `netdev_lock_ops()`.
`netdev_assert_locked_ops()` does not exist. Bug introduced when
instance-lock protection was added (`2bcf4772e45ad`) without covering
the workqueue path.

### Step 6.2: Backport Complications
**Record:** Minor context difference — local tree uses
`netdev_ops_assert_locked` naming; patch base uses
`netdev_assert_locked_ops_compat`. New helper
`netdev_assert_locked_ops()` adds cleanly near existing helpers.
Expected: **clean apply with possible trivial context adjustment**.

### Step 6.3: Related Fixes Already Present?
**Record:** Related module-flash race fixes (`9f5108f5ee273`,
`9e70c8efb0caf`, `61848c83b9132`) are in tree. This specific locking fix
is **not** yet applied.

---

## PHASE 7: SUBSYSTEM CONTEXT

### Step 7.1: Subsystem Criticality
**Record:** `net/ethtool` — **IMPORTANT** (datacenter NICs, switches;
admin-triggered but safety-critical during FW updates).

### Step 7.2: Activity
**Record:** Actively maintained — multiple module-flash fixes landed in
2025–2026, several already backported to 6.18.y.

---

## PHASE 8: IMPACT AND RISK ASSESSMENT

### Step 8.1: Who Is Affected
**Record:** Users of ops-locked drivers (bnxt, mlx5, bnge) performing
CMIS transceiver module firmware flashing. Config-specific: requires
`CONFIG_ETHTOOL` and driver `set_module_eeprom_by_page` support.

### Step 8.2: Trigger Conditions
**Record:** Module FW flash in progress on ops-locked netdev **and**
concurrent ethtool operation on same netdev. Uncommon but plausible in
automated datacenter management. Privileged admin operation.

### Step 8.3: Failure Mode Severity
**Record:** Concurrent unsynchronized driver ethtool callbacks →
possible **CRITICAL/HIGH** (NIC misbehavior, kernel oops, transceiver FW
corruption during update). No confirmed crash report, but consequences
of FW-update races are severe.

### Step 8.4: Risk-Benefit
**Record:**
- **Benefit:** HIGH for affected hardware — correct locking on safety-
  critical FW flash path
- **Risk:** VERY LOW — ~20 lines, no-op on non-ops-locked drivers,
  preserves parallel flash across netdevs
- **Ratio:** Strongly favorable

---

## PHASE 9: FINAL SYNTHESIS

### Step 9.1: Evidence Summary

**FOR backport:**
- Real locking hole on ops-locked drivers during module FW flash
- Prerequisites (module flash + netdev ops lock) present in 6.18.44
- Small, surgical, maintainer-reviewed fix
- Related module-flash fixes already backported to this tree
- Affects common datacenter drivers (bnxt with `request_ops_lock=true`)
- Lockdep assertions catch future regressions

**AGAINST backport:**
- Part of larger net-next rtnl-unlock series (but this patch is
  standalone)
- No user/fuzzer crash reports
- Rare code path (admin FW flash only)
- Naming context may need minor adjustment on backport

**Unresolved:** No runtime crash evidence; impact inferred from code
analysis and locking model.

### Step 9.2: Stable Rules Checklist
| Criterion | Result |
|-----------|--------|
| 1. Obviously correct and tested? | **PASS** — reviewed/acked; lockdep
assertions; logically sound |
| 2. Fixes real bug? | **PASS** — missing instance lock on fw-flash work
path |
| 3. Important issue? | **PASS** — race on ops-locked drivers during FW
update (HIGH severity) |
| 4. Small and contained? | **PASS** — 4 files, ~20 lines |
| 5. No new features/APIs? | **PASS** — locking only |
| 6. Applies to local tree? | **PASS** — buggy code present;
prerequisites in tree |

### Step 9.3: Exception Categories
**Record:** N/A — standard race-condition bug fix.

### Step 9.4: Decision Rationale

This commit closes a locking gap introduced when netdev instance-lock
protection was extended to ethtool callbacks but the deferred module-
firmware-flash workqueue path was overlooked. On ops-locked drivers like
bnxt, concurrent ethtool operations during a firmware flash can enter
driver callbacks without serialization — a real race with potentially
severe consequences during transceiver FW updates. The fix is minimal,
self-contained, does not depend on the rest of the rtnl-unlock series,
and follows the same pattern as related module-flash fixes already
backported to 6.18.y.

---

## Verification

- [Phase 1] `git describe HEAD` → v6.18.44; parsed subject, tags
  (Reviewed-by, Acked-by, Link), body
- [Phase 2] Analyzed provided diff; read current `module.c:221-242`,
  `cmis_fw_update.c:435-445`, `cmis_cdb.c:161-187,535-557`
- [Phase 3] `git blame` on `module_flash_fw_work` → introduced
  `32b4c8b53ee77`, lock gap since `2bcf4772e45ad`
- [Phase 3] `git show 2bcf4772e45ad` → added lock to init path and
  `cmis_fw_update_reset`, not work path
- [Phase 3] `git merge-base --is-ancestor` → module flash YES, netdev
  lock ethtool YES
- [Phase 3] `git log --oneline -20` on ethtool module files → related
  fixes present
- [Phase 4] Fetched https://lists.openwall.net/netdev/2026/06/03/31 → v2
  02/11 patch content confirmed
- [Phase 4] Web search → series context, Fomichev Acked-by, naming
  discussion
- [Phase 4] `b4 dig -c 2bcf4772e45ad` → found related thread
  (patch.msgid.link blocked by bot protection)
- [Phase 5] `grep` call chain: `ethnl_act_module_fw_flash` →
  `module_flash_fw_schedule` → `schedule_work` → `module_flash_fw_work`
- [Phase 5] `grep request_ops_lock` → bnxt, mlx5, bnge set it; mlxsw
  does not
- [Phase 5] `grep set_module_eeprom_by_page` → bnxt, mlxsw implement it
- [Phase 6] `grep netdev_assert_locked_ops` → not in tree (fix not
  applied)
- [Phase 6] Read current `module_flash_fw_work()` → missing outer
  `netdev_lock_ops()`
- [Phase 6] Read `include/net/netdev_lock.h` → `netdev_lock_ops()` no-op
  when `!netdev_need_ops_lock()`
- [Phase 7] Confirmed net/ethtool subsystem; recent backports
  `9f5108f5ee273`, `9e70c8efb0caf` in tree
- [Phase 8] Assessed severity from locking model + bnxt
  `request_ops_lock=true` (no crash report)

**YES**

 include/net/netdev_lock.h    | 6 ++++++
 net/ethtool/cmis_cdb.c       | 3 +++
 net/ethtool/cmis_fw_update.c | 8 ++------
 net/ethtool/module.c         | 2 ++
 4 files changed, 13 insertions(+), 6 deletions(-)
diff --git a/include/net/netdev_lock.h b/include/net/netdev_lock.h
index 3d3aef80beac1..849b91ab4e28b 100644
--- a/include/net/netdev_lock.h
+++ b/include/net/netdev_lock.h
@@ -80,6 +80,12 @@ netdev_ops_assert_locked_or_invisible(const struct net_device *dev)
 		netdev_ops_assert_locked(dev);
 }
 
+static inline void netdev_assert_locked_ops(const struct net_device *dev)
+{
+	if (netdev_need_ops_lock(dev))
+		netdev_assert_locked(dev);
+}
+
 static inline void netdev_lock_ops_compat(struct net_device *dev)
 {
 	if (netdev_need_ops_lock(dev))
diff --git a/net/ethtool/cmis_cdb.c b/net/ethtool/cmis_cdb.c
index fe156991d0bec..b39c7d47580da 100644
--- a/net/ethtool/cmis_cdb.c
+++ b/net/ethtool/cmis_cdb.c
@@ -2,6 +2,7 @@
 
 #include <linux/ethtool.h>
 #include <linux/jiffies.h>
+#include <net/netdev_lock.h>
 
 #include "common.h"
 #include "module_fw.h"
@@ -179,6 +180,7 @@ cmis_cdb_validate_password(struct ethtool_cmis_cdb *cdb,
 
 	pe_pl = *((struct cmis_password_entry_pl *)page_data.data);
 	pe_pl.password = params->password;
+	netdev_assert_locked_ops(dev);
 	err = ops->set_module_eeprom_by_page(dev, &page_data, &extack);
 	if (err < 0) {
 		if (extack._msg)
@@ -546,6 +548,7 @@ __ethtool_cmis_cdb_execute_cmd(struct net_device *dev,
 	if (!page_data->data)
 		return -ENOMEM;
 
+	netdev_assert_locked_ops(dev);
 	err = ops->set_module_eeprom_by_page(dev, page_data, &extack);
 	if (err < 0) {
 		if (extack._msg)
diff --git a/net/ethtool/cmis_fw_update.c b/net/ethtool/cmis_fw_update.c
index 291d04d2776a5..dff83807e975c 100644
--- a/net/ethtool/cmis_fw_update.c
+++ b/net/ethtool/cmis_fw_update.c
@@ -435,13 +435,9 @@ cmis_fw_update_commit_image(struct ethtool_cmis_cdb *cdb,
 static int cmis_fw_update_reset(struct net_device *dev)
 {
 	__u32 reset_data = ETH_RESET_PHY;
-	int ret;
 
-	netdev_lock_ops(dev);
-	ret = dev->ethtool_ops->reset(dev, &reset_data);
-	netdev_unlock_ops(dev);
-
-	return ret;
+	netdev_assert_locked_ops(dev);
+	return dev->ethtool_ops->reset(dev, &reset_data);
 }
 
 void
diff --git a/net/ethtool/module.c b/net/ethtool/module.c
index 9a11e7def0029..8a313c975e2f5 100644
--- a/net/ethtool/module.c
+++ b/net/ethtool/module.c
@@ -226,7 +226,9 @@ static void module_flash_fw_work(struct work_struct *work)
 	module_fw = container_of(work, struct ethtool_module_fw_flash, work);
 	dev = module_fw->fw_update.dev;
 
+	netdev_lock_ops(dev);
 	ethtool_cmis_fw_update(&module_fw->fw_update);
+	netdev_unlock_ops(dev);
 
 	module_flash_fw_work_list_del(&module_fw->list);
 
-- 
2.53.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help