Skip to content

Commit cd431e7

Browse files
edumazetdavem330
authored andcommitted
macvlan: add multicast filter
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 <[email protected]> Cc: Ben Greear <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 6d1ccff commit cd431e7

File tree

2 files changed

+29
-0
lines changed

2 files changed

+29
-0
lines changed

drivers/net/macvlan.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <linux/if_vlan.h>
3030
#include <linux/if_link.h>
3131
#include <linux/if_macvlan.h>
32+
#include <linux/hash.h>
3233
#include <net/rtnetlink.h>
3334
#include <net/xfrm.h>
3435

@@ -126,6 +127,13 @@ static int macvlan_broadcast_one(struct sk_buff *skb,
126127
return vlan->receive(skb);
127128
}
128129

130+
static unsigned int mc_hash(const unsigned char *addr)
131+
{
132+
u32 val = __get_unaligned_cpu32(addr + 2);
133+
134+
return hash_32(val, MACVLAN_MC_FILTER_BITS);
135+
}
136+
129137
static void macvlan_broadcast(struct sk_buff *skb,
130138
const struct macvlan_port *port,
131139
struct net_device *src,
@@ -137,6 +145,7 @@ static void macvlan_broadcast(struct sk_buff *skb,
137145
struct sk_buff *nskb;
138146
unsigned int i;
139147
int err;
148+
unsigned int hash = mc_hash(eth->h_dest);
140149

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

158+
if (!test_bit(hash, vlan->mc_filter))
159+
continue;
149160
nskb = skb_clone(skb, GFP_ATOMIC);
150161
err = macvlan_broadcast_one(nskb, vlan, eth,
151162
mode == MACVLAN_MODE_BRIDGE);
@@ -405,6 +416,18 @@ static void macvlan_set_mac_lists(struct net_device *dev)
405416
{
406417
struct macvlan_dev *vlan = netdev_priv(dev);
407418

419+
if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
420+
bitmap_fill(vlan->mc_filter, MACVLAN_MC_FILTER_SZ);
421+
} else {
422+
struct netdev_hw_addr *ha;
423+
DECLARE_BITMAP(filter, MACVLAN_MC_FILTER_SZ);
424+
425+
bitmap_zero(filter, MACVLAN_MC_FILTER_SZ);
426+
netdev_for_each_mc_addr(ha, dev) {
427+
__set_bit(mc_hash(ha->addr), filter);
428+
}
429+
bitmap_copy(vlan->mc_filter, filter, MACVLAN_MC_FILTER_SZ);
430+
}
408431
dev_uc_sync(vlan->lowerdev, dev);
409432
dev_mc_sync(vlan->lowerdev, dev);
410433
}

include/linux/if_macvlan.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,19 @@ struct macvlan_pcpu_stats {
5252
*/
5353
#define MAX_MACVTAP_QUEUES (NR_CPUS < 16 ? NR_CPUS : 16)
5454

55+
#define MACVLAN_MC_FILTER_BITS 8
56+
#define MACVLAN_MC_FILTER_SZ (1 << MACVLAN_MC_FILTER_BITS)
57+
5558
struct macvlan_dev {
5659
struct net_device *dev;
5760
struct list_head list;
5861
struct hlist_node hlist;
5962
struct macvlan_port *port;
6063
struct net_device *lowerdev;
6164
struct macvlan_pcpu_stats __percpu *pcpu_stats;
65+
66+
DECLARE_BITMAP(mc_filter, MACVLAN_MC_FILTER_SZ);
67+
6268
enum macvlan_mode mode;
6369
u16 flags;
6470
int (*receive)(struct sk_buff *skb);

0 commit comments

Comments
 (0)