Skip to content

Commit 03cb4d0

Browse files
mdmilleriidavem330
authored andcommitted
net/ncsi: Avoid channel_monitor hrtimer deadlock
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: 0795fb2 ("net/ncsi: Stop monitor if channel times out or is inactive") Signed-off-by: Milton Miller <[email protected]> Signed-off-by: Eddie James <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 6e5a03b commit 03cb4d0

File tree

1 file changed

+13
-7
lines changed

1 file changed

+13
-7
lines changed

net/ncsi/ncsi-manage.c

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,20 @@ static void ncsi_channel_monitor(struct timer_list *t)
105105
monitor_state = nc->monitor.state;
106106
spin_unlock_irqrestore(&nc->lock, flags);
107107

108-
if (!enabled || chained) {
109-
ncsi_stop_channel_monitor(nc);
110-
return;
111-
}
108+
if (!enabled)
109+
return; /* expected race disabling timer */
110+
if (WARN_ON_ONCE(chained))
111+
goto bad_state;
112+
112113
if (state != NCSI_CHANNEL_INACTIVE &&
113114
state != NCSI_CHANNEL_ACTIVE) {
114-
ncsi_stop_channel_monitor(nc);
115+
bad_state:
116+
netdev_warn(ndp->ndev.dev,
117+
"Bad NCSI monitor state channel %d 0x%x %s queue\n",
118+
nc->id, state, chained ? "on" : "off");
119+
spin_lock_irqsave(&nc->lock, flags);
120+
nc->monitor.enabled = false;
121+
spin_unlock_irqrestore(&nc->lock, flags);
115122
return;
116123
}
117124

@@ -136,10 +143,9 @@ static void ncsi_channel_monitor(struct timer_list *t)
136143
ncsi_report_link(ndp, true);
137144
ndp->flags |= NCSI_DEV_RESHUFFLE;
138145

139-
ncsi_stop_channel_monitor(nc);
140-
141146
ncm = &nc->modes[NCSI_MODE_LINK];
142147
spin_lock_irqsave(&nc->lock, flags);
148+
nc->monitor.enabled = false;
143149
nc->state = NCSI_CHANNEL_INVISIBLE;
144150
ncm->data[2] &= ~0x1;
145151
spin_unlock_irqrestore(&nc->lock, flags);

0 commit comments

Comments
 (0)