2022-03-03 08:49:44 +03:00
|
|
|
/* $NetBSD: usbnet.c,v 1.67 2022/03/03 05:49:44 riastradh Exp $ */
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2019 Matthew R. Green
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2019-08-12 02:55:43 +03:00
|
|
|
* Common code shared between USB network drivers.
|
2019-07-31 12:13:16 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
2022-03-03 08:49:44 +03:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: usbnet.c,v 1.67 2022/03/03 05:49:44 riastradh Exp $");
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/kmem.h>
|
|
|
|
#include <sys/module.h>
|
2019-08-23 07:29:28 +03:00
|
|
|
#include <sys/atomic.h>
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
#include <dev/usb/usbnet.h>
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
#include <dev/usb/usbhist.h>
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata {
|
|
|
|
struct usbnet_chain *uncd_tx_chain;
|
|
|
|
struct usbnet_chain *uncd_rx_chain;
|
|
|
|
|
|
|
|
int uncd_tx_prod;
|
|
|
|
int uncd_tx_cnt;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct usbnet_private {
|
|
|
|
/*
|
2020-03-16 02:04:50 +03:00
|
|
|
* - unp_core_lock protects most of this structure, the public one,
|
|
|
|
* and the MII / media data.
|
2019-08-10 05:17:36 +03:00
|
|
|
* - unp_rxlock protects the rx path and its data
|
|
|
|
* - unp_txlock protects the tx path and its data
|
|
|
|
* - unp_detachcv handles detach vs open references
|
2019-09-09 10:20:16 +03:00
|
|
|
*
|
|
|
|
* the lock ordering is:
|
2020-03-16 02:04:50 +03:00
|
|
|
* ifnet lock -> unp_core_lock -> unp_rxlock -> unp_txlock
|
|
|
|
* - ifnet lock is not needed for unp_core_lock, but if ifnet lock is
|
2019-11-06 10:30:59 +03:00
|
|
|
* involved, it must be taken first
|
2019-08-10 05:17:36 +03:00
|
|
|
*/
|
2020-03-16 02:04:50 +03:00
|
|
|
kmutex_t unp_core_lock;
|
2019-08-10 05:17:36 +03:00
|
|
|
kmutex_t unp_rxlock;
|
|
|
|
kmutex_t unp_txlock;
|
|
|
|
kcondvar_t unp_detachcv;
|
|
|
|
|
|
|
|
struct usbnet_cdata unp_cdata;
|
|
|
|
|
|
|
|
struct ethercom unp_ec;
|
|
|
|
struct mii_data unp_mii;
|
usbnet: Defer hardware multicast filter updates to USB task.
Breaks deadlock:
- usbnet_detach holds usbnet lock, awaits kpause in ure_reset
- callout holds softclock `lock' (sequential softints, blocks kpause
wakeup), awaits softnet_lock in tcp_timer_keep, frag6_fasttimo, &c.
- soclose holds softnet_lock, awaits usbnet lock in SIOCDELMULTI
This change breaks the deadlock by not passing the SIOCADDMULTI or
SIOCDELMULTI ioctl synchronously to the driver, which typically takes
the usbnet lock.
With this change, the ethernet layer still maintains the list of
multicast addresses synchronously, but we defer the driver logic that
updates the hardware multicast filter to an asynchronous USB task
without softnet_lock held.
This doesn't cause exactly the same ioctl to be sent to the driver --
usbnet just sends SIOCDELMULTI with an all-zero struct ifreq, and
might drop some ioctls if issued in quick succession. This is OK
because none of the drivers actually distinguish between SIOCADDMULTI
and SIOCDELMULTI, or examine the argument; the drivers just commit
whatever multicast addresses are listed in the ethercom.
Other than the different ioctl submitted, there is no change to the
ABI or locking scheme of usbnet, so this is safe to pull up to
netbsd-9. This means we unfortunately can't guarantee that if a
process issues SIOCADDMULTI and then sendto, the multicast filter
update will be done by the time of the sendto -- and, more
importantly, the packets received in reply to it. But failing to
guarantee that is better than deadlocking! Later changes on HEAD
will restore the synchronous multicast filter updates with much more
extensive ABI changes and API simplifications in usbnet(9).
Proposed on tech-net:
https://mail-index.netbsd.org/tech-net/2021/12/30/msg008164.html
XXX pullup-9
2022-01-30 00:37:07 +03:00
|
|
|
struct usb_task unp_mcasttask;
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usb_task unp_ticktask;
|
|
|
|
struct callout unp_stat_ch;
|
|
|
|
struct usbd_pipe *unp_ep[USBNET_ENDPT_MAX];
|
|
|
|
|
|
|
|
bool unp_dying;
|
|
|
|
bool unp_stopping;
|
|
|
|
bool unp_attached;
|
2022-03-03 08:47:14 +03:00
|
|
|
bool unp_ifp_attached;
|
2019-08-10 05:17:36 +03:00
|
|
|
bool unp_link;
|
|
|
|
|
|
|
|
int unp_refcnt;
|
|
|
|
int unp_timer;
|
2019-09-13 10:55:05 +03:00
|
|
|
unsigned short unp_if_flags;
|
2019-08-23 07:29:28 +03:00
|
|
|
unsigned unp_number;
|
2019-08-10 05:17:36 +03:00
|
|
|
|
|
|
|
krndsource_t unp_rndsrc;
|
|
|
|
|
|
|
|
struct timeval unp_rx_notice;
|
|
|
|
struct timeval unp_tx_notice;
|
|
|
|
struct timeval unp_intr_notice;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define un_cdata(un) (&(un)->un_pri->unp_cdata)
|
|
|
|
|
2019-08-23 07:29:28 +03:00
|
|
|
volatile unsigned usbnet_number;
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
static int usbnet_modcmd(modcmd_t, void *);
|
|
|
|
|
2019-08-01 02:47:16 +03:00
|
|
|
#ifdef USB_DEBUG
|
|
|
|
#ifndef USBNET_DEBUG
|
|
|
|
#define usbnetdebug 0
|
|
|
|
#else
|
2019-09-08 21:59:32 +03:00
|
|
|
static int usbnetdebug = 0;
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
|
2019-08-01 02:47:16 +03:00
|
|
|
SYSCTL_SETUP(sysctl_hw_usbnet_setup, "sysctl hw.usbnet setup")
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
const struct sysctlnode *rnode;
|
|
|
|
const struct sysctlnode *cnode;
|
|
|
|
|
|
|
|
err = sysctl_createv(clog, 0, NULL, &rnode,
|
|
|
|
CTLFLAG_PERMANENT, CTLTYPE_NODE, "usbnet",
|
|
|
|
SYSCTL_DESCR("usbnet global controls"),
|
|
|
|
NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL);
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
/* control debugging printfs */
|
|
|
|
err = sysctl_createv(clog, 0, &rnode, &cnode,
|
|
|
|
CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
|
|
|
|
"debug", SYSCTL_DESCR("Enable debugging output"),
|
|
|
|
NULL, 0, &usbnetdebug, sizeof(usbnetdebug), CTL_CREATE, CTL_EOL);
|
|
|
|
if (err)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
return;
|
|
|
|
fail:
|
|
|
|
aprint_error("%s: sysctl_createv failed (err = %d)\n", __func__, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* USBNET_DEBUG */
|
|
|
|
#endif /* USB_DEBUG */
|
|
|
|
|
|
|
|
#define DPRINTF(FMT,A,B,C,D) USBHIST_LOGN(usbnetdebug,1,FMT,A,B,C,D)
|
|
|
|
#define DPRINTFN(N,FMT,A,B,C,D) USBHIST_LOGN(usbnetdebug,N,FMT,A,B,C,D)
|
|
|
|
#define USBNETHIST_FUNC() USBHIST_FUNC()
|
|
|
|
#define USBNETHIST_CALLED(name) USBHIST_CALLED(usbnetdebug)
|
2019-08-19 09:35:14 +03:00
|
|
|
#define USBNETHIST_CALLARGS(FMT,A,B,C,D) \
|
|
|
|
USBHIST_CALLARGS(usbnetdebug,FMT,A,B,C,D)
|
2019-08-23 07:29:28 +03:00
|
|
|
#define USBNETHIST_CALLARGSN(N,FMT,A,B,C,D) \
|
|
|
|
USBHIST_CALLARGSN(usbnetdebug,N,FMT,A,B,C,D)
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-09 04:17:33 +03:00
|
|
|
/* Callback vectors. */
|
|
|
|
|
|
|
|
static void
|
|
|
|
uno_stop(struct usbnet *un, struct ifnet *ifp, int disable)
|
|
|
|
{
|
2022-03-03 08:47:36 +03:00
|
|
|
KASSERTMSG(!un->un_pri->unp_ifp_attached || IFNET_LOCKED(ifp),
|
|
|
|
"%s", ifp->if_xname);
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_isowned_core(un);
|
2019-08-09 04:17:33 +03:00
|
|
|
if (un->un_ops->uno_stop)
|
|
|
|
(*un->un_ops->uno_stop)(ifp, disable);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
uno_ioctl(struct usbnet *un, struct ifnet *ifp, u_long cmd, void *data)
|
|
|
|
{
|
2020-03-16 02:04:50 +03:00
|
|
|
/*
|
|
|
|
* There are cases where IFNET_LOCK will not be held when we
|
|
|
|
* are called (e.g. add/delete multicast address), so we can't
|
|
|
|
* assert it.
|
|
|
|
*/
|
2019-08-09 04:17:33 +03:00
|
|
|
if (un->un_ops->uno_ioctl)
|
|
|
|
return (*un->un_ops->uno_ioctl)(ifp, cmd, data);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
uno_override_ioctl(struct usbnet *un, struct ifnet *ifp, u_long cmd, void *data)
|
|
|
|
{
|
2020-03-16 02:04:50 +03:00
|
|
|
/* See above. */
|
2019-08-09 04:17:33 +03:00
|
|
|
return (*un->un_ops->uno_override_ioctl)(ifp, cmd, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
uno_init(struct usbnet *un, struct ifnet *ifp)
|
|
|
|
{
|
2020-03-16 02:04:50 +03:00
|
|
|
KASSERT(IFNET_LOCKED(ifp));
|
2019-08-09 04:17:33 +03:00
|
|
|
return (*un->un_ops->uno_init)(ifp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
uno_read_reg(struct usbnet *un, int phy, int reg, uint16_t *val)
|
|
|
|
{
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_isowned_core(un);
|
2019-08-09 04:17:33 +03:00
|
|
|
return (*un->un_ops->uno_read_reg)(un, phy, reg, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
uno_write_reg(struct usbnet *un, int phy, int reg, uint16_t val)
|
|
|
|
{
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_isowned_core(un);
|
2019-08-09 04:17:33 +03:00
|
|
|
return (*un->un_ops->uno_write_reg)(un, phy, reg, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
uno_mii_statchg(struct usbnet *un, struct ifnet *ifp)
|
|
|
|
{
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_isowned_core(un);
|
2019-08-09 04:17:33 +03:00
|
|
|
(*un->un_ops->uno_statchg)(ifp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
uno_tx_prepare(struct usbnet *un, struct mbuf *m, struct usbnet_chain *c)
|
|
|
|
{
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_isowned_tx(un);
|
2019-08-09 04:17:33 +03:00
|
|
|
return (*un->un_ops->uno_tx_prepare)(un, m, c);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-08-15 08:52:23 +03:00
|
|
|
uno_rx_loop(struct usbnet *un, struct usbnet_chain *c, uint32_t total_len)
|
|
|
|
{
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_isowned_rx(un);
|
2019-08-15 08:52:23 +03:00
|
|
|
(*un->un_ops->uno_rx_loop)(un, c, total_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
uno_tick(struct usbnet *un)
|
2019-08-09 04:17:33 +03:00
|
|
|
{
|
2019-08-15 08:52:23 +03:00
|
|
|
if (un->un_ops->uno_tick)
|
|
|
|
(*un->un_ops->uno_tick)(un);
|
2019-08-09 04:17:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
uno_intr(struct usbnet *un, usbd_status status)
|
|
|
|
{
|
|
|
|
if (un->un_ops->uno_intr)
|
|
|
|
(*un->un_ops->uno_intr)(un, status);
|
|
|
|
}
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
/* Interrupt handling. */
|
|
|
|
|
|
|
|
static struct mbuf *
|
2019-08-16 11:38:21 +03:00
|
|
|
usbnet_newbuf(size_t buflen)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
|
|
|
struct mbuf *m;
|
|
|
|
|
2020-08-28 20:05:32 +03:00
|
|
|
if (buflen > MCLBYTES)
|
|
|
|
return NULL;
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
|
|
|
if (m == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2019-08-16 11:38:21 +03:00
|
|
|
if (buflen > MHLEN - ETHER_ALIGN) {
|
|
|
|
MCLGET(m, M_DONTWAIT);
|
|
|
|
if (!(m->m_flags & M_EXT)) {
|
|
|
|
m_freem(m);
|
|
|
|
return NULL;
|
|
|
|
}
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
m_adj(m, ETHER_ALIGN);
|
2019-08-16 11:38:21 +03:00
|
|
|
m->m_len = m->m_pkthdr.len = buflen;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* usbnet_rxeof() is designed to be the done callback for rx completion.
|
|
|
|
* it provides generic setup and finalisation, calls a different usbnet
|
|
|
|
* rx_loop callback in the middle, which can use usbnet_enqueue() to
|
2019-08-06 03:19:57 +03:00
|
|
|
* enqueue a packet for higher levels (or usbnet_input() if previously
|
|
|
|
* using if_input() path.)
|
2019-07-31 12:13:16 +03:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
usbnet_enqueue(struct usbnet * const un, uint8_t *buf, size_t buflen,
|
2019-08-06 03:19:57 +03:00
|
|
|
int csum_flags, uint32_t csum_data, int mbuf_flags)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-23 07:29:28 +03:00
|
|
|
USBNETHIST_FUNC();
|
2019-08-10 05:17:36 +03:00
|
|
|
struct ifnet * const ifp = usbnet_ifp(un);
|
2019-08-23 07:29:28 +03:00
|
|
|
struct usbnet_private * const unp __unused = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
struct mbuf *m;
|
|
|
|
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGSN(5, "%jd: enter: len=%ju csf %#jx mbf %#jx",
|
2019-08-23 07:29:28 +03:00
|
|
|
unp->unp_number, buflen, csum_flags, mbuf_flags);
|
|
|
|
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_isowned_rx(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-16 11:38:21 +03:00
|
|
|
m = usbnet_newbuf(buflen);
|
2019-07-31 12:13:16 +03:00
|
|
|
if (m == NULL) {
|
2020-03-13 21:17:40 +03:00
|
|
|
DPRINTF("%jd: no memory", unp->unp_number, 0, 0, 0);
|
2020-01-29 09:35:28 +03:00
|
|
|
if_statinc(ifp, if_ierrors);
|
2019-07-31 12:13:16 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_set_rcvif(m, ifp);
|
2019-08-06 03:19:57 +03:00
|
|
|
m->m_pkthdr.csum_flags = csum_flags;
|
|
|
|
m->m_pkthdr.csum_data = csum_data;
|
|
|
|
m->m_flags |= mbuf_flags;
|
2019-08-16 11:38:21 +03:00
|
|
|
memcpy(mtod(m, uint8_t *), buf, buflen);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
/* push the packet up */
|
|
|
|
if_percpuq_enqueue(ifp->if_percpuq, m);
|
|
|
|
}
|
|
|
|
|
2019-08-06 03:19:57 +03:00
|
|
|
void
|
|
|
|
usbnet_input(struct usbnet * const un, uint8_t *buf, size_t buflen)
|
|
|
|
{
|
2019-08-23 07:29:28 +03:00
|
|
|
USBNETHIST_FUNC();
|
2019-08-06 03:19:57 +03:00
|
|
|
struct ifnet * const ifp = usbnet_ifp(un);
|
2019-08-23 07:29:28 +03:00
|
|
|
struct usbnet_private * const unp __unused = un->un_pri;
|
2019-08-06 03:19:57 +03:00
|
|
|
struct mbuf *m;
|
|
|
|
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGSN(5, "%jd: enter: buf %#jx len %ju",
|
2019-08-23 07:29:28 +03:00
|
|
|
unp->unp_number, (uintptr_t)buf, buflen, 0);
|
|
|
|
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_isowned_rx(un);
|
2019-08-06 03:19:57 +03:00
|
|
|
|
2019-08-16 11:38:21 +03:00
|
|
|
m = usbnet_newbuf(buflen);
|
2019-08-06 03:19:57 +03:00
|
|
|
if (m == NULL) {
|
2020-01-29 09:35:28 +03:00
|
|
|
if_statinc(ifp, if_ierrors);
|
2019-08-06 03:19:57 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_set_rcvif(m, ifp);
|
|
|
|
memcpy(mtod(m, char *), buf, buflen);
|
|
|
|
|
|
|
|
/* push the packet up */
|
|
|
|
if_input(ifp, m);
|
|
|
|
}
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
/*
|
|
|
|
* A frame has been uploaded: pass the resulting mbuf chain up to
|
|
|
|
* the higher level protocols.
|
|
|
|
*/
|
|
|
|
static void
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
usbnet_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-23 07:29:28 +03:00
|
|
|
USBNETHIST_FUNC();
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_chain * const c = priv;
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = c->unc_un;
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
uint32_t total_len;
|
|
|
|
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGSN(5, "%jd: enter: status %#jx xfer %#jx",
|
2019-08-23 07:29:28 +03:00
|
|
|
unp->unp_number, status, (uintptr_t)xfer, 0);
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_enter(&unp->unp_rxlock);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
if (unp->unp_dying || unp->unp_stopping ||
|
2019-07-31 12:13:16 +03:00
|
|
|
status == USBD_INVAL || status == USBD_NOT_STARTED ||
|
2022-03-03 08:47:43 +03:00
|
|
|
status == USBD_CANCELLED)
|
2019-07-31 12:13:16 +03:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (status != USBD_NORMAL_COMPLETION) {
|
2019-08-10 05:17:36 +03:00
|
|
|
if (usbd_ratecheck(&unp->unp_rx_notice))
|
2021-03-01 20:41:00 +03:00
|
|
|
device_printf(un->un_dev, "usb errors on rx: %s\n",
|
2019-07-31 12:13:16 +03:00
|
|
|
usbd_errstr(status));
|
|
|
|
if (status == USBD_STALLED)
|
2019-08-10 05:17:36 +03:00
|
|
|
usbd_clear_endpoint_stall_async(unp->unp_ep[USBNET_ENDPT_RX]);
|
2019-07-31 12:13:16 +03:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
if (total_len > un->un_rx_bufsz) {
|
2019-07-31 12:13:16 +03:00
|
|
|
aprint_error_dev(un->un_dev,
|
|
|
|
"rxeof: too large transfer (%u > %u)\n",
|
2019-08-10 05:17:36 +03:00
|
|
|
total_len, un->un_rx_bufsz);
|
2019-07-31 12:13:16 +03:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2019-08-15 08:52:23 +03:00
|
|
|
uno_rx_loop(un, c, total_len);
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_isowned_rx(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
done:
|
2019-08-10 05:17:36 +03:00
|
|
|
if (unp->unp_dying || unp->unp_stopping)
|
2019-07-31 12:13:16 +03:00
|
|
|
goto out;
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_exit(&unp->unp_rxlock);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
/* Setup new transfer. */
|
2019-08-10 05:17:36 +03:00
|
|
|
usbd_setup_xfer(xfer, c, c->unc_buf, un->un_rx_bufsz,
|
|
|
|
un->un_rx_xfer_flags, USBD_NO_TIMEOUT, usbnet_rxeof);
|
2019-07-31 12:13:16 +03:00
|
|
|
usbd_transfer(xfer);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_exit(&unp->unp_rxlock);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
usbnet_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_chain * const c = priv;
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = c->unc_un;
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
struct ifnet * const ifp = usbnet_ifp(un);
|
|
|
|
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGSN(5, "%jd: enter: status %#jx xfer %#jx",
|
2019-08-23 07:29:28 +03:00
|
|
|
unp->unp_number, status, (uintptr_t)xfer, 0);
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_enter(&unp->unp_txlock);
|
|
|
|
if (unp->unp_stopping || unp->unp_dying) {
|
|
|
|
mutex_exit(&unp->unp_txlock);
|
2019-07-31 12:13:16 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
KASSERT(cd->uncd_tx_cnt > 0);
|
|
|
|
cd->uncd_tx_cnt--;
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
unp->unp_timer = 0;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
switch (status) {
|
|
|
|
case USBD_NOT_STARTED:
|
|
|
|
case USBD_CANCELLED:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USBD_NORMAL_COMPLETION:
|
2020-01-29 09:35:28 +03:00
|
|
|
if_statinc(ifp, if_opackets);
|
2019-07-31 12:13:16 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
2020-01-29 09:35:28 +03:00
|
|
|
if_statinc(ifp, if_oerrors);
|
2019-08-10 05:17:36 +03:00
|
|
|
if (usbd_ratecheck(&unp->unp_tx_notice))
|
2021-03-01 20:41:00 +03:00
|
|
|
device_printf(un->un_dev, "usb error on tx: %s\n",
|
2019-07-31 12:13:16 +03:00
|
|
|
usbd_errstr(status));
|
|
|
|
if (status == USBD_STALLED)
|
2019-08-10 05:17:36 +03:00
|
|
|
usbd_clear_endpoint_stall_async(unp->unp_ep[USBNET_ENDPT_TX]);
|
2019-07-31 12:13:16 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_exit(&unp->unp_txlock);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
if (status == USBD_NORMAL_COMPLETION && !IFQ_IS_EMPTY(&ifp->if_snd))
|
|
|
|
(*ifp->if_start)(ifp);
|
|
|
|
}
|
|
|
|
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
static void
|
2019-08-10 05:17:36 +03:00
|
|
|
usbnet_pipe_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
{
|
2019-09-08 21:59:32 +03:00
|
|
|
USBNETHIST_FUNC();
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet * const un = priv;
|
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
|
|
|
struct usbnet_intr * const uni = un->un_intr;
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
if (uni == NULL || unp->unp_dying || unp->unp_stopping ||
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
status == USBD_INVAL || status == USBD_NOT_STARTED ||
|
2022-03-03 08:47:50 +03:00
|
|
|
status == USBD_CANCELLED) {
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGS("%jd: uni %#jx d/s %#jx status %#jx",
|
2019-09-08 21:59:32 +03:00
|
|
|
unp->unp_number, (uintptr_t)uni,
|
|
|
|
(unp->unp_dying << 8) | unp->unp_stopping, status);
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
return;
|
2019-09-08 21:59:32 +03:00
|
|
|
}
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
|
|
|
|
if (status != USBD_NORMAL_COMPLETION) {
|
2019-08-10 05:17:36 +03:00
|
|
|
if (usbd_ratecheck(&unp->unp_intr_notice)) {
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
aprint_error_dev(un->un_dev, "usb error on intr: %s\n",
|
|
|
|
usbd_errstr(status));
|
|
|
|
}
|
|
|
|
if (status == USBD_STALLED)
|
2019-08-10 05:17:36 +03:00
|
|
|
usbd_clear_endpoint_stall_async(unp->unp_ep[USBNET_ENDPT_INTR]);
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGS("%jd: not normal status %#jx",
|
2019-09-08 21:59:32 +03:00
|
|
|
unp->unp_number, status, 0, 0);
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-08-09 04:17:33 +03:00
|
|
|
uno_intr(un, status);
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
}
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
static void
|
|
|
|
usbnet_start_locked(struct ifnet *ifp)
|
|
|
|
{
|
2019-09-08 21:59:32 +03:00
|
|
|
USBNETHIST_FUNC();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = ifp->if_softc;
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
struct mbuf *m;
|
|
|
|
unsigned length;
|
2019-09-08 21:59:32 +03:00
|
|
|
bool done_transmit = false;
|
2021-04-25 08:15:20 +03:00
|
|
|
int idx, count;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGS("%jd: tx_cnt %jd list_cnt %jd link %jd",
|
2019-09-08 21:59:32 +03:00
|
|
|
unp->unp_number, cd->uncd_tx_cnt, un->un_tx_list_cnt,
|
|
|
|
unp->unp_link);
|
|
|
|
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_isowned_tx(un);
|
2019-08-10 05:17:36 +03:00
|
|
|
KASSERT(cd->uncd_tx_cnt <= un->un_tx_list_cnt);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-15 08:52:23 +03:00
|
|
|
if (!unp->unp_link || (ifp->if_flags & IFF_RUNNING) == 0) {
|
2020-03-14 06:01:36 +03:00
|
|
|
DPRINTF("start called no link (%jx) or running (flags %jx)",
|
2019-08-15 08:52:23 +03:00
|
|
|
unp->unp_link, ifp->if_flags, 0, 0);
|
2019-07-31 12:13:16 +03:00
|
|
|
return;
|
2019-08-15 08:52:23 +03:00
|
|
|
}
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-23 07:29:28 +03:00
|
|
|
if (cd->uncd_tx_cnt == un->un_tx_list_cnt) {
|
2020-03-13 21:17:40 +03:00
|
|
|
DPRINTF("start called, tx busy (%#jx == %#jx)",
|
2019-08-23 07:29:28 +03:00
|
|
|
cd->uncd_tx_cnt, un->un_tx_list_cnt, 0, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
idx = cd->uncd_tx_prod;
|
2021-04-25 08:15:20 +03:00
|
|
|
count = 0;
|
2019-08-10 05:17:36 +03:00
|
|
|
while (cd->uncd_tx_cnt < un->un_tx_list_cnt) {
|
2019-07-31 12:13:16 +03:00
|
|
|
IFQ_POLL(&ifp->if_snd, m);
|
2019-08-23 07:29:28 +03:00
|
|
|
if (m == NULL) {
|
|
|
|
DPRINTF("start called, queue empty", 0, 0, 0, 0);
|
2019-07-31 12:13:16 +03:00
|
|
|
break;
|
2019-08-23 07:29:28 +03:00
|
|
|
}
|
2019-08-12 02:55:43 +03:00
|
|
|
KASSERT(m->m_pkthdr.len <= un->un_tx_bufsz);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-09 04:17:33 +03:00
|
|
|
struct usbnet_chain *c = &cd->uncd_tx_chain[idx];
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-09 04:17:33 +03:00
|
|
|
length = uno_tx_prepare(un, m, c);
|
2019-07-31 12:13:16 +03:00
|
|
|
if (length == 0) {
|
2019-08-23 07:29:28 +03:00
|
|
|
DPRINTF("uno_tx_prepare gave zero length", 0, 0, 0, 0);
|
2020-01-29 09:35:28 +03:00
|
|
|
if_statinc(ifp, if_oerrors);
|
2019-07-31 12:13:16 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (__predict_false(c->unc_xfer == NULL)) {
|
2019-08-23 07:29:28 +03:00
|
|
|
DPRINTF("unc_xfer is NULL", 0, 0, 0, 0);
|
2020-01-29 09:35:28 +03:00
|
|
|
if_statinc(ifp, if_oerrors);
|
2019-07-31 12:13:16 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
usbd_setup_xfer(c->unc_xfer, c, c->unc_buf, length,
|
2019-08-10 05:17:36 +03:00
|
|
|
un->un_tx_xfer_flags, 10000, usbnet_txeof);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
/* Transmit */
|
|
|
|
usbd_status err = usbd_transfer(c->unc_xfer);
|
|
|
|
if (err != USBD_IN_PROGRESS) {
|
2020-03-14 06:01:36 +03:00
|
|
|
DPRINTF("usbd_transfer on %#jx for %ju bytes: %jd",
|
2019-08-23 07:29:28 +03:00
|
|
|
(uintptr_t)c->unc_buf, length, err, 0);
|
2020-01-29 09:35:28 +03:00
|
|
|
if_statinc(ifp, if_oerrors);
|
2019-07-31 12:13:16 +03:00
|
|
|
break;
|
|
|
|
}
|
2019-09-08 21:59:32 +03:00
|
|
|
done_transmit = true;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
IFQ_DEQUEUE(&ifp->if_snd, m);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If there's a BPF listener, bounce a copy of this frame
|
|
|
|
* to him.
|
|
|
|
*/
|
|
|
|
bpf_mtap(ifp, m, BPF_D_OUT);
|
|
|
|
m_freem(m);
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
idx = (idx + 1) % un->un_tx_list_cnt;
|
2019-07-31 12:13:16 +03:00
|
|
|
cd->uncd_tx_cnt++;
|
2021-04-25 08:15:20 +03:00
|
|
|
count++;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
cd->uncd_tx_prod = idx;
|
|
|
|
|
2020-03-14 06:01:36 +03:00
|
|
|
DPRINTF("finished with start; tx_cnt %jd list_cnt %jd link %jd",
|
2019-09-08 21:59:32 +03:00
|
|
|
cd->uncd_tx_cnt, un->un_tx_list_cnt, unp->unp_link, 0);
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
/*
|
|
|
|
* Set a timeout in case the chip goes out to lunch.
|
|
|
|
*/
|
2019-09-08 21:59:32 +03:00
|
|
|
if (done_transmit)
|
|
|
|
unp->unp_timer = 5;
|
2021-04-25 08:15:20 +03:00
|
|
|
|
|
|
|
if (count != 0)
|
|
|
|
rnd_add_uint32(&unp->unp_rndsrc, count);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_if_start(struct ifnet *ifp)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
|
|
|
struct usbnet * const un = ifp->if_softc;
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-09-08 21:59:32 +03:00
|
|
|
USBNETHIST_FUNC();
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGS("%jd: stopping %jd",
|
2019-09-08 21:59:32 +03:00
|
|
|
unp->unp_number, unp->unp_stopping, 0, 0);
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_enter(&unp->unp_txlock);
|
|
|
|
if (!unp->unp_stopping)
|
2019-07-31 12:13:16 +03:00
|
|
|
usbnet_start_locked(ifp);
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_exit(&unp->unp_txlock);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Chain management.
|
|
|
|
*
|
|
|
|
* RX and TX are identical. Keep them that way.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Start of common RX functions */
|
|
|
|
|
|
|
|
static size_t
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_rx_list_size(struct usbnet_cdata * const cd, struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
return sizeof(*cd->uncd_rx_chain) * un->un_rx_list_cnt;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_rx_list_alloc(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
cd->uncd_rx_chain = kmem_zalloc(usbnet_rx_list_size(cd, un), KM_SLEEP);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_rx_list_free(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
if (cd->uncd_rx_chain) {
|
2019-08-10 05:17:36 +03:00
|
|
|
kmem_free(cd->uncd_rx_chain, usbnet_rx_list_size(cd, un));
|
2019-07-31 12:13:16 +03:00
|
|
|
cd->uncd_rx_chain = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_rx_list_init(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
for (size_t i = 0; i < un->un_rx_list_cnt; i++) {
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet_chain *c = &cd->uncd_rx_chain[i];
|
|
|
|
|
|
|
|
c->unc_un = un;
|
|
|
|
if (c->unc_xfer == NULL) {
|
2019-08-10 05:17:36 +03:00
|
|
|
int err = usbd_create_xfer(unp->unp_ep[USBNET_ENDPT_RX],
|
|
|
|
un->un_rx_bufsz, un->un_rx_xfer_flags, 0,
|
2019-08-09 04:17:33 +03:00
|
|
|
&c->unc_xfer);
|
2019-07-31 12:13:16 +03:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
c->unc_buf = usbd_get_buffer(c->unc_xfer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_rx_list_fini(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
for (size_t i = 0; i < un->un_rx_list_cnt; i++) {
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet_chain *c = &cd->uncd_rx_chain[i];
|
|
|
|
|
|
|
|
if (c->unc_xfer != NULL) {
|
|
|
|
usbd_destroy_xfer(c->unc_xfer);
|
|
|
|
c->unc_xfer = NULL;
|
|
|
|
c->unc_buf = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* End of common RX functions */
|
|
|
|
|
|
|
|
static void
|
2019-08-16 11:38:21 +03:00
|
|
|
usbnet_rx_start_pipes(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_enter(&unp->unp_rxlock);
|
|
|
|
mutex_enter(&unp->unp_txlock);
|
|
|
|
unp->unp_stopping = false;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
for (size_t i = 0; i < un->un_rx_list_cnt; i++) {
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet_chain *c = &cd->uncd_rx_chain[i];
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
usbd_setup_xfer(c->unc_xfer, c, c->unc_buf, un->un_rx_bufsz,
|
2019-08-16 11:38:21 +03:00
|
|
|
un->un_rx_xfer_flags, USBD_NO_TIMEOUT, usbnet_rxeof);
|
2019-07-31 12:13:16 +03:00
|
|
|
usbd_transfer(c->unc_xfer);
|
2019-08-03 18:58:14 +03:00
|
|
|
}
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_exit(&unp->unp_txlock);
|
|
|
|
mutex_exit(&unp->unp_rxlock);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Start of common TX functions */
|
|
|
|
|
|
|
|
static size_t
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_tx_list_size(struct usbnet_cdata * const cd, struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
return sizeof(*cd->uncd_tx_chain) * un->un_tx_list_cnt;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_tx_list_alloc(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
cd->uncd_tx_chain = kmem_zalloc(usbnet_tx_list_size(cd, un), KM_SLEEP);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_tx_list_free(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
if (cd->uncd_tx_chain) {
|
2019-08-10 05:17:36 +03:00
|
|
|
kmem_free(cd->uncd_tx_chain, usbnet_tx_list_size(cd, un));
|
2019-07-31 12:13:16 +03:00
|
|
|
cd->uncd_tx_chain = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_tx_list_init(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
for (size_t i = 0; i < un->un_tx_list_cnt; i++) {
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet_chain *c = &cd->uncd_tx_chain[i];
|
|
|
|
|
|
|
|
c->unc_un = un;
|
|
|
|
if (c->unc_xfer == NULL) {
|
2019-08-10 05:17:36 +03:00
|
|
|
int err = usbd_create_xfer(unp->unp_ep[USBNET_ENDPT_TX],
|
|
|
|
un->un_tx_bufsz, un->un_tx_xfer_flags, 0,
|
2019-08-09 04:17:33 +03:00
|
|
|
&c->unc_xfer);
|
2019-07-31 12:13:16 +03:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
c->unc_buf = usbd_get_buffer(c->unc_xfer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_tx_list_fini(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
for (size_t i = 0; i < un->un_tx_list_cnt; i++) {
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet_chain *c = &cd->uncd_tx_chain[i];
|
|
|
|
|
|
|
|
if (c->unc_xfer != NULL) {
|
|
|
|
usbd_destroy_xfer(c->unc_xfer);
|
|
|
|
c->unc_xfer = NULL;
|
|
|
|
c->unc_buf = NULL;
|
|
|
|
}
|
|
|
|
}
|
2019-08-23 07:29:28 +03:00
|
|
|
cd->uncd_tx_prod = cd->uncd_tx_cnt = 0;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* End of common TX functions */
|
|
|
|
|
|
|
|
/* Endpoint pipe management. */
|
|
|
|
|
|
|
|
static void
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_ep_close_pipes(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < __arraycount(unp->unp_ep); i++) {
|
|
|
|
if (unp->unp_ep[i] == NULL)
|
2019-07-31 12:13:16 +03:00
|
|
|
continue;
|
2019-08-10 05:17:36 +03:00
|
|
|
usbd_status err = usbd_close_pipe(unp->unp_ep[i]);
|
2019-07-31 12:13:16 +03:00
|
|
|
if (err)
|
|
|
|
aprint_error_dev(un->un_dev, "close pipe %zu: %s\n", i,
|
|
|
|
usbd_errstr(err));
|
2019-08-10 05:17:36 +03:00
|
|
|
unp->unp_ep[i] = NULL;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static usbd_status
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_ep_open_pipes(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_intr * const uni = un->un_intr;
|
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < __arraycount(unp->unp_ep); i++) {
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
usbd_status err;
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
if (un->un_ed[i] == 0)
|
|
|
|
continue;
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
if (i == USBNET_ENDPT_INTR && uni) {
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
err = usbd_open_pipe_intr(un->un_iface, un->un_ed[i],
|
2019-08-10 05:17:36 +03:00
|
|
|
USBD_EXCLUSIVE_USE | USBD_MPSAFE, &unp->unp_ep[i], un,
|
|
|
|
uni->uni_buf, uni->uni_bufsz, usbnet_pipe_intr,
|
|
|
|
uni->uni_interval);
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
} else {
|
|
|
|
err = usbd_open_pipe(un->un_iface, un->un_ed[i],
|
2019-08-10 05:17:36 +03:00
|
|
|
USBD_EXCLUSIVE_USE | USBD_MPSAFE, &unp->unp_ep[i]);
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
}
|
2019-07-31 12:13:16 +03:00
|
|
|
if (err) {
|
|
|
|
usbnet_ep_close_pipes(un);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return USBD_NORMAL_COMPLETION;
|
|
|
|
}
|
|
|
|
|
|
|
|
static usbd_status
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_ep_stop_pipes(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-08-18 12:29:38 +03:00
|
|
|
usbd_status err = USBD_NORMAL_COMPLETION;
|
2019-08-10 05:17:36 +03:00
|
|
|
|
|
|
|
for (size_t i = 0; i < __arraycount(unp->unp_ep); i++) {
|
|
|
|
if (unp->unp_ep[i] == NULL)
|
2019-07-31 12:13:16 +03:00
|
|
|
continue;
|
2019-08-18 12:29:38 +03:00
|
|
|
usbd_status err2 = usbd_abort_pipe(unp->unp_ep[i]);
|
|
|
|
if (err == USBD_NORMAL_COMPLETION && err2)
|
2019-08-18 12:46:58 +03:00
|
|
|
err = err2;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
2019-08-18 12:29:38 +03:00
|
|
|
return err;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2019-08-09 04:17:33 +03:00
|
|
|
usbnet_init_rx_tx(struct usbnet * const un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
struct ifnet * const ifp = usbnet_ifp(un);
|
|
|
|
usbd_status err;
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
int error = 0;
|
|
|
|
|
2022-03-03 08:47:36 +03:00
|
|
|
KASSERTMSG(!unp->unp_ifp_attached || IFNET_LOCKED(ifp),
|
|
|
|
"%s", ifp->if_xname);
|
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_isowned_core(un);
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
if (unp->unp_dying) {
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
return EIO;
|
|
|
|
}
|
2020-03-16 02:04:50 +03:00
|
|
|
|
|
|
|
usbnet_busy(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
/* Open RX and TX pipes. */
|
|
|
|
err = usbnet_ep_open_pipes(un);
|
|
|
|
if (err) {
|
|
|
|
aprint_error_dev(un->un_dev, "open rx/tx pipes failed: %s\n",
|
|
|
|
usbd_errstr(err));
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
error = EIO;
|
|
|
|
goto out;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Init RX ring. */
|
2019-08-09 04:17:33 +03:00
|
|
|
if (usbnet_rx_list_init(un)) {
|
2019-07-31 12:13:16 +03:00
|
|
|
aprint_error_dev(un->un_dev, "rx list init failed\n");
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
error = ENOBUFS;
|
|
|
|
goto out;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Init TX ring. */
|
2019-08-09 04:17:33 +03:00
|
|
|
if (usbnet_tx_list_init(un)) {
|
2019-07-31 12:13:16 +03:00
|
|
|
aprint_error_dev(un->un_dev, "tx list init failed\n");
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
error = ENOBUFS;
|
|
|
|
goto out;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Indicate we are up and running. */
|
2022-03-03 08:49:14 +03:00
|
|
|
/* XXX urndis calls usbnet_init_rx_tx before usbnet_attach_ifp. */
|
|
|
|
KASSERTMSG(!unp->unp_ifp_attached || IFNET_LOCKED(ifp),
|
|
|
|
"%s", ifp->if_xname);
|
2019-07-31 12:13:16 +03:00
|
|
|
ifp->if_flags |= IFF_RUNNING;
|
|
|
|
|
2022-03-03 08:46:58 +03:00
|
|
|
/* Start up the receive pipe(s). */
|
|
|
|
usbnet_rx_start_pipes(un);
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
callout_schedule(&unp->unp_stat_ch, hz);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
out:
|
|
|
|
if (error) {
|
|
|
|
usbnet_rx_list_fini(un);
|
|
|
|
usbnet_tx_list_fini(un);
|
|
|
|
usbnet_ep_close_pipes(un);
|
|
|
|
}
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_unbusy(un);
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_isowned_core(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
return error;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_busy(struct usbnet *un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_isowned_core(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
unp->unp_refcnt++;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_unbusy(struct usbnet *un)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_isowned_core(un);
|
2019-08-10 05:17:36 +03:00
|
|
|
|
|
|
|
if (--unp->unp_refcnt < 0)
|
|
|
|
cv_broadcast(&unp->unp_detachcv);
|
|
|
|
}
|
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
/* MII management. */
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
int
|
2019-08-09 04:17:33 +03:00
|
|
|
usbnet_mii_readreg(device_t dev, int phy, int reg, uint16_t *val)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-19 09:35:14 +03:00
|
|
|
USBNETHIST_FUNC();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = device_private(dev);
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-08-20 09:37:06 +03:00
|
|
|
int err;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
/* MII layer ensures core_lock is held. */
|
|
|
|
usbnet_isowned_core(un);
|
|
|
|
|
2019-08-19 10:33:37 +03:00
|
|
|
if (unp->unp_dying) {
|
2019-07-31 12:13:16 +03:00
|
|
|
return EIO;
|
|
|
|
}
|
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_busy(un);
|
2019-08-09 04:17:33 +03:00
|
|
|
err = uno_read_reg(un, phy, reg, val);
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_unbusy(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
if (err) {
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGS("%jd: read PHY failed: %jd",
|
|
|
|
unp->unp_number, err, 0, 0);
|
2019-08-20 09:37:06 +03:00
|
|
|
return err;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2019-08-09 04:17:33 +03:00
|
|
|
usbnet_mii_writereg(device_t dev, int phy, int reg, uint16_t val)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-19 09:35:14 +03:00
|
|
|
USBNETHIST_FUNC();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = device_private(dev);
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-08-20 09:37:06 +03:00
|
|
|
int err;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
/* MII layer ensures core_lock is held. */
|
|
|
|
usbnet_isowned_core(un);
|
|
|
|
|
2019-08-19 10:33:37 +03:00
|
|
|
if (unp->unp_dying) {
|
2019-07-31 12:13:16 +03:00
|
|
|
return EIO;
|
|
|
|
}
|
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_busy(un);
|
2019-08-09 04:17:33 +03:00
|
|
|
err = uno_write_reg(un, phy, reg, val);
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_unbusy(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
if (err) {
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGS("%jd: write PHY failed: %jd",
|
|
|
|
unp->unp_number, err, 0, 0);
|
2019-08-20 09:37:06 +03:00
|
|
|
return err;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-08-09 04:17:33 +03:00
|
|
|
usbnet_mii_statchg(struct ifnet *ifp)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = ifp->if_softc;
|
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
/* MII layer ensures core_lock is held. */
|
|
|
|
usbnet_isowned_core(un);
|
|
|
|
|
|
|
|
usbnet_busy(un);
|
2019-08-09 04:17:33 +03:00
|
|
|
uno_mii_statchg(un, ifp);
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_unbusy(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
usbnet_media_upd(struct ifnet *ifp)
|
|
|
|
{
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = ifp->if_softc;
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
struct mii_data * const mii = usbnet_mii(un);
|
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
/* ifmedia layer ensures core_lock is held. */
|
|
|
|
usbnet_isowned_core(un);
|
|
|
|
|
2022-03-03 08:48:30 +03:00
|
|
|
/* ifmedia changes only with IFNET_LOCK held. */
|
|
|
|
KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
if (unp->unp_dying)
|
2019-07-31 12:13:16 +03:00
|
|
|
return EIO;
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
unp->unp_link = false;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
if (mii->mii_instance) {
|
|
|
|
struct mii_softc *miisc;
|
|
|
|
|
|
|
|
LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
|
|
|
|
mii_phy_reset(miisc);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ether_mediachange(ifp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ioctl */
|
|
|
|
|
|
|
|
static int
|
|
|
|
usbnet_ifflags_cb(struct ethercom *ec)
|
|
|
|
{
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct ifnet *ifp = &ec->ec_if;
|
|
|
|
struct usbnet *un = ifp->if_softc;
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
int rv = 0;
|
|
|
|
|
2022-03-03 08:47:28 +03:00
|
|
|
KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
|
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_enter(&unp->unp_core_lock);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-09-13 10:55:05 +03:00
|
|
|
const u_short changed = ifp->if_flags ^ unp->unp_if_flags;
|
2019-07-31 12:13:16 +03:00
|
|
|
if ((changed & ~(IFF_CANTCHANGE | IFF_DEBUG)) == 0) {
|
2019-08-10 05:17:36 +03:00
|
|
|
unp->unp_if_flags = ifp->if_flags;
|
2019-07-31 12:13:16 +03:00
|
|
|
if ((changed & IFF_PROMISC) != 0)
|
|
|
|
rv = ENETRESET;
|
|
|
|
} else {
|
|
|
|
rv = ENETRESET;
|
|
|
|
}
|
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_exit(&unp->unp_core_lock);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_if_ioctl(struct ifnet *ifp, u_long cmd, void *data)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
2019-08-23 07:29:28 +03:00
|
|
|
USBNETHIST_FUNC();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = ifp->if_softc;
|
2019-08-23 07:29:28 +03:00
|
|
|
struct usbnet_private * const unp __unused = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
int error;
|
|
|
|
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGSN(11, "%jd: enter %#jx data %#jx",
|
2019-08-23 07:29:28 +03:00
|
|
|
unp->unp_number, cmd, (uintptr_t)data, 0);
|
|
|
|
|
2019-08-09 04:17:33 +03:00
|
|
|
if (un->un_ops->uno_override_ioctl)
|
|
|
|
return uno_override_ioctl(un, ifp, cmd, data);
|
2019-08-06 03:19:57 +03:00
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
error = ether_ioctl(ifp, cmd, data);
|
usbnet: Defer hardware multicast filter updates to USB task.
Breaks deadlock:
- usbnet_detach holds usbnet lock, awaits kpause in ure_reset
- callout holds softclock `lock' (sequential softints, blocks kpause
wakeup), awaits softnet_lock in tcp_timer_keep, frag6_fasttimo, &c.
- soclose holds softnet_lock, awaits usbnet lock in SIOCDELMULTI
This change breaks the deadlock by not passing the SIOCADDMULTI or
SIOCDELMULTI ioctl synchronously to the driver, which typically takes
the usbnet lock.
With this change, the ethernet layer still maintains the list of
multicast addresses synchronously, but we defer the driver logic that
updates the hardware multicast filter to an asynchronous USB task
without softnet_lock held.
This doesn't cause exactly the same ioctl to be sent to the driver --
usbnet just sends SIOCDELMULTI with an all-zero struct ifreq, and
might drop some ioctls if issued in quick succession. This is OK
because none of the drivers actually distinguish between SIOCADDMULTI
and SIOCDELMULTI, or examine the argument; the drivers just commit
whatever multicast addresses are listed in the ethercom.
Other than the different ioctl submitted, there is no change to the
ABI or locking scheme of usbnet, so this is safe to pull up to
netbsd-9. This means we unfortunately can't guarantee that if a
process issues SIOCADDMULTI and then sendto, the multicast filter
update will be done by the time of the sendto -- and, more
importantly, the packets received in reply to it. But failing to
guarantee that is better than deadlocking! Later changes on HEAD
will restore the synchronous multicast filter updates with much more
extensive ABI changes and API simplifications in usbnet(9).
Proposed on tech-net:
https://mail-index.netbsd.org/tech-net/2021/12/30/msg008164.html
XXX pullup-9
2022-01-30 00:37:07 +03:00
|
|
|
if (error == ENETRESET) {
|
|
|
|
switch (cmd) {
|
|
|
|
case SIOCADDMULTI:
|
|
|
|
case SIOCDELMULTI:
|
|
|
|
usb_add_task(un->un_udev, &unp->unp_mcasttask,
|
|
|
|
USB_TASKQ_DRIVER);
|
|
|
|
error = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
error = uno_ioctl(un, ifp, cmd, data);
|
|
|
|
}
|
|
|
|
}
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
usbnet: Defer hardware multicast filter updates to USB task.
Breaks deadlock:
- usbnet_detach holds usbnet lock, awaits kpause in ure_reset
- callout holds softclock `lock' (sequential softints, blocks kpause
wakeup), awaits softnet_lock in tcp_timer_keep, frag6_fasttimo, &c.
- soclose holds softnet_lock, awaits usbnet lock in SIOCDELMULTI
This change breaks the deadlock by not passing the SIOCADDMULTI or
SIOCDELMULTI ioctl synchronously to the driver, which typically takes
the usbnet lock.
With this change, the ethernet layer still maintains the list of
multicast addresses synchronously, but we defer the driver logic that
updates the hardware multicast filter to an asynchronous USB task
without softnet_lock held.
This doesn't cause exactly the same ioctl to be sent to the driver --
usbnet just sends SIOCDELMULTI with an all-zero struct ifreq, and
might drop some ioctls if issued in quick succession. This is OK
because none of the drivers actually distinguish between SIOCADDMULTI
and SIOCDELMULTI, or examine the argument; the drivers just commit
whatever multicast addresses are listed in the ethercom.
Other than the different ioctl submitted, there is no change to the
ABI or locking scheme of usbnet, so this is safe to pull up to
netbsd-9. This means we unfortunately can't guarantee that if a
process issues SIOCADDMULTI and then sendto, the multicast filter
update will be done by the time of the sendto -- and, more
importantly, the packets received in reply to it. But failing to
guarantee that is better than deadlocking! Later changes on HEAD
will restore the synchronous multicast filter updates with much more
extensive ABI changes and API simplifications in usbnet(9).
Proposed on tech-net:
https://mail-index.netbsd.org/tech-net/2021/12/30/msg008164.html
XXX pullup-9
2022-01-30 00:37:07 +03:00
|
|
|
static void
|
|
|
|
usbnet_mcast_task(void *arg)
|
|
|
|
{
|
|
|
|
USBNETHIST_FUNC();
|
|
|
|
struct usbnet * const un = arg;
|
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
|
|
|
struct ifnet * const ifp = usbnet_ifp(un);
|
|
|
|
bool dying;
|
|
|
|
struct ifreq ifr;
|
|
|
|
|
|
|
|
USBNETHIST_CALLARGSN(10, "%jd: enter", unp->unp_number, 0, 0, 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we're detaching, we must check unp_dying _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.
|
|
|
|
*/
|
|
|
|
mutex_enter(&unp->unp_core_lock);
|
|
|
|
dying = unp->unp_dying;
|
|
|
|
mutex_exit(&unp->unp_core_lock);
|
|
|
|
if (dying)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Pass a bogus ifr with SIOCDELMULTI -- the goal is to just
|
|
|
|
* notify the driver to reprogram any hardware multicast
|
|
|
|
* filter, according to what's already stored in the ethercom.
|
|
|
|
* None of the drivers actually examine this argument, so it
|
|
|
|
* doesn't change the ABI as far as they can tell.
|
|
|
|
*/
|
|
|
|
IFNET_LOCK(ifp);
|
|
|
|
if (ifp->if_flags & IFF_RUNNING) {
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
|
|
(void)uno_ioctl(un, ifp, SIOCDELMULTI, &ifr);
|
|
|
|
}
|
|
|
|
IFNET_UNLOCK(ifp);
|
|
|
|
}
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
/*
|
|
|
|
* Generic stop network function:
|
|
|
|
* - mark as stopping
|
|
|
|
* - call DD routine to stop the device
|
|
|
|
* - turn off running, timer, statchg callout, link
|
|
|
|
* - stop transfers
|
|
|
|
* - free RX and TX resources
|
|
|
|
* - close pipes
|
|
|
|
*
|
|
|
|
* usbnet_stop() is exported for drivers to use, expects lock held.
|
|
|
|
*
|
2020-03-16 02:04:50 +03:00
|
|
|
* usbnet_if_stop() is for the if_stop handler.
|
2019-07-31 12:13:16 +03:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
usbnet_stop(struct usbnet *un, struct ifnet *ifp, int disable)
|
|
|
|
{
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
|
|
|
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
|
|
|
|
2022-03-03 08:47:36 +03:00
|
|
|
KASSERTMSG(!unp->unp_ifp_attached || IFNET_LOCKED(ifp),
|
|
|
|
"%s", ifp->if_xname);
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_isowned_core(un);
|
|
|
|
|
|
|
|
usbnet_busy(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2022-03-03 08:49:37 +03:00
|
|
|
/*
|
|
|
|
* Prevent new activity (rescheduling ticks, xfers, &c.) and
|
|
|
|
* clear the watchdog timer.
|
|
|
|
*/
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_enter(&unp->unp_rxlock);
|
|
|
|
mutex_enter(&unp->unp_txlock);
|
|
|
|
unp->unp_stopping = true;
|
2022-03-03 08:49:37 +03:00
|
|
|
unp->unp_timer = 0;
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_exit(&unp->unp_txlock);
|
|
|
|
mutex_exit(&unp->unp_rxlock);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2022-03-03 08:48:22 +03:00
|
|
|
/*
|
|
|
|
* Stop the timer first, then the task -- if the timer was
|
|
|
|
* already firing, we stop the task or wait for it complete
|
|
|
|
* only after if last fired. Setting unp_stopping prevents the
|
|
|
|
* timer task from being scheduled again.
|
|
|
|
*/
|
|
|
|
callout_halt(&unp->unp_stat_ch, &unp->unp_core_lock);
|
|
|
|
usb_rem_task_wait(un->un_udev, &unp->unp_ticktask, USB_TASKQ_DRIVER,
|
|
|
|
&unp->unp_core_lock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now that the software is quiescent, ask the driver to stop
|
|
|
|
* the hardware. The driver's uno_stop routine now has
|
|
|
|
* exclusive access to any registers that might previously have
|
|
|
|
* been used by to ifmedia, mii, or ioctl callbacks.
|
|
|
|
*/
|
2019-08-09 04:17:33 +03:00
|
|
|
uno_stop(un, ifp, disable);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
/* Stop transfers. */
|
|
|
|
usbnet_ep_stop_pipes(un);
|
|
|
|
|
|
|
|
/* Free RX/TX resources. */
|
|
|
|
usbnet_rx_list_fini(un);
|
|
|
|
usbnet_tx_list_fini(un);
|
|
|
|
|
|
|
|
/* Close pipes. */
|
|
|
|
usbnet_ep_close_pipes(un);
|
2020-03-16 02:04:50 +03:00
|
|
|
|
2022-03-03 08:47:36 +03:00
|
|
|
/* Everything is quesced now. */
|
|
|
|
KASSERTMSG(!unp->unp_ifp_attached || IFNET_LOCKED(ifp),
|
|
|
|
"%s", ifp->if_xname);
|
2022-03-03 08:46:58 +03:00
|
|
|
ifp->if_flags &= ~IFF_RUNNING;
|
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_unbusy(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_if_stop(struct ifnet *ifp, int disable)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
|
|
|
struct usbnet * const un = ifp->if_softc;
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2022-03-03 08:47:28 +03:00
|
|
|
KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
|
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_enter(&unp->unp_core_lock);
|
2019-07-31 12:13:16 +03:00
|
|
|
usbnet_stop(un, ifp, disable);
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_exit(&unp->unp_core_lock);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generic tick task function.
|
|
|
|
*
|
|
|
|
* usbnet_tick() is triggered from a callout, and triggers a call to
|
|
|
|
* usbnet_tick_task() from the usb_task subsystem.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
usbnet_tick(void *arg)
|
|
|
|
{
|
2019-09-08 21:59:32 +03:00
|
|
|
USBNETHIST_FUNC();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = arg;
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2020-03-13 21:17:40 +03:00
|
|
|
USBNETHIST_CALLARGSN(10, "%jd: enter", unp->unp_number, 0, 0, 0);
|
2019-09-08 21:59:32 +03:00
|
|
|
|
2022-03-03 08:49:22 +03:00
|
|
|
/* Perform periodic stuff in process context */
|
|
|
|
usb_add_task(un->un_udev, &unp->unp_ticktask, USB_TASKQ_DRIVER);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
usbnet_watchdog(struct ifnet *ifp)
|
|
|
|
{
|
2019-08-23 07:29:28 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = ifp->if_softc;
|
2019-08-18 12:29:38 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_cdata * const cd = un_cdata(un);
|
2019-08-18 12:29:38 +03:00
|
|
|
usbd_status err;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2020-01-29 09:35:28 +03:00
|
|
|
if_statinc(ifp, if_oerrors);
|
2021-03-01 20:41:00 +03:00
|
|
|
device_printf(un->un_dev, "watchdog timeout\n");
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
if (cd->uncd_tx_cnt > 0) {
|
2020-03-14 06:01:36 +03:00
|
|
|
DPRINTF("uncd_tx_cnt=%ju non zero, aborting pipe", 0, 0, 0, 0);
|
2019-08-18 12:29:38 +03:00
|
|
|
err = usbd_abort_pipe(unp->unp_ep[USBNET_ENDPT_TX]);
|
2019-08-19 10:33:37 +03:00
|
|
|
if (err)
|
2021-03-01 20:41:00 +03:00
|
|
|
device_printf(un->un_dev, "pipe abort failed: %s\n",
|
2019-08-19 10:33:37 +03:00
|
|
|
usbd_errstr(err));
|
2019-08-23 07:29:28 +03:00
|
|
|
if (cd->uncd_tx_cnt != 0)
|
2020-03-14 06:01:36 +03:00
|
|
|
DPRINTF("uncd_tx_cnt now %ju", cd->uncd_tx_cnt, 0, 0, 0);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!IFQ_IS_EMPTY(&ifp->if_snd))
|
|
|
|
(*ifp->if_start)(ifp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
usbnet_tick_task(void *arg)
|
|
|
|
{
|
2019-09-08 21:59:32 +03:00
|
|
|
USBNETHIST_FUNC();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = arg;
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
struct ifnet * const ifp = usbnet_ifp(un);
|
|
|
|
struct mii_data * const mii = usbnet_mii(un);
|
|
|
|
|
2022-03-03 08:49:29 +03:00
|
|
|
USBNETHIST_CALLARGSN(8, "%jd: enter", unp->unp_number, 0, 0, 0);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2022-03-03 08:47:21 +03:00
|
|
|
mutex_enter(&unp->unp_txlock);
|
|
|
|
const bool timeout = unp->unp_timer != 0 && --unp->unp_timer == 0;
|
|
|
|
mutex_exit(&unp->unp_txlock);
|
|
|
|
if (timeout)
|
2019-07-31 12:13:16 +03:00
|
|
|
usbnet_watchdog(ifp);
|
|
|
|
|
2020-03-13 21:17:40 +03:00
|
|
|
DPRINTFN(8, "mii %#jx ifp %#jx", (uintptr_t)mii, (uintptr_t)ifp, 0, 0);
|
2019-12-14 18:40:43 +03:00
|
|
|
if (mii) {
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_enter(&unp->unp_core_lock);
|
2019-12-14 18:40:43 +03:00
|
|
|
mii_tick(mii);
|
|
|
|
if (!unp->unp_link)
|
|
|
|
(*mii->mii_statchg)(ifp);
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_exit(&unp->unp_core_lock);
|
2019-12-14 18:40:43 +03:00
|
|
|
}
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-15 08:52:23 +03:00
|
|
|
/* Call driver if requested. */
|
|
|
|
uno_tick(un);
|
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_enter(&unp->unp_core_lock);
|
2019-08-10 05:17:36 +03:00
|
|
|
if (!unp->unp_stopping && !unp->unp_dying)
|
|
|
|
callout_schedule(&unp->unp_stat_ch, hz);
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_exit(&unp->unp_core_lock);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_if_init(struct ifnet *ifp)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = ifp->if_softc;
|
2022-03-03 08:48:37 +03:00
|
|
|
bool dying;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2022-03-03 08:47:28 +03:00
|
|
|
KASSERTMSG(IFNET_LOCKED(ifp), "%s", ifp->if_xname);
|
|
|
|
|
2022-03-03 08:48:37 +03:00
|
|
|
/*
|
|
|
|
* Prevent anyone from bringing the interface back up once
|
|
|
|
* we're detaching.
|
|
|
|
*/
|
|
|
|
mutex_enter(&un->un_pri->unp_core_lock);
|
|
|
|
dying = un->un_pri->unp_dying;
|
|
|
|
mutex_exit(&un->un_pri->unp_core_lock);
|
|
|
|
if (dying)
|
|
|
|
return EIO;
|
|
|
|
|
2019-08-09 04:17:33 +03:00
|
|
|
return uno_init(un, ifp);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
2019-08-11 04:31:19 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
/* Various accessors. */
|
|
|
|
|
|
|
|
void
|
|
|
|
usbnet_set_link(struct usbnet *un, bool link)
|
|
|
|
{
|
|
|
|
un->un_pri->unp_link = link;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ifnet *
|
|
|
|
usbnet_ifp(struct usbnet *un)
|
|
|
|
{
|
|
|
|
return &un->un_pri->unp_ec.ec_if;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ethercom *
|
|
|
|
usbnet_ec(struct usbnet *un)
|
|
|
|
{
|
|
|
|
return &un->un_pri->unp_ec;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct mii_data *
|
|
|
|
usbnet_mii(struct usbnet *un)
|
|
|
|
{
|
|
|
|
return un->un_pri->unp_ec.ec_mii;
|
|
|
|
}
|
|
|
|
|
|
|
|
krndsource_t *
|
|
|
|
usbnet_rndsrc(struct usbnet *un)
|
|
|
|
{
|
|
|
|
return &un->un_pri->unp_rndsrc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *
|
|
|
|
usbnet_softc(struct usbnet *un)
|
|
|
|
{
|
|
|
|
return un->un_sc;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
usbnet_havelink(struct usbnet *un)
|
|
|
|
{
|
|
|
|
return un->un_pri->unp_link;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
usbnet_isdying(struct usbnet *un)
|
|
|
|
{
|
2022-03-03 08:46:50 +03:00
|
|
|
return un->un_pri->unp_dying;
|
2019-08-10 05:17:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Locking. */
|
|
|
|
|
|
|
|
void
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_lock_core(struct usbnet *un)
|
2019-08-10 05:17:36 +03:00
|
|
|
{
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_enter(&un->un_pri->unp_core_lock);
|
2019-08-10 05:17:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_unlock_core(struct usbnet *un)
|
2019-08-10 05:17:36 +03:00
|
|
|
{
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_exit(&un->un_pri->unp_core_lock);
|
2019-08-10 05:17:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
kmutex_t *
|
2020-03-16 02:04:50 +03:00
|
|
|
usbnet_mutex_core(struct usbnet *un)
|
2019-08-10 05:17:36 +03:00
|
|
|
{
|
2020-03-16 02:04:50 +03:00
|
|
|
return &un->un_pri->unp_core_lock;
|
2019-08-10 05:17:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
usbnet_lock_rx(struct usbnet *un)
|
|
|
|
{
|
|
|
|
mutex_enter(&un->un_pri->unp_rxlock);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
usbnet_unlock_rx(struct usbnet *un)
|
|
|
|
{
|
|
|
|
mutex_exit(&un->un_pri->unp_rxlock);
|
|
|
|
}
|
|
|
|
|
|
|
|
kmutex_t *
|
|
|
|
usbnet_mutex_rx(struct usbnet *un)
|
|
|
|
{
|
|
|
|
return &un->un_pri->unp_rxlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
usbnet_lock_tx(struct usbnet *un)
|
|
|
|
{
|
|
|
|
mutex_enter(&un->un_pri->unp_txlock);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
usbnet_unlock_tx(struct usbnet *un)
|
|
|
|
{
|
|
|
|
mutex_exit(&un->un_pri->unp_txlock);
|
|
|
|
}
|
|
|
|
|
|
|
|
kmutex_t *
|
|
|
|
usbnet_mutex_tx(struct usbnet *un)
|
|
|
|
{
|
|
|
|
return &un->un_pri->unp_txlock;
|
|
|
|
}
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
/* Autoconf management. */
|
|
|
|
|
2019-08-06 03:19:57 +03:00
|
|
|
static bool
|
2019-08-11 04:31:19 +03:00
|
|
|
usbnet_empty_eaddr(struct usbnet * const un)
|
2019-08-06 03:19:57 +03:00
|
|
|
{
|
|
|
|
return (un->un_eaddr[0] == 0 && un->un_eaddr[1] == 0 &&
|
|
|
|
un->un_eaddr[2] == 0 && un->un_eaddr[3] == 0 &&
|
|
|
|
un->un_eaddr[4] == 0 && un->un_eaddr[5] == 0);
|
|
|
|
}
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
/*
|
|
|
|
* usbnet_attach() and usbnet_attach_ifp() perform setup of the relevant
|
|
|
|
* 'usbnet'. The first is enough to enable device access (eg, endpoints
|
|
|
|
* are connected and commands can be sent), and the second connects the
|
|
|
|
* device to the system networking.
|
|
|
|
*
|
|
|
|
* Always call usbnet_detach(), even if usbnet_attach_ifp() is skippped.
|
|
|
|
* Also usable as driver detach directly.
|
2019-08-06 03:19:57 +03:00
|
|
|
*
|
|
|
|
* To skip ethernet configuration (eg, point-to-point), make sure that
|
|
|
|
* the un_eaddr[] is fully zero.
|
2019-07-31 12:13:16 +03:00
|
|
|
*/
|
2019-08-10 05:17:36 +03:00
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
void
|
|
|
|
usbnet_attach(struct usbnet *un,
|
2019-08-10 05:17:36 +03:00
|
|
|
const char *detname) /* detach cv name */
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-09 04:17:33 +03:00
|
|
|
/* Required inputs. */
|
|
|
|
KASSERT(un->un_ops->uno_tx_prepare);
|
|
|
|
KASSERT(un->un_ops->uno_rx_loop);
|
|
|
|
KASSERT(un->un_ops->uno_init);
|
2019-08-10 05:17:36 +03:00
|
|
|
KASSERT(un->un_rx_bufsz);
|
|
|
|
KASSERT(un->un_tx_bufsz);
|
|
|
|
KASSERT(un->un_rx_list_cnt);
|
|
|
|
KASSERT(un->un_tx_list_cnt);
|
|
|
|
|
2019-08-12 02:55:43 +03:00
|
|
|
/* Unfortunate fact. */
|
|
|
|
KASSERT(un == device_private(un->un_dev));
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
un->un_pri = kmem_zalloc(sizeof(*un->un_pri), KM_SLEEP);
|
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
|
|
|
|
usbnet: Defer hardware multicast filter updates to USB task.
Breaks deadlock:
- usbnet_detach holds usbnet lock, awaits kpause in ure_reset
- callout holds softclock `lock' (sequential softints, blocks kpause
wakeup), awaits softnet_lock in tcp_timer_keep, frag6_fasttimo, &c.
- soclose holds softnet_lock, awaits usbnet lock in SIOCDELMULTI
This change breaks the deadlock by not passing the SIOCADDMULTI or
SIOCDELMULTI ioctl synchronously to the driver, which typically takes
the usbnet lock.
With this change, the ethernet layer still maintains the list of
multicast addresses synchronously, but we defer the driver logic that
updates the hardware multicast filter to an asynchronous USB task
without softnet_lock held.
This doesn't cause exactly the same ioctl to be sent to the driver --
usbnet just sends SIOCDELMULTI with an all-zero struct ifreq, and
might drop some ioctls if issued in quick succession. This is OK
because none of the drivers actually distinguish between SIOCADDMULTI
and SIOCDELMULTI, or examine the argument; the drivers just commit
whatever multicast addresses are listed in the ethercom.
Other than the different ioctl submitted, there is no change to the
ABI or locking scheme of usbnet, so this is safe to pull up to
netbsd-9. This means we unfortunately can't guarantee that if a
process issues SIOCADDMULTI and then sendto, the multicast filter
update will be done by the time of the sendto -- and, more
importantly, the packets received in reply to it. But failing to
guarantee that is better than deadlocking! Later changes on HEAD
will restore the synchronous multicast filter updates with much more
extensive ABI changes and API simplifications in usbnet(9).
Proposed on tech-net:
https://mail-index.netbsd.org/tech-net/2021/12/30/msg008164.html
XXX pullup-9
2022-01-30 00:37:07 +03:00
|
|
|
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);
|
2019-08-10 05:17:36 +03:00
|
|
|
callout_init(&unp->unp_stat_ch, CALLOUT_MPSAFE);
|
|
|
|
callout_setfunc(&unp->unp_stat_ch, usbnet_tick, un);
|
|
|
|
|
|
|
|
mutex_init(&unp->unp_txlock, MUTEX_DEFAULT, IPL_SOFTUSB);
|
|
|
|
mutex_init(&unp->unp_rxlock, MUTEX_DEFAULT, IPL_SOFTUSB);
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_init(&unp->unp_core_lock, MUTEX_DEFAULT, IPL_NONE);
|
2019-08-10 05:17:36 +03:00
|
|
|
cv_init(&unp->unp_detachcv, detname);
|
|
|
|
|
|
|
|
rnd_attach_source(&unp->unp_rndsrc, device_xname(un->un_dev),
|
2019-07-31 12:13:16 +03:00
|
|
|
RND_TYPE_NET, RND_FLAG_DEFAULT);
|
|
|
|
|
2019-08-09 04:17:33 +03:00
|
|
|
usbnet_rx_list_alloc(un);
|
|
|
|
usbnet_tx_list_alloc(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-23 07:29:28 +03:00
|
|
|
unp->unp_number = atomic_inc_uint_nv(&usbnet_number);
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
unp->unp_attached = true;
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-08-20 09:37:06 +03:00
|
|
|
usbnet_attach_mii(struct usbnet *un, const struct usbnet_mii *unm)
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
|
|
|
struct mii_data * const mii = &unp->unp_mii;
|
|
|
|
struct ifnet * const ifp = usbnet_ifp(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-09 04:17:33 +03:00
|
|
|
KASSERT(un->un_ops->uno_read_reg);
|
|
|
|
KASSERT(un->un_ops->uno_write_reg);
|
|
|
|
KASSERT(un->un_ops->uno_statchg);
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
mii->mii_ifp = ifp;
|
2019-08-09 04:17:33 +03:00
|
|
|
mii->mii_readreg = usbnet_mii_readreg;
|
|
|
|
mii->mii_writereg = usbnet_mii_writereg;
|
|
|
|
mii->mii_statchg = usbnet_mii_statchg;
|
2019-07-31 12:13:16 +03:00
|
|
|
mii->mii_flags = MIIF_AUTOTSLEEP;
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
usbnet_ec(un)->ec_mii = mii;
|
2020-03-16 02:04:50 +03:00
|
|
|
ifmedia_init_with_lock(&mii->mii_media, 0,
|
|
|
|
usbnet_media_upd, ether_mediastatus, usbnet_mutex_core(un));
|
2019-08-20 09:37:06 +03:00
|
|
|
mii_attach(un->un_dev, mii, unm->un_mii_capmask, unm->un_mii_phyloc,
|
|
|
|
unm->un_mii_offset, unm->un_mii_flags);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
if (LIST_FIRST(&mii->mii_phys) == NULL) {
|
|
|
|
ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL);
|
|
|
|
ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE);
|
|
|
|
} else
|
|
|
|
ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
usbnet_attach_ifp(struct usbnet *un,
|
|
|
|
unsigned if_flags, /* additional if_flags */
|
2019-08-01 02:47:16 +03:00
|
|
|
unsigned if_extflags, /* additional if_extflags */
|
2019-08-20 09:37:06 +03:00
|
|
|
const struct usbnet_mii *unm) /* additional mii_attach flags */
|
2019-07-31 12:13:16 +03:00
|
|
|
{
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
|
|
|
struct ifnet * const ifp = usbnet_ifp(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
KASSERT(unp->unp_attached);
|
2022-03-03 08:47:14 +03:00
|
|
|
KASSERT(!unp->unp_ifp_attached);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2022-03-03 08:47:14 +03:00
|
|
|
ifp->if_softc = un;
|
2019-07-31 12:13:16 +03:00
|
|
|
strlcpy(ifp->if_xname, device_xname(un->un_dev), IFNAMSIZ);
|
|
|
|
ifp->if_flags = if_flags;
|
|
|
|
ifp->if_extflags = IFEF_MPSAFE | if_extflags;
|
2020-03-16 02:04:50 +03:00
|
|
|
ifp->if_ioctl = usbnet_if_ioctl;
|
|
|
|
ifp->if_start = usbnet_if_start;
|
|
|
|
ifp->if_init = usbnet_if_init;
|
|
|
|
ifp->if_stop = usbnet_if_stop;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-20 09:37:06 +03:00
|
|
|
if (unm)
|
|
|
|
usbnet_attach_mii(un, unm);
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
else
|
2019-08-10 05:17:36 +03:00
|
|
|
unp->unp_link = true;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
/* Attach the interface. */
|
if_attach and if_initialize cannot fail, don't test return value
These were originally made failable back in 2017 when if_initialize
allocated a softint in every interface for link state changes, so
that it could fail gracefully instead of panicking:
https://mail-index.NetBSD.org/source-changes/2017/10/23/msg089053.html
However, this spawned many seldom- or never-tested error branches,
which are risky to have around. And that softint in every interface
has since been replaced by a single global workqueue, because link
state changes require thread context but not low latency or high
throughput:
https://mail-index.NetBSD.org/source-changes/2020/02/06/msg113759.html
So there is no longer any reason for if_initialize to fail. (The
subroutine if_stats_init can't fail because percpu_alloc can't fail
either.)
There is a snag: the softint_establish in if_percpuq_create could
fail, potentially leading to bad consequences later on trying to use
the softint. This change doesn't introduce any new bugs because of
the snag -- if_percpuq_attach was already broken. However, the snag
can be better addressed without spawning error branches, either by
using a single softint or making softints less scarce.
(Separate commit will change the signatures of if_attach and
if_initialize to return void, scheduled to ride whatever is the next
convenient kernel bump.)
Patch and testing on amd64 and evbmips64-eb by maya@; commit message
soliloquy, and compile-testing on evbppc/i386/earmv7hf, by me.
2021-06-16 03:21:17 +03:00
|
|
|
if_initialize(ifp);
|
2019-08-18 12:29:38 +03:00
|
|
|
if (ifp->_if_input == NULL)
|
|
|
|
ifp->if_percpuq = if_percpuq_create(ifp);
|
|
|
|
if_register(ifp);
|
2022-03-03 08:47:14 +03:00
|
|
|
unp->unp_ifp_attached = true;
|
2019-08-06 03:19:57 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If ethernet address is all zero, skip ether_ifattach() and
|
|
|
|
* instead attach bpf here..
|
|
|
|
*/
|
|
|
|
if (!usbnet_empty_eaddr(un)) {
|
2019-08-10 05:17:36 +03:00
|
|
|
ether_set_ifflags_cb(&unp->unp_ec, usbnet_ifflags_cb);
|
2019-08-06 04:42:22 +03:00
|
|
|
aprint_normal_dev(un->un_dev, "Ethernet address %s\n",
|
|
|
|
ether_sprintf(un->un_eaddr));
|
2019-08-06 03:19:57 +03:00
|
|
|
ether_ifattach(ifp, un->un_eaddr);
|
|
|
|
} else {
|
|
|
|
if_alloc_sadl(ifp);
|
|
|
|
bpf_attach(ifp, DLT_RAW, 0);
|
|
|
|
}
|
2019-08-07 04:47:18 +03:00
|
|
|
|
2019-08-18 12:29:38 +03:00
|
|
|
/* Now ready, and attached. */
|
|
|
|
IFQ_SET_READY(&ifp->if_snd);
|
|
|
|
|
2019-08-07 04:47:18 +03:00
|
|
|
usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, un->un_udev, un->un_dev);
|
|
|
|
|
|
|
|
if (!pmf_device_register(un->un_dev, NULL, NULL))
|
|
|
|
aprint_error_dev(un->un_dev, "couldn't establish power handler\n");
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
usbnet_detach(device_t self, int flags)
|
|
|
|
{
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = device_private(self);
|
2019-08-28 09:07:21 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
|
|
|
|
|
|
|
/* Detached before attached finished, so just bail out. */
|
|
|
|
if (unp == NULL || !unp->unp_attached)
|
|
|
|
return 0;
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
struct ifnet * const ifp = usbnet_ifp(un);
|
|
|
|
struct mii_data * const mii = usbnet_mii(un);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2022-03-03 08:48:37 +03:00
|
|
|
/*
|
|
|
|
* Prevent new activity. After we stop the interface, it
|
|
|
|
* cannot be brought back up.
|
|
|
|
*/
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_enter(&unp->unp_core_lock);
|
2019-08-10 05:17:36 +03:00
|
|
|
unp->unp_dying = true;
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_exit(&unp->unp_core_lock);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2022-03-03 08:48:37 +03:00
|
|
|
/*
|
|
|
|
* If we're still running on the network, stop and wait for all
|
|
|
|
* asynchronous activity to finish.
|
2022-03-03 08:49:44 +03:00
|
|
|
*
|
|
|
|
* If usbnet_attach_ifp never ran, IFNET_LOCK won't work, but
|
|
|
|
* no activity is possible, so just skip this part.
|
2022-03-03 08:48:37 +03:00
|
|
|
*/
|
2022-03-03 08:49:44 +03:00
|
|
|
if (unp->unp_ifp_attached) {
|
|
|
|
IFNET_LOCK(ifp);
|
|
|
|
if (ifp->if_flags & IFF_RUNNING) {
|
|
|
|
usbnet_if_stop(ifp, 1);
|
|
|
|
}
|
|
|
|
IFNET_UNLOCK(ifp);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
|
|
|
|
2022-03-03 08:48:45 +03:00
|
|
|
/*
|
|
|
|
* The callout and tick task can't be scheduled anew at this
|
|
|
|
* point, and usbnet_if_stop has waited for them to complete.
|
|
|
|
*/
|
|
|
|
KASSERT(!callout_pending(&unp->unp_stat_ch));
|
|
|
|
KASSERT(!usb_task_pending(un->un_udev, &unp->unp_ticktask));
|
|
|
|
|
usbnet: Defer hardware multicast filter updates to USB task.
Breaks deadlock:
- usbnet_detach holds usbnet lock, awaits kpause in ure_reset
- callout holds softclock `lock' (sequential softints, blocks kpause
wakeup), awaits softnet_lock in tcp_timer_keep, frag6_fasttimo, &c.
- soclose holds softnet_lock, awaits usbnet lock in SIOCDELMULTI
This change breaks the deadlock by not passing the SIOCADDMULTI or
SIOCDELMULTI ioctl synchronously to the driver, which typically takes
the usbnet lock.
With this change, the ethernet layer still maintains the list of
multicast addresses synchronously, but we defer the driver logic that
updates the hardware multicast filter to an asynchronous USB task
without softnet_lock held.
This doesn't cause exactly the same ioctl to be sent to the driver --
usbnet just sends SIOCDELMULTI with an all-zero struct ifreq, and
might drop some ioctls if issued in quick succession. This is OK
because none of the drivers actually distinguish between SIOCADDMULTI
and SIOCDELMULTI, or examine the argument; the drivers just commit
whatever multicast addresses are listed in the ethercom.
Other than the different ioctl submitted, there is no change to the
ABI or locking scheme of usbnet, so this is safe to pull up to
netbsd-9. This means we unfortunately can't guarantee that if a
process issues SIOCADDMULTI and then sendto, the multicast filter
update will be done by the time of the sendto -- and, more
importantly, the packets received in reply to it. But failing to
guarantee that is better than deadlocking! Later changes on HEAD
will restore the synchronous multicast filter updates with much more
extensive ABI changes and API simplifications in usbnet(9).
Proposed on tech-net:
https://mail-index.netbsd.org/tech-net/2021/12/30/msg008164.html
XXX pullup-9
2022-01-30 00:37:07 +03:00
|
|
|
usb_rem_task_wait(un->un_udev, &unp->unp_mcasttask, USB_TASKQ_DRIVER,
|
|
|
|
NULL);
|
2019-09-08 21:59:32 +03:00
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
if (mii) {
|
|
|
|
mii_detach(mii, MII_PHY_ANY, MII_OFFSET_ANY);
|
2020-02-04 08:44:14 +03:00
|
|
|
ifmedia_fini(&mii->mii_media);
|
2019-07-31 12:13:16 +03:00
|
|
|
}
|
2022-03-03 08:47:14 +03:00
|
|
|
if (unp->unp_ifp_attached) {
|
2019-08-06 03:19:57 +03:00
|
|
|
if (!usbnet_empty_eaddr(un))
|
|
|
|
ether_ifdetach(ifp);
|
|
|
|
else
|
|
|
|
bpf_detach(ifp);
|
2019-07-31 12:13:16 +03:00
|
|
|
if_detach(ifp);
|
|
|
|
}
|
2019-12-03 08:01:45 +03:00
|
|
|
usbnet_ec(un)->ec_mii = NULL;
|
2019-07-31 12:13:16 +03:00
|
|
|
|
usbnet: Defer hardware multicast filter updates to USB task.
Breaks deadlock:
- usbnet_detach holds usbnet lock, awaits kpause in ure_reset
- callout holds softclock `lock' (sequential softints, blocks kpause
wakeup), awaits softnet_lock in tcp_timer_keep, frag6_fasttimo, &c.
- soclose holds softnet_lock, awaits usbnet lock in SIOCDELMULTI
This change breaks the deadlock by not passing the SIOCADDMULTI or
SIOCDELMULTI ioctl synchronously to the driver, which typically takes
the usbnet lock.
With this change, the ethernet layer still maintains the list of
multicast addresses synchronously, but we defer the driver logic that
updates the hardware multicast filter to an asynchronous USB task
without softnet_lock held.
This doesn't cause exactly the same ioctl to be sent to the driver --
usbnet just sends SIOCDELMULTI with an all-zero struct ifreq, and
might drop some ioctls if issued in quick succession. This is OK
because none of the drivers actually distinguish between SIOCADDMULTI
and SIOCDELMULTI, or examine the argument; the drivers just commit
whatever multicast addresses are listed in the ethercom.
Other than the different ioctl submitted, there is no change to the
ABI or locking scheme of usbnet, so this is safe to pull up to
netbsd-9. This means we unfortunately can't guarantee that if a
process issues SIOCADDMULTI and then sendto, the multicast filter
update will be done by the time of the sendto -- and, more
importantly, the packets received in reply to it. But failing to
guarantee that is better than deadlocking! Later changes on HEAD
will restore the synchronous multicast filter updates with much more
extensive ABI changes and API simplifications in usbnet(9).
Proposed on tech-net:
https://mail-index.netbsd.org/tech-net/2021/12/30/msg008164.html
XXX pullup-9
2022-01-30 00:37:07 +03:00
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
|
2022-03-03 08:48:52 +03:00
|
|
|
mutex_enter(&unp->unp_core_lock);
|
|
|
|
unp->unp_refcnt--;
|
|
|
|
while (unp->unp_refcnt >= 0) {
|
|
|
|
/* Wait for processes to go away */
|
|
|
|
cv_wait(&unp->unp_detachcv, &unp->unp_core_lock);
|
|
|
|
}
|
|
|
|
mutex_exit(&unp->unp_core_lock);
|
|
|
|
|
|
|
|
usbnet_rx_list_free(un);
|
|
|
|
usbnet_tx_list_free(un);
|
|
|
|
|
|
|
|
rnd_detach_source(&unp->unp_rndsrc);
|
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
cv_destroy(&unp->unp_detachcv);
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_destroy(&unp->unp_core_lock);
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_destroy(&unp->unp_rxlock);
|
|
|
|
mutex_destroy(&unp->unp_txlock);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2022-03-03 08:49:00 +03:00
|
|
|
callout_destroy(&unp->unp_stat_ch);
|
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
pmf_device_deregister(un->un_dev);
|
|
|
|
|
2022-03-03 08:49:07 +03:00
|
|
|
/*
|
|
|
|
* Notify userland that we're going away, if we arrived in the
|
|
|
|
* first place.
|
|
|
|
*/
|
|
|
|
if (unp->unp_ifp_attached) {
|
|
|
|
usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, un->un_udev,
|
|
|
|
un->un_dev);
|
|
|
|
}
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
kmem_free(unp, sizeof(*unp));
|
2019-09-08 21:59:32 +03:00
|
|
|
un->un_pri = NULL;
|
2019-08-10 05:17:36 +03:00
|
|
|
|
2019-07-31 12:13:16 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
usbnet_activate(device_t self, devact_t act)
|
|
|
|
{
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
USBNETHIST_FUNC(); USBNETHIST_CALLED();
|
2019-07-31 12:13:16 +03:00
|
|
|
struct usbnet * const un = device_private(self);
|
2019-08-10 05:17:36 +03:00
|
|
|
struct usbnet_private * const unp = un->un_pri;
|
2019-07-31 12:13:16 +03:00
|
|
|
struct ifnet * const ifp = usbnet_ifp(un);
|
|
|
|
|
|
|
|
switch (act) {
|
|
|
|
case DVACT_DEACTIVATE:
|
|
|
|
if_deactivate(ifp);
|
|
|
|
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_enter(&unp->unp_core_lock);
|
2019-08-10 05:17:36 +03:00
|
|
|
unp->unp_dying = true;
|
2020-03-16 02:04:50 +03:00
|
|
|
mutex_exit(&unp->unp_core_lock);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
2019-08-10 05:17:36 +03:00
|
|
|
mutex_enter(&unp->unp_rxlock);
|
|
|
|
mutex_enter(&unp->unp_txlock);
|
|
|
|
unp->unp_stopping = true;
|
|
|
|
mutex_exit(&unp->unp_txlock);
|
|
|
|
mutex_exit(&unp->unp_rxlock);
|
2019-07-31 12:13:16 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MODULE(MODULE_CLASS_MISC, usbnet, NULL);
|
|
|
|
|
|
|
|
static int
|
|
|
|
usbnet_modcmd(modcmd_t cmd, void *arg)
|
|
|
|
{
|
|
|
|
switch (cmd) {
|
|
|
|
case MODULE_CMD_INIT:
|
- adjust usbnet interface to allow usbd_open_pipe_intr(), from the
new comment:
* if un_intr_buf is not NULL, use usbd_open_pipe_intr() not
* usbd_open_pipe() for USBNET_ENDPT_INTR, with this buffer,
* size, and interval.
the standard handling is in usbnet.c, with a callback to deal with
the interrupt it self. not fully tested, designed for if_aue.c
and a few others not yet converted.
- make usbhist for usbnet.c work, thanks paulg
- usbnet_init_rx_tx() clears out all allocations upon failure now
- add usbnet_ec() to get a pointer to the struct ethercom
- add usbnet_{lock,unlock,owned}*() to lock/unlock the various locks
and *owned*() for asserting
welcome 9.99.3!
2019-08-04 11:59:13 +03:00
|
|
|
return 0;
|
2019-07-31 12:13:16 +03:00
|
|
|
case MODULE_CMD_FINI:
|
|
|
|
return 0;
|
|
|
|
case MODULE_CMD_STAT:
|
|
|
|
case MODULE_CMD_AUTOUNLOAD:
|
|
|
|
default:
|
|
|
|
return ENOTTY;
|
|
|
|
}
|
|
|
|
}
|