diff options
Diffstat (limited to 'lib/test_bpf.c')
| -rw-r--r-- | lib/test_bpf.c | 112 | 
1 files changed, 103 insertions, 9 deletions
diff --git a/lib/test_bpf.c b/lib/test_bpf.c index 5ef3eccee27c..cecb230833be 100644 --- a/lib/test_bpf.c +++ b/lib/test_bpf.c @@ -6859,34 +6859,128 @@ err_page0:  	return NULL;  } -static __init int test_skb_segment(void) +static __init struct sk_buff *build_test_skb_linear_no_head_frag(void)  { +	unsigned int alloc_size = 2000; +	unsigned int headroom = 102, doffset = 72, data_size = 1308; +	struct sk_buff *skb[2]; +	int i; + +	/* skbs linked in a frag_list, both with linear data, with head_frag=0 +	 * (data allocated by kmalloc), both have tcp data of 1308 bytes +	 * (total payload is 2616 bytes). +	 * Data offset is 72 bytes (40 ipv6 hdr, 32 tcp hdr). Some headroom. +	 */ +	for (i = 0; i < 2; i++) { +		skb[i] = alloc_skb(alloc_size, GFP_KERNEL); +		if (!skb[i]) { +			if (i == 0) +				goto err_skb0; +			else +				goto err_skb1; +		} + +		skb[i]->protocol = htons(ETH_P_IPV6); +		skb_reserve(skb[i], headroom); +		skb_put(skb[i], doffset + data_size); +		skb_reset_network_header(skb[i]); +		if (i == 0) +			skb_reset_mac_header(skb[i]); +		else +			skb_set_mac_header(skb[i], -ETH_HLEN); +		__skb_pull(skb[i], doffset); +	} + +	/* setup shinfo. +	 * mimic bpf_skb_proto_4_to_6, which resets gso_segs and assigns a +	 * reduced gso_size. +	 */ +	skb_shinfo(skb[0])->gso_size = 1288; +	skb_shinfo(skb[0])->gso_type = SKB_GSO_TCPV6 | SKB_GSO_DODGY; +	skb_shinfo(skb[0])->gso_segs = 0; +	skb_shinfo(skb[0])->frag_list = skb[1]; + +	/* adjust skb[0]'s len */ +	skb[0]->len += skb[1]->len; +	skb[0]->data_len += skb[1]->len; +	skb[0]->truesize += skb[1]->truesize; + +	return skb[0]; + +err_skb1: +	kfree_skb(skb[0]); +err_skb0: +	return NULL; +} + +struct skb_segment_test { +	const char *descr; +	struct sk_buff *(*build_skb)(void);  	netdev_features_t features; +}; + +static struct skb_segment_test skb_segment_tests[] __initconst = { +	{ +		.descr = "gso_with_rx_frags", +		.build_skb = build_test_skb, +		.features = NETIF_F_SG | NETIF_F_GSO_PARTIAL | NETIF_F_IP_CSUM | +			    NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM +	}, +	{ +		.descr = "gso_linear_no_head_frag", +		.build_skb = build_test_skb_linear_no_head_frag, +		.features = NETIF_F_SG | NETIF_F_FRAGLIST | +			    NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_GSO | +			    NETIF_F_LLTX_BIT | NETIF_F_GRO | +			    NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | +			    NETIF_F_HW_VLAN_STAG_TX_BIT +	} +}; + +static __init int test_skb_segment_single(const struct skb_segment_test *test) +{  	struct sk_buff *skb, *segs;  	int ret = -1; -	features = NETIF_F_SG | NETIF_F_GSO_PARTIAL | NETIF_F_IP_CSUM | -		   NETIF_F_IPV6_CSUM; -	features |= NETIF_F_RXCSUM; -	skb = build_test_skb(); +	skb = test->build_skb();  	if (!skb) {  		pr_info("%s: failed to build_test_skb", __func__);  		goto done;  	} -	segs = skb_segment(skb, features); +	segs = skb_segment(skb, test->features);  	if (!IS_ERR(segs)) {  		kfree_skb_list(segs);  		ret = 0; -		pr_info("%s: success in skb_segment!", __func__); -	} else { -		pr_info("%s: failed in skb_segment!", __func__);  	}  	kfree_skb(skb);  done:  	return ret;  } +static __init int test_skb_segment(void) +{ +	int i, err_cnt = 0, pass_cnt = 0; + +	for (i = 0; i < ARRAY_SIZE(skb_segment_tests); i++) { +		const struct skb_segment_test *test = &skb_segment_tests[i]; + +		pr_info("#%d %s ", i, test->descr); + +		if (test_skb_segment_single(test)) { +			pr_cont("FAIL\n"); +			err_cnt++; +		} else { +			pr_cont("PASS\n"); +			pass_cnt++; +		} +	} + +	pr_info("%s: Summary: %d PASSED, %d FAILED\n", __func__, +		pass_cnt, err_cnt); +	return err_cnt ? -EINVAL : 0; +} +  static __init int test_bpf(void)  {  	int i, err_cnt = 0, pass_cnt = 0;  | 
