summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/usb/if_aue.c170
-rw-r--r--sys/dev/usb/if_auereg.h14
2 files changed, 164 insertions, 20 deletions
diff --git a/sys/dev/usb/if_aue.c b/sys/dev/usb/if_aue.c
index 223eb91..693a107 100644
--- a/sys/dev/usb/if_aue.c
+++ b/sys/dev/usb/if_aue.c
@@ -74,9 +74,10 @@ __FBSDID("$FreeBSD$");
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
+#include <sys/kdb.h>
#include <sys/module.h>
#include <sys/socket.h>
-#include <sys/sx.h>
+#include <sys/condvar.h>
#include <sys/taskqueue.h>
#include <net/if.h>
@@ -205,6 +206,7 @@ static void aue_start(struct ifnet *);
static void aue_start_thread(struct aue_softc *);
static int aue_ioctl(struct ifnet *, u_long, caddr_t);
static void aue_init(void *);
+static void aue_init_body(struct aue_softc *);
static void aue_stop(struct aue_softc *);
static void aue_watchdog(struct ifnet *);
static void aue_shutdown(device_t);
@@ -225,6 +227,14 @@ static int aue_csr_write_1(struct aue_softc *, int, int);
static int aue_csr_read_2(struct aue_softc *, int);
static int aue_csr_write_2(struct aue_softc *, int, int);
+static int aue_xlock(struct aue_softc *, int);
+static void aue_xunlock(struct aue_softc *);
+static void aue_xlockkill(struct aue_softc *);
+static void aue_xlockrevive(struct aue_softc *);
+static void aue_xlockassert(struct aue_softc *, int,
+ const char *, const char *, int);
+static void aue_stopasynctasks(struct aue_softc *);
+
static device_method_t aue_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, aue_match),
@@ -530,6 +540,7 @@ aue_setmulti(struct aue_softc *sc)
u_int32_t h = 0, i;
u_int8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ AUE_SXASSERTLOCKED(sc);
ifp = sc->aue_ifp;
if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
@@ -702,7 +713,7 @@ USB_ATTACH(aue)
mtx_init(&sc->aue_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK,
MTX_DEF | MTX_RECURSE);
- sx_init(&sc->aue_sx, device_get_nameunit(self));
+ cv_init(&sc->aue_cv, device_get_nameunit(self));
AUE_SXLOCK(sc);
/* Reset the adapter. */
@@ -718,7 +729,7 @@ USB_ATTACH(aue)
printf("aue%d: can not if_alloc()\n", sc->aue_unit);
AUE_SXUNLOCK(sc);
mtx_destroy(&sc->aue_mtx);
- sx_destroy(&sc->aue_sx);
+ cv_destroy(&sc->aue_cv);
USB_ATTACH_ERROR_RETURN;
}
ifp->if_softc = sc;
@@ -750,7 +761,7 @@ USB_ATTACH(aue)
if_free(ifp);
AUE_SXUNLOCK(sc);
mtx_destroy(&sc->aue_mtx);
- sx_destroy(&sc->aue_sx);
+ cv_destroy(&sc->aue_cv);
USB_ATTACH_ERROR_RETURN;
}
@@ -763,6 +774,7 @@ USB_ATTACH(aue)
ether_ifattach(ifp, eaddr);
usb_register_netisr();
sc->aue_dying = 0;
+ sc->aue_link = 1;
AUE_SXUNLOCK(sc);
USB_ATTACH_SUCCESS_RETURN;
@@ -779,8 +791,7 @@ aue_detach(device_t dev)
ifp = sc->aue_ifp;
sc->aue_dying = 1;
- callout_drain(&sc->aue_tick_callout);
- taskqueue_drain(taskqueue_thread, &sc->aue_task);
+ aue_stopasynctasks(sc);
ether_ifdetach(ifp);
if_free(ifp);
@@ -795,7 +806,7 @@ aue_detach(device_t dev)
AUE_SXUNLOCK(sc);
mtx_destroy(&sc->aue_mtx);
- sx_destroy(&sc->aue_sx);
+ cv_destroy(&sc->aue_cv);
return (0);
}
@@ -860,6 +871,7 @@ aue_rxeof_thread(struct aue_softc *sc)
usbd_status status = c->ue_status;
+ AUE_SXASSERTLOCKED(sc);
if (sc->aue_dying)
return;
ifp = sc->aue_ifp;
@@ -1109,6 +1121,15 @@ static void
aue_init(void *xsc)
{
struct aue_softc *sc = xsc;
+
+ AUE_SXLOCK(sc);
+ aue_init_body(sc);
+ AUE_SXUNLOCK(sc);
+}
+
+static void
+aue_init_body(struct aue_softc *sc)
+{
struct ifnet *ifp = sc->aue_ifp;
struct mii_data *mii = GET_MII(sc);
struct ue_chain *c;
@@ -1212,6 +1233,7 @@ aue_ifmedia_upd(struct ifnet *ifp)
mii_phy_reset(miisc);
}
mii_mediachg(mii);
+ sc->aue_link = 1;
return (0);
}
@@ -1240,11 +1262,14 @@ aue_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
struct mii_data *mii;
int error = 0;
+ if (sc->aue_dying)
+ return(0);
+
AUE_GIANTLOCK();
- AUE_SXLOCK(sc);
switch(command) {
case SIOCSIFFLAGS:
+ AUE_SXLOCK(sc);
if (ifp->if_flags & IFF_UP) {
if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
ifp->if_flags & IFF_PROMISC &&
@@ -1255,30 +1280,32 @@ aue_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
sc->aue_if_flags & IFF_PROMISC) {
AUE_CLRBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC);
} else if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
- aue_init(sc);
+ aue_init_body(sc);
} else {
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
aue_stop(sc);
}
sc->aue_if_flags = ifp->if_flags;
- error = 0;
+ AUE_SXUNLOCK(sc);
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
+ AUE_SXLOCK(sc);
aue_setmulti(sc);
- error = 0;
+ AUE_SXUNLOCK(sc);
break;
case SIOCGIFMEDIA:
case SIOCSIFMEDIA:
+ AUE_SXLOCK(sc);
mii = GET_MII(sc);
error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
+ AUE_SXUNLOCK(sc);
break;
default:
error = ether_ioctl(ifp, command, data);
break;
}
- AUE_SXUNLOCK(sc);
AUE_GIANTUNLOCK();
return (error);
@@ -1329,8 +1356,7 @@ aue_stop(struct aue_softc *sc)
aue_csr_write_1(sc, AUE_CTL0, 0);
aue_csr_write_1(sc, AUE_CTL1, 0);
aue_reset(sc);
- callout_drain(&sc->aue_tick_callout);
- taskqueue_drain(taskqueue_thread, &sc->aue_task);
+ aue_stopasynctasks(sc);
/* Stop transfers. */
if (sc->aue_ep[AUE_ENDPT_RX] != NULL) {
@@ -1423,6 +1449,111 @@ aue_task_sched(struct aue_softc *sc, int task)
AUE_UNLOCK(sc);
}
+static int
+aue_xlock(struct aue_softc *sc, int interruptable)
+{
+ AUE_LOCK(sc);
+ while ((sc->aue_lockflags & AUE_LOCKED) != 0) {
+ if (sc->aue_locker == curthread) {
+#if 1
+ panic("aue: locking against myself");
+#else
+ kdb_backtrace();
+ printf("aue: locking against myself\n");
+#endif
+ }
+ if (interruptable) {
+ if ((sc->aue_lockflags & AUE_LOCKDEAD) != 0) {
+ AUE_UNLOCK(sc);
+ return (ENOLCK);
+ }
+ }
+ cv_wait(&sc->aue_cv, &sc->aue_mtx);
+ }
+ sc->aue_lockflags |= AUE_LOCKED;
+ sc->aue_locker = curthread;
+ AUE_UNLOCK(sc);
+ return (0);
+}
+
+static void
+aue_xunlock(struct aue_softc *sc)
+{
+
+ AUE_LOCK(sc);
+ if (sc->aue_locker != curthread) {
+#if 1
+ panic("unlocking lock not owned by me");
+#else
+ kdb_backtrace();
+ printf("unlocking lock not owned by me\n");
+#endif
+ }
+ sc->aue_lockflags &= ~AUE_LOCKED;
+ sc->aue_locker = NULL;
+ cv_signal(&sc->aue_cv);
+ AUE_UNLOCK(sc);
+}
+
+static void
+aue_xlockkill(struct aue_softc *sc)
+{
+
+ AUE_LOCK(sc);
+ sc->aue_lockflags |= AUE_LOCKDEAD;
+ cv_broadcast(&sc->aue_cv);
+ AUE_UNLOCK(sc);
+}
+
+static void
+aue_xlockrevive(struct aue_softc *sc)
+{
+
+ AUE_LOCK(sc);
+ sc->aue_lockflags &= ~AUE_LOCKDEAD;
+ cv_broadcast(&sc->aue_cv);
+ AUE_UNLOCK(sc);
+}
+
+static void
+aue_xlockassert(
+ struct aue_softc *sc,
+ int locked,
+ const char *file,
+ const char *fun,
+ int line)
+{
+ struct thread *td;
+ int flags;
+
+ AUE_LOCK(sc);
+ flags = sc->aue_lockflags;
+ td = sc->aue_locker;
+ AUE_UNLOCK(sc);
+ if (locked == 1) {
+ if ((flags & AUE_LOCKED) == 0 || td != curthread) {
+ panic("aue assert: lock not owned @%s:%s:%d",
+ file, fun, line);
+ }
+ } else {
+ if ((flags & AUE_LOCKED) != 0 && td == curthread) {
+ panic("aue assert: lock owned @%s:%s:%d",
+ file, fun, line);
+ }
+ }
+}
+
+static void
+aue_stopasynctasks(struct aue_softc *sc)
+{
+
+ AUE_SXASSERTLOCKED(sc);
+ callout_drain(&sc->aue_tick_callout);
+ aue_xlockkill(sc);
+ taskqueue_drain(taskqueue_thread, &sc->aue_task);
+ aue_xlockrevive(sc);
+}
+
/*
* We defer all interrupt operations to this function.
*
@@ -1440,7 +1571,16 @@ aue_task(void *arg, int pending)
sc->aue_deferedtasks = 0;
AUE_UNLOCK(sc);
AUE_GIANTLOCK(); // XXX: usb not giant safe
- AUE_SXLOCK(sc);
+ /*
+ * Try to lock the exclusive lock, if we fail
+ * then we are probably draining the taskqueue.
+ * If we did an unconditional lock here we would
+ * deadlock.
+ */
+ if (aue_xlock(sc, 1)) {
+ AUE_GIANTUNLOCK(); // XXX: usb not giant safe
+ break;
+ }
if ((tasks & AUE_TASK_WATCHDOG) != 0) {
aue_watchdog_thread(sc);
}
diff --git a/sys/dev/usb/if_auereg.h b/sys/dev/usb/if_auereg.h
index 02158c3..8d4f528 100644
--- a/sys/dev/usb/if_auereg.h
+++ b/sys/dev/usb/if_auereg.h
@@ -228,7 +228,11 @@ struct aue_softc {
struct callout aue_tick_callout;
struct task aue_task;
struct mtx aue_mtx;
- struct sx aue_sx;
+ struct cv aue_cv;
+ struct thread * aue_locker; /* lock owner */
+ int aue_lockflags;
+#define AUE_LOCKED 0x01 /* locked */
+#define AUE_LOCKDEAD 0x02 /* lock draining */
u_int16_t aue_flags;
char aue_dying;
struct timeval aue_rx_notice;
@@ -265,10 +269,10 @@ aue_dumpstate(const char *func, const char *tag)
#define AUE_LOCK(_sc) mtx_lock(&(_sc)->aue_mtx)
#define AUE_UNLOCK(_sc) mtx_unlock(&(_sc)->aue_mtx)
#define AUE_SXLOCK(_sc) \
- do { AUE_DUMPSTATE("sxlock"); sx_xlock(&(_sc)->aue_sx); } while(0)
-#define AUE_SXUNLOCK(_sc) sx_xunlock(&(_sc)->aue_sx)
-#define AUE_SXASSERTLOCKED(_sc) sx_assert(&(_sc)->aue_sx, SX_XLOCKED)
-#define AUE_SXASSERTUNLOCKED(_sc) sx_assert(&(_sc)->aue_sx, SX_UNLOCKED)
+ do { AUE_DUMPSTATE("sxlock"); aue_xlock((_sc), 0); } while(0)
+#define AUE_SXUNLOCK(_sc) aue_xunlock(_sc)
+#define AUE_SXASSERTLOCKED(_sc) aue_xlockassert((_sc), 1, __FILE__, __func__, __LINE__)
+#define AUE_SXASSERTUNLOCKED(_sc) aue_xlockassert((_sc), 0, __FILE__, __func__, __LINE__)
#define AUE_TIMEOUT 1000
#define AUE_MIN_FRAMELEN 60
OpenPOWER on IntegriCloud