From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AE5101A9B58; Tue, 17 Mar 2026 17:16:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773767763; cv=none; b=ontUjAuAHzMR3O/nm2a9c29lxVIk7hsL6fYCdbS+52LQfyI9xPYZL0N2sNwa1hwfHknUyETRtC01gN9OOny8laTm4jYoJsJpWoj9LXnLEC4x/7dDgy/N5wxlGpUjJ8cn/h6dkRV5faxq39bnkpTtpxEs71nRh3H4DOlTKztuCFA= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773767763; c=relaxed/simple; bh=EXtvnY//5m46fk5DRjpF9I6xvzdc0d7y6ntbXaN5rtE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZTGd6OjH6j2l9rpuDpC9hjEc1Yqq7zzajMB7QfqdJZFIcfHfEiURhCBMCy0qF78jTWEWgPqTWcwnOIdF0To/ivEevel+HrNQaUL26dJ3ECWtbiuOoQjYGSPnNxhS+hwiUnubSPb/cznT7xECXGCvT0Vg09BmN8fMJODWxw9D5lA= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b=VADEtlTV; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b="VADEtlTV" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 0C98EC4CEF7; Tue, 17 Mar 2026 17:16:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=linuxfoundation.org; s=korg; t=1773767763; bh=EXtvnY//5m46fk5DRjpF9I6xvzdc0d7y6ntbXaN5rtE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=VADEtlTV6USsFKqcLlE053Yuf+JT9/rJmNeZvF+WhvK5IZKxW2ZnV2VLvFB9Jy7rB xTUtmR5LxTe+B0GNshvBJde+fkKu7zbDxt30jRVPVaAYelXgI5FPOH7EGM479YLNzg GGO/+rswhYBWNusDnOQGxt1UJAz+BNF2pRqvkuXU= From: Greg Kroah-Hartman To: stable@vger.kernel.org Cc: Greg Kroah-Hartman , patches@lists.linux.dev, David Heidelberg , stable , Kuen-Han Tsai Subject: [PATCH 6.18 153/333] Revert "usb: gadget: f_ncm: align net_device lifecycle with bind/unbind" Date: Tue, 17 Mar 2026 17:33:02 +0100 Message-ID: <20260317163005.032360367@linuxfoundation.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260317162959.345812316@linuxfoundation.org> References: <20260317162959.345812316@linuxfoundation.org> User-Agent: quilt/0.69 X-stable: review X-Patchwork-Hint: ignore Precedence: bulk X-Mailing-List: stable@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 6.18-stable review patch. If anyone has any objections, please let me know. ------------------ From: Kuen-Han Tsai commit 37893bc5de2460c543ec1aa8250c37a305234054 upstream. This reverts commit 56a512a9b4107079f68701e7d55da8507eb963d9. This commit is being reverted as part of a series-wide revert. By deferring the net_device allocation to the bind() phase, a single function instance will spawn multiple network devices if it is symlinked to multiple USB configurations. This causes regressions for userspace tools (like the postmarketOS DHCP daemon) that rely on reading the interface name (e.g., "usb0") from configfs. Currently, configfs returns the template "usb%d", causing the userspace network setup to fail. Crucially, because this patch breaks the 1:1 mapping between the function instance and the network device, this naming issue cannot simply be patched. Configfs only exposes a single 'ifname' attribute per instance, making it impossible to accurately report the actual interface name when multiple underlying network devices can exist for that single instance. All configurations tied to the same function instance are meant to share a single network device. Revert this change to restore the 1:1 mapping by allocating the network device at the instance level (alloc_inst). Reported-by: David Heidelberg Closes: https://lore.kernel.org/linux-usb/70b558ea-a12e-4170-9b8e-c951131249af@ixit.cz/ Fixes: 56a512a9b410 ("usb: gadget: f_ncm: align net_device lifecycle with bind/unbind") Cc: stable Signed-off-by: Kuen-Han Tsai Link: https://patch.msgid.link/20260309-f-ncm-revert-v2-3-ea2afbc7d9b2@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_ncm.c | 128 ++++++++++++++++++------------------ drivers/usb/gadget/function/u_ncm.h | 4 - 2 files changed, 66 insertions(+), 66 deletions(-) --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -83,11 +83,6 @@ static inline struct f_ncm *func_to_ncm( return container_of(f, struct f_ncm, port.func); } -static inline struct f_ncm_opts *func_to_ncm_opts(struct usb_function *f) -{ - return container_of(f->fi, struct f_ncm_opts, func_inst); -} - /*-------------------------------------------------------------------------*/ /* @@ -864,7 +859,6 @@ invalid: static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { struct f_ncm *ncm = func_to_ncm(f); - struct f_ncm_opts *opts = func_to_ncm_opts(f); struct usb_composite_dev *cdev = f->config->cdev; /* Control interface has only altsetting 0 */ @@ -887,13 +881,12 @@ static int ncm_set_alt(struct usb_functi if (alt > 1) goto fail; - scoped_guard(mutex, &opts->lock) - if (opts->net) { - DBG(cdev, "reset ncm\n"); - opts->net = NULL; - gether_disconnect(&ncm->port); - ncm_reset_values(ncm); - } + if (ncm->netdev) { + DBG(cdev, "reset ncm\n"); + ncm->netdev = NULL; + gether_disconnect(&ncm->port); + ncm_reset_values(ncm); + } /* * CDC Network only sends data in non-default altsettings. @@ -926,8 +919,7 @@ static int ncm_set_alt(struct usb_functi net = gether_connect(&ncm->port); if (IS_ERR(net)) return PTR_ERR(net); - scoped_guard(mutex, &opts->lock) - opts->net = net; + ncm->netdev = net; } spin_lock(&ncm->lock); @@ -1374,16 +1366,14 @@ err: static void ncm_disable(struct usb_function *f) { struct f_ncm *ncm = func_to_ncm(f); - struct f_ncm_opts *opts = func_to_ncm_opts(f); struct usb_composite_dev *cdev = f->config->cdev; DBG(cdev, "ncm deactivated\n"); - scoped_guard(mutex, &opts->lock) - if (opts->net) { - opts->net = NULL; - gether_disconnect(&ncm->port); - } + if (ncm->netdev) { + ncm->netdev = NULL; + gether_disconnect(&ncm->port); + } if (ncm->notify->enabled) { usb_ep_disable(ncm->notify); @@ -1443,44 +1433,39 @@ static int ncm_bind(struct usb_configura { struct usb_composite_dev *cdev = c->cdev; struct f_ncm *ncm = func_to_ncm(f); - struct f_ncm_opts *ncm_opts = func_to_ncm_opts(f); struct usb_string *us; int status = 0; struct usb_ep *ep; + struct f_ncm_opts *ncm_opts; struct usb_os_desc_table *os_desc_table __free(kfree) = NULL; - struct net_device *netdev __free(free_gether_netdev) = NULL; struct usb_request *request __free(free_usb_request) = NULL; if (!can_support_ecm(cdev->gadget)) return -EINVAL; + ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst); + if (cdev->use_os_string) { os_desc_table = kzalloc(sizeof(*os_desc_table), GFP_KERNEL); if (!os_desc_table) return -ENOMEM; } - netdev = gether_setup_default(); - if (IS_ERR(netdev)) - return -ENOMEM; - - scoped_guard(mutex, &ncm_opts->lock) { - gether_apply_opts(netdev, &ncm_opts->net_opts); - netdev->mtu = ncm_opts->max_segment_size - ETH_HLEN; + 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); - gether_set_gadget(netdev, cdev->gadget); - status = gether_register_netdev(netdev); if (status) return status; - /* export host's Ethernet address in CDC format */ - status = gether_get_host_addr_cdc(netdev, ncm->ethaddr, - sizeof(ncm->ethaddr)); - if (status < 12) - return -EINVAL; - ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr; + ncm_opts->bound = true; + + ncm_string_defs[1].s = ncm->ethaddr; us = usb_gstrings_attach(cdev, ncm_strings, ARRAY_SIZE(ncm_string_defs)); @@ -1578,8 +1563,6 @@ static int ncm_bind(struct usb_configura f->os_desc_n = 1; } ncm->notify_req = no_free_ptr(request); - ncm->netdev = no_free_ptr(netdev); - ncm->port.ioport = netdev_priv(ncm->netdev); DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n", ncm->port.in_ep->name, ncm->port.out_ep->name, @@ -1594,19 +1577,19 @@ static inline struct f_ncm_opts *to_f_nc } /* f_ncm_item_ops */ -USB_ETHER_OPTS_ITEM(ncm); +USB_ETHERNET_CONFIGFS_ITEM(ncm); /* f_ncm_opts_dev_addr */ -USB_ETHER_OPTS_ATTR_DEV_ADDR(ncm); +USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ncm); /* f_ncm_opts_host_addr */ -USB_ETHER_OPTS_ATTR_HOST_ADDR(ncm); +USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ncm); /* f_ncm_opts_qmult */ -USB_ETHER_OPTS_ATTR_QMULT(ncm); +USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm); /* f_ncm_opts_ifname */ -USB_ETHER_OPTS_ATTR_IFNAME(ncm); +USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ncm); static ssize_t ncm_opts_max_segment_size_show(struct config_item *item, char *page) @@ -1672,27 +1655,34 @@ 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) + gether_cleanup(netdev_priv(opts->net)); + else + free_netdev(opts->net); kfree(opts->ncm_interf_group); kfree(opts); } static struct usb_function_instance *ncm_alloc_inst(void) { - struct usb_function_instance *ret; + struct f_ncm_opts *opts; struct usb_os_desc *descs[1]; char *names[1]; struct config_group *ncm_interf_group; - struct f_ncm_opts *opts __free(kfree) = kzalloc(sizeof(*opts), GFP_KERNEL); + opts = kzalloc(sizeof(*opts), GFP_KERNEL); if (!opts) return ERR_PTR(-ENOMEM); - - opts->net = NULL; opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id; - gether_setup_opts_default(&opts->net_opts, "usb"); mutex_init(&opts->lock); opts->func_inst.free_func_inst = ncm_free_inst; + opts->net = gether_setup_default(); + if (IS_ERR(opts->net)) { + struct net_device *net = opts->net; + kfree(opts); + return ERR_CAST(net); + } opts->max_segment_size = ETH_FRAME_LEN; INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop); @@ -1703,22 +1693,26 @@ static struct usb_function_instance *ncm ncm_interf_group = usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs, names, THIS_MODULE); - if (IS_ERR(ncm_interf_group)) + if (IS_ERR(ncm_interf_group)) { + ncm_free_inst(&opts->func_inst); return ERR_CAST(ncm_interf_group); + } opts->ncm_interf_group = ncm_interf_group; - ret = &opts->func_inst; - retain_and_null_ptr(opts); - return ret; + return &opts->func_inst; } static void ncm_free(struct usb_function *f) { - struct f_ncm_opts *opts = func_to_ncm_opts(f); + struct f_ncm *ncm; + struct f_ncm_opts *opts; - scoped_guard(mutex, &opts->lock) - opts->refcnt--; - kfree(func_to_ncm(f)); + ncm = func_to_ncm(f); + opts = container_of(f->fi, struct f_ncm_opts, func_inst); + kfree(ncm); + mutex_lock(&opts->lock); + opts->refcnt--; + mutex_unlock(&opts->lock); } static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) @@ -1742,15 +1736,13 @@ static void ncm_unbind(struct usb_config kfree(ncm->notify_req->buf); usb_ep_free_request(ncm->notify, ncm->notify_req); - - ncm->port.ioport = NULL; - gether_cleanup(netdev_priv(ncm->netdev)); } static struct usb_function *ncm_alloc(struct usb_function_instance *fi) { struct f_ncm *ncm; struct f_ncm_opts *opts; + int status; /* allocate and initialize one new instance */ ncm = kzalloc(sizeof(*ncm), GFP_KERNEL); @@ -1758,12 +1750,22 @@ static struct usb_function *ncm_alloc(st return ERR_PTR(-ENOMEM); opts = container_of(fi, struct f_ncm_opts, func_inst); + mutex_lock(&opts->lock); + opts->refcnt++; - scoped_guard(mutex, &opts->lock) - opts->refcnt++; + /* export host's Ethernet address in CDC format */ + status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr, + sizeof(ncm->ethaddr)); + if (status < 12) { /* strlen("01234567890a") */ + kfree(ncm); + mutex_unlock(&opts->lock); + return ERR_PTR(-EINVAL); + } spin_lock_init(&ncm->lock); ncm_reset_values(ncm); + ncm->port.ioport = netdev_priv(opts->net); + mutex_unlock(&opts->lock); ncm->port.is_fixed = true; ncm->port.supports_multi_frame = true; --- a/drivers/usb/gadget/function/u_ncm.h +++ b/drivers/usb/gadget/function/u_ncm.h @@ -15,13 +15,11 @@ #include -#include "u_ether.h" - struct f_ncm_opts { struct usb_function_instance func_inst; struct net_device *net; + bool bound; - struct gether_opts net_opts; struct config_group *ncm_interf_group; struct usb_os_desc ncm_os_desc; char ncm_ext_compat_id[16];