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: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2016-06-01 17:48:46 -07:00
commit 250ba7480f

View File

@ -49,6 +49,7 @@ struct macvlan_port {
bool passthru;
int count;
struct hlist_head vlan_source_hash[MACVLAN_HASH_SIZE];
DECLARE_BITMAP(mc_filter, MACVLAN_MC_FILTER_SZ);
};
struct macvlan_source_entry {
@ -305,11 +306,14 @@ static void macvlan_process_broadcast(struct work_struct *w)
rcu_read_unlock();
if (src)
dev_put(src->dev);
kfree_skb(skb);
}
}
static void macvlan_broadcast_enqueue(struct macvlan_port *port,
const struct macvlan_dev *src,
struct sk_buff *skb)
{
struct sk_buff *nskb;
@ -319,8 +323,12 @@ static void macvlan_broadcast_enqueue(struct macvlan_port *port,
if (!nskb)
goto err;
MACVLAN_SKB_CB(nskb)->src = src;
spin_lock(&port->bc_queue.lock);
if (skb_queue_len(&port->bc_queue) < MACVLAN_BC_QUEUE_LEN) {
if (src)
dev_hold(src->dev);
__skb_queue_tail(&port->bc_queue, nskb);
err = 0;
}
@ -412,6 +420,8 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
port = macvlan_port_get_rcu(skb->dev);
if (is_multicast_ether_addr(eth->h_dest)) {
unsigned int hash;
skb = ip_check_defrag(dev_net(skb->dev), skb, IP_DEFRAG_MACVLAN);
if (!skb)
return RX_HANDLER_CONSUMED;
@ -429,8 +439,9 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
goto out;
}
MACVLAN_SKB_CB(skb)->src = src;
macvlan_broadcast_enqueue(port, skb);
hash = mc_hash(NULL, eth->h_dest);
if (test_bit(hash, port->mc_filter))
macvlan_broadcast_enqueue(port, src, skb);
return RX_HANDLER_PASS;
}
@ -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)) {
bitmap_fill(vlan->mc_filter, MACVLAN_MC_FILTER_SZ);
bitmap_fill(mc_filter, MACVLAN_MC_FILTER_SZ);
} else {
struct netdev_hw_addr *ha;
DECLARE_BITMAP(filter, MACVLAN_MC_FILTER_SZ);
@ -733,10 +744,33 @@ static void macvlan_set_mac_lists(struct net_device *dev)
__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_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)