RE: [PATCH iwl-net 01/12] ice: use reference counting and RCU for PTP port access
"Nowlin, Alexander" <[email protected]>
| Newsgroups | org.osuosl.intel-wired-lan,org.kernel.vger.netdev |
|---|---|
| Message-ID | <LV1PR11MB87909FCB7E24DCA7E52CA4EC90A22@LV1PR11MB8790.namprd11.prod.outlook.com> |
> -----Original Message----- > From: Keller, Jacob E <[email protected]> > Sent: Friday, August 21, 2026 5:13 PM > To: Intel Wired LAN <[email protected]> > Cc: [email protected]; Machnikowski, Maciej <[email protected]>; Nguyen, Anthony L <[email protected]>; Korba, Przemyslaw <[email protected]>; Nitka, Grzegorz <[email protected]>; Oros, Petr <[email protected]>; Nowlin, Alexander <[email protected]>; Bross, Kevin <[email protected]>; Cavatur, Ranjit <[email protected]>; Keller, Jacob E <[email protected]>; Machnikowski, Maciej <[email protected]> > Subject: [PATCH iwl-net 01/12] ice: use reference counting and RCU for PTP port access > > The ice adapter structure maintains a list of ports associated with the adapter. This is used for supporting PTP, where the clock owner must handle many operations that require access to the PTP port structures > of the associated PFs. > > This is implemented using a linked list and a mutex. This sort of works, but a few places within the code do not acquire the mutex when iterating the list. This includes ice_ptp_flush_all_tx_tracker(), ice_ptp_restart_all_phy(), and ice_ptp_prepare_rebuild_sec(). > > Fixing this is tricky, especially since it is not clear if we can simply acquire the lock around the complete iterations. > > The pattern of use for the port list is read-mostly with modifications only happening during PF initialization when elements are inserted. This typically only happens during early boot, though a PF could in principle be removed or loaded at arbitrary times via bind and unbind operations. > > The use of a mutex does mean the driver can sleep while holding it, but it still creates complicates with lock ordering and prevents iterating the list in any code path that *can't* sleep. > > Instead, use the RCU primitives for the port linked list, along with a reference count on the port. The kref reference counter ensures that we can safely acquire pointers with a guarantee of their lifetime, ensuring > the associated PF will not be removed until the reference is released. > > For port iterations which are short and definitely can't sleep, wrap the entire loop with rcu_read_lock() and rcu_read_unlock(). > > For longer operations, or those which might sleep, we need to close the critical section between each loop iteration. To make this safe, start the loop iteration with rcu_read_lock(), then acquire a reference for the port with kref_get_unless_zero. If this returns 0, the port is already in the process of being removed, so that port should be skipped when iterating. Once a reference to the port is acquired, exit the RCU critical > section. Then, perform the desired operations on the port, followed by releasing the reference with kref_put and then re-entering a critical section at the end of the loop body. Note that kref_put() is done outside the RCU critical section. This is safe, as the port will not be freed until all references are dropped. > > The ice_ptp_release_port_rcu() function is used as the release function for the kref_put() call. To avoid a potential infinite loop of new references, the release function simply uses a wake_up_var() call to wake the closing thread. The ice_ptp_cleanup_pf() function will remove the port from the linked list using list_del_rcu, then release its primary reference, then wait for all references to drop via wait queue. Finally synchronize_rcu() is called to guarantee the port remains valid for at least one RCU grace period. Then PF removal will continue. > > This flow ensures that all accesses to ports via the port list will remain valid until either the RCU critical sections end, or the references have been dropped. > > One major complication of this reference count is that ice_ptp_port is embedded inside of other structures and not merely allocated. As a result, we can't use the standard pattern of kfree_rcu() to just delay freeing until references are dropped, and instead are delaying PF port teardown. If any code path leaks the reference, the driver will be unable to teardown. > Instead, a 15 second timeout with a WARN() is used when waiting to finally allow PF teardown to continue. This has the risk of potentially allowing use-after-free, assuming some path really is stuck for 15 seconds. However, this both less likely and a less bad outcome compared to blocking indefinitely on a reference leak. > > Fixes: e800654e85b5 ("ice: Use ice_adapter for PTP shared data instead of auxdev") > Signed-off-by: Jacob Keller <[email protected]> > Reviewed-by: Maciek Machnikowski <[email protected]> > --- > drivers/net/ethernet/intel/ice/ice_adapter.h | 6 +- > drivers/net/ethernet/intel/ice/ice_ptp.h | 4 + > drivers/net/ethernet/intel/ice/ice_adapter.c | 7 +- > drivers/net/ethernet/intel/ice/ice_ptp.c | 118 +++++++++++++++++++-------- > 4 files changed, 96 insertions(+), 39 deletions(-) Tested-by: Alexander Nowlin <[email protected]>