aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoseph Gasparakis <joseph.gasparakis@intel.com>2012-12-07 09:14:14 -0500
committerDavid S. Miller <davem@davemloft.net>2012-12-09 00:20:28 -0500
commit6a674e9c75b17e7a88ff15b3c2e269eed54f7cfb (patch)
treecff48ec155b5c7d8a595d66cc7dc0216a91f9ec3
parent9ecb9aabaf634677c77af467f4e3028b09d7bcda (diff)
net: Add support for hardware-offloaded encapsulation
This patch adds support in the kernel for offloading in the NIC Tx and Rx checksumming for encapsulated packets (such as VXLAN and IP GRE). For Tx encapsulation offload, the driver will need to set the right bits in netdev->hw_enc_features. The protocol driver will have to set the skb->encapsulation bit and populate the inner headers, so the NIC driver will use those inner headers to calculate the csum in hardware. For Rx encapsulation offload, the driver will need to set again the skb->encapsulation flag and the skb->ip_csum to CHECKSUM_UNNECESSARY. In that case the protocol driver should push the decapsulated packet up to the stack, again with CHECKSUM_UNNECESSARY. In ether case, the protocol driver should set the skb->encapsulation flag back to zero. Finally the protocol driver should have NETIF_F_RXCSUM flag set in its features. Signed-off-by: Joseph Gasparakis <joseph.gasparakis@intel.com> Signed-off-by: Peter P Waskiewicz Jr <peter.p.waskiewicz.jr@intel.com> Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/linux/ip.h5
-rw-r--r--include/linux/ipv6.h5
-rw-r--r--include/linux/netdevice.h6
-rw-r--r--include/linux/skbuff.h95
-rw-r--r--include/linux/tcp.h10
-rw-r--r--include/linux/udp.h5
-rw-r--r--net/core/skbuff.c9
7 files changed, 134 insertions, 1 deletions
diff --git a/include/linux/ip.h b/include/linux/ip.h
index 58b82a22a52b..492bc6513533 100644
--- a/include/linux/ip.h
+++ b/include/linux/ip.h
@@ -25,6 +25,11 @@ static inline struct iphdr *ip_hdr(const struct sk_buff *skb)
25 return (struct iphdr *)skb_network_header(skb); 25 return (struct iphdr *)skb_network_header(skb);
26} 26}
27 27
28static inline struct iphdr *inner_ip_hdr(const struct sk_buff *skb)
29{
30 return (struct iphdr *)skb_inner_network_header(skb);
31}
32
28static inline struct iphdr *ipip_hdr(const struct sk_buff *skb) 33static inline struct iphdr *ipip_hdr(const struct sk_buff *skb)
29{ 34{
30 return (struct iphdr *)skb_transport_header(skb); 35 return (struct iphdr *)skb_transport_header(skb);
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 12729e966dc9..faed1e357dd6 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -67,6 +67,11 @@ static inline struct ipv6hdr *ipv6_hdr(const struct sk_buff *skb)
67 return (struct ipv6hdr *)skb_network_header(skb); 67 return (struct ipv6hdr *)skb_network_header(skb);
68} 68}
69 69
70static inline struct ipv6hdr *inner_ipv6_hdr(const struct sk_buff *skb)
71{
72 return (struct ipv6hdr *)skb_inner_network_header(skb);
73}
74
70static inline struct ipv6hdr *ipipv6_hdr(const struct sk_buff *skb) 75static inline struct ipv6hdr *ipipv6_hdr(const struct sk_buff *skb)
71{ 76{
72 return (struct ipv6hdr *)skb_transport_header(skb); 77 return (struct ipv6hdr *)skb_transport_header(skb);
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 18c5dc98f6dc..c6a14d4d1396 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1063,6 +1063,12 @@ struct net_device {
1063 netdev_features_t wanted_features; 1063 netdev_features_t wanted_features;
1064 /* mask of features inheritable by VLAN devices */ 1064 /* mask of features inheritable by VLAN devices */
1065 netdev_features_t vlan_features; 1065 netdev_features_t vlan_features;
1066 /* mask of features inherited by encapsulating devices
1067 * This field indicates what encapsulation offloads
1068 * the hardware is capable of doing, and drivers will
1069 * need to set them appropriately.
1070 */
1071 netdev_features_t hw_enc_features;
1066 1072
1067 /* Interface index. Unique device identifier */ 1073 /* Interface index. Unique device identifier */
1068 int ifindex; 1074 int ifindex;
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index f2af494330ab..320e976d5ab8 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -376,6 +376,8 @@ typedef unsigned char *sk_buff_data_t;
376 * @mark: Generic packet mark 376 * @mark: Generic packet mark
377 * @dropcount: total number of sk_receive_queue overflows 377 * @dropcount: total number of sk_receive_queue overflows
378 * @vlan_tci: vlan tag control information 378 * @vlan_tci: vlan tag control information
379 * @inner_transport_header: Inner transport layer header (encapsulation)
380 * @inner_network_header: Network layer header (encapsulation)
379 * @transport_header: Transport layer header 381 * @transport_header: Transport layer header
380 * @network_header: Network layer header 382 * @network_header: Network layer header
381 * @mac_header: Link layer header 383 * @mac_header: Link layer header
@@ -471,7 +473,13 @@ struct sk_buff {
471 __u8 wifi_acked:1; 473 __u8 wifi_acked:1;
472 __u8 no_fcs:1; 474 __u8 no_fcs:1;
473 __u8 head_frag:1; 475 __u8 head_frag:1;
474 /* 8/10 bit hole (depending on ndisc_nodetype presence) */ 476 /* Encapsulation protocol and NIC drivers should use
477 * this flag to indicate to each other if the skb contains
478 * encapsulated packet or not and maybe use the inner packet
479 * headers if needed
480 */
481 __u8 encapsulation:1;
482 /* 7/9 bit hole (depending on ndisc_nodetype presence) */
475 kmemcheck_bitfield_end(flags2); 483 kmemcheck_bitfield_end(flags2);
476 484
477#ifdef CONFIG_NET_DMA 485#ifdef CONFIG_NET_DMA
@@ -486,6 +494,8 @@ struct sk_buff {
486 __u32 avail_size; 494 __u32 avail_size;
487 }; 495 };
488 496
497 sk_buff_data_t inner_transport_header;
498 sk_buff_data_t inner_network_header;
489 sk_buff_data_t transport_header; 499 sk_buff_data_t transport_header;
490 sk_buff_data_t network_header; 500 sk_buff_data_t network_header;
491 sk_buff_data_t mac_header; 501 sk_buff_data_t mac_header;
@@ -1435,12 +1445,53 @@ static inline void skb_reserve(struct sk_buff *skb, int len)
1435 skb->tail += len; 1445 skb->tail += len;
1436} 1446}
1437 1447
1448static inline void skb_reset_inner_headers(struct sk_buff *skb)
1449{
1450 skb->inner_network_header = skb->network_header;
1451 skb->inner_transport_header = skb->transport_header;
1452}
1453
1438static inline void skb_reset_mac_len(struct sk_buff *skb) 1454static inline void skb_reset_mac_len(struct sk_buff *skb)
1439{ 1455{
1440 skb->mac_len = skb->network_header - skb->mac_header; 1456 skb->mac_len = skb->network_header - skb->mac_header;
1441} 1457}
1442 1458
1443#ifdef NET_SKBUFF_DATA_USES_OFFSET 1459#ifdef NET_SKBUFF_DATA_USES_OFFSET
1460static inline unsigned char *skb_inner_transport_header(const struct sk_buff
1461 *skb)
1462{
1463 return skb->head + skb->inner_transport_header;
1464}
1465
1466static inline void skb_reset_inner_transport_header(struct sk_buff *skb)
1467{
1468 skb->inner_transport_header = skb->data - skb->head;
1469}
1470
1471static inline void skb_set_inner_transport_header(struct sk_buff *skb,
1472 const int offset)
1473{
1474 skb_reset_inner_transport_header(skb);
1475 skb->inner_transport_header += offset;
1476}
1477
1478static inline unsigned char *skb_inner_network_header(const struct sk_buff *skb)
1479{
1480 return skb->head + skb->inner_network_header;
1481}
1482
1483static inline void skb_reset_inner_network_header(struct sk_buff *skb)
1484{
1485 skb->inner_network_header = skb->data - skb->head;
1486}
1487
1488static inline void skb_set_inner_network_header(struct sk_buff *skb,
1489 const int offset)
1490{
1491 skb_reset_inner_network_header(skb);
1492 skb->inner_network_header += offset;
1493}
1494
1444static inline unsigned char *skb_transport_header(const struct sk_buff *skb) 1495static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
1445{ 1496{
1446 return skb->head + skb->transport_header; 1497 return skb->head + skb->transport_header;
@@ -1496,6 +1547,38 @@ static inline void skb_set_mac_header(struct sk_buff *skb, const int offset)
1496} 1547}
1497 1548
1498#else /* NET_SKBUFF_DATA_USES_OFFSET */ 1549#else /* NET_SKBUFF_DATA_USES_OFFSET */
1550static inline unsigned char *skb_inner_transport_header(const struct sk_buff
1551 *skb)
1552{
1553 return skb->inner_transport_header;
1554}
1555
1556static inline void skb_reset_inner_transport_header(struct sk_buff *skb)
1557{
1558 skb->inner_transport_header = skb->data;
1559}
1560
1561static inline void skb_set_inner_transport_header(struct sk_buff *skb,
1562 const int offset)
1563{
1564 skb->inner_transport_header = skb->data + offset;
1565}
1566
1567static inline unsigned char *skb_inner_network_header(const struct sk_buff *skb)
1568{
1569 return skb->inner_network_header;
1570}
1571
1572static inline void skb_reset_inner_network_header(struct sk_buff *skb)
1573{
1574 skb->inner_network_header = skb->data;
1575}
1576
1577static inline void skb_set_inner_network_header(struct sk_buff *skb,
1578 const int offset)
1579{
1580 skb->inner_network_header = skb->data + offset;
1581}
1499 1582
1500static inline unsigned char *skb_transport_header(const struct sk_buff *skb) 1583static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
1501{ 1584{
@@ -1574,11 +1657,21 @@ static inline u32 skb_network_header_len(const struct sk_buff *skb)
1574 return skb->transport_header - skb->network_header; 1657 return skb->transport_header - skb->network_header;
1575} 1658}
1576 1659
1660static inline u32 skb_inner_network_header_len(const struct sk_buff *skb)
1661{
1662 return skb->inner_transport_header - skb->inner_network_header;
1663}
1664
1577static inline int skb_network_offset(const struct sk_buff *skb) 1665static inline int skb_network_offset(const struct sk_buff *skb)
1578{ 1666{
1579 return skb_network_header(skb) - skb->data; 1667 return skb_network_header(skb) - skb->data;
1580} 1668}
1581 1669
1670static inline int skb_inner_network_offset(const struct sk_buff *skb)
1671{
1672 return skb_inner_network_header(skb) - skb->data;
1673}
1674
1582static inline int pskb_network_may_pull(struct sk_buff *skb, unsigned int len) 1675static inline int pskb_network_may_pull(struct sk_buff *skb, unsigned int len)
1583{ 1676{
1584 return pskb_may_pull(skb, skb_network_offset(skb) + len); 1677 return pskb_may_pull(skb, skb_network_offset(skb) + len);
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index 60b7aac15e0e..4e1d2283e3cc 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -35,6 +35,16 @@ static inline unsigned int tcp_hdrlen(const struct sk_buff *skb)
35 return tcp_hdr(skb)->doff * 4; 35 return tcp_hdr(skb)->doff * 4;
36} 36}
37 37
38static inline struct tcphdr *inner_tcp_hdr(const struct sk_buff *skb)
39{
40 return (struct tcphdr *)skb_inner_transport_header(skb);
41}
42
43static inline unsigned int inner_tcp_hdrlen(const struct sk_buff *skb)
44{
45 return inner_tcp_hdr(skb)->doff * 4;
46}
47
38static inline unsigned int tcp_optlen(const struct sk_buff *skb) 48static inline unsigned int tcp_optlen(const struct sk_buff *skb)
39{ 49{
40 return (tcp_hdr(skb)->doff - 5) * 4; 50 return (tcp_hdr(skb)->doff - 5) * 4;
diff --git a/include/linux/udp.h b/include/linux/udp.h
index 0b67d7793520..9d81de123c90 100644
--- a/include/linux/udp.h
+++ b/include/linux/udp.h
@@ -27,6 +27,11 @@ static inline struct udphdr *udp_hdr(const struct sk_buff *skb)
27 return (struct udphdr *)skb_transport_header(skb); 27 return (struct udphdr *)skb_transport_header(skb);
28} 28}
29 29
30static inline struct udphdr *inner_udp_hdr(const struct sk_buff *skb)
31{
32 return (struct udphdr *)skb_inner_transport_header(skb);
33}
34
30#define UDP_HTABLE_SIZE_MIN (CONFIG_BASE_SMALL ? 128 : 256) 35#define UDP_HTABLE_SIZE_MIN (CONFIG_BASE_SMALL ? 128 : 256)
31 36
32static inline int udp_hashfn(struct net *net, unsigned num, unsigned mask) 37static inline int udp_hashfn(struct net *net, unsigned num, unsigned mask)
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 880722e22cc5..ccbabf565732 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -682,11 +682,14 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
682 new->transport_header = old->transport_header; 682 new->transport_header = old->transport_header;
683 new->network_header = old->network_header; 683 new->network_header = old->network_header;
684 new->mac_header = old->mac_header; 684 new->mac_header = old->mac_header;
685 new->inner_transport_header = old->inner_transport_header;
686 new->inner_network_header = old->inner_transport_header;
685 skb_dst_copy(new, old); 687 skb_dst_copy(new, old);
686 new->rxhash = old->rxhash; 688 new->rxhash = old->rxhash;
687 new->ooo_okay = old->ooo_okay; 689 new->ooo_okay = old->ooo_okay;
688 new->l4_rxhash = old->l4_rxhash; 690 new->l4_rxhash = old->l4_rxhash;
689 new->no_fcs = old->no_fcs; 691 new->no_fcs = old->no_fcs;
692 new->encapsulation = old->encapsulation;
690#ifdef CONFIG_XFRM 693#ifdef CONFIG_XFRM
691 new->sp = secpath_get(old->sp); 694 new->sp = secpath_get(old->sp);
692#endif 695#endif
@@ -892,6 +895,8 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
892 new->network_header += offset; 895 new->network_header += offset;
893 if (skb_mac_header_was_set(new)) 896 if (skb_mac_header_was_set(new))
894 new->mac_header += offset; 897 new->mac_header += offset;
898 new->inner_transport_header += offset;
899 new->inner_network_header += offset;
895#endif 900#endif
896 skb_shinfo(new)->gso_size = skb_shinfo(old)->gso_size; 901 skb_shinfo(new)->gso_size = skb_shinfo(old)->gso_size;
897 skb_shinfo(new)->gso_segs = skb_shinfo(old)->gso_segs; 902 skb_shinfo(new)->gso_segs = skb_shinfo(old)->gso_segs;
@@ -1089,6 +1094,8 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
1089 skb->network_header += off; 1094 skb->network_header += off;
1090 if (skb_mac_header_was_set(skb)) 1095 if (skb_mac_header_was_set(skb))
1091 skb->mac_header += off; 1096 skb->mac_header += off;
1097 skb->inner_transport_header += off;
1098 skb->inner_network_header += off;
1092 /* Only adjust this if it actually is csum_start rather than csum */ 1099 /* Only adjust this if it actually is csum_start rather than csum */
1093 if (skb->ip_summed == CHECKSUM_PARTIAL) 1100 if (skb->ip_summed == CHECKSUM_PARTIAL)
1094 skb->csum_start += nhead; 1101 skb->csum_start += nhead;
@@ -1188,6 +1195,8 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
1188 n->network_header += off; 1195 n->network_header += off;
1189 if (skb_mac_header_was_set(skb)) 1196 if (skb_mac_header_was_set(skb))
1190 n->mac_header += off; 1197 n->mac_header += off;
1198 n->inner_transport_header += off;
1199 n->inner_network_header += off;
1191#endif 1200#endif
1192 1201
1193 return n; 1202 return n;