Skip to content

Commit 68c3316

Browse files
Pravin B Shelardavem330
authored andcommitted
v4 GRE: Add TCP segmentation offload for GRE
Following patch adds GRE protocol offload handler so that skb_gso_segment() can segment GRE packets. SKB GSO CB is added to keep track of total header length so that skb_segment can push entire header. e.g. in case of GRE, skb_segment need to push inner and outer headers to every segment. New NETIF_F_GRE_GSO feature is added for devices which support HW GRE TSO offload. Currently none of devices support it therefore GRE GSO always fall backs to software GSO. [ Compute pkt_len before ip_local_out() invocation. -DaveM ] Signed-off-by: Pravin B Shelar <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 05e8ef4 commit 68c3316

File tree

12 files changed

+226
-11
lines changed

12 files changed

+226
-11
lines changed

include/linux/netdev_features.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ enum {
4141
NETIF_F_TSO_ECN_BIT, /* ... TCP ECN support */
4242
NETIF_F_TSO6_BIT, /* ... TCPv6 segmentation */
4343
NETIF_F_FSO_BIT, /* ... FCoE segmentation */
44-
NETIF_F_GSO_RESERVED1, /* ... free (fill GSO_MASK to 8 bits) */
44+
NETIF_F_GSO_GRE_BIT, /* ... GRE with TSO */
4545
/**/NETIF_F_GSO_LAST, /* [can't be last bit, see GSO_MASK] */
4646
NETIF_F_GSO_RESERVED2 /* ... free (fill GSO_MASK to 8 bits) */
4747
= NETIF_F_GSO_LAST,
@@ -102,6 +102,7 @@ enum {
102102
#define NETIF_F_VLAN_CHALLENGED __NETIF_F(VLAN_CHALLENGED)
103103
#define NETIF_F_RXFCS __NETIF_F(RXFCS)
104104
#define NETIF_F_RXALL __NETIF_F(RXALL)
105+
#define NETIF_F_GRE_GSO __NETIF_F(GSO_GRE)
105106

106107
/* Features valid for ethtool to change */
107108
/* = all defined minus driver/device-class-related */

include/linux/skbuff.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,8 @@ enum {
314314
SKB_GSO_TCPV6 = 1 << 4,
315315

316316
SKB_GSO_FCOE = 1 << 5,
317+
318+
SKB_GSO_GRE = 1 << 6,
317319
};
318320

319321
#if BITS_PER_LONG > 32
@@ -2732,6 +2734,21 @@ static inline struct sec_path *skb_sec_path(struct sk_buff *skb)
27322734
}
27332735
#endif
27342736

2737+
/* Keeps track of mac header offset relative to skb->head.
2738+
* It is useful for TSO of Tunneling protocol. e.g. GRE.
2739+
* For non-tunnel skb it points to skb_mac_header() and for
2740+
* tunnel skb it points to outer mac header. */
2741+
struct skb_gso_cb {
2742+
int mac_offset;
2743+
};
2744+
#define SKB_GSO_CB(skb) ((struct skb_gso_cb *)(skb)->cb)
2745+
2746+
static inline int skb_tnl_header_len(const struct sk_buff *inner_skb)
2747+
{
2748+
return (skb_mac_header(inner_skb) - inner_skb->head) -
2749+
SKB_GSO_CB(inner_skb)->mac_offset;
2750+
}
2751+
27352752
static inline bool skb_is_gso(const struct sk_buff *skb)
27362753
{
27372754
return skb_shinfo(skb)->gso_size;

net/core/dev.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2413,6 +2413,7 @@ struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
24132413
return ERR_PTR(err);
24142414
}
24152415

2416+
SKB_GSO_CB(skb)->mac_offset = skb_headroom(skb);
24162417
skb_reset_mac_header(skb);
24172418
skb_reset_mac_len(skb);
24182419

net/core/ethtool.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN]
7777
[NETIF_F_TSO_ECN_BIT] = "tx-tcp-ecn-segmentation",
7878
[NETIF_F_TSO6_BIT] = "tx-tcp6-segmentation",
7979
[NETIF_F_FSO_BIT] = "tx-fcoe-segmentation",
80+
[NETIF_F_GSO_GRE_BIT] = "tx-gre-segmentation",
8081

8182
[NETIF_F_FCOE_CRC_BIT] = "tx-checksum-fcoe-crc",
8283
[NETIF_F_SCTP_CSUM_BIT] = "tx-checksum-sctp",

net/core/skbuff.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2738,6 +2738,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features)
27382738
unsigned int mss = skb_shinfo(skb)->gso_size;
27392739
unsigned int doffset = skb->data - skb_mac_header(skb);
27402740
unsigned int offset = doffset;
2741+
unsigned int tnl_hlen = skb_tnl_header_len(skb);
27412742
unsigned int headroom;
27422743
unsigned int len;
27432744
int sg = !!(features & NETIF_F_SG);
@@ -2814,7 +2815,10 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features)
28142815
skb_set_network_header(nskb, skb->mac_len);
28152816
nskb->transport_header = (nskb->network_header +
28162817
skb_network_header_len(skb));
2817-
skb_copy_from_linear_data(skb, nskb->data, doffset);
2818+
2819+
skb_copy_from_linear_data_offset(skb, -tnl_hlen,
2820+
nskb->data - tnl_hlen,
2821+
doffset + tnl_hlen);
28182822

28192823
if (fskb != skb_shinfo(skb)->frag_list)
28202824
continue;

net/ipv4/af_inet.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,6 +1287,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
12871287
SKB_GSO_UDP |
12881288
SKB_GSO_DODGY |
12891289
SKB_GSO_TCP_ECN |
1290+
SKB_GSO_GRE |
12901291
0)))
12911292
goto out;
12921293

net/ipv4/gre.c

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,19 @@
1919
#include <linux/in.h>
2020
#include <linux/ip.h>
2121
#include <linux/netdevice.h>
22+
#include <linux/if_tunnel.h>
2223
#include <linux/spinlock.h>
2324
#include <net/protocol.h>
2425
#include <net/gre.h>
2526

2627

2728
static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly;
2829
static DEFINE_SPINLOCK(gre_proto_lock);
30+
struct gre_base_hdr {
31+
__be16 flags;
32+
__be16 protocol;
33+
};
34+
#define GRE_HEADER_SECTION 4
2935

3036
int gre_add_protocol(const struct gre_protocol *proto, u8 version)
3137
{
@@ -112,12 +118,117 @@ static void gre_err(struct sk_buff *skb, u32 info)
112118
rcu_read_unlock();
113119
}
114120

121+
static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
122+
netdev_features_t features)
123+
{
124+
struct sk_buff *segs = ERR_PTR(-EINVAL);
125+
netdev_features_t enc_features;
126+
int ghl = GRE_HEADER_SECTION;
127+
struct gre_base_hdr *greh;
128+
int mac_len = skb->mac_len;
129+
int tnl_hlen;
130+
bool csum;
131+
132+
if (unlikely(skb_shinfo(skb)->gso_type &
133+
~(SKB_GSO_TCPV4 |
134+
SKB_GSO_TCPV6 |
135+
SKB_GSO_UDP |
136+
SKB_GSO_DODGY |
137+
SKB_GSO_TCP_ECN |
138+
SKB_GSO_GRE)))
139+
goto out;
140+
141+
if (unlikely(!pskb_may_pull(skb, sizeof(*greh))))
142+
goto out;
143+
144+
greh = (struct gre_base_hdr *)skb_transport_header(skb);
145+
146+
if (greh->flags & GRE_KEY)
147+
ghl += GRE_HEADER_SECTION;
148+
if (greh->flags & GRE_SEQ)
149+
ghl += GRE_HEADER_SECTION;
150+
if (greh->flags & GRE_CSUM) {
151+
ghl += GRE_HEADER_SECTION;
152+
csum = true;
153+
} else
154+
csum = false;
155+
156+
/* setup inner skb. */
157+
if (greh->protocol == htons(ETH_P_TEB)) {
158+
struct ethhdr *eth = eth_hdr(skb);
159+
skb->protocol = eth->h_proto;
160+
} else {
161+
skb->protocol = greh->protocol;
162+
}
163+
164+
skb->encapsulation = 0;
165+
166+
if (unlikely(!pskb_may_pull(skb, ghl)))
167+
goto out;
168+
__skb_pull(skb, ghl);
169+
skb_reset_mac_header(skb);
170+
skb_set_network_header(skb, skb_inner_network_offset(skb));
171+
skb->mac_len = skb_inner_network_offset(skb);
172+
173+
/* segment inner packet. */
174+
enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
175+
segs = skb_mac_gso_segment(skb, enc_features);
176+
if (!segs || IS_ERR(segs))
177+
goto out;
178+
179+
skb = segs;
180+
tnl_hlen = skb_tnl_header_len(skb);
181+
do {
182+
__skb_push(skb, ghl);
183+
if (csum) {
184+
__be32 *pcsum;
185+
186+
if (skb_has_shared_frag(skb)) {
187+
int err;
188+
189+
err = __skb_linearize(skb);
190+
if (err) {
191+
kfree_skb(segs);
192+
segs = ERR_PTR(err);
193+
goto out;
194+
}
195+
}
196+
197+
greh = (struct gre_base_hdr *)(skb->data);
198+
pcsum = (__be32 *)(greh + 1);
199+
*pcsum = 0;
200+
*(__sum16 *)pcsum = csum_fold(skb_checksum(skb, 0, skb->len, 0));
201+
}
202+
__skb_push(skb, tnl_hlen - ghl);
203+
204+
skb_reset_mac_header(skb);
205+
skb_set_network_header(skb, mac_len);
206+
skb->mac_len = mac_len;
207+
} while ((skb = skb->next));
208+
out:
209+
return segs;
210+
}
211+
212+
static int gre_gso_send_check(struct sk_buff *skb)
213+
{
214+
if (!skb->encapsulation)
215+
return -EINVAL;
216+
return 0;
217+
}
218+
115219
static const struct net_protocol net_gre_protocol = {
116220
.handler = gre_rcv,
117221
.err_handler = gre_err,
118222
.netns_ok = 1,
119223
};
120224

225+
static const struct net_offload gre_offload = {
226+
.callbacks = {
227+
.gso_send_check = gre_gso_send_check,
228+
.gso_segment = gre_gso_segment,
229+
},
230+
};
231+
121232
static int __init gre_init(void)
122233
{
123234
pr_info("GRE over IPv4 demultiplexor driver\n");
@@ -127,11 +238,18 @@ static int __init gre_init(void)
127238
return -EAGAIN;
128239
}
129240

241+
if (inet_add_offload(&gre_offload, IPPROTO_GRE)) {
242+
pr_err("can't add protocol offload\n");
243+
inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
244+
return -EAGAIN;
245+
}
246+
130247
return 0;
131248
}
132249

133250
static void __exit gre_exit(void)
134251
{
252+
inet_del_offload(&gre_offload, IPPROTO_GRE);
135253
inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
136254
}
137255

net/ipv4/ip_gre.c

Lines changed: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -735,8 +735,33 @@ static int ipgre_rcv(struct sk_buff *skb)
735735
return 0;
736736
}
737737

738+
static struct sk_buff *handle_offloads(struct sk_buff *skb)
739+
{
740+
int err;
741+
742+
if (skb_is_gso(skb)) {
743+
err = skb_unclone(skb, GFP_ATOMIC);
744+
if (unlikely(err))
745+
goto error;
746+
skb_shinfo(skb)->gso_type |= SKB_GSO_GRE;
747+
return skb;
748+
} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
749+
err = skb_checksum_help(skb);
750+
if (unlikely(err))
751+
goto error;
752+
}
753+
skb->ip_summed = CHECKSUM_NONE;
754+
755+
return skb;
756+
757+
error:
758+
kfree_skb(skb);
759+
return ERR_PTR(err);
760+
}
761+
738762
static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
739763
{
764+
struct pcpu_tstats *tstats = this_cpu_ptr(dev->tstats);
740765
struct ip_tunnel *tunnel = netdev_priv(dev);
741766
const struct iphdr *old_iph;
742767
const struct iphdr *tiph;
@@ -751,10 +776,19 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
751776
__be32 dst;
752777
int mtu;
753778
u8 ttl;
779+
int err;
780+
int pkt_len;
754781

755-
if (skb->ip_summed == CHECKSUM_PARTIAL &&
756-
skb_checksum_help(skb))
757-
goto tx_error;
782+
skb = handle_offloads(skb);
783+
if (IS_ERR(skb)) {
784+
dev->stats.tx_dropped++;
785+
return NETDEV_TX_OK;
786+
}
787+
788+
if (!skb->encapsulation) {
789+
skb_reset_inner_headers(skb);
790+
skb->encapsulation = 1;
791+
}
758792

759793
old_iph = ip_hdr(skb);
760794

@@ -855,7 +889,8 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
855889
if (skb->protocol == htons(ETH_P_IP)) {
856890
df |= (old_iph->frag_off&htons(IP_DF));
857891

858-
if ((old_iph->frag_off&htons(IP_DF)) &&
892+
if (!skb_is_gso(skb) &&
893+
(old_iph->frag_off&htons(IP_DF)) &&
859894
mtu < ntohs(old_iph->tot_len)) {
860895
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
861896
ip_rt_put(rt);
@@ -875,7 +910,9 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
875910
}
876911
}
877912

878-
if (mtu >= IPV6_MIN_MTU && mtu < skb->len - tunnel->hlen + gre_hlen) {
913+
if (!skb_is_gso(skb) &&
914+
mtu >= IPV6_MIN_MTU &&
915+
mtu < skb->len - tunnel->hlen + gre_hlen) {
879916
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
880917
ip_rt_put(rt);
881918
goto tx_error;
@@ -936,6 +973,7 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
936973
iph->daddr = fl4.daddr;
937974
iph->saddr = fl4.saddr;
938975
iph->ttl = ttl;
976+
iph->id = 0;
939977

940978
if (ttl == 0) {
941979
if (skb->protocol == htons(ETH_P_IP))
@@ -964,17 +1002,39 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
9641002
*ptr = tunnel->parms.o_key;
9651003
ptr--;
9661004
}
967-
if (tunnel->parms.o_flags&GRE_CSUM) {
1005+
/* Skip GRE checksum if skb is getting offloaded. */
1006+
if (!(skb_shinfo(skb)->gso_type & SKB_GSO_GRE) &&
1007+
(tunnel->parms.o_flags&GRE_CSUM)) {
9681008
int offset = skb_transport_offset(skb);
9691009

1010+
if (skb_has_shared_frag(skb)) {
1011+
err = __skb_linearize(skb);
1012+
if (err) {
1013+
ip_rt_put(rt);
1014+
goto tx_error;
1015+
}
1016+
}
1017+
9701018
*ptr = 0;
9711019
*(__sum16 *)ptr = csum_fold(skb_checksum(skb, offset,
9721020
skb->len - offset,
9731021
0));
9741022
}
9751023
}
9761024

977-
iptunnel_xmit(skb, dev);
1025+
nf_reset(skb);
1026+
1027+
pkt_len = skb->len - skb_transport_offset(skb);
1028+
err = ip_local_out(skb);
1029+
if (likely(net_xmit_eval(err) == 0)) {
1030+
u64_stats_update_begin(&tstats->syncp);
1031+
tstats->tx_bytes += pkt_len;
1032+
tstats->tx_packets++;
1033+
u64_stats_update_end(&tstats->syncp);
1034+
} else {
1035+
dev->stats.tx_errors++;
1036+
dev->stats.tx_aborted_errors++;
1037+
}
9781038
return NETDEV_TX_OK;
9791039

9801040
#if IS_ENABLED(CONFIG_IPV6)
@@ -1044,6 +1104,11 @@ static int ipgre_tunnel_bind_dev(struct net_device *dev)
10441104
mtu = 68;
10451105

10461106
tunnel->hlen = addend;
1107+
/* TCP offload with GRE SEQ is not supported. */
1108+
if (!(tunnel->parms.o_flags & GRE_SEQ)) {
1109+
dev->features |= NETIF_F_GSO_SOFTWARE;
1110+
dev->hw_features |= NETIF_F_GSO_SOFTWARE;
1111+
}
10471112

10481113
return mtu;
10491114
}
@@ -1593,6 +1658,9 @@ static void ipgre_tap_setup(struct net_device *dev)
15931658

15941659
dev->iflink = 0;
15951660
dev->features |= NETIF_F_NETNS_LOCAL;
1661+
1662+
dev->features |= GRE_FEATURES;
1663+
dev->hw_features |= GRE_FEATURES;
15961664
}
15971665

15981666
static int ipgre_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[],

0 commit comments

Comments
 (0)