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 following reply was made to PR kern/57965; it has been noted by GNATS.

From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
To: gnats-bugs%NetBSD.org@localhost
Cc: netbsd-bugs%NetBSD.org@localhost
Subject: Re: kern/57965: urtwn(4) command queue can overflow and deadlock
Date: Mon, 26 Feb 2024 20:24:18 +0000

 This is a multi-part message in MIME format.
 --=_QJyEfqlAfncoM8fB56hLDQdX5vxg3caH
 
 The attached patch is a candidate workaround, not yet tested.
 
 --=_QJyEfqlAfncoM8fB56hLDQdX5vxg3caH
 Content-Type: text/plain; charset="ISO-8859-1"; name="pr57965-urtwncmdqoverflow"
 Content-Transfer-Encoding: quoted-printable
 Content-Disposition: attachment; filename="pr57965-urtwncmdqoverflow.patch"
 
 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);
  }
 =20
 +static void
 +urtwn_cmdq_invariants(struct urtwn_softc *sc)
 +{
 +	struct urtwn_host_cmd_ring *const ring =3D &sc->cmdq;
 +
 +	KASSERT(mutex_owned(&sc->sc_task_mtx));
 +	KASSERTMSG((ring->cur >=3D 0 && ring->cur < URTWN_HOST_CMD_RING_COUNT),
 +	    "%s: cur=3D%d next=3D%d queued=3D%d",
 +	    device_xname(sc->sc_dev), ring->cur, ring->next, ring->queued);
 +	KASSERTMSG((ring->next >=3D 0 && ring->next < URTWN_HOST_CMD_RING_COUNT),
 +	    "%s: cur=3D%d next=3D%d queued=3D%d",
 +	    device_xname(sc->sc_dev), ring->cur, ring->next, ring->queued);
 +	KASSERTMSG((ring->queued >=3D 0 &&
 +		ring->queued <=3D 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 =3D splusb();
  	mutex_spin_enter(&sc->sc_task_mtx);
 +	urtwn_cmdq_invariants(sc);
  	while (ring->next !=3D ring->cur) {
 +		KASSERTMSG(ring->queued > 0, "%s: cur=3D%d next=3D%d queued=3D%d",
 +		    device_xname(sc->sc_dev),
 +		    ring->cur, ring->next, ring->queued);
  		cmd =3D &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 =3D splusb();
  		mutex_spin_enter(&sc->sc_task_mtx);
 +		urtwn_cmdq_invariants(sc);
 +		KASSERTMSG(ring->queued > 0, "%s: cur=3D%d next=3D%d queued=3D%d",
 +		    device_xname(sc->sc_dev),
 +		    ring->cur, ring->next, ring->queued);
  		ring->queued--;
  		ring->next =3D (ring->next + 1) % URTWN_HOST_CMD_RING_COUNT;
  	}
 @@ -925,6 +951,7 @@ urtwn_do_async(struct urtwn_softc *sc, void (*cb)(struc=
 t urtwn_softc *, void *),
  {
  	struct urtwn_host_cmd_ring *ring =3D &sc->cmdq;
  	struct urtwn_host_cmd *cmd;
 +	bool schedtask =3D false;
  	int s;
 =20
  	URTWNHIST_FUNC();
 @@ -933,19 +960,27 @@ urtwn_do_async(struct urtwn_softc *sc, void (*cb)(str=
 uct urtwn_softc *, void *),
 =20
  	s =3D splusb();
  	mutex_spin_enter(&sc->sc_task_mtx);
 +	urtwn_cmdq_invariants(sc);
  	cmd =3D &ring->cmd[ring->cur];
  	cmd->cb =3D cb;
  	KASSERT(len <=3D sizeof(cmd->data));
  	memcpy(cmd->data, arg, len);
  	ring->cur =3D (ring->cur + 1) % URTWN_HOST_CMD_RING_COUNT;
 =20
 -	/* If there is no pending command already, schedule a task. */
 -	if (!sc->sc_dying && ++ring->queued =3D=3D 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 =3D=3D URTWN_HOST_CMD_RING_COUNT)
 +			device_printf(sc->sc_dev, "command queue overflow\n");
 +		else if (ring->queued++ =3D=3D 0)
 +			schedtask =3D true;
 +	}
 +	mutex_spin_exit(&sc->sc_task_mtx);
  	splx(s);
 +
 +	if (schedtask)
 +		usb_add_task(sc->sc_udev, &sc->sc_task, USB_TASKQ_DRIVER);
  }
 =20
  static void
 
 --=_QJyEfqlAfncoM8fB56hLDQdX5vxg3caH--
 


Home | Main Index | Thread Index | Old Index