diff options
author | David S. Miller <davem@davemloft.net> | 2011-03-08 13:38:33 -0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-03-08 13:38:33 -0800 |
commit | c50880263a1c69f61f0e79125a6f357b0f38d3f8 (patch) | |
tree | 18922ebbfeb520c06c8a999087ff749b971ab729 | |
parent | d44e265a04f72aa06a566bc49d5f236eb372d378 (diff) | |
download | net_test_tools-c50880263a1c69f61f0e79125a6f357b0f38d3f8.tar.gz |
kbench_mod: Add kernel module route lookup tester.
Flow keys can be specified on the kernel command line.
A warmup of "warmup_count" lookups are performed, then
a single cycle counted lookup is performed with the
cycle count reported in the kernel logs.
X86, Powerpc, and Sparc64 are currently supported.
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | Makefile | 13 | ||||
-rw-r--r-- | kbench_mod.c | 494 |
2 files changed, 505 insertions, 2 deletions
@@ -1,12 +1,21 @@ +ifneq ($(KERNELRELEASE),) +obj-m := kbench_mod.o +else CC := gcc CFLAGS := -Wall -O2 -I/usr/local -L/usr/local LDFLAGS := -lmnl -all: route_bench udpflood +KDIR ?= /lib/modules/`uname -r`/build + +all: route_bench udpflood kbench_mod route_bench: route_bench.c +kbench_mod: kbench_mod.c + $(MAKE) -C $(KDIR) M=$$PWD + udpflood: udpflood.c clean: - rm route_bench udpflood + rm route_bench udpflood kbench_mod +endif diff --git a/kbench_mod.c b/kbench_mod.c new file mode 100644 index 0000000..35dc0dc --- /dev/null +++ b/kbench_mod.c @@ -0,0 +1,494 @@ +#define pr_fmt(fmt) "kbench: " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/inet.h> + +#include <net/route.h> +#include <net/ip_fib.h> + +#include <linux/timex.h> + +/* We can't just use "get_cycles()" as on some platforms, such + * as sparc64, that gives system cycles rather than cpu clock + * cycles. + */ + +#ifdef CONFIG_SPARC64 +static inline unsigned long long get_tick(void) +{ + unsigned long long t; + + __asm__ __volatile__("rd %%tick, %0" : "=r" (t)); + return t; +} +#elif defined(CONFIG_X86) +static inline unsigned long long get_tick(void) +{ + unsigned long long t; + + rdtscll(t); + + return t; +} +#elif defined(CONFIG_POWERPC) +static inline unsigned long long get_tick(void) +{ + return get_cycles(); +} +#else +#error Unsupported architecture, please implement get_tick() +#endif + +#define IP_ROUTE_RETURNS_PTR +#undef IP_ROUTE_HAVE_PREALLOC +#undef IP_ROUTE_CYCLE_SAMPLES +#undef IP_FIB_LOOKUP_EXPORTED + +#ifdef IP_ROUTE_CYCLE_SAMPLES +extern int ip_route_output_cycles_1; +extern int ip_route_output_cycles_2; +extern int ip_route_output_cycles_3; +extern int ip_route_output_cycles_4; +extern int ip_route_output_cycles_5; +extern int ip_route_output_cycles_6; +extern int ip_route_output_cycles_7; +#endif + +#define DEFAULT_WARMUP_COUNT 100000 + +#define DEFAULT_DST_IP_ADDR 0x4a800001 +#define DEFAULT_SRC_IP_ADDR 0x00000000 +#define DEFAULT_OIF 0 +#define DEFAULT_IIF 0 +#define DEFAULT_MARK 0x00000000 +#define DEFAULT_TOS 0x00 + +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 int flow_tos = DEFAULT_TOS; + +static char dst_string[64]; +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) +{ + if (dst_string[0]) + flow_dst_ip_addr = in_aton(dst_string); + if (src_string[0]) + flow_src_ip_addr = in_aton(src_string); +} + +module_param_named(oif, flow_oif, int, 0); +module_param_named(iif, flow_iif, int, 0); +module_param_named(mark, flow_mark, uint, 0); +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 flowi *fl) +{ + memset(fl, 0, sizeof(*fl)); + fl->oif = flow_oif; + fl->iif = flow_iif; + fl->mark = flow_mark; + fl->fl4_dst = flow_dst_ip_addr; + fl->fl4_src = flow_src_ip_addr; + fl->fl4_tos = flow_tos; +} + +#ifdef IP_ROUTE_RETURNS_PTR +static struct rtable *route_output(struct net *net, struct flowi *fl) +{ + return ip_route_output_key(net, fl); +} +#else +static struct rtable *route_output(struct net *net, struct flowi *fl) +{ + struct rtable *rt; + int err; + + err = ip_route_output_key(net, &rt, fl); + if (err) + return ERR_PTR(err); + return rt; +} +#endif + +static void do_full_output_lookup_bench(void) +{ + unsigned long long t1, t2, tdiff; + struct rtable *rt; + struct flowi fl; + int i; + + rt = NULL; + + for (i = 0; i < warmup_count; i++) { + flow_init(&fl); + + rt = route_output(&init_net, &fl); + if (IS_ERR(rt)) + break; + ip_rt_put(rt); + } + if (IS_ERR(rt)) { + pr_info("ip_route_output_key: err=%ld\n", PTR_ERR(rt)); + return; + } + +#ifdef IP_ROUTE_CYCLE_SAMPLES + ip_route_output_cycles_1 = 0; + ip_route_output_cycles_2 = 0; + ip_route_output_cycles_3 = 0; + ip_route_output_cycles_4 = 0; + ip_route_output_cycles_5 = 0; + ip_route_output_cycles_6 = 0; + ip_route_output_cycles_7 = 0; +#endif + + flow_init(&fl); + + t1 = get_tick(); + rt = route_output(&init_net, &fl); + t2 = get_tick(); + if (!IS_ERR(rt)) + ip_rt_put(rt); + + tdiff = t2 - t1; + pr_info("ip_route_output_key tdiff: %llu\n", tdiff); +#ifdef IP_ROUTE_CYCLE_SAMPLES + pr_info("ip_route_output_key cycls: [%d %d %d %d %d %d]\n", + ip_route_output_cycles_1, + ip_route_output_cycles_2, + ip_route_output_cycles_3, + ip_route_output_cycles_4, + ip_route_output_cycles_5, + ip_route_output_cycles_6); +#endif +} + +static void do_full_input_lookup_bench(void) +{ + unsigned long long t1, t2, tdiff; + struct net_device *dev; + struct sk_buff *skb; + int err, i; + + 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"); + 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 (err) + break; + skb_dst_drop(skb); + } + local_bh_enable(); + + if (err) { + pr_info("Input route lookup fails with err=%d\n", err); + goto out_free; + } + + local_bh_disable(); + t1 = get_tick(); + err = ip_route_input(skb, flow_dst_ip_addr, flow_src_ip_addr, flow_tos, dev); + t2 = get_tick(); + local_bh_enable(); + + if (err) { + pr_info("Input route lookup fails with err=%d\n", err); + goto out_free; + } + + skb_dst_drop(skb); + + tdiff = t2 - t1; + pr_info("ip_route_input tdiff: %llu\n", tdiff); + +out_free: + kfree_skb(skb); +} + +static void do_full_lookup_bench(void) +{ + if (!flow_iif) + do_full_output_lookup_bench(); + else + do_full_input_lookup_bench(); +} + +#ifdef IP_ROUTE_HAVE_PREALLOC +static void do_full_lookup_prealloc_bench(void) +{ + unsigned long long t1, t2, tdiff; + struct rtable *rt, rt_stack; + struct flowi fl; + int err, i; + + err = 0; + + for (i = 0; i < warmup_count; i++) { + flow_init(&fl); + + rt = ip_route_output_flow_prealloc(&init_net, &fl, NULL, &rt_stack.dst); + if (IS_ERR(rt)) { + err = PTR_ERR(rt); + break; + } + ip_rt_put(rt); + } + if (err) { + pr_info("ip_route_output_key prealloc: err=%d\n", err); + return; + } + +#ifdef IP_ROUTE_CYCLE_SAMPLES + ip_route_output_cycles_1 = 0; + ip_route_output_cycles_2 = 0; + ip_route_output_cycles_3 = 0; + ip_route_output_cycles_4 = 0; + ip_route_output_cycles_5 = 0; + ip_route_output_cycles_6 = 0; + ip_route_output_cycles_7 = 0; +#endif + + flow_init(&fl); + + t1 = get_tick(); + rt = ip_route_output_flow_prealloc(&init_net, &fl, NULL, &rt_stack.dst); + t2 = get_tick(); + if (!IS_ERR(rt)) + ip_rt_put(rt); + + tdiff = t2 - t1; + pr_info("ip_route_output_key prealloc tdiff: %llu\n", tdiff); +#ifdef IP_ROUTE_CYCLE_SAMPLES + pr_info("ip_route_output_key prealloc cycls: [%d %d %d %d %d %d %d]\n", + ip_route_output_cycles_1, + ip_route_output_cycles_2, + ip_route_output_cycles_3, + ip_route_output_cycles_4, + ip_route_output_cycles_5, + ip_route_output_cycles_6, + ip_route_output_cycles_7); +#endif +} +#endif + +#ifdef IP_FIB_LOOKUP_EXPORTED +static void do_fib_lookup_bench(void) +{ + unsigned long long t1, t2, tdiff; + struct fib_result res; + struct flowi fl; + int err, i; + + flow_init(&fl); + + for (i = 0; i < warmup_count; i++) { + struct fib_table *table; + + table = fib_get_table(&init_net, RT_TABLE_MAIN); + if (!table) { + pr_info("fib_lookup: No main table.\n"); + return; + } + err = fib_table_lookup(table, &fl, &res, FIB_LOOKUP_NOREF); + if (err) { + pr_info("fib_lookup: err=%d\n", err); + return; + } + } + + { + struct fib_table *table; + + t1 = get_tick(); + table = fib_get_table(&init_net, RT_TABLE_MAIN); + if (!table) { + pr_info("fib_lookup: No main table.\n"); + return; + } + err = fib_table_lookup(table, &fl, &res, FIB_LOOKUP_NOREF); + t2 = get_tick(); + } + tdiff = t2 - t1; + pr_info("fib_lookup tdiff: %llu\n", tdiff); +} + +struct net_route { + struct net_device *dev; + unsigned long _metrics; + struct neighbour *neighbour; + struct hh_cache *hh; + atomic_t __refcnt; +}; + +struct ipv4_route { + struct net_route base; + + int rt_genid; + + __be32 rt_dst; + __be32 rt_src; + __u16 rt_type; + __u8 rt_tos; + + int rt_iif; + int rt_oif; + __u32 rt_mark; + + __be32 rt_gateway; + __be32 rt_spec_dst; + + u32 rt_peer_genid; + struct inet_peer *peer; + struct fib_info *fi; +}; + +static atomic_t foo_id = ATOMIC_INIT(1); + +static int new_output_lookup(const struct flowi *fl, struct ipv4_route *rt) +{ + struct fib_table *table = fib_get_table(&init_net, RT_TABLE_MAIN); + struct fib_result res; + int err; + + if (!table) { + pr_info("new_output_lookup: No main table.\n"); + return -ENODEV; + } + err = fib_table_lookup(table, fl, &res, FIB_LOOKUP_NOREF); + if (err) + return err; + + rt->rt_genid = atomic_read(&foo_id); + rt->rt_dst = fl->fl4_dst; + rt->rt_src = fl->fl4_src; + rt->rt_type = res.type; + rt->rt_tos = fl->fl4_tos; + rt->rt_iif = fl->iif; + rt->rt_oif = fl->oif; + rt->rt_mark = fl->mark; + rt->rt_gateway = FIB_RES_GW(res); + rt->rt_spec_dst = fl->fl4_src; + rt->rt_peer_genid = 0; + rt->peer = NULL; + rt->fi = NULL; + + return 0; +} + +static void do_new_lookup_bench(void) +{ + unsigned long long t1, t2, tdiff; + struct ipv4_route rt; + struct flowi fl; + int err, i; + + flow_init(&fl); + + for (i = 0; i < warmup_count; i++) { + err = new_output_lookup(&fl, &rt); + if (err) { + pr_info("new_output_lookup: err=%d\n", err); + return; + } + } + t1 = get_tick(); + err = new_output_lookup(&fl, &rt); + t2 = get_tick(); + + if (err) { + pr_info("new_output_lookup: err=%d\n", err); + return; + } + + tdiff = t2 - t1; + pr_info("new_output_lookup tdiff: %llu\n", tdiff); +} +#endif + +static void do_bench(void) +{ + do_full_lookup_bench(); + do_full_lookup_bench(); + do_full_lookup_bench(); + do_full_lookup_bench(); + +#ifdef IP_ROUTE_HAVE_PREALLOC + do_full_lookup_prealloc_bench(); + do_full_lookup_prealloc_bench(); + do_full_lookup_prealloc_bench(); + do_full_lookup_prealloc_bench(); +#endif + +#ifdef IP_FIB_LOOKUP_EXPORTED + do_fib_lookup_bench(); + do_fib_lookup_bench(); + do_fib_lookup_bench(); + do_fib_lookup_bench(); + + do_new_lookup_bench(); + do_new_lookup_bench(); + do_new_lookup_bench(); + do_new_lookup_bench(); +#endif +} + +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 defined(CONFIG_X86) + if (!cpu_has_tsc) { + pr_err("X86 TSC is required, but is unavailable.\n"); + return -EINVAL; + } +#endif + + pr_info("sizeof(struct rtable)==%Zd\n", sizeof(struct rtable)); + + do_bench(); + + return -ENODEV; +} + +static void __exit kbench_exit(void) +{ +} + +module_init(kbench_init); +module_exit(kbench_exit); +MODULE_LICENSE("GPL"); |