FAILED: patch "[PATCH] usbnet: fix cyclical race on disconnect with work queue" failed to apply to 5.10-stable tree
Subsystems (5)
[email protected]
maintainerThe patch below does not apply to the 5.10-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.10.y
git checkout FETCH_HEAD
git cherry-pick -x 04e906839a053f092ef53f4fb2d610983412b904
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2024100137-tiara-corned-9da8@gregkh' --subject-prefix 'PATCH 5.10.y' HEAD^..
Possible dependencies:
04e906839a05 ("usbnet: fix cyclical race on disconnect with work queue") a69e617e533e ("usbnet: Fix linkwatch use-after-free on disconnect") 478890682ff7 ("usbnet: add usbnet_event_names[] for kevent") 956baa99571b ("usbnet: add method for reporting speed without MII") 77651900cede ("usbnet: add _mii suffix to usbnet_set/get_link_ksettings")
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 04e906839a053f092ef53f4fb2d610983412b904 Mon Sep 17 00:00:00 2001
From: Oliver Neukum <[email protected]>
Date: Thu, 19 Sep 2024 14:33:42 +0200
Subject: [PATCH] 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.
drivers/net/usb/usbnet.c +28 -9
@@ -464,10 +464,15 @@ static enum skb_state defer_bh(struct usbnet *dev, struct sk_buff *skb,void usbnet_defer_kevent (struct usbnet *dev,int work) {set_bit (work, &dev->flags); -if (!schedule_work (&dev->kevent)) -netdev_dbg (dev->net,"kevent %s may have been dropped\n" , usbnet_event_names[work]); -else -netdev_dbg (dev->net,"kevent %s scheduled\n" , usbnet_event_names[work]); +if (!usbnet_going_away (dev)) { +if (!schedule_work (&dev->kevent)) +netdev_dbg (dev->net, +"kevent %s may have been dropped\n" , + usbnet_event_names[work]); +else +netdev_dbg (dev->net, +"kevent %s scheduled\n" , usbnet_event_names[work]); + } }EXPORT_SYMBOL_GPL (usbnet_defer_kevent);
@@ -535,7 +540,8 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)tasklet_schedule (&dev->bh);break ;case 0 : -__usbnet_queue_skb (&dev->rxq, skb, rx_start); +if (!usbnet_going_away (dev)) +__usbnet_queue_skb (&dev->rxq, skb, rx_start); } }else {netif_dbg (dev, ifdown, dev->net,"rx: stopped\n" );
@@ -843,9 +849,18 @@ int usbnet_stop (struct net_device *net)/* deferred work (timer, softirq, task) must also stop */ dev->flags =0 ; -del_timer_sync (&dev->delay); -tasklet_kill (&dev->bh); +del_timer_sync (&dev->delay); +tasklet_kill (&dev->bh);cancel_work_sync (&dev->kevent); + +/* We have cyclic dependencies. Those calls are needed +* to break a cycle. We cannot fall into the gaps because +* we have a flag +*/ +tasklet_kill (&dev->bh); +del_timer_sync (&dev->delay); +cancel_work_sync (&dev->kevent); +if (!pm)usb_autopm_put_interface (dev->intf);
@@ -1171,7 +1186,8 @@ usbnet_deferred_kevent (struct work_struct *work) status); }else {clear_bit (EVENT_RX_HALT , &dev->flags); -tasklet_schedule (&dev->bh); +if (!usbnet_going_away (dev)) +tasklet_schedule (&dev->bh); } }
@@ -1196,7 +1212,8 @@ usbnet_deferred_kevent (struct work_struct *work)usb_autopm_put_interface (dev->intf); fail_lowmem:if (resched) -tasklet_schedule (&dev->bh); +if (!usbnet_going_away (dev)) +tasklet_schedule (&dev->bh); } }
@@ -1559,6 +1576,7 @@ static void usbnet_bh (struct timer_list *t) }else if (netif_running (dev->net) &&netif_device_present (dev->net) &&netif_carrier_ok (dev->net) && + !usbnet_going_away (dev) && !timer_pending (&dev->delay) && !test_bit (EVENT_RX_PAUSED , &dev->flags) && !test_bit (EVENT_RX_HALT , &dev->flags)) {
@@ -1606,6 +1624,7 @@ void usbnet_disconnect (struct usb_interface *intf)usb_set_intfdata (intf,NULL );if (!dev)return ; +usbnet_mark_going_away (dev); xdev =interface_to_usbdev (intf);
include/linux/usb/usbnet.h +15 -0
@@ -76,8 +76,23 @@ struct usbnet {# define EVENT_LINK_CHANGE 11# define EVENT_SET_RX_MODE 12# define EVENT_NO_IP_ALIGN 13 +/* This one is special, as it indicates that the device is going away +* there are cyclic dependencies between tasklet, timer and bh +* that must be broken +*/ +# define EVENT_UNPLUG 31 }; +static inline bool usbnet_going_away (struct usbnet *ubn) +{ +return test_bit (EVENT_UNPLUG , &ubn->flags); +} + +static inline void usbnet_mark_going_away (struct usbnet *ubn) +{ +set_bit (EVENT_UNPLUG , &ubn->flags); +} +static inline struct usb_driver *driver_of (struct usb_interface *intf) {return to_usb_driver (intf->dev.driver);