Module Name:    src
Committed By:   skrll
Date:           Fri Sep 16 03:55:53 UTC 2022

Modified Files:
        src/sys/dev/pci: if_aq.c

Log Message:
Some MP improvements

- Remove use of IFF_OACTIVE

- Remove use of if_timer and provide an MP safe multiqueue watchdog

- Sprinkle some lock assertions.

Tested by ryo@. Thanks.


To generate a diff of this commit:
cvs rdiff -u -r1.32 -r1.33 src/sys/dev/pci/if_aq.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/pci/if_aq.c
diff -u src/sys/dev/pci/if_aq.c:1.32 src/sys/dev/pci/if_aq.c:1.33
--- src/sys/dev/pci/if_aq.c:1.32	Thu Sep  8 07:05:42 2022
+++ src/sys/dev/pci/if_aq.c	Fri Sep 16 03:55:53 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_aq.c,v 1.32 2022/09/08 07:05:42 skrll Exp $	*/
+/*	$NetBSD: if_aq.c,v 1.33 2022/09/16 03:55:53 skrll Exp $	*/
 
 /**
  * aQuantia Corporation Network Driver
@@ -62,7 +62,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_aq.c,v 1.32 2022/09/08 07:05:42 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_aq.c,v 1.33 2022/09/16 03:55:53 skrll Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_if_aq.h"
@@ -854,6 +854,9 @@ struct aq_txring {
 	int txr_index;
 	kmutex_t txr_mutex;
 	bool txr_active;
+	bool txr_stopping;
+	bool txr_sending;
+	time_t txr_lastsent;
 
 	pcq_t *txr_pcq;
 	void *txr_softint;
@@ -878,6 +881,7 @@ struct aq_rxring {
 	kmutex_t rxr_mutex;
 	bool rxr_active;
 	bool rxr_discarding;
+	bool rxr_stopping;
 	struct mbuf *rxr_receiving_m;		/* receiving jumboframe */
 	struct mbuf *rxr_receiving_m_last;	/* last mbuf of jumboframe */
 
@@ -934,10 +938,12 @@ struct aq_firmware_ops {
 
 #define AQ_LOCK(sc)		mutex_enter(&(sc)->sc_mutex);
 #define AQ_UNLOCK(sc)		mutex_exit(&(sc)->sc_mutex);
+#define AQ_LOCKED(sc)		KASSERT(mutex_owned(&(sc)->sc_mutex));
 
 /* lock for FW2X_MPI_{CONTROL,STATE]_REG read-modify-write */
 #define AQ_MPI_LOCK(sc)		mutex_enter(&(sc)->sc_mpi_mutex);
 #define AQ_MPI_UNLOCK(sc)	mutex_exit(&(sc)->sc_mpi_mutex);
+#define AQ_MPI_LOCKED(sc)	KASSERT(mutex_owned(&(sc)->sc_mpi_mutex));
 
 
 struct aq_softc {
@@ -1018,6 +1024,15 @@ struct aq_softc {
 	int sc_ec_capenable;		/* last ec_capenable */
 	unsigned short sc_if_flags;	/* last if_flags */
 
+	bool sc_tx_sending;
+	bool sc_stopping;
+
+	struct workqueue *sc_reset_wq;
+	struct work sc_reset_work;
+	volatile unsigned sc_reset_pending;
+
+	bool sc_trigger_reset;
+
 #ifdef AQ_EVENT_COUNTERS
 	aq_hw_stats_s_t sc_statistics[2];
 	int sc_statistics_idx;
@@ -1059,13 +1074,14 @@ static void aq_ifmedia_status(struct ifn
 static int aq_vlan_cb(struct ethercom *ec, uint16_t vid, bool set);
 static int aq_ifflags_cb(struct ethercom *);
 static int aq_init(struct ifnet *);
+static int aq_init_locked(struct ifnet *);
 static void aq_send_common_locked(struct ifnet *, struct aq_softc *,
     struct aq_txring *, bool);
 static int aq_transmit(struct ifnet *, struct mbuf *);
 static void aq_deferred_transmit(void *);
 static void aq_start(struct ifnet *);
 static void aq_stop(struct ifnet *, int);
-static void aq_watchdog(struct ifnet *);
+static void aq_stop_locked(struct ifnet *, bool);
 static int aq_ioctl(struct ifnet *, unsigned long, void *);
 
 static int aq_txrx_rings_alloc(struct aq_softc *);
@@ -1076,6 +1092,10 @@ static void aq_tx_pcq_free(struct aq_sof
 static void aq_initmedia(struct aq_softc *);
 static void aq_enable_intr(struct aq_softc *, bool, bool);
 
+static void aq_handle_reset_work(struct work *, void *);
+static void aq_unset_stopping_flags(struct aq_softc *);
+static void aq_set_stopping_flags(struct aq_softc *);
+
 #if NSYSMON_ENVSYS > 0
 static void aq_temp_refresh(struct sysmon_envsys *, envsys_data_t *);
 #endif
@@ -1119,6 +1139,12 @@ static int fw2x_get_stats(struct aq_soft
 static int fw2x_get_temperature(struct aq_softc *, uint32_t *);
 #endif
 
+#ifndef AQ_WATCHDOG_TIMEOUT
+#define AQ_WATCHDOG_TIMEOUT 5
+#endif
+static int aq_watchdog_timeout = AQ_WATCHDOG_TIMEOUT;
+
+
 static const struct aq_firmware_ops aq_fw1x_ops = {
 	.reset = fw1x_reset,
 	.set_mode = fw1x_set_mode,
@@ -1370,9 +1396,20 @@ aq_attach(device_t parent, device_t self
 	if (error != 0)
 		goto attach_failure;
 
-	callout_init(&sc->sc_tick_ch, 0);
+	callout_init(&sc->sc_tick_ch, CALLOUT_MPSAFE);
 	callout_setfunc(&sc->sc_tick_ch, aq_tick, sc);
 
+	char wqname[MAXCOMLEN];
+	snprintf(wqname, sizeof(wqname), "%sReset", device_xname(sc->sc_dev));
+	error = workqueue_create(&sc->sc_reset_wq, wqname,
+	    aq_handle_reset_work, sc, PRI_SOFTNET, IPL_SOFTCLOCK,
+	    WQ_MPSAFE);
+	if (error) {
+		aprint_error_dev(sc->sc_dev,
+		    "unable to create reset workqueue\n");
+		goto attach_failure;
+	}
+
 	sc->sc_intr_moderation_enable = CONFIG_INTR_MODERATION_ENABLE;
 
 	if (sc->sc_msix && (sc->sc_nqueues > 1))
@@ -1423,7 +1460,7 @@ aq_attach(device_t parent, device_t self
 		ifp->if_transmit = aq_transmit;
 	ifp->if_start = aq_start;
 	ifp->if_stop = aq_stop;
-	ifp->if_watchdog = aq_watchdog;
+	ifp->if_watchdog = NULL;
 	IFQ_SET_READY(&ifp->if_snd);
 
 	/* initialize capabilities */
@@ -1551,9 +1588,7 @@ aq_detach(device_t self, int flags __unu
 
 	if (sc->sc_iosize != 0) {
 		if (ifp->if_softc != NULL) {
-			const int s = splnet();
 			aq_stop(ifp, 0);
-			splx(s);
 		}
 
 		for (i = 0; i < AQ_NINTR_MAX; i++) {
@@ -2753,6 +2788,7 @@ static int
 aq_ifmedia_change(struct ifnet * const ifp)
 {
 	struct aq_softc * const sc = ifp->if_softc;
+
 	aq_link_speed_t rate = AQ_LINK_NONE;
 	aq_link_fc_t fc = AQ_FC_NONE;
 	aq_link_eee_t eee = AQ_EEE_DISABLE;
@@ -3828,10 +3864,67 @@ aq_temp_refresh(struct sysmon_envsys *sm
 }
 #endif
 
+
+
+static bool
+aq_watchdog_check(struct aq_softc * const sc)
+{
+
+	AQ_LOCKED(sc);
+
+	bool ok = true;
+	for (u_int n = 0; n < sc->sc_nqueues; n++) {
+		struct aq_txring *txring = &sc->sc_queue[n].txring;
+
+		mutex_enter(&txring->txr_mutex);
+		if (txring->txr_sending &&
+		    time_uptime - txring->txr_lastsent > aq_watchdog_timeout)
+			ok = false;
+
+		mutex_exit(&txring->txr_mutex);
+
+		if (!ok)
+			return false;
+	}
+
+	if (sc->sc_trigger_reset) {
+		/* debug operation, no need for atomicity or reliability */
+		sc->sc_trigger_reset = 0;
+		return false;
+	}
+
+	return true;
+}
+
+
+
+static bool
+aq_watchdog_tick(struct ifnet *ifp)
+{
+	struct aq_softc * const sc = ifp->if_softc;
+
+	AQ_LOCKED(sc);
+
+	if (!sc->sc_trigger_reset && aq_watchdog_check(sc))
+		return true;
+
+	if (atomic_swap_uint(&sc->sc_reset_pending, 1) == 0) {
+		workqueue_enqueue(sc->sc_reset_wq, &sc->sc_reset_work, NULL);
+	}
+
+	return false;
+}
+
 static void
 aq_tick(void *arg)
 {
-	struct aq_softc *sc = arg;
+	struct aq_softc * const sc = arg;
+
+	AQ_LOCK(sc);
+	if (sc->sc_stopping) {
+		AQ_UNLOCK(sc);
+		return;
+	}
 
 	if (sc->sc_poll_linkstat || sc->sc_detect_linkstat) {
 		sc->sc_detect_linkstat = false;
@@ -3843,13 +3936,12 @@ aq_tick(void *arg)
 		aq_update_statistics(sc);
 #endif
 
-	if (sc->sc_poll_linkstat
-#ifdef AQ_EVENT_COUNTERS
-	    || sc->sc_poll_statistics
-#endif
-	    ) {
+	struct ifnet * const ifp = &sc->sc_ethercom.ec_if;
+	const bool ok = aq_watchdog_tick(ifp);
+	if (ok)
 		callout_schedule(&sc->sc_tick_ch, hz);
-	}
+
+	AQ_UNLOCK(sc);
 }
 
 /* interrupt enable/disable */
@@ -3931,7 +4023,7 @@ aq_txrx_intr(void *arg)
 static int
 aq_link_intr(void *arg)
 {
-	struct aq_softc *sc = arg;
+	struct aq_softc * const sc = arg;
 	uint32_t status;
 	int nintr = 0;
 
@@ -4229,6 +4321,7 @@ aq_tx_intr(void *arg)
 	hw_head = AQ_READ_REG_BIT(sc, TX_DMA_DESC_HEAD_PTR_REG(ringidx),
 	    TX_DMA_DESC_HEAD_PTR);
 	if (hw_head == txring->txr_considx) {
+		txring->txr_sending = false;
 		goto tx_intr_done;
 	}
 
@@ -4256,12 +4349,9 @@ aq_tx_intr(void *arg)
 
 	IF_STAT_PUTREF(ifp);
 
-	if (ringidx == 0 && txring->txr_nfree >= AQ_TXD_MIN)
-		ifp->if_flags &= ~IFF_OACTIVE;
-
 	/* no more pending TX packet, cancel watchdog */
 	if (txring->txr_nfree >= AQ_TXD_NUM)
-		ifp->if_timer = 0;
+		txring->txr_sending = false;
 
  tx_intr_done:
 	mutex_exit(&txring->txr_mutex);
@@ -4536,15 +4626,31 @@ aq_ifflags_cb(struct ethercom *ec)
 	return error;
 }
 
+
 static int
 aq_init(struct ifnet *ifp)
 {
 	struct aq_softc * const sc = ifp->if_softc;
+
+	AQ_LOCK(sc);
+
+	int ret = aq_init_locked(ifp);
+
+	AQ_UNLOCK(sc);
+
+	return ret;
+}
+
+static int
+aq_init_locked(struct ifnet *ifp)
+{
+	struct aq_softc * const sc = ifp->if_softc;
 	int i, error = 0;
 
-	aq_stop(ifp, false);
+	KASSERT(IFNET_LOCKED(ifp));
+	AQ_LOCKED(sc);
 
-	AQ_LOCK(sc);
+	aq_stop_locked(ifp, false);
 
 	aq_set_vlan_filters(sc);
 	aq_set_capability(sc);
@@ -4570,18 +4676,13 @@ aq_init(struct ifnet *ifp)
 	aq_init_rss(sc);
 	aq_hw_l3_filter_set(sc);
 
-	/* need to start callout? */
-	if (sc->sc_poll_linkstat
-#ifdef AQ_EVENT_COUNTERS
-	    || sc->sc_poll_statistics
-#endif
-	    ) {
-		callout_schedule(&sc->sc_tick_ch, hz);
-	}
+	/* ring reset? */
+	aq_unset_stopping_flags(sc);
+
+	callout_schedule(&sc->sc_tick_ch, hz);
 
 	/* ready */
 	ifp->if_flags |= IFF_RUNNING;
-	ifp->if_flags &= ~IFF_OACTIVE;
 
 	/* start TX and RX */
 	aq_enable_intr(sc, true, true);
@@ -4591,8 +4692,6 @@ aq_init(struct ifnet *ifp)
  aq_init_failure:
 	sc->sc_if_flags = ifp->if_flags;
 
-	AQ_UNLOCK(sc);
-
 	return error;
 }
 
@@ -4603,7 +4702,7 @@ aq_send_common_locked(struct ifnet *ifp,
 	struct mbuf *m;
 	int npkt, error;
 
-	if ((ifp->if_flags & IFF_RUNNING) == 0)
+	if (txring->txr_nfree < AQ_TXD_MIN)
 		return;
 
 	for (npkt = 0; ; npkt++) {
@@ -4615,9 +4714,6 @@ aq_send_common_locked(struct ifnet *ifp,
 		if (m == NULL)
 			break;
 
-		if (txring->txr_nfree < AQ_TXD_MIN)
-			break;
-
 		if (is_transmit)
 			pcq_get(txring->txr_pcq);
 		else
@@ -4628,8 +4724,6 @@ aq_send_common_locked(struct ifnet *ifp,
 			/* too many mbuf chains? or not enough descriptors? */
 			m_freem(m);
 			if_statinc(ifp, if_oerrors);
-			if (txring->txr_index == 0 && error == ENOBUFS)
-				ifp->if_flags |= IFF_OACTIVE;
 			break;
 		}
 
@@ -4641,11 +4735,11 @@ aq_send_common_locked(struct ifnet *ifp,
 		bpf_mtap(ifp, m, BPF_D_OUT);
 	}
 
-	if (txring->txr_index == 0 && txring->txr_nfree < AQ_TXD_MIN)
-		ifp->if_flags |= IFF_OACTIVE;
-
-	if (npkt)
-		ifp->if_timer = 5;
+	if (npkt) {
+		/* Set a watchdog timer in case the chip flakes out. */
+		txring->txr_lastsent = time_uptime;
+		txring->txr_sending = true;
+	}
 }
 
 static void
@@ -4656,7 +4750,7 @@ aq_start(struct ifnet *ifp)
 	struct aq_txring * const txring = &sc->sc_queue[0].txring;
 
 	mutex_enter(&txring->txr_mutex);
-	if (txring->txr_active && !ISSET(ifp->if_flags, IFF_OACTIVE))
+	if (txring->txr_active && !txring->txr_stopping)
 		aq_send_common_locked(ifp, sc, txring, false);
 	mutex_exit(&txring->txr_mutex);
 }
@@ -4701,15 +4795,80 @@ aq_deferred_transmit(void *arg)
 	mutex_exit(&txring->txr_mutex);
 }
 
+
+static void
+aq_unset_stopping_flags(struct aq_softc *sc)
+{
+
+	AQ_LOCKED(sc);
+
+	/* Must unset stopping flags in ascending order. */
+	for (unsigned i = 0; i < sc->sc_nqueues; i++) {
+		struct aq_txring *txr = &sc->sc_queue[i].txring;
+		struct aq_rxring *rxr = &sc->sc_queue[i].rxring;
+
+		mutex_enter(&txr->txr_mutex);
+		txr->txr_stopping = false;
+		mutex_exit(&txr->txr_mutex);
+
+		mutex_enter(&rxr->rxr_mutex);
+		rxr->rxr_stopping = false;
+		mutex_exit(&rxr->rxr_mutex);
+	}
+
+	sc->sc_stopping = false;
+}
+
+static void
+aq_set_stopping_flags(struct aq_softc *sc)
+{
+
+	AQ_LOCKED(sc);
+
+	/* Must unset stopping flags in ascending order. */
+	for (unsigned i = 0; i < sc->sc_nqueues; i++) {
+		struct aq_txring *txr = &sc->sc_queue[i].txring;
+		struct aq_rxring *rxr = &sc->sc_queue[i].rxring;
+
+		mutex_enter(&txr->txr_mutex);
+		txr->txr_stopping = true;
+		mutex_exit(&txr->txr_mutex);
+
+		mutex_enter(&rxr->rxr_mutex);
+		rxr->rxr_stopping = true;
+		mutex_exit(&rxr->rxr_mutex);
+	}
+
+	sc->sc_stopping = true;
+}
+
+
 static void
 aq_stop(struct ifnet *ifp, int disable)
 {
 	struct aq_softc * const sc = ifp->if_softc;
-	int i;
+
+	ASSERT_SLEEPABLE();
+	KASSERT(IFNET_LOCKED(ifp));
 
 	AQ_LOCK(sc);
+	aq_stop_locked(ifp, disable ? true : false);
+	AQ_UNLOCK(sc);
+}
+
+
+
+static void
+aq_stop_locked(struct ifnet *ifp, bool disable)
+{
+	struct aq_softc * const sc = ifp->if_softc;
+	int i;
+
+	ASSERT_SLEEPABLE();
+	KASSERT(IFNET_LOCKED(ifp));
+	AQ_LOCKED(sc);
 
-	ifp->if_timer = 0;
+	aq_set_stopping_flags(sc);
 
 	if ((ifp->if_flags & IFF_RUNNING) == 0)
 		goto already_stopped;
@@ -4732,25 +4891,25 @@ aq_stop(struct ifnet *ifp, int disable)
 	    AQ_READ_REG_BIT(sc,
 	    RX_DMA_DESC_CACHE_INIT_REG, RX_DMA_DESC_CACHE_INIT) ^ 1);
 
-	ifp->if_timer = 0;
-
  already_stopped:
 	if (!disable) {
 		/* when pmf stop, disable link status intr and callout */
 		aq_enable_intr(sc, false, false);
-		callout_stop(&sc->sc_tick_ch);
+		callout_halt(&sc->sc_tick_ch, &sc->sc_mutex);
 	}
 
-	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
-
-	AQ_UNLOCK(sc);
+	ifp->if_flags &= ~IFF_RUNNING;
+	sc->sc_if_flags = ifp->if_flags;
 }
 
+
 static void
-aq_watchdog(struct ifnet *ifp)
+aq_handle_reset_work(struct work *work, void *arg)
 {
-	struct aq_softc * const sc = ifp->if_softc;
-	int n, head, tail;
+	struct aq_softc * const sc = arg;
+	struct ifnet * const ifp = &sc->sc_ethercom.ec_if;
+
+	printf("%s: watchdog timeout -- resetting\n", ifp->if_xname);
 
 	AQ_LOCK(sc);
 
@@ -4758,15 +4917,15 @@ aq_watchdog(struct ifnet *ifp)
 	    __func__, AQ_READ_REG(sc, AQ_INTR_MASK_REG),
 	    AQ_READ_REG(sc, AQ_INTR_STATUS_REG));
 
-	for (n = 0; n < sc->sc_nqueues; n++) {
-		struct aq_txring * const txring = &sc->sc_queue[n].txring;
-		head = AQ_READ_REG_BIT(sc,
+	for (u_int n = 0; n < sc->sc_nqueues; n++) {
+		struct aq_txring *txring = &sc->sc_queue[n].txring;
+		u_int head = AQ_READ_REG_BIT(sc,
 		    TX_DMA_DESC_HEAD_PTR_REG(txring->txr_index),
-		    TX_DMA_DESC_HEAD_PTR),
-		tail = AQ_READ_REG(sc,
+		    TX_DMA_DESC_HEAD_PTR);
+		u_int tail = AQ_READ_REG(sc,
 		    TX_DMA_DESC_TAIL_PTR_REG(txring->txr_index));
 
-		device_printf(sc->sc_dev, "%s: TXring[%d] HEAD/TAIL=%d/%d\n",
+		device_printf(sc->sc_dev, "%s: TXring[%u] HEAD/TAIL=%u/%u\n",
 		    __func__, txring->txr_index, head, tail);
 
 		aq_tx_intr(txring);
@@ -4774,7 +4933,15 @@ aq_watchdog(struct ifnet *ifp)
 
 	AQ_UNLOCK(sc);
 
+	/* Don't want ioctl operations to happen */
+	IFNET_LOCK(ifp);
+
+	/* reset the interface. */
 	aq_init(ifp);
+
+	IFNET_UNLOCK(ifp);
+
+	atomic_store_relaxed(&sc->sc_reset_pending, 0);
 }
 
 static int
@@ -4784,6 +4951,14 @@ aq_ioctl(struct ifnet *ifp, unsigned lon
 	struct ifreq * const ifr = data;
 	int error = 0;
 
+	switch (cmd) {
+	case SIOCADDMULTI:
+	case SIOCDELMULTI:
+		break;
+	default:
+		KASSERT(IFNET_LOCKED(ifp));
+	}
+
 	const int s = splnet();
 	switch (cmd) {
 	case SIOCSIFMTU:
@@ -4809,14 +4984,15 @@ aq_ioctl(struct ifnet *ifp, unsigned lon
 		break;
 	case SIOCADDMULTI:
 	case SIOCDELMULTI:
-		if ((ifp->if_flags & IFF_RUNNING) == 0)
-			break;
-
-		/*
-		 * Multicast list has changed; set the hardware filter
-		 * accordingly.
-		 */
-		error = aq_set_filter(sc);
+		AQ_LOCK(sc);
+		if ((sc->sc_if_flags & IFF_RUNNING) != 0) {
+		       /*
+			* Multicast list has changed; set the hardware filter
+			* accordingly.
+			*/
+			error = aq_set_filter(sc);
+		}
+		AQ_UNLOCK(sc);
 		break;
 	}
 

Reply via email to