mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-28 23:24:50 +00:00
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:
commit
250ba7480f
1 changed files with 41 additions and 7 deletions
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue