kern/112089: re(4): only works after a lot of patching
Ed Schouten
ed at fxq.nl
Tue Apr 24 19:40:06 UTC 2007
>Number: 112089
>Category: kern
>Synopsis: re(4): only works after a lot of patching
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: sw-bug
>Submitter-Id: current-users
>Arrival-Date: Tue Apr 24 19:40:05 GMT 2007
>Closed-Date:
>Last-Modified:
>Originator: Ed Schouten
>Release: FreeBSD 6.2-STABLE i386
>Organization:
>Environment:
FreeBSD 6.2-STABLE #0: Sun Apr 22 17:39:47 CEST 2007
re0: <RealTek 8168/8111B PCIe Gigabit Ethernet> port 0xa800-0xa8ff mem 0xfeaff000-0xfeafffff irq 19 at device 0.0 on pci3
miibus0: <MII bus> on re0
rgephy0: <RTL8169S/8110S media interface> on miibus0
rgephy0: 10baseT, 10baseT-FDX, 100baseTX, 100baseTX-FDX, 1000baseT, 1000baseT-FDX, auto
re0: Ethernet address: 00:18:f3:75:0a:0d
re0: [FAST]
re0 at pci3:0:0: class=0x020000 card=0x81aa1043 chip=0x816810ec rev=0x01 hdr=0x00
vendor = 'Realtek Semiconductor'
class = network
subclass = ethernet
>Description:
My desktop with an ASUS P5B motherboard has an on-board re(4) network
interface. It has three problems, sorted by importance:
- I have to run `ifconfig re0 promisc' in order to accept IPv6 traffic
properly.
- In a lot of cases, TCP traffic gets lost, causing important webpages
like Google.com to not appear.
- Running `tcpdump -i re0 -n' renders the interface for about 8 seconds
inactive.
>How-To-Repeat:
>Fix:
Based on a lot of emails on the FreeBSD mailinglists, I've composed a
patch that fixes these issues. I've been using it on my desktop for a
couple of months now and it's the only way I can use my NIC properly.
--- src/sys/dev/re/if_re.c Thu Mar 15 19:34:07 2007
+++ src/sys/dev/re/if_re.c Thu Mar 15 19:31:20 2007
@@ -249,6 +249,7 @@
static int re_ioctl (struct ifnet *, u_long, caddr_t);
static void re_init (void *);
static void re_init_locked (struct rl_softc *);
+static void re_init_rxcfg (struct rl_softc *);
static void re_stop (struct rl_softc *);
static void re_watchdog (struct rl_softc *);
static int re_suspend (device_t);
@@ -620,6 +621,7 @@
struct ifmultiaddr *ifma;
u_int32_t rxfilt;
int mcnt = 0;
+ u_int32_t hwrev;
RL_LOCK_ASSERT(sc);
@@ -660,8 +662,24 @@
rxfilt &= ~RL_RXCFG_RX_MULTI;
CSR_WRITE_4(sc, RL_RXCFG, rxfilt);
- CSR_WRITE_4(sc, RL_MAR0, hashes[0]);
- CSR_WRITE_4(sc, RL_MAR4, hashes[1]);
+
+ /*
+ * For some unfathomable reason, RealTek decided to reverse
+ * the order of the multicast hash registers in the PCI Express
+ * parts. This means we have to write the hash pattern in reverse
+ * order for those devices.
+ */
+
+ hwrev = CSR_READ_4(sc, RL_TXCFG) & RL_TXCFG_HWREV;
+
+ if (hwrev == RL_HWREV_8100E || hwrev == RL_HWREV_8101E ||
+ hwrev == RL_HWREV_8168_SPIN1 || hwrev == RL_HWREV_8168_SPIN2) {
+ CSR_WRITE_4(sc, RL_MAR0, bswap32(hashes[1]));
+ CSR_WRITE_4(sc, RL_MAR4, bswap32(hashes[0]));
+ } else {
+ CSR_WRITE_4(sc, RL_MAR0, hashes[0]);
+ CSR_WRITE_4(sc, RL_MAR4, hashes[1]);
+ }
}
static void
@@ -2040,7 +2058,8 @@
* below can assemble the packet into a single buffer that's
* padded out to the mininum frame size.
*/
- if (arg.rl_flags && (*m_head)->m_pkthdr.len < RL_MIN_FRAMELEN)
+ if (arg.rl_flags && !(arg.rl_flags & RL_TDESC_CMD_TCPCSUM) &&
+ (*m_head)->m_pkthdr.len < RL_MIN_FRAMELEN)
error = EFBIG;
else
error = bus_dmamap_load_mbuf(sc->rl_ldata.rl_mtag, map,
@@ -2243,7 +2262,6 @@
{
struct ifnet *ifp = sc->rl_ifp;
struct mii_data *mii;
- u_int32_t rxcfg = 0;
union {
uint32_t align_dummy;
u_char eaddr[ETHER_ADDR_LEN];
@@ -2322,31 +2340,8 @@
CSR_WRITE_1(sc, RL_EARLY_TX_THRESH, 16);
CSR_WRITE_4(sc, RL_RXCFG, RL_RXCFG_CONFIG);
-
- /* Set the individual bit to receive frames for this host only. */
- rxcfg = CSR_READ_4(sc, RL_RXCFG);
- rxcfg |= RL_RXCFG_RX_INDIV;
-
- /* If we want promiscuous mode, set the allframes bit. */
- if (ifp->if_flags & IFF_PROMISC)
- rxcfg |= RL_RXCFG_RX_ALLPHYS;
- else
- rxcfg &= ~RL_RXCFG_RX_ALLPHYS;
- CSR_WRITE_4(sc, RL_RXCFG, rxcfg);
-
- /*
- * Set capture broadcast bit to capture broadcast frames.
- */
- if (ifp->if_flags & IFF_BROADCAST)
- rxcfg |= RL_RXCFG_RX_BROAD;
- else
- rxcfg &= ~RL_RXCFG_RX_BROAD;
- CSR_WRITE_4(sc, RL_RXCFG, rxcfg);
-
- /*
- * Program the multicast filter, if necessary.
- */
- re_setmulti(sc);
+
+ re_init_rxcfg(sc);
#ifdef DEVICE_POLLING
/*
@@ -2412,6 +2407,39 @@
callout_reset(&sc->rl_stat_callout, hz, re_tick, sc);
}
+static void
+re_init_rxcfg(sc)
+ struct rl_softc *sc;
+{
+ u_int32_t rxcfg;
+ struct ifnet *ifp = sc->rl_ifp;
+
+ /* Set the individual bit to receive frames for this host only. */
+ rxcfg = CSR_READ_4(sc, RL_RXCFG);
+ rxcfg |= RL_RXCFG_RX_INDIV;
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC)
+ rxcfg |= RL_RXCFG_RX_ALLPHYS;
+ else
+ rxcfg &= ~RL_RXCFG_RX_ALLPHYS;
+ CSR_WRITE_4(sc, RL_RXCFG, rxcfg);
+
+ /*
+ * Set capture broadcast bit to capture broadcast frames.
+ */
+ if (ifp->if_flags & IFF_BROADCAST)
+ rxcfg |= RL_RXCFG_RX_BROAD;
+ else
+ rxcfg &= ~RL_RXCFG_RX_BROAD;
+ CSR_WRITE_4(sc, RL_RXCFG, rxcfg);
+
+ /*
+ * Program the multicast filter, if necessary.
+ */
+ re_setmulti(sc);
+}
+
/*
* Set media options.
*/
@@ -2473,10 +2501,16 @@
break;
case SIOCSIFFLAGS:
RL_LOCK(sc);
- if (ifp->if_flags & IFF_UP)
- re_init_locked(sc);
- else if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ if (ifp->if_flags & IFF_UP) {
+ if ((ifp->if_flags ^ sc->rl_if_flags) &
+ (IFF_PROMISC | IFF_BROADCAST))
+ re_init_rxcfg(sc);
+ else
+ re_init_locked(sc);
+ } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
re_stop(sc);
+ }
+ sc->rl_if_flags = ifp->if_flags;
RL_UNLOCK(sc);
break;
case SIOCADDMULTI:
--- src/sys/pci/if_rlreg.h Sat Dec 2 00:07:27 2006
+++ src/sys/pci/if_rlreg.h Sat Dec 2 00:18:53 2006
@@ -737,6 +737,7 @@
struct mtx rl_intlock;
int rl_txstart;
int rl_link;
+ int rl_if_flags;
};
#define RL_LOCK(_sc) mtx_lock(&(_sc)->rl_mtx)
>Release-Note:
>Audit-Trail:
>Unformatted:
More information about the freebsd-bugs
mailing list