Date: Sun, 20 Sep 2009 12:56:50 +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-7@freebsd.org Subject: svn commit: r197349 - in stable/7/sys: . conf contrib/pf dev/gem modules/gem Message-ID: <200909201256.n8KCuofu057064@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: marius Date: Sun Sep 20 12:56:50 2009 New Revision: 197349 URL: http://svn.freebsd.org/changeset/base/197349 Log: MFC: r194763, r194886 - Initialize the ifnet structure, especially if_dname, before probing the PHYs as some PHY drivers use it (but probably shouldn't). How gem(4) has worked with brgphy(4) on powerpc without this so far is unclear to me. - Call ether_ifdetach(9) before stopping the controller and the callouts. The consensus is that the latter is now safe to do and should also solve the problem of active BPF listeners clearing promiscuous mode can result in the tick callout being restarted which in turn will trigger a panic once it's actually gone. - Introduce a dying flag which is set during detach and checked in gem_ioctl() in order to prevent active BPF listeners to clear promiscuous mode which may lead to the tick callout being restarted which will trigger a panic once it's actually gone. - In gem_stop() reset rather than just disable the transmitter and receiver in order to ensure we're not unloading DMA maps still in use by the hardware. [1] - The blanking time is specified in PCI clocks so we should use twice the value when operating at 66MHz. - Spell some 2 as ETHER_ALIGN and a 19 as GEM_STATUS_TX_COMPLETION_SHFT to make the actual intentions clear. - As we don't unload the peak attempts counter ignore its overflow interrupts. - Remove a stale setting of a variable to GEM_TD_INTERRUPT_ME which isn't used afterwards. - For optimum performance increment the TX kick register in multiples of 4 if possible as suggested by the documentation. - Partially revert r164931; drivers should only clear the watchdog timer if all outstanding TX descriptors are done. - Fix some debugging strings. - Add a missing BUS_DMASYNC_POSTWRITE in gem_rint(). - As the error paths in the interrupt handler are generally unlikely predict them as false. - Add support for the SBus version of the GEM controller. [2] - Add some lock assertions. - Improve some comments. - Fix some more or less cosmetic issues in the code of the PCI front-end. - Change some softc members to be unsigned where more appropriate and remove unused ones. Obtained from: NetBSD (partially) [2], OpenBSD [1] Added: stable/7/sys/dev/gem/if_gem_sbus.c - copied unchanged from r194763, head/sys/dev/gem/if_gem_sbus.c Modified: stable/7/sys/ (props changed) stable/7/sys/conf/files stable/7/sys/contrib/pf/ (props changed) stable/7/sys/dev/gem/if_gem.c stable/7/sys/dev/gem/if_gem_pci.c stable/7/sys/dev/gem/if_gemreg.h stable/7/sys/dev/gem/if_gemvar.h stable/7/sys/modules/gem/Makefile Modified: stable/7/sys/conf/files ============================================================================== --- stable/7/sys/conf/files Sun Sep 20 12:40:56 2009 (r197348) +++ stable/7/sys/conf/files Sun Sep 20 12:56:50 2009 (r197349) @@ -841,6 +841,7 @@ dev/flash/at45d.c optional at45d dev/fxp/if_fxp.c optional fxp dev/gem/if_gem.c optional gem dev/gem/if_gem_pci.c optional gem pci +dev/gem/if_gem_sbus.c optional gem sbus dev/hatm/if_hatm.c optional hatm pci dev/hatm/if_hatm_intr.c optional hatm pci dev/hatm/if_hatm_ioctl.c optional hatm pci Modified: stable/7/sys/dev/gem/if_gem.c ============================================================================== --- stable/7/sys/dev/gem/if_gem.c Sun Sep 20 12:40:56 2009 (r197348) +++ stable/7/sys/dev/gem/if_gem.c Sun Sep 20 12:56:50 2009 (r197349) @@ -84,7 +84,7 @@ __FBSDID("$FreeBSD$"); CTASSERT(powerof2(GEM_NRXDESC) && GEM_NRXDESC >= 32 && GEM_NRXDESC <= 8192); CTASSERT(powerof2(GEM_NTXDESC) && GEM_NTXDESC >= 32 && GEM_NTXDESC <= 8192); -#define TRIES 10000 +#define GEM_TRIES 10000 /* * The hardware supports basic TCP/UDP checksum offloading. However, @@ -119,7 +119,7 @@ static void gem_rint(struct gem_softc *s #ifdef GEM_RINT_TIMEOUT static void gem_rint_timeout(void *arg); #endif -static __inline void gem_rxcksum(struct mbuf *m, uint64_t flags); +static inline void gem_rxcksum(struct mbuf *m, uint64_t flags); static void gem_rxdrain(struct gem_softc *sc); static void gem_setladrf(struct gem_softc *sc); static void gem_start(struct ifnet *ifp); @@ -127,6 +127,7 @@ static void gem_start_locked(struct ifne static void gem_stop(struct ifnet *ifp, int disable); static void gem_tick(void *arg); static void gem_tint(struct gem_softc *sc); +static inline void gem_txkick(struct gem_softc *sc); static int gem_watchdog(struct gem_softc *sc); devclass_t gem_devclass; @@ -151,9 +152,24 @@ gem_attach(struct gem_softc *sc) int error, i; uint32_t v; + if (bootverbose) + device_printf(sc->sc_dev, "flags=0x%x\n", sc->sc_flags); + + /* Set up ifnet structure. */ ifp = sc->sc_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) return (ENOSPC); + sc->sc_csum_features = GEM_CSUM_FEATURES; + ifp->if_softc = sc; + if_initname(ifp, device_get_name(sc->sc_dev), + device_get_unit(sc->sc_dev)); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_start = gem_start; + ifp->if_ioctl = gem_ioctl; + ifp->if_init = gem_init; + IFQ_SET_MAXLEN(&ifp->if_snd, GEM_TXQUEUELEN); + ifp->if_snd.ifq_drv_maxlen = GEM_TXQUEUELEN; + IFQ_SET_READY(&ifp->if_snd); callout_init_mtx(&sc->sc_tick_ch, &sc->sc_mtx, 0); #ifdef GEM_RINT_TIMEOUT @@ -161,27 +177,26 @@ gem_attach(struct gem_softc *sc) #endif /* Make sure the chip is stopped. */ - ifp->if_softc = sc; gem_reset(sc); error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE_32BIT, 0, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, &sc->sc_pdmatag); - if (error) + if (error != 0) goto fail_ifnet; error = bus_dma_tag_create(sc->sc_pdmatag, 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->sc_rdmatag); - if (error) + if (error != 0) goto fail_ptag; error = bus_dma_tag_create(sc->sc_pdmatag, 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES * GEM_NTXSEGS, GEM_NTXSEGS, MCLBYTES, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->sc_tdmatag); - if (error) + if (error != 0) goto fail_rtag; error = bus_dma_tag_create(sc->sc_pdmatag, PAGE_SIZE, 0, @@ -189,7 +204,7 @@ gem_attach(struct gem_softc *sc) sizeof(struct gem_control_data), 1, sizeof(struct gem_control_data), 0, NULL, NULL, &sc->sc_cdmatag); - if (error) + if (error != 0) goto fail_ttag; /* @@ -199,7 +214,7 @@ gem_attach(struct gem_softc *sc) if ((error = bus_dmamem_alloc(sc->sc_cdmatag, (void **)&sc->sc_control_data, BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, - &sc->sc_cddmamap))) { + &sc->sc_cddmamap)) != 0) { device_printf(sc->sc_dev, "unable to allocate control data, error = %d\n", error); goto fail_ctag; @@ -338,19 +353,6 @@ gem_attach(struct gem_softc *sc) device_printf(sc->sc_dev, "%ukB RX FIFO, %ukB TX FIFO\n", sc->sc_rxfifosize / 1024, v / 16); - sc->sc_csum_features = GEM_CSUM_FEATURES; - /* Initialize ifnet structure. */ - ifp->if_softc = sc; - if_initname(ifp, device_get_name(sc->sc_dev), - device_get_unit(sc->sc_dev)); - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; - ifp->if_start = gem_start; - ifp->if_ioctl = gem_ioctl; - ifp->if_init = gem_init; - IFQ_SET_MAXLEN(&ifp->if_snd, GEM_TXQUEUELEN); - ifp->if_snd.ifq_drv_maxlen = GEM_TXQUEUELEN; - IFQ_SET_READY(&ifp->if_snd); - /* Attach the interface. */ ether_ifattach(ifp, sc->sc_enaddr); @@ -401,6 +403,7 @@ gem_detach(struct gem_softc *sc) struct ifnet *ifp = sc->sc_ifp; int i; + ether_ifdetach(ifp); GEM_LOCK(sc); gem_stop(ifp, 1); GEM_UNLOCK(sc); @@ -408,7 +411,6 @@ gem_detach(struct gem_softc *sc) #ifdef GEM_RINT_TIMEOUT callout_drain(&sc->sc_rx_ch); #endif - ether_ifdetach(ifp); if_free(ifp); device_delete_child(sc->sc_dev, sc->sc_miibus); @@ -456,7 +458,7 @@ gem_resume(struct gem_softc *sc) GEM_UNLOCK(sc); } -static __inline void +static inline void gem_rxcksum(struct mbuf *m, uint64_t flags) { struct ether_header *eh; @@ -535,12 +537,11 @@ static void gem_tick(void *arg) { struct gem_softc *sc = arg; - struct ifnet *ifp; + struct ifnet *ifp = sc->sc_ifp; uint32_t v; GEM_LOCK_ASSERT(sc, MA_OWNED); - ifp = sc->sc_ifp; /* * Unload collision and error counters. */ @@ -584,7 +585,7 @@ gem_bitwait(struct gem_softc *sc, u_int int i; uint32_t reg; - for (i = TRIES; i--; DELAY(100)) { + for (i = GEM_TRIES; i--; DELAY(100)) { reg = GEM_BANKN_READ_M(bank, 4, sc, r); if ((reg & clr) == 0 && (reg & set) == set) return (1); @@ -593,8 +594,7 @@ gem_bitwait(struct gem_softc *sc, u_int } static void -gem_reset(sc) - struct gem_softc *sc; +gem_reset(struct gem_softc *sc) { #ifdef GEM_DEBUG @@ -644,9 +644,8 @@ gem_stop(struct ifnet *ifp, int disable) callout_stop(&sc->sc_rx_ch); #endif - /* XXX should we reset these instead? */ - gem_disable_tx(sc); - gem_disable_rx(sc); + gem_reset_tx(sc); + gem_reset_rx(sc); /* * Release any queued transmit buffers. @@ -721,7 +720,7 @@ gem_reset_rxdma(struct gem_softc *sc) if (sc->sc_rxsoft[i].rxs_mbuf != NULL) GEM_UPDATE_RXDESC(sc, i); sc->sc_rxptr = 0; - GEM_CDSYNC(sc, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + GEM_CDSYNC(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); /* NOTE: we use only 32-bit DMA addresses here. */ GEM_BANK1_WRITE_4(sc, GEM_RX_RING_PTR_HI, 0); @@ -732,9 +731,11 @@ gem_reset_rxdma(struct gem_softc *sc) ((ETHER_HDR_LEN + sizeof(struct ip)) << GEM_RX_CONFIG_CXM_START_SHFT) | (GEM_THRSH_1024 << GEM_RX_CONFIG_FIFO_THRS_SHIFT) | - (2 << GEM_RX_CONFIG_FBOFF_SHFT)); + (ETHER_ALIGN << GEM_RX_CONFIG_FBOFF_SHFT)); + /* Adjust for the SBus clock probably isn't worth the fuzz. */ GEM_BANK1_WRITE_4(sc, GEM_RX_BLANKING, - (6 << GEM_RX_BLANKING_TIME_SHIFT) | 6); + ((6 * (sc->sc_flags & GEM_PCI66) != 0 ? 2 : 1) << + GEM_RX_BLANKING_TIME_SHIFT) | 6); GEM_BANK1_WRITE_4(sc, GEM_RX_PAUSE_THRESH, (3 * sc->sc_rxfifosize / 256) | ((sc->sc_rxfifosize / 256) << 12)); @@ -798,12 +799,13 @@ gem_disable_tx(struct gem_softc *sc) } static int -gem_meminit(sc) - struct gem_softc *sc; +gem_meminit(struct gem_softc *sc) { struct gem_rxsoft *rxs; int error, i; + GEM_LOCK_ASSERT(sc, MA_OWNED); + /* * Initialize the transmit descriptor ring. */ @@ -837,7 +839,8 @@ gem_meminit(sc) GEM_INIT_RXDESC(sc, i); } sc->sc_rxptr = 0; - GEM_CDSYNC(sc, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + + GEM_CDSYNC(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); return (0); } @@ -938,6 +941,20 @@ gem_init_locked(struct gem_softc *sc) #endif /* step 8. Global Configuration & Interrupt Mask */ + + /* + * Set the internal arbitration to "infinite" bursts of the + * maximum length of 31 * 64 bytes so DMA transfers aren't + * split up in cache line size chunks. This greatly improves + * RX performance. + * Enable silicon bug workarounds for the Apple variants. + */ + GEM_BANK1_WRITE_4(sc, GEM_CONFIG, + GEM_CONFIG_TXDMA_LIMIT | GEM_CONFIG_RXDMA_LIMIT | + ((sc->sc_flags & GEM_PCI) != 0 ? GEM_CONFIG_BURST_INF : + GEM_CONFIG_BURST_64) | (GEM_IS_APPLE(sc) ? + GEM_CONFIG_RONPAULBIT | GEM_CONFIG_BUG2FIX : 0)); + GEM_BANK1_WRITE_4(sc, GEM_INTMASK, ~(GEM_INTR_TX_INTME | GEM_INTR_TX_EMPTY | GEM_INTR_RX_DONE | GEM_INTR_RX_NOBUF | GEM_INTR_RX_TAG_ERR | GEM_INTR_PERR | @@ -949,7 +966,8 @@ gem_init_locked(struct gem_softc *sc) GEM_BANK1_WRITE_4(sc, GEM_MAC_RX_MASK, GEM_MAC_RX_DONE | GEM_MAC_RX_FRAME_CNT); GEM_BANK1_WRITE_4(sc, GEM_MAC_TX_MASK, - GEM_MAC_TX_XMIT_DONE | GEM_MAC_TX_DEFER_EXP); + GEM_MAC_TX_XMIT_DONE | GEM_MAC_TX_DEFER_EXP | + GEM_MAC_TX_PEAK_EXP); #ifdef GEM_DEBUG GEM_BANK1_WRITE_4(sc, GEM_MAC_CONTROL_MASK, ~(GEM_MAC_PAUSED | GEM_MAC_PAUSE | GEM_MAC_RESUME)); @@ -961,7 +979,8 @@ gem_init_locked(struct gem_softc *sc) /* step 9. ETX Configuration: use mostly default values. */ /* Enable DMA. */ - v = gem_ringsize(GEM_NTXDESC /* XXX */); + v = gem_ringsize(GEM_NTXDESC); + /* Set TX FIFO threshold and enable DMA. */ v |= ((sc->sc_variant == GEM_SUN_ERI ? 0x100 : 0x4ff) << 10) & GEM_TX_CONFIG_TXFIFO_TH; GEM_BANK1_WRITE_4(sc, GEM_TX_CONFIG, v | GEM_TX_CONFIG_TXDMA_EN); @@ -973,14 +992,16 @@ gem_init_locked(struct gem_softc *sc) /* RX TCP/UDP checksum offset */ v |= ((ETHER_HDR_LEN + sizeof(struct ip)) << GEM_RX_CONFIG_CXM_START_SHFT); - - /* Enable DMA. */ + /* Set RX FIFO threshold, set first byte offset and enable DMA. */ GEM_BANK1_WRITE_4(sc, GEM_RX_CONFIG, v | (GEM_THRSH_1024 << GEM_RX_CONFIG_FIFO_THRS_SHIFT) | - (2 << GEM_RX_CONFIG_FBOFF_SHFT) | GEM_RX_CONFIG_RXDMA_EN); + (ETHER_ALIGN << GEM_RX_CONFIG_FBOFF_SHFT) | + GEM_RX_CONFIG_RXDMA_EN); + /* Adjust for the SBus clock probably isn't worth the fuzz. */ GEM_BANK1_WRITE_4(sc, GEM_RX_BLANKING, - (6 << GEM_RX_BLANKING_TIME_SHIFT) | 6); + ((6 * (sc->sc_flags & GEM_PCI66) != 0 ? 2 : 1) << + GEM_RX_BLANKING_TIME_SHIFT) | 6); /* * The following value is for an OFF Threshold of about 3/4 full @@ -1002,7 +1023,7 @@ gem_init_locked(struct gem_softc *sc) device_printf(sc->sc_dev, "cannot configure RX MAC\n"); GEM_BANK1_WRITE_4(sc, GEM_MAC_RX_CONFIG, v); - /* step 13. TX_MAC Configuration Register */ + /* step 13. TX_MAC Configuration Register */ v = GEM_BANK1_READ_4(sc, GEM_MAC_TX_CONFIG); v |= GEM_MAC_TX_ENABLE; GEM_BANK1_WRITE_4(sc, GEM_MAC_TX_CONFIG, 0); @@ -1037,6 +1058,8 @@ gem_load_txmbuf(struct gem_softc *sc, st uint64_t cflags, flags; int error, nexttx, nsegs, offset, seg; + GEM_LOCK_ASSERT(sc, MA_OWNED); + /* Get a work queue entry. */ if ((txs = STAILQ_FIRST(&sc->sc_txfreeq)) == NULL) { /* Ran out of descriptors. */ @@ -1143,7 +1166,6 @@ gem_load_txmbuf(struct gem_softc *sc, st #endif if (++sc->sc_txwin > GEM_NTXSEGS * 2 / 3) { sc->sc_txwin = 0; - flags |= GEM_TD_INTERRUPT_ME; sc->sc_txdescs[txs->txs_firstdesc].gd_flags |= GEM_DMA_WRITE(sc, GEM_TD_INTERRUPT_ME | GEM_TD_START_OF_PACKET); @@ -1175,6 +1197,8 @@ gem_init_regs(struct gem_softc *sc) { const u_char *laddr = IF_LLADDR(sc->sc_ifp); + GEM_LOCK_ASSERT(sc, MA_OWNED); + /* These registers are not cleared on reset. */ if ((sc->sc_flags & GEM_INITED) == 0) { /* magic values */ @@ -1182,16 +1206,19 @@ gem_init_regs(struct gem_softc *sc) GEM_BANK1_WRITE_4(sc, GEM_MAC_IPG1, 8); GEM_BANK1_WRITE_4(sc, GEM_MAC_IPG2, 4); + /* min frame length */ GEM_BANK1_WRITE_4(sc, GEM_MAC_MAC_MIN_FRAME, ETHER_MIN_LEN); - /* max frame and max burst size */ + /* max frame length and max burst size */ GEM_BANK1_WRITE_4(sc, GEM_MAC_MAC_MAX_FRAME, (ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN) | (0x2000 << 16)); + /* more magic values */ GEM_BANK1_WRITE_4(sc, GEM_MAC_PREAMBLE_LEN, 0x7); GEM_BANK1_WRITE_4(sc, GEM_MAC_JAM_SIZE, 0x4); GEM_BANK1_WRITE_4(sc, GEM_MAC_ATTEMPT_LIMIT, 0x10); - /* dunno... */ GEM_BANK1_WRITE_4(sc, GEM_MAC_CONTROL_TYPE, 0x8088); + + /* random number seed */ GEM_BANK1_WRITE_4(sc, GEM_MAC_RANDOM_SEED, ((laddr[5] << 8) | laddr[4]) & 0x3ff); @@ -1209,7 +1236,6 @@ gem_init_regs(struct gem_softc *sc) GEM_BANK1_WRITE_4(sc, GEM_MAC_ADDR_FILTER0, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_ADDR_FILTER1, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_ADDR_FILTER2, 0); - GEM_BANK1_WRITE_4(sc, GEM_MAC_ADR_FLT_MASK1_2, 0); GEM_BANK1_WRITE_4(sc, GEM_MAC_ADR_FLT_MASK0, 0); @@ -1232,18 +1258,6 @@ gem_init_regs(struct gem_softc *sc) /* Set XOFF PAUSE time. */ GEM_BANK1_WRITE_4(sc, GEM_MAC_SEND_PAUSE_CMD, 0x1BF0); - /* - * Set the internal arbitration to "infinite" bursts of the - * maximum length of 31 * 64 bytes so DMA transfers aren't - * split up in cache line size chunks. This greatly improves - * especially RX performance. - * Enable silicon bug workarounds for the Apple variants. - */ - GEM_BANK1_WRITE_4(sc, GEM_CONFIG, - GEM_CONFIG_TXDMA_LIMIT | GEM_CONFIG_RXDMA_LIMIT | - GEM_CONFIG_BURST_INF | (GEM_IS_APPLE(sc) ? - GEM_CONFIG_RONPAULBIT | GEM_CONFIG_BUG2FIX : 0)); - /* Set the station address. */ GEM_BANK1_WRITE_4(sc, GEM_MAC_ADDR0, (laddr[4] << 8) | laddr[5]); GEM_BANK1_WRITE_4(sc, GEM_MAC_ADDR1, (laddr[2] << 8) | laddr[3]); @@ -1263,12 +1277,32 @@ gem_start(struct ifnet *ifp) GEM_UNLOCK(sc); } +static inline void +gem_txkick(struct gem_softc *sc) +{ + + /* + * Update the TX kick register. This register has to point to the + * descriptor after the last valid one and for optimum performance + * should be incremented in multiples of 4 (the DMA engine fetches/ + * updates descriptors in batches of 4). + */ +#ifdef GEM_DEBUG + CTR3(KTR_GEM, "%s: %s: kicking TX %d", + device_get_name(sc->sc_dev), __func__, sc->sc_txnext); +#endif + GEM_CDSYNC(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + GEM_BANK1_WRITE_4(sc, GEM_TX_KICK, sc->sc_txnext); +} + static void gem_start_locked(struct ifnet *ifp) { struct gem_softc *sc = ifp->if_softc; struct mbuf *m; - int ntx; + int kicked, ntx; + + GEM_LOCK_ASSERT(sc, MA_OWNED); if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING || (sc->sc_flags & GEM_LINK) == 0) @@ -1280,6 +1314,7 @@ gem_start_locked(struct ifnet *ifp) sc->sc_txnext); #endif ntx = 0; + kicked = 0; for (; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) && sc->sc_txfree > 1;) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) @@ -1291,19 +1326,18 @@ gem_start_locked(struct ifnet *ifp) IFQ_DRV_PREPEND(&ifp->if_snd, m); break; } + if ((sc->sc_txnext % 4) == 0) { + gem_txkick(sc); + kicked = 1; + } else + kicked = 0; ntx++; - /* Kick the transmitter. */ -#ifdef GEM_DEBUG - CTR3(KTR_GEM, "%s: %s: kicking TX %d", - device_get_name(sc->sc_dev), __func__, sc->sc_txnext); -#endif - GEM_CDSYNC(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); - GEM_BANK1_WRITE_4(sc, GEM_TX_KICK, sc->sc_txnext); - BPF_MTAP(ifp, m); } if (ntx > 0) { + if (kicked == 0) + gem_txkick(sc); #ifdef GEM_DEBUG CTR2(KTR_GEM, "%s: packets enqueued, OWN on %d", device_get_name(sc->sc_dev), sc->sc_txnext); @@ -1324,10 +1358,13 @@ gem_tint(struct gem_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct gem_txsoft *txs; - int txlast, progress; + int progress; + uint32_t txlast; #ifdef GEM_DEBUG int i; + GEM_LOCK_ASSERT(sc, MA_OWNED); + CTR2(KTR_GEM, "%s: %s", device_get_name(sc->sc_dev), __func__); #endif @@ -1338,7 +1375,6 @@ gem_tint(struct gem_softc *sc) progress = 0; GEM_CDSYNC(sc, BUS_DMASYNC_POSTREAD); while ((txs = STAILQ_FIRST(&sc->sc_txdirtyq)) != NULL) { - #ifdef GEM_DEBUG if ((ifp->if_flags & IFF_DEBUG) != 0) { printf(" txsoft %p transmit chain:\n", txs); @@ -1419,8 +1455,8 @@ gem_tint(struct gem_softc *sc) * and restart. */ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - sc->sc_wdog_timer = STAILQ_EMPTY(&sc->sc_txdirtyq) ? 0 : 5; - + if (STAILQ_EMPTY(&sc->sc_txdirtyq)) + sc->sc_wdog_timer = 0; gem_start_locked(ifp); } @@ -1437,6 +1473,7 @@ gem_rint_timeout(void *arg) struct gem_softc *sc = arg; GEM_LOCK_ASSERT(sc, MA_OWNED); + gem_rint(sc); } #endif @@ -1449,6 +1486,8 @@ gem_rint(struct gem_softc *sc) uint64_t rxstat; uint32_t rxcomp; + GEM_LOCK_ASSERT(sc, MA_OWNED); + #ifdef GEM_RINT_TIMEOUT callout_stop(&sc->sc_rx_ch); #endif @@ -1461,12 +1500,11 @@ gem_rint(struct gem_softc *sc) * how long the following loop can execute. */ rxcomp = GEM_BANK1_READ_4(sc, GEM_RX_COMPLETION); - #ifdef GEM_DEBUG - CTR3(KTR_GEM, "%s: sc->rxptr %d, complete %d", + CTR3(KTR_GEM, "%s: sc->sc_rxptr %d, complete %d", __func__, sc->sc_rxptr, rxcomp); #endif - GEM_CDSYNC(sc, BUS_DMASYNC_POSTREAD); + GEM_CDSYNC(sc, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); for (; sc->sc_rxptr != rxcomp;) { m = sc->sc_rxsoft[sc->sc_rxptr].rxs_mbuf; rxstat = GEM_DMA_READ(sc, @@ -1525,9 +1563,9 @@ gem_rint(struct gem_softc *sc) /* * Update the RX kick register. This register has to point * to the descriptor after the last valid one (before the - * current batch) and must be incremented in multiples of - * 4 (because the DMA engine fetches/updates descriptors - * in batches of 4). + * current batch) and for optimum performance should be + * incremented in multiples of 4 (the DMA engine fetches/ + * updates descriptors in batches of 4). */ sc->sc_rxptr = GEM_NEXTRX(sc->sc_rxptr); if ((sc->sc_rxptr % 4) == 0) { @@ -1545,7 +1583,7 @@ gem_rint(struct gem_softc *sc) } ifp->if_ipackets++; - m->m_data += 2; /* We're already off by two */ + m->m_data += ETHER_ALIGN; /* first byte offset */ m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = GEM_RD_BUFLEN(rxstat); @@ -1559,7 +1597,7 @@ gem_rint(struct gem_softc *sc) } #ifdef GEM_DEBUG - CTR3(KTR_GEM, "%s: done sc->rxptr %d, complete %d", __func__, + CTR3(KTR_GEM, "%s: done sc->sc_rxptr %d, complete %d", __func__, sc->sc_rxptr, GEM_BANK1_READ_4(sc, GEM_RX_COMPLETION)); #endif } @@ -1572,6 +1610,8 @@ gem_add_rxbuf(struct gem_softc *sc, int bus_dma_segment_t segs[1]; int error, nsegs; + GEM_LOCK_ASSERT(sc, MA_OWNED); + m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) return (ENOBUFS); @@ -1620,7 +1660,15 @@ gem_eint(struct gem_softc *sc, u_int sta return; } - device_printf(sc->sc_dev, "%s: status=%x\n", __func__, status); + device_printf(sc->sc_dev, "%s: status 0x%x", __func__, status); + if ((status & GEM_INTR_BERR) != 0) { + if ((sc->sc_flags & GEM_PCI) != 0) + printf(", PCI bus error 0x%x\n", + GEM_BANK1_READ_4(sc, GEM_PCI_ERROR_STATUS)); + else + printf(", SBus error 0x%x\n", + GEM_BANK1_READ_4(sc, GEM_SBUS_STATUS)); + } } void @@ -1634,8 +1682,8 @@ gem_intr(void *v) #ifdef GEM_DEBUG CTR4(KTR_GEM, "%s: %s: cplt %x, status %x", - device_get_name(sc->sc_dev), __func__, (status >> 19), - (u_int)status); + device_get_name(sc->sc_dev), __func__, + (status >> GEM_STATUS_TX_COMPLETION_SHFT), (u_int)status); /* * PCS interrupts must be cleared, otherwise no traffic is passed! @@ -1665,7 +1713,7 @@ gem_intr(void *v) device_printf(sc->sc_dev, "%s: MIF interrupt\n", __func__); #endif - if ((status & + if (__predict_false(status & (GEM_INTR_RX_TAG_ERR | GEM_INTR_PERR | GEM_INTR_BERR)) != 0) gem_eint(sc, status); @@ -1675,17 +1723,20 @@ gem_intr(void *v) if ((status & (GEM_INTR_TX_EMPTY | GEM_INTR_TX_INTME)) != 0) gem_tint(sc); - if (status & GEM_INTR_TX_MAC) { + if (__predict_false((status & GEM_INTR_TX_MAC) != 0)) { status2 = GEM_BANK1_READ_4(sc, GEM_MAC_TX_STATUS); if ((status2 & - ~(GEM_MAC_TX_XMIT_DONE | GEM_MAC_TX_DEFER_EXP)) != 0) + ~(GEM_MAC_TX_XMIT_DONE | GEM_MAC_TX_DEFER_EXP | + GEM_MAC_TX_PEAK_EXP)) != 0) device_printf(sc->sc_dev, "MAC TX fault, status %x\n", status2); if ((status2 & - (GEM_MAC_TX_UNDERRUN | GEM_MAC_TX_PKT_TOO_LONG)) != 0) + (GEM_MAC_TX_UNDERRUN | GEM_MAC_TX_PKT_TOO_LONG)) != 0) { + sc->sc_ifp->if_oerrors++; gem_init_locked(sc); + } } - if (status & GEM_INTR_RX_MAC) { + if (__predict_false((status & GEM_INTR_RX_MAC) != 0)) { status2 = GEM_BANK1_READ_4(sc, GEM_MAC_RX_STATUS); /* * At least with GEM_SUN_GEM and some GEM_SUN_ERI @@ -1906,6 +1957,8 @@ gem_mii_statchg(device_t dev) sc = device_get_softc(dev); + GEM_LOCK_ASSERT(sc, MA_OWNED); + #ifdef GEM_DEBUG if ((sc->sc_ifp->if_flags & IFF_DEBUG) != 0) device_printf(sc->sc_dev, "%s: status change: PHY = %d\n", @@ -1985,7 +2038,7 @@ gem_mii_statchg(device_t dev) if ((GEM_BANK1_READ_4(sc, GEM_MIF_CONFIG) & GEM_MIF_CONFIG_PHY_SEL) != 0) { /* External MII needs echo disable if half duplex. */ - if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & + if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & IFM_FDX) == 0) v |= GEM_MAC_XIF_ECHO_DISABL; } else Modified: stable/7/sys/dev/gem/if_gem_pci.c ============================================================================== --- stable/7/sys/dev/gem/if_gem_pci.c Sun Sep 20 12:40:56 2009 (r197348) +++ stable/7/sys/dev/gem/if_gem_pci.c Sun Sep 20 12:56:50 2009 (r197349) @@ -90,7 +90,7 @@ static device_method_t gem_pci_methods[] DEVMETHOD(miibus_writereg, gem_mii_writereg), DEVMETHOD(miibus_statchg, gem_mii_statchg), - { 0, 0 } + KOBJMETHOD_END }; static driver_t gem_pci_driver = { @@ -107,7 +107,7 @@ static const struct gem_pci_dev { uint32_t gpd_devid; int gpd_variant; const char *gpd_desc; -} gem_pci_devlist[] = { +} const gem_pci_devlist[] = { { 0x1101108e, GEM_SUN_ERI, "Sun ERI 10/100 Ethernet" }, { 0x2bad108e, GEM_SUN_GEM, "Sun GEM Gigabit Ethernet" }, { 0x0021106b, GEM_APPLE_GMAC, "Apple UniNorth GMAC Ethernet" }, @@ -200,13 +200,18 @@ gem_pci_attach(device_t dev) GEM_PCI_BANK2_OFFSET, GEM_PCI_BANK2_SIZE, &sc->sc_res[GEM_RES_BANK2]->r_bushandle); + /* Determine whether we're running at 66MHz. */ + if ((GEM_BANK2_READ_4(sc, GEM_PCI_BIF_CONFIG) & + GEM_PCI_BIF_CNF_M66EN) != 0) + sc->sc_flags |= GEM_PCI66; + #if defined(__powerpc__) || defined(__sparc64__) OF_getetheraddr(dev, sc->sc_enaddr); #else /* * Dig out VPD (vital product data) and read NA (network address). - * The VPD of GEM resides in the PCI Expansion ROM (PCI FCode) and - * can't be accessed via the PCI capability pointer. + * The VPD resides in the PCI Expansion ROM (PCI FCode) and can't + * be accessed via the PCI capability pointer. * ``Writing FCode 3.x Programs'' (newer ones, dated 1997 and later) * chapter 2 describes the data structure. */ @@ -225,22 +230,21 @@ gem_pci_attach(device_t dev) #define PCI_VPDRES_BYTE0 0x00 #define PCI_VPDRES_ISLARGE(x) ((x) & 0x80) #define PCI_VPDRES_LARGE_NAME(x) ((x) & 0x7f) -#define PCI_VPDRES_TYPE_VPD 0x10 /* large */ #define PCI_VPDRES_LARGE_LEN_LSB 0x01 #define PCI_VPDRES_LARGE_LEN_MSB 0x02 -#define PCI_VPDRES_LARGE_DATA 0x03 -#define PCI_VPD_SIZE 0x03 +#define PCI_VPDRES_LARGE_SIZE 0x03 +#define PCI_VPDRES_TYPE_VPD 0x10 /* large */ #define PCI_VPD_KEY0 0x00 #define PCI_VPD_KEY1 0x01 #define PCI_VPD_LEN 0x02 -#define PCI_VPD_DATA 0x03 +#define PCI_VPD_SIZE 0x03 #define GEM_ROM_READ_1(sc, offs) \ - GEM_BANK1_READ_1((sc), GEM_PCI_ROM_OFFSET + (offs)) + GEM_BANK1_READ_1((sc), GEM_PCI_ROM_OFFSET + (offs)) #define GEM_ROM_READ_2(sc, offs) \ - GEM_BANK1_READ_2((sc), GEM_PCI_ROM_OFFSET + (offs)) + GEM_BANK1_READ_2((sc), GEM_PCI_ROM_OFFSET + (offs)) #define GEM_ROM_READ_4(sc, offs) \ - GEM_BANK1_READ_4((sc), GEM_PCI_ROM_OFFSET + (offs)) + GEM_BANK1_READ_4((sc), GEM_PCI_ROM_OFFSET + (offs)) /* Read PCI Expansion ROM header. */ if (GEM_ROM_READ_2(sc, PCI_ROMHDR_SIG) != PCI_ROMHDR_SIG_MAGIC || @@ -273,22 +277,22 @@ gem_pci_attach(device_t dev) j + PCI_VPDRES_BYTE0)) == 0 || PCI_VPDRES_LARGE_NAME(GEM_ROM_READ_1(sc, j + PCI_VPDRES_BYTE0)) != PCI_VPDRES_TYPE_VPD || - (GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_LEN_LSB) << 8 | + ((GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_LEN_LSB) << 8) | GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_LEN_MSB)) != PCI_VPD_SIZE + ETHER_ADDR_LEN || - GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_DATA + PCI_VPD_KEY0) != + GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_SIZE + PCI_VPD_KEY0) != 0x4e /* N */ || - GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_DATA + PCI_VPD_KEY1) != + GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_SIZE + PCI_VPD_KEY1) != 0x41 /* A */ || - GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_DATA + PCI_VPD_LEN) != + GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_SIZE + PCI_VPD_LEN) != ETHER_ADDR_LEN || - GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_DATA + PCI_VPD_DATA + + GEM_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_SIZE + PCI_VPD_SIZE + ETHER_ADDR_LEN) != 0x79) { device_printf(dev, "unexpected PCI VPD\n"); goto fail; } bus_read_region_1(sc->sc_res[GEM_RES_BANK1], - GEM_PCI_ROM_OFFSET + j + PCI_VPDRES_LARGE_DATA + PCI_VPD_DATA, + GEM_PCI_ROM_OFFSET + j + PCI_VPDRES_LARGE_SIZE + PCI_VPD_SIZE, sc->sc_enaddr, ETHER_ADDR_LEN); #endif @@ -330,19 +334,15 @@ gem_pci_detach(device_t dev) static int gem_pci_suspend(device_t dev) { - struct gem_softc *sc; - sc = device_get_softc(dev); - gem_suspend(sc); + gem_suspend(device_get_softc(dev)); return (0); } static int gem_pci_resume(device_t dev) { - struct gem_softc *sc; - sc = device_get_softc(dev); - gem_resume(sc); + gem_resume(device_get_softc(dev)); return (0); } Copied: stable/7/sys/dev/gem/if_gem_sbus.c (from r194763, head/sys/dev/gem/if_gem_sbus.c) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ stable/7/sys/dev/gem/if_gem_sbus.c Sun Sep 20 12:56:50 2009 (r197349, copy of r194763, head/sys/dev/gem/if_gem_sbus.c) @@ -0,0 +1,210 @@ +/*- + * Copyright (C) 2001 Eduardo Horvath. + * Copyright (c) 2007 Marius Strobl <marius@FreeBSD.org> + * All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: NetBSD: if_gem_pci.c,v 1.7 2001/10/18 15:09:15 thorpej Exp + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * SBus bindings for Sun GEM Ethernet controllers + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/resource.h> +#include <sys/rman.h> +#include <sys/socket.h> + +#include <net/ethernet.h> +#include <net/if.h> + +#include <dev/ofw/ofw_bus.h> + +#include <machine/bus.h> +#include <machine/ofw_machdep.h> +#include <machine/resource.h> + +#include <sparc64/sbus/sbusvar.h> + +#include <dev/gem/if_gemreg.h> +#include <dev/gem/if_gemvar.h> + +#include "miibus_if.h" + +static device_probe_t gem_sbus_probe; +static device_attach_t gem_sbus_attach; +static device_detach_t gem_sbus_detach; +static device_suspend_t gem_sbus_suspend; +static device_resume_t gem_sbus_resume; + +static device_method_t gem_sbus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, gem_sbus_probe), + DEVMETHOD(device_attach, gem_sbus_attach), + DEVMETHOD(device_detach, gem_sbus_detach), + DEVMETHOD(device_suspend, gem_sbus_suspend), + DEVMETHOD(device_resume, gem_sbus_resume), + /* Use the suspend handler here, it is all that is required. */ + DEVMETHOD(device_shutdown, gem_sbus_suspend), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, gem_mii_readreg), + DEVMETHOD(miibus_writereg, gem_mii_writereg), + DEVMETHOD(miibus_statchg, gem_mii_statchg), + + KOBJMETHOD_END +}; + +static driver_t gem_sbus_driver = { + "gem", + gem_sbus_methods, + sizeof(struct gem_softc) +}; + +DRIVER_MODULE(gem, sbus, gem_sbus_driver, gem_devclass, 0, 0); +MODULE_DEPEND(gem, sbus, 1, 1, 1); +MODULE_DEPEND(gem, ether, 1, 1, 1); + +static int +gem_sbus_probe(device_t dev) +{ + + if (strcmp(ofw_bus_get_name(dev), "network") == 0 && + ofw_bus_get_compat(dev) != NULL && + strcmp(ofw_bus_get_compat(dev), "SUNW,sbus-gem") == 0) { + device_set_desc(dev, "Sun GEM Gigabit Ethernet"); + return (0); + } + + return (ENXIO); +} + +static struct resource_spec gem_sbus_res_spec[] = { + { SYS_RES_IRQ, 0, RF_SHAREABLE | RF_ACTIVE }, /* GEM_RES_INTR */ + { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* GEM_RES_BANK1 */ + { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* GEM_RES_BANK2 */ + { -1, 0 } +}; + +static int +gem_sbus_attach(device_t dev) +{ + struct gem_softc *sc; + int burst; + uint32_t val; + + sc = device_get_softc(dev); + sc->sc_variant = GEM_SUN_GEM; + sc->sc_dev = dev; + + if (bus_alloc_resources(dev, gem_sbus_res_spec, sc->sc_res)) { + device_printf(dev, "failed to allocate resources\n"); + bus_release_resources(dev, gem_sbus_res_spec, sc->sc_res); + return (ENXIO); + } + + GEM_LOCK_INIT(sc, device_get_nameunit(dev)); + + OF_getetheraddr(dev, sc->sc_enaddr); + + burst = sbus_get_burstsz(dev); + val = GEM_SBUS_CFG_PARITY; + if ((burst & SBUS_BURST64_MASK) != 0) { + val |= GEM_SBUS_CFG_64BIT; + burst >>= SBUS_BURST64_SHIFT; + } + if ((burst & SBUS_BURST_64) != 0) + val |= GEM_SBUS_CFG_BURST_64; + else if ((burst & SBUS_BURST_32) != 0) + val |= GEM_SBUS_CFG_BURST_32; + else { + device_printf(dev, "unsupported burst size\n"); + goto fail; + } + /* Reset the SBus interface only. */ + (void)GEM_BANK2_READ_4(sc, GEM_SBUS_BIF_RESET); + DELAY(100); + GEM_BANK2_WRITE_4(sc, GEM_SBUS_CONFIG, val); + + if (gem_attach(sc) != 0) { + device_printf(dev, "could not be attached\n"); + goto fail; + } + + if (bus_setup_intr(dev, sc->sc_res[GEM_RES_INTR], INTR_TYPE_NET | + INTR_MPSAFE, NULL, gem_intr, sc, &sc->sc_ih) != 0) { + device_printf(dev, "failed to set up interrupt\n"); + gem_detach(sc); + goto fail; + } + return (0); + + fail: + GEM_LOCK_DESTROY(sc); + bus_release_resources(dev, gem_sbus_res_spec, sc->sc_res); + return (ENXIO); +} + +static int +gem_sbus_detach(device_t dev) +{ + struct gem_softc *sc; + + sc = device_get_softc(dev); + bus_teardown_intr(dev, sc->sc_res[GEM_RES_INTR], sc->sc_ih); + gem_detach(sc); + GEM_LOCK_DESTROY(sc); + bus_release_resources(dev, gem_sbus_res_spec, sc->sc_res); + return (0); +} + +static int +gem_sbus_suspend(device_t dev) +{ + + gem_suspend(device_get_softc(dev)); + return (0); +} + +static int +gem_sbus_resume(device_t dev) +{ + + gem_resume(device_get_softc(dev)); + return (0); +} Modified: stable/7/sys/dev/gem/if_gemreg.h ============================================================================== --- stable/7/sys/dev/gem/if_gemreg.h Sun Sep 20 12:40:56 2009 (r197348) +++ stable/7/sys/dev/gem/if_gemreg.h Sun Sep 20 12:56:50 2009 (r197349) @@ -32,7 +32,7 @@ #ifndef _IF_GEMREG_H #define _IF_GEMREG_H -/* Register definitions for Sun GEM gigabit ethernet */ +/* register definitions for Apple GMAC, Sun ERI and Sun GEM */ /* * First bank: this registers live at the start of the PCI @@ -47,97 +47,110 @@ #define GEM_INTACK 0x0014 /* Interrupt acknowledge, W/O */ #define GEM_STATUS_ALIAS 0x001c *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200909201256.n8KCuofu057064>