From nobody@FreeBSD.org  Mon Apr 27 21:02:59 2009
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id 825E1106568E
	for <freebsd-gnats-submit@FreeBSD.org>; Mon, 27 Apr 2009 21:02:59 +0000 (UTC)
	(envelope-from nobody@FreeBSD.org)
Received: from www.freebsd.org (www.freebsd.org [IPv6:2001:4f8:fff6::21])
	by mx1.freebsd.org (Postfix) with ESMTP id 70BCB8FC28
	for <freebsd-gnats-submit@FreeBSD.org>; Mon, 27 Apr 2009 21:02:59 +0000 (UTC)
	(envelope-from nobody@FreeBSD.org)
Received: from www.freebsd.org (localhost [127.0.0.1])
	by www.freebsd.org (8.14.3/8.14.3) with ESMTP id n3RL2wCF018681
	for <freebsd-gnats-submit@FreeBSD.org>; Mon, 27 Apr 2009 21:02:58 GMT
	(envelope-from nobody@www.freebsd.org)
Received: (from nobody@localhost)
	by www.freebsd.org (8.14.3/8.14.3/Submit) id n3RL2wTS018680;
	Mon, 27 Apr 2009 21:02:58 GMT
	(envelope-from nobody)
Message-Id: <200904272102.n3RL2wTS018680@www.freebsd.org>
Date: Mon, 27 Apr 2009 21:02:58 GMT
From: Scott Ullrich <sullrich@gmail.com>
To: freebsd-gnats-submit@FreeBSD.org
Subject: SK driver does not honor carp advskew unless PROMISC mode is enabled
X-Send-Pr-Version: www-3.1
X-GNATS-Notify:

>Number:         134051
>Category:       kern
>Synopsis:       [sk] SK driver does not honor carp advskew unless PROMISC mode is enabled
>Confidential:   no
>Severity:       serious
>Priority:       low
>Responsible:    yongari
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon Apr 27 21:10:03 UTC 2009
>Closed-Date:    Wed May 20 02:44:06 UTC 2009
>Last-Modified:  Wed May 20 02:44:06 UTC 2009
>Originator:     Scott Ullrich
>Release:        8-CURRENT
>Organization:
pfSense
>Environment:
FreeBSD gate1.geekgod.com 8.0-CURRENT FreeBSD 8.0-CURRENT #0: Fri Apr 24 13:41:50 EDT 2009     sullrich@FreeBSD_8_Builder.pfsense.org:/usr/obj.pfSense/usr/pfSensesrc/src/sys/pfSense_SMP.8  i386
>Description:
CARP does not work correctly with the SK driver unless PROMISC mode is enabled.  This problem is present on FreeBSD6-FreeBSD8.  It seems the multicast filter is not being setup correctly?

skc0: [ITHREAD]
skc1: <Marvell Gigabit Ethernet> port 0xd100-0xd1ff mem 0xd0420000-0xd0423fff irq 19 
at device 5.0 on pci5
skc1: Marvell Yukon Lite Gigabit Ethernet rev. (0x9)
sk1: <Marvell Semiconductor, Inc. Yukon> on skc1sk1: Ethernet address: 00:10:f3:0b:49:23
miibus5: <MII bus> on sk1
e1000phy5: <Marvell 88E1011 Gigabit PHY> PHY 0 on miibus5
e1000phy5:  10baseT, 10baseT-FDX, 100baseTX, 100baseTX-FDX, 1000baseTX-FDX, auto
skc1: [ITHREAD]
skc2: <Marvell Gigabit Ethernet> port 0xd200-0xd2ff mem 0xd0424000-0xd0427fff irq 18 
at device 6.0 on pci5
skc2: Marvell Yukon Lite Gigabit Ethernet rev. (0x9)
sk2: <Marvell Semiconductor, Inc. Yukon> on skc2sk2: Ethernet address: 00:10:f3:0b:49:24
miibus6: <MII bus> on sk2

>How-To-Repeat:
Install 2 FreeBSD machines.  One with SK nics and one without.

One the machine with SK NICS:

ifconfig carp0 create
ifconfig carp0 192.168.1.1/24
ifconfig carp0 advskew 25

On the second machine:

ifconfig carp0 create
ifconfig carp0 192.168.1.1/24
ifconfig carp0 advskew 254

Notice the machine with the lower advskew is BACKUP.

Now issue the command on the machine with SK nics:

ifconfig sk0 PROMISC

carp0 should go from BACKUP to MASTER after enabling PROMISC.

>Fix:
ifconfig carp0 PROMISC


>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->yongari 
Responsible-Changed-By: yongari 
Responsible-Changed-When: Thu Apr 30 04:24:15 UTC 2009 
Responsible-Changed-Why:  
Grab. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=134051 
State-Changed-From-To: open->feedback 
State-Changed-By: yongari 
State-Changed-When: Thu Apr 30 04:25:13 UTC 2009 
State-Changed-Why:  
It looks that multicast handling in Marvell Yukon controller is 
broken. Would you try the patch at the following URL? 
http://people.freebsd.org/~yongari/sk/sk.rxfilter.patch 

I recall I have old Yukon controller somewhere in my room but 
I can't find it at this moment, so the patch was not tested 
at all. Be careful! 

http://www.freebsd.org/cgi/query-pr.cgi?pr=134051 

From: Scott Ullrich <sullrich@gmail.com>
To: bug-followup@freebsd.org, sullrich@gmail.com
Cc:  
Subject: Re: kern/134051: [sk] SK driver does not honor carp advskew unless 
	PROMISC mode is enabled
Date: Thu, 30 Apr 2009 14:11:12 -0400

 Hi!  Thanks for the patch.  It appears to have worked!
 
 # ifconfig carp0
 carp0: flags=49<UP,LOOPBACK,RUNNING> metric 0 mtu 1500
 	inet 10.0.250.1 netmask 0xffffff00
 	carp: MASTER vhid 254 advbase 1 advskew 25
 # ifconfig carp0
 carp0: flags=49<UP,LOOPBACK,RUNNING> metric 0 mtu 1500
 	inet 10.0.250.1 netmask 0xffffff00
 	carp: BACKUP vhid 254 advbase 1 advskew 25
 #
 
 Thanks much!  I'll keep testing but for the most part it has resolved
 the multicast filter CARP issues.

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/134051: commit references a PR
Date: Fri,  1 May 2009 03:24:13 +0000 (UTC)

 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);
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
State-Changed-From-To: feedback->patched 
State-Changed-By: yongari 
State-Changed-When: Fri May 1 03:37:19 UTC 2009 
State-Changed-Why:  
Thanks a lot for quick reply. Patch committed to HEAD(r191716). 

http://www.freebsd.org/cgi/query-pr.cgi?pr=134051 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/134051: commit references a PR
Date: Wed, 20 May 2009 02:24:20 +0000 (UTC)

 Author: yongari
 Date: Wed May 20 02:24:09 2009
 New Revision: 192414
 URL: http://svn.freebsd.org/changeset/base/192414
 
 Log:
   MFC r191716:
     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:
   stable/7/sys/   (props changed)
   stable/7/sys/contrib/pf/   (props changed)
   stable/7/sys/dev/ath/ath_hal/   (props changed)
   stable/7/sys/dev/cxgb/   (props changed)
   stable/7/sys/dev/sk/if_sk.c
 
 Modified: stable/7/sys/dev/sk/if_sk.c
 ==============================================================================
 --- stable/7/sys/dev/sk/if_sk.c	Wed May 20 02:18:26 2009	(r192413)
 +++ stable/7/sys/dev/sk/if_sk.c	Wed May 20 02:24:09 2009	(r192414)
 @@ -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);
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
State-Changed-From-To: patched->closed 
State-Changed-By: yongari 
State-Changed-When: Wed May 20 02:43:27 UTC 2009 
State-Changed-Why:  
MFC done. Thanks for testing/reporting. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=134051 
>Unformatted:
