From: Eric Dumazet <hidden> Date: 2016-12-05 02:43:20
We currently access 3 cache lines from an skb in receive queue while
holding receive queue lock :
First cache line (contains ->next / prev pointers )
2nd cache line (skb->peeked)
3rd cache line (skb->truesize)
I believe we could get rid of skb->peeked completely.
I will cook a patch, but basically the idea is that the last owner of a
skb (right before skb->users becomes 0) can have the 'ownership' and
thus increase stats.
The 3rd cache line miss is easily avoided by the following patch.
But I also want to work on the idea I gave few days back, having a
separate queue and use splice to transfer the 'softirq queue' into
a calm queue in a different cache line.
I expect a 50 % performance increase under load, maybe 1.5 Mpps.
@@ -1191,7 +1191,13 @@ static void udp_rmem_release(struct sock *sk, int size, int partial)/* Note: called with sk_receive_queue.lock held */voidudp_skb_destructor(structsock*sk,structsk_buff*skb){-udp_rmem_release(sk,skb->truesize,1);+/* HACK HACK HACK :+*Insteadofusingskb->truesizehere,findacopyofitinskb->dev.+*Thisavoidsacachelinemissinthispath,+*whilesk_receive_queuelockisheld.+*Lookat__udp_enqueue_schedule_skb()tofindwherethiscopyisdone.+*/+udp_rmem_release(sk,(int)(unsignedlong)skb->dev,1);}EXPORT_SYMBOL(udp_skb_destructor);
@@ -1201,6 +1207,11 @@ int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb)intrmem,delta,amt,err=-ENOMEM;intsize=skb->truesize;+/* help udp_skb_destructor() to get skb->truesize from skb->dev+*withoutacachelinemiss.+*/+skb->dev=(structnet_device*)(unsignedlong)size;+/* try to avoid the costly atomic add/sub pair when the receive*queueisfull;alwaysallowatleastapacket*/
@@ -1233,7 +1244,6 @@ int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb)/* no need to setup a destructor, we will explicitly release the*forwardallocatedmemoryondequeue*/-skb->dev=NULL;sock_skb_set_dropcount(sk,skb);__skb_queue_tail(list,skb);
From: Paolo Abeni <pabeni@redhat.com> Date: 2016-12-05 13:30:03
Hi Eric,
On Sun, 2016-12-04 at 18:43 -0800, Eric Dumazet wrote:
We currently access 3 cache lines from an skb in receive queue while
holding receive queue lock :
First cache line (contains ->next / prev pointers )
2nd cache line (skb->peeked)
3rd cache line (skb->truesize)
I believe we could get rid of skb->peeked completely.
I will cook a patch, but basically the idea is that the last owner of a
skb (right before skb->users becomes 0) can have the 'ownership' and
thus increase stats.
Agreed.
The 3rd cache line miss is easily avoided by the following patch.
I run some performance tests on top of your patch "net: reorganize
struct sock for better data locality", and I see an additional ~7%
improvement on top of that, in the udp flood scenario.
In my tests, the topmost perf offenders for the u/s process are now:
9.98% udp_sink [kernel.kallsyms] [k] udp_rmem_release
8.76% udp_sink [kernel.kallsyms] [k] inet_recvmsg
6.71% udp_sink [kernel.kallsyms] [k] _raw_spin_lock_irqsave
5.40% udp_sink [kernel.kallsyms] [k] __skb_try_recv_datagram
5.19% udp_sink [kernel.kallsyms] [k] copy_user_enhanced_fast_string
udp_rmem_release() spends most of its time doing:
atomic_sub(size, &sk->sk_rmem_alloc);
I see a cacheline miss while accessing sk_rmem_alloc; most probably due
to sk_rmem_alloc being updated by the writer outside the rx queue lock.
Moving such update inside the lock show remove this cache miss but will
increase the pressure on the rx lock. What do you think ?
inet_recvmsg() is there because with "net: reorganize struct sock for
better data locality" we get a cache miss while accessing skc_rxhash in
sock_rps_record_flow(); touching sk_drops is dirtying that cacheline -
sorry for not noticing this before. Do you have CONFIG_RPS disabled ?
But I also want to work on the idea I gave few days back, having a
separate queue and use splice to transfer the 'softirq queue' into
a calm queue in a different cache line.
I expect a 50 % performance increase under load, maybe 1.5 Mpps.
It should work nicely under contention, but won't that increase the
overhead for the uncontended/single flow scenario ? the user space
reader needs to acquire 2 lock when splicing the 'softirq queue'. On my
system ksoftirqd and the u/s process work at similar speeds, so splicing
will happen quite often.
Paolo
From: Eric Dumazet <hidden> Date: 2016-12-05 14:46:56
On Mon, 2016-12-05 at 14:22 +0100, Paolo Abeni wrote:
Hi Eric,
On Sun, 2016-12-04 at 18:43 -0800, Eric Dumazet wrote:
quoted
We currently access 3 cache lines from an skb in receive queue while
holding receive queue lock :
First cache line (contains ->next / prev pointers )
2nd cache line (skb->peeked)
3rd cache line (skb->truesize)
I believe we could get rid of skb->peeked completely.
I will cook a patch, but basically the idea is that the last owner of a
skb (right before skb->users becomes 0) can have the 'ownership' and
thus increase stats.
Agreed.
quoted
The 3rd cache line miss is easily avoided by the following patch.
I run some performance tests on top of your patch "net: reorganize
struct sock for better data locality", and I see an additional ~7%
improvement on top of that, in the udp flood scenario.
In my tests, the topmost perf offenders for the u/s process are now:
9.98% udp_sink [kernel.kallsyms] [k] udp_rmem_release
8.76% udp_sink [kernel.kallsyms] [k] inet_recvmsg
6.71% udp_sink [kernel.kallsyms] [k] _raw_spin_lock_irqsave
5.40% udp_sink [kernel.kallsyms] [k] __skb_try_recv_datagram
5.19% udp_sink [kernel.kallsyms] [k] copy_user_enhanced_fast_string
udp_rmem_release() spends most of its time doing:
atomic_sub(size, &sk->sk_rmem_alloc);
I see a cacheline miss while accessing sk_rmem_alloc; most probably due
to sk_rmem_alloc being updated by the writer outside the rx queue lock.
Moving such update inside the lock show remove this cache miss but will
increase the pressure on the rx lock. What do you think ?
I want to accumulate the sk_rmem_alloc deficit in another variable in
the same cache line than the secondary list, updated from process
context at udp recvmsg() time.
And only transfer the accumulated deficit in one go when the second
queue is emptied, or if the accumulated deficite is > (rcvbuf/2)
If done right, we should interfere between the softirq flooder(s) and
the process thread only once per batch.
inet_recvmsg() is there because with "net: reorganize struct sock for
better data locality" we get a cache miss while accessing skc_rxhash in
sock_rps_record_flow(); touching sk_drops is dirtying that cacheline -
sorry for not noticing this before. Do you have CONFIG_RPS disabled ?
quoted
But I also want to work on the idea I gave few days back, having a
separate queue and use splice to transfer the 'softirq queue' into
a calm queue in a different cache line.
I expect a 50 % performance increase under load, maybe 1.5 Mpps.
It should work nicely under contention, but won't that increase the
overhead for the uncontended/single flow scenario ? the user space
reader needs to acquire 2 lock when splicing the 'softirq queue'. On my
system ksoftirqd and the u/s process work at similar speeds, so splicing
will happen quite often.
Well, the splice would happen only if you have more than one message in
the softirq queue. So no real overhead for uncontended flow scenario.
This reminds me of the busylock I added in __dev_xmit_skb(), which
basically is acquired only when we detect a possible contention on qdisc
lock.
Thanks.
On Mon, 05 Dec 2016 06:28:53 -0800 Eric Dumazet [off-list ref] wrote:
On Mon, 2016-12-05 at 14:22 +0100, Paolo Abeni wrote:
quoted
On Sun, 2016-12-04 at 18:43 -0800, Eric Dumazet wrote:
[...]
quoted
quoted
But I also want to work on the idea I gave few days back, having a
separate queue and use splice to transfer the 'softirq queue' into
a calm queue in a different cache line.
I expect a 50 % performance increase under load, maybe 1.5 Mpps.
I also have high hopes for such a solution. I'm very excited that you
are working on this! :-)
quoted
It should work nicely under contention, but won't that increase the
overhead for the uncontended/single flow scenario ? the user space
reader needs to acquire 2 lock when splicing the 'softirq queue'.
On my system ksoftirqd and the u/s process work at similar speeds,
so splicing will happen quite often.
Well, the splice would happen only if you have more than one message
in the softirq queue. So no real overhead for uncontended flow
scenario.
This reminds me of the busylock I added in __dev_xmit_skb(), which
basically is acquired only when we detect a possible contention on
qdisc lock.
Do you think the splice technique would, have the same performance
benefit as having a MPMC queue with separate enqueue and dequeue locking?
(like we have with skb_array/ptr_ring that avoids cache bouncing)?
--
Best regards,
Jesper Dangaard Brouer
MSc.CS, Principal Kernel Engineer at Red Hat
LinkedIn: http://www.linkedin.com/in/brouer
From: Eric Dumazet <hidden> Date: 2016-12-05 16:12:45
On Mon, 2016-12-05 at 16:37 +0100, Jesper Dangaard Brouer wrote:
Do you think the splice technique would, have the same performance
benefit as having a MPMC queue with separate enqueue and dequeue locking?
(like we have with skb_array/ptr_ring that avoids cache bouncing)?
I believe ring buffers make sense for critical points in the kernel,
but for an arbitrary number of TCP/UDP sockets in a host, they are a big
increase of memory, and a practical problem when SO_RCVBUF is changed,
since dynamic resize of the ring buffer would be needed.
If you think about it, most sockets have few outstanding packets, like
0, 1 , 2. But they also might have ~100 packets, sometimes...
For most of TCP/UDP sockets, a linked list is simply good enough.
( We only very recently converted the out of order receive queue to an
RB tree )
Now, if _two_ linked list are also good in the very rare case of floods,
I would use two linked lists, if they can offer us a 50 % increase at
small memory cost.
Then for very special cases, we have af_packet which should be optimized
for all the fancy stuff.
If an application really receives more than 1.5 Mpps per UDP socket,
then the author should seriously consider SO_REUSEPORT, and have more
than 1 vcpu on its VM. I think we have cheap cloud offers available from
many providers.
The ring buffer queue might make sense in net/core/dev.c, since
we currently have 2 queues per cpu.
So you might want to experiment with that, because it looks like we
might go to a model where a single cpu is (busypoll) processing all low
level RX processing from a single queue per NUMA node, then dispatch to
other cpus the IP/{TCP|UDP} processing.
From: Eric Dumazet <hidden> Date: 2016-12-05 17:58:22
From: Eric Dumazet <edumazet@google.com>
In UDP recvmsg() path we currently access 3 cache lines from an skb
while holding receive queue lock, plus another one if packet is
dequeued, since we need to change skb->next->prev
1st cache line (contains ->next/prev pointers, offsets 0x00 and 0x08)
2nd cache line (skb->len & skb->peeked, offsets 0x80 and 0x8e)
3rd cache line (skb->truesize/users, offsets 0xe0 and 0xe4)
skb->peeked is only needed to make sure 0-length packets are properly
handled while MSG_PEEK is operated.
I had first the intent to remove skb->peeked but the "MSG_PEEK at
non-zero offset" support added by Sam Kumar makes this not possible.
This patch avoids one cache line miss during the locked section, when
skb->len and skb->peeked do not have to be read.
It also avoids the skb_set_peeked() cost for non empty UDP datagrams.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
net/core/datagram.c | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
@@ -214,6 +214,7 @@ struct sk_buff *__skb_try_recv_datagram(struct sock *sk, unsigned int flags,if(error)gotono_packet;+*peeked=0;do{/* Again only user level code calls this function, so nothing*interruptlevelwillsuddenlyeatthereceive_queue.
From: Paolo Abeni <pabeni@redhat.com> Date: 2016-12-06 09:55:07
Hi Eric,
On Mon, 2016-12-05 at 09:57 -0800, Eric Dumazet wrote:
From: Eric Dumazet <edumazet@google.com>
In UDP recvmsg() path we currently access 3 cache lines from an skb
while holding receive queue lock, plus another one if packet is
dequeued, since we need to change skb->next->prev
1st cache line (contains ->next/prev pointers, offsets 0x00 and 0x08)
2nd cache line (skb->len & skb->peeked, offsets 0x80 and 0x8e)
3rd cache line (skb->truesize/users, offsets 0xe0 and 0xe4)
skb->peeked is only needed to make sure 0-length packets are properly
handled while MSG_PEEK is operated.
I had first the intent to remove skb->peeked but the "MSG_PEEK at
non-zero offset" support added by Sam Kumar makes this not possible.
I'm wondering if peeking with offset is going to complicate the 2 queues
patch, too.
quoted hunk
This patch avoids one cache line miss during the locked section, when
skb->len and skb->peeked do not have to be read.
It also avoids the skb_set_peeked() cost for non empty UDP datagrams.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
net/core/datagram.c | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
@@ -214,6 +214,7 @@ struct sk_buff *__skb_try_recv_datagram(struct sock *sk, unsigned int flags,if(error)gotono_packet;+*peeked=0;do{/* Again only user level code calls this function, so nothing*interruptlevelwillsuddenlyeatthereceive_queue.
I don't understand why we can avoid setting skb->peek if len > 0. I
think that will change the kernel behavior if:
- peek with offset is set
- 3 skbs with len > 0 are enqueued
- the u/s peek (with offset) the second one
- the u/s disable peeking with offset and peeks 2 more skbs.
With the current code in the last step the u/s is going to peek the 1#
and the 3# skbs, after this patch will peek the 1# and the 2#. Am I
missing something ? Probably the new behavior is more correct, but still
is a change.
I gave this a run in my test bed on top of your udp-related patches I
see additional ~3 improvement in the udp flood scenario, and a bit more
in the un-contended scenario.
Thank you,
Paolo
From: Paolo Abeni <pabeni@redhat.com> Date: 2016-12-06 10:34:40
Hi Eric,
On Mon, 2016-12-05 at 09:57 -0800, Eric Dumazet wrote:
From: Eric Dumazet <edumazet@google.com>
In UDP recvmsg() path we currently access 3 cache lines from an skb
while holding receive queue lock, plus another one if packet is
dequeued, since we need to change skb->next->prev
1st cache line (contains ->next/prev pointers, offsets 0x00 and 0x08)
2nd cache line (skb->len & skb->peeked, offsets 0x80 and 0x8e)
3rd cache line (skb->truesize/users, offsets 0xe0 and 0xe4)
skb->peeked is only needed to make sure 0-length packets are properly
handled while MSG_PEEK is operated.
I had first the intent to remove skb->peeked but the "MSG_PEEK at
non-zero offset" support added by Sam Kumar makes this not possible.
This patch avoids one cache line miss during the locked section, when
skb->len and skb->peeked do not have to be read.
It also avoids the skb_set_peeked() cost for non empty UDP datagrams.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Thank you for all the good work.
After all your improvement, I see the cacheline miss in inet_recvmsg()
as a major perf offender for the user space process in the udp flood
scenario due to skc_rxhash sharing the same sk_drops cacheline.
Using an udp-specific drop counter (and an sk_drops accessor to wrap
sk_drops access where needed), we could avoid such cache miss. With that
- patch for udp.h only below - I get 3% improvement on top of all the
pending udp patches, and the gain should be more relevant after the 2
queues rework. What do you think ?
Cheers,
Paolo.
---
@@ -49,7 +49,11 @@ struct udp_sock {unsignedintcorkflag;/* Cork is required */__u8encap_type;/* Is this an Encapsulation socket? */unsignedcharno_check6_tx:1,/* Send zero UDP6 checksums on TX? */-no_check6_rx:1;/* Allow zero UDP6 checksums on RX? */+no_check6_rx:1,/* Allow zero UDP6 checksums on RX? */+pcflag:6;/* UDP-Lite specific, moved here to */+/* fill an hole, marks socket as */+/* UDP-Lite if > 0 */+/**FollowingmemberretainstheinformationtocreateaUDPheader*whenthesocketisuncorked.
@@ -64,8 +68,7 @@ struct udp_sock {#define UDPLITE_BIT 0x1 /* set by udplite proto init function */#define UDPLITE_SEND_CC 0x2 /* set via udplite setsockopt */#define UDPLITE_RECV_CC 0x4 /* set via udplite setsocktopt */-__u8pcflag;/* marks socket as UDP-Lite if > 0 */-__u8unused[3];+/**Forencapsulationsockets.*/
@@ -79,6 +82,9 @@ struct udp_sock {int(*gro_complete)(structsock*sk,structsk_buff*skb,intnhoff);++/* since we are prone to drops, avoid dirtying any sk cacheline */+atomic_tdrops____cacheline_aligned_in_smp;};staticinlinestructudp_sock*udp_sk(conststructsock*sk)
From: Paolo Abeni <pabeni@redhat.com> Date: 2016-12-06 12:10:16
On Tue, 2016-12-06 at 10:53 +0100, Paolo Abeni wrote:
On Mon, 2016-12-05 at 09:57 -0800, Eric Dumazet wrote:
quoted
From: Eric Dumazet <edumazet@google.com>
In UDP recvmsg() path we currently access 3 cache lines from an skb
while holding receive queue lock, plus another one if packet is
dequeued, since we need to change skb->next->prev
1st cache line (contains ->next/prev pointers, offsets 0x00 and 0x08)
2nd cache line (skb->len & skb->peeked, offsets 0x80 and 0x8e)
3rd cache line (skb->truesize/users, offsets 0xe0 and 0xe4)
skb->peeked is only needed to make sure 0-length packets are properly
handled while MSG_PEEK is operated.
I had first the intent to remove skb->peeked but the "MSG_PEEK at
non-zero offset" support added by Sam Kumar makes this not possible.
I'm wondering if peeking with offset is going to complicate the 2 queues
patch, too.
quoted
This patch avoids one cache line miss during the locked section, when
skb->len and skb->peeked do not have to be read.
It also avoids the skb_set_peeked() cost for non empty UDP datagrams.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
net/core/datagram.c | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
@@ -214,6 +214,7 @@ struct sk_buff *__skb_try_recv_datagram(struct sock *sk, unsigned int flags,if(error)gotono_packet;+*peeked=0;do{/* Again only user level code calls this function, so nothing*interruptlevelwillsuddenlyeatthereceive_queue.
I don't understand why we can avoid setting skb->peek if len > 0. I
think that will change the kernel behavior if:
- peek with offset is set
- 3 skbs with len > 0 are enqueued
- the u/s peek (with offset) the second one
- the u/s disable peeking with offset and peeks 2 more skbs.
With the current code in the last step the u/s is going to peek the 1#
and the 3# skbs, after this patch will peek the 1# and the 2#. Am I
missing something ? Probably the new behavior is more correct, but still
is a change.
Please ignore the above dumb comment. I misread the 'skip condition'.
I'm fine with the patch in its current form.
Acked-by: Paolo Abeni <pabeni@redhat.com>
From: Eric Dumazet <hidden> Date: 2016-12-06 14:34:44
On Tue, 2016-12-06 at 10:53 +0100, Paolo Abeni wrote:
Hi Eric,
I don't understand why we can avoid setting skb->peek if len > 0. I
think that will change the kernel behavior if:
- peek with offset is set
- 3 skbs with len > 0 are enqueued
- the u/s peek (with offset) the second one
- the u/s disable peeking with offset and peeks 2 more skbs.
With the current code in the last step the u/s is going to peek the 1#
and the 3# skbs, after this patch will peek the 1# and the 2#. Am I
missing something ? Probably the new behavior is more correct, but still
is a change.
I gave this a run in my test bed on top of your udp-related patches I
see additional ~3 improvement in the udp flood scenario, and a bit more
in the un-contended scenario.
Thank you,
MSG_PEEK always grab the first skb in queue, regardless of its
skb->peeked status.
Unless an offset (given in bytes) is given. Then we skip X bytes in the
queue, again regardless of skb->peeked bit.
skb->peeked is _needed_ to avoid peeking the same 0-byte skb over and
over, since user land could not skip it, as the offset to skip skbs is
computed by sum ( lengthes). An infinite loop of recvmsg() would happen,
stuck on the same skb.
For regular non 0-bytes payload, we can skip over them without even
looking at skb->peeked.
Initially, skb->peeked was only a (bad) way to make sure UDP would
increment its stats only for non peeked messages. We can implement that
using at MSG_PEEK flag.
From: Eric Dumazet <hidden> Date: 2016-12-06 14:35:59
On Tue, 2016-12-06 at 13:10 +0100, Paolo Abeni wrote:
Please ignore the above dumb comment. I misread the 'skip condition'.
I'm fine with the patch in its current form.
Acked-by: Paolo Abeni <pabeni@redhat.com>
No worries, I prefer having multiple eyes on this stuff before doing the
next step ;)
Thanks !
From: David Miller <davem@davemloft.net> Date: 2016-12-06 15:42:39
From: Eric Dumazet <redacted>
Date: Mon, 05 Dec 2016 09:57:19 -0800
From: Eric Dumazet <edumazet@google.com>
In UDP recvmsg() path we currently access 3 cache lines from an skb
while holding receive queue lock, plus another one if packet is
dequeued, since we need to change skb->next->prev
1st cache line (contains ->next/prev pointers, offsets 0x00 and 0x08)
2nd cache line (skb->len & skb->peeked, offsets 0x80 and 0x8e)
3rd cache line (skb->truesize/users, offsets 0xe0 and 0xe4)
skb->peeked is only needed to make sure 0-length packets are properly
handled while MSG_PEEK is operated.
I had first the intent to remove skb->peeked but the "MSG_PEEK at
non-zero offset" support added by Sam Kumar makes this not possible.
This patch avoids one cache line miss during the locked section, when
skb->len and skb->peeked do not have to be read.
It also avoids the skb_set_peeked() cost for non empty UDP datagrams.
Signed-off-by: Eric Dumazet <edumazet@google.com>
From: Paolo Abeni <pabeni@redhat.com> Date: 2016-12-06 17:08:27
On Tue, 2016-12-06 at 11:34 +0100, Paolo Abeni wrote:
On Mon, 2016-12-05 at 09:57 -0800, Eric Dumazet wrote:
quoted
From: Eric Dumazet <edumazet@google.com>
In UDP recvmsg() path we currently access 3 cache lines from an skb
while holding receive queue lock, plus another one if packet is
dequeued, since we need to change skb->next->prev
1st cache line (contains ->next/prev pointers, offsets 0x00 and 0x08)
2nd cache line (skb->len & skb->peeked, offsets 0x80 and 0x8e)
3rd cache line (skb->truesize/users, offsets 0xe0 and 0xe4)
skb->peeked is only needed to make sure 0-length packets are properly
handled while MSG_PEEK is operated.
I had first the intent to remove skb->peeked but the "MSG_PEEK at
non-zero offset" support added by Sam Kumar makes this not possible.
This patch avoids one cache line miss during the locked section, when
skb->len and skb->peeked do not have to be read.
It also avoids the skb_set_peeked() cost for non empty UDP datagrams.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Thank you for all the good work.
After all your improvement, I see the cacheline miss in inet_recvmsg()
as a major perf offender for the user space process in the udp flood
scenario due to skc_rxhash sharing the same sk_drops cacheline.
Using an udp-specific drop counter (and an sk_drops accessor to wrap
sk_drops access where needed), we could avoid such cache miss. With that
- patch for udp.h only below - I get 3% improvement on top of all the
pending udp patches, and the gain should be more relevant after the 2
queues rework. What do you think ?
Here follow what I'm experimenting.
The 'pcflag' changes is not strictly needed, but it shrinks the udp_sock
struct a bit, so that the newly added cacheline does not create
additional holes - with my kconfig, at least. I can use a separate patch
for that chunk.
---
@@ -49,7 +49,11 @@ struct udp_sock {unsignedintcorkflag;/* Cork is required */__u8encap_type;/* Is this an Encapsulation socket? */unsignedcharno_check6_tx:1,/* Send zero UDP6 checksums on TX? */-no_check6_rx:1;/* Allow zero UDP6 checksums on RX? */+no_check6_rx:1,/* Allow zero UDP6 checksums on RX? */+pcflag:6;/* UDP-Lite specific, moved here to */+/* fill an hole, marks socket as */+/* UDP-Lite if > 0 */+/**FollowingmemberretainstheinformationtocreateaUDPheader*whenthesocketisuncorked.
@@ -64,8 +68,7 @@ struct udp_sock {#define UDPLITE_BIT 0x1 /* set by udplite proto init function */#define UDPLITE_SEND_CC 0x2 /* set via udplite setsockopt */#define UDPLITE_RECV_CC 0x4 /* set via udplite setsocktopt */-__u8pcflag;/* marks socket as UDP-Lite if > 0 */-__u8unused[3];+/**Forencapsulationsockets.*/
@@ -79,6 +82,9 @@ struct udp_sock {int(*gro_complete)(structsock*sk,structsk_buff*skb,intnhoff);++/* since we are prone to drops, avoid dirtying any sk cacheline */+atomic_tdrops____cacheline_aligned_in_smp;};staticinlinestructudp_sock*udp_sk(conststructsock*sk)
@@ -1174,6 +1174,11 @@ int udp_sendpage(struct sock *sk, struct page *page, int offset,returnret;}+staticvoidudp_drops_inc(structsock*sk)+{+atomic_inc(&udp_sk(sk)->drops);+}+/* fully reclaim rmem/fwd memory allocated for skb */staticvoidudp_rmem_release(structsock*sk,intsize,intpartial){
@@ -1244,7 +1249,7 @@ int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb)/* no need to setup a destructor, we will explicitly release the*forwardallocatedmemoryondequeue*/-sock_skb_set_dropcount(sk,skb);+udp_skb_set_dropcount(sk,skb);__skb_queue_tail(list,skb);spin_unlock(&list->lock);
From: Eric Dumazet <hidden> Date: 2016-12-06 17:48:16
On Tue, 2016-12-06 at 18:08 +0100, Paolo Abeni wrote:
On Tue, 2016-12-06 at 11:34 +0100, Paolo Abeni wrote:
quoted
On Mon, 2016-12-05 at 09:57 -0800, Eric Dumazet wrote:
quoted
From: Eric Dumazet <edumazet@google.com>
In UDP recvmsg() path we currently access 3 cache lines from an skb
while holding receive queue lock, plus another one if packet is
dequeued, since we need to change skb->next->prev
1st cache line (contains ->next/prev pointers, offsets 0x00 and 0x08)
2nd cache line (skb->len & skb->peeked, offsets 0x80 and 0x8e)
3rd cache line (skb->truesize/users, offsets 0xe0 and 0xe4)
skb->peeked is only needed to make sure 0-length packets are properly
handled while MSG_PEEK is operated.
I had first the intent to remove skb->peeked but the "MSG_PEEK at
non-zero offset" support added by Sam Kumar makes this not possible.
This patch avoids one cache line miss during the locked section, when
skb->len and skb->peeked do not have to be read.
It also avoids the skb_set_peeked() cost for non empty UDP datagrams.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Thank you for all the good work.
After all your improvement, I see the cacheline miss in inet_recvmsg()
as a major perf offender for the user space process in the udp flood
scenario due to skc_rxhash sharing the same sk_drops cacheline.
Using an udp-specific drop counter (and an sk_drops accessor to wrap
sk_drops access where needed), we could avoid such cache miss. With that
- patch for udp.h only below - I get 3% improvement on top of all the
pending udp patches, and the gain should be more relevant after the 2
queues rework. What do you think ?
Here follow what I'm experimenting.
Well, new socket layout makes this kind of patches not really needed ?
/* --- cacheline 2 boundary (128 bytes) was 8 bytes ago --- */
socket_lock_t sk_lock; /* 0x88 0x20 */
atomic_t sk_drops; /* 0xa8 0x4 */
int sk_rcvlowat; /* 0xac 0x4 */
struct sk_buff_head sk_error_queue; /* 0xb0 0x18 */
/* --- cacheline 3 boundary (192 bytes) was 8 bytes ago --- */
Nothing in UDP fast path needs to access a field from this cache line, but sk_drops.
So wherever we put sk_drops (even in a cache line of its own), we will still have
false sharing on it.
And really this should be fine.
Eventually we could have per NUMA node counter to help a bit.
I mentioned at some point that we can very easily instruct
sock_skb_set_dropcount() to not read sk_drops if application
does not care about getting sk_drops ;)
https://patchwork.kernel.org/patch/9405677/
Now sk_drops was moved, the plan is to submit this patch in an official way.
Thanks !
From: Paolo Abeni <pabeni@redhat.com> Date: 2016-12-06 18:31:24
On Tue, 2016-12-06 at 09:47 -0800, Eric Dumazet wrote:
On Tue, 2016-12-06 at 18:08 +0100, Paolo Abeni wrote:
quoted
On Tue, 2016-12-06 at 11:34 +0100, Paolo Abeni wrote:
quoted
On Mon, 2016-12-05 at 09:57 -0800, Eric Dumazet wrote:
quoted
From: Eric Dumazet <edumazet@google.com>
In UDP recvmsg() path we currently access 3 cache lines from an skb
while holding receive queue lock, plus another one if packet is
dequeued, since we need to change skb->next->prev
1st cache line (contains ->next/prev pointers, offsets 0x00 and 0x08)
2nd cache line (skb->len & skb->peeked, offsets 0x80 and 0x8e)
3rd cache line (skb->truesize/users, offsets 0xe0 and 0xe4)
skb->peeked is only needed to make sure 0-length packets are properly
handled while MSG_PEEK is operated.
I had first the intent to remove skb->peeked but the "MSG_PEEK at
non-zero offset" support added by Sam Kumar makes this not possible.
This patch avoids one cache line miss during the locked section, when
skb->len and skb->peeked do not have to be read.
It also avoids the skb_set_peeked() cost for non empty UDP datagrams.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Thank you for all the good work.
After all your improvement, I see the cacheline miss in inet_recvmsg()
as a major perf offender for the user space process in the udp flood
scenario due to skc_rxhash sharing the same sk_drops cacheline.
Using an udp-specific drop counter (and an sk_drops accessor to wrap
sk_drops access where needed), we could avoid such cache miss. With that
- patch for udp.h only below - I get 3% improvement on top of all the
pending udp patches, and the gain should be more relevant after the 2
queues rework. What do you think ?
Here follow what I'm experimenting.
Well, new socket layout makes this kind of patches not really needed ?
/* --- cacheline 2 boundary (128 bytes) was 8 bytes ago --- */
socket_lock_t sk_lock; /* 0x88 0x20 */
atomic_t sk_drops; /* 0xa8 0x4 */
int sk_rcvlowat; /* 0xac 0x4 */
struct sk_buff_head sk_error_queue; /* 0xb0 0x18 */
/* --- cacheline 3 boundary (192 bytes) was 8 bytes ago --- */
cacheline 2 boundary (128 bytes) is 8 bytes before sk_lock: cacheline 2
includes also skc_refcnt and skc_rxhash from __sk_common (I use 'pahole
-E ...' to get the full blown output). skc_rxhash is read for each
packet in inet_recvmsg()/sock_rps_record_flow() if CONFIG_RPS is set. I
get a cache miss per packet there and inet_recvmsg() in my test takes
about 8% of the whole u/s processing time.
I mentioned at some point that we can very easily instruct
sock_skb_set_dropcount() to not read sk_drops if application
does not care about getting sk_drops ;)
https://patchwork.kernel.org/patch/9405677/
Now sk_drops was moved, the plan is to submit this patch in an official way.
From: Eric Dumazet <hidden> Date: 2016-12-06 18:59:37
On Tue, 2016-12-06 at 19:31 +0100, Paolo Abeni wrote:
cacheline 2 boundary (128 bytes) is 8 bytes before sk_lock: cacheline 2
includes also skc_refcnt and skc_rxhash from __sk_common (I use 'pahole
-E ...' to get the full blown output). skc_rxhash is read for each
packet in inet_recvmsg()/sock_rps_record_flow() if CONFIG_RPS is set. I
get a cache miss per packet there and inet_recvmsg() in my test takes
about 8% of the whole u/s processing time.
Wait a minute, this sk->sk_rxhash should only be read on connected
socket. Relying on it being 0 was okay only if we did not care
of false sharing. And UDP sockets used to grab socket refcount, so we
had false sharing a _lot_ in the past.
We must fix this if not already done properly.
Can you take care of this problem ?
Thanks !
From: Paolo Abeni <pabeni@redhat.com> Date: 2016-12-06 19:16:29
On Tue, 2016-12-06 at 10:58 -0800, Eric Dumazet wrote:
On Tue, 2016-12-06 at 19:31 +0100, Paolo Abeni wrote:
quoted
cacheline 2 boundary (128 bytes) is 8 bytes before sk_lock: cacheline 2
includes also skc_refcnt and skc_rxhash from __sk_common (I use 'pahole
-E ...' to get the full blown output). skc_rxhash is read for each
packet in inet_recvmsg()/sock_rps_record_flow() if CONFIG_RPS is set. I
get a cache miss per packet there and inet_recvmsg() in my test takes
about 8% of the whole u/s processing time.
Wait a minute, this sk->sk_rxhash should only be read on connected
socket. Relying on it being 0 was okay only if we did not care
of false sharing. And UDP sockets used to grab socket refcount, so we
had false sharing a _lot_ in the past.
Thank you for the pointer.
We must fix this if not already done properly.
Can you take care of this problem ?
I'll try, but it can be very soon: I'll have limited time and bad
internet connection up to next week.
Cheers,
Paolo
From: Eric Dumazet <hidden> Date: 2016-12-06 19:36:35
On Tue, 2016-12-06 at 20:16 +0100, Paolo Abeni wrote:
On Tue, 2016-12-06 at 10:58 -0800, Eric Dumazet wrote:
quoted
On Tue, 2016-12-06 at 19:31 +0100, Paolo Abeni wrote:
quoted
cacheline 2 boundary (128 bytes) is 8 bytes before sk_lock: cacheline 2
includes also skc_refcnt and skc_rxhash from __sk_common (I use 'pahole
-E ...' to get the full blown output). skc_rxhash is read for each
packet in inet_recvmsg()/sock_rps_record_flow() if CONFIG_RPS is set. I
get a cache miss per packet there and inet_recvmsg() in my test takes
about 8% of the whole u/s processing time.
Wait a minute, this sk->sk_rxhash should only be read on connected
socket. Relying on it being 0 was okay only if we did not care
of false sharing. And UDP sockets used to grab socket refcount, so we
had false sharing a _lot_ in the past.
Thank you for the pointer.
quoted
We must fix this if not already done properly.
Can you take care of this problem ?
I'll try, but it can be very soon: I'll have limited time and bad
internet connection up to next week.
Do not worry, I had a better idea anyway. I am testing it ;)
From: Eric Dumazet <hidden> Date: 2016-12-07 03:32:53
From: Eric Dumazet <edumazet@google.com>
Paolo noticed a cache line miss in UDP recvmsg() to access
sk_rxhash, sharing a cache line with sk_drops.
sk_drops might be heavily incremented by cpus handling a flood targeting
this socket.
We might place sk_drops on a separate cache line, but lets try
to avoid wasting 64 bytes per socket just for this, since we have
other bottlenecks to take care of.
sock_rps_record_flow() should only access sk_rxhash for connected
flows.
Testing sk_state for TCP_ESTABLISHED covers most of the cases for
connected sockets, for a zero cost, since system calls using
sock_rps_record_flow() also access sk->sk_prot which is on the
same cache line.
A follow up patch will provide a static_key (Jump Label) since most
hosts do not even use RFS.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Paolo Abeni <pabeni@redhat.com>
---
include/net/sock.h | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
From: Eric Dumazet <hidden> Date: 2016-12-07 06:47:30
On Tue, 2016-12-06 at 19:32 -0800, Eric Dumazet wrote:
A follow up patch will provide a static_key (Jump Label) since most
hosts do not even use RFS.
Speaking of static_key, it appears we now have GRO on UDP, and this
consumes a considerable amount of cpu cycles.
Turning off GRO allows me to get +20 % more packets on my single UDP
socket. (1.2 Mpps instead of 1.0 Mpps)
Surely udp_gro_receive() should be bypassed if no UDP socket has
registered a udp_sk(sk)->gro_receive handler
And/or delay the inet_add_offload(&udpv{4|6}_offload, IPPROTO_UDP); to
the first UDP sockets setting udp_sk(sk)->gro_receive handler,
ie udp_encap_enable() and udpv6_encap_enable()
:(
From: Paolo Abeni <pabeni@redhat.com> Date: 2016-12-07 07:57:13
On Tue, 2016-12-06 at 22:47 -0800, Eric Dumazet wrote:
On Tue, 2016-12-06 at 19:32 -0800, Eric Dumazet wrote:
quoted
A follow up patch will provide a static_key (Jump Label) since most
hosts do not even use RFS.
Speaking of static_key, it appears we now have GRO on UDP, and this
consumes a considerable amount of cpu cycles.
Turning off GRO allows me to get +20 % more packets on my single UDP
socket. (1.2 Mpps instead of 1.0 Mpps)
I see also an improvement for single flow tests disabling GRO, but on a
smaller scale (~5% if I recall correctly).
Surely udp_gro_receive() should be bypassed if no UDP socket has
registered a udp_sk(sk)->gro_receive handler
And/or delay the inet_add_offload(&udpv{4|6}_offload, IPPROTO_UDP); to
the first UDP sockets setting udp_sk(sk)->gro_receive handler,
ie udp_encap_enable() and udpv6_encap_enable()
I had some patches adding explicit static keys for udp_gro_receive, but
they were ugly and I did not get that much gain (I measured ~1-2%
skipping udp_gro_receive only). I can try to refresh them anyway.
We have some experimental patches to implement GRO for plain UDP
connected sockets, using frag_list to preserve the individual skb len,
and deliver the packet to user space individually. With that I got
~3mpps with a single queue/user space sink - before the recent udp
improvements. I would like to present these patches on netdev soon (no
sooner than next week, anyway).
Cheers,
Paolo
From: Paolo Abeni <pabeni@redhat.com> Date: 2016-12-07 07:59:14
On Tue, 2016-12-06 at 19:32 -0800, Eric Dumazet wrote:
quoted hunk
From: Eric Dumazet <edumazet@google.com>
Paolo noticed a cache line miss in UDP recvmsg() to access
sk_rxhash, sharing a cache line with sk_drops.
sk_drops might be heavily incremented by cpus handling a flood targeting
this socket.
We might place sk_drops on a separate cache line, but lets try
to avoid wasting 64 bytes per socket just for this, since we have
other bottlenecks to take care of.
sock_rps_record_flow() should only access sk_rxhash for connected
flows.
Testing sk_state for TCP_ESTABLISHED covers most of the cases for
connected sockets, for a zero cost, since system calls using
sock_rps_record_flow() also access sk->sk_prot which is on the
same cache line.
A follow up patch will provide a static_key (Jump Label) since most
hosts do not even use RFS.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Paolo Abeni <pabeni@redhat.com>
---
include/net/sock.h | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
@@ -913,7 +913,17 @@ static inline void sock_rps_record_flow_hash(__u32 hash)staticinlinevoidsock_rps_record_flow(conststructsock*sk){#ifdef CONFIG_RPS-sock_rps_record_flow_hash(sk->sk_rxhash);+/* Reading sk->sk_rxhash might incur an expensive cache line miss.+*+*TCP_ESTABLISHEDdoescoveralmostallstateswhereRFS+*mightbeuseful,andischeaper[1]thantesting:+*IPv4:inet_sk(sk)->inet_daddr+*IPv6:ipv6_addr_any(&sk->sk_v6_daddr)+*ORanadditionalsocketflag+*[1]:sk_stateandsk_protareinthesamecacheline.+*/+if(sk->sk_state==TCP_ESTABLISHED)+sock_rps_record_flow_hash(sk->sk_rxhash);#endif}
Thank you for the very prompt patch!
You made me curious about your other idea on this topic, this what you
initially talked about, right ?
LGTM.
Acked-by: Paolo Abeni <pabeni@redhat.com>
From: Eric Dumazet <hidden> Date: 2016-12-07 13:58:21
On Wed, 2016-12-07 at 08:59 +0100, Paolo Abeni wrote:
On Tue, 2016-12-06 at 19:32 -0800, Eric Dumazet wrote:
quoted
From: Eric Dumazet <edumazet@google.com>
Paolo noticed a cache line miss in UDP recvmsg() to access
sk_rxhash, sharing a cache line with sk_drops.
sk_drops might be heavily incremented by cpus handling a flood targeting
this socket.
We might place sk_drops on a separate cache line, but lets try
to avoid wasting 64 bytes per socket just for this, since we have
other bottlenecks to take care of.
sock_rps_record_flow() should only access sk_rxhash for connected
flows.
Testing sk_state for TCP_ESTABLISHED covers most of the cases for
connected sockets, for a zero cost, since system calls using
sock_rps_record_flow() also access sk->sk_prot which is on the
same cache line.
A follow up patch will provide a static_key (Jump Label) since most
hosts do not even use RFS.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Paolo Abeni <pabeni@redhat.com>
---
include/net/sock.h | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
@@ -913,7 +913,17 @@ static inline void sock_rps_record_flow_hash(__u32 hash)staticinlinevoidsock_rps_record_flow(conststructsock*sk){#ifdef CONFIG_RPS-sock_rps_record_flow_hash(sk->sk_rxhash);+/* Reading sk->sk_rxhash might incur an expensive cache line miss.+*+*TCP_ESTABLISHEDdoescoveralmostallstateswhereRFS+*mightbeuseful,andischeaper[1]thantesting:+*IPv4:inet_sk(sk)->inet_daddr+*IPv6:ipv6_addr_any(&sk->sk_v6_daddr)+*ORanadditionalsocketflag+*[1]:sk_stateandsk_protareinthesamecacheline.+*/+if(sk->sk_state==TCP_ESTABLISHED)+sock_rps_record_flow_hash(sk->sk_rxhash);#endif}
Thank you for the very prompt patch!
You made me curious about your other idea on this topic, this what you
initially talked about, right ?
That was what I first thought, but then having an rfs_key would also
help here. I will submit the patch on top of this one, so that final
code looks like :
From: Eric Dumazet <hidden> Date: 2016-12-07 14:26:33
On Wed, 2016-12-07 at 08:57 +0100, Paolo Abeni wrote:
On Tue, 2016-12-06 at 22:47 -0800, Eric Dumazet wrote:
quoted
On Tue, 2016-12-06 at 19:32 -0800, Eric Dumazet wrote:
quoted
A follow up patch will provide a static_key (Jump Label) since most
hosts do not even use RFS.
Speaking of static_key, it appears we now have GRO on UDP, and this
consumes a considerable amount of cpu cycles.
Turning off GRO allows me to get +20 % more packets on my single UDP
socket. (1.2 Mpps instead of 1.0 Mpps)
I see also an improvement for single flow tests disabling GRO, but on a
smaller scale (~5% if I recall correctly).
Was it on a NUMA host ?
My tests are spreading packets on 8 RX queues, 50/50 split on two NUMA
nodes.
From: Eric Dumazet <hidden> Date: 2016-12-07 14:29:32
On Wed, 2016-12-07 at 08:57 +0100, Paolo Abeni wrote:
We have some experimental patches to implement GRO for plain UDP
connected sockets, using frag_list to preserve the individual skb len,
and deliver the packet to user space individually. With that I got
~3mpps with a single queue/user space sink - before the recent udp
improvements. I would like to present these patches on netdev soon (no
sooner than next week, anyway).
Make sure you handle properly all netfilter helpers :(
Keeping frag_list means you keep one sk_buff per segment, so this really
looks like a legacy UDP server (like a DNS server) wont benefit from
this anyway.
From: David Miller <davem@davemloft.net> Date: 2016-12-07 15:47:52
From: Eric Dumazet <redacted>
Date: Tue, 06 Dec 2016 19:32:50 -0800
From: Eric Dumazet <edumazet@google.com>
Paolo noticed a cache line miss in UDP recvmsg() to access
sk_rxhash, sharing a cache line with sk_drops.
sk_drops might be heavily incremented by cpus handling a flood targeting
this socket.
We might place sk_drops on a separate cache line, but lets try
to avoid wasting 64 bytes per socket just for this, since we have
other bottlenecks to take care of.
sock_rps_record_flow() should only access sk_rxhash for connected
flows.
Testing sk_state for TCP_ESTABLISHED covers most of the cases for
connected sockets, for a zero cost, since system calls using
sock_rps_record_flow() also access sk->sk_prot which is on the
same cache line.
A follow up patch will provide a static_key (Jump Label) since most
hosts do not even use RFS.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Paolo Abeni <pabeni@redhat.com>
From: Eric Dumazet <hidden> Date: 2016-12-07 16:00:34
On Wed, 2016-12-07 at 06:29 -0800, Eric Dumazet wrote:
Keeping frag_list means you keep one sk_buff per segment, so this really
looks like a legacy UDP server (like a DNS server) wont benefit from
this anyway.
I played with the idea of preparing the skb for minimal overhead for the
process doing udp_recvmsg().
If socket is under pressure, softirq handler(s) can try to pull in
skb->head the payload of the packet if it fits.
Meaning the softirq handler can free/reuse the page fragment
immediately, instead of letting udp_recvmsg() do this hundreds of usec
later.
( Sort of copybreak, but without reallocating skb->head )
Gains :
- We reduce skb->truesize and thus can store more packets per SO_RCVBUF
KB
- We avoid a cache line misses at copyout() time and consume_skb() time,
and avoid one put_page() with potential alien freeing on NUMA hosts.
From: David Laight <hidden> Date: 2016-12-07 17:09:49
From: Paolo Abeni
Sent: 06 December 2016 17:08
...
quoted hunk
@@ -79,6 +82,9 @@ struct udp_sock { int (*gro_complete)(struct sock *sk, struct sk_buff *skb, int nhoff);++ /* since we are prone to drops, avoid dirtying any sk cacheline */+ atomic_t drops ____cacheline_aligned_in_smp; };
Isn't that likely to create a large hole on systems with large cache lines.
(Same as any other use of ____cacheline_aligned_in_smp.)
David
From: Eric Dumazet <hidden> Date: 2016-12-07 17:33:06
On Wed, 2016-12-07 at 17:09 +0000, David Laight wrote:
From: Paolo Abeni
quoted
Sent: 06 December 2016 17:08
...
quoted
@@ -79,6 +82,9 @@ struct udp_sock { int (*gro_complete)(struct sock *sk, struct sk_buff *skb, int nhoff);++ /* since we are prone to drops, avoid dirtying any sk cacheline */+ atomic_t drops ____cacheline_aligned_in_smp; };
Isn't that likely to create a large hole on systems with large cache lines.
(Same as any other use of ____cacheline_aligned_in_smp.)
Yes, I would like to avoid that, unless we come to the conclusion it is
absolutely needed.
I feel that we could simply use a pointer, and allocate memory on
demand, since many sockets do not ever experience a drop.
The pointer could stay in a read mostly section.
We even could use per cpu or node counter for some heavy drop cases.
From: Hannes Frederic Sowa <hidden> Date: 2016-12-07 17:37:48
On Wed, Dec 7, 2016, at 18:32, Eric Dumazet wrote:
On Wed, 2016-12-07 at 17:09 +0000, David Laight wrote:
quoted
From: Paolo Abeni
quoted
Sent: 06 December 2016 17:08
...
quoted
@@ -79,6 +82,9 @@ struct udp_sock { int (*gro_complete)(struct sock *sk, struct sk_buff *skb, int nhoff);++ /* since we are prone to drops, avoid dirtying any sk cacheline */+ atomic_t drops ____cacheline_aligned_in_smp; };
Isn't that likely to create a large hole on systems with large cache lines.
(Same as any other use of ____cacheline_aligned_in_smp.)
Yes, I would like to avoid that, unless we come to the conclusion it is
absolutely needed.
I feel that we could simply use a pointer, and allocate memory on
demand, since many sockets do not ever experience a drop.
The pointer could stay in a read mostly section.
We even could use per cpu or node counter for some heavy drop cases.
I had the same idea while discussing that with Paolo, merely using an
*atomic_t = kmalloc(sizeof(atomic_t)) out of band of the socket.
My fear was that those could be aggregated by the slab cache into one
cache line, causing even more heating on cachelines.
Bye,
Hannes
From: Eric Dumazet <hidden> Date: 2016-12-07 17:53:56
On Wed, 2016-12-07 at 18:37 +0100, Hannes Frederic Sowa wrote:
I had the same idea while discussing that with Paolo, merely using an
*atomic_t = kmalloc(sizeof(atomic_t)) out of band of the socket.
My fear was that those could be aggregated by the slab cache into one
cache line, causing even more heating on cachelines.
For hot stuff, better use kmalloc(max_t(size_t,
L1_CACHE_BYTES,
sizeof(...))
to avoid false sharing, unless this is per cpu data of course.
From: Eric Dumazet <hidden> Date: 2016-12-07 17:56:14
On Wed, 2016-12-07 at 18:37 +0100, Hannes Frederic Sowa wrote:
I had the same idea while discussing that with Paolo, merely using an
*atomic_t = kmalloc(sizeof(atomic_t)) out of band of the socket.
My fear was that those could be aggregated by the slab cache into one
cache line, causing even more heating on cachelines.
My exact idea was to let up to 4095 (or PAGE_SIZE - 1) increments being
done on the counter before switching to dynamically allocated memory.
( Some packets might be dropped by TCP sockets, not necessarily a sign
of an attack. just some spurious retransmits )
From: Tom Herbert <hidden> Date: 2016-12-08 17:49:24
On Tue, Dec 6, 2016 at 10:47 PM, Eric Dumazet [off-list ref] wrote:
On Tue, 2016-12-06 at 19:32 -0800, Eric Dumazet wrote:
quoted
A follow up patch will provide a static_key (Jump Label) since most
hosts do not even use RFS.
Speaking of static_key, it appears we now have GRO on UDP, and this
consumes a considerable amount of cpu cycles.
Turning off GRO allows me to get +20 % more packets on my single UDP
socket. (1.2 Mpps instead of 1.0 Mpps)
Surely udp_gro_receive() should be bypassed if no UDP socket has
registered a udp_sk(sk)->gro_receive handler
And/or delay the inet_add_offload(&udpv{4|6}_offload, IPPROTO_UDP); to
the first UDP sockets setting udp_sk(sk)->gro_receive handler,
ie udp_encap_enable() and udpv6_encap_enable()
Of course that would only help on systems where no one enable encaps,
ie. looks good in the the simple benchmarks but in real life if just
one socket enables encap everyone else takes the hit. Alternatively,
maybe we could do early demux when we do the lookup in GRO to
eliminate the extra lookup?
Tom
From: Paolo Abeni <pabeni@redhat.com> Date: 2016-12-08 17:50:02
On Wed, 2016-12-07 at 06:26 -0800, Eric Dumazet wrote:
On Wed, 2016-12-07 at 08:57 +0100, Paolo Abeni wrote:
quoted
On Tue, 2016-12-06 at 22:47 -0800, Eric Dumazet wrote:
quoted
On Tue, 2016-12-06 at 19:32 -0800, Eric Dumazet wrote:
quoted
A follow up patch will provide a static_key (Jump Label) since most
hosts do not even use RFS.
Speaking of static_key, it appears we now have GRO on UDP, and this
consumes a considerable amount of cpu cycles.
Turning off GRO allows me to get +20 % more packets on my single UDP
socket. (1.2 Mpps instead of 1.0 Mpps)
I see also an improvement for single flow tests disabling GRO, but on a
smaller scale (~5% if I recall correctly).
Was it on a NUMA host ?
I'm using a single socket host, with 12 cores/24 threads and 16 RX
queues.
But my data is old. I'll re-run the test on top of current net-next.
Paolo
From: Eric Dumazet <hidden> Date: 2016-12-08 18:03:23
On Thu, 2016-12-08 at 09:49 -0800, Tom Herbert wrote:
Of course that would only help on systems where no one enable encaps,
ie. looks good in the the simple benchmarks but in real life if just
one socket enables encap everyone else takes the hit. Alternatively,
maybe we could do early demux when we do the lookup in GRO to
eliminate the extra lookup?
Well, if you do the lookup in GRO, wont it be done for every incoming
MSS, instead of once per GRO packet ?
Anyway, the flooded UDP sockets out there are not normally connected
ones.
From: Eric Dumazet <hidden> Date: 2016-12-08 18:08:10
On Thu, 2016-12-08 at 09:49 -0800, Tom Herbert wrote:
Of course that would only help on systems where no one enable encaps,
ie. looks good in the the simple benchmarks but in real life if just
one socket enables encap everyone else takes the hit.
Well, in real life most linux hosts do not use any UDP encapsulation.
Or if they do, maybe they still have to handle a lot of UDP traffic
which does not hit a tunnel in the kernel.
Anyway, my difference vs GRO on/off were caused by copybreak in mlx4
driver.
GRO off --> mlx4 uses copybreak for small messages (all protocols)
GRO on --> no copybreak for native protocols (IP+TCP IP+UDP)
The lookup being done twice is not that expensive, if the first two
cache lines of the socket stay shared (mostly read)
From: Paolo Abeni <pabeni@redhat.com> Date: 2016-12-08 18:50:12
On Wed, 2016-12-07 at 06:29 -0800, Eric Dumazet wrote:
On Wed, 2016-12-07 at 08:57 +0100, Paolo Abeni wrote:
quoted
We have some experimental patches to implement GRO for plain UDP
connected sockets, using frag_list to preserve the individual skb len,
and deliver the packet to user space individually. With that I got
~3mpps with a single queue/user space sink - before the recent udp
improvements. I would like to present these patches on netdev soon (no
sooner than next week, anyway).
Make sure you handle properly all netfilter helpers :(
Thank you for the head-up!
UDP-GRO will be enabled by a specific netdev feature bit, disabled by
default, should not impact by default any setup.
Keeping frag_list means you keep one sk_buff per segment, so this really
looks like a legacy UDP server (like a DNS server) wont benefit from
this anyway.
I'm sorry, I do not follow.
UDP GRO will require connected socket - very likely no DNS server. The
use-case is an application using long lived UDP sockets doing a lot of
traffic, like fix protocol feeds over UDP.
Thank you,
Paolo
From: Tom Herbert <hidden> Date: 2016-12-08 19:15:39
On Thu, Dec 8, 2016 at 10:02 AM, Eric Dumazet [off-list ref] wrote:
On Thu, 2016-12-08 at 09:49 -0800, Tom Herbert wrote:
quoted
Of course that would only help on systems where no one enable encaps,
ie. looks good in the the simple benchmarks but in real life if just
one socket enables encap everyone else takes the hit. Alternatively,
maybe we could do early demux when we do the lookup in GRO to
eliminate the extra lookup?
Well, if you do the lookup in GRO, wont it be done for every incoming
MSS, instead of once per GRO packet ?
We should be able to avoid that. We already do the lookup for every
UDP packet going into GRO, would only need to take the refcnt once for
the whole GRO packet.
Anyway, the flooded UDP sockets out there are not normally connected
We still should be able to use early demux in that case, just can't
avoid the route lookup. I wonder if we might be able to cache a soft
route maybe for the last local destination received to help the
unconnected sockets case...
In any case, I can take a look at of doing early demux from with UDP GRO.
Tom
From: Edward Cree <hidden> Date: 2016-12-08 19:21:59
On 07/12/16 07:57, Paolo Abeni wrote:
We have some experimental patches to implement GRO for plain UDP
connected sockets, using frag_list to preserve the individual skb len,
and deliver the packet to user space individually. With that I got
~3mpps with a single queue/user space sink - before the recent udp
improvements.
You might want to benchmark these against my batched receive patches
from a while ago[1], both seem to have broadly the same objective.
In my benchmarking (obviously with different hardware) I was using
multiple sink processes, but all (processes and irqs) on a single
core; the unpatched kernel was getting ~5Mpps. Then with my patches
I was getting ~6.4Mpps. (Limitations of my test scripts meant that
having a single sink process meant also having a single source
process, in which case I was TX limited to ~3Mpps, and using about
60% CPU on the RX side.)
Let me know if you're interested in doing this comparison; if so I'll
post updated patches against net-next. My own attempts to benchmark
them more have been held up by lack of time and not really knowing
what constitutes a realistic netfilter setup.
Of course if you're using a device other than sfc you'll need to add
your own equivalent of patch #2 to call the netif_receive_skb_list()
entry point from the driver.
-Ed
[1] https://www.spinics.net/lists/netdev/msg373769.html
From: Eric Dumazet <hidden> Date: 2016-12-08 19:33:03
On Thu, 2016-12-08 at 19:50 +0100, Paolo Abeni wrote:
UDP GRO will require connected socket - very likely no DNS server. The
use-case is an application using long lived UDP sockets doing a lot of
traffic, like fix protocol feeds over UDP.
You mean, the kind of traffic that should use TCP in the first place,
right ? ;)
NFS switched to TCP a long time ago.
From: Hannes Frederic Sowa <hidden> Date: 2016-12-08 20:05:17
Hello,
On Thu, Dec 8, 2016, at 20:15, Tom Herbert wrote:
On Thu, Dec 8, 2016 at 10:02 AM, Eric Dumazet [off-list ref]
wrote:
quoted
On Thu, 2016-12-08 at 09:49 -0800, Tom Herbert wrote:
quoted
Of course that would only help on systems where no one enable encaps,
ie. looks good in the the simple benchmarks but in real life if just
one socket enables encap everyone else takes the hit. Alternatively,
maybe we could do early demux when we do the lookup in GRO to
eliminate the extra lookup?
Well, if you do the lookup in GRO, wont it be done for every incoming
MSS, instead of once per GRO packet ?
We should be able to avoid that. We already do the lookup for every
UDP packet going into GRO, would only need to take the refcnt once for
the whole GRO packet.
quoted
Anyway, the flooded UDP sockets out there are not normally connected
We still should be able to use early demux in that case, just can't
avoid the route lookup. I wonder if we might be able to cache a soft
route maybe for the last local destination received to help the
unconnected sockets case...
In any case, I can take a look at of doing early demux from with UDP GRO.
Early demux already breaks ip rules: we might set up a rule so an
incoming packet might depending on the rule not find an input route at
all and would be forwarded. Same problem might occur with VRF, when you
have multiple ip addresses in different "realms".
That said, I don't see why we can't be more aggressive for GRO in the
unconnected case: we simply must make sure that the current namespace
holds the ip address, which is simply a hash lookup. After that we can
even accept packets for a wildcard bounded socket.
Probably we should disable this logic as soon as soon as vrf and/or
rules are active to have correct semantics.
Bye,
Hannes
From: Tom Herbert <hidden> Date: 2016-12-08 20:31:06
On Thu, Dec 8, 2016 at 12:05 PM, Hannes Frederic Sowa
[off-list ref] wrote:
Hello,
On Thu, Dec 8, 2016, at 20:15, Tom Herbert wrote:
quoted
On Thu, Dec 8, 2016 at 10:02 AM, Eric Dumazet [off-list ref]
wrote:
quoted
On Thu, 2016-12-08 at 09:49 -0800, Tom Herbert wrote:
quoted
Of course that would only help on systems where no one enable encaps,
ie. looks good in the the simple benchmarks but in real life if just
one socket enables encap everyone else takes the hit. Alternatively,
maybe we could do early demux when we do the lookup in GRO to
eliminate the extra lookup?
Well, if you do the lookup in GRO, wont it be done for every incoming
MSS, instead of once per GRO packet ?
We should be able to avoid that. We already do the lookup for every
UDP packet going into GRO, would only need to take the refcnt once for
the whole GRO packet.
quoted
Anyway, the flooded UDP sockets out there are not normally connected
We still should be able to use early demux in that case, just can't
avoid the route lookup. I wonder if we might be able to cache a soft
route maybe for the last local destination received to help the
unconnected sockets case...
In any case, I can take a look at of doing early demux from with UDP GRO.
Early demux already breaks ip rules: we might set up a rule so an
incoming packet might depending on the rule not find an input route at
all and would be forwarded. Same problem might occur with VRF, when you
have multiple ip addresses in different "realms".
That said, I don't see why we can't be more aggressive for GRO in the
unconnected case: we simply must make sure that the current namespace
holds the ip address, which is simply a hash lookup. After that we can
even accept packets for a wildcard bounded socket.
Probably we should disable this logic as soon as soon as vrf and/or
rules are active to have correct semantics.
All this gets dicey in the presence of encapsulation. One problem is
that we can't tell when or if a packet crosses network namespace just
by parsing the packet. It's a subset of the general problem of
correctly identifying packets outside of the terminal protocol
processing (we've already talked about the incorrectness of devices
that identify UDP encapsulation based on port numbers). But even
without encapsulation there is still the problem as you point out with
vrf, IPvlan, etc. I think the answer thus far has been to hand wave
and rely on probability, for instance identifying UDP encapsulation by
port number in device probably works almost all of the time. Matching
an unconnected UDP socket in GRO and then accepting a route associated
with that probably would also work nearly all the time. Maybe if we
can quantify the dependencies that early parsing has in this area,
other mechanisms (vrf) might be able to do something intelligent to
ensure correctness-- does seem like a hard problem though!
Tom
From: Tom Herbert <hidden> Date: 2016-12-08 20:44:52
On Thu, Dec 8, 2016 at 12:05 PM, Hannes Frederic Sowa
[off-list ref] wrote:
Hello,
On Thu, Dec 8, 2016, at 20:15, Tom Herbert wrote:
quoted
On Thu, Dec 8, 2016 at 10:02 AM, Eric Dumazet [off-list ref]
wrote:
quoted
On Thu, 2016-12-08 at 09:49 -0800, Tom Herbert wrote:
quoted
Of course that would only help on systems where no one enable encaps,
ie. looks good in the the simple benchmarks but in real life if just
one socket enables encap everyone else takes the hit. Alternatively,
maybe we could do early demux when we do the lookup in GRO to
eliminate the extra lookup?
Well, if you do the lookup in GRO, wont it be done for every incoming
MSS, instead of once per GRO packet ?
We should be able to avoid that. We already do the lookup for every
UDP packet going into GRO, would only need to take the refcnt once for
the whole GRO packet.
quoted
Anyway, the flooded UDP sockets out there are not normally connected
We still should be able to use early demux in that case, just can't
avoid the route lookup. I wonder if we might be able to cache a soft
route maybe for the last local destination received to help the
unconnected sockets case...
In any case, I can take a look at of doing early demux from with UDP GRO.
Early demux already breaks ip rules: we might set up a rule so an
incoming packet might depending on the rule not find an input route at
all and would be forwarded. Same problem might occur with VRF, when you
have multiple ip addresses in different "realms".
That said, I don't see why we can't be more aggressive for GRO in the
unconnected case: we simply must make sure that the current namespace
holds the ip address, which is simply a hash lookup. After that we can
even accept packets for a wildcard bounded socket.
Or just depend on encapsulation sockets to bind to an address. That
would eliminate most the ambiguity especially if it can be pushed into
a device that is trying to parse encapsulation. We would need new
interfaces to support that in HW, or use n-tuple filtering (which I
still maintain is the only right way to do it).
Tom
Probably we should disable this logic as soon as soon as vrf and/or
rules are active to have correct semantics.
Bye,
Hannes