diff --git a/sys/netgraph7/bluetooth/drivers/bt3c/ng_bt3c_var.h b/sys/netgraph7/bluetooth/drivers/bt3c/ng_bt3c_var.h --- a/sys/netgraph7/bluetooth/drivers/bt3c/ng_bt3c_var.h +++ b/sys/netgraph7/bluetooth/drivers/bt3c/ng_bt3c_var.h @@ -92,6 +92,15 @@ u_int32_t status; /* from ISR */ void *ith; /* ithread handler */ + /* + * Serializes inq/outq mutation between the hard ISR (bt3c_intr -> + * bt3c_receive: IF_ENQUEUE) and the SWI/netgraph paths (bt3c_forward, + * bt3c_send, bt3c_rcvdata: IF_DEQUEUE/IF_ENQUEUE). IF_*QUEUE macros + * are lock-free linked-list ops, so without this the hard ISR can + * corrupt the list mid-IF_DEQUEUE. (DF-0533) + */ + struct spinlock sc_lock; + struct mbuf *m; /* current frame */ u_int32_t want; /* # of chars we want */ diff --git a/sys/netgraph7/bluetooth/drivers/bt3c/ng_bt3c_pccard.c b/sys/netgraph7/bluetooth/drivers/bt3c/ng_bt3c_pccard.c --- a/sys/netgraph7/bluetooth/drivers/bt3c/ng_bt3c_pccard.c +++ b/sys/netgraph7/bluetooth/drivers/bt3c/ng_bt3c_pccard.c @@ -53,6 +53,7 @@ #include #include +#include #include #include @@ -554,6 +555,7 @@ NGI_GET_M(item, m); + spin_lock(&sc->sc_lock); if (IF_QFULL(&sc->outq)) { NG_BT3C_ERR(sc->dev, "Outgoing queue is full. Dropping mbuf, len=%d\n", m->m_pkthdr.len); @@ -564,6 +566,7 @@ NG_FREE_M(m); } else IF_ENQUEUE(&sc->outq, m); + spin_unlock(&sc->sc_lock); error = ng_send_fn(sc->node, NULL, bt3c_send, NULL, 0 /* new send */); out: @@ -666,6 +669,7 @@ sc->debug = NG_BT3C_WARN_LEVEL; sc->inq.ifq_maxlen = sc->outq.ifq_maxlen = BT3C_DEFAULTQLEN; + spin_init(&sc->sc_lock, "bt3c sc"); sc->state = NG_BT3C_W4_PKT_IND; sc->want = 1; @@ -927,6 +931,7 @@ NG_BT3C_STAT_BYTES_RECV(sc->stat, sc->m->m_pkthdr.len); NG_BT3C_STAT_PCKTS_RECV(sc->stat); + spin_lock(&sc->sc_lock); if (IF_QFULL(&sc->inq)) { NG_BT3C_ERR(sc->dev, "Incoming queue is full. Dropping mbuf, len=%d\n", sc->m->m_pkthdr.len); @@ -939,6 +944,7 @@ IF_ENQUEUE(&sc->inq, sc->m); sc->m = NULL; } + spin_unlock(&sc->sc_lock); sc->state = NG_BT3C_W4_PKT_IND; sc->want = 1; @@ -1013,6 +1019,7 @@ return; if (sc->hook != NULL && NG_HOOK_IS_VALID(sc->hook)) { + spin_lock(&sc->sc_lock); for (;;) { IF_DEQUEUE(&sc->inq, m); if (m == NULL) @@ -1022,7 +1029,9 @@ if (error != 0) NG_BT3C_STAT_IERROR(sc->stat); } + spin_unlock(&sc->sc_lock); } else { + spin_lock(&sc->sc_lock); for (;;) { IF_DEQUEUE(&sc->inq, m); if (m == NULL) @@ -1031,6 +1040,7 @@ NG_BT3C_STAT_IERROR(sc->stat); NG_FREE_M(m); } + spin_unlock(&sc->sc_lock); } } /* bt3c_forward */ @@ -1056,6 +1066,7 @@ bt3c_set_address(sc, 0x7080); + spin_lock(&sc->sc_lock); for (wrote = 0; wrote < BT3C_FIFO_SIZE; ) { IF_DEQUEUE(&sc->outq, m); if (m == NULL) @@ -1084,6 +1095,7 @@ NG_BT3C_STAT_PCKTS_SENT(sc->stat); } + spin_unlock(&sc->sc_lock); if (wrote > 0) { NG_BT3C_INFO(sc->dev, "Wrote %d bytes\n", wrote);