mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-27 19:29:37 +00:00
net/ncsi: Avoid channel_monitor hrtimer deadlock
[ Upstream commit03cb4d05b4
] Calling ncsi_stop_channel_monitor from channel_monitor is a guaranteed deadlock on SMP because stop calls del_timer_sync on the timer that invoked channel_monitor as its timer function. Recognise the inherent race of marking the monitor disabled before deleting the timer by just returning if enable was cleared. After a timeout (the default case -- reset to START when response received) just mark the monitor.enabled false. If the channel has an entry on the channel_queue list, or if the state is not ACTIVE or INACTIVE, then warn and mark the timer stopped and don't restart, as the locking is broken somehow. Fixes:0795fb2021
("net/ncsi: Stop monitor if channel times out or is inactive") Signed-off-by: Milton Miller <miltonm@us.ibm.com> Signed-off-by: Eddie James <eajames@linux.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
c66b672a23
commit
bbbee59f4f
1 changed files with 13 additions and 7 deletions
|
@ -103,13 +103,20 @@ static void ncsi_channel_monitor(struct timer_list *t)
|
|||
monitor_state = nc->monitor.state;
|
||||
spin_unlock_irqrestore(&nc->lock, flags);
|
||||
|
||||
if (!enabled || chained) {
|
||||
ncsi_stop_channel_monitor(nc);
|
||||
return;
|
||||
}
|
||||
if (!enabled)
|
||||
return; /* expected race disabling timer */
|
||||
if (WARN_ON_ONCE(chained))
|
||||
goto bad_state;
|
||||
|
||||
if (state != NCSI_CHANNEL_INACTIVE &&
|
||||
state != NCSI_CHANNEL_ACTIVE) {
|
||||
ncsi_stop_channel_monitor(nc);
|
||||
bad_state:
|
||||
netdev_warn(ndp->ndev.dev,
|
||||
"Bad NCSI monitor state channel %d 0x%x %s queue\n",
|
||||
nc->id, state, chained ? "on" : "off");
|
||||
spin_lock_irqsave(&nc->lock, flags);
|
||||
nc->monitor.enabled = false;
|
||||
spin_unlock_irqrestore(&nc->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -134,10 +141,9 @@ static void ncsi_channel_monitor(struct timer_list *t)
|
|||
ncsi_report_link(ndp, true);
|
||||
ndp->flags |= NCSI_DEV_RESHUFFLE;
|
||||
|
||||
ncsi_stop_channel_monitor(nc);
|
||||
|
||||
ncm = &nc->modes[NCSI_MODE_LINK];
|
||||
spin_lock_irqsave(&nc->lock, flags);
|
||||
nc->monitor.enabled = false;
|
||||
nc->state = NCSI_CHANNEL_INVISIBLE;
|
||||
ncm->data[2] &= ~0x1;
|
||||
spin_unlock_irqrestore(&nc->lock, flags);
|
||||
|
|
Loading…
Reference in a new issue