Subject: Re: net_test_tools: add ipv6 support for kbench_mod

From: Shaohua Li <[email-masked]>
Date: Thu, 22 Jan 2015 14:12:56 -0800

On Thu, Jan 15, 2015 at 06:08:39PM -0500, David Miller wrote:
> From: Shaohua Li <[email-masked]>
> Date: Thu, 15 Jan 2015 13:17:37 -0800
> 
> > On Wed, Jan 14, 2015 at 01:35:40AM -0500, David Miller wrote:
> >> From: Shaohua Li <[email-masked]>
> >> Date: Tue, 13 Jan 2015 21:45:48 -0800
> >> 
> >> > This patch adds ipv6 support for kbench_mod test module
> >> 
> >> This doesn't even link because ip6_route_input is not an
> >> exported symbol.
> >> 
> >> So you either didn't test this, or it depends upon custom
> >> kernel changes which you didn't mention.
> >> 
> >> Either way I can't apply this, sorry
> > 
> > Yes, we need export the sysmbol for the test. Can we export the symbol?
> > or I can delete the route input test, which one do you prefer?
> 
> There is no justification upstream to export that symbol since
> there are no modular users in-tree.

Hi,
I changed it to do the ip6_route_input test optionally. If the test is
required, somebody should change the kernel to export it and define
HAVE_IP6_ROUTE_INPUT in the test module. I thought this is fine for a
test module. How do you think?

Thanks,
Shaohua

This patch adds ipv6 support for kbench_mod test module. Since
ip6_route_input() isn't exported, it can only be tested with
HAVE_IP6_ROUTE_INPUT defined (ofcourse, changing kernel to export it)

diff --git a/kbench_mod.c b/kbench_mod.c
index fc3765c..9f5dccc 100644
--- a/kbench_mod.c
+++ b/kbench_mod.c
@@ -3,9 +3,11 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/inet.h>
+#include <linux/in6.h>
 
 #include <net/route.h>
 #include <net/ip_fib.h>
+#include <net/ip6_route.h>
 
 #include <linux/timex.h>
 
@@ -66,9 +68,13 @@ extern int ip_route_output_cycles_7;
 static int flow_oif = DEFAULT_OIF;
 static int flow_iif = DEFAULT_IIF;
 static u32 flow_mark = DEFAULT_MARK;
-static u32 flow_dst_ip_addr = DEFAULT_DST_IP_ADDR;
-static u32 flow_src_ip_addr = DEFAULT_SRC_IP_ADDR;
+static u32 ip4_flow_dst_ip_addr = DEFAULT_DST_IP_ADDR;
+static u32 ip4_flow_src_ip_addr = DEFAULT_SRC_IP_ADDR;
+static struct in6_addr ip6_flow_dst_ip_addr;
+static struct in6_addr ip6_flow_src_ip_addr;
 static int flow_tos = DEFAULT_TOS;
+static int ip6_bench;
+module_param(ip6_bench, int, 0);
 
 static char dst_string[64];
 static char src_string[64];
@@ -76,12 +82,29 @@ static char src_string[64];
 module_param_string(dst, dst_string, sizeof(dst_string), 0);
 module_param_string(src, src_string, sizeof(src_string), 0);
 
-static void __init flow_setup(void)
+static int __init flow_setup(void)
 {
+	if (ip6_bench) {
+		if (dst_string[0] &&
+		    !in6_pton(dst_string, -1, ip6_flow_dst_ip_addr.s6_addr, -1, NULL)) {
+			pr_info("cannot parse \"%s\"\n", dst_string);
+			return -1;
+		}
+
+		if (src_string[0] &&
+		    !in6_pton(src_string, -1, ip6_flow_src_ip_addr.s6_addr, -1, NULL)) {
+			pr_info("cannot parse \"%s\"\n", src_string);
+			return -1;
+		}
+
+		return 0;
+	}
+
 	if (dst_string[0])
-		flow_dst_ip_addr = in_aton(dst_string);
+		ip4_flow_dst_ip_addr = in_aton(dst_string);
 	if (src_string[0])
-		flow_src_ip_addr = in_aton(src_string);
+		ip4_flow_src_ip_addr = in_aton(src_string);
+	return 0;
 }
 
 module_param_named(oif, flow_oif, int, 0);
@@ -92,15 +115,70 @@ module_param_named(tos, flow_tos, int, 0);
 static int warmup_count = DEFAULT_WARMUP_COUNT;
 module_param_named(count, warmup_count, int, 0);
 
-static void flow_init(struct flowi4 *fl4)
+#define flow_init(fl, gen) 				\
+do {							\
+	memset((fl), 0, sizeof(*(fl)));			\
+	(fl)->flowi##gen##_oif = flow_oif;		\
+	(fl)->flowi##gen##_iif = flow_iif;		\
+	(fl)->flowi##gen##_mark = flow_mark;		\
+	(fl)->flowi##gen##_tos = flow_tos;		\
+	(fl)->daddr = ip##gen##_flow_dst_ip_addr;	\
+	(fl)->saddr = ip##gen##_flow_src_ip_addr;	\
+} while (0)
+
+#define flow_init_ip6(fl) 				\
+do {							\
+	flow_init(fl, 6);				\
+	(fl)->flowi6_proto = IPPROTO_ICMPV6;		\
+} while(0)
+
+static int skb_init_ip6(struct sk_buff *skb)
 {
-	memset(fl4, 0, sizeof(*fl4));
-	fl4->flowi4_oif = flow_oif;
-	fl4->flowi4_iif = flow_iif;
-	fl4->flowi4_mark = flow_mark;
-	fl4->flowi4_tos = flow_tos;
-	fl4->daddr = flow_dst_ip_addr;
-	fl4->saddr = flow_src_ip_addr;
+	struct ipv6hdr *hdr;
+	struct net_device *dev;
+
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
+	skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
+	hdr = ipv6_hdr(skb);
+
+	hdr->priority = 0;
+	hdr->version = 6;
+	memset(hdr->flow_lbl, 0, sizeof(hdr->flow_lbl));
+	hdr->payload_len = htons(sizeof(struct icmp6hdr));
+	hdr->nexthdr = IPPROTO_ICMPV6;
+	hdr->saddr = ip6_flow_src_ip_addr;
+	hdr->daddr = ip6_flow_dst_ip_addr;
+
+	dev = __dev_get_by_index(&init_net, flow_iif);
+	if (dev == NULL) {
+		pr_info("Input device does not exist\n");
+		return -ENODEV;
+	}
+	skb->protocol = htons(ETH_P_IPV6);
+	skb->dev = dev;
+	skb->mark = flow_mark;
+	return 0;
+}
+
+static int skb_init_ip4(struct sk_buff *skb)
+{
+	struct net_device *dev;
+
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
+	ip_hdr(skb)->protocol = IPPROTO_ICMP;
+	skb_reserve(skb, MAX_HEADER + sizeof(struct iphdr));
+
+	dev = __dev_get_by_index(&init_net, flow_iif);
+	if (dev == NULL) {
+		pr_info("Input device does not exist\n");
+		return -ENODEV;
+	}
+	skb->protocol = htons(ETH_P_IP);
+	skb->dev = dev;
+	skb->mark = flow_mark;
+	return 0;
 }
 
 static struct rtable *route_output(struct net *net, struct flowi4 *fl4)
@@ -108,7 +186,7 @@ static struct rtable *route_output(struct net *net, struct flowi4 *fl4)
 	return ip_route_output_key(net, fl4);
 }
 
-static void do_full_output_lookup_bench(void)
+static void do_full_output_lookup_bench_ip4(void)
 {
 	unsigned long long t1, t2, tdiff;
 	struct rtable *rt;
@@ -118,7 +196,7 @@ static void do_full_output_lookup_bench(void)
 	rt = NULL;
 
 	for (i = 0; i < warmup_count; i++) {
-		flow_init(&fl4);
+		flow_init(&fl4, 4);
 
 		rt = route_output(&init_net, &fl4);
 		if (IS_ERR(rt))
@@ -140,7 +218,7 @@ static void do_full_output_lookup_bench(void)
 	ip_route_output_cycles_7 = 0;
 #endif
 
-	flow_init(&fl4);
+	flow_init(&fl4, 4);
 
 	t1 = get_tick();
 	rt = route_output(&init_net, &fl4);
@@ -161,35 +239,73 @@ static void do_full_output_lookup_bench(void)
 #endif
 }
 
+static void do_full_output_lookup_bench_ip6(void)
+{
+	unsigned long long t1, t2, tdiff;
+	struct rt6_info *rt;
+	struct flowi6 fl6;
+	int i;
+
+	rt = NULL;
+
+	for (i = 0; i < warmup_count; i++) {
+		flow_init_ip6(&fl6);
+
+		rt = (struct rt6_info *)ip6_route_output(&init_net, NULL, &fl6);
+		if (IS_ERR(rt))
+			break;
+		ip6_rt_put(rt);
+	}
+	if (IS_ERR(rt)) {
+		pr_info("ip6_route_output: err=%ld\n", PTR_ERR(rt));
+		return;
+	}
+
+	flow_init_ip6(&fl6);
+
+	t1 = get_tick();
+	rt = (struct rt6_info *)ip6_route_output(&init_net, NULL, &fl6);
+	t2 = get_tick();
+	if (!IS_ERR(rt))
+		ip6_rt_put(rt);
+
+	tdiff = t2 - t1;
+	pr_info("ip6_route_output tdiff: %llu\n", tdiff);
+}
+
 static void do_full_input_lookup_bench(void)
 {
 	unsigned long long t1, t2, tdiff;
-	struct net_device *dev;
 	struct sk_buff *skb;
+	struct rt6_info *rt;
 	int err, i;
 
+#ifndef HAVE_IP6_ROUTE_INPUT
+#define ip6_route_input(s)
+	if (ip6_bench)
+		return;
+#endif
 	skb = alloc_skb(4096, GFP_KERNEL);
 	if (!skb) {
 		pr_info("Cannot alloc SKB for test\n");
 		return;
 	}
-	skb_reset_mac_header(skb);
-	skb_reset_network_header(skb);
-	ip_hdr(skb)->protocol = IPPROTO_ICMP;
-	skb_reserve(skb, MAX_HEADER + sizeof(struct iphdr));
-
-	dev = __dev_get_by_index(&init_net, flow_iif);
-	if (dev == NULL) {
-		pr_info("Input device does not exist\n");
+	if (ip6_bench)
+		err = skb_init_ip6(skb);
+	else
+		err = skb_init_ip4(skb);
+	if (err)
 		goto out_free;
-	}
-	skb->protocol = htons(ETH_P_IP);
-	skb->dev = dev;
-	skb->mark = flow_mark;
+
 	local_bh_disable();
 	err = 0;
 	for (i = 0; i < warmup_count; i++) {
-		err = ip_route_input(skb, flow_dst_ip_addr, flow_src_ip_addr, flow_tos, dev);
+		if (ip6_bench) {
+			ip6_route_input(skb);
+			rt = (struct rt6_info *)skb_dst(skb);
+			err = (!rt || rt == init_net.ipv6.ip6_null_entry);
+		} else
+			err = ip_route_input(skb, ip4_flow_dst_ip_addr, ip4_flow_src_ip_addr, flow_tos, skb->dev);
 		if (err)
 			break;
 		skb_dst_drop(skb);
@@ -203,7 +319,12 @@ static void do_full_input_lookup_bench(void)
 
 	local_bh_disable();
 	t1 = get_tick();
-	err = ip_route_input(skb, flow_dst_ip_addr, flow_src_ip_addr, flow_tos, dev);
+	if (ip6_bench) {
+		ip6_route_input(skb);
+		rt = (struct rt6_info *)skb_dst(skb);
+		err = (!rt || rt == init_net.ipv6.ip6_null_entry);
+	} else
+		err = ip_route_input(skb, ip4_flow_dst_ip_addr, ip4_flow_src_ip_addr, flow_tos, skb->dev);
 	t2 = get_tick();
 	local_bh_enable();
 
@@ -215,7 +336,10 @@ static void do_full_input_lookup_bench(void)
 	skb_dst_drop(skb);
 
 	tdiff = t2 - t1;
-	pr_info("ip_route_input tdiff: %llu\n", tdiff);
+	if (ip6_bench)
+		pr_info("ip6_route_input tdiff: %llu\n", tdiff);
+	else
+		pr_info("ip_route_input tdiff: %llu\n", tdiff);
 
 out_free:
 	kfree_skb(skb);
@@ -223,9 +347,12 @@ static void do_full_input_lookup_bench(void)
 
 static void do_full_lookup_bench(void)
 {
-	if (!flow_iif)
-		do_full_output_lookup_bench();
-	else
+	if (!flow_iif) {
+		if (ip6_bench)
+			do_full_output_lookup_bench_ip6();
+		else
+			do_full_output_lookup_bench_ip4();
+	} else
 		do_full_input_lookup_bench();
 }
 
@@ -240,7 +367,7 @@ static void do_full_lookup_prealloc_bench(void)
 	err = 0;
 
 	for (i = 0; i < warmup_count; i++) {
-		flow_init(&fl4);
+		flow_init(&fl4, 4);
 
 		rt = ip_route_output_flow_prealloc(&init_net, &fl4, NULL, &rt_stack.dst);
 		if (IS_ERR(rt)) {
@@ -264,7 +391,7 @@ static void do_full_lookup_prealloc_bench(void)
 	ip_route_output_cycles_7 = 0;
 #endif
 
-	flow_init(&fl4);
+	flow_init(&fl4, 4);
 
 	t1 = get_tick();
 	rt = ip_route_output_flow_prealloc(&init_net, &fl4, NULL, &rt_stack.dst);
@@ -295,7 +422,7 @@ static void do_fib_lookup_bench(void)
 	struct flowi4 fl4;
 	int err, i;
 
-	flow_init(&fl4);
+	flow_init(&fl4, 4);
 
 	for (i = 0; i < warmup_count; i++) {
 		struct fib_table *table;
@@ -398,7 +525,7 @@ static void do_new_lookup_bench(void)
 	struct flowi fl;
 	int err, i;
 
-	flow_init(&fl);
+	flow_init(&fl, 4);
 
 	for (i = 0; i < warmup_count; i++) {
 		err = new_output_lookup(&fl, &rt);
@@ -428,6 +555,8 @@ static void do_bench(void)
 	do_full_lookup_bench();
 	do_full_lookup_bench();
 
+	if (ip6_bench)
+		return;
 #ifdef IP_ROUTE_HAVE_PREALLOC
 	do_full_lookup_prealloc_bench();
 	do_full_lookup_prealloc_bench();
@@ -452,10 +581,19 @@ static int __init kbench_init(void)
 {
 	flow_setup();
 
-	pr_info("flow [IIF(%d),OIF(%d),MARK(0x%08x),D(%pI4),S(%pI4),TOS(0x%02x)]\n",
-		flow_iif, flow_oif, flow_mark,
-		&flow_dst_ip_addr,
-		&flow_src_ip_addr, flow_tos);
+	if (!ip6_bench) {
+		pr_info("flow [IIF(%d),OIF(%d),MARK(0x%08x),D(%pI4),S(%pI4),TOS(0x%02x)]\n",
+			flow_iif, flow_oif, flow_mark,
+			&ip4_flow_dst_ip_addr,
+			&ip4_flow_src_ip_addr, flow_tos);
+	} else {
+		pr_info("flow [IIF(%d),OIF(%d),MARK(0x%08x),D(%pI6),"
+			"S(%pI6),TOS(0x%02x)]\n",
+			flow_iif, flow_oif, flow_mark,
+			&ip6_flow_dst_ip_addr,
+			&ip6_flow_src_ip_addr,
+			flow_tos);
+	}
 
 #if defined(CONFIG_X86)
 	if (!cpu_has_tsc) {