Commit 412ca1550cbecb2c ("macvlan: Move broadcasts into a work queue")
moved processing of all macvlan multicasts into a work queue. This
causes a noticable performance regression when there is heavy multicast
traffic on the underlying interface for multicast groups that the
macvlan subinterfaces are not members of, in which case we end up
cloning all those packets and then freeing them again from a work queue
without really doing any useful work with them in between.
The commit message for commit 412ca1550cbecb2c says:
| Fundamentally, we need to ensure that the amount of work handled
| in each netif_rx backlog run is constrained. As broadcasts are
| anything but constrained, it either needs to be limited per run
| or moved to process context.
This patch moves multicast handling back into macvlan_handle_frame()
context if there are 100 or fewer macvlan subinterfaces, while keeping
the work queue for if there are more macvlan subinterfaces than that.
I played around with keeping track of the number of macvlan
subinterfaces that have each multicast filter bit set, but that ended
up being more complicated than I liked. Conditionalising the work
queue deferring on the total number of macvlan subinterfaces seems
like a fair compromise.
On a quickly whipped together test program that creates an ethertap
interface with a single macvlan subinterface and then blasts 16 Mi
multicast packets through the ethertap interface for a multicast
group that the macvlan subinterface is not a member of, run time goes
from (vanilla kernel):
# time ./stress
real 0m41.864s
user 0m0.622s
sys 0m20.754s
to (with this patch):
# time ./stress
real 0m16.539s
user 0m0.519s
sys 0m15.949s
Reported-by: Grant Zhang <redacted>
Signed-off-by: Lennert Buytenhek <redacted>
---
drivers/net/macvlan.c | 71 ++++++++++++++++++++++++++++++++-------------------
1 file changed, 45 insertions(+), 26 deletions(-)
From: Cong Wang <hidden> Date: 2016-05-27 17:57:05
On Thu, May 26, 2016 at 4:44 PM, Lennert Buytenhek
[off-list ref] wrote:
Commit 412ca1550cbecb2c ("macvlan: Move broadcasts into a work queue")
moved processing of all macvlan multicasts into a work queue. This
causes a noticable performance regression when there is heavy multicast
traffic on the underlying interface for multicast groups that the
macvlan subinterfaces are not members of, in which case we end up
cloning all those packets and then freeing them again from a work queue
without really doing any useful work with them in between.
But we only queue up to 1000 packets in our backlog.
How about adding a quick check before cloning it?
On Fri, May 27, 2016 at 10:56:44AM -0700, Cong Wang wrote:
quoted hunk
quoted
Commit 412ca1550cbecb2c ("macvlan: Move broadcasts into a work queue")
moved processing of all macvlan multicasts into a work queue. This
causes a noticable performance regression when there is heavy multicast
traffic on the underlying interface for multicast groups that the
macvlan subinterfaces are not members of, in which case we end up
cloning all those packets and then freeing them again from a work queue
without really doing any useful work with them in between.
But we only queue up to 1000 packets in our backlog.
How about adding a quick check before cloning it?
macvlan_port *port,
struct sk_buff *nskb;
int err = -ENOMEM;
+ if (skb_queue_len(&port->bc_queue) >= MACVLAN_BC_QUEUE_LEN)
+ return;
+
nskb = skb_clone(skb, GFP_ATOMIC);
if (!nskb)
goto err;
We're not hitting the bc_queue skb limit in our environment, as the
machine can keep up with the traffic -- it's just that taking an
extra clone of the skb and queueing and running the work queue item
to free it again is eating up a lot of cycles.
But doing the queue length check before the clone might not be a bad
idea? (You'd probably want to atomic_long_inc(&skb->dev->rx_dropped)
before returning, though?)
From: Herbert Xu <herbert@gondor.apana.org.au> Date: 2016-05-30 08:18:17
On Fri, May 27, 2016 at 02:44:33AM +0300, Lennert Buytenhek wrote:
Commit 412ca1550cbecb2c ("macvlan: Move broadcasts into a work queue")
moved processing of all macvlan multicasts into a work queue. This
causes a noticable performance regression when there is heavy multicast
traffic on the underlying interface for multicast groups that the
macvlan subinterfaces are not members of, in which case we end up
cloning all those packets and then freeing them again from a work queue
without really doing any useful work with them in between.
OK so your motivation is to get rid of the unnecessary memory
allocation, right?
Here's my totally untested patch, it tries to resolve your problem
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.
Thanks,
--
Email: Herbert Xu [off-list ref]
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
From: Herbert Xu <herbert@gondor.apana.org.au> Date: 2016-05-30 08:23:23
When we postpone a broadcast packet we save the source port in
the skb if it is local. However, the source port can disappear
before we get a chance to process the packet.
This patch fixes this by holding a ref count on the netdev.
It also delays the skb->cb modification until after we allocate
the new skb as you should not modify shared skbs.
Fixes: 412ca1550cbe ("macvlan: Move broadcasts into a work queue")
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
From: Herbert Xu <herbert@gondor.apana.org.au> Date: 2016-05-30 08:28:45
Currently we always queue a multicast packet for further processing,
even if none of the macvlan devices are subscribed to the address.
This patch optimises this by adding a global multicast filter for
a macvlan_port.
Note that this patch doesn't handle the broadcast addresses of the
individual macvlan devices correctly, if they are not all identical.
However, this is already broken because there is no mechanism in
place to update the individual multicast filters when you change
the broadcast address.
If someone cares enough they should fix this by collecting all
broadcast addresses for a macvlan as we do for multicast and unicast.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
@@ -739,9 +746,31 @@ 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);++/* This is slightly inaccurate as we're including+*thesubscriptionlistofvlan->lowerdevtoo.+*/+bitmap_zero(filter,MACVLAN_MC_FILTER_SZ);+netdev_for_each_mc_addr(ha,vlan->lowerdev){+__set_bit(mc_hash(NULL,ha->addr),filter);+}++/* Bug alert: This only works if everyone has the+*samebroadcastaddress.Assoonassomeone+*changestheirsthiswillbreak.+*+*However,thisisalreadybrokenaswhenyou+*changeyourbroadcastaddresswedon'tget+*called.+*+*Thesolutionistomaintainalistofbroadcast+*addresseslikewedoforuc/mc,ifyoucare.+*/+__set_bit(mc_hash(NULL,dev->broadcast),filter);++bitmap_copy(vlan->port->mc_filter,filter,+MACVLAN_MC_FILTER_SZ);}-dev_uc_sync(vlan->lowerdev,dev);-dev_mc_sync(vlan->lowerdev,dev);}staticintmacvlan_change_mtu(structnet_device*dev,intnew_mtu)
--
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
On Mon, May 30, 2016 at 04:17:52PM +0800, Herbert Xu wrote:
quoted
Commit 412ca1550cbecb2c ("macvlan: Move broadcasts into a work queue")
moved processing of all macvlan multicasts into a work queue. This
causes a noticable performance regression when there is heavy multicast
traffic on the underlying interface for multicast groups that the
macvlan subinterfaces are not members of, in which case we end up
cloning all those packets and then freeing them again from a work queue
without really doing any useful work with them in between.
OK so your motivation is to get rid of the unnecessary memory
allocation, right?
That and stack switches to kworker threads and serialisation on
the bc_queue queue lock.
I think you need to set the vlan->port->mc_filter to all 1's in the
PROMISC/ALLMUTI branch here.
Otherwise packets won't properly pass your new hash test.
From: Herbert Xu <herbert@gondor.apana.org.au> Date: 2016-06-01 03:42:30
On Tue, May 31, 2016 at 02:07:13PM -0700, David Miller wrote:
I think you need to set the vlan->port->mc_filter to all 1's in the
PROMISC/ALLMUTI branch here.
Otherwise packets won't properly pass your new hash test.
Good point. Here's v2.
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.
Thanks,
--
Email: Herbert Xu [off-list ref]
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
From: Herbert Xu <herbert@gondor.apana.org.au> Date: 2016-06-01 03:43:06
When we postpone a broadcast packet we save the source port in
the skb if it is local. However, the source port can disappear
before we get a chance to process the packet.
This patch fixes this by holding a ref count on the netdev.
It also delays the skb->cb modification until after we allocate
the new skb as you should not modify shared skbs.
Fixes: 412ca1550cbe ("macvlan: Move broadcasts into a work queue")
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
From: Herbert Xu <herbert@gondor.apana.org.au> Date: 2016-06-01 03:45:54
Currently we always queue a multicast packet for further processing,
even if none of the macvlan devices are subscribed to the address.
This patch optimises this by adding a global multicast filter for
a macvlan_port.
Note that this patch doesn't handle the broadcast addresses of the
individual macvlan devices correctly, if they are not all identical
to vlan->lowerdev. However, this is already broken because there
is no mechanism in place to update the individual multicast filters
when you change the broadcast address.
If someone cares enough they should fix this by collecting all
broadcast addresses for a macvlan as we do for multicast and unicast.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
@@ -739,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);}+}++staticvoidmacvlan_set_mac_lists(structnet_device*dev)+{+structmacvlan_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+*listofvlan->lowerdevtoo.+*+*Bugalert:Thisonlyworksifeveryonehasthesamebroadcast+*addressaslowerdev.Assoonassomeonechangestheirsthis+*willbreak.+*+*However,thisisalreadybrokenaswhenyouchangeyourbroadcast+*addresswedon'tgetcalled.+*+*Thesolutionistomaintainalistofbroadcastaddresseslike+*wedoforuc/mc,ifyoucare.+*/+macvlan_compute_filter(vlan->port->mc_filter,vlan->lowerdev,NULL);}staticintmacvlan_change_mtu(structnet_device*dev,intnew_mtu)
--
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
From: Cong Wang <hidden> Date: 2016-06-01 04:19:57
On Tue, May 31, 2016 at 8:43 PM, Herbert Xu [off-list ref] wrote:
When we postpone a broadcast packet we save the source port in
the skb if it is local. However, the source port can disappear
before we get a chance to process the packet.
Hmm, why could this happen? The upper device should be linked
with the lower device, where a refcount is already held.
Also, the work is cancelled in ->uninit().
From: Herbert Xu <herbert@gondor.apana.org.au> Date: 2016-06-01 04:27:29
On Tue, May 31, 2016 at 09:19:37PM -0700, Cong Wang wrote:
Hmm, why could this happen? The upper device should be linked
with the lower device, where a refcount is already held.
Also, the work is cancelled in ->uninit().
Of course it can happen. We are talking about the source macvlan
device that we just looked up using the Ethernet address. That
device has nothing to do with the packet now so it may be deleted
at any time.
We do flush the work but only when the all macvlan devices on a
port have been deleted. Perhaps you're confusing the source
device with vlan->lowerdev which is confusingly the actual
hardware device?
Cheers,
--
Email: Herbert Xu [off-list ref]
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
From: Cong Wang <hidden> Date: 2016-06-01 23:37:13
On Tue, May 31, 2016 at 9:27 PM, Herbert Xu [off-list ref] wrote:
On Tue, May 31, 2016 at 09:19:37PM -0700, Cong Wang wrote:
quoted
Hmm, why could this happen? The upper device should be linked
with the lower device, where a refcount is already held.
Also, the work is cancelled in ->uninit().
Of course it can happen. We are talking about the source macvlan
device that we just looked up using the Ethernet address. That
device has nothing to do with the packet now so it may be deleted
at any time.
We do flush the work but only when the all macvlan devices on a
port have been deleted. Perhaps you're confusing the source
device with vlan->lowerdev which is confusingly the actual
hardware device?
I thought all the on-flying packets are waited by synchronize_net()
during the removal of any of these devices. But since you moved
them to a workqueue, aka process context, so I think it won't
work any more.
Your patch makes sense to me now. :)
Thanks!
From: David Miller <davem@davemloft.net> Date: 2016-06-02 00:49:24
From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Wed, 1 Jun 2016 11:42:18 +0800
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.