Skip to content

Commit 6ae23ad

Browse files
tomratbertdavem330
authored andcommitted
net: Add driver helper functions to determine checksum offloadability
Add skb_csum_offload_chk driver helper function to determine if a device with limited checksum offload capabilities is able to offload the checksum for a given packet. This patch includes: - The skb_csum_offload_chk function. Returns true if checksum is offloadable, else false. Optionally, in the case that the checksum is not offloable, the function can call skb_checksum_help to resolve the checksum. skb_csum_offload_chk also returns whether the checksum refers to an encapsulated checksum. - Definition of skb_csum_offl_spec structure that caller uses to indicate rules about what it can offload (e.g. IPv4/v6, TCP/UDP only, whether encapsulated checksums can be offloaded, whether checksum with IPv6 extension headers can be offloaded). - Ancilary functions called skb_csum_offload_chk_help, skb_csum_off_chk_help_cmn, skb_csum_off_chk_help_cmn_v4_only. Signed-off-by: Tom Herbert <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 9a49850 commit 6ae23ad

File tree

2 files changed

+214
-0
lines changed

2 files changed

+214
-0
lines changed

include/linux/netdevice.h

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2522,6 +2522,71 @@ static inline void skb_gro_remcsum_cleanup(struct sk_buff *skb,
25222522
remcsum_unadjust((__sum16 *)ptr, grc->delta);
25232523
}
25242524

2525+
struct skb_csum_offl_spec {
2526+
__u16 ipv4_okay:1,
2527+
ipv6_okay:1,
2528+
encap_okay:1,
2529+
ip_options_okay:1,
2530+
ext_hdrs_okay:1,
2531+
tcp_okay:1,
2532+
udp_okay:1,
2533+
sctp_okay:1,
2534+
vlan_okay:1,
2535+
no_encapped_ipv6:1,
2536+
no_not_encapped:1;
2537+
};
2538+
2539+
bool __skb_csum_offload_chk(struct sk_buff *skb,
2540+
const struct skb_csum_offl_spec *spec,
2541+
bool *csum_encapped,
2542+
bool csum_help);
2543+
2544+
static inline bool skb_csum_offload_chk(struct sk_buff *skb,
2545+
const struct skb_csum_offl_spec *spec,
2546+
bool *csum_encapped,
2547+
bool csum_help)
2548+
{
2549+
if (skb->ip_summed != CHECKSUM_PARTIAL)
2550+
return false;
2551+
2552+
return __skb_csum_offload_chk(skb, spec, csum_encapped, csum_help);
2553+
}
2554+
2555+
static inline bool skb_csum_offload_chk_help(struct sk_buff *skb,
2556+
const struct skb_csum_offl_spec *spec)
2557+
{
2558+
bool csum_encapped;
2559+
2560+
return skb_csum_offload_chk(skb, spec, &csum_encapped, true);
2561+
}
2562+
2563+
static inline bool skb_csum_off_chk_help_cmn(struct sk_buff *skb)
2564+
{
2565+
static const struct skb_csum_offl_spec csum_offl_spec = {
2566+
.ipv4_okay = 1,
2567+
.ip_options_okay = 1,
2568+
.ipv6_okay = 1,
2569+
.vlan_okay = 1,
2570+
.tcp_okay = 1,
2571+
.udp_okay = 1,
2572+
};
2573+
2574+
return skb_csum_offload_chk_help(skb, &csum_offl_spec);
2575+
}
2576+
2577+
static inline bool skb_csum_off_chk_help_cmn_v4_only(struct sk_buff *skb)
2578+
{
2579+
static const struct skb_csum_offl_spec csum_offl_spec = {
2580+
.ipv4_okay = 1,
2581+
.ip_options_okay = 1,
2582+
.tcp_okay = 1,
2583+
.udp_okay = 1,
2584+
.vlan_okay = 1,
2585+
};
2586+
2587+
return skb_csum_offload_chk_help(skb, &csum_offl_spec);
2588+
}
2589+
25252590
static inline int dev_hard_header(struct sk_buff *skb, struct net_device *dev,
25262591
unsigned short type,
25272592
const void *daddr, const void *saddr,
@@ -3711,6 +3776,19 @@ static inline bool can_checksum_protocol(netdev_features_t features,
37113776
}
37123777
}
37133778

3779+
/* Map an ethertype into IP protocol if possible */
3780+
static inline int eproto_to_ipproto(int eproto)
3781+
{
3782+
switch (eproto) {
3783+
case htons(ETH_P_IP):
3784+
return IPPROTO_IP;
3785+
case htons(ETH_P_IPV6):
3786+
return IPPROTO_IPV6;
3787+
default:
3788+
return -1;
3789+
}
3790+
}
3791+
37143792
#ifdef CONFIG_BUG
37153793
void netdev_rx_csum_fault(struct net_device *dev);
37163794
#else

net/core/dev.c

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@
138138
#include <linux/errqueue.h>
139139
#include <linux/hrtimer.h>
140140
#include <linux/netfilter_ingress.h>
141+
#include <linux/sctp.h>
141142

142143
#include "net-sysfs.h"
143144

@@ -2471,6 +2472,141 @@ int skb_checksum_help(struct sk_buff *skb)
24712472
}
24722473
EXPORT_SYMBOL(skb_checksum_help);
24732474

2475+
/* skb_csum_offload_check - Driver helper function to determine if a device
2476+
* with limited checksum offload capabilities is able to offload the checksum
2477+
* for a given packet.
2478+
*
2479+
* Arguments:
2480+
* skb - sk_buff for the packet in question
2481+
* spec - contains the description of what device can offload
2482+
* csum_encapped - returns true if the checksum being offloaded is
2483+
* encpasulated. That is it is checksum for the transport header
2484+
* in the inner headers.
2485+
* checksum_help - when set indicates that helper function should
2486+
* call skb_checksum_help if offload checks fail
2487+
*
2488+
* Returns:
2489+
* true: Packet has passed the checksum checks and should be offloadable to
2490+
* the device (a driver may still need to check for additional
2491+
* restrictions of its device)
2492+
* false: Checksum is not offloadable. If checksum_help was set then
2493+
* skb_checksum_help was called to resolve checksum for non-GSO
2494+
* packets and when IP protocol is not SCTP
2495+
*/
2496+
bool __skb_csum_offload_chk(struct sk_buff *skb,
2497+
const struct skb_csum_offl_spec *spec,
2498+
bool *csum_encapped,
2499+
bool csum_help)
2500+
{
2501+
struct iphdr *iph;
2502+
struct ipv6hdr *ipv6;
2503+
void *nhdr;
2504+
int protocol;
2505+
u8 ip_proto;
2506+
2507+
if (skb->protocol == htons(ETH_P_8021Q) ||
2508+
skb->protocol == htons(ETH_P_8021AD)) {
2509+
if (!spec->vlan_okay)
2510+
goto need_help;
2511+
}
2512+
2513+
/* We check whether the checksum refers to a transport layer checksum in
2514+
* the outermost header or an encapsulated transport layer checksum that
2515+
* corresponds to the inner headers of the skb. If the checksum is for
2516+
* something else in the packet we need help.
2517+
*/
2518+
if (skb_checksum_start_offset(skb) == skb_transport_offset(skb)) {
2519+
/* Non-encapsulated checksum */
2520+
protocol = eproto_to_ipproto(vlan_get_protocol(skb));
2521+
nhdr = skb_network_header(skb);
2522+
*csum_encapped = false;
2523+
if (spec->no_not_encapped)
2524+
goto need_help;
2525+
} else if (skb->encapsulation && spec->encap_okay &&
2526+
skb_checksum_start_offset(skb) ==
2527+
skb_inner_transport_offset(skb)) {
2528+
/* Encapsulated checksum */
2529+
*csum_encapped = true;
2530+
switch (skb->inner_protocol_type) {
2531+
case ENCAP_TYPE_ETHER:
2532+
protocol = eproto_to_ipproto(skb->inner_protocol);
2533+
break;
2534+
case ENCAP_TYPE_IPPROTO:
2535+
protocol = skb->inner_protocol;
2536+
break;
2537+
}
2538+
nhdr = skb_inner_network_header(skb);
2539+
} else {
2540+
goto need_help;
2541+
}
2542+
2543+
switch (protocol) {
2544+
case IPPROTO_IP:
2545+
if (!spec->ipv4_okay)
2546+
goto need_help;
2547+
iph = nhdr;
2548+
ip_proto = iph->protocol;
2549+
if (iph->ihl != 5 && !spec->ip_options_okay)
2550+
goto need_help;
2551+
break;
2552+
case IPPROTO_IPV6:
2553+
if (!spec->ipv6_okay)
2554+
goto need_help;
2555+
if (spec->no_encapped_ipv6 && *csum_encapped)
2556+
goto need_help;
2557+
ipv6 = nhdr;
2558+
nhdr += sizeof(*ipv6);
2559+
ip_proto = ipv6->nexthdr;
2560+
break;
2561+
default:
2562+
goto need_help;
2563+
}
2564+
2565+
ip_proto_again:
2566+
switch (ip_proto) {
2567+
case IPPROTO_TCP:
2568+
if (!spec->tcp_okay ||
2569+
skb->csum_offset != offsetof(struct tcphdr, check))
2570+
goto need_help;
2571+
break;
2572+
case IPPROTO_UDP:
2573+
if (!spec->udp_okay ||
2574+
skb->csum_offset != offsetof(struct udphdr, check))
2575+
goto need_help;
2576+
break;
2577+
case IPPROTO_SCTP:
2578+
if (!spec->sctp_okay ||
2579+
skb->csum_offset != offsetof(struct sctphdr, checksum))
2580+
goto cant_help;
2581+
break;
2582+
case NEXTHDR_HOP:
2583+
case NEXTHDR_ROUTING:
2584+
case NEXTHDR_DEST: {
2585+
u8 *opthdr = nhdr;
2586+
2587+
if (protocol != IPPROTO_IPV6 || !spec->ext_hdrs_okay)
2588+
goto need_help;
2589+
2590+
ip_proto = opthdr[0];
2591+
nhdr += (opthdr[1] + 1) << 3;
2592+
2593+
goto ip_proto_again;
2594+
}
2595+
default:
2596+
goto need_help;
2597+
}
2598+
2599+
/* Passed the tests for offloading checksum */
2600+
return true;
2601+
2602+
need_help:
2603+
if (csum_help && !skb_shinfo(skb)->gso_size)
2604+
skb_checksum_help(skb);
2605+
cant_help:
2606+
return false;
2607+
}
2608+
EXPORT_SYMBOL(__skb_csum_offload_chk);
2609+
24742610
__be16 skb_network_protocol(struct sk_buff *skb, int *depth)
24752611
{
24762612
__be16 type = skb->protocol;

0 commit comments

Comments
 (0)