Could we avoid touching dst->refcount in some cases ?
Subsystems (5)
RFC 1/1 Could we avoid touching dst->refcount in some cases ?
tbench has hard time incrementing decrementing the route cache refcount shared by all communications on localhost.
On real world, we also have this problem on RTP servers sending many UDP frames to mediagateways, especially big ones handling thousand of streams.
Given that route entries are using RCU, we probably can avoid incrementing their refcount in case of connected sockets ?
Here is a (untested and probably not working at all) patch on UDP part to illustrate the idea :
net/ipv4/udp.c +11 -4
@@ -553,6 +553,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,int ulen = len;struct ipcm_cookie ipc;struct rtable *rt =NULL ; +int rt_release =0 ;int free =0 ;int connected =0 ;__be32 daddr, faddr, saddr;
@@ -656,8 +657,9 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, connected =0 ; } +rcu_read_lock ();if (connected) - rt = (struct rtable *)sk_dst_check (sk,0 ); + rt = (struct rtable *)__sk_dst_check (sk,0 );if (rt ==NULL ) {struct flowi fl = { .oif = ipc.oif,
@@ -681,11 +683,14 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, } err = -EACCES ; + rt_release =1 ;if ((rt->rt_flags &RTCF_BROADCAST ) && !sock_flag (sk,SOCK_BROADCAST )) goto out; -if (connected) -sk_dst_set (sk,dst_clone (&rt->u.dst)); +if (connected) { +sk_dst_set (sk, &rt->u.dst); + rt_release =0 ; + } }if (msg->msg_flags&MSG_CONFIRM )
@@ -730,7 +735,9 @@ do_append_data:release_sock (sk); out: -ip_rt_put (rt); +if (rt_release) +ip_rt_put (rt); +rcu_read_unlock ();if (free)kfree (ipc.opt);if (!err)
Quoted 2 lines · Eric Dumazet
tbench has hard time incrementing decrementing the route cache refcount shared by all communications on localhost.
iirc there was a patch some time ago to use per CPU loopback devices to avoid this, but it was considered too much a benchmark hack. As core counts increase it might stop being that though.
Quoted 6 lines
On real world, we also have this problem on RTP servers sending many UDP frames to mediagateways, especially big ones handling thousand of streams.
Given that route entries are using RCU, we probably can avoid incrementing their refcount in case of connected sockets ?
Normally they can be hold over sleeps or queuing of skbs too, and RCU doesn't handle that. To make it handle that you would need to define a custom RCU period designed for this case, but this would be probably tricky and fragile: especially I'm not sure even if you had a "any packet queued" RCU method it be guaranteed to always finish because there is no fixed upper livetime of a packet.
The other issue is that on preemptible kernels you would need to disable preemption all the time such a routing entry is hold, which could be potentially quite long.
-Andi
Signature · 1 line
[email protected]
Quoted 7 lines · Andi Kleen
Quoted 2 lines · Eric Dumazet
tbench has hard time incrementing decrementing the route cache refcount shared by all communications on localhost.
iirc there was a patch some time ago to use per CPU loopback devices to avoid this, but it was considered too much a benchmark hack. As core counts increase it might stop being that though.
Well, you probably mention Stephen patch to avoid dirtying other contended cache lines (one napi structure per cpu)
Having multiple loopback dev would really be a hack I agree.
Quoted 7 lines
Quoted 6 lines
On real world, we also have this problem on RTP servers sending many UDP frames to mediagateways, especially big ones handling thousand of streams.
Given that route entries are using RCU, we probably can avoid incrementing their refcount in case of connected sockets ?
Quoted 9 lines
Normally they can be hold over sleeps or queuing of skbs too, and RCU
doesn't handle that. To make it handle that you would need to define a
custom RCU period designed for this case, but this would be probably
tricky and fragile: especially I'm not sure even if you had a "any
packet queued" RCU method it be guaranteed to always finish because there is no fixed upper livetime of a packet.The other issue is that on preemptible kernels you would need to disable preemption all the time such a routing entry is hold, which could be potentially quite long.
Well, in case of UDP, we call ip_push_pending_frames() and this one does the increment of refcount (again). I was not considering avoiding the refcount hold we do when queing a skb in transmit queue, only during a short period of time. Oh well, ip_append_data() might sleep, so this cannot work...
I agree avoiding one refcount increment/decrement is probably not a huge gain, considering we *have* to do the increment, but when many cpus are using UDP send/receive in //, this might show a gain somehow.
So maybe we could make ip_append_data() (or its callers) a litle bit smarter, avoiding increment/decrement if possible.
Quoted 6 lines · Eric Dumazet
Quoted 5 lines · Andi Kleen
Quoted 2 lines · Eric Dumazet
tbench has hard time incrementing decrementing the route cache refcount shared by all communications on localhost.
Quoted 6 lines
Quoted 3 lines
iirc there was a patch some time ago to use per CPU loopback devices to avoid this, but it was considered too much a benchmark hack. As core counts increase it might stop being that though.
Well, you probably mention Stephen patch to avoid dirtying other contended cache lines (one napi structure per cpu)
No that patch wasn't from Stephen. iirc it was from someone at SGI.
-Andi
Signature · 1 line
[email protected]
David Miller
maintainerFrom: Andi Kleen <[email protected]>
Date: Mon, 24 Nov 2008 12:27:09 +0100
Quoted 7 lines · Eric Dumazet
Quoted 6 lines
Quoted 5 lines · Andi Kleen
Quoted 2 lines · Eric Dumazet
tbench has hard time incrementing decrementing the route cache refcount shared by all communications on localhost.
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
iirc there was a patch some time ago to use per CPU loopback devices to avoid this, but it was considered too much a benchmark hack. As core counts increase it might stop being that though.
Well, you probably mention Stephen patch to avoid dirtying other contended cache lines (one napi structure per cpu)
No that patch wasn't from Stephen. iirc it was from someone at SGI.
That's how I remember it too.
PATCH 1/1 net: avoid a pair of dst_hold()/dst_release() in ip_append_data()
In reply to Eric DumazetQuoted 37 lines · Eric Dumazet
Quoted 7 lines · Andi Kleen
Quoted 2 lines · Eric Dumazet
tbench has hard time incrementing decrementing the route cache refcount shared by all communications on localhost.
iirc there was a patch some time ago to use per CPU loopback devices to avoid this, but it was considered too much a benchmark hack. As core counts increase it might stop being that though.
Well, you probably mention Stephen patch to avoid dirtying other contended cache lines (one napi structure per cpu)
Having multiple loopback dev would really be a hack I agree.
Quoted 7 lines
Quoted 6 lines
On real world, we also have this problem on RTP servers sending many UDP frames to mediagateways, especially big ones handling thousand of streams.
Given that route entries are using RCU, we probably can avoid incrementing their refcount in case of connected sockets ?
Quoted 9 lines
Normally they can be hold over sleeps or queuing of skbs too, and RCU
doesn't handle that. To make it handle that you would need to define a
custom RCU period designed for this case, but this would be probably
tricky and fragile: especially I'm not sure even if you had a "any
packet queued" RCU method it be guaranteed to always finish because there is no fixed upper livetime of a packet.The other issue is that on preemptible kernels you would need to disable preemption all the time such a routing entry is hold, which could be potentially quite long.
Well, in case of UDP, we call
ip_push_pending_frames()and this one does the increment of refcount (again). I was not considering avoiding the refcount hold we do when queing a skb in transmit queue, only during a short period of time. Oh well,ip_append_data()might sleep, so this cannot work...
Quoted 7 lines
I agree avoiding one refcount increment/decrement is probably not a huge gain, considering we *have* to do the increment, but when many cpus are using UDP send/receive in //, this might show a gain somehow.
So maybe we could make
ip_append_data()(or its callers) a litle bit smarter, avoiding increment/decrement if possible.
Here is a patch to remove one dst_hold()/dst_release() pair in UDP/RAW transmit path.
[PATCH] net: avoid a pair of dst_hold()/dst_release() in ip_append_data()
We can reduce pressure on dst entry refcount that slowdown UDP transmit path on SMP machines. This pressure is visible on RTP servers when delivering content to mediagateways, especially big ones, handling thousand of streams. Several cpus send UDP frames to the same destination, hence use the same dst entry.
This patch makes ip_append_data() eventually steal the refcount its callers had to take on the dst entry.
This doesnt avoid all refcounting, but still gives speedups on SMP, on UDP/RAW transmit path
---
include/net/ip.h | 2 +- net/ipv4/icmp.c | 8 ++++---- net/ipv4/ip_output.c | 11 ++++++++--- net/ipv4/raw.c | 2 +- net/ipv4/udp.c | 2 +- 5 files changed, 15 insertions(+), 10 deletions(-)
include/net/ip.h +1 -1
@@ -110,7 +110,7 @@ extern int ip_append_data(struct sock *sk, int odd, struct sk_buff *skb), void *from, int len, int protolen, struct ipcm_cookie *ipc, - struct rtable *rt, + struct rtable **rt, unsigned int flags);extern int ip_generic_getfrag (void *from,char *to,int offset,int len,int odd,struct sk_buff *skb); extern ssize_t ip_append_page(struct sock *sk, struct page *page,
net/ipv4/icmp.c +4 -4
@@ -321,12 +321,12 @@ static int icmp_glue_bits(void *from, char *to, int offset, int len, int odd, }static void icmp_push_reply (struct icmp_bxm *icmp_param, -struct ipcm_cookie *ipc,struct rtable *rt) +struct ipcm_cookie *ipc,struct rtable **rt) {struct sock *sk;struct sk_buff *skb; - sk =icmp_sk (dev_net (rt->u.dst.dev)); + sk =icmp_sk (dev_net ((*rt)->u.dst.dev)); if (ip_append_data(sk, icmp_glue_bits, icmp_param, icmp_param->data_len+icmp_param->head_len, icmp_param->head_len,
@@ -392,7 +392,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) }if (icmpv4_xrlim_allow (net, rt, icmp_param->data.icmph.type, icmp_param->data.icmph.code)) -icmp_push_reply (icmp_param, &ipc, rt); +icmp_push_reply (icmp_param, &ipc, &rt);ip_rt_put (rt); out_unlock:icmp_xmit_unlock (sk);
@@ -635,7 +635,7 @@ route_done: icmp_param.data_len = room; icmp_param.head_len =sizeof (struct icmphdr ); -icmp_push_reply (&icmp_param, &ipc, rt); +icmp_push_reply (&icmp_param, &ipc, &rt); ende:ip_rt_put (rt); out_unlock:
net/ipv4/ip_output.c +8 -3
@@ -778,7 +778,7 @@ int ip_append_data(struct sock *sk,int getfrag (void *from,char *to,int offset,int len,int odd,struct sk_buff *skb), void *from, int length, int transhdrlen, - struct ipcm_cookie *ipc, struct rtable *rt, + struct ipcm_cookie *ipc, struct rtable **rtp, unsigned int flags) { struct inet_sock *inet = inet_sk(sk);
@@ -793,6 +793,7 @@ int ip_append_data(struct sock *sk,int offset =0 ;unsigned int maxfraglen, fragheaderlen;int csummode =CHECKSUM_NONE ; +struct rtable *rt;if (flags&MSG_PROBE )return 0 ;
@@ -812,7 +813,11 @@ int ip_append_data(struct sock *sk, inet->cork.flags |=IPCORK_OPT ; inet->cork.addr = ipc->addr; } -dst_hold (&rt->u.dst); + rt = *rtp; +/* +* We steal reference to this route, caller should not release it +*/ + *rtp =NULL ; inet->cork.fragsize = mtu = inet->pmtudisc ==IP_PMTUDISC_PROBE ? rt->u.dst.dev->mtu :dst_mtu (rt->u.dst.path);
@@ -1391,7 +1396,7 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar sk->sk_protocol =ip_hdr (skb)->protocol; sk->sk_bound_dev_if = arg->bound_dev_if;ip_append_data (sk, ip_reply_glue_bits, arg->iov->iov_base, len,0 , - &ipc, rt,MSG_DONTWAIT ); + &ipc, &rt,MSG_DONTWAIT );if ((skb =skb_peek (&sk->sk_write_queue)) !=NULL ) {if (arg->csumoffset >=0 ) *((__sum16 *)skb_transport_header(skb) +
net/ipv4/raw.c +1 -1
@@ -572,7 +572,7 @@ back_from_confirm: ipc.addr = rt->rt_dst;lock_sock (sk); err =ip_append_data (sk, ip_generic_getfrag, msg->msg_iov, len,0 , - &ipc, rt, msg->msg_flags); + &ipc, &rt, msg->msg_flags);if (err)ip_flush_pending_frames (sk);else if (!(msg->msg_flags &MSG_MORE ))
net/ipv4/udp.c +1 -1
@@ -719,7 +719,7 @@ do_append_data: up->len += ulen; getfrag = is_udplite ? udplite_getfrag : ip_generic_getfrag; err =ip_append_data (sk, getfrag, msg->msg_iov, ulen, -sizeof (struct udphdr ), &ipc, rt, +sizeof (struct udphdr ), &ipc, &rt, corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);if (err)udp_flush_pending_frames (sk);
PATCH 1/1 net: avoid a pair of dst_hold()/dst_release() in ip_push_pending_frames()
We can reduce pressure on dst entry refcount that slowdown UDP transmit path on SMP machines. This pressure is visible on RTP servers when delivering content to mediagateways, especially big ones, handling thousand of streams. Several cpus send UDP frames to the same destination, hence use the same dst entry.
This patch makes ip_push_pending_frames() steal the refcount its callers had to take when filling inet->cork.dst.
This doesnt avoid all refcounting, but still gives speedups on SMP, on UDP/RAW transmit path.
---net/ipv4/ip_output.c | 7 ++++++-
1 files changed, 6 insertions(+), 1 deletion(-)
net/ipv4/ip_output.c +6 -1
@@ -1279,7 +1279,12 @@ int ip_push_pending_frames(struct sock *sk) skb->priority = sk->sk_priority; skb->mark = sk->sk_mark; - skb->dst =dst_clone (&rt->u.dst); +/* +* Steal rt from cork.dst to avoid a pair of atomic_inc/atomic_dec +* on dst refcount +*/ + inet->cork.dst =NULL ; + skb->dst = &rt->u.dst;if (iph->protocol ==IPPROTO_ICMP ) icmp_out_count(net, ((struct icmphdr *)
Re: [PATCH] net: avoid a pair of dst_hold()/dst_release() in ip_push_pending_frames()
From: Eric Dumazet <[email protected]>
Date: Mon, 24 Nov 2008 14:59:51 +0100
Quoted 6 lines
We can reduce pressure on dst entry refcount that slowdown UDP transmit path on SMP machines. This pressure is visible on RTP servers when delivering content to mediagateways, especially big ones, handling thousand of streams. Several cpus send UDP frames to the same destination, hence use the same dst entry.
Quoted 7 lines
This patch makes
ip_push_pending_frames()steal the refcount its callers had to take when fillinginet->cork.dst.This doesnt avoid all refcounting, but still gives speedups on SMP, on UDP/RAW transmit path.
Signed-off-by Eric Dumazet <[email protected]>
Applied.
David Miller
maintainerRe: [PATCH] net: avoid a pair of dst_hold()/dst_release() in ip_append_data()
In reply to Eric DumazetFrom: Eric Dumazet <[email protected]>
Date: Mon, 24 Nov 2008 12:24:53 +0100
Quoted 8 lines
[PATCH] net: avoid a pair of dst_hold()/dst_release() in
ip_append_data()We can reduce pressure on dst entry refcount that slowdown UDP transmit path on SMP machines. This pressure is visible on RTP servers when delivering content to mediagateways, especially big ones, handling thousand of streams. Several cpus send UDP frames to the same destination, hence use the same dst entry.
Quoted 7 lines
This patch makes
ip_append_data()eventually steal the refcount its callers had to take on the dst entry.This doesnt avoid all refcounting, but still gives speedups on SMP, on UDP/RAW transmit path
Signed-off-by Eric Dumazet <[email protected]>
Ok, this looks fine to me, thanks Eric. Although as you know I'm not a big fan of pass by reference arguments :-)
Thinking more I believe we can do similar tricks for all TCP transmit traffic.
Packets bound to sockets never outlive those sockets (and thus their cached routes) unless we skb_orphan().
The only not covered case is where the socket cached route is reset or changed. We could defer the dst put until the transmit queue reaches a certain point, kind of like a retransmit queue RCU :-)
Just some ideas...
Re: [PATCH] net: avoid a pair of dst_hold()/dst_release() in ip_append_data()
Quoted 2 lines
Thinking more I believe we can do similar tricks for all TCP transmit traffic.
Sounds reasonable.
Quoted 10 lines
Packets bound to sockets never outlive those sockets (and thus their cached routes) unless we
skb_orphan().The only not covered case is where the socket cached route is reset or changed. We could defer the dst put until the transmit queue reaches a certain point, kind of like a retransmit queue RCU :-)
Just some ideas...
netfilter makes it somewhat tricky, for compatibility you would need to reclone the route on the fly.
-Andi
Signature · 1 line
[email protected]
David Miller
maintainerFrom: Eric Dumazet <[email protected]>
Date: Mon, 24 Nov 2008 11:14:29 +0100
Quoted 2 lines
So maybe we could make
ip_append_data()(or its callers) a litle bit smarter, avoiding increment/decrement if possible.
These ideas are interesting but hard to make work.
I think the receive path has more chance of getting gains from this, to be honest.
One third (effectively) of TCP stream packets are ACKs and freed immediately. This means that the looked up route does not escape the packet receive path. So we could elide the counter increment in that case.
In fact, once we queue even TCP data, there is no need for that cached skb->dst route any longer.
So pretty much all TCP packets could avoid the dst refcounting on receive.
Quoted 16 lines · David Miller
From: Eric Dumazet <[email protected]>
Date: Mon, 24 Nov 2008 11:14:29 +0100Quoted 2 lines
So maybe we could make
ip_append_data()(or its callers) a litle bit smarter, avoiding increment/decrement if possible.These ideas are interesting but hard to make work.
I think the receive path has more chance of getting gains from this, to be honest.
One third (effectively) of TCP stream packets are ACKs and freed immediately. This means that the looked up route does not escape the packet receive path. So we could elide the counter increment in that case.
Quoted 5 lines
In fact, once we queue even TCP data, there is no need for that cached
skb->dstroute any longer.So pretty much all TCP packets could avoid the dst refcounting on receive.
Very interesting. So we could try the following path :
1) First try to release dst when queueing skb to various queues (UDP, TCP, ...) while its hot. Reader wont have to release it while its cold.
2) Check if we can handle the input path without any refcount dirtying ?
To make the transition easy, we could use a bit on skb to mark dst being not refcounted (ie no dst_release() should be done on it)
David Miller
maintainerFrom: Eric Dumazet <[email protected]>
Date: Tue, 25 Nov 2008 05:43:32 +0100
Quoted 11 lines
Very interesting. So we could try the following path :
1) First try to release dst when queueing skb to various queues (UDP, TCP, ...) while its hot. Reader wont have to release it while its cold.
2) Check if we can handle the input path without any refcount dirtying ?
To make the transition easy, we could use a bit on skb to mark dst being not refcounted (ie no
dst_release()should be done on it)
It is possible to make this self-auditing. For example, by using the usual trick where we encode a pointer in an unsigned long and use the low bits for states.
In the first step, make each skb->dst access go through some accessor inline function.
Next, audit the paths where skb->dst's can "escape" the pure packet input path. Add annotations, in the form of a inline function call, for these locations.
Also, audit the other locations where we enqueue into a socket queue and no longer care about the skb->dst, and annotate those with another inline function.
Finally, the initial skb->dst assignment in the input path doesn't grab a reference, but sets the low bit ("refcount pending") in the encoded skb->dst pointer. The skb->dst "escape" inline function performs the deferred refcount grab. And kfree_skb() is taught to not dst_release() on skb->dst's which have the low bit set.
Anyways, something like that.
PATCH 1/1 net: release skb->dst in sock_queue_rcv_skb()
Quoted 30 lines · David Miller
From: Eric Dumazet <[email protected]>
Date: Tue, 25 Nov 2008 05:43:32 +0100Quoted 11 lines
Very interesting. So we could try the following path :
1) First try to release dst when queueing skb to various queues (UDP, TCP, ...) while its hot. Reader wont have to release it while its cold.
2) Check if we can handle the input path without any refcount dirtying ?
To make the transition easy, we could use a bit on skb to mark dst being not refcounted (ie no
dst_release()should be done on it)It is possible to make this self-auditing. For example, by using the usual trick where we encode a pointer in an unsigned long and use the low bits for states.
In the first step, make each
skb->dstaccess go through some accessor inline function.Next, audit the paths where skb->dst's can "escape" the pure packet input path. Add annotations, in the form of a inline function call, for these locations.
Also, audit the other locations where we enqueue into a socket queue and no longer care about the
skb->dst, and annotate those with another inline function.
Quoted 8 lines
Finally, the initial
skb->dstassignment in the input path doesn't grab a reference, but sets the low bit ("refcount pending") in the encodedskb->dstpointer. Theskb->dst"escape" inline function performs the deferred refcount grab. Andkfree_skb()is taught to notdst_release()on skb->dst's which have the low bit set.Anyways, something like that.
I looked this stuff and found it would be difficult to not grab a reference (and more important not writing to dst) in input path.
ip_rcv_finish() calls ip_route_input()
and ip_route_input() calls dst_use(&rth->u.dst, jiffies);
static inline void dst_use(struct dst_entry *dst, unsigned long time)
{
dst_hold(dst);
dst->__use++;
dst->lastuse = time;
}Even if we avoid the refcount increment, I guess we need the lastuse assignement in order to keep dst in cache. Not sure about the role of __use field. Hum... for a tcp connection, dst refcount should already be pinned by a sk->sk_dst_cache. Maybe test refcount value, and if this value is > 1, dont take a reference. (given rcu_read_lock() is done before calling ip_rcv_finish())
In the meantime, what do you think of the following patch ?
[PATCH] net: release skb->dst in sock_queue_rcv_skb()
When queuing a skb to sk->sk_receive_queue, we can release its dst, not anymore needed.
Since current cpu did the dst_hold(), refcount is probably still hot int this cpu caches.
This avoids readers to access the original dst to decrement its refcount, possibly a long time after packet reception. This should speedup UDP and RAW receive path.
net/core/sock.c +5 -1
@@ -289,7 +289,11 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) skb->dev =NULL ;skb_set_owner_r (skb, sk); - +/* +* release dst right now while its hot +*/ +dst_release (skb->dst); + skb->dst =NULL ;/* Cache the SKB length before we tack it onto the receive * queue. Once it is added it no longer belongs to us and * may be freed by other threads of control pulling packets
David Miller
maintainerRe: [PATCH] net: release skb->dst in sock_queue_rcv_skb()
From: Eric Dumazet <[email protected]>
Date: Wed, 26 Nov 2008 01:00:30 +0100
Quoted 4 lines
Hum... for a tcp connection, dst refcount should already be pinned by a
sk->sk_dst_cache. Maybe test refcount value, and if this value is > 1, dont take a reference. (givenrcu_read_lock()is done before callingip_rcv_finish())
Input route is different from output route. sk->sk_dst_cache holds the output route. So I can't see how this can help here.
David Miller
maintainerRe: [PATCH] net: release skb->dst in sock_queue_rcv_skb()
In reply to Eric DumazetFrom: Eric Dumazet <[email protected]>
Date: Wed, 26 Nov 2008 01:00:30 +0100
Quoted 9 lines
In the meantime, what do you think of the following patch ?
[PATCH] net: release
skb->dstinsock_queue_rcv_skb()When queuing a skb to
sk->sk_receive_queue, we can release its dst, not anymore needed.
Since current cpu did thedst_hold(), refcount is probably still hot int this cpu caches.
Quoted 5 lines
This avoids readers to access the original dst to decrement its refcount, possibly a long time after packet reception. This should speedup UDP and RAW receive path.
Signed-off-by Eric Dumazet <[email protected]>
I guess the idea is that if we release quickly we'll not have to reget the cacheline in owned state.
I wonder if this might actually slightly hurt loads like tbench where we are banging on the refcnt constantly on every cpu anyways.
Can you do a quick check?
Re: [PATCH] net: release skb->dst in sock_queue_rcv_skb()
Quoted 12 lines · David Miller
From: Eric Dumazet <[email protected]>
Date: Wed, 26 Nov 2008 01:00:30 +0100Quoted 9 lines
In the meantime, what do you think of the following patch ?
[PATCH] net: release
skb->dstinsock_queue_rcv_skb()When queuing a skb to
sk->sk_receive_queue, we can release its dst, not anymore needed.
Since current cpu did thedst_hold(), refcount is probably still hot int this cpu caches.
Quoted 8 lines
Quoted 5 lines
This avoids readers to access the original dst to decrement its refcount, possibly a long time after packet reception. This should speedup UDP and RAW receive path.
Signed-off-by Eric Dumazet <[email protected]>I guess the idea is that if we release quickly we'll not have to reget the cacheline in owned state.
Yes, this is the idea.
Quoted 3 lines
I wonder if this might actually slightly hurt loads like tbench where we are banging on the refcnt constantly on every cpu anyways.
Yes, the only way to reduce the load on this case is not moving the increments/decrements : It could help on some machines, and hurt on others. In the long term, only reducing the number of dirtying can help the average.
Quoted 4 lines
Can you do a quick check?
Sure I can do a check :)
No impact at all on tbench, unless this bench can run in UDP mode :)
CPU: Core 2, speed 3000.1 MHz (estimated)
Counted CPU_CLK_UNHALTED events (Clock cycles when not halted) with a unit mask of 0x00 (Unhalted core cycles) count 100000
samples cum. samples % cum. % symbol name 599983 599983 11.2948 11.2948 copy_from_user 549349 1149332 10.3416 21.6364 ipt_do_table 245697 1395029 4.6253 26.2617 copy_to_user 221663 1616692 4.1729 30.4346 schedule 144888 1761580 2.7275 33.1621 tcp_sendmsg 136967 1898547 2.5784 35.7406 tcp_ack 115513 2014060 2.1746 37.9151 tcp_transmit_skb 99517 2113577 1.8734 39.7885 sysenter_past_esp 99438 2213015 1.8719 41.6605 ip_queue_xmit 98171 2311186 1.8481 43.5086 tcp_recvmsg 80763 2391949 1.5204 45.0290 __switch_to 79621 2471570 1.4989 46.5278 tcp_v4_rcv 77210 2548780 1.4535 47.9813 dst_release 69387 2618167 1.3062 49.2876 tcp_rcv_established 64709 2682876 1.2182 50.5057 __tcp_push_pending_frames 55223 2738099 1.0396 51.5453 lock_sock_nested 53754 2791853 1.0119 52.5572 sys_socketcall 50092 2841945 0.9430 53.5002 netif_receive_skb 49499 2891444 0.9318 54.4321 release_sock 47796 2939240 0.8998 55.3318 __inet_lookup_established 45162 2984402 0.8502 56.1820 update_curr 44895 3029297 0.8452 57.0272 ip_rcv 42945 3072242 0.8084 57.8356 dev_queue_xmit 42892 3115134 0.8075 58.6431 tcp_event_data_recv 42768 3157902 0.8051 59.4482 local_bh_enable 41555 3199457 0.7823 60.2305 netif_rx 38613 3238070 0.7269 60.9574 __alloc_skb 38016 3276086 0.7157 61.6730 ip_finish_output 36867 3312953 0.6940 62.3671 tcp_current_mss 36759 3349712 0.6920 63.0591 skb_release_data 35560 3385272 0.6694 63.7285 local_bh_enable_ip 34100 3419372 0.6419 64.3704 sock_recvmsg 33829 3453201 0.6368 65.0073 __kfree_skb 32949 3486150 0.6203 65.6275 sched_clock_cpu
David Miller
maintainerRe: [PATCH] net: release skb->dst in sock_queue_rcv_skb()
From: Eric Dumazet <[email protected]>
Date: Wed, 26 Nov 2008 08:39:30 +0100
Quoted 10 lines
Quoted 5 lines · David Miller
From: Eric Dumazet <[email protected]>
Date: Wed, 26 Nov 2008 01:00:30 +0100Can you do a quick check?
Sure I can do a check :)
No impact at all on tbench, unless this bench can run in UDP mode :)
Durrr, of course!
Patch applied, thanks Eric.
net-next: broken IP_PKTINFO [was Re: [PATCH] net: release skb->dst in sock_queue_rcv_skb()]
In reply to Eric DumazetHi,
Quoted 18 lines · Eric Dumazet
[PATCH] net: release
skb->dstinsock_queue_rcv_skb()When queuing a skb to
sk->sk_receive_queue, we can release its dst, not anymore needed.
Since current cpu did thedst_hold(), refcount is probably still hot int this cpu caches.This avoids readers to access the original dst to decrement its refcount, possibly a long time after packet reception. This should speedup UDP and RAW receive path.
Signed-off-by Eric Dumazet <[email protected]>plain text document attachment (sock_queue_rcv_skb.patch)
net/core/sock.c +0
-0
net/core/sock.c int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) · line 296
Quoted 9 lines
net/core/sock.c +5
-1@@ -289,7 +289,11 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) skb->dev =NULL ;skb_set_owner_r (skb, sk); - +/* +* release dst right now while its hot +*/ +dst_release (skb->dst); + skb->dst =NULL ;
IP_PKTINFO cmsg data is one post-queueing user:
static void ip_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb)
{
struct in_pktinfo info;
struct rtable *rt = skb->rtable; info.ipi_addr.s_addr = ip_hdr(skb)->daddr;
if (rt) {
info.ipi_ifindex = rt->rt_iif;
info.ipi_spec_dst.s_addr = rt->rt_spec_dst;
} else {
info.ipi_ifindex = 0;
info.ipi_spec_dst.s_addr = 0;
}
put_cmsg(msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);}
(i.e. skb->rtable is NULL at this point)
I'm seeing dnsmasq not working on net-next-2.6 because of this and reverting commit 7035560 makes things work as expected again.
Cheers,
Mark.
David Miller
maintainerRe: net-next: broken IP_PKTINFO
From: Mark McLoughlin <[email protected]>
Date: Wed, 17 Dec 2008 11:25:01 +0000
Quoted 3 lines · Eric Dumazet
Quoted 2 lines
[PATCH] net: release
skb->dstinsock_queue_rcv_skb()
...
Quoted 1 line
IP_PKTINFOcmsg data is one post-queueing user:
Eric, we'll need to rever this change I think.
Re: net-next: broken IP_PKTINFO
Quoted 10 lines · David Miller
From: Mark McLoughlin <[email protected]>
Date: Wed, 17 Dec 2008 11:25:01 +0000Quoted 3 lines · Eric Dumazet
Quoted 2 lines
[PATCH] net: release
skb->dstinsock_queue_rcv_skb()...
Quoted 1 line
IP_PKTINFOcmsg data is one post-queueing user:Eric, we'll need to rever this change I think.
I am afraid we have to revert it, yes.
META_COLLECTOR(int_rtiif) & META_COLLECTOR(int_rtclassid) in net/sched/em_meta.c also need rtable, I am not sure how it is used.
About ip_cmsg_recv_pktinfo() :
iif can be found in skb->iif instead of rt->rt_iif, but I am not sure about rt_spec_dst : Shouldnt we find it in ip_hdr(skb)->saddr ?
Do you know if we really need rtable in ip_cmsg_recv_pktinfo() ?
net/ipv4/ip_sockglue.c +2 -2
@@ -64,8 +64,8 @@ static void ip_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb) info.ipi_ifindex = rt->rt_iif; info.ipi_spec_dst.s_addr = rt->rt_spec_dst; }else { - info.ipi_ifindex =0 ; - info.ipi_spec_dst.s_addr =0 ; + info.ipi_ifindex = skb->iif; + info.ipi_spec_dst.s_addr =ip_hdr (skb)->saddr; }put_cmsg (msg,SOL_IP ,IP_PKTINFO ,sizeof (info), &info);
David Miller
maintainerRe: net-next: broken IP_PKTINFO
From: Eric Dumazet <[email protected]>
Date: Thu, 18 Dec 2008 06:59:26 +0100
Quoted 4 lines
Quoted 1 line · David Miller
Eric, we'll need to rever this change I think.
I am afraid we have to revert it, yes.
Done.
Quoted 6 lines
About
ip_cmsg_recv_pktinfo():iif can be found in
skb->iifinstead ofrt->rt_iif, but I am not sure aboutrt_spec_dst: Shouldnt we find it in ip_hdr(skb)->saddr ?Do you know if we really need rtable in
ip_cmsg_recv_pktinfo()?
I think we might, as these are routing attributes, which not necessarily match up with the values found in the SKB.
In fact, I do remember that "specific destination" has a very exact definition in the routing RFCs and it has to do with the matched route.
Conversely, in what situations (other than with the reverted patch applied, hehe) would the route not be attached here?