1 /* Copyright (c) 2016 Facebook 2 * 3 * This program is free software; you can redistribute it and/or 4 * modify it under the terms of version 2 of the GNU General Public 5 * License as published by the Free Software Foundation. 6 */ 7 #define _GNU_SOURCE 8 #include <sched.h> 9 #include <stdio.h> 10 #include <sys/types.h> 11 #include <asm/unistd.h> 12 #include <unistd.h> 13 #include <assert.h> 14 #include <sys/wait.h> 15 #include <stdlib.h> 16 #include <signal.h> 17 #include <linux/bpf.h> 18 #include <string.h> 19 #include <time.h> 20 #include <sys/resource.h> 21 #include "libbpf.h" 22 #include "bpf_load.h" 23 24 #define MAX_CNT 1000000 25 26 static __u64 time_get_ns(void) 27 { 28 struct timespec ts; 29 30 clock_gettime(CLOCK_MONOTONIC, &ts); 31 return ts.tv_sec * 1000000000ull + ts.tv_nsec; 32 } 33 34 #define HASH_PREALLOC (1 << 0) 35 #define PERCPU_HASH_PREALLOC (1 << 1) 36 #define HASH_KMALLOC (1 << 2) 37 #define PERCPU_HASH_KMALLOC (1 << 3) 38 #define LRU_HASH_PREALLOC (1 << 4) 39 #define PERCPU_LRU_HASH_PREALLOC (1 << 5) 40 #define LPM_KMALLOC (1 << 6) 41 42 static int test_flags = ~0; 43 44 static void test_hash_prealloc(int cpu) 45 { 46 __u64 start_time; 47 int i; 48 49 start_time = time_get_ns(); 50 for (i = 0; i < MAX_CNT; i++) 51 syscall(__NR_getuid); 52 printf("%d:hash_map_perf pre-alloc %lld events per sec\n", 53 cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); 54 } 55 56 static void test_lru_hash_prealloc(int cpu) 57 { 58 __u64 start_time; 59 int i; 60 61 start_time = time_get_ns(); 62 for (i = 0; i < MAX_CNT; i++) 63 syscall(__NR_getpid); 64 printf("%d:lru_hash_map_perf pre-alloc %lld events per sec\n", 65 cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); 66 } 67 68 static void test_percpu_lru_hash_prealloc(int cpu) 69 { 70 __u64 start_time; 71 int i; 72 73 start_time = time_get_ns(); 74 for (i = 0; i < MAX_CNT; i++) 75 syscall(__NR_getppid); 76 printf("%d:lru_hash_map_perf pre-alloc %lld events per sec\n", 77 cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); 78 } 79 80 static void test_percpu_hash_prealloc(int cpu) 81 { 82 __u64 start_time; 83 int i; 84 85 start_time = time_get_ns(); 86 for (i = 0; i < MAX_CNT; i++) 87 syscall(__NR_geteuid); 88 printf("%d:percpu_hash_map_perf pre-alloc %lld events per sec\n", 89 cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); 90 } 91 92 static void test_hash_kmalloc(int cpu) 93 { 94 __u64 start_time; 95 int i; 96 97 start_time = time_get_ns(); 98 for (i = 0; i < MAX_CNT; i++) 99 syscall(__NR_getgid); 100 printf("%d:hash_map_perf kmalloc %lld events per sec\n", 101 cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); 102 } 103 104 static void test_percpu_hash_kmalloc(int cpu) 105 { 106 __u64 start_time; 107 int i; 108 109 start_time = time_get_ns(); 110 for (i = 0; i < MAX_CNT; i++) 111 syscall(__NR_getegid); 112 printf("%d:percpu_hash_map_perf kmalloc %lld events per sec\n", 113 cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); 114 } 115 116 static void test_lpm_kmalloc(int cpu) 117 { 118 __u64 start_time; 119 int i; 120 121 start_time = time_get_ns(); 122 for (i = 0; i < MAX_CNT; i++) 123 syscall(__NR_gettid); 124 printf("%d:lpm_perf kmalloc %lld events per sec\n", 125 cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); 126 } 127 128 static void loop(int cpu) 129 { 130 cpu_set_t cpuset; 131 132 CPU_ZERO(&cpuset); 133 CPU_SET(cpu, &cpuset); 134 sched_setaffinity(0, sizeof(cpuset), &cpuset); 135 136 if (test_flags & HASH_PREALLOC) 137 test_hash_prealloc(cpu); 138 139 if (test_flags & PERCPU_HASH_PREALLOC) 140 test_percpu_hash_prealloc(cpu); 141 142 if (test_flags & HASH_KMALLOC) 143 test_hash_kmalloc(cpu); 144 145 if (test_flags & PERCPU_HASH_KMALLOC) 146 test_percpu_hash_kmalloc(cpu); 147 148 if (test_flags & LRU_HASH_PREALLOC) 149 test_lru_hash_prealloc(cpu); 150 151 if (test_flags & PERCPU_LRU_HASH_PREALLOC) 152 test_percpu_lru_hash_prealloc(cpu); 153 154 if (test_flags & LPM_KMALLOC) 155 test_lpm_kmalloc(cpu); 156 } 157 158 static void run_perf_test(int tasks) 159 { 160 pid_t pid[tasks]; 161 int i; 162 163 for (i = 0; i < tasks; i++) { 164 pid[i] = fork(); 165 if (pid[i] == 0) { 166 loop(i); 167 exit(0); 168 } else if (pid[i] == -1) { 169 printf("couldn't spawn #%d process\n", i); 170 exit(1); 171 } 172 } 173 for (i = 0; i < tasks; i++) { 174 int status; 175 176 assert(waitpid(pid[i], &status, 0) == pid[i]); 177 assert(status == 0); 178 } 179 } 180 181 static void fill_lpm_trie(void) 182 { 183 struct bpf_lpm_trie_key *key; 184 unsigned long value = 0; 185 unsigned int i; 186 int r; 187 188 key = alloca(sizeof(*key) + 4); 189 key->prefixlen = 32; 190 191 for (i = 0; i < 512; ++i) { 192 key->prefixlen = rand() % 33; 193 key->data[0] = rand() & 0xff; 194 key->data[1] = rand() & 0xff; 195 key->data[2] = rand() & 0xff; 196 key->data[3] = rand() & 0xff; 197 r = bpf_map_update_elem(map_fd[6], key, &value, 0); 198 assert(!r); 199 } 200 201 key->prefixlen = 32; 202 key->data[0] = 192; 203 key->data[1] = 168; 204 key->data[2] = 0; 205 key->data[3] = 1; 206 value = 128; 207 208 r = bpf_map_update_elem(map_fd[6], key, &value, 0); 209 assert(!r); 210 } 211 212 int main(int argc, char **argv) 213 { 214 struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; 215 char filename[256]; 216 int num_cpu = 8; 217 218 snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 219 setrlimit(RLIMIT_MEMLOCK, &r); 220 221 if (argc > 1) 222 test_flags = atoi(argv[1]) ? : test_flags; 223 224 if (argc > 2) 225 num_cpu = atoi(argv[2]) ? : num_cpu; 226 227 if (load_bpf_file(filename)) { 228 printf("%s", bpf_log_buf); 229 return 1; 230 } 231 232 fill_lpm_trie(); 233 234 run_perf_test(num_cpu); 235 236 return 0; 237 } 238