mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 03:27:30 -04:00
netconsole: close netdevice unregister window during target resume
process_resume_target() removes the target from target_list before calling resume_target() so that netpoll_setup() can run with interrupts enabled, then re-adds it once setup completes. netpoll_setup() acquires a net_device reference (netdev_hold()) and releases the RTNL before returning. While the target is off target_list and the RTNL is not held, netconsole_netdev_event() cannot find it. If the egress device is unregistered in that window, the NETDEV_UNREGISTER notifier walks target_list, misses the resuming target, and never tears it down. The target is then re-added in STATE_ENABLED still holding a reference to the now-unregistered device, leaking it and hanging unregister_netdevice() in netdev_wait_allrefs(). Re-check under RTNL before re-publishing the target: if the device left NETREG_REGISTERED while we were off the list, run do_netpoll_cleanup() and mark the target disabled. Taking the RTNL across the check and the list_add() serialises against the NETDEV_UNREGISTER notifier, which also runs under RTNL, so the device is either still registered (and the notifier will find the re-added target later) or already unregistering (and we drop the reference here). netdev_wait_allrefs() runs from netdev_run_todo() outside the RTNL, so dropping the reference here cannot deadlock against the pending unregister. Signed-off-by: Breno Leitao <leitao@debian.org> Link: https://patch.msgid.link/20260604-netcons_fix_before_move-v3-5-ab055b3a6aa5@debian.org Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
committed by
Paolo Abeni
parent
4cfcd6acc2
commit
0360976d7e
@@ -335,6 +335,24 @@ static void process_resume_target(struct work_struct *work)
|
||||
|
||||
resume_target(nt);
|
||||
|
||||
/* netpoll_setup() took a net_device reference and dropped the RTNL
|
||||
* before returning, all while this target was off target_list and
|
||||
* thus invisible to netconsole_netdev_event(). If the device was
|
||||
* unregistered in that window the NETDEV_UNREGISTER notifier could not
|
||||
* tear this target down, which would leak the reference and hang
|
||||
* unregister_netdevice(). Re-check under the RTNL before re-publishing:
|
||||
* taking it across the check and the list_add() serialises against the
|
||||
* notifier (which also runs under the RTNL), so the device is either
|
||||
* still registered (the notifier will find the re-added target) or
|
||||
* already unregistering (we drop the reference here).
|
||||
*/
|
||||
rtnl_lock();
|
||||
if (nt->state == STATE_ENABLED && nt->np.dev &&
|
||||
nt->np.dev->reg_state != NETREG_REGISTERED) {
|
||||
do_netpoll_cleanup(&nt->np);
|
||||
nt->state = STATE_DISABLED;
|
||||
}
|
||||
|
||||
/* At this point the target is either enabled or disabled and
|
||||
* was cleaned up before getting deactivated. Either way, add it
|
||||
* back to target list.
|
||||
@@ -342,6 +360,7 @@ static void process_resume_target(struct work_struct *work)
|
||||
spin_lock_irqsave(&target_list_lock, flags);
|
||||
list_add(&nt->list, &target_list);
|
||||
spin_unlock_irqrestore(&target_list_lock, flags);
|
||||
rtnl_unlock();
|
||||
|
||||
out_unlock:
|
||||
dynamic_netconsole_mutex_unlock();
|
||||
|
||||
Reference in New Issue
Block a user