]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.19-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 16 Mar 2026 15:55:09 +0000 (16:55 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 16 Mar 2026 15:55:09 +0000 (16:55 +0100)
added patches:
usb-gadget-f_ncm-fix-net_device-lifecycle-with-device_move.patch

queue-6.19/series
queue-6.19/usb-gadget-f_ncm-fix-net_device-lifecycle-with-device_move.patch [new file with mode: 0644]

index 7e5369c46f3a45afa6f66eb4e743d8f5cb248069..6d48d513e625677e62a601400be821eae8bc3856 100644 (file)
@@ -169,3 +169,4 @@ revert-usb-legacy-ncm-fix-npe-in-gncm_bind.patch
 revert-usb-gadget-u_ether-add-auto-cleanup-helper-for-freeing-net_device.patch
 revert-usb-gadget-f_ncm-align-net_device-lifecycle-with-bind-unbind.patch
 revert-usb-gadget-u_ether-add-gether_opts-for-config-caching.patch
+usb-gadget-f_ncm-fix-net_device-lifecycle-with-device_move.patch
diff --git a/queue-6.19/usb-gadget-f_ncm-fix-net_device-lifecycle-with-device_move.patch b/queue-6.19/usb-gadget-f_ncm-fix-net_device-lifecycle-with-device_move.patch
new file mode 100644 (file)
index 0000000..15e7ba8
--- /dev/null
@@ -0,0 +1,208 @@
+From ec35c1969650e7cb6c8a91020e568ed46e3551b0 Mon Sep 17 00:00:00 2001
+From: Kuen-Han Tsai <khtsai@google.com>
+Date: Mon, 9 Mar 2026 20:04:52 +0800
+Subject: usb: gadget: f_ncm: Fix net_device lifecycle with device_move
+
+From: Kuen-Han Tsai <khtsai@google.com>
+
+commit ec35c1969650e7cb6c8a91020e568ed46e3551b0 upstream.
+
+The network device outlived its parent gadget device during
+disconnection, resulting in dangling sysfs links and null pointer
+dereference problems.
+
+A prior attempt to solve this by removing SET_NETDEV_DEV entirely [1]
+was reverted due to power management ordering concerns and a NO-CARRIER
+regression.
+
+A subsequent attempt to defer net_device allocation to bind [2] broke
+1:1 mapping between function instance and network device, making it
+impossible for configfs to report the resolved interface name. This
+results in a regression where the DHCP server fails on pmOS.
+
+Use device_move to reparent the net_device between the gadget device and
+/sys/devices/virtual/ across bind/unbind cycles. This preserves the
+network interface across USB reconnection, allowing the DHCP server to
+retain their binding.
+
+Introduce gether_attach_gadget()/gether_detach_gadget() helpers and use
+__free(detach_gadget) macro to undo attachment on bind failure. The
+bind_count ensures device_move executes only on the first bind.
+
+[1] https://lore.kernel.org/lkml/f2a4f9847617a0929d62025748384092e5f35cce.camel@crapouillou.net/
+[2] https://lore.kernel.org/linux-usb/795ea759-7eaf-4f78-81f4-01ffbf2d7961@ixit.cz/
+
+Fixes: 40d133d7f542 ("usb: gadget: f_ncm: convert to new function interface with backward compatibility")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Kuen-Han Tsai <khtsai@google.com>
+Link: https://patch.msgid.link/20260309-f-ncm-revert-v2-7-ea2afbc7d9b2@google.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/f_ncm.c   |   38 ++++++++++++++++++++++------------
+ drivers/usb/gadget/function/u_ether.c |   22 +++++++++++++++++++
+ drivers/usb/gadget/function/u_ether.h |   26 +++++++++++++++++++++++
+ drivers/usb/gadget/function/u_ncm.h   |    2 -
+ 4 files changed, 74 insertions(+), 14 deletions(-)
+
+--- a/drivers/usb/gadget/function/f_ncm.c
++++ b/drivers/usb/gadget/function/f_ncm.c
+@@ -1439,6 +1439,7 @@ static int ncm_bind(struct usb_configura
+       struct f_ncm_opts       *ncm_opts;
+       struct usb_os_desc_table        *os_desc_table __free(kfree) = NULL;
++      struct net_device               *net __free(detach_gadget) = NULL;
+       struct usb_request              *request __free(free_usb_request) = NULL;
+       if (!can_support_ecm(cdev->gadget))
+@@ -1452,18 +1453,19 @@ static int ncm_bind(struct usb_configura
+                       return -ENOMEM;
+       }
+-      mutex_lock(&ncm_opts->lock);
+-      gether_set_gadget(ncm_opts->net, cdev->gadget);
+-      if (!ncm_opts->bound) {
+-              ncm_opts->net->mtu = (ncm_opts->max_segment_size - ETH_HLEN);
+-              status = gether_register_netdev(ncm_opts->net);
+-      }
+-      mutex_unlock(&ncm_opts->lock);
+-
+-      if (status)
+-              return status;
+-
+-      ncm_opts->bound = true;
++      scoped_guard(mutex, &ncm_opts->lock)
++              if (ncm_opts->bind_count == 0) {
++                      if (!device_is_registered(&ncm_opts->net->dev)) {
++                              ncm_opts->net->mtu = (ncm_opts->max_segment_size - ETH_HLEN);
++                              gether_set_gadget(ncm_opts->net, cdev->gadget);
++                              status = gether_register_netdev(ncm_opts->net);
++                      } else
++                              status = gether_attach_gadget(ncm_opts->net, cdev->gadget);
++
++                      if (status)
++                              return status;
++                      net = ncm_opts->net;
++              }
+       ncm_string_defs[1].s = ncm->ethaddr;
+@@ -1564,6 +1566,9 @@ static int ncm_bind(struct usb_configura
+       }
+       ncm->notify_req = no_free_ptr(request);
++      ncm_opts->bind_count++;
++      retain_and_null_ptr(net);
++
+       DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n",
+                       ncm->port.in_ep->name, ncm->port.out_ep->name,
+                       ncm->notify->name);
+@@ -1655,7 +1660,7 @@ static void ncm_free_inst(struct usb_fun
+       struct f_ncm_opts *opts;
+       opts = container_of(f, struct f_ncm_opts, func_inst);
+-      if (opts->bound)
++      if (device_is_registered(&opts->net->dev))
+               gether_cleanup(netdev_priv(opts->net));
+       else
+               free_netdev(opts->net);
+@@ -1718,9 +1723,12 @@ static void ncm_free(struct usb_function
+ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
+ {
+       struct f_ncm *ncm = func_to_ncm(f);
++      struct f_ncm_opts *ncm_opts;
+       DBG(c->cdev, "ncm unbind\n");
++      ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
++
+       hrtimer_cancel(&ncm->task_timer);
+       kfree(f->os_desc_table);
+@@ -1736,6 +1744,10 @@ static void ncm_unbind(struct usb_config
+       kfree(ncm->notify_req->buf);
+       usb_ep_free_request(ncm->notify, ncm->notify_req);
++
++      ncm_opts->bind_count--;
++      if (ncm_opts->bind_count == 0)
++              gether_detach_gadget(ncm_opts->net);
+ }
+ static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
+--- a/drivers/usb/gadget/function/u_ether.c
++++ b/drivers/usb/gadget/function/u_ether.c
+@@ -896,6 +896,28 @@ void gether_set_gadget(struct net_device
+ }
+ EXPORT_SYMBOL_GPL(gether_set_gadget);
++int gether_attach_gadget(struct net_device *net, struct usb_gadget *g)
++{
++      int ret;
++
++      ret = device_move(&net->dev, &g->dev, DPM_ORDER_DEV_AFTER_PARENT);
++      if (ret)
++              return ret;
++
++      gether_set_gadget(net, g);
++      return 0;
++}
++EXPORT_SYMBOL_GPL(gether_attach_gadget);
++
++void gether_detach_gadget(struct net_device *net)
++{
++      struct eth_dev *dev = netdev_priv(net);
++
++      device_move(&net->dev, NULL, DPM_ORDER_NONE);
++      dev->gadget = NULL;
++}
++EXPORT_SYMBOL_GPL(gether_detach_gadget);
++
+ int gether_set_dev_addr(struct net_device *net, const char *dev_addr)
+ {
+       struct eth_dev *dev;
+--- a/drivers/usb/gadget/function/u_ether.h
++++ b/drivers/usb/gadget/function/u_ether.h
+@@ -151,6 +151,32 @@ static inline struct net_device *gether_
+ void gether_set_gadget(struct net_device *net, struct usb_gadget *g);
+ /**
++ * gether_attach_gadget - Reparent net_device to the gadget device.
++ * @net: The network device to reparent.
++ * @g: The target USB gadget device to parent to.
++ *
++ * This function moves the network device to be a child of the USB gadget
++ * device in the device hierarchy. This is typically done when the function
++ * is bound to a configuration.
++ *
++ * Returns 0 on success, or a negative error code on failure.
++ */
++int gether_attach_gadget(struct net_device *net, struct usb_gadget *g);
++
++/**
++ * gether_detach_gadget - Detach net_device from its gadget parent.
++ * @net: The network device to detach.
++ *
++ * This function moves the network device to be a child of the virtual
++ * devices parent, effectively detaching it from the USB gadget device
++ * hierarchy. This is typically done when the function is unbound
++ * from a configuration but the instance is not yet freed.
++ */
++void gether_detach_gadget(struct net_device *net);
++
++DEFINE_FREE(detach_gadget, struct net_device *, if (_T) gether_detach_gadget(_T))
++
++/**
+  * gether_set_dev_addr - initialize an ethernet-over-usb link with eth address
+  * @net: device representing this link
+  * @dev_addr: eth address of this device
+--- a/drivers/usb/gadget/function/u_ncm.h
++++ b/drivers/usb/gadget/function/u_ncm.h
+@@ -18,7 +18,7 @@
+ struct f_ncm_opts {
+       struct usb_function_instance    func_inst;
+       struct net_device               *net;
+-      bool                            bound;
++      int                             bind_count;
+       struct config_group             *ncm_interf_group;
+       struct usb_os_desc              ncm_os_desc;