Skip to content

Commit

Permalink
Merge branch 'macvlan-broadcast-queue-bypass'
Browse files Browse the repository at this point in the history
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: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Mar 29, 2023
2 parents 6fc5f5b + 954d1fa commit 37018b5
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 28 deletions.
101 changes: 73 additions & 28 deletions drivers/net/macvlan.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,11 @@ struct macvlan_port {
struct sk_buff_head bc_queue;
struct work_struct bc_work;
u32 bc_queue_len_used;
int bc_cutoff;
u32 flags;
int count;
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);
unsigned char perm_addr[ETH_ALEN];
};
Expand Down Expand Up @@ -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)
{
struct macvlan_port *port = container_of(w, struct macvlan_port,
Expand All @@ -308,27 +335,7 @@ static void macvlan_process_broadcast(struct work_struct *w)
const struct macvlan_dev *src = MACVLAN_SKB_CB(skb)->src;

rcu_read_lock();

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);

macvlan_multicast_rx(port, src, skb);
rcu_read_unlock();

if (src)
Expand Down Expand Up @@ -476,8 +483,10 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
}

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);
else if (test_bit(hash, port->mc_filter))
macvlan_multicast_rx(port, src, skb);

return RX_HANDLER_PASS;
}
Expand Down Expand Up @@ -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,
struct net_device *dev,
struct macvlan_dev *vlan)
struct macvlan_dev *vlan, int cutoff)
{
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 {
struct netdev_hw_addr *ha;
DECLARE_BITMAP(filter, MACVLAN_MC_FILTER_SZ);
struct netdev_hw_addr *ha;

bitmap_zero(filter, MACVLAN_MC_FILTER_SZ);
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, dev->broadcast), filter);
if (cutoff >= 0)
__set_bit(mc_hash(vlan, dev->broadcast), filter);

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)
{
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_mc_sync(vlan->lowerdev, dev);
Expand All @@ -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
* 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)
Expand Down Expand Up @@ -1236,6 +1269,7 @@ static int macvlan_port_create(struct net_device *dev)
INIT_HLIST_HEAD(&port->vlan_source_hash[i]);

port->bc_queue_len_used = 0;
port->bc_cutoff = 1;
skb_queue_head_init(&port->bc_queue);
INIT_WORK(&port->bc_work, macvlan_process_broadcast);

Expand Down Expand Up @@ -1509,6 +1543,10 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
if (data && 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);
if (err < 0)
goto destroy_macvlan_port;
Expand Down Expand Up @@ -1605,6 +1643,10 @@ static int macvlan_changelink(struct net_device *dev,
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)
vlan->mode = mode;
if (data && data[IFLA_MACVLAN_MACADDR_MODE]) {
Expand Down Expand Up @@ -1685,6 +1727,9 @@ static int macvlan_fill_info(struct sk_buff *skb,
goto nla_put_failure;
if (nla_put_u32(skb, IFLA_MACVLAN_BC_QUEUE_LEN_USED, port->bc_queue_len_used))
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;

nla_put_failure:
Expand Down
1 change: 1 addition & 0 deletions include/uapi/linux/if_link.h
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,7 @@ enum {
IFLA_MACVLAN_MACADDR_COUNT,
IFLA_MACVLAN_BC_QUEUE_LEN,
IFLA_MACVLAN_BC_QUEUE_LEN_USED,
IFLA_MACVLAN_BC_CUTOFF,
__IFLA_MACVLAN_MAX,
};

Expand Down
1 change: 1 addition & 0 deletions tools/include/uapi/linux/if_link.h
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,7 @@ enum {
IFLA_MACVLAN_MACADDR_COUNT,
IFLA_MACVLAN_BC_QUEUE_LEN,
IFLA_MACVLAN_BC_QUEUE_LEN_USED,
IFLA_MACVLAN_BC_CUTOFF,
__IFLA_MACVLAN_MAX,
};

Expand Down

0 comments on commit 37018b5

Please sign in to comment.