diff options
author | Ben Hutchings <ben@decadent.org.uk> | 2010-10-14 13:41:53 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-10-19 09:40:03 -0400 |
commit | e2269308359d5863b6aa1fcb95a425a2ab255f1f (patch) | |
tree | effd1a03896c16febad96758fea6230eaa2e7064 /drivers/net/r6040.c | |
parent | 9756403b16cfeda85efe77e62832a2f6b5752fdf (diff) |
r6040: Fix multicast filter some more
This code has been broken forever, but in several different and
creative ways.
So far as I can work out, the R6040 MAC filter has 4 exact-match
entries, the first of which the driver uses for its assigned unicast
address, plus a 64-entry hash-based filter for multicast addresses
(maybe unicast as well?).
The original version of this code would write the first 4 multicast
addresses as exact-match entries from offset 1 (bug #1: there is no
entry 4 so this could write to some PHY registers). It would fill the
remainder of the exact-match entries with the broadcast address (bug #2:
this would overwrite the last used entry). If more than 4 multicast
addresses were configured, it would set up the hash table, write some
random crap to the MAC control register (bug #3) and finally walk off
the end of the list when filling the exact-match entries (bug #4).
All of this seems to be pointless, since it sets the promiscuous bit
when the interface is made promiscuous or if >4 multicast addresses
are enabled, and never clears it (bug #5, masking bug #2).
The recent(ish) changes to the multicast list fixed bug #4, but
completely removed the limit on iteration over the exact-match entries
(bug #6).
Bug #4 was reported as
<https://bugzilla.kernel.org/show_bug.cgi?id=15355> and more recently
as <http://bugs.debian.org/600155>. Florian Fainelli attempted to fix
these in commit 3bcf8229a8c49769e48d3e0bd1e20d8e003f8106, but that
actually dealt with bugs #1-3, bug #4 having been fixed in mainline at
that point.
That commit fixes the most important current bug #6.
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Cc: stable@kernel.org [2.6.35 only]
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/r6040.c')
-rw-r--r-- | drivers/net/r6040.c | 22 |
1 files changed, 12 insertions, 10 deletions
diff --git a/drivers/net/r6040.c b/drivers/net/r6040.c index 142c381e1d73..80666f097ce6 100644 --- a/drivers/net/r6040.c +++ b/drivers/net/r6040.c | |||
@@ -893,16 +893,18 @@ static void r6040_multicast_list(struct net_device *dev) | |||
893 | /* Multicast Address 1~4 case */ | 893 | /* Multicast Address 1~4 case */ |
894 | i = 0; | 894 | i = 0; |
895 | netdev_for_each_mc_addr(ha, dev) { | 895 | netdev_for_each_mc_addr(ha, dev) { |
896 | if (i < MCAST_MAX) { | 896 | if (i >= MCAST_MAX) |
897 | adrp = (u16 *) ha->addr; | 897 | break; |
898 | iowrite16(adrp[0], ioaddr + MID_1L + 8 * i); | 898 | adrp = (u16 *) ha->addr; |
899 | iowrite16(adrp[1], ioaddr + MID_1M + 8 * i); | 899 | iowrite16(adrp[0], ioaddr + MID_1L + 8 * i); |
900 | iowrite16(adrp[2], ioaddr + MID_1H + 8 * i); | 900 | iowrite16(adrp[1], ioaddr + MID_1M + 8 * i); |
901 | } else { | 901 | iowrite16(adrp[2], ioaddr + MID_1H + 8 * i); |
902 | iowrite16(0xffff, ioaddr + MID_1L + 8 * i); | 902 | i++; |
903 | iowrite16(0xffff, ioaddr + MID_1M + 8 * i); | 903 | } |
904 | iowrite16(0xffff, ioaddr + MID_1H + 8 * i); | 904 | while (i < MCAST_MAX) { |
905 | } | 905 | iowrite16(0xffff, ioaddr + MID_1L + 8 * i); |
906 | iowrite16(0xffff, ioaddr + MID_1M + 8 * i); | ||
907 | iowrite16(0xffff, ioaddr + MID_1H + 8 * i); | ||
906 | i++; | 908 | i++; |
907 | } | 909 | } |
908 | } | 910 | } |