Zero adapter->ctrl_pf when the PF owning it is removed and wait for
pre-existing RCU readers before its storage can be released. Without
this the pointer outlives the control PF, so sibling PFs keep
dereferencing it after it has been torn down.
The preceding patches make this safe to do: readers already resolve the
pointer once per critical section, and the PTP paths already reject a
missing control PF instead of falling back to the caller's own register
space.
Clear the pointer before unregistering the PTP clock on every teardown
path. ice_ptp_clock_index() samples ctrl_ptp->clock under RCU only, and
ptp_clock_release() frees struct ptp_clock outright rather than
deferring to a grace period, so a sibling PF could otherwise read the
clock index out of freed memory.
Fixes: e2193f9f9ec9 ("ice: enable timesync operation on 2xNAC E825 devices")
Fixes: e800654e85b5 ("ice: Use ice_adapter for PTP shared data instead of
auxdev")
Reported-by: Frederick Lawler <[email protected]>
Closes: https://lore.kernel.org/all/aIKWoZzEPoa1omlw@CMGLRV3/
Signed-off-by: Sergey Temerkhanov <[email protected]>
Reviewed-by: Arkadiusz Kubalewski <[email protected]>
Reviewed-by: Aleksandr Loktionov <[email protected]>
Tested-by: Frederick Lawler <[email protected]>
---
drivers/net/ethernet/intel/ice/ice_ptp.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c
b/drivers/net/ethernet/intel/ice/ice_ptp.c
index 471fc39f379b..043a386c2657 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -3377,6 +3377,20 @@ static void ice_ptp_setup_adapter(struct ice_pf *pf)
rcu_assign_pointer(pf->adapter->ctrl_pf, pf);
}
+static void ice_ptp_cleanup_adapter(struct ice_pf *pf)
+{
+ guard(rwsem_write)(&pf->adapter->ctrl_pf_lock);
+
+ /* Zero out adapter->ctrl_pf pointer when the ctrl_pf itself
+ * is being removed to prevent any secondary PFs from accessing
+ * it after it is deleted.
+ */
+ if (ice_get_ctrl_pf(pf) == pf) {
+ rcu_assign_pointer(pf->adapter->ctrl_pf, NULL);
+ synchronize_rcu();
+ }
+}
+
static int ice_ptp_setup_pf(struct ice_pf *pf)
{
struct ice_ptp *ptp = &pf->ptp;
@@ -3716,6 +3730,7 @@ void ice_ptp_init(struct ice_pf *pf)
err_release_tx_tracker:
ice_ptp_release_tx_tracker(pf, &pf->ptp.port.tx);
err_exit:
+ ice_ptp_cleanup_adapter(pf);
/* If we registered a PTP clock, release it */
if (pf->ptp.clock) {
ptp_clock_unregister(ptp->clock);
@@ -3748,11 +3763,15 @@ void ice_ptp_release(struct ice_pf *pf)
kthread_destroy_worker(pf->ptp.kworker);
pf->ptp.kworker = NULL;
}
+ ice_ptp_cleanup_pf(pf);
+ ice_ptp_cleanup_adapter(pf);
+ /* Unregister only once no sibling PF can reach pf->ptp.clock
+ * through adapter->ctrl_pf any more.
+ */
if (pf->ptp.clock) {
ptp_clock_unregister(pf->ptp.clock);
pf->ptp.clock = NULL;
}
- ice_ptp_cleanup_pf(pf);
ice_ptp_release_tx_tracker(pf, &pf->ptp.port.tx);
return;
}
@@ -3764,6 +3783,8 @@ void ice_ptp_release(struct ice_pf *pf)
ice_ptp_cleanup_pf(pf);
+ ice_ptp_cleanup_adapter(pf);
+
ice_ptp_release_tx_tracker(pf, &pf->ptp.port.tx);
ice_ptp_disable_all_extts(pf);
--
2.53.0