diff options
author | Eric Dumazet <edumazet@google.com> | 2018-03-31 16:16:25 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-04-01 14:08:21 -0400 |
commit | 694aba690de062cf27b28a5e56e7a5a7185b0a1c (patch) | |
tree | 1fbdc27ecff23dc1babcf50d1fd9884d84d800ee /net/ipv4/ip_output.c | |
parent | c07255020551cadae8bf903f2c5e1fcbad731bac (diff) |
ipv4: factorize sk_wmem_alloc updates done by __ip_append_data()
While testing my inet defrag changes, I found that the senders
could spend ~20% of cpu cycles in skb_set_owner_w() updating
sk->sk_wmem_alloc for every fragment they cook.
The solution to this problem is to use alloc_skb() instead
of sock_wmalloc() and manually perform a single sk_wmem_alloc change.
Similar change for IPv6 is provided in following patch.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/ip_output.c')
-rw-r--r-- | net/ipv4/ip_output.c | 17 |
1 files changed, 12 insertions, 5 deletions
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 66340ab750e6..94cacae76aca 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c | |||
@@ -876,6 +876,7 @@ static int __ip_append_data(struct sock *sk, | |||
876 | unsigned int maxfraglen, fragheaderlen, maxnonfragsize; | 876 | unsigned int maxfraglen, fragheaderlen, maxnonfragsize; |
877 | int csummode = CHECKSUM_NONE; | 877 | int csummode = CHECKSUM_NONE; |
878 | struct rtable *rt = (struct rtable *)cork->dst; | 878 | struct rtable *rt = (struct rtable *)cork->dst; |
879 | unsigned int wmem_alloc_delta = 0; | ||
879 | u32 tskey = 0; | 880 | u32 tskey = 0; |
880 | 881 | ||
881 | skb = skb_peek_tail(queue); | 882 | skb = skb_peek_tail(queue); |
@@ -971,11 +972,10 @@ alloc_new_skb: | |||
971 | (flags & MSG_DONTWAIT), &err); | 972 | (flags & MSG_DONTWAIT), &err); |
972 | } else { | 973 | } else { |
973 | skb = NULL; | 974 | skb = NULL; |
974 | if (refcount_read(&sk->sk_wmem_alloc) <= | 975 | if (refcount_read(&sk->sk_wmem_alloc) + wmem_alloc_delta <= |
975 | 2 * sk->sk_sndbuf) | 976 | 2 * sk->sk_sndbuf) |
976 | skb = sock_wmalloc(sk, | 977 | skb = alloc_skb(alloclen + hh_len + 15, |
977 | alloclen + hh_len + 15, 1, | 978 | sk->sk_allocation); |
978 | sk->sk_allocation); | ||
979 | if (unlikely(!skb)) | 979 | if (unlikely(!skb)) |
980 | err = -ENOBUFS; | 980 | err = -ENOBUFS; |
981 | } | 981 | } |
@@ -1033,6 +1033,11 @@ alloc_new_skb: | |||
1033 | /* | 1033 | /* |
1034 | * Put the packet on the pending queue. | 1034 | * Put the packet on the pending queue. |
1035 | */ | 1035 | */ |
1036 | if (!skb->destructor) { | ||
1037 | skb->destructor = sock_wfree; | ||
1038 | skb->sk = sk; | ||
1039 | wmem_alloc_delta += skb->truesize; | ||
1040 | } | ||
1036 | __skb_queue_tail(queue, skb); | 1041 | __skb_queue_tail(queue, skb); |
1037 | continue; | 1042 | continue; |
1038 | } | 1043 | } |
@@ -1079,12 +1084,13 @@ alloc_new_skb: | |||
1079 | skb->len += copy; | 1084 | skb->len += copy; |
1080 | skb->data_len += copy; | 1085 | skb->data_len += copy; |
1081 | skb->truesize += copy; | 1086 | skb->truesize += copy; |
1082 | refcount_add(copy, &sk->sk_wmem_alloc); | 1087 | wmem_alloc_delta += copy; |
1083 | } | 1088 | } |
1084 | offset += copy; | 1089 | offset += copy; |
1085 | length -= copy; | 1090 | length -= copy; |
1086 | } | 1091 | } |
1087 | 1092 | ||
1093 | refcount_add(wmem_alloc_delta, &sk->sk_wmem_alloc); | ||
1088 | return 0; | 1094 | return 0; |
1089 | 1095 | ||
1090 | error_efault: | 1096 | error_efault: |
@@ -1092,6 +1098,7 @@ error_efault: | |||
1092 | error: | 1098 | error: |
1093 | cork->length -= length; | 1099 | cork->length -= length; |
1094 | IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTDISCARDS); | 1100 | IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTDISCARDS); |
1101 | refcount_add(wmem_alloc_delta, &sk->sk_wmem_alloc); | ||
1095 | return err; | 1102 | return err; |
1096 | } | 1103 | } |
1097 | 1104 | ||