Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 30 Oct 2011 01:24:53 +0000 (UTC)
From:      Marius Strobl <marius@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org
Subject:   svn commit: r226923 - in stable/8/sys/dev: lge nve pcn tl wb
Message-ID:  <201110300124.p9U1OrQW041679@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: marius
Date: Sun Oct 30 01:24:53 2011
New Revision: 226923
URL: http://svn.freebsd.org/changeset/base/226923

Log:
  MFC: r199560
  
  - Hook into the existing stat timer to drive the transmit watchdog instead
    of using if_watchdog and if_timer.
  - Reorder detach to call ether_ifdetach() before anything else in tl(4)
    and wb(4).

Modified:
  stable/8/sys/dev/lge/if_lge.c
  stable/8/sys/dev/lge/if_lgereg.h
  stable/8/sys/dev/nve/if_nve.c
  stable/8/sys/dev/nve/if_nvereg.h
  stable/8/sys/dev/pcn/if_pcn.c
  stable/8/sys/dev/pcn/if_pcnreg.h
  stable/8/sys/dev/tl/if_tl.c
  stable/8/sys/dev/tl/if_tlreg.h
  stable/8/sys/dev/wb/if_wb.c
  stable/8/sys/dev/wb/if_wbreg.h
Directory Properties:
  stable/8/sys/   (props changed)
  stable/8/sys/amd64/include/xen/   (props changed)
  stable/8/sys/cddl/contrib/opensolaris/   (props changed)
  stable/8/sys/contrib/dev/acpica/   (props changed)
  stable/8/sys/contrib/pf/   (props changed)

Modified: stable/8/sys/dev/lge/if_lge.c
==============================================================================
--- stable/8/sys/dev/lge/if_lge.c	Sun Oct 30 01:13:49 2011	(r226922)
+++ stable/8/sys/dev/lge/if_lge.c	Sun Oct 30 01:24:53 2011	(r226923)
@@ -137,7 +137,7 @@ static int lge_ioctl(struct ifnet *, u_l
 static void lge_init(void *);
 static void lge_init_locked(struct lge_softc *);
 static void lge_stop(struct lge_softc *);
-static void lge_watchdog(struct ifnet *);
+static void lge_watchdog(struct lge_softc *);
 static int lge_shutdown(device_t);
 static int lge_ifmedia_upd(struct ifnet *);
 static void lge_ifmedia_upd_locked(struct ifnet *);
@@ -544,7 +544,6 @@ lge_attach(dev)
 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
 	ifp->if_ioctl = lge_ioctl;
 	ifp->if_start = lge_start;
-	ifp->if_watchdog = lge_watchdog;
 	ifp->if_init = lge_init;
 	ifp->if_snd.ifq_maxlen = LGE_TX_LIST_CNT - 1;
 	ifp->if_capabilities = IFCAP_RXCSUM;
@@ -1000,7 +999,7 @@ lge_txeof(sc)
 	ifp = sc->lge_ifp;
 
 	/* Clear the timeout timer. */
-	ifp->if_timer = 0;
+	sc->lge_timer = 0;
 
 	/*
 	 * Go through our tx list and free mbufs for those
@@ -1021,7 +1020,7 @@ lge_txeof(sc)
 
 		txdone--;
 		LGE_INC(idx, LGE_TX_LIST_CNT);
-		ifp->if_timer = 0;
+		sc->lge_timer = 0;
 	}
 
 	sc->lge_cdata.lge_tx_cons = idx;
@@ -1064,6 +1063,8 @@ lge_tick(xsc)
 		}
 	}
 
+	if (sc->lge_timer != 0 && --sc->lge_timer == 0)
+		lge_watchdog(sc);
 	callout_reset(&sc->lge_stat_callout, hz, lge_tick, sc);
 
 	return;
@@ -1236,7 +1237,7 @@ lge_start_locked(ifp)
 	/*
 	 * Set a timeout in case the chip goes out to lunch.
 	 */
-	ifp->if_timer = 5;
+	sc->lge_timer = 5;
 
 	return;
 }
@@ -1503,14 +1504,14 @@ lge_ioctl(ifp, command, data)
 }
 
 static void
-lge_watchdog(ifp)
-	struct ifnet		*ifp;
-{
+lge_watchdog(sc)
 	struct lge_softc	*sc;
+{
+	struct ifnet		*ifp;
 
-	sc = ifp->if_softc;
+	LGE_LOCK_ASSERT(sc);
+	ifp = sc->lge_ifp;
 
-	LGE_LOCK(sc);
 	ifp->if_oerrors++;
 	if_printf(ifp, "watchdog timeout\n");
 
@@ -1521,9 +1522,6 @@ lge_watchdog(ifp)
 
 	if (ifp->if_snd.ifq_head != NULL)
 		lge_start_locked(ifp);
-	LGE_UNLOCK(sc);
-
-	return;
 }
 
 /*
@@ -1539,7 +1537,7 @@ lge_stop(sc)
 
 	LGE_LOCK_ASSERT(sc);
 	ifp = sc->lge_ifp;
-	ifp->if_timer = 0;
+	sc->lge_timer = 0;
 	callout_stop(&sc->lge_stat_callout);
 	CSR_WRITE_4(sc, LGE_IMR, LGE_IMR_INTR_ENB);
 

Modified: stable/8/sys/dev/lge/if_lgereg.h
==============================================================================
--- stable/8/sys/dev/lge/if_lgereg.h	Sun Oct 30 01:13:49 2011	(r226922)
+++ stable/8/sys/dev/lge/if_lgereg.h	Sun Oct 30 01:24:53 2011	(r226923)
@@ -534,6 +534,7 @@ struct lge_softc {
 	u_int8_t		lge_link;
 	u_int8_t		lge_pcs;
 	int			lge_if_flags;
+	int			lge_timer;
 	struct lge_list_data	*lge_ldata;
 	struct lge_ring_data	lge_cdata;
 	struct callout		lge_stat_callout;

Modified: stable/8/sys/dev/nve/if_nve.c
==============================================================================
--- stable/8/sys/dev/nve/if_nve.c	Sun Oct 30 01:13:49 2011	(r226922)
+++ stable/8/sys/dev/nve/if_nve.c	Sun Oct 30 01:24:53 2011	(r226923)
@@ -140,7 +140,7 @@ static int      nve_ioctl(struct ifnet *
 static void     nve_intr(void *);
 static void     nve_tick(void *);
 static void     nve_setmulti(struct nve_softc *);
-static void     nve_watchdog(struct ifnet *);
+static void     nve_watchdog(struct nve_softc *);
 static void     nve_update_stats(struct nve_softc *);
 
 static int      nve_ifmedia_upd(struct ifnet *);
@@ -532,8 +532,6 @@ nve_attach(device_t dev)
 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
 	ifp->if_ioctl = nve_ioctl;
 	ifp->if_start = nve_ifstart;
-	ifp->if_watchdog = nve_watchdog;
-	ifp->if_timer = 0;
 	ifp->if_init = nve_init;
 	ifp->if_mtu = ETHERMTU;
 	ifp->if_baudrate = IF_Mbps(100);
@@ -710,7 +708,7 @@ nve_stop(struct nve_softc *sc)
 	DEBUGOUT(NVE_DEBUG_RUNNING, "nve: nve_stop - entry\n");
 
 	ifp = sc->ifp;
-	ifp->if_timer = 0;
+	sc->tx_timer = 0;
 
 	/* Cancel tick timer */
 	callout_stop(&sc->stat_callout);
@@ -984,7 +982,7 @@ nve_ifstart_locked(struct ifnet *ifp)
 			return;
 		}
 		/* Set watchdog timer. */
-		ifp->if_timer = 8;
+		sc->tx_timer = 8;
 
 		/* Copy packet to BPF tap */
 		BPF_MTAP(ifp, m0);
@@ -1096,7 +1094,7 @@ nve_intr(void *arg)
 
 	/* If no pending packets we don't need a timeout */
 	if (sc->pending_txs == 0)
-		sc->ifp->if_timer = 0;
+		sc->tx_timer = 0;
 	NVE_UNLOCK(sc);
 
 	DEBUGOUT(NVE_DEBUG_INTERRUPT, "nve: nve_intr - exit\n");
@@ -1233,6 +1231,9 @@ nve_tick(void *xsc)
 		if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
 			nve_ifstart_locked(ifp);
 	}
+
+	if (sc->tx_timer > 0 && --sc->tx_timer == 0)
+		nve_watchdog(sc);
 	callout_reset(&sc->stat_callout, hz, nve_tick, sc);
 
 	return;
@@ -1304,12 +1305,13 @@ nve_miibus_writereg(device_t dev, int ph
 
 /* Watchdog timer to prevent PHY lockups */
 static void
-nve_watchdog(struct ifnet *ifp)
+nve_watchdog(struct nve_softc *sc)
 {
-	struct nve_softc *sc = ifp->if_softc;
+	struct ifnet *ifp;
 	int pending_txs_start;
 
-	NVE_LOCK(sc);
+	NVE_LOCK_ASSERT(sc);
+	ifp = sc->ifp;
 
 	/*
 	 * The nvidia driver blob defers tx completion notifications.
@@ -1325,24 +1327,18 @@ nve_watchdog(struct ifnet *ifp)
 	sc->hwapi->pfnDisableInterrupts(sc->hwapi->pADCX);
 	sc->hwapi->pfnHandleInterrupt(sc->hwapi->pADCX);
 	sc->hwapi->pfnEnableInterrupts(sc->hwapi->pADCX);
-	if (sc->pending_txs < pending_txs_start) {
-		NVE_UNLOCK(sc);
+	if (sc->pending_txs < pending_txs_start)
 		return;
-	}
 
 	device_printf(sc->dev, "device timeout (%d)\n", sc->pending_txs);
 
 	sc->tx_errors++;
 
 	nve_stop(sc);
-	ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
 	nve_init_locked(sc);
 
 	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
 		nve_ifstart_locked(ifp);
-	NVE_UNLOCK(sc);
-
-	return;
 }
 
 /* --- Start of NVOSAPI interface --- */

Modified: stable/8/sys/dev/nve/if_nvereg.h
==============================================================================
--- stable/8/sys/dev/nve/if_nvereg.h	Sun Oct 30 01:13:49 2011	(r226922)
+++ stable/8/sys/dev/nve/if_nvereg.h	Sun Oct 30 01:24:53 2011	(r226923)
@@ -138,6 +138,7 @@ struct nve_softc {
 	device_t miibus;
 	device_t dev;
 	struct callout stat_callout;
+	int tx_timer;
 
 	void *sc_ih;
 	bus_space_tag_t sc_st;

Modified: stable/8/sys/dev/pcn/if_pcn.c
==============================================================================
--- stable/8/sys/dev/pcn/if_pcn.c	Sun Oct 30 01:13:49 2011	(r226922)
+++ stable/8/sys/dev/pcn/if_pcn.c	Sun Oct 30 01:24:53 2011	(r226923)
@@ -143,7 +143,7 @@ static int pcn_ioctl(struct ifnet *, u_l
 static void pcn_init(void *);
 static void pcn_init_locked(struct pcn_softc *);
 static void pcn_stop(struct pcn_softc *);
-static void pcn_watchdog(struct ifnet *);
+static void pcn_watchdog(struct pcn_softc *);
 static int pcn_shutdown(device_t);
 static int pcn_ifmedia_upd(struct ifnet *);
 static void pcn_ifmedia_sts(struct ifnet *, struct ifmediareq *);
@@ -630,7 +630,6 @@ pcn_attach(dev)
 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
 	ifp->if_ioctl = pcn_ioctl;
 	ifp->if_start = pcn_start;
-	ifp->if_watchdog = pcn_watchdog;
 	ifp->if_init = pcn_init;
 	ifp->if_snd.ifq_maxlen = PCN_TX_LIST_CNT - 1;
 
@@ -951,7 +950,7 @@ pcn_txeof(sc)
 		sc->pcn_cdata.pcn_tx_cons = idx;
 		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
 	}
-	ifp->if_timer = (sc->pcn_cdata.pcn_tx_cnt == 0) ? 0 : 5;
+	sc->pcn_timer = (sc->pcn_cdata.pcn_tx_cnt == 0) ? 0 : 5;
 
 	return;
 }
@@ -983,6 +982,8 @@ pcn_tick(xsc)
 			pcn_start_locked(ifp);
 	}
 
+	if (sc->pcn_timer > 0 && --sc->pcn_timer == 0)
+		pcn_watchdog(sc);
 	callout_reset(&sc->pcn_stat_callout, hz, pcn_tick, sc);
 
 	return;
@@ -1150,7 +1151,7 @@ pcn_start_locked(ifp)
 	/*
 	 * Set a timeout in case the chip goes out to lunch.
 	 */
-	ifp->if_timer = 5;
+	sc->pcn_timer = 5;
 
 	return;
 }
@@ -1432,14 +1433,12 @@ pcn_ioctl(ifp, command, data)
 }
 
 static void
-pcn_watchdog(ifp)
-	struct ifnet		*ifp;
+pcn_watchdog(struct pcn_softc *sc)
 {
-	struct pcn_softc	*sc;
-
-	sc = ifp->if_softc;
+	struct ifnet		*ifp;
 
-	PCN_LOCK(sc);
+	PCN_LOCK_ASSERT(sc);
+	ifp = sc->pcn_ifp;
 
 	ifp->if_oerrors++;
 	if_printf(ifp, "watchdog timeout\n");
@@ -1450,10 +1449,6 @@ pcn_watchdog(ifp)
 
 	if (ifp->if_snd.ifq_head != NULL)
 		pcn_start_locked(ifp);
-
-	PCN_UNLOCK(sc);
-
-	return;
 }
 
 /*
@@ -1468,7 +1463,7 @@ pcn_stop(struct pcn_softc *sc)
 
 	PCN_LOCK_ASSERT(sc);
 	ifp = sc->pcn_ifp;
-	ifp->if_timer = 0;
+	sc->pcn_timer = 0;
 
 	callout_stop(&sc->pcn_stat_callout);
 

Modified: stable/8/sys/dev/pcn/if_pcnreg.h
==============================================================================
--- stable/8/sys/dev/pcn/if_pcnreg.h	Sun Oct 30 01:13:49 2011	(r226922)
+++ stable/8/sys/dev/pcn/if_pcnreg.h	Sun Oct 30 01:24:53 2011	(r226923)
@@ -465,6 +465,7 @@ struct pcn_softc {
 	struct pcn_ring_data	pcn_cdata;
 	struct callout		pcn_stat_callout;
 	struct mtx		pcn_mtx;
+	int			pcn_timer;
 };
 
 #define	PCN_LOCK(_sc)		mtx_lock(&(_sc)->pcn_mtx)

Modified: stable/8/sys/dev/tl/if_tl.c
==============================================================================
--- stable/8/sys/dev/tl/if_tl.c	Sun Oct 30 01:13:49 2011	(r226922)
+++ stable/8/sys/dev/tl/if_tl.c	Sun Oct 30 01:24:53 2011	(r226923)
@@ -281,7 +281,7 @@ static int tl_ioctl(struct ifnet *, u_lo
 static void tl_init(void *);
 static void tl_init_locked(struct tl_softc *);
 static void tl_stop(struct tl_softc *);
-static void tl_watchdog(struct ifnet *);
+static void tl_watchdog(struct tl_softc *);
 static int tl_shutdown(device_t);
 static int tl_ifmedia_upd(struct ifnet *);
 static void tl_ifmedia_sts(struct ifnet *, struct ifmediareq *);
@@ -1261,7 +1261,6 @@ tl_attach(dev)
 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
 	ifp->if_ioctl = tl_ioctl;
 	ifp->if_start = tl_start;
-	ifp->if_watchdog = tl_watchdog;
 	ifp->if_init = tl_init;
 	ifp->if_mtu = ETHERMTU;
 	ifp->if_snd.ifq_maxlen = TL_TX_LIST_CNT - 1;
@@ -1354,11 +1353,11 @@ tl_detach(dev)
 
 	/* These should only be active if attach succeeded */
 	if (device_is_attached(dev)) {
+		ether_ifdetach(ifp);
 		TL_LOCK(sc);
 		tl_stop(sc);
 		TL_UNLOCK(sc);
 		callout_drain(&sc->tl_stat_callout);
-		ether_ifdetach(ifp);
 	}
 	if (sc->tl_miibus)
 		device_delete_child(dev, sc->tl_miibus);
@@ -1652,7 +1651,7 @@ tl_intvec_txeoc(xsc, type)
 	ifp = sc->tl_ifp;
 
 	/* Clear the timeout timer. */
-	ifp->if_timer = 0;
+	sc->tl_timer = 0;
 
 	if (sc->tl_cdata.tl_tx_head == NULL) {
 		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
@@ -1838,6 +1837,9 @@ tl_stats_update(xsc)
 		}
 	}
 
+	if (sc->tl_timer > 0 && --sc->tl_timer == 0)
+		tl_watchdog(sc);
+
 	callout_reset(&sc->tl_stat_callout, hz, tl_stats_update, sc);
 
 	if (!sc->tl_bitrate) {
@@ -2046,7 +2048,7 @@ tl_start_locked(ifp)
 	/*
 	 * Set a timeout in case the chip goes out to lunch.
 	 */
-	ifp->if_timer = 5;
+	sc->tl_timer = 5;
 
 	return;
 }
@@ -2271,21 +2273,20 @@ tl_ioctl(ifp, command, data)
 }
 
 static void
-tl_watchdog(ifp)
-	struct ifnet		*ifp;
-{
+tl_watchdog(sc)
 	struct tl_softc		*sc;
+{
+	struct ifnet		*ifp;
 
-	sc = ifp->if_softc;
+	TL_LOCK_ASSERT(sc);
+	ifp = sc->tl_ifp;
 
 	if_printf(ifp, "device timeout\n");
 
-	TL_LOCK(sc);
 	ifp->if_oerrors++;
 
 	tl_softreset(sc, 1);
 	tl_init_locked(sc);
-	TL_UNLOCK(sc);
 
 	return;
 }

Modified: stable/8/sys/dev/tl/if_tlreg.h
==============================================================================
--- stable/8/sys/dev/tl/if_tlreg.h	Sun Oct 30 01:13:49 2011	(r226922)
+++ stable/8/sys/dev/tl/if_tlreg.h	Sun Oct 30 01:24:53 2011	(r226923)
@@ -126,6 +126,7 @@ struct tl_softc {
 	int			tl_if_flags;
 	struct callout		tl_stat_callout;
 	struct mtx		tl_mtx;
+	int			tl_timer;
 };
 
 #define	TL_LOCK(_sc)		mtx_lock(&(_sc)->tl_mtx)

Modified: stable/8/sys/dev/wb/if_wb.c
==============================================================================
--- stable/8/sys/dev/wb/if_wb.c	Sun Oct 30 01:13:49 2011	(r226922)
+++ stable/8/sys/dev/wb/if_wb.c	Sun Oct 30 01:24:53 2011	(r226923)
@@ -158,7 +158,7 @@ static int wb_ioctl(struct ifnet *, u_lo
 static void wb_init(void *);
 static void wb_init_locked(struct wb_softc *);
 static void wb_stop(struct wb_softc *);
-static void wb_watchdog(struct ifnet *);
+static void wb_watchdog(struct wb_softc *);
 static int wb_shutdown(device_t);
 static int wb_ifmedia_upd(struct ifnet *);
 static void wb_ifmedia_sts(struct ifnet *, struct ifmediareq *);
@@ -840,7 +840,6 @@ wb_attach(dev)
 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
 	ifp->if_ioctl = wb_ioctl;
 	ifp->if_start = wb_start;
-	ifp->if_watchdog = wb_watchdog;
 	ifp->if_init = wb_init;
 	ifp->if_snd.ifq_maxlen = WB_TX_LIST_CNT - 1;
 
@@ -899,11 +898,11 @@ wb_detach(dev)
 	 * This should only be done if attach succeeded.
 	 */
 	if (device_is_attached(dev)) {
+		ether_ifdetach(ifp);
 		WB_LOCK(sc);
 		wb_stop(sc);
 		WB_UNLOCK(sc);
 		callout_drain(&sc->wb_stat_callout);
-		ether_ifdetach(ifp);
 	}
 	if (sc->wb_miibus)
 		device_delete_child(dev, sc->wb_miibus);
@@ -1149,7 +1148,7 @@ wb_txeof(sc)
 	ifp = sc->wb_ifp;
 
 	/* Clear the timeout timer. */
-	ifp->if_timer = 0;
+	sc->wb_timer = 0;
 
 	if (sc->wb_cdata.wb_tx_head == NULL)
 		return;
@@ -1204,7 +1203,7 @@ wb_txeoc(sc)
 
 	ifp = sc->wb_ifp;
 
-	ifp->if_timer = 0;
+	sc->wb_timer = 0;
 
 	if (sc->wb_cdata.wb_tx_head == NULL) {
 		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
@@ -1212,7 +1211,7 @@ wb_txeoc(sc)
 	} else {
 		if (WB_TXOWN(sc->wb_cdata.wb_tx_head) == WB_UNSENT) {
 			WB_TXOWN(sc->wb_cdata.wb_tx_head) = WB_TXSTAT_OWN;
-			ifp->if_timer = 5;
+			sc->wb_timer = 5;
 			CSR_WRITE_4(sc, WB_TXSTART, 0xFFFFFFFF);
 		}
 	}
@@ -1321,6 +1320,8 @@ wb_tick(xsc)
 
 	mii_tick(mii);
 
+	if (sc->wb_timer > 0 && --sc->wb_timer == 0)
+		wb_watchdog(sc);
 	callout_reset(&sc->wb_stat_callout, hz, wb_tick, sc);
 
 	return;
@@ -1521,7 +1522,7 @@ wb_start_locked(ifp)
 	/*
 	 * Set a timeout in case the chip goes out to lunch.
 	 */
-	ifp->if_timer = 5;
+	sc->wb_timer = 5;
 
 	return;
 }
@@ -1740,14 +1741,13 @@ wb_ioctl(ifp, command, data)
 }
 
 static void
-wb_watchdog(ifp)
-	struct ifnet		*ifp;
-{
+wb_watchdog(sc)
 	struct wb_softc		*sc;
+{
+	struct ifnet		*ifp;
 
-	sc = ifp->if_softc;
-
-	WB_LOCK(sc);
+	WB_LOCK_ASSERT(sc);
+	ifp = sc->wb_ifp;
 	ifp->if_oerrors++;
 	if_printf(ifp, "watchdog timeout\n");
 #ifdef foo
@@ -1760,7 +1760,6 @@ wb_watchdog(ifp)
 
 	if (ifp->if_snd.ifq_head != NULL)
 		wb_start_locked(ifp);
-	WB_UNLOCK(sc);
 
 	return;
 }
@@ -1778,7 +1777,7 @@ wb_stop(sc)
 
 	WB_LOCK_ASSERT(sc);
 	ifp = sc->wb_ifp;
-	ifp->if_timer = 0;
+	sc->wb_timer = 0;
 
 	callout_stop(&sc->wb_stat_callout);
 

Modified: stable/8/sys/dev/wb/if_wbreg.h
==============================================================================
--- stable/8/sys/dev/wb/if_wbreg.h	Sun Oct 30 01:13:49 2011	(r226922)
+++ stable/8/sys/dev/wb/if_wbreg.h	Sun Oct 30 01:24:53 2011	(r226923)
@@ -374,6 +374,7 @@ struct wb_softc {
 	u_int8_t		wb_type;
 	u_int16_t		wb_txthresh;
 	int			wb_cachesize;
+	int			wb_timer;
 	caddr_t			wb_ldata_ptr;
 	struct wb_list_data	*wb_ldata;
 	struct wb_chain_data	wb_cdata;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201110300124.p9U1OrQW041679>