Module Name:    src
Committed By:   thorpej
Date:           Tue Feb  4 19:42:56 UTC 2020

Modified Files:
        src/sys/dev/pci/ixgbe: ixgbe.c ixgbe.h

Log Message:
- Fix locking problem with optics module interrupts: ifmedia_add()
  may block on memory allocation, and so it cannot be safely done from
  a softint nor can it be done while holding a spin lock.  Fix this by
  using a workqueue rather than a softint, and hold the IFNET_LOCK
  across the entire handler, and the CORE_LOCK only across the code that
  needs to serialize access to the hardware state.
- Use ifmedia_fini().

Tested in a variety of devices by msaitoh@.  (Thanks!)


To generate a diff of this commit:
cvs rdiff -u -r1.222 -r1.223 src/sys/dev/pci/ixgbe/ixgbe.c
cvs rdiff -u -r1.62 -r1.63 src/sys/dev/pci/ixgbe/ixgbe.h

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/ixgbe/ixgbe.c
diff -u src/sys/dev/pci/ixgbe/ixgbe.c:1.222 src/sys/dev/pci/ixgbe/ixgbe.c:1.223
--- src/sys/dev/pci/ixgbe/ixgbe.c:1.222	Sat Feb  1 12:55:22 2020
+++ src/sys/dev/pci/ixgbe/ixgbe.c	Tue Feb  4 19:42:55 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: ixgbe.c,v 1.222 2020/02/01 12:55:22 thorpej Exp $ */
+/* $NetBSD: ixgbe.c,v 1.223 2020/02/04 19:42:55 thorpej Exp $ */
 
 /******************************************************************************
 
@@ -267,10 +267,11 @@ static int	ixgbe_msix_link(void *);
 /* Software interrupts for deferred work */
 static void	ixgbe_handle_que(void *);
 static void	ixgbe_handle_link(void *);
-static void	ixgbe_handle_msf(void *);
 static void	ixgbe_handle_mod(void *);
 static void	ixgbe_handle_phy(void *);
 
+static void	ixgbe_handle_msf(struct work *, void *);
+
 /* Workqueue handler for deferred work */
 static void	ixgbe_handle_que_work(struct work *, void *);
 
@@ -410,10 +411,12 @@ static int (*ixgbe_ring_empty)(struct if
 #define IXGBE_CALLOUT_FLAGS	CALLOUT_MPSAFE
 #define IXGBE_SOFTINFT_FLAGS	SOFTINT_MPSAFE
 #define IXGBE_WORKQUEUE_FLAGS	WQ_PERCPU | WQ_MPSAFE
+#define IXGBE_TASKLET_WQ_FLAGS	WQ_MPSAFE
 #else
 #define IXGBE_CALLOUT_FLAGS	0
 #define IXGBE_SOFTINFT_FLAGS	0
 #define IXGBE_WORKQUEUE_FLAGS	WQ_PERCPU
+#define IXGBE_TASKLET_WQ_FLAGS	0
 #endif
 #define IXGBE_WORKQUEUE_PRI PRI_SOFTNET
 
@@ -1099,8 +1102,6 @@ ixgbe_attach(device_t parent, device_t d
 	    ixgbe_handle_link, adapter);
 	adapter->mod_si = softint_establish(SOFTINT_NET | IXGBE_SOFTINFT_FLAGS,
 	    ixgbe_handle_mod, adapter);
-	adapter->msf_si = softint_establish(SOFTINT_NET | IXGBE_SOFTINFT_FLAGS,
-	    ixgbe_handle_msf, adapter);
 	adapter->phy_si = softint_establish(SOFTINT_NET | IXGBE_SOFTINFT_FLAGS,
 	    ixgbe_handle_phy, adapter);
 	if (adapter->feat_en & IXGBE_FEATURE_FDIR)
@@ -1108,13 +1109,23 @@ ixgbe_attach(device_t parent, device_t d
 		    softint_establish(SOFTINT_NET | IXGBE_SOFTINFT_FLAGS,
 			ixgbe_reinit_fdir, adapter);
 	if ((adapter->link_si == NULL) || (adapter->mod_si == NULL)
-	    || (adapter->msf_si == NULL) || (adapter->phy_si == NULL)
+	    || (adapter->phy_si == NULL)
 	    || ((adapter->feat_en & IXGBE_FEATURE_FDIR)
 		&& (adapter->fdir_si == NULL))) {
 		aprint_error_dev(dev,
 		    "could not establish software interrupts ()\n");
 		goto err_out;
 	}
+	char wqname[MAXCOMLEN];
+	snprintf(wqname, sizeof(wqname), "%s-msf", device_xname(dev));
+	error = workqueue_create(&adapter->msf_wq, wqname,
+	    ixgbe_handle_msf, adapter, IXGBE_WORKQUEUE_PRI, IPL_NET,
+	    IXGBE_TASKLET_WQ_FLAGS);
+	if (error) {
+		aprint_error_dev(dev,
+		    "could not create MSF workqueue (%d)\n", error);
+		goto err_out;
+	}
 
 	error = ixgbe_start_hw(hw);
 	switch (error) {
@@ -1510,7 +1521,9 @@ ixgbe_config_link(struct adapter *adapte
 		if (hw->phy.multispeed_fiber) {
 			ixgbe_enable_tx_laser(hw);
 			kpreempt_disable();
-			softint_schedule(adapter->msf_si);
+			if (adapter->schedule_wqs_ok)
+				workqueue_enqueue(adapter->msf_wq,
+				    &adapter->msf_wc, NULL);
 			kpreempt_enable();
 		}
 		kpreempt_disable();
@@ -3088,7 +3101,9 @@ ixgbe_msix_link(void *arg)
 		    (eicr & IXGBE_EICR_GPI_SDP1_BY_MAC(hw))) {
 			IXGBE_WRITE_REG(hw, IXGBE_EICR,
 			    IXGBE_EICR_GPI_SDP1_BY_MAC(hw));
-			softint_schedule(adapter->msf_si);
+			if (adapter->schedule_wqs_ok)
+				workqueue_enqueue(adapter->msf_wq,
+				    &adapter->msf_wc, NULL);
 		}
 	}
 
@@ -3500,9 +3515,9 @@ ixgbe_free_softint(struct adapter *adapt
 		softint_disestablish(adapter->mod_si);
 		adapter->mod_si = NULL;
 	}
-	if (adapter->msf_si != NULL) {
-		softint_disestablish(adapter->msf_si);
-		adapter->msf_si = NULL;
+	if (adapter->msf_wq != NULL) {
+		workqueue_destroy(adapter->msf_wq);
+		adapter->msf_wq = NULL;
 	}
 	if (adapter->phy_si != NULL) {
 		softint_disestablish(adapter->phy_si);
@@ -3593,6 +3608,7 @@ ixgbe_detach(device_t dev, int flags)
 	bus_generic_detach(dev);
 #endif
 	if_detach(adapter->ifp);
+	ifmedia_fini(&adapter->media);
 	if_percpuq_destroy(adapter->ipq);
 
 	sysctl_teardown(&adapter->sysctllog);
@@ -4129,6 +4145,9 @@ ixgbe_init_locked(struct adapter *adapte
 	/* Now inform the stack we're ready */
 	ifp->if_flags |= IFF_RUNNING;
 
+	/* OK to schedule workqueues. */
+	adapter->schedule_wqs_ok = true;
+
 	return;
 } /* ixgbe_init_locked */
 
@@ -4657,7 +4676,8 @@ ixgbe_handle_mod(void *context)
 			goto out;
 		}
 	}
-	softint_schedule(adapter->msf_si);
+	if (adapter->schedule_wqs_ok)
+		workqueue_enqueue(adapter->msf_wq, &adapter->msf_wc, NULL);
 out:
 	IXGBE_CORE_UNLOCK(adapter);
 } /* ixgbe_handle_mod */
@@ -4667,13 +4687,23 @@ out:
  * ixgbe_handle_msf - Tasklet for MSF (multispeed fiber) interrupts
  ************************************************************************/
 static void
-ixgbe_handle_msf(void *context)
+ixgbe_handle_msf(struct work *wk, void *context)
 {
 	struct adapter	*adapter = context;
+	struct ifnet	*ifp = adapter->ifp;
 	struct ixgbe_hw *hw = &adapter->hw;
 	u32		autoneg;
 	bool		negotiate;
 
+	/*
+	 * Hold the IFNET_LOCK across this entire call.  This will
+	 * prevent additional changes to adapter->phy_layer and
+	 * and serialize calls to this tasklet.  We cannot hold the
+	 * CORE_LOCK while calling into the ifmedia functions as
+	 * they may block while allocating memory.
+	 */
+	IFNET_LOCK(ifp);
+
 	IXGBE_CORE_LOCK(adapter);
 	++adapter->msf_sicount.ev_count;
 	/* get_supported_phy_layer will call hw->phy.ops.identify_sfp() */
@@ -4686,12 +4716,13 @@ ixgbe_handle_msf(void *context)
 		negotiate = 0;
 	if (hw->mac.ops.setup_link)
 		hw->mac.ops.setup_link(hw, autoneg, TRUE);
+	IXGBE_CORE_UNLOCK(adapter);
 
 	/* Adjust media types shown in ifconfig */
 	ifmedia_removeall(&adapter->media);
 	ixgbe_add_media_types(adapter);
 	ifmedia_set(&adapter->media, IFM_ETHER | IFM_AUTO);
-	IXGBE_CORE_UNLOCK(adapter);
+	IFNET_UNLOCK(ifp);
 } /* ixgbe_handle_msf */
 
 /************************************************************************
@@ -4723,6 +4754,8 @@ ixgbe_ifstop(struct ifnet *ifp, int disa
 	IXGBE_CORE_LOCK(adapter);
 	ixgbe_stop(adapter);
 	IXGBE_CORE_UNLOCK(adapter);
+
+	workqueue_wait(adapter->msf_wq, &adapter->msf_wc);
 }
 
 /************************************************************************
@@ -4746,6 +4779,9 @@ ixgbe_stop(void *arg)
 	ixgbe_disable_intr(adapter);
 	callout_stop(&adapter->timer);
 
+	/* Don't schedule workqueues. */
+	adapter->schedule_wqs_ok = false;
+
 	/* Let the stack know...*/
 	ifp->if_flags &= ~IFF_RUNNING;
 
@@ -5088,7 +5124,9 @@ ixgbe_legacy_irq(void *arg)
 		    (eicr & IXGBE_EICR_GPI_SDP1_BY_MAC(hw))) {
 			IXGBE_WRITE_REG(hw, IXGBE_EICR,
 			    IXGBE_EICR_GPI_SDP1_BY_MAC(hw));
-			softint_schedule(adapter->msf_si);
+			if (adapter->schedule_wqs_ok)
+				workqueue_enqueue(adapter->msf_wq,
+				    &adapter->msf_wc, NULL);
 		}
 	}
 

Index: src/sys/dev/pci/ixgbe/ixgbe.h
diff -u src/sys/dev/pci/ixgbe/ixgbe.h:1.62 src/sys/dev/pci/ixgbe/ixgbe.h:1.63
--- src/sys/dev/pci/ixgbe/ixgbe.h:1.62	Tue Jan 21 14:55:55 2020
+++ src/sys/dev/pci/ixgbe/ixgbe.h	Tue Feb  4 19:42:55 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: ixgbe.h,v 1.62 2020/01/21 14:55:55 msaitoh Exp $ */
+/* $NetBSD: ixgbe.h,v 1.63 2020/02/04 19:42:55 thorpej Exp $ */
 
 /******************************************************************************
   SPDX-License-Identifier: BSD-3-Clause
@@ -507,11 +507,14 @@ struct adapter {
 	/* Mbuf cluster size */
 	u32			rx_mbuf_sz;
 
+	bool			schedule_wqs_ok;
+
 	/* Support for pluggable optics */
 	bool			sfp_probe;
 	void			*link_si;  /* Link tasklet */
 	void			*mod_si;   /* SFP tasklet */
-	void			*msf_si;   /* Multispeed Fiber */
+	struct workqueue	*msf_wq;   /* Multispeed Fiber */
+	struct work		 msf_wc;
 	void			*mbx_si;   /* VF -> PF mailbox interrupt */
 
 	/* Flow Director */

Reply via email to