summaryrefslogtreecommitdiffstats
path: root/sys/dev/sk
diff options
context:
space:
mode:
authoryongari <yongari@FreeBSD.org>2009-05-01 03:24:03 +0000
committeryongari <yongari@FreeBSD.org>2009-05-01 03:24:03 +0000
commit700abeab8a81c590ae255dfa7225c8d891c5ca88 (patch)
tree0be56c8944829e4dad93d8f499c34b08f7c3f9b2 /sys/dev/sk
parentd462264a61048da50ccb5ab1ed29191f5a2e0a2f (diff)
downloadFreeBSD-src-700abeab8a81c590ae255dfa7225c8d891c5ca88.zip
FreeBSD-src-700abeab8a81c590ae255dfa7225c8d891c5ca88.tar.gz
Separate multicast filtering of SysKonnect GENESIS and Marvell
Yukon from common multicast handling code. Yukon uses hash-based multicast filtering(big endian form) but GENESIS uses perfect multicast filtering as well as hash-based one(little endian form). Due to the differences of multicast filtering there is no much sense to have a common code. o Remove sk_setmulti() and introduce sk_rxfilter_yukon(), sk_rxfilter_yukon() that handles multicast filtering setup. o Have sk_rxfilter_{yukon, genesis} handle promiscuous mode and nuke sk_setpromisc(). This simplifies ioctl handler as well as giving a chance to check validity of Rx control register of Yukon. o Don't reinitialize controller when IFF_ALLMULTI flags is changed. o Nuke sk_gmchash(), it's not needed anymore. o Always reconfigure Rx control register whenever a new multicast filtering condition is changed. This fixes multicast filtering setup on Yukon. PR: kern/134051
Diffstat (limited to 'sys/dev/sk')
-rw-r--r--sys/dev/sk/if_sk.c197
1 files changed, 81 insertions, 116 deletions
diff --git a/sys/dev/sk/if_sk.c b/sys/dev/sk/if_sk.c
index bda9e6c..7e2e563 100644
--- a/sys/dev/sk/if_sk.c
+++ b/sys/dev/sk/if_sk.c
@@ -255,10 +255,10 @@ static int sk_marv_miibus_writereg(struct sk_if_softc *, int, int,
static void sk_marv_miibus_statchg(struct sk_if_softc *);
static uint32_t sk_xmchash(const uint8_t *);
-static uint32_t sk_gmchash(const uint8_t *);
static void sk_setfilt(struct sk_if_softc *, u_int16_t *, int);
-static void sk_setmulti(struct sk_if_softc *);
-static void sk_setpromisc(struct sk_if_softc *);
+static void sk_rxfilter(struct sk_if_softc *);
+static void sk_rxfilter_genesis(struct sk_if_softc *);
+static void sk_rxfilter_yukon(struct sk_if_softc *);
static int sysctl_int_range(SYSCTL_HANDLER_ARGS, int low, int high);
static int sysctl_hw_sk_int_mod(SYSCTL_HANDLER_ARGS);
@@ -697,19 +697,6 @@ sk_xmchash(addr)
return (~crc & ((1 << HASH_BITS) - 1));
}
-/* gmchash is just a big endian crc */
-static u_int32_t
-sk_gmchash(addr)
- const uint8_t *addr;
-{
- uint32_t crc;
-
- /* Compute CRC for the address value. */
- crc = ether_crc32_be(addr, ETHER_ADDR_LEN);
-
- return (crc & ((1 << HASH_BITS) - 1));
-}
-
static void
sk_setfilt(sc_if, addr, slot)
struct sk_if_softc *sc_if;
@@ -728,12 +715,26 @@ sk_setfilt(sc_if, addr, slot)
}
static void
-sk_setmulti(sc_if)
+sk_rxfilter(sc_if)
+ struct sk_if_softc *sc_if;
+{
+ struct sk_softc *sc;
+
+ SK_IF_LOCK_ASSERT(sc_if);
+
+ sc = sc_if->sk_softc;
+ if (sc->sk_type == SK_GENESIS)
+ sk_rxfilter_genesis(sc_if);
+ else
+ sk_rxfilter_yukon(sc_if);
+}
+
+static void
+sk_rxfilter_genesis(sc_if)
struct sk_if_softc *sc_if;
{
- struct sk_softc *sc = sc_if->sk_softc;
struct ifnet *ifp = sc_if->sk_ifp;
- u_int32_t hashes[2] = { 0, 0 };
+ u_int32_t hashes[2] = { 0, 0 }, mode;
int h = 0, i;
struct ifmultiaddr *ifma;
u_int16_t dummy[] = { 0, 0, 0 };
@@ -741,124 +742,96 @@ sk_setmulti(sc_if)
SK_IF_LOCK_ASSERT(sc_if);
- /* First, zot all the existing filters. */
- switch(sc->sk_type) {
- case SK_GENESIS:
- for (i = 1; i < XM_RXFILT_MAX; i++)
- sk_setfilt(sc_if, dummy, i);
-
- SK_XM_WRITE_4(sc_if, XM_MAR0, 0);
- SK_XM_WRITE_4(sc_if, XM_MAR2, 0);
- break;
- case SK_YUKON:
- case SK_YUKON_LITE:
- case SK_YUKON_LP:
- SK_YU_WRITE_2(sc_if, YUKON_MCAH1, 0);
- SK_YU_WRITE_2(sc_if, YUKON_MCAH2, 0);
- SK_YU_WRITE_2(sc_if, YUKON_MCAH3, 0);
- SK_YU_WRITE_2(sc_if, YUKON_MCAH4, 0);
- break;
- }
+ mode = SK_XM_READ_4(sc_if, XM_MODE);
+ mode &= ~(XM_MODE_RX_PROMISC | XM_MODE_RX_USE_HASH |
+ XM_MODE_RX_USE_PERFECT);
+ /* First, zot all the existing perfect filters. */
+ for (i = 1; i < XM_RXFILT_MAX; i++)
+ sk_setfilt(sc_if, dummy, i);
/* Now program new ones. */
if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ if (ifp->if_flags & IFF_ALLMULTI)
+ mode |= XM_MODE_RX_USE_HASH;
+ if (ifp->if_flags & IFF_PROMISC)
+ mode |= XM_MODE_RX_PROMISC;
hashes[0] = 0xFFFFFFFF;
hashes[1] = 0xFFFFFFFF;
} else {
i = 1;
IF_ADDR_LOCK(ifp);
- TAILQ_FOREACH_REVERSE(ifma, &ifp->if_multiaddrs, ifmultihead, ifma_link) {
+ TAILQ_FOREACH_REVERSE(ifma, &ifp->if_multiaddrs, ifmultihead,
+ ifma_link) {
if (ifma->ifma_addr->sa_family != AF_LINK)
continue;
/*
* Program the first XM_RXFILT_MAX multicast groups
- * into the perfect filter. For all others,
- * use the hash table.
+ * into the perfect filter.
*/
- if (sc->sk_type == SK_GENESIS && i < XM_RXFILT_MAX) {
- bcopy(LLADDR(
- (struct sockaddr_dl *)ifma->ifma_addr),
- maddr, ETHER_ADDR_LEN);
+ bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
+ maddr, ETHER_ADDR_LEN);
+ if (i < XM_RXFILT_MAX) {
sk_setfilt(sc_if, maddr, i);
+ mode |= XM_MODE_RX_USE_PERFECT;
i++;
continue;
}
-
- switch(sc->sk_type) {
- case SK_GENESIS:
- bcopy(LLADDR(
- (struct sockaddr_dl *)ifma->ifma_addr),
- maddr, ETHER_ADDR_LEN);
- h = sk_xmchash((const uint8_t *)maddr);
- break;
- case SK_YUKON:
- case SK_YUKON_LITE:
- case SK_YUKON_LP:
- bcopy(LLADDR(
- (struct sockaddr_dl *)ifma->ifma_addr),
- maddr, ETHER_ADDR_LEN);
- h = sk_gmchash((const uint8_t *)maddr);
- break;
- }
+ h = sk_xmchash((const uint8_t *)maddr);
if (h < 32)
hashes[0] |= (1 << h);
else
hashes[1] |= (1 << (h - 32));
+ mode |= XM_MODE_RX_USE_HASH;
}
IF_ADDR_UNLOCK(ifp);
}
- switch(sc->sk_type) {
- case SK_GENESIS:
- SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_RX_USE_HASH|
- XM_MODE_RX_USE_PERFECT);
- SK_XM_WRITE_4(sc_if, XM_MAR0, hashes[0]);
- SK_XM_WRITE_4(sc_if, XM_MAR2, hashes[1]);
- break;
- case SK_YUKON:
- case SK_YUKON_LITE:
- case SK_YUKON_LP:
- SK_YU_WRITE_2(sc_if, YUKON_MCAH1, hashes[0] & 0xffff);
- SK_YU_WRITE_2(sc_if, YUKON_MCAH2, (hashes[0] >> 16) & 0xffff);
- SK_YU_WRITE_2(sc_if, YUKON_MCAH3, hashes[1] & 0xffff);
- SK_YU_WRITE_2(sc_if, YUKON_MCAH4, (hashes[1] >> 16) & 0xffff);
- break;
- }
-
- return;
+ SK_XM_WRITE_4(sc_if, XM_MODE, mode);
+ SK_XM_WRITE_4(sc_if, XM_MAR0, hashes[0]);
+ SK_XM_WRITE_4(sc_if, XM_MAR2, hashes[1]);
}
static void
-sk_setpromisc(sc_if)
+sk_rxfilter_yukon(sc_if)
struct sk_if_softc *sc_if;
{
- struct sk_softc *sc = sc_if->sk_softc;
- struct ifnet *ifp = sc_if->sk_ifp;
+ struct ifnet *ifp;
+ u_int32_t crc, hashes[2] = { 0, 0 }, mode;
+ struct ifmultiaddr *ifma;
SK_IF_LOCK_ASSERT(sc_if);
- switch(sc->sk_type) {
- case SK_GENESIS:
- if (ifp->if_flags & IFF_PROMISC) {
- SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_RX_PROMISC);
- } else {
- SK_XM_CLRBIT_4(sc_if, XM_MODE, XM_MODE_RX_PROMISC);
- }
- break;
- case SK_YUKON:
- case SK_YUKON_LITE:
- case SK_YUKON_LP:
- if (ifp->if_flags & IFF_PROMISC) {
- SK_YU_CLRBIT_2(sc_if, YUKON_RCR,
- YU_RCR_UFLEN | YU_RCR_MUFLEN);
- } else {
- SK_YU_SETBIT_2(sc_if, YUKON_RCR,
- YU_RCR_UFLEN | YU_RCR_MUFLEN);
+ ifp = sc_if->sk_ifp;
+ mode = SK_YU_READ_2(sc_if, YUKON_RCR);
+ if (ifp->if_flags & IFF_PROMISC)
+ mode &= ~(YU_RCR_UFLEN | YU_RCR_MUFLEN);
+ else if (ifp->if_flags & IFF_ALLMULTI) {
+ mode |= YU_RCR_UFLEN | YU_RCR_MUFLEN;
+ hashes[0] = 0xFFFFFFFF;
+ hashes[1] = 0xFFFFFFFF;
+ } else {
+ mode |= YU_RCR_UFLEN;
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ crc = ether_crc32_be(LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr), ETHER_ADDR_LEN);
+ /* Just want the 6 least significant bits. */
+ crc &= 0x3f;
+ /* Set the corresponding bit in the hash table. */
+ hashes[crc >> 5] |= 1 << (crc & 0x1f);
}
- break;
+ IF_ADDR_UNLOCK(ifp);
+ if (hashes[0] != 0 || hashes[1] != 0)
+ mode |= YU_RCR_MUFLEN;
}
- return;
+ SK_YU_WRITE_2(sc_if, YUKON_MCAH1, hashes[0] & 0xffff);
+ SK_YU_WRITE_2(sc_if, YUKON_MCAH2, (hashes[0] >> 16) & 0xffff);
+ SK_YU_WRITE_2(sc_if, YUKON_MCAH3, hashes[1] & 0xffff);
+ SK_YU_WRITE_2(sc_if, YUKON_MCAH4, (hashes[1] >> 16) & 0xffff);
+ SK_YU_WRITE_2(sc_if, YUKON_RCR, mode);
}
static int
@@ -1166,10 +1139,8 @@ sk_ioctl(ifp, command, data)
if (ifp->if_flags & IFF_UP) {
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
if ((ifp->if_flags ^ sc_if->sk_if_flags)
- & IFF_PROMISC) {
- sk_setpromisc(sc_if);
- sk_setmulti(sc_if);
- }
+ & (IFF_PROMISC | IFF_ALLMULTI))
+ sk_rxfilter(sc_if);
} else
sk_init_locked(sc_if);
} else {
@@ -1183,7 +1154,7 @@ sk_ioctl(ifp, command, data)
case SIOCDELMULTI:
SK_IF_LOCK(sc_if);
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- sk_setmulti(sc_if);
+ sk_rxfilter(sc_if);
SK_IF_UNLOCK(sc_if);
break;
case SIOCGIFMEDIA:
@@ -3302,11 +3273,8 @@ sk_init_xmac(sc_if)
*/
SK_XM_WRITE_2(sc_if, XM_TX_REQTHRESH, SK_XM_TX_FIFOTHRESH);
- /* Set promiscuous mode */
- sk_setpromisc(sc_if);
-
- /* Set multicast filter */
- sk_setmulti(sc_if);
+ /* Set Rx filter */
+ sk_rxfilter_genesis(sc_if);
/* Clear and enable interrupts */
SK_XM_READ_2(sc_if, XM_ISR);
@@ -3447,11 +3415,8 @@ sk_init_yukon(sc_if)
SK_YU_WRITE_2(sc_if, YUKON_SAL2 + i * 4, reg);
}
- /* Set promiscuous mode */
- sk_setpromisc(sc_if);
-
- /* Set multicast filter */
- sk_setmulti(sc_if);
+ /* Set Rx filter */
+ sk_rxfilter_yukon(sc_if);
/* enable interrupt mask for counter overflows */
SK_YU_WRITE_2(sc_if, YUKON_TIMR, 0);
OpenPOWER on IntegriCloud