I2C Mux Locking: Prevent a Channel Switch from Redirecting Another Transfer

A Linux I2C mux transaction has a route-selection phase, a target transfer, and an optional deselection phase. The locking model determines which other transfers may occur between those phases. Review that exact model and the complete upstream topology before adding a lock: an extra parent-adapter acquisition can deadlock a mux callback, while a missing topology constraint can expose the wrong device.

Applicability: this article uses Linux v6.12 I2C mux documentation and implementation, including i2c-mux-pca954x. It assumes kernel-owned mux adapters. The diagrams are hypothetical logical interleavings, not bus captures. Physical address-collision remedies and electrical qualification are separate design decisions.

A mux-locked sequence allows a direct-parent device transaction between selector and target payload, while a parent-locked sequence excludes it.
The comparison assumes the simple topology stated. Nested muxes require their own documented lock analysis; this is not a captured bus trace.

1. Name the adapter boundary for every operation

Draw the root controller, each mux, its logical child adapters, and devices attached directly to each ancestor. Record the bound mux driver and kernel revision. A bus number alone does not reveal which route or lock hierarchy protects the operation.

For each client request, identify the adapter through which the target transfer enters the kernel. Then identify how the mux’s select and deselect callbacks communicate with their controls: an I2C register operation, GPIO provider, pin controller, or another mechanism. A GPIO-controlled selector can itself depend on I2C, creating a dependency that is invisible in a simple connector drawing.

List every actor capable of changing selection, including reset and power-management paths. A diagnostic program writing the selector through the root bus can invalidate assumptions made by the kernel owner. Keep control inside the supported mux-aware path rather than coordinating ordinary clients with a private application mutex.

2. Distinguish mux-locked from parent-locked behavior

The v6.12 topology documentation defines two models. A mux-locked mux serializes mux users on its parent but does not hold the entire parent adapter throughout select, transfer, and deselect. An unrelated direct-parent transaction may interleave. A parent-locked mux holds its parent for the complete sequence.

Consider a hypothetical root adapter with mux M, target A behind M, and device R attached directly to the root. During access to A, a mux-locked arrangement can permit R’s transaction between selection and A’s payload. That is not automatically a bug. It is safe only if the selected route and device behavior tolerate the interleaving.

For the same simple topology, parent locking excludes R during the whole sequence. This gives a stronger exclusion boundary but also constrains what callbacks may call. Neither label should be interpreted as an unrestricted guarantee about the root when several differently locked mux levels are nested.

3. Match callback calls to the held lock

The v6.12 mux core chooses between transfer wrappers using normal i2c_transfer and unlocked __i2c_transfer paths according to the mux model. Parent-locked callbacks must use the appropriate unlocked parent operations when that parent lock is already held. Calling the normal locking path again can deadlock.

As a concrete implementation check, the v6.12 pca954x driver allocates its mux without I2C_MUX_LOCKED and uses an unlocked SMBus transfer for its selector write. Read both the allocation flags and callback implementation; a driver name or chip family alone is weaker evidence.

This does not authorize arbitrary clients to call unlocked internals. The correctness argument depends on who already owns the lock and on the framework path. Keep that proof beside the mux callback, including the error path, rather than distributing special locking assumptions among unrelated consumers.

4. Review nested topology assumptions explicitly

The topology guide identifies cases where mixing mux-locked and parent-locked levels breaks a child’s expectation of uninterrupted root access. It also warns about auto-closing gates and same-address devices behind certain nonsibling mux arrangements. Read the documented topology case matching the actual hierarchy.

Build an allowed-interleaving table. For each target operation, list sibling-channel traffic, direct-parent traffic, and traffic through another ancestor branch. Mark whether each may run between select and payload, between payload and deselect, or only after the complete sequence.

For an auto-closing gate, ask which transaction consumes its opening. An unrelated permitted transfer can close the gate before the intended payload arrives. For colliding addresses, ask whether two independently selected routes can become visible together. A lock that protects one mux object may be insufficient for the physical visibility rule.

Prefer a supported topology or driver design that makes the required exclusion explicit. Do not add a root lock around a random callback without checking its own dependencies and nesting order. A topology change can be a cleaner correction than an increasingly complicated lock workaround.

5. Separate one transfer from a longer device operation

A mux adapter protects its defined transfer sequence, not every later call made by the same application. If a device protocol requires several messages within one combined transfer, express that through the supported adapter interface. If it requires separate transfers with delays or state between them, define device-level ownership separately.

The route may legitimately be deselected between separate transfers. A long application operation must not assume that a previous selection remains active merely because no other application thread touched its object. Kernel clients, runtime power transitions, and unrelated device traffic can still participate.

Record route identity with the request and result. After an error, do not publish a value under the intended branch label unless the completed transfer provides that attribution. Revalidate selector state through the driver after reset rather than trusting a software cache that survived hardware state loss.

6. Test permitted and forbidden interleavings

  1. Different child channels: run concurrent read-only fixtures with distinguishable responses and verify correct attribution.
  2. Direct-parent traffic: exercise the permitted interleaving points and confirm they do not close a gate or alter visibility.
  3. Nested route: trace selection at every level and compare the actual ordering with the documented model.
  4. Select failure: require the target payload not to run after failed route selection; inspect deselection/error handling too.
  5. Reset or resume: verify that the next request establishes a valid route without relying on stale selection state.

Use safe fixtures and trace logical adapter, channel, selection, payload, deselection, and errors. Avoid inducing address contention on operational equipment. A transaction counter without route information cannot expose a response attributed to the wrong branch.

No mux transfer or concurrency test was executed for this article. The release artifact is a topology-specific lock map, callback call-path review, allowed-interleaving table, and concurrent-client evidence. That demonstrates which route remains valid across a transfer, rather than merely demonstrating that a mutex exists somewhere in the code.

Similar Posts