Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Revert "usb: gadget: f_ncm: align net_device lifecycle with bind/unbind"

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 <david@ixit.cz>
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 <stable@kernel.org>
Signed-off-by: Kuen-Han Tsai <khtsai@google.com>
Link: https://patch.msgid.link/20260309-f-ncm-revert-v2-3-ea2afbc7d9b2@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Kuen-Han Tsai and committed by
Greg Kroah-Hartman
37893bc5 f2524c0e

+66 -66
+65 -63
drivers/usb/gadget/function/f_ncm.c
··· 83 83 return container_of(f, struct f_ncm, port.func); 84 84 } 85 85 86 - static inline struct f_ncm_opts *func_to_ncm_opts(struct usb_function *f) 87 - { 88 - return container_of(f->fi, struct f_ncm_opts, func_inst); 89 - } 90 - 91 86 /*-------------------------------------------------------------------------*/ 92 87 93 88 /* ··· 859 864 static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) 860 865 { 861 866 struct f_ncm *ncm = func_to_ncm(f); 862 - struct f_ncm_opts *opts = func_to_ncm_opts(f); 863 867 struct usb_composite_dev *cdev = f->config->cdev; 864 868 865 869 /* Control interface has only altsetting 0 */ ··· 881 887 if (alt > 1) 882 888 goto fail; 883 889 884 - scoped_guard(mutex, &opts->lock) 885 - if (opts->net) { 886 - DBG(cdev, "reset ncm\n"); 887 - opts->net = NULL; 888 - gether_disconnect(&ncm->port); 889 - ncm_reset_values(ncm); 890 - } 890 + if (ncm->netdev) { 891 + DBG(cdev, "reset ncm\n"); 892 + ncm->netdev = NULL; 893 + gether_disconnect(&ncm->port); 894 + ncm_reset_values(ncm); 895 + } 891 896 892 897 /* 893 898 * CDC Network only sends data in non-default altsettings. ··· 919 926 net = gether_connect(&ncm->port); 920 927 if (IS_ERR(net)) 921 928 return PTR_ERR(net); 922 - scoped_guard(mutex, &opts->lock) 923 - opts->net = net; 929 + ncm->netdev = net; 924 930 } 925 931 926 932 spin_lock(&ncm->lock); ··· 1366 1374 static void ncm_disable(struct usb_function *f) 1367 1375 { 1368 1376 struct f_ncm *ncm = func_to_ncm(f); 1369 - struct f_ncm_opts *opts = func_to_ncm_opts(f); 1370 1377 struct usb_composite_dev *cdev = f->config->cdev; 1371 1378 1372 1379 DBG(cdev, "ncm deactivated\n"); 1373 1380 1374 - scoped_guard(mutex, &opts->lock) 1375 - if (opts->net) { 1376 - opts->net = NULL; 1377 - gether_disconnect(&ncm->port); 1378 - } 1381 + if (ncm->netdev) { 1382 + ncm->netdev = NULL; 1383 + gether_disconnect(&ncm->port); 1384 + } 1379 1385 1380 1386 if (ncm->notify->enabled) { 1381 1387 usb_ep_disable(ncm->notify); ··· 1433 1443 { 1434 1444 struct usb_composite_dev *cdev = c->cdev; 1435 1445 struct f_ncm *ncm = func_to_ncm(f); 1436 - struct f_ncm_opts *ncm_opts = func_to_ncm_opts(f); 1437 1446 struct usb_string *us; 1438 1447 int status = 0; 1439 1448 struct usb_ep *ep; 1449 + struct f_ncm_opts *ncm_opts; 1440 1450 1441 1451 struct usb_os_desc_table *os_desc_table __free(kfree) = NULL; 1442 - struct net_device *netdev __free(free_gether_netdev) = NULL; 1443 1452 struct usb_request *request __free(free_usb_request) = NULL; 1444 1453 1445 1454 if (!can_support_ecm(cdev->gadget)) 1446 1455 return -EINVAL; 1456 + 1457 + ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst); 1447 1458 1448 1459 if (cdev->use_os_string) { 1449 1460 os_desc_table = kzalloc(sizeof(*os_desc_table), GFP_KERNEL); ··· 1452 1461 return -ENOMEM; 1453 1462 } 1454 1463 1455 - netdev = gether_setup_default(); 1456 - if (IS_ERR(netdev)) 1457 - return -ENOMEM; 1458 - 1459 - scoped_guard(mutex, &ncm_opts->lock) { 1460 - gether_apply_opts(netdev, &ncm_opts->net_opts); 1461 - netdev->mtu = ncm_opts->max_segment_size - ETH_HLEN; 1464 + mutex_lock(&ncm_opts->lock); 1465 + gether_set_gadget(ncm_opts->net, cdev->gadget); 1466 + if (!ncm_opts->bound) { 1467 + ncm_opts->net->mtu = (ncm_opts->max_segment_size - ETH_HLEN); 1468 + status = gether_register_netdev(ncm_opts->net); 1462 1469 } 1470 + mutex_unlock(&ncm_opts->lock); 1463 1471 1464 - gether_set_gadget(netdev, cdev->gadget); 1465 - status = gether_register_netdev(netdev); 1466 1472 if (status) 1467 1473 return status; 1468 1474 1469 - /* export host's Ethernet address in CDC format */ 1470 - status = gether_get_host_addr_cdc(netdev, ncm->ethaddr, 1471 - sizeof(ncm->ethaddr)); 1472 - if (status < 12) 1473 - return -EINVAL; 1474 - ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr; 1475 + ncm_opts->bound = true; 1476 + 1477 + ncm_string_defs[1].s = ncm->ethaddr; 1475 1478 1476 1479 us = usb_gstrings_attach(cdev, ncm_strings, 1477 1480 ARRAY_SIZE(ncm_string_defs)); ··· 1563 1578 f->os_desc_n = 1; 1564 1579 } 1565 1580 ncm->notify_req = no_free_ptr(request); 1566 - ncm->netdev = no_free_ptr(netdev); 1567 - ncm->port.ioport = netdev_priv(ncm->netdev); 1568 1581 1569 1582 DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n", 1570 1583 ncm->port.in_ep->name, ncm->port.out_ep->name, ··· 1577 1594 } 1578 1595 1579 1596 /* f_ncm_item_ops */ 1580 - USB_ETHER_OPTS_ITEM(ncm); 1597 + USB_ETHERNET_CONFIGFS_ITEM(ncm); 1581 1598 1582 1599 /* f_ncm_opts_dev_addr */ 1583 - USB_ETHER_OPTS_ATTR_DEV_ADDR(ncm); 1600 + USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ncm); 1584 1601 1585 1602 /* f_ncm_opts_host_addr */ 1586 - USB_ETHER_OPTS_ATTR_HOST_ADDR(ncm); 1603 + USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ncm); 1587 1604 1588 1605 /* f_ncm_opts_qmult */ 1589 - USB_ETHER_OPTS_ATTR_QMULT(ncm); 1606 + USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm); 1590 1607 1591 1608 /* f_ncm_opts_ifname */ 1592 - USB_ETHER_OPTS_ATTR_IFNAME(ncm); 1609 + USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ncm); 1593 1610 1594 1611 static ssize_t ncm_opts_max_segment_size_show(struct config_item *item, 1595 1612 char *page) ··· 1655 1672 struct f_ncm_opts *opts; 1656 1673 1657 1674 opts = container_of(f, struct f_ncm_opts, func_inst); 1675 + if (opts->bound) 1676 + gether_cleanup(netdev_priv(opts->net)); 1677 + else 1678 + free_netdev(opts->net); 1658 1679 kfree(opts->ncm_interf_group); 1659 1680 kfree(opts); 1660 1681 } 1661 1682 1662 1683 static struct usb_function_instance *ncm_alloc_inst(void) 1663 1684 { 1664 - struct usb_function_instance *ret; 1685 + struct f_ncm_opts *opts; 1665 1686 struct usb_os_desc *descs[1]; 1666 1687 char *names[1]; 1667 1688 struct config_group *ncm_interf_group; 1668 1689 1669 - struct f_ncm_opts *opts __free(kfree) = kzalloc_obj(*opts); 1690 + opts = kzalloc_obj(*opts); 1670 1691 if (!opts) 1671 1692 return ERR_PTR(-ENOMEM); 1672 - 1673 - opts->net = NULL; 1674 1693 opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id; 1675 - gether_setup_opts_default(&opts->net_opts, "usb"); 1676 1694 1677 1695 mutex_init(&opts->lock); 1678 1696 opts->func_inst.free_func_inst = ncm_free_inst; 1697 + opts->net = gether_setup_default(); 1698 + if (IS_ERR(opts->net)) { 1699 + struct net_device *net = opts->net; 1700 + kfree(opts); 1701 + return ERR_CAST(net); 1702 + } 1679 1703 opts->max_segment_size = ETH_FRAME_LEN; 1680 1704 INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop); 1681 1705 ··· 1693 1703 ncm_interf_group = 1694 1704 usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs, 1695 1705 names, THIS_MODULE); 1696 - if (IS_ERR(ncm_interf_group)) 1706 + if (IS_ERR(ncm_interf_group)) { 1707 + ncm_free_inst(&opts->func_inst); 1697 1708 return ERR_CAST(ncm_interf_group); 1709 + } 1698 1710 opts->ncm_interf_group = ncm_interf_group; 1699 1711 1700 - ret = &opts->func_inst; 1701 - retain_and_null_ptr(opts); 1702 - return ret; 1712 + return &opts->func_inst; 1703 1713 } 1704 1714 1705 1715 static void ncm_free(struct usb_function *f) 1706 1716 { 1707 - struct f_ncm_opts *opts = func_to_ncm_opts(f); 1717 + struct f_ncm *ncm; 1718 + struct f_ncm_opts *opts; 1708 1719 1709 - scoped_guard(mutex, &opts->lock) 1710 - opts->refcnt--; 1711 - kfree(func_to_ncm(f)); 1720 + ncm = func_to_ncm(f); 1721 + opts = container_of(f->fi, struct f_ncm_opts, func_inst); 1722 + kfree(ncm); 1723 + mutex_lock(&opts->lock); 1724 + opts->refcnt--; 1725 + mutex_unlock(&opts->lock); 1712 1726 } 1713 1727 1714 1728 static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) ··· 1736 1742 1737 1743 kfree(ncm->notify_req->buf); 1738 1744 usb_ep_free_request(ncm->notify, ncm->notify_req); 1739 - 1740 - ncm->port.ioport = NULL; 1741 - gether_cleanup(netdev_priv(ncm->netdev)); 1742 1745 } 1743 1746 1744 1747 static struct usb_function *ncm_alloc(struct usb_function_instance *fi) 1745 1748 { 1746 1749 struct f_ncm *ncm; 1747 1750 struct f_ncm_opts *opts; 1751 + int status; 1748 1752 1749 1753 /* allocate and initialize one new instance */ 1750 1754 ncm = kzalloc(sizeof(*ncm), GFP_KERNEL); ··· 1750 1758 return ERR_PTR(-ENOMEM); 1751 1759 1752 1760 opts = container_of(fi, struct f_ncm_opts, func_inst); 1761 + mutex_lock(&opts->lock); 1762 + opts->refcnt++; 1753 1763 1754 - scoped_guard(mutex, &opts->lock) 1755 - opts->refcnt++; 1764 + /* export host's Ethernet address in CDC format */ 1765 + status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr, 1766 + sizeof(ncm->ethaddr)); 1767 + if (status < 12) { /* strlen("01234567890a") */ 1768 + kfree(ncm); 1769 + mutex_unlock(&opts->lock); 1770 + return ERR_PTR(-EINVAL); 1771 + } 1756 1772 1757 1773 spin_lock_init(&ncm->lock); 1758 1774 ncm_reset_values(ncm); 1775 + ncm->port.ioport = netdev_priv(opts->net); 1776 + mutex_unlock(&opts->lock); 1759 1777 ncm->port.is_fixed = true; 1760 1778 ncm->port.supports_multi_frame = true; 1761 1779
+1 -3
drivers/usb/gadget/function/u_ncm.h
··· 15 15 16 16 #include <linux/usb/composite.h> 17 17 18 - #include "u_ether.h" 19 - 20 18 struct f_ncm_opts { 21 19 struct usb_function_instance func_inst; 22 20 struct net_device *net; 21 + bool bound; 23 22 24 - struct gether_opts net_opts; 25 23 struct config_group *ncm_interf_group; 26 24 struct usb_os_desc ncm_os_desc; 27 25 char ncm_ext_compat_id[16];