Skip to content

Commit d9270c9

Browse files
Kuen-Han Tsaigregkh
authored andcommitted
usb: gadget: f_eem: Fix net_device lifecycle with device_move
The net_device is allocated during function instance creation and registered during the bind phase with the gadget device as its sysfs parent. When the function unbinds, the parent device is destroyed, but the net_device survives, resulting in dangling sysfs symlinks: console:/ # ls -l /sys/class/net/usb0 lrwxrwxrwx ... /sys/class/net/usb0 -> /sys/devices/platform/.../gadget.0/net/usb0 console:/ # ls -l /sys/devices/platform/.../gadget.0/net/usb0 ls: .../gadget.0/net/usb0: No such file or directory Use device_move() to reparent the net_device between the gadget device tree and /sys/devices/virtual across bind and unbind cycles. During the final unbind, calling device_move(NULL) moves the net_device to the virtual device tree before the gadget device is destroyed. On rebinding, device_move() reparents the device back under the new gadget, ensuring proper sysfs topology and power management ordering. To maintain compatibility with legacy composite drivers (e.g., multi.c), the bound flag is used to indicate whether the network device is shared and pre-registered during the legacy driver's bind phase. Fixes: b29002a ("usb: gadget: f_eem: convert to new function interface with backward compatibility") Cc: stable@vger.kernel.org Signed-off-by: Kuen-Han Tsai <khtsai@google.com> Link: https://patch.msgid.link/20260320-usb-net-lifecycle-v1-5-4886b578161b@google.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent b2cc4fa commit d9270c9

2 files changed

Lines changed: 46 additions & 34 deletions

File tree

drivers/usb/gadget/function/f_eem.c

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* Copyright (C) 2009 EF Johnson Technologies
88
*/
99

10+
#include <linux/cleanup.h>
1011
#include <linux/kernel.h>
1112
#include <linux/module.h>
1213
#include <linux/device.h>
@@ -251,24 +252,22 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f)
251252
struct usb_ep *ep;
252253

253254
struct f_eem_opts *eem_opts;
255+
struct net_device *net __free(detach_gadget) = NULL;
254256

255257
eem_opts = container_of(f->fi, struct f_eem_opts, func_inst);
256-
/*
257-
* in drivers/usb/gadget/configfs.c:configfs_composite_bind()
258-
* configurations are bound in sequence with list_for_each_entry,
259-
* in each configuration its functions are bound in sequence
260-
* with list_for_each_entry, so we assume no race condition
261-
* with regard to eem_opts->bound access
262-
*/
263-
if (!eem_opts->bound) {
264-
mutex_lock(&eem_opts->lock);
265-
gether_set_gadget(eem_opts->net, cdev->gadget);
266-
status = gether_register_netdev(eem_opts->net);
267-
mutex_unlock(&eem_opts->lock);
268-
if (status)
269-
return status;
270-
eem_opts->bound = true;
271-
}
258+
259+
scoped_guard(mutex, &eem_opts->lock)
260+
if (eem_opts->bind_count == 0 && !eem_opts->bound) {
261+
if (!device_is_registered(&eem_opts->net->dev)) {
262+
gether_set_gadget(eem_opts->net, cdev->gadget);
263+
status = gether_register_netdev(eem_opts->net);
264+
} else
265+
status = gether_attach_gadget(eem_opts->net, cdev->gadget);
266+
267+
if (status)
268+
return status;
269+
net = eem_opts->net;
270+
}
272271

273272
us = usb_gstrings_attach(cdev, eem_strings,
274273
ARRAY_SIZE(eem_string_defs));
@@ -279,21 +278,19 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f)
279278
/* allocate instance-specific interface IDs */
280279
status = usb_interface_id(c, f);
281280
if (status < 0)
282-
goto fail;
281+
return status;
283282
eem->ctrl_id = status;
284283
eem_intf.bInterfaceNumber = status;
285284

286-
status = -ENODEV;
287-
288285
/* allocate instance-specific endpoints */
289286
ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_in_desc);
290287
if (!ep)
291-
goto fail;
288+
return -ENODEV;
292289
eem->port.in_ep = ep;
293290

294291
ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_out_desc);
295292
if (!ep)
296-
goto fail;
293+
return -ENODEV;
297294
eem->port.out_ep = ep;
298295

299296
/* support all relevant hardware speeds... we expect that when
@@ -309,16 +306,14 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f)
309306
status = usb_assign_descriptors(f, eem_fs_function, eem_hs_function,
310307
eem_ss_function, eem_ss_function);
311308
if (status)
312-
goto fail;
309+
return status;
310+
311+
eem_opts->bind_count++;
312+
retain_and_null_ptr(net);
313313

314314
DBG(cdev, "CDC Ethernet (EEM): IN/%s OUT/%s\n",
315315
eem->port.in_ep->name, eem->port.out_ep->name);
316316
return 0;
317-
318-
fail:
319-
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
320-
321-
return status;
322317
}
323318

324319
static void eem_cmd_complete(struct usb_ep *ep, struct usb_request *req)
@@ -597,7 +592,7 @@ static void eem_free_inst(struct usb_function_instance *f)
597592
struct f_eem_opts *opts;
598593

599594
opts = container_of(f, struct f_eem_opts, func_inst);
600-
if (opts->bound)
595+
if (device_is_registered(&opts->net->dev))
601596
gether_cleanup(netdev_priv(opts->net));
602597
else
603598
free_netdev(opts->net);
@@ -640,9 +635,17 @@ static void eem_free(struct usb_function *f)
640635

641636
static void eem_unbind(struct usb_configuration *c, struct usb_function *f)
642637
{
638+
struct f_eem_opts *opts;
639+
643640
DBG(c->cdev, "eem unbind\n");
644641

642+
opts = container_of(f->fi, struct f_eem_opts, func_inst);
643+
645644
usb_free_all_descriptors(f);
645+
646+
opts->bind_count--;
647+
if (opts->bind_count == 0 && !opts->bound)
648+
gether_detach_gadget(opts->net);
646649
}
647650

648651
static struct usb_function *eem_alloc(struct usb_function_instance *fi)

drivers/usb/gadget/function/u_eem.h

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,26 @@
1515

1616
#include <linux/usb/composite.h>
1717

18+
/**
19+
* struct f_eem_opts - EEM function options
20+
* @func_inst: USB function instance.
21+
* @net: The net_device associated with the EEM function.
22+
* @bound: True if the net_device is shared and pre-registered during the
23+
* legacy composite driver's bind phase (e.g., multi.c). If false,
24+
* the EEM function will register the net_device during its own
25+
* bind phase.
26+
* @bind_count: Tracks the number of configurations the EEM function is
27+
* bound to, preventing double-registration of the @net device.
28+
* @lock: Protects the data from concurrent access by configfs read/write
29+
* and create symlink/remove symlink operations.
30+
* @refcnt: Reference counter for the function instance.
31+
*/
1832
struct f_eem_opts {
1933
struct usb_function_instance func_inst;
2034
struct net_device *net;
2135
bool bound;
36+
int bind_count;
2237

23-
/*
24-
* Read/write access to configfs attributes is handled by configfs.
25-
*
26-
* This is to protect the data from concurrent access by read/write
27-
* and create symlink/remove symlink.
28-
*/
2938
struct mutex lock;
3039
int refcnt;
3140
};

0 commit comments

Comments
 (0)