Module Name: src Committed By: riastradh Date: Thu Mar 3 05:53:23 UTC 2022
Modified Files: src/sys/dev/usb: if_aue.c if_axe.c if_axen.c if_smsc.c if_udav.c if_ure.c if_url.c usbnet.c Log Message: usbnet: Apply hardware multicast filter updates synchronously again. To make this work: 1. Do it only under a new lock, unp_mcastlock. This lock lives at IPL_SOFTCLOCK so it can be taken from network stack callouts. It is forbidden to acquire the usbnet core lock under unp_mcastlock. 2. Do it only after usbnet_init_rx_tx and before usbnet_stop; if issued at any other time, drop the update on the floor. 3. Make usbnet_init_rx_tx apply any pending multicast filter updates under the lock before setting the flag that allows SIOCADDMULTI or SIOCDELMULTI to apply the updates. 4. Remove core lock asserts from various drivers' register access routines. This is necessary because the multicast filter updates are done with register reads/writes, but _cannot_ take the core lock when the caller holds softnet_lock. This now programs the hardware multicast filter redundantly in many drivers which already explicitly call *_uno_mcast from the *_uno_init routines. This is probably harmless, but it will likely be better to remove the explicit calls. To generate a diff of this commit: cvs rdiff -u -r1.180 -r1.181 src/sys/dev/usb/if_aue.c cvs rdiff -u -r1.141 -r1.142 src/sys/dev/usb/if_axe.c cvs rdiff -u -r1.83 -r1.84 src/sys/dev/usb/if_axen.c cvs rdiff -u -r1.82 -r1.83 src/sys/dev/usb/if_smsc.c cvs rdiff -u -r1.87 -r1.88 src/sys/dev/usb/if_udav.c src/sys/dev/usb/if_url.c cvs rdiff -u -r1.48 -r1.49 src/sys/dev/usb/if_ure.c cvs rdiff -u -r1.81 -r1.82 src/sys/dev/usb/usbnet.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/usb/if_aue.c diff -u src/sys/dev/usb/if_aue.c:1.180 src/sys/dev/usb/if_aue.c:1.181 --- src/sys/dev/usb/if_aue.c:1.180 Thu Mar 3 05:53:14 2022 +++ src/sys/dev/usb/if_aue.c Thu Mar 3 05:53:23 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: if_aue.c,v 1.180 2022/03/03 05:53:14 riastradh Exp $ */ +/* $NetBSD: if_aue.c,v 1.181 2022/03/03 05:53:23 riastradh Exp $ */ /* * Copyright (c) 1997, 1998, 1999, 2000 @@ -76,7 +76,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_aue.c,v 1.180 2022/03/03 05:53:14 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_aue.c,v 1.181 2022/03/03 05:53:23 riastradh Exp $"); #ifdef _KERNEL_OPT #include "opt_usb.h" @@ -284,8 +284,6 @@ aue_csr_read_1(struct aue_softc *sc, int usbd_status err; uByte val = 0; - usbnet_isowned_core(un); - if (usbnet_isdying(un)) return 0; @@ -315,8 +313,6 @@ aue_csr_read_2(struct aue_softc *sc, int usbd_status err; uWord val; - usbnet_isowned_core(un); - if (usbnet_isdying(un)) return 0; @@ -346,8 +342,6 @@ aue_csr_write_1(struct aue_softc *sc, in usbd_status err; uByte val; - usbnet_isowned_core(un); - if (usbnet_isdying(un)) return 0; @@ -378,8 +372,6 @@ aue_csr_write_2(struct aue_softc *sc, in usbd_status err; uWord val; - usbnet_isowned_core(un); - if (usbnet_isdying(un)) return 0; @@ -624,8 +616,6 @@ aue_uno_mcast(struct ifnet *ifp) AUEHIST_FUNC(); AUEHIST_CALLARGSN(5, "aue%jd: enter", device_unit(un->un_dev), 0, 0, 0); - usbnet_isowned_core(un); - if (ifp->if_flags & IFF_PROMISC) { ETHER_LOCK(ec); allmulti: Index: src/sys/dev/usb/if_axe.c diff -u src/sys/dev/usb/if_axe.c:1.141 src/sys/dev/usb/if_axe.c:1.142 --- src/sys/dev/usb/if_axe.c:1.141 Thu Mar 3 05:53:04 2022 +++ src/sys/dev/usb/if_axe.c Thu Mar 3 05:53:23 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: if_axe.c,v 1.141 2022/03/03 05:53:04 riastradh Exp $ */ +/* $NetBSD: if_axe.c,v 1.142 2022/03/03 05:53:23 riastradh Exp $ */ /* $OpenBSD: if_axe.c,v 1.137 2016/04/13 11:03:37 mpi Exp $ */ /* @@ -87,7 +87,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_axe.c,v 1.141 2022/03/03 05:53:04 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_axe.c,v 1.142 2022/03/03 05:53:23 riastradh Exp $"); #ifdef _KERNEL_OPT #include "opt_usb.h" @@ -293,8 +293,6 @@ axe_cmd(struct axe_softc *sc, int cmd, i usb_device_request_t req; usbd_status err; - usbnet_isowned_core(un); - if (usbnet_isdying(un)) return -1; Index: src/sys/dev/usb/if_axen.c diff -u src/sys/dev/usb/if_axen.c:1.83 src/sys/dev/usb/if_axen.c:1.84 --- src/sys/dev/usb/if_axen.c:1.83 Thu Mar 3 05:53:04 2022 +++ src/sys/dev/usb/if_axen.c Thu Mar 3 05:53:23 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: if_axen.c,v 1.83 2022/03/03 05:53:04 riastradh Exp $ */ +/* $NetBSD: if_axen.c,v 1.84 2022/03/03 05:53:23 riastradh Exp $ */ /* $OpenBSD: if_axen.c,v 1.3 2013/10/21 10:10:22 yuo Exp $ */ /* @@ -23,7 +23,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_axen.c,v 1.83 2022/03/03 05:53:04 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_axen.c,v 1.84 2022/03/03 05:53:23 riastradh Exp $"); #ifdef _KERNEL_OPT #include "opt_usb.h" @@ -108,8 +108,6 @@ axen_cmd(struct usbnet *un, int cmd, int usb_device_request_t req; usbd_status err; - usbnet_isowned_core(un); - if (usbnet_isdying(un)) return 0; @@ -239,8 +237,6 @@ axen_uno_mcast(struct ifnet *ifp) if (usbnet_isdying(un)) return; - usbnet_isowned_core(un); - rxmode = 0; /* Enable receiver, set RX mode */ Index: src/sys/dev/usb/if_smsc.c diff -u src/sys/dev/usb/if_smsc.c:1.82 src/sys/dev/usb/if_smsc.c:1.83 --- src/sys/dev/usb/if_smsc.c:1.82 Thu Mar 3 05:53:14 2022 +++ src/sys/dev/usb/if_smsc.c Thu Mar 3 05:53:23 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: if_smsc.c,v 1.82 2022/03/03 05:53:14 riastradh Exp $ */ +/* $NetBSD: if_smsc.c,v 1.83 2022/03/03 05:53:23 riastradh Exp $ */ /* $OpenBSD: if_smsc.c,v 1.4 2012/09/27 12:38:11 jsg Exp $ */ /* $FreeBSD: src/sys/dev/usb/net/if_smsc.c,v 1.1 2012/08/15 04:03:55 gonzo Exp $ */ @@ -61,7 +61,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_smsc.c,v 1.82 2022/03/03 05:53:14 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_smsc.c,v 1.83 2022/03/03 05:53:23 riastradh Exp $"); #ifdef _KERNEL_OPT #include "opt_usb.h" @@ -211,8 +211,6 @@ smsc_readreg(struct usbnet *un, uint32_t uint32_t buf; usbd_status err; - usbnet_isowned_core(un); - if (usbnet_isdying(un)) return 0; @@ -238,8 +236,6 @@ smsc_writereg(struct usbnet *un, uint32_ uint32_t buf; usbd_status err; - usbnet_isowned_core(un); - if (usbnet_isdying(un)) return 0; @@ -422,8 +418,6 @@ smsc_uno_mcast(struct ifnet *ifp) uint32_t hashtbl[2] = { 0, 0 }; uint32_t hash; - usbnet_isowned_core(un); - if (usbnet_isdying(un)) return; Index: src/sys/dev/usb/if_udav.c diff -u src/sys/dev/usb/if_udav.c:1.87 src/sys/dev/usb/if_udav.c:1.88 --- src/sys/dev/usb/if_udav.c:1.87 Thu Mar 3 05:53:14 2022 +++ src/sys/dev/usb/if_udav.c Thu Mar 3 05:53:23 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: if_udav.c,v 1.87 2022/03/03 05:53:14 riastradh Exp $ */ +/* $NetBSD: if_udav.c,v 1.88 2022/03/03 05:53:23 riastradh Exp $ */ /* $nabe: if_udav.c,v 1.3 2003/08/21 16:57:19 nabe Exp $ */ /* @@ -45,7 +45,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_udav.c,v 1.87 2022/03/03 05:53:14 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_udav.c,v 1.88 2022/03/03 05:53:23 riastradh Exp $"); #ifdef _KERNEL_OPT #include "opt_usb.h" @@ -364,7 +364,6 @@ udav_csr_read(struct usbnet *un, int off usb_device_request_t req; usbd_status err; - usbnet_isowned_core(un); KASSERT(!usbnet_isdying(un)); DPRINTFN(0x200, @@ -395,7 +394,6 @@ udav_csr_write(struct usbnet *un, int of usb_device_request_t req; usbd_status err; - usbnet_isowned_core(un); KASSERT(!usbnet_isdying(un)); DPRINTFN(0x200, @@ -424,8 +422,6 @@ udav_csr_read1(struct usbnet *un, int of { uint8_t val = 0; - usbnet_isowned_core(un); - DPRINTFN(0x200, ("%s: %s: enter\n", device_xname(un->un_dev), __func__)); @@ -442,7 +438,6 @@ udav_csr_write1(struct usbnet *un, int o usb_device_request_t req; usbd_status err; - usbnet_isowned_core(un); KASSERT(!usbnet_isdying(un)); DPRINTFN(0x200, @@ -586,8 +581,6 @@ udav_uno_mcast(struct ifnet *ifp) DPRINTF(("%s: %s: enter\n", device_xname(un->un_dev), __func__)); - usbnet_isowned_core(un); - if (usbnet_isdying(un)) return; Index: src/sys/dev/usb/if_url.c diff -u src/sys/dev/usb/if_url.c:1.87 src/sys/dev/usb/if_url.c:1.88 --- src/sys/dev/usb/if_url.c:1.87 Thu Mar 3 05:53:04 2022 +++ src/sys/dev/usb/if_url.c Thu Mar 3 05:53:23 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: if_url.c,v 1.87 2022/03/03 05:53:04 riastradh Exp $ */ +/* $NetBSD: if_url.c,v 1.88 2022/03/03 05:53:23 riastradh Exp $ */ /* * Copyright (c) 2001, 2002 @@ -44,7 +44,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_url.c,v 1.87 2022/03/03 05:53:04 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_url.c,v 1.88 2022/03/03 05:53:23 riastradh Exp $"); #ifdef _KERNEL_OPT #include "opt_inet.h" @@ -268,8 +268,6 @@ url_mem(struct usbnet *un, int cmd, int usb_device_request_t req; usbd_status err; - usbnet_isowned_core(un); - DPRINTFN(0x200, ("%s: %s: enter\n", device_xname(un->un_dev), __func__)); @@ -435,8 +433,6 @@ url_uno_mcast(struct ifnet *ifp) DPRINTF(("%s: %s: enter\n", device_xname(un->un_dev), __func__)); - usbnet_isowned_core(un); - if (usbnet_isdying(un)) return; Index: src/sys/dev/usb/if_ure.c diff -u src/sys/dev/usb/if_ure.c:1.48 src/sys/dev/usb/if_ure.c:1.49 --- src/sys/dev/usb/if_ure.c:1.48 Thu Mar 3 05:53:04 2022 +++ src/sys/dev/usb/if_ure.c Thu Mar 3 05:53:23 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: if_ure.c,v 1.48 2022/03/03 05:53:04 riastradh Exp $ */ +/* $NetBSD: if_ure.c,v 1.49 2022/03/03 05:53:23 riastradh Exp $ */ /* $OpenBSD: if_ure.c,v 1.10 2018/11/02 21:32:30 jcs Exp $ */ /*- @@ -30,7 +30,7 @@ /* RealTek RTL8152/RTL8153 10/100/Gigabit USB Ethernet device */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_ure.c,v 1.48 2022/03/03 05:53:04 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_ure.c,v 1.49 2022/03/03 05:53:23 riastradh Exp $"); #ifdef _KERNEL_OPT #include "opt_usb.h" @@ -340,8 +340,6 @@ ure_uno_mcast(struct ifnet *ifp) uint32_t mchash[2] = { 0, 0 }; uint32_t h = 0, rxmode; - usbnet_isowned_core(un); - if (usbnet_isdying(un)) return; Index: src/sys/dev/usb/usbnet.c diff -u src/sys/dev/usb/usbnet.c:1.81 src/sys/dev/usb/usbnet.c:1.82 --- src/sys/dev/usb/usbnet.c:1.81 Thu Mar 3 05:52:46 2022 +++ src/sys/dev/usb/usbnet.c Thu Mar 3 05:53:23 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: usbnet.c,v 1.81 2022/03/03 05:52:46 riastradh Exp $ */ +/* $NetBSD: usbnet.c,v 1.82 2022/03/03 05:53:23 riastradh Exp $ */ /* * Copyright (c) 2019 Matthew R. Green @@ -31,7 +31,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: usbnet.c,v 1.81 2022/03/03 05:52:46 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: usbnet.c,v 1.82 2022/03/03 05:53:23 riastradh Exp $"); #include <sys/param.h> #include <sys/kernel.h> @@ -59,6 +59,7 @@ struct usbnet_private { * * the lock ordering is: * ifnet lock -> unp_core_lock -> unp_rxlock -> unp_txlock + * -> unp_mcastlock * - ifnet lock is not needed for unp_core_lock, but if ifnet lock is * involved, it must be taken first */ @@ -66,11 +67,13 @@ struct usbnet_private { kmutex_t unp_rxlock; kmutex_t unp_txlock; + kmutex_t unp_mcastlock; + bool unp_mcastactive; + struct usbnet_cdata unp_cdata; struct ethercom unp_ec; struct mii_data unp_mii; - struct usb_task unp_mcasttask; struct usb_task unp_ticktask; struct callout unp_stat_ch; struct usbd_pipe *unp_ep[USBNET_ENDPT_MAX]; @@ -866,6 +869,17 @@ usbnet_init_rx_tx(struct usbnet * const "%s", ifp->if_xname); ifp->if_flags |= IFF_RUNNING; + /* + * If the hardware has a multicast filter, program it and then + * allow updates to it while we're running. + */ + if (un->un_ops->uno_mcast) { + mutex_enter(&unp->unp_mcastlock); + (*un->un_ops->uno_mcast)(ifp); + unp->unp_mcastactive = true; + mutex_exit(&unp->unp_mcastlock); + } + /* Start up the receive pipe(s). */ usbnet_rx_start_pipes(un); @@ -1018,8 +1032,20 @@ usbnet_if_ioctl(struct ifnet *ifp, u_lon switch (cmd) { case SIOCADDMULTI: case SIOCDELMULTI: - usb_add_task(un->un_udev, &unp->unp_mcasttask, - USB_TASKQ_DRIVER); + /* + * If there's a hardware multicast filter, and + * it has been programmed by usbnet_init_rx_tx + * and is active, update it now. Otherwise, + * drop the update on the floor -- it will be + * observed by usbnet_init_rx_tx next time we + * bring the interface up. + */ + if (un->un_ops->uno_mcast) { + mutex_enter(&unp->unp_mcastlock); + if (unp->unp_mcastactive) + (*un->un_ops->uno_mcast)(ifp); + mutex_exit(&unp->unp_mcastlock); + } error = 0; break; default: @@ -1030,45 +1056,6 @@ usbnet_if_ioctl(struct ifnet *ifp, u_lon return error; } -static void -usbnet_mcast_task(void *arg) -{ - USBNETHIST_FUNC(); - struct usbnet * const un = arg; - struct ifnet * const ifp = usbnet_ifp(un); - - USBNETHIST_CALLARGSN(10, "%jd: enter", - un->un_pri->unp_number, 0, 0, 0); - - /* - * If we're detaching, we must check usbnet_isdying _before_ - * touching IFNET_LOCK -- the ifnet may have been detached by - * the time this task runs. This is racy -- unp_dying may be - * set immediately after we test it -- but nevertheless safe, - * because usbnet_detach waits for the task to complete before - * issuing if_detach, and necessary, so that we don't touch - * IFNET_LOCK after if_detach. See usbnet_detach for details. - */ - if (usbnet_isdying(un)) - return; - - /* - * If the hardware is running, ask the driver to reprogram the - * multicast filter. If the hardware is not running, the - * driver is responsible for programming the multicast filter - * as part of its uno_init routine to bring the hardware up. - */ - IFNET_LOCK(ifp); - if (ifp->if_flags & IFF_RUNNING) { - if (un->un_ops->uno_mcast) { - mutex_enter(&un->un_pri->unp_core_lock); - (*un->un_ops->uno_mcast)(ifp); - mutex_exit(&un->un_pri->unp_core_lock); - } - } - IFNET_UNLOCK(ifp); -} - /* * Generic stop network function: * - mark as stopping @@ -1094,6 +1081,18 @@ usbnet_stop(struct usbnet *un, struct if usbnet_isowned_core(un); /* + * For drivers with hardware multicast filter update callbacks: + * Prevent concurrent access to the hardware registers by + * multicast filter updates, which happens without IFNET_LOCK + * or the usbnet core lock. + */ + if (un->un_ops->uno_mcast) { + mutex_enter(&unp->unp_mcastlock); + unp->unp_mcastactive = false; + mutex_exit(&unp->unp_mcastlock); + } + + /* * Prevent new activity (rescheduling ticks, xfers, &c.) and * clear the watchdog timer. */ @@ -1387,8 +1386,6 @@ usbnet_attach(struct usbnet *un, un->un_pri = kmem_zalloc(sizeof(*un->un_pri), KM_SLEEP); struct usbnet_private * const unp = un->un_pri; - usb_init_task(&unp->unp_mcasttask, usbnet_mcast_task, un, - USB_TASKQ_MPSAFE); usb_init_task(&unp->unp_ticktask, usbnet_tick_task, un, USB_TASKQ_MPSAFE); callout_init(&unp->unp_stat_ch, CALLOUT_MPSAFE); @@ -1397,6 +1394,7 @@ usbnet_attach(struct usbnet *un, mutex_init(&unp->unp_txlock, MUTEX_DEFAULT, IPL_SOFTUSB); mutex_init(&unp->unp_rxlock, MUTEX_DEFAULT, IPL_SOFTUSB); mutex_init(&unp->unp_core_lock, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&unp->unp_mcastlock, MUTEX_DEFAULT, IPL_SOFTCLOCK); rnd_attach_source(&unp->unp_rndsrc, device_xname(un->un_dev), RND_TYPE_NET, RND_FLAG_DEFAULT); @@ -1539,9 +1537,6 @@ usbnet_detach(device_t self, int flags) KASSERT(!callout_pending(&unp->unp_stat_ch)); KASSERT(!usb_task_pending(un->un_udev, &unp->unp_ticktask)); - usb_rem_task_wait(un->un_udev, &unp->unp_mcasttask, USB_TASKQ_DRIVER, - NULL); - if (mii) { mii_detach(mii, MII_PHY_ANY, MII_OFFSET_ANY); ifmedia_fini(&mii->mii_media); @@ -1555,45 +1550,12 @@ usbnet_detach(device_t self, int flags) } usbnet_ec(un)->ec_mii = NULL; - /* - * We have already waited for the multicast task to complete. - * Unfortunately, until if_detach, nothing has prevented it - * from running again -- another thread might issue if_mcast_op - * between the time of our first usb_rem_task_wait and the time - * we actually get around to if_detach. - * - * Fortunately, the first usb_rem_task_wait ensures that if the - * task is scheduled again, it will witness our setting of - * unp_dying to true[*]. So after that point, if the task is - * scheduled again, it will decline to touch IFNET_LOCK and do - * nothing. But we still need to wait for it to complete. - * - * It would be nice if we could write - * - * if_pleasestopissuingmcastopsthanks(ifp); - * usb_rem_task_wait(..., &unp->unp_mcasttask, ...); - * if_detach(ifp); - * - * and then we would need only one usb_rem_task_wait. - * - * Unfortunately, there is no such operation available in - * sys/net at the moment, and it would require a bit of - * coordination with if_mcast_op and doifioctl probably under a - * new lock. So we'll use this kludge until that mechanism is - * invented. - * - * [*] This is not exactly a documented property of the API, - * but it is implied by the single lock in the task queue - * serializing changes to the task state. - */ - usb_rem_task_wait(un->un_udev, &unp->unp_mcasttask, USB_TASKQ_DRIVER, - NULL); - usbnet_rx_list_free(un); usbnet_tx_list_free(un); rnd_detach_source(&unp->unp_rndsrc); + mutex_destroy(&unp->unp_mcastlock); mutex_destroy(&unp->unp_core_lock); mutex_destroy(&unp->unp_rxlock); mutex_destroy(&unp->unp_txlock);