FAILED: patch "[PATCH] usb: gadget: f_ncm: Fix net_device lifecycle with device_move" failed to apply to 5.15-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 5.15-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.15.y
git checkout FETCH_HEAD
git cherry-pick -x ec35c1969650e7cb6c8a91020e568ed46e3551b0
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026031607-phobia-duh-6aa4@gregkh' --subject-prefix 'PATCH 5.15.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From ec35c1969650e7cb6c8a91020e568ed46e3551b0 Mon Sep 17 00:00:00 2001
From: Kuen-Han Tsai <[email protected]>
Date: Mon, 9 Mar 2026 20:04:52 +0800
Subject: [PATCH] usb: gadget: f_ncm: Fix net_device lifecycle with device_move
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/[email protected]/
[2] https://lore.kernel.org/linux-usb/[email protected]/
drivers/usb/gadget/function/f_ncm.c +24 -12
@@ -1439,6 +1439,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)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_configuration *c, struct usb_function *f)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); + 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; - - ncm_opts->bound = true; +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_configuration *c, struct usb_function *f) } 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_function_instance *f)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 *f)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_configuration *c, struct usb_function *f)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)
drivers/usb/gadget/function/u_ether.c +22 -0
@@ -897,6 +897,28 @@ void gether_set_gadget(struct net_device *net, struct usb_gadget *g) }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;
drivers/usb/gadget/function/u_ether.h +26 -0
@@ -150,6 +150,32 @@ static inline struct net_device *gether_setup_default(void)*/ 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
drivers/usb/gadget/function/u_ncm.h +1 -1
@@ -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;