2016-03-08 15:07:54 -08:00
/* Copyright (c) 2016 Facebook
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation .
*/
# define _GNU_SOURCE
# include <sched.h>
# include <stdio.h>
# include <sys/types.h>
# include <asm/unistd.h>
# include <unistd.h>
# include <assert.h>
# include <sys/wait.h>
# include <stdlib.h>
# include <signal.h>
# include <linux/bpf.h>
# include <string.h>
# include <time.h>
2016-04-04 22:31:32 +05:30
# include <sys/resource.h>
2017-04-14 10:30:27 -07:00
# include <arpa/inet.h>
# include <errno.h>
2016-03-08 15:07:54 -08:00
# include "libbpf.h"
# include "bpf_load.h"
2017-04-14 10:30:28 -07:00
# define TEST_BIT(t) (1U << (t))
2017-04-14 10:30:30 -07:00
# define MAX_NR_CPUS 1024
2016-03-08 15:07:54 -08:00
static __u64 time_get_ns ( void )
{
struct timespec ts ;
clock_gettime ( CLOCK_MONOTONIC , & ts ) ;
return ts . tv_sec * 1000000000ull + ts . tv_nsec ;
}
2017-04-14 10:30:28 -07:00
enum test_type {
HASH_PREALLOC ,
PERCPU_HASH_PREALLOC ,
HASH_KMALLOC ,
PERCPU_HASH_KMALLOC ,
LRU_HASH_PREALLOC ,
NOCOMMON_LRU_HASH_PREALLOC ,
LPM_KMALLOC ,
HASH_LOOKUP ,
ARRAY_LOOKUP ,
2017-04-14 10:30:30 -07:00
INNER_LRU_HASH_PREALLOC ,
2017-04-14 10:30:28 -07:00
NR_TESTS ,
} ;
const char * test_map_names [ NR_TESTS ] = {
[ HASH_PREALLOC ] = " hash_map " ,
[ PERCPU_HASH_PREALLOC ] = " percpu_hash_map " ,
[ HASH_KMALLOC ] = " hash_map_alloc " ,
[ PERCPU_HASH_KMALLOC ] = " percpu_hash_map_alloc " ,
[ LRU_HASH_PREALLOC ] = " lru_hash_map " ,
[ NOCOMMON_LRU_HASH_PREALLOC ] = " nocommon_lru_hash_map " ,
[ LPM_KMALLOC ] = " lpm_trie_map_alloc " ,
[ HASH_LOOKUP ] = " hash_map " ,
[ ARRAY_LOOKUP ] = " array_map " ,
2017-04-14 10:30:30 -07:00
[ INNER_LRU_HASH_PREALLOC ] = " inner_lru_hash_map " ,
2017-04-14 10:30:28 -07:00
} ;
2016-03-08 15:07:54 -08:00
static int test_flags = ~ 0 ;
2017-04-14 10:30:28 -07:00
static uint32_t num_map_entries ;
2017-04-14 10:30:30 -07:00
static uint32_t inner_lru_hash_size ;
static int inner_lru_hash_idx = - 1 ;
static int array_of_lru_hashs_idx = - 1 ;
2017-04-14 10:30:28 -07:00
static uint32_t max_cnt = 1000000 ;
static int check_test_flags ( enum test_type t )
{
return test_flags & TEST_BIT ( t ) ;
}
2016-03-08 15:07:54 -08:00
static void test_hash_prealloc ( int cpu )
{
__u64 start_time ;
int i ;
start_time = time_get_ns ( ) ;
2017-04-14 10:30:28 -07:00
for ( i = 0 ; i < max_cnt ; i + + )
2016-03-08 15:07:54 -08:00
syscall ( __NR_getuid ) ;
printf ( " %d:hash_map_perf pre-alloc %lld events per sec \n " ,
2017-04-14 10:30:28 -07:00
cpu , max_cnt * 1000000000ll / ( time_get_ns ( ) - start_time ) ) ;
2016-03-08 15:07:54 -08:00
}
2017-04-14 10:30:28 -07:00
static void do_test_lru ( enum test_type test , int cpu )
2016-11-11 10:55:11 -08:00
{
2017-04-14 10:30:30 -07:00
static int inner_lru_map_fds [ MAX_NR_CPUS ] ;
2017-04-14 10:30:27 -07:00
struct sockaddr_in6 in6 = { . sin6_family = AF_INET6 } ;
const char * test_name ;
2016-11-11 10:55:11 -08:00
__u64 start_time ;
2017-04-14 10:30:27 -07:00
int i , ret ;
2017-04-14 10:30:30 -07:00
if ( test = = INNER_LRU_HASH_PREALLOC ) {
int outer_fd = map_fd [ array_of_lru_hashs_idx ] ;
assert ( cpu < MAX_NR_CPUS ) ;
if ( cpu ) {
inner_lru_map_fds [ cpu ] =
bpf_create_map ( BPF_MAP_TYPE_LRU_HASH ,
sizeof ( uint32_t ) , sizeof ( long ) ,
inner_lru_hash_size , 0 ) ;
if ( inner_lru_map_fds [ cpu ] = = - 1 ) {
printf ( " cannot create BPF_MAP_TYPE_LRU_HASH %s(%d) \n " ,
strerror ( errno ) , errno ) ;
exit ( 1 ) ;
}
} else {
inner_lru_map_fds [ cpu ] = map_fd [ inner_lru_hash_idx ] ;
}
ret = bpf_map_update_elem ( outer_fd , & cpu ,
& inner_lru_map_fds [ cpu ] ,
BPF_ANY ) ;
if ( ret ) {
printf ( " cannot update ARRAY_OF_LRU_HASHS with key:%u. %s(%d) \n " ,
cpu , strerror ( errno ) , errno ) ;
exit ( 1 ) ;
}
}
2017-04-14 10:30:27 -07:00
in6 . sin6_addr . s6_addr16 [ 0 ] = 0xdead ;
in6 . sin6_addr . s6_addr16 [ 1 ] = 0xbeef ;
2017-04-14 10:30:28 -07:00
if ( test = = LRU_HASH_PREALLOC ) {
2017-04-14 10:30:27 -07:00
test_name = " lru_hash_map_perf " ;
in6 . sin6_addr . s6_addr16 [ 7 ] = 0 ;
2017-04-14 10:30:28 -07:00
} else if ( test = = NOCOMMON_LRU_HASH_PREALLOC ) {
2017-04-14 10:30:27 -07:00
test_name = " nocommon_lru_hash_map_perf " ;
in6 . sin6_addr . s6_addr16 [ 7 ] = 1 ;
2017-04-14 10:30:30 -07:00
} else if ( test = = INNER_LRU_HASH_PREALLOC ) {
test_name = " inner_lru_hash_map_perf " ;
in6 . sin6_addr . s6_addr16 [ 7 ] = 2 ;
2017-04-14 10:30:27 -07:00
} else {
assert ( 0 ) ;
}
2016-11-11 10:55:11 -08:00
start_time = time_get_ns ( ) ;
2017-04-14 10:30:28 -07:00
for ( i = 0 ; i < max_cnt ; i + + ) {
2017-04-14 10:30:27 -07:00
ret = connect ( - 1 , ( const struct sockaddr * ) & in6 , sizeof ( in6 ) ) ;
assert ( ret = = - 1 & & errno = = EBADF ) ;
}
printf ( " %d:%s pre-alloc %lld events per sec \n " ,
cpu , test_name ,
2017-04-14 10:30:28 -07:00
max_cnt * 1000000000ll / ( time_get_ns ( ) - start_time ) ) ;
2016-11-11 10:55:11 -08:00
}
2017-04-14 10:30:27 -07:00
static void test_lru_hash_prealloc ( int cpu )
2016-11-11 10:55:11 -08:00
{
2017-04-14 10:30:27 -07:00
do_test_lru ( LRU_HASH_PREALLOC , cpu ) ;
}
2016-11-11 10:55:11 -08:00
2017-04-14 10:30:27 -07:00
static void test_nocommon_lru_hash_prealloc ( int cpu )
{
do_test_lru ( NOCOMMON_LRU_HASH_PREALLOC , cpu ) ;
2016-11-11 10:55:11 -08:00
}
2017-04-14 10:30:30 -07:00
static void test_inner_lru_hash_prealloc ( int cpu )
{
do_test_lru ( INNER_LRU_HASH_PREALLOC , cpu ) ;
}
2016-03-08 15:07:54 -08:00
static void test_percpu_hash_prealloc ( int cpu )
{
__u64 start_time ;
int i ;
start_time = time_get_ns ( ) ;
2017-04-14 10:30:28 -07:00
for ( i = 0 ; i < max_cnt ; i + + )
2016-03-08 15:07:54 -08:00
syscall ( __NR_geteuid ) ;
printf ( " %d:percpu_hash_map_perf pre-alloc %lld events per sec \n " ,
2017-04-14 10:30:28 -07:00
cpu , max_cnt * 1000000000ll / ( time_get_ns ( ) - start_time ) ) ;
2016-03-08 15:07:54 -08:00
}
static void test_hash_kmalloc ( int cpu )
{
__u64 start_time ;
int i ;
start_time = time_get_ns ( ) ;
2017-04-14 10:30:28 -07:00
for ( i = 0 ; i < max_cnt ; i + + )
2016-03-08 15:07:54 -08:00
syscall ( __NR_getgid ) ;
printf ( " %d:hash_map_perf kmalloc %lld events per sec \n " ,
2017-04-14 10:30:28 -07:00
cpu , max_cnt * 1000000000ll / ( time_get_ns ( ) - start_time ) ) ;
2016-03-08 15:07:54 -08:00
}
static void test_percpu_hash_kmalloc ( int cpu )
{
__u64 start_time ;
int i ;
start_time = time_get_ns ( ) ;
2017-04-14 10:30:28 -07:00
for ( i = 0 ; i < max_cnt ; i + + )
2016-03-08 15:07:54 -08:00
syscall ( __NR_getegid ) ;
printf ( " %d:percpu_hash_map_perf kmalloc %lld events per sec \n " ,
2017-04-14 10:30:28 -07:00
cpu , max_cnt * 1000000000ll / ( time_get_ns ( ) - start_time ) ) ;
2016-03-08 15:07:54 -08:00
}
2017-01-21 17:26:13 +01:00
static void test_lpm_kmalloc ( int cpu )
{
__u64 start_time ;
int i ;
start_time = time_get_ns ( ) ;
2017-04-14 10:30:28 -07:00
for ( i = 0 ; i < max_cnt ; i + + )
2017-01-21 17:26:13 +01:00
syscall ( __NR_gettid ) ;
printf ( " %d:lpm_perf kmalloc %lld events per sec \n " ,
2017-04-14 10:30:28 -07:00
cpu , max_cnt * 1000000000ll / ( time_get_ns ( ) - start_time ) ) ;
2017-01-21 17:26:13 +01:00
}
2017-03-15 18:26:44 -07:00
static void test_hash_lookup ( int cpu )
{
__u64 start_time ;
int i ;
start_time = time_get_ns ( ) ;
2017-04-14 10:30:28 -07:00
for ( i = 0 ; i < max_cnt ; i + + )
2017-03-15 18:26:44 -07:00
syscall ( __NR_getpgid , 0 ) ;
printf ( " %d:hash_lookup %lld lookups per sec \n " ,
2017-04-14 10:30:28 -07:00
cpu , max_cnt * 1000000000ll * 64 / ( time_get_ns ( ) - start_time ) ) ;
2017-03-15 18:26:44 -07:00
}
static void test_array_lookup ( int cpu )
{
__u64 start_time ;
int i ;
start_time = time_get_ns ( ) ;
2017-04-14 10:30:28 -07:00
for ( i = 0 ; i < max_cnt ; i + + )
2017-03-15 18:26:44 -07:00
syscall ( __NR_getpgrp , 0 ) ;
printf ( " %d:array_lookup %lld lookups per sec \n " ,
2017-04-14 10:30:28 -07:00
cpu , max_cnt * 1000000000ll * 64 / ( time_get_ns ( ) - start_time ) ) ;
2017-03-15 18:26:44 -07:00
}
2017-04-14 10:30:28 -07:00
typedef void ( * test_func ) ( int cpu ) ;
const test_func test_funcs [ ] = {
[ HASH_PREALLOC ] = test_hash_prealloc ,
[ PERCPU_HASH_PREALLOC ] = test_percpu_hash_prealloc ,
[ HASH_KMALLOC ] = test_hash_kmalloc ,
[ PERCPU_HASH_KMALLOC ] = test_percpu_hash_kmalloc ,
[ LRU_HASH_PREALLOC ] = test_lru_hash_prealloc ,
[ NOCOMMON_LRU_HASH_PREALLOC ] = test_nocommon_lru_hash_prealloc ,
[ LPM_KMALLOC ] = test_lpm_kmalloc ,
[ HASH_LOOKUP ] = test_hash_lookup ,
[ ARRAY_LOOKUP ] = test_array_lookup ,
2017-04-14 10:30:30 -07:00
[ INNER_LRU_HASH_PREALLOC ] = test_inner_lru_hash_prealloc ,
2017-04-14 10:30:28 -07:00
} ;
2016-03-08 15:07:54 -08:00
static void loop ( int cpu )
{
cpu_set_t cpuset ;
2017-04-14 10:30:28 -07:00
int i ;
2016-03-08 15:07:54 -08:00
CPU_ZERO ( & cpuset ) ;
CPU_SET ( cpu , & cpuset ) ;
sched_setaffinity ( 0 , sizeof ( cpuset ) , & cpuset ) ;
2017-04-14 10:30:28 -07:00
for ( i = 0 ; i < NR_TESTS ; i + + ) {
if ( check_test_flags ( i ) )
test_funcs [ i ] ( cpu ) ;
}
2016-03-08 15:07:54 -08:00
}
static void run_perf_test ( int tasks )
{
pid_t pid [ tasks ] ;
int i ;
for ( i = 0 ; i < tasks ; i + + ) {
pid [ i ] = fork ( ) ;
if ( pid [ i ] = = 0 ) {
loop ( i ) ;
exit ( 0 ) ;
} else if ( pid [ i ] = = - 1 ) {
printf ( " couldn't spawn #%d process \n " , i ) ;
exit ( 1 ) ;
}
}
for ( i = 0 ; i < tasks ; i + + ) {
int status ;
assert ( waitpid ( pid [ i ] , & status , 0 ) = = pid [ i ] ) ;
assert ( status = = 0 ) ;
}
}
2017-01-21 17:26:13 +01:00
static void fill_lpm_trie ( void )
{
struct bpf_lpm_trie_key * key ;
unsigned long value = 0 ;
unsigned int i ;
int r ;
key = alloca ( sizeof ( * key ) + 4 ) ;
key - > prefixlen = 32 ;
for ( i = 0 ; i < 512 ; + + i ) {
key - > prefixlen = rand ( ) % 33 ;
key - > data [ 0 ] = rand ( ) & 0xff ;
key - > data [ 1 ] = rand ( ) & 0xff ;
key - > data [ 2 ] = rand ( ) & 0xff ;
key - > data [ 3 ] = rand ( ) & 0xff ;
r = bpf_map_update_elem ( map_fd [ 6 ] , key , & value , 0 ) ;
assert ( ! r ) ;
}
key - > prefixlen = 32 ;
key - > data [ 0 ] = 192 ;
key - > data [ 1 ] = 168 ;
key - > data [ 2 ] = 0 ;
key - > data [ 3 ] = 1 ;
value = 128 ;
r = bpf_map_update_elem ( map_fd [ 6 ] , key , & value , 0 ) ;
assert ( ! r ) ;
}
2017-05-02 14:32:01 +02:00
static void fixup_map ( struct bpf_map_data * map , int idx )
2017-04-14 10:30:28 -07:00
{
int i ;
2017-05-02 14:32:01 +02:00
if ( ! strcmp ( " inner_lru_hash_map " , map - > name ) ) {
2017-04-14 10:30:30 -07:00
inner_lru_hash_idx = idx ;
2017-05-02 14:32:01 +02:00
inner_lru_hash_size = map - > def . max_entries ;
2017-04-14 10:30:30 -07:00
}
2017-05-02 14:32:01 +02:00
if ( ! strcmp ( " array_of_lru_hashs " , map - > name ) ) {
2017-04-14 10:30:30 -07:00
if ( inner_lru_hash_idx = = - 1 ) {
printf ( " inner_lru_hash_map must be defined before array_of_lru_hashs \n " ) ;
exit ( 1 ) ;
}
2017-05-02 14:32:01 +02:00
map - > def . inner_map_idx = inner_lru_hash_idx ;
2017-04-14 10:30:30 -07:00
array_of_lru_hashs_idx = idx ;
}
2017-04-14 10:30:28 -07:00
if ( num_map_entries < = 0 )
return ;
2017-04-14 10:30:30 -07:00
inner_lru_hash_size = num_map_entries ;
2017-04-14 10:30:28 -07:00
/* Only change the max_entries for the enabled test(s) */
for ( i = 0 ; i < NR_TESTS ; i + + ) {
2017-05-02 14:32:01 +02:00
if ( ! strcmp ( test_map_names [ i ] , map - > name ) & &
2017-04-14 10:30:28 -07:00
( check_test_flags ( i ) ) ) {
2017-05-02 14:32:01 +02:00
map - > def . max_entries = num_map_entries ;
2017-04-14 10:30:28 -07:00
}
}
}
2016-03-08 15:07:54 -08:00
int main ( int argc , char * * argv )
{
struct rlimit r = { RLIM_INFINITY , RLIM_INFINITY } ;
char filename [ 256 ] ;
int num_cpu = 8 ;
snprintf ( filename , sizeof ( filename ) , " %s_kern.o " , argv [ 0 ] ) ;
setrlimit ( RLIMIT_MEMLOCK , & r ) ;
if ( argc > 1 )
test_flags = atoi ( argv [ 1 ] ) ? : test_flags ;
if ( argc > 2 )
num_cpu = atoi ( argv [ 2 ] ) ? : num_cpu ;
2017-04-14 10:30:28 -07:00
if ( argc > 3 )
num_map_entries = atoi ( argv [ 3 ] ) ;
if ( argc > 4 )
max_cnt = atoi ( argv [ 4 ] ) ;
if ( load_bpf_file_fixup_map ( filename , fixup_map ) ) {
2016-03-08 15:07:54 -08:00
printf ( " %s " , bpf_log_buf ) ;
return 1 ;
}
2017-01-21 17:26:13 +01:00
fill_lpm_trie ( ) ;
2016-03-08 15:07:54 -08:00
run_perf_test ( num_cpu ) ;
return 0 ;
}