Commit 250ba748 authored by David S. Miller's avatar David S. Miller

Merge branch 'macvlan-less-mcast-cloning'

Herbert Xu says:

====================
macvlan: Avoid unnecessary multicast cloning

This patch tries to improve macvlan multicast performance by
maintaining a filter hash at the macvlan_port level so that we
can quickly determine whether a given packet is needed or not.

It is preceded by a patch that fixes a potential use-after-free
bug that I discovered while looking over this.

v2 fixed a bug where promiscuous/allmulti settings weren't handled
correctly.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 595d0b29 9c127a01
...@@ -49,6 +49,7 @@ struct macvlan_port { ...@@ -49,6 +49,7 @@ struct macvlan_port {
bool passthru; bool passthru;
int count; int count;
struct hlist_head vlan_source_hash[MACVLAN_HASH_SIZE]; struct hlist_head vlan_source_hash[MACVLAN_HASH_SIZE];
DECLARE_BITMAP(mc_filter, MACVLAN_MC_FILTER_SZ);
}; };
struct macvlan_source_entry { struct macvlan_source_entry {
...@@ -305,11 +306,14 @@ static void macvlan_process_broadcast(struct work_struct *w) ...@@ -305,11 +306,14 @@ static void macvlan_process_broadcast(struct work_struct *w)
rcu_read_unlock(); rcu_read_unlock();
if (src)
dev_put(src->dev);
kfree_skb(skb); kfree_skb(skb);
} }
} }
static void macvlan_broadcast_enqueue(struct macvlan_port *port, static void macvlan_broadcast_enqueue(struct macvlan_port *port,
const struct macvlan_dev *src,
struct sk_buff *skb) struct sk_buff *skb)
{ {
struct sk_buff *nskb; struct sk_buff *nskb;
...@@ -319,8 +323,12 @@ static void macvlan_broadcast_enqueue(struct macvlan_port *port, ...@@ -319,8 +323,12 @@ static void macvlan_broadcast_enqueue(struct macvlan_port *port,
if (!nskb) if (!nskb)
goto err; goto err;
MACVLAN_SKB_CB(nskb)->src = src;
spin_lock(&port->bc_queue.lock); spin_lock(&port->bc_queue.lock);
if (skb_queue_len(&port->bc_queue) < MACVLAN_BC_QUEUE_LEN) { if (skb_queue_len(&port->bc_queue) < MACVLAN_BC_QUEUE_LEN) {
if (src)
dev_hold(src->dev);
__skb_queue_tail(&port->bc_queue, nskb); __skb_queue_tail(&port->bc_queue, nskb);
err = 0; err = 0;
} }
...@@ -412,6 +420,8 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb) ...@@ -412,6 +420,8 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
port = macvlan_port_get_rcu(skb->dev); port = macvlan_port_get_rcu(skb->dev);
if (is_multicast_ether_addr(eth->h_dest)) { if (is_multicast_ether_addr(eth->h_dest)) {
unsigned int hash;
skb = ip_check_defrag(dev_net(skb->dev), skb, IP_DEFRAG_MACVLAN); skb = ip_check_defrag(dev_net(skb->dev), skb, IP_DEFRAG_MACVLAN);
if (!skb) if (!skb)
return RX_HANDLER_CONSUMED; return RX_HANDLER_CONSUMED;
...@@ -429,8 +439,9 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb) ...@@ -429,8 +439,9 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
goto out; goto out;
} }
MACVLAN_SKB_CB(skb)->src = src; hash = mc_hash(NULL, eth->h_dest);
macvlan_broadcast_enqueue(port, skb); if (test_bit(hash, port->mc_filter))
macvlan_broadcast_enqueue(port, src, skb);
return RX_HANDLER_PASS; return RX_HANDLER_PASS;
} }
...@@ -716,12 +727,12 @@ static void macvlan_change_rx_flags(struct net_device *dev, int change) ...@@ -716,12 +727,12 @@ static void macvlan_change_rx_flags(struct net_device *dev, int change)
} }
} }
static void macvlan_set_mac_lists(struct net_device *dev) static void macvlan_compute_filter(unsigned long *mc_filter,
struct net_device *dev,
struct macvlan_dev *vlan)
{ {
struct macvlan_dev *vlan = netdev_priv(dev);
if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) { if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
bitmap_fill(vlan->mc_filter, MACVLAN_MC_FILTER_SZ); bitmap_fill(mc_filter, MACVLAN_MC_FILTER_SZ);
} else { } else {
struct netdev_hw_addr *ha; struct netdev_hw_addr *ha;
DECLARE_BITMAP(filter, MACVLAN_MC_FILTER_SZ); DECLARE_BITMAP(filter, MACVLAN_MC_FILTER_SZ);
...@@ -733,10 +744,33 @@ static void macvlan_set_mac_lists(struct net_device *dev) ...@@ -733,10 +744,33 @@ static void macvlan_set_mac_lists(struct net_device *dev)
__set_bit(mc_hash(vlan, dev->broadcast), filter); __set_bit(mc_hash(vlan, dev->broadcast), filter);
bitmap_copy(vlan->mc_filter, filter, MACVLAN_MC_FILTER_SZ); bitmap_copy(mc_filter, filter, MACVLAN_MC_FILTER_SZ);
} }
}
static void macvlan_set_mac_lists(struct net_device *dev)
{
struct macvlan_dev *vlan = netdev_priv(dev);
macvlan_compute_filter(vlan->mc_filter, dev, vlan);
dev_uc_sync(vlan->lowerdev, dev); dev_uc_sync(vlan->lowerdev, dev);
dev_mc_sync(vlan->lowerdev, dev); dev_mc_sync(vlan->lowerdev, dev);
/* This is slightly inaccurate as we're including the subscription
* list of vlan->lowerdev too.
*
* Bug alert: This only works if everyone has the same broadcast
* address as lowerdev. As soon as someone changes theirs this
* will break.
*
* However, this is already broken as when you change your broadcast
* address we don't get called.
*
* The solution is to maintain a list of broadcast addresses like
* we do for uc/mc, if you care.
*/
macvlan_compute_filter(vlan->port->mc_filter, vlan->lowerdev, NULL);
} }
static int macvlan_change_mtu(struct net_device *dev, int new_mtu) static int macvlan_change_mtu(struct net_device *dev, int new_mtu)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment