NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: kern/57965: urtwn(4) command queue can overflow and deadlock
The attached patch is a candidate workaround, not yet tested.
From 491269b43e8cdbe70b174550cbd70fe8369baacf Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Mon, 26 Feb 2024 19:51:13 +0000
Subject: [PATCH] urtwn(4): Ditch old queued commands on overflow.
Don't increment ring->queued past what the task will decrement.
This is a stop-gap measure; really, we should just have one task for
each operation that is deferred to the task thread.
PR kern/57965
---
sys/dev/usb/if_urtwn.c | 47 ++++++++++++++++++++++++++++++++++++------
1 file changed, 41 insertions(+), 6 deletions(-)
diff --git a/sys/dev/usb/if_urtwn.c b/sys/dev/usb/if_urtwn.c
index 7e7c6c176d6d..f9fd12143fca 100644
--- a/sys/dev/usb/if_urtwn.c
+++ b/sys/dev/usb/if_urtwn.c
@@ -871,6 +871,24 @@ urtwn_tx_beacon(struct urtwn_softc *sc, struct mbuf *m,
return urtwn_tx(sc, m, ni, data);
}
+static void
+urtwn_cmdq_invariants(struct urtwn_softc *sc)
+{
+ struct urtwn_host_cmd_ring *const ring = &sc->cmdq;
+
+ KASSERT(mutex_owned(&sc->sc_task_mtx));
+ KASSERTMSG((ring->cur >= 0 && ring->cur < URTWN_HOST_CMD_RING_COUNT),
+ "%s: cur=%d next=%d queued=%d",
+ device_xname(sc->sc_dev), ring->cur, ring->next, ring->queued);
+ KASSERTMSG((ring->next >= 0 && ring->next < URTWN_HOST_CMD_RING_COUNT),
+ "%s: cur=%d next=%d queued=%d",
+ device_xname(sc->sc_dev), ring->cur, ring->next, ring->queued);
+ KASSERTMSG((ring->queued >= 0 &&
+ ring->queued <= URTWN_HOST_CMD_RING_COUNT),
+ "%s: %d commands queued",
+ device_xname(sc->sc_dev), ring->queued);
+}
+
static void
urtwn_task(void *arg)
{
@@ -903,7 +921,11 @@ urtwn_task(void *arg)
/* Process host commands. */
s = splusb();
mutex_spin_enter(&sc->sc_task_mtx);
+ urtwn_cmdq_invariants(sc);
while (ring->next != ring->cur) {
+ KASSERTMSG(ring->queued > 0, "%s: cur=%d next=%d queued=%d",
+ device_xname(sc->sc_dev),
+ ring->cur, ring->next, ring->queued);
cmd = &ring->cmd[ring->next];
mutex_spin_exit(&sc->sc_task_mtx);
splx(s);
@@ -911,6 +933,10 @@ urtwn_task(void *arg)
cmd->cb(sc, cmd->data);
s = splusb();
mutex_spin_enter(&sc->sc_task_mtx);
+ urtwn_cmdq_invariants(sc);
+ KASSERTMSG(ring->queued > 0, "%s: cur=%d next=%d queued=%d",
+ device_xname(sc->sc_dev),
+ ring->cur, ring->next, ring->queued);
ring->queued--;
ring->next = (ring->next + 1) % URTWN_HOST_CMD_RING_COUNT;
}
@@ -925,6 +951,7 @@ urtwn_do_async(struct urtwn_softc *sc, void (*cb)(struct urtwn_softc *, void *),
{
struct urtwn_host_cmd_ring *ring = &sc->cmdq;
struct urtwn_host_cmd *cmd;
+ bool schedtask = false;
int s;
URTWNHIST_FUNC();
@@ -933,19 +960,27 @@ urtwn_do_async(struct urtwn_softc *sc, void (*cb)(struct urtwn_softc *, void *),
s = splusb();
mutex_spin_enter(&sc->sc_task_mtx);
+ urtwn_cmdq_invariants(sc);
cmd = &ring->cmd[ring->cur];
cmd->cb = cb;
KASSERT(len <= sizeof(cmd->data));
memcpy(cmd->data, arg, len);
ring->cur = (ring->cur + 1) % URTWN_HOST_CMD_RING_COUNT;
- /* If there is no pending command already, schedule a task. */
- if (!sc->sc_dying && ++ring->queued == 1) {
- mutex_spin_exit(&sc->sc_task_mtx);
- usb_add_task(sc->sc_udev, &sc->sc_task, USB_TASKQ_DRIVER);
- } else
- mutex_spin_exit(&sc->sc_task_mtx);
+ /*
+ * Schedule a task to process the command if need be.
+ */
+ if (!sc->sc_dying) {
+ if (ring->queued == URTWN_HOST_CMD_RING_COUNT)
+ device_printf(sc->sc_dev, "command queue overflow\n");
+ else if (ring->queued++ == 0)
+ schedtask = true;
+ }
+ mutex_spin_exit(&sc->sc_task_mtx);
splx(s);
+
+ if (schedtask)
+ usb_add_task(sc->sc_udev, &sc->sc_task, USB_TASKQ_DRIVER);
}
static void
Home |
Main Index |
Thread Index |
Old Index