Module Name: src Committed By: jym Date: Fri Jul 15 11:29:31 UTC 2011
Modified Files: src/sys/dev/pci: if_bnx.c if_bnxvar.h Log Message: Use a workqueue(9) to defer allocation of TX packets. Loosely inspired from the OpenBSD bnx(4) driver. Tested by Roger Pau Monné, roger.pau at entel.upc.edu. Fixes kern/45051. To generate a diff of this commit: cvs rdiff -u -r1.43 -r1.44 src/sys/dev/pci/if_bnx.c cvs rdiff -u -r1.1 -r1.2 src/sys/dev/pci/if_bnxvar.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/if_bnx.c diff -u src/sys/dev/pci/if_bnx.c:1.43 src/sys/dev/pci/if_bnx.c:1.44 --- src/sys/dev/pci/if_bnx.c:1.43 Mon May 2 09:03:10 2011 +++ src/sys/dev/pci/if_bnx.c Fri Jul 15 11:29:31 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: if_bnx.c,v 1.43 2011/05/02 09:03:10 jym Exp $ */ +/* $NetBSD: if_bnx.c,v 1.44 2011/07/15 11:29:31 jym Exp $ */ /* $OpenBSD: if_bnx.c,v 1.85 2009/11/09 14:32:41 dlg Exp $ */ /*- @@ -35,7 +35,7 @@ #if 0 __FBSDID("$FreeBSD: src/sys/dev/bce/if_bce.c,v 1.3 2006/04/13 14:12:26 ru Exp $"); #endif -__KERNEL_RCSID(0, "$NetBSD: if_bnx.c,v 1.43 2011/05/02 09:03:10 jym Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_bnx.c,v 1.44 2011/07/15 11:29:31 jym Exp $"); /* * The following controllers are supported by this driver: @@ -397,7 +397,7 @@ void bnx_tick(void *); struct pool *bnx_tx_pool = NULL; -int bnx_alloc_pkts(struct bnx_softc *); +void bnx_alloc_pkts(struct work *, void *); /****************************************************************************/ /* OpenBSD device dispatch table. */ @@ -705,6 +705,13 @@ } aprint_normal_dev(sc->bnx_dev, "interrupting at %s\n", intrstr); + /* create workqueue to handle packet allocations */ + if (workqueue_create(&sc->bnx_wq, device_xname(self), + bnx_alloc_pkts, sc, PRI_NONE, IPL_NONE, WQ_MPSAFE) != 0) { + aprint_error_dev(self, "failed to create workqueue\n"); + goto bnx_attach_fail; + } + sc->bnx_mii.mii_ifp = ifp; sc->bnx_mii.mii_readreg = bnx_miibus_read_reg; sc->bnx_mii.mii_writereg = bnx_miibus_write_reg; @@ -799,6 +806,7 @@ pmf_device_deregister(dev); callout_destroy(&sc->bnx_timeout); ether_ifdetach(ifp); + workqueue_destroy(sc->bnx_wq); /* Delete all remaining media. */ ifmedia_delete_instance(&sc->bnx_mii.mii_media, IFM_INST_ANY); @@ -3839,21 +3847,22 @@ return(rc); } -int -bnx_alloc_pkts(struct bnx_softc *sc) +void +bnx_alloc_pkts(struct work * unused, void * arg) { + struct bnx_softc *sc = arg; struct ifnet *ifp = &sc->bnx_ec.ec_if; struct bnx_pkt *pkt; - int i; + int i, s; for (i = 0; i < 4; i++) { /* magic! */ - pkt = pool_get(bnx_tx_pool, PR_NOWAIT); + pkt = pool_get(bnx_tx_pool, PR_WAITOK); if (pkt == NULL) break; if (bus_dmamap_create(sc->bnx_dmatag, MCLBYTES * BNX_MAX_SEGMENTS, USABLE_TX_BD, - MCLBYTES, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, + MCLBYTES, 0, BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &pkt->pkt_dmamap) != 0) goto put; @@ -3866,13 +3875,23 @@ mutex_exit(&sc->tx_pkt_mtx); } - return (i == 0) ? ENOMEM : 0; + mutex_enter(&sc->tx_pkt_mtx); + CLR(sc->bnx_flags, BNX_ALLOC_PKTS_FLAG); + mutex_exit(&sc->tx_pkt_mtx); + + /* fire-up TX now that allocations have been done */ + s = splnet(); + if (!IFQ_IS_EMPTY(&ifp->if_snd)) + bnx_start(ifp); + splx(s); + + return; stopping: bus_dmamap_destroy(sc->bnx_dmatag, pkt->pkt_dmamap); put: pool_put(bnx_tx_pool, pkt); - return (i == 0) ? ENOMEM : 0; + return; } /****************************************************************************/ @@ -3933,7 +3952,7 @@ DBPRINT(sc, BNX_VERBOSE_RESET, "Entering %s()\n", __func__); /* Force an allocation of some dmamaps for tx up front */ - bnx_alloc_pkts(sc); + bnx_alloc_pkts(NULL, sc); /* Set the initial TX producer/consumer indices. */ sc->tx_prod = 0; @@ -4805,8 +4824,8 @@ if ((error = ether_mediachange(ifp)) != 0) goto bnx_init_exit; - ifp->if_flags |= IFF_RUNNING; - ifp->if_flags &= ~IFF_OACTIVE; + SET(ifp->if_flags, IFF_RUNNING); + CLR(ifp->if_flags, IFF_OACTIVE); callout_reset(&sc->bnx_timeout, hz, bnx_tick, sc); @@ -4839,8 +4858,8 @@ u_int32_t addr, prod_bseq; int i, error; struct m_tag *mtag; + static struct work bnx_wk; /* Dummy work. Statically allocated. */ -again: mutex_enter(&sc->tx_pkt_mtx); pkt = TAILQ_FIRST(&sc->tx_free_pkts); if (pkt == NULL) { @@ -4848,14 +4867,15 @@ mutex_exit(&sc->tx_pkt_mtx); return ENETDOWN; } - if (sc->tx_pkt_count <= TOTAL_TX_BD) { - mutex_exit(&sc->tx_pkt_mtx); - if (bnx_alloc_pkts(sc) == 0) - goto again; - } else { - mutex_exit(&sc->tx_pkt_mtx); + + if (sc->tx_pkt_count <= TOTAL_TX_BD && + !ISSET(sc->bnx_flags, BNX_ALLOC_PKTS_FLAG)) { + workqueue_enqueue(sc->bnx_wq, &bnx_wk, NULL); + SET(sc->bnx_flags, BNX_ALLOC_PKTS_FLAG); } - return (ENOMEM); + + mutex_exit(&sc->tx_pkt_mtx); + return ENOMEM; } TAILQ_REMOVE(&sc->tx_free_pkts, pkt, pkt_entry); mutex_exit(&sc->tx_pkt_mtx); @@ -5082,7 +5102,7 @@ /* XXX set an ifflags callback and let ether_ioctl * handle all of this. */ - if (ifp->if_flags & IFF_UP) { + if (ISSET(ifp->if_flags, IFF_UP)) { if (ifp->if_flags & IFF_RUNNING) error = ENETRESET; else Index: src/sys/dev/pci/if_bnxvar.h diff -u src/sys/dev/pci/if_bnxvar.h:1.1 src/sys/dev/pci/if_bnxvar.h:1.2 --- src/sys/dev/pci/if_bnxvar.h:1.1 Thu Dec 9 22:34:37 2010 +++ src/sys/dev/pci/if_bnxvar.h Fri Jul 15 11:29:31 2011 @@ -46,7 +46,7 @@ #include <sys/device.h> #include <sys/socket.h> #include <sys/sysctl.h> -//#include <sys/workqueue.h> +#include <sys/workqueue.h> #include <net/if.h> #include <net/if_dl.h> @@ -126,6 +126,9 @@ void *bnx_intrhand; /* Interrupt handler */ + /* packet allocation workqueue */ + struct workqueue *bnx_wq; + /* ASIC Chip ID. */ u_int32_t bnx_chipid;