Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

macvlan: Skip broadcast queue if multicast with single receiver

As it stands all broadcast and multicast packets are queued and
processed in a work queue. This is so that we don't overwhelm
the receive softirq path by generating thousands of packets or
more (see commit 412ca1550cbe "macvlan: Move broadcasts into a
work queue").

As such all multicast packets will be delayed, even if they will
be received by a single macvlan device. As using a workqueue
is not free in terms of latency, we should avoid this where possible.

This patch adds a new filter to determine which addresses should
be delayed and which ones won't. This is done using a crude
counter of how many times an address has been added to the macvlan
port (ha->synced). For now if an address has been added more than
once, then it will be considered to be broadcast. This could be
tuned further by making this threshold configurable.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Herbert Xu and committed by
David S. Miller
d45276e7 6fc5f5bc

+46 -28
+46 -28
drivers/net/macvlan.c
··· 50 50 u32 flags; 51 51 int count; 52 52 struct hlist_head vlan_source_hash[MACVLAN_HASH_SIZE]; 53 + DECLARE_BITMAP(bc_filter, MACVLAN_MC_FILTER_SZ); 53 54 DECLARE_BITMAP(mc_filter, MACVLAN_MC_FILTER_SZ); 54 55 unsigned char perm_addr[ETH_ALEN]; 55 56 }; ··· 292 291 } 293 292 } 294 293 294 + static void macvlan_multicast_rx(const struct macvlan_port *port, 295 + const struct macvlan_dev *src, 296 + struct sk_buff *skb) 297 + { 298 + if (!src) 299 + /* frame comes from an external address */ 300 + macvlan_broadcast(skb, port, NULL, 301 + MACVLAN_MODE_PRIVATE | 302 + MACVLAN_MODE_VEPA | 303 + MACVLAN_MODE_PASSTHRU| 304 + MACVLAN_MODE_BRIDGE); 305 + else if (src->mode == MACVLAN_MODE_VEPA) 306 + /* flood to everyone except source */ 307 + macvlan_broadcast(skb, port, src->dev, 308 + MACVLAN_MODE_VEPA | 309 + MACVLAN_MODE_BRIDGE); 310 + else 311 + /* 312 + * flood only to VEPA ports, bridge ports 313 + * already saw the frame on the way out. 314 + */ 315 + macvlan_broadcast(skb, port, src->dev, 316 + MACVLAN_MODE_VEPA); 317 + } 318 + 295 319 static void macvlan_process_broadcast(struct work_struct *w) 296 320 { 297 321 struct macvlan_port *port = container_of(w, struct macvlan_port, ··· 334 308 const struct macvlan_dev *src = MACVLAN_SKB_CB(skb)->src; 335 309 336 310 rcu_read_lock(); 337 - 338 - if (!src) 339 - /* frame comes from an external address */ 340 - macvlan_broadcast(skb, port, NULL, 341 - MACVLAN_MODE_PRIVATE | 342 - MACVLAN_MODE_VEPA | 343 - MACVLAN_MODE_PASSTHRU| 344 - MACVLAN_MODE_BRIDGE); 345 - else if (src->mode == MACVLAN_MODE_VEPA) 346 - /* flood to everyone except source */ 347 - macvlan_broadcast(skb, port, src->dev, 348 - MACVLAN_MODE_VEPA | 349 - MACVLAN_MODE_BRIDGE); 350 - else 351 - /* 352 - * flood only to VEPA ports, bridge ports 353 - * already saw the frame on the way out. 354 - */ 355 - macvlan_broadcast(skb, port, src->dev, 356 - MACVLAN_MODE_VEPA); 357 - 311 + macvlan_multicast_rx(port, src, skb); 358 312 rcu_read_unlock(); 359 313 360 314 if (src) ··· 482 476 } 483 477 484 478 hash = mc_hash(NULL, eth->h_dest); 485 - if (test_bit(hash, port->mc_filter)) 479 + if (test_bit(hash, port->bc_filter)) 486 480 macvlan_broadcast_enqueue(port, src, skb); 481 + else if (test_bit(hash, port->mc_filter)) 482 + macvlan_multicast_rx(port, src, skb); 487 483 488 484 return RX_HANDLER_PASS; 489 485 } ··· 788 780 789 781 static void macvlan_compute_filter(unsigned long *mc_filter, 790 782 struct net_device *dev, 791 - struct macvlan_dev *vlan) 783 + struct macvlan_dev *vlan, int cutoff) 792 784 { 793 785 if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) { 794 - bitmap_fill(mc_filter, MACVLAN_MC_FILTER_SZ); 786 + if (cutoff >= 0) 787 + bitmap_fill(mc_filter, MACVLAN_MC_FILTER_SZ); 788 + else 789 + bitmap_zero(mc_filter, MACVLAN_MC_FILTER_SZ); 795 790 } else { 796 - struct netdev_hw_addr *ha; 797 791 DECLARE_BITMAP(filter, MACVLAN_MC_FILTER_SZ); 792 + struct netdev_hw_addr *ha; 798 793 799 794 bitmap_zero(filter, MACVLAN_MC_FILTER_SZ); 800 795 netdev_for_each_mc_addr(ha, dev) { 796 + if (cutoff >= 0 && ha->synced <= cutoff) 797 + continue; 798 + 801 799 __set_bit(mc_hash(vlan, ha->addr), filter); 802 800 } 803 801 804 - __set_bit(mc_hash(vlan, dev->broadcast), filter); 802 + if (cutoff >= 0) 803 + __set_bit(mc_hash(vlan, dev->broadcast), filter); 805 804 806 805 bitmap_copy(mc_filter, filter, MACVLAN_MC_FILTER_SZ); 807 806 } ··· 818 803 { 819 804 struct macvlan_dev *vlan = netdev_priv(dev); 820 805 821 - macvlan_compute_filter(vlan->mc_filter, dev, vlan); 806 + macvlan_compute_filter(vlan->mc_filter, dev, vlan, 0); 822 807 823 808 dev_uc_sync(vlan->lowerdev, dev); 824 809 dev_mc_sync(vlan->lowerdev, dev); ··· 836 821 * The solution is to maintain a list of broadcast addresses like 837 822 * we do for uc/mc, if you care. 838 823 */ 839 - macvlan_compute_filter(vlan->port->mc_filter, vlan->lowerdev, NULL); 824 + macvlan_compute_filter(vlan->port->mc_filter, vlan->lowerdev, NULL, 825 + 0); 826 + macvlan_compute_filter(vlan->port->bc_filter, vlan->lowerdev, NULL, 827 + 1); 840 828 } 841 829 842 830 static int macvlan_change_mtu(struct net_device *dev, int new_mtu)