Skip to content

Commit

Permalink
macvlan: add multicast filter
Browse files Browse the repository at this point in the history
Setting up IPv6 addresses on configurations with many macvlans
is not really working, as many multicast messages are dropped.

Add a multicast filter to macvlan to reduce the amount of cloned
skbs and overhead.

Successfully tested with 1024 macvlans on one ethernet device.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Ben Greear <greearb@candelatech.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Eric Dumazet authored and David S. Miller committed Feb 6, 2013
1 parent 6d1ccff commit cd431e7
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 0 deletions.
23 changes: 23 additions & 0 deletions drivers/net/macvlan.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <linux/if_vlan.h>
#include <linux/if_link.h>
#include <linux/if_macvlan.h>
#include <linux/hash.h>
#include <net/rtnetlink.h>
#include <net/xfrm.h>

Expand Down Expand Up @@ -126,6 +127,13 @@ static int macvlan_broadcast_one(struct sk_buff *skb,
return vlan->receive(skb);
}

static unsigned int mc_hash(const unsigned char *addr)
{
u32 val = __get_unaligned_cpu32(addr + 2);

return hash_32(val, MACVLAN_MC_FILTER_BITS);
}

static void macvlan_broadcast(struct sk_buff *skb,
const struct macvlan_port *port,
struct net_device *src,
Expand All @@ -137,6 +145,7 @@ static void macvlan_broadcast(struct sk_buff *skb,
struct sk_buff *nskb;
unsigned int i;
int err;
unsigned int hash = mc_hash(eth->h_dest);

if (skb->protocol == htons(ETH_P_PAUSE))
return;
Expand All @@ -146,6 +155,8 @@ static void macvlan_broadcast(struct sk_buff *skb,
if (vlan->dev == src || !(vlan->mode & mode))
continue;

if (!test_bit(hash, vlan->mc_filter))
continue;
nskb = skb_clone(skb, GFP_ATOMIC);
err = macvlan_broadcast_one(nskb, vlan, eth,
mode == MACVLAN_MODE_BRIDGE);
Expand Down Expand Up @@ -405,6 +416,18 @@ static void macvlan_set_mac_lists(struct net_device *dev)
{
struct macvlan_dev *vlan = netdev_priv(dev);

if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
bitmap_fill(vlan->mc_filter, MACVLAN_MC_FILTER_SZ);
} else {
struct netdev_hw_addr *ha;
DECLARE_BITMAP(filter, MACVLAN_MC_FILTER_SZ);

bitmap_zero(filter, MACVLAN_MC_FILTER_SZ);
netdev_for_each_mc_addr(ha, dev) {
__set_bit(mc_hash(ha->addr), filter);
}
bitmap_copy(vlan->mc_filter, filter, MACVLAN_MC_FILTER_SZ);
}
dev_uc_sync(vlan->lowerdev, dev);
dev_mc_sync(vlan->lowerdev, dev);
}
Expand Down
6 changes: 6 additions & 0 deletions include/linux/if_macvlan.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,19 @@ struct macvlan_pcpu_stats {
*/
#define MAX_MACVTAP_QUEUES (NR_CPUS < 16 ? NR_CPUS : 16)

#define MACVLAN_MC_FILTER_BITS 8
#define MACVLAN_MC_FILTER_SZ (1 << MACVLAN_MC_FILTER_BITS)

struct macvlan_dev {
struct net_device *dev;
struct list_head list;
struct hlist_node hlist;
struct macvlan_port *port;
struct net_device *lowerdev;
struct macvlan_pcpu_stats __percpu *pcpu_stats;

DECLARE_BITMAP(mc_filter, MACVLAN_MC_FILTER_SZ);

enum macvlan_mode mode;
u16 flags;
int (*receive)(struct sk_buff *skb);
Expand Down

0 comments on commit cd431e7

Please sign in to comment.