Commit 028e0a47 authored by Paolo Abeni's avatar Paolo Abeni Committed by David S. Miller

net: use indirect call wrappers at GRO transport layer

This avoids an indirect call in the receive path for TCP and UDP
packets. TCP takes precedence on UDP, so that we have a single
additional conditional in the common case.

When IPV6 is build as module, all gro symbols except UDPv6 are
builtin, while the latter belong to the ipv6 module, so we
need some special care.

v1 -> v2:
 - adapted to INDIRECT_CALL_ changes
v2 -> v3:
 - fix build issue with CONFIG_IPV6=m
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent aaa5d90b
...@@ -56,4 +56,11 @@ static inline void inet_ctl_sock_destroy(struct sock *sk) ...@@ -56,4 +56,11 @@ static inline void inet_ctl_sock_destroy(struct sock *sk)
sock_release(sk->sk_socket); sock_release(sk->sk_socket);
} }
#define indirect_call_gro_receive(f2, f1, cb, head, skb) \
({ \
unlikely(gro_recursion_inc_test(skb)) ? \
NAPI_GRO_CB(skb)->flush |= 1, NULL : \
INDIRECT_CALL_2(cb, f2, f1, head, skb); \
})
#endif #endif
...@@ -1385,6 +1385,10 @@ struct sk_buff *inet_gso_segment(struct sk_buff *skb, ...@@ -1385,6 +1385,10 @@ struct sk_buff *inet_gso_segment(struct sk_buff *skb,
} }
EXPORT_SYMBOL(inet_gso_segment); EXPORT_SYMBOL(inet_gso_segment);
INDIRECT_CALLABLE_DECLARE(struct sk_buff *tcp4_gro_receive(struct list_head *,
struct sk_buff *));
INDIRECT_CALLABLE_DECLARE(struct sk_buff *udp4_gro_receive(struct list_head *,
struct sk_buff *));
struct sk_buff *inet_gro_receive(struct list_head *head, struct sk_buff *skb) struct sk_buff *inet_gro_receive(struct list_head *head, struct sk_buff *skb)
{ {
const struct net_offload *ops; const struct net_offload *ops;
...@@ -1494,7 +1498,8 @@ struct sk_buff *inet_gro_receive(struct list_head *head, struct sk_buff *skb) ...@@ -1494,7 +1498,8 @@ struct sk_buff *inet_gro_receive(struct list_head *head, struct sk_buff *skb)
skb_gro_pull(skb, sizeof(*iph)); skb_gro_pull(skb, sizeof(*iph));
skb_set_transport_header(skb, skb_gro_offset(skb)); skb_set_transport_header(skb, skb_gro_offset(skb));
pp = call_gro_receive(ops->callbacks.gro_receive, head, skb); pp = indirect_call_gro_receive(tcp4_gro_receive, udp4_gro_receive,
ops->callbacks.gro_receive, head, skb);
out_unlock: out_unlock:
rcu_read_unlock(); rcu_read_unlock();
...@@ -1556,6 +1561,8 @@ int inet_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) ...@@ -1556,6 +1561,8 @@ int inet_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
return -EINVAL; return -EINVAL;
} }
INDIRECT_CALLABLE_DECLARE(int tcp4_gro_complete(struct sk_buff *, int));
INDIRECT_CALLABLE_DECLARE(int udp4_gro_complete(struct sk_buff *, int));
int inet_gro_complete(struct sk_buff *skb, int nhoff) int inet_gro_complete(struct sk_buff *skb, int nhoff)
{ {
__be16 newlen = htons(skb->len - nhoff); __be16 newlen = htons(skb->len - nhoff);
...@@ -1581,7 +1588,9 @@ int inet_gro_complete(struct sk_buff *skb, int nhoff) ...@@ -1581,7 +1588,9 @@ int inet_gro_complete(struct sk_buff *skb, int nhoff)
* because any hdr with option will have been flushed in * because any hdr with option will have been flushed in
* inet_gro_receive(). * inet_gro_receive().
*/ */
err = ops->callbacks.gro_complete(skb, nhoff + sizeof(*iph)); err = INDIRECT_CALL_2(ops->callbacks.gro_complete,
tcp4_gro_complete, udp4_gro_complete,
skb, nhoff + sizeof(*iph));
out_unlock: out_unlock:
rcu_read_unlock(); rcu_read_unlock();
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
* TCPv4 GSO/GRO support * TCPv4 GSO/GRO support
*/ */
#include <linux/indirect_call_wrapper.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <net/tcp.h> #include <net/tcp.h>
#include <net/protocol.h> #include <net/protocol.h>
...@@ -305,7 +306,8 @@ int tcp_gro_complete(struct sk_buff *skb) ...@@ -305,7 +306,8 @@ int tcp_gro_complete(struct sk_buff *skb)
} }
EXPORT_SYMBOL(tcp_gro_complete); EXPORT_SYMBOL(tcp_gro_complete);
static struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb) INDIRECT_CALLABLE_SCOPE
struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb)
{ {
/* Don't bother verifying checksum if we're going to flush anyway. */ /* Don't bother verifying checksum if we're going to flush anyway. */
if (!NAPI_GRO_CB(skb)->flush && if (!NAPI_GRO_CB(skb)->flush &&
...@@ -318,7 +320,7 @@ static struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff * ...@@ -318,7 +320,7 @@ static struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *
return tcp_gro_receive(head, skb); return tcp_gro_receive(head, skb);
} }
static int tcp4_gro_complete(struct sk_buff *skb, int thoff) INDIRECT_CALLABLE_SCOPE int tcp4_gro_complete(struct sk_buff *skb, int thoff)
{ {
const struct iphdr *iph = ip_hdr(skb); const struct iphdr *iph = ip_hdr(skb);
struct tcphdr *th = tcp_hdr(skb); struct tcphdr *th = tcp_hdr(skb);
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <net/udp.h> #include <net/udp.h>
#include <net/protocol.h> #include <net/protocol.h>
#include <net/inet_common.h>
static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb, static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
netdev_features_t features, netdev_features_t features,
...@@ -451,8 +452,8 @@ struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb, ...@@ -451,8 +452,8 @@ struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb,
} }
EXPORT_SYMBOL(udp_gro_receive); EXPORT_SYMBOL(udp_gro_receive);
static struct sk_buff *udp4_gro_receive(struct list_head *head, INDIRECT_CALLABLE_SCOPE
struct sk_buff *skb) struct sk_buff *udp4_gro_receive(struct list_head *head, struct sk_buff *skb)
{ {
struct udphdr *uh = udp_gro_udphdr(skb); struct udphdr *uh = udp_gro_udphdr(skb);
...@@ -525,7 +526,7 @@ int udp_gro_complete(struct sk_buff *skb, int nhoff, ...@@ -525,7 +526,7 @@ int udp_gro_complete(struct sk_buff *skb, int nhoff,
} }
EXPORT_SYMBOL(udp_gro_complete); EXPORT_SYMBOL(udp_gro_complete);
static int udp4_gro_complete(struct sk_buff *skb, int nhoff) INDIRECT_CALLABLE_SCOPE int udp4_gro_complete(struct sk_buff *skb, int nhoff)
{ {
const struct iphdr *iph = ip_hdr(skb); const struct iphdr *iph = ip_hdr(skb);
struct udphdr *uh = (struct udphdr *)(skb->data + nhoff); struct udphdr *uh = (struct udphdr *)(skb->data + nhoff);
......
...@@ -20,6 +20,23 @@ ...@@ -20,6 +20,23 @@
#include "ip6_offload.h" #include "ip6_offload.h"
/* All GRO functions are always builtin, except UDP over ipv6, which lays in
* ipv6 module, as it depends on UDPv6 lookup function, so we need special care
* when ipv6 is built as a module
*/
#if IS_BUILTIN(CONFIG_IPV6)
#define INDIRECT_CALL_L4(f, f2, f1, ...) INDIRECT_CALL_2(f, f2, f1, __VA_ARGS__)
#else
#define INDIRECT_CALL_L4(f, f2, f1, ...) INDIRECT_CALL_1(f, f2, __VA_ARGS__)
#endif
#define indirect_call_gro_receive_l4(f2, f1, cb, head, skb) \
({ \
unlikely(gro_recursion_inc_test(skb)) ? \
NAPI_GRO_CB(skb)->flush |= 1, NULL : \
INDIRECT_CALL_L4(cb, f2, f1, head, skb); \
})
static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto) static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto)
{ {
const struct net_offload *ops = NULL; const struct net_offload *ops = NULL;
...@@ -164,6 +181,10 @@ static int ipv6_exthdrs_len(struct ipv6hdr *iph, ...@@ -164,6 +181,10 @@ static int ipv6_exthdrs_len(struct ipv6hdr *iph,
return len; return len;
} }
INDIRECT_CALLABLE_DECLARE(struct sk_buff *tcp6_gro_receive(struct list_head *,
struct sk_buff *));
INDIRECT_CALLABLE_DECLARE(struct sk_buff *udp6_gro_receive(struct list_head *,
struct sk_buff *));
INDIRECT_CALLABLE_SCOPE struct sk_buff *ipv6_gro_receive(struct list_head *head, INDIRECT_CALLABLE_SCOPE struct sk_buff *ipv6_gro_receive(struct list_head *head,
struct sk_buff *skb) struct sk_buff *skb)
{ {
...@@ -260,7 +281,8 @@ INDIRECT_CALLABLE_SCOPE struct sk_buff *ipv6_gro_receive(struct list_head *head, ...@@ -260,7 +281,8 @@ INDIRECT_CALLABLE_SCOPE struct sk_buff *ipv6_gro_receive(struct list_head *head,
skb_gro_postpull_rcsum(skb, iph, nlen); skb_gro_postpull_rcsum(skb, iph, nlen);
pp = call_gro_receive(ops->callbacks.gro_receive, head, skb); pp = indirect_call_gro_receive_l4(tcp6_gro_receive, udp6_gro_receive,
ops->callbacks.gro_receive, head, skb);
out_unlock: out_unlock:
rcu_read_unlock(); rcu_read_unlock();
...@@ -301,6 +323,8 @@ static struct sk_buff *ip4ip6_gro_receive(struct list_head *head, ...@@ -301,6 +323,8 @@ static struct sk_buff *ip4ip6_gro_receive(struct list_head *head,
return inet_gro_receive(head, skb); return inet_gro_receive(head, skb);
} }
INDIRECT_CALLABLE_DECLARE(int tcp6_gro_complete(struct sk_buff *, int));
INDIRECT_CALLABLE_DECLARE(int udp6_gro_complete(struct sk_buff *, int));
INDIRECT_CALLABLE_SCOPE int ipv6_gro_complete(struct sk_buff *skb, int nhoff) INDIRECT_CALLABLE_SCOPE int ipv6_gro_complete(struct sk_buff *skb, int nhoff)
{ {
const struct net_offload *ops; const struct net_offload *ops;
...@@ -320,7 +344,8 @@ INDIRECT_CALLABLE_SCOPE int ipv6_gro_complete(struct sk_buff *skb, int nhoff) ...@@ -320,7 +344,8 @@ INDIRECT_CALLABLE_SCOPE int ipv6_gro_complete(struct sk_buff *skb, int nhoff)
if (WARN_ON(!ops || !ops->callbacks.gro_complete)) if (WARN_ON(!ops || !ops->callbacks.gro_complete))
goto out_unlock; goto out_unlock;
err = ops->callbacks.gro_complete(skb, nhoff); err = INDIRECT_CALL_L4(ops->callbacks.gro_complete, tcp6_gro_complete,
udp6_gro_complete, skb, nhoff);
out_unlock: out_unlock:
rcu_read_unlock(); rcu_read_unlock();
......
...@@ -9,14 +9,15 @@ ...@@ -9,14 +9,15 @@
* *
* TCPv6 GSO/GRO support * TCPv6 GSO/GRO support
*/ */
#include <linux/indirect_call_wrapper.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <net/protocol.h> #include <net/protocol.h>
#include <net/tcp.h> #include <net/tcp.h>
#include <net/ip6_checksum.h> #include <net/ip6_checksum.h>
#include "ip6_offload.h" #include "ip6_offload.h"
static struct sk_buff *tcp6_gro_receive(struct list_head *head, INDIRECT_CALLABLE_SCOPE
struct sk_buff *skb) struct sk_buff *tcp6_gro_receive(struct list_head *head, struct sk_buff *skb)
{ {
/* Don't bother verifying checksum if we're going to flush anyway. */ /* Don't bother verifying checksum if we're going to flush anyway. */
if (!NAPI_GRO_CB(skb)->flush && if (!NAPI_GRO_CB(skb)->flush &&
...@@ -29,7 +30,7 @@ static struct sk_buff *tcp6_gro_receive(struct list_head *head, ...@@ -29,7 +30,7 @@ static struct sk_buff *tcp6_gro_receive(struct list_head *head,
return tcp_gro_receive(head, skb); return tcp_gro_receive(head, skb);
} }
static int tcp6_gro_complete(struct sk_buff *skb, int thoff) INDIRECT_CALLABLE_SCOPE int tcp6_gro_complete(struct sk_buff *skb, int thoff)
{ {
const struct ipv6hdr *iph = ipv6_hdr(skb); const struct ipv6hdr *iph = ipv6_hdr(skb);
struct tcphdr *th = tcp_hdr(skb); struct tcphdr *th = tcp_hdr(skb);
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
*/ */
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/indirect_call_wrapper.h>
#include <net/protocol.h> #include <net/protocol.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#include <net/udp.h> #include <net/udp.h>
...@@ -114,8 +115,8 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, ...@@ -114,8 +115,8 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
return segs; return segs;
} }
static struct sk_buff *udp6_gro_receive(struct list_head *head, INDIRECT_CALLABLE_SCOPE
struct sk_buff *skb) struct sk_buff *udp6_gro_receive(struct list_head *head, struct sk_buff *skb)
{ {
struct udphdr *uh = udp_gro_udphdr(skb); struct udphdr *uh = udp_gro_udphdr(skb);
...@@ -142,7 +143,7 @@ static struct sk_buff *udp6_gro_receive(struct list_head *head, ...@@ -142,7 +143,7 @@ static struct sk_buff *udp6_gro_receive(struct list_head *head,
return NULL; return NULL;
} }
static int udp6_gro_complete(struct sk_buff *skb, int nhoff) INDIRECT_CALLABLE_SCOPE int udp6_gro_complete(struct sk_buff *skb, int nhoff)
{ {
const struct ipv6hdr *ipv6h = ipv6_hdr(skb); const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
struct udphdr *uh = (struct udphdr *)(skb->data + nhoff); struct udphdr *uh = (struct udphdr *)(skb->data + nhoff);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment