Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 1 May 2009 03:24:03 +0000 (UTC)
From:      Pyun YongHyeon <yongari@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r191716 - head/sys/dev/sk
Message-ID:  <200905010324.n413O3o0030668@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: yongari
Date: Fri May  1 03:24:03 2009
New Revision: 191716
URL: http://svn.freebsd.org/changeset/base/191716

Log:
  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

Modified:
  head/sys/dev/sk/if_sk.c

Modified: head/sys/dev/sk/if_sk.c
==============================================================================
--- head/sys/dev/sk/if_sk.c	Fri May  1 02:51:58 2009	(r191715)
+++ head/sys/dev/sk/if_sk.c	Fri May  1 03:24:03 2009	(r191716)
@@ -255,10 +255,10 @@ static int sk_marv_miibus_writereg(struc
 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);



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