Commit 37018b5a authored by David S. Miller's avatar David S. Miller

Merge branch 'macvlan-broadcast-queue-bypass'

Herbert Xu says:

====================
macvlan: Allow some packets to bypass broadcast queue

This patch series allows some packets to bypass the broadcast
queue on receive.  Currently all multicast packets are queued
on receive and then processed in a work queue.  This is to avoid
an unbounded amount of work occurring in the receive path, as
one broadcast packet could easily translate into 4,000 packets.

However, for multicast packets with just one receiver (possible
for IPv6 ND), this introduces unnecessary latency as the packet
will go to exactly one device.

This series allows such multicast packets to be processed inline.
It also adds a toggle which lets the admin control what threshold
to set between queueing and not queueing.  A follow-up patch for
iproute will be posted.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 6fc5f5bc 954d1fa1
...@@ -47,9 +47,11 @@ struct macvlan_port { ...@@ -47,9 +47,11 @@ struct macvlan_port {
struct sk_buff_head bc_queue; struct sk_buff_head bc_queue;
struct work_struct bc_work; struct work_struct bc_work;
u32 bc_queue_len_used; u32 bc_queue_len_used;
int bc_cutoff;
u32 flags; u32 flags;
int count; int count;
struct hlist_head vlan_source_hash[MACVLAN_HASH_SIZE]; struct hlist_head vlan_source_hash[MACVLAN_HASH_SIZE];
DECLARE_BITMAP(bc_filter, MACVLAN_MC_FILTER_SZ);
DECLARE_BITMAP(mc_filter, MACVLAN_MC_FILTER_SZ); DECLARE_BITMAP(mc_filter, MACVLAN_MC_FILTER_SZ);
unsigned char perm_addr[ETH_ALEN]; unsigned char perm_addr[ETH_ALEN];
}; };
...@@ -291,6 +293,31 @@ static void macvlan_broadcast(struct sk_buff *skb, ...@@ -291,6 +293,31 @@ static void macvlan_broadcast(struct sk_buff *skb,
} }
} }
static void macvlan_multicast_rx(const struct macvlan_port *port,
const struct macvlan_dev *src,
struct sk_buff *skb)
{
if (!src)
/* frame comes from an external address */
macvlan_broadcast(skb, port, NULL,
MACVLAN_MODE_PRIVATE |
MACVLAN_MODE_VEPA |
MACVLAN_MODE_PASSTHRU|
MACVLAN_MODE_BRIDGE);
else if (src->mode == MACVLAN_MODE_VEPA)
/* flood to everyone except source */
macvlan_broadcast(skb, port, src->dev,
MACVLAN_MODE_VEPA |
MACVLAN_MODE_BRIDGE);
else
/*
* flood only to VEPA ports, bridge ports
* already saw the frame on the way out.
*/
macvlan_broadcast(skb, port, src->dev,
MACVLAN_MODE_VEPA);
}
static void macvlan_process_broadcast(struct work_struct *w) static void macvlan_process_broadcast(struct work_struct *w)
{ {
struct macvlan_port *port = container_of(w, struct macvlan_port, struct macvlan_port *port = container_of(w, struct macvlan_port,
...@@ -308,27 +335,7 @@ static void macvlan_process_broadcast(struct work_struct *w) ...@@ -308,27 +335,7 @@ static void macvlan_process_broadcast(struct work_struct *w)
const struct macvlan_dev *src = MACVLAN_SKB_CB(skb)->src; const struct macvlan_dev *src = MACVLAN_SKB_CB(skb)->src;
rcu_read_lock(); rcu_read_lock();
macvlan_multicast_rx(port, src, skb);
if (!src)
/* frame comes from an external address */
macvlan_broadcast(skb, port, NULL,
MACVLAN_MODE_PRIVATE |
MACVLAN_MODE_VEPA |
MACVLAN_MODE_PASSTHRU|
MACVLAN_MODE_BRIDGE);
else if (src->mode == MACVLAN_MODE_VEPA)
/* flood to everyone except source */
macvlan_broadcast(skb, port, src->dev,
MACVLAN_MODE_VEPA |
MACVLAN_MODE_BRIDGE);
else
/*
* flood only to VEPA ports, bridge ports
* already saw the frame on the way out.
*/
macvlan_broadcast(skb, port, src->dev,
MACVLAN_MODE_VEPA);
rcu_read_unlock(); rcu_read_unlock();
if (src) if (src)
...@@ -476,8 +483,10 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb) ...@@ -476,8 +483,10 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
} }
hash = mc_hash(NULL, eth->h_dest); hash = mc_hash(NULL, eth->h_dest);
if (test_bit(hash, port->mc_filter)) if (test_bit(hash, port->bc_filter))
macvlan_broadcast_enqueue(port, src, skb); macvlan_broadcast_enqueue(port, src, skb);
else if (test_bit(hash, port->mc_filter))
macvlan_multicast_rx(port, src, skb);
return RX_HANDLER_PASS; return RX_HANDLER_PASS;
} }
...@@ -780,30 +789,43 @@ static void macvlan_change_rx_flags(struct net_device *dev, int change) ...@@ -780,30 +789,43 @@ static void macvlan_change_rx_flags(struct net_device *dev, int change)
static void macvlan_compute_filter(unsigned long *mc_filter, static void macvlan_compute_filter(unsigned long *mc_filter,
struct net_device *dev, struct net_device *dev,
struct macvlan_dev *vlan) struct macvlan_dev *vlan, int cutoff)
{ {
if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) { if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
bitmap_fill(mc_filter, MACVLAN_MC_FILTER_SZ); if (cutoff >= 0)
bitmap_fill(mc_filter, MACVLAN_MC_FILTER_SZ);
else
bitmap_zero(mc_filter, MACVLAN_MC_FILTER_SZ);
} else { } else {
struct netdev_hw_addr *ha;
DECLARE_BITMAP(filter, MACVLAN_MC_FILTER_SZ); DECLARE_BITMAP(filter, MACVLAN_MC_FILTER_SZ);
struct netdev_hw_addr *ha;
bitmap_zero(filter, MACVLAN_MC_FILTER_SZ); bitmap_zero(filter, MACVLAN_MC_FILTER_SZ);
netdev_for_each_mc_addr(ha, dev) { netdev_for_each_mc_addr(ha, dev) {
if (cutoff >= 0 && ha->synced <= cutoff)
continue;
__set_bit(mc_hash(vlan, ha->addr), filter); __set_bit(mc_hash(vlan, ha->addr), filter);
} }
__set_bit(mc_hash(vlan, dev->broadcast), filter); if (cutoff >= 0)
__set_bit(mc_hash(vlan, dev->broadcast), filter);
bitmap_copy(mc_filter, filter, MACVLAN_MC_FILTER_SZ); bitmap_copy(mc_filter, filter, MACVLAN_MC_FILTER_SZ);
} }
} }
static void macvlan_recompute_bc_filter(struct macvlan_dev *vlan)
{
macvlan_compute_filter(vlan->port->bc_filter, vlan->lowerdev, NULL,
vlan->port->bc_cutoff);
}
static void macvlan_set_mac_lists(struct net_device *dev) static void macvlan_set_mac_lists(struct net_device *dev)
{ {
struct macvlan_dev *vlan = netdev_priv(dev); struct macvlan_dev *vlan = netdev_priv(dev);
macvlan_compute_filter(vlan->mc_filter, dev, vlan); macvlan_compute_filter(vlan->mc_filter, dev, vlan, 0);
dev_uc_sync(vlan->lowerdev, dev); dev_uc_sync(vlan->lowerdev, dev);
dev_mc_sync(vlan->lowerdev, dev); dev_mc_sync(vlan->lowerdev, dev);
...@@ -821,7 +843,18 @@ static void macvlan_set_mac_lists(struct net_device *dev) ...@@ -821,7 +843,18 @@ static void macvlan_set_mac_lists(struct net_device *dev)
* The solution is to maintain a list of broadcast addresses like * The solution is to maintain a list of broadcast addresses like
* we do for uc/mc, if you care. * we do for uc/mc, if you care.
*/ */
macvlan_compute_filter(vlan->port->mc_filter, vlan->lowerdev, NULL); macvlan_compute_filter(vlan->port->mc_filter, vlan->lowerdev, NULL,
0);
macvlan_recompute_bc_filter(vlan);
}
static void update_port_bc_cutoff(struct macvlan_dev *vlan, int cutoff)
{
if (vlan->port->bc_cutoff == cutoff)
return;
vlan->port->bc_cutoff = cutoff;
macvlan_recompute_bc_filter(vlan);
} }
static int macvlan_change_mtu(struct net_device *dev, int new_mtu) static int macvlan_change_mtu(struct net_device *dev, int new_mtu)
...@@ -1236,6 +1269,7 @@ static int macvlan_port_create(struct net_device *dev) ...@@ -1236,6 +1269,7 @@ static int macvlan_port_create(struct net_device *dev)
INIT_HLIST_HEAD(&port->vlan_source_hash[i]); INIT_HLIST_HEAD(&port->vlan_source_hash[i]);
port->bc_queue_len_used = 0; port->bc_queue_len_used = 0;
port->bc_cutoff = 1;
skb_queue_head_init(&port->bc_queue); skb_queue_head_init(&port->bc_queue);
INIT_WORK(&port->bc_work, macvlan_process_broadcast); INIT_WORK(&port->bc_work, macvlan_process_broadcast);
...@@ -1509,6 +1543,10 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev, ...@@ -1509,6 +1543,10 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
if (data && data[IFLA_MACVLAN_BC_QUEUE_LEN]) if (data && data[IFLA_MACVLAN_BC_QUEUE_LEN])
vlan->bc_queue_len_req = nla_get_u32(data[IFLA_MACVLAN_BC_QUEUE_LEN]); vlan->bc_queue_len_req = nla_get_u32(data[IFLA_MACVLAN_BC_QUEUE_LEN]);
if (data && data[IFLA_MACVLAN_BC_CUTOFF])
update_port_bc_cutoff(
vlan, nla_get_s32(data[IFLA_MACVLAN_BC_CUTOFF]));
err = register_netdevice(dev); err = register_netdevice(dev);
if (err < 0) if (err < 0)
goto destroy_macvlan_port; goto destroy_macvlan_port;
...@@ -1605,6 +1643,10 @@ static int macvlan_changelink(struct net_device *dev, ...@@ -1605,6 +1643,10 @@ static int macvlan_changelink(struct net_device *dev,
update_port_bc_queue_len(vlan->port); update_port_bc_queue_len(vlan->port);
} }
if (data && data[IFLA_MACVLAN_BC_CUTOFF])
update_port_bc_cutoff(
vlan, nla_get_s32(data[IFLA_MACVLAN_BC_CUTOFF]));
if (set_mode) if (set_mode)
vlan->mode = mode; vlan->mode = mode;
if (data && data[IFLA_MACVLAN_MACADDR_MODE]) { if (data && data[IFLA_MACVLAN_MACADDR_MODE]) {
...@@ -1685,6 +1727,9 @@ static int macvlan_fill_info(struct sk_buff *skb, ...@@ -1685,6 +1727,9 @@ static int macvlan_fill_info(struct sk_buff *skb,
goto nla_put_failure; goto nla_put_failure;
if (nla_put_u32(skb, IFLA_MACVLAN_BC_QUEUE_LEN_USED, port->bc_queue_len_used)) if (nla_put_u32(skb, IFLA_MACVLAN_BC_QUEUE_LEN_USED, port->bc_queue_len_used))
goto nla_put_failure; goto nla_put_failure;
if (port->bc_cutoff != 1 &&
nla_put_s32(skb, IFLA_MACVLAN_BC_CUTOFF, port->bc_cutoff))
goto nla_put_failure;
return 0; return 0;
nla_put_failure: nla_put_failure:
......
...@@ -635,6 +635,7 @@ enum { ...@@ -635,6 +635,7 @@ enum {
IFLA_MACVLAN_MACADDR_COUNT, IFLA_MACVLAN_MACADDR_COUNT,
IFLA_MACVLAN_BC_QUEUE_LEN, IFLA_MACVLAN_BC_QUEUE_LEN,
IFLA_MACVLAN_BC_QUEUE_LEN_USED, IFLA_MACVLAN_BC_QUEUE_LEN_USED,
IFLA_MACVLAN_BC_CUTOFF,
__IFLA_MACVLAN_MAX, __IFLA_MACVLAN_MAX,
}; };
......
...@@ -605,6 +605,7 @@ enum { ...@@ -605,6 +605,7 @@ enum {
IFLA_MACVLAN_MACADDR_COUNT, IFLA_MACVLAN_MACADDR_COUNT,
IFLA_MACVLAN_BC_QUEUE_LEN, IFLA_MACVLAN_BC_QUEUE_LEN,
IFLA_MACVLAN_BC_QUEUE_LEN_USED, IFLA_MACVLAN_BC_QUEUE_LEN_USED,
IFLA_MACVLAN_BC_CUTOFF,
__IFLA_MACVLAN_MAX, __IFLA_MACVLAN_MAX,
}; };
......
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