Skip to content

Commit 04e9068

Browse files
oneukumPaolo Abeni
authored andcommitted
usbnet: fix cyclical race on disconnect with work queue
The work can submit URBs and the URBs can schedule the work. This cycle needs to be broken, when a device is to be stopped. Use a flag to do so. This is a design issue as old as the driver. Signed-off-by: Oliver Neukum <[email protected]> Fixes: 1da177e ("Linux-2.6.12-rc2") CC: [email protected] Link: https://patch.msgid.link/[email protected] Signed-off-by: Paolo Abeni <[email protected]>
1 parent b514c47 commit 04e9068

File tree

2 files changed

+43
-9
lines changed

2 files changed

+43
-9
lines changed

drivers/net/usb/usbnet.c

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -464,10 +464,15 @@ static enum skb_state defer_bh(struct usbnet *dev, struct sk_buff *skb,
464464
void usbnet_defer_kevent (struct usbnet *dev, int work)
465465
{
466466
set_bit (work, &dev->flags);
467-
if (!schedule_work (&dev->kevent))
468-
netdev_dbg(dev->net, "kevent %s may have been dropped\n", usbnet_event_names[work]);
469-
else
470-
netdev_dbg(dev->net, "kevent %s scheduled\n", usbnet_event_names[work]);
467+
if (!usbnet_going_away(dev)) {
468+
if (!schedule_work(&dev->kevent))
469+
netdev_dbg(dev->net,
470+
"kevent %s may have been dropped\n",
471+
usbnet_event_names[work]);
472+
else
473+
netdev_dbg(dev->net,
474+
"kevent %s scheduled\n", usbnet_event_names[work]);
475+
}
471476
}
472477
EXPORT_SYMBOL_GPL(usbnet_defer_kevent);
473478

@@ -535,7 +540,8 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
535540
tasklet_schedule (&dev->bh);
536541
break;
537542
case 0:
538-
__usbnet_queue_skb(&dev->rxq, skb, rx_start);
543+
if (!usbnet_going_away(dev))
544+
__usbnet_queue_skb(&dev->rxq, skb, rx_start);
539545
}
540546
} else {
541547
netif_dbg(dev, ifdown, dev->net, "rx: stopped\n");
@@ -843,9 +849,18 @@ int usbnet_stop (struct net_device *net)
843849

844850
/* deferred work (timer, softirq, task) must also stop */
845851
dev->flags = 0;
846-
del_timer_sync (&dev->delay);
847-
tasklet_kill (&dev->bh);
852+
del_timer_sync(&dev->delay);
853+
tasklet_kill(&dev->bh);
848854
cancel_work_sync(&dev->kevent);
855+
856+
/* We have cyclic dependencies. Those calls are needed
857+
* to break a cycle. We cannot fall into the gaps because
858+
* we have a flag
859+
*/
860+
tasklet_kill(&dev->bh);
861+
del_timer_sync(&dev->delay);
862+
cancel_work_sync(&dev->kevent);
863+
849864
if (!pm)
850865
usb_autopm_put_interface(dev->intf);
851866

@@ -1171,7 +1186,8 @@ usbnet_deferred_kevent (struct work_struct *work)
11711186
status);
11721187
} else {
11731188
clear_bit (EVENT_RX_HALT, &dev->flags);
1174-
tasklet_schedule (&dev->bh);
1189+
if (!usbnet_going_away(dev))
1190+
tasklet_schedule(&dev->bh);
11751191
}
11761192
}
11771193

@@ -1196,7 +1212,8 @@ usbnet_deferred_kevent (struct work_struct *work)
11961212
usb_autopm_put_interface(dev->intf);
11971213
fail_lowmem:
11981214
if (resched)
1199-
tasklet_schedule (&dev->bh);
1215+
if (!usbnet_going_away(dev))
1216+
tasklet_schedule(&dev->bh);
12001217
}
12011218
}
12021219

@@ -1559,6 +1576,7 @@ static void usbnet_bh (struct timer_list *t)
15591576
} else if (netif_running (dev->net) &&
15601577
netif_device_present (dev->net) &&
15611578
netif_carrier_ok(dev->net) &&
1579+
!usbnet_going_away(dev) &&
15621580
!timer_pending(&dev->delay) &&
15631581
!test_bit(EVENT_RX_PAUSED, &dev->flags) &&
15641582
!test_bit(EVENT_RX_HALT, &dev->flags)) {
@@ -1606,6 +1624,7 @@ void usbnet_disconnect (struct usb_interface *intf)
16061624
usb_set_intfdata(intf, NULL);
16071625
if (!dev)
16081626
return;
1627+
usbnet_mark_going_away(dev);
16091628

16101629
xdev = interface_to_usbdev (intf);
16111630

include/linux/usb/usbnet.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,23 @@ struct usbnet {
7676
# define EVENT_LINK_CHANGE 11
7777
# define EVENT_SET_RX_MODE 12
7878
# define EVENT_NO_IP_ALIGN 13
79+
/* This one is special, as it indicates that the device is going away
80+
* there are cyclic dependencies between tasklet, timer and bh
81+
* that must be broken
82+
*/
83+
# define EVENT_UNPLUG 31
7984
};
8085

86+
static inline bool usbnet_going_away(struct usbnet *ubn)
87+
{
88+
return test_bit(EVENT_UNPLUG, &ubn->flags);
89+
}
90+
91+
static inline void usbnet_mark_going_away(struct usbnet *ubn)
92+
{
93+
set_bit(EVENT_UNPLUG, &ubn->flags);
94+
}
95+
8196
static inline struct usb_driver *driver_of(struct usb_interface *intf)
8297
{
8398
return to_usb_driver(intf->dev.driver);

0 commit comments

Comments
 (0)