1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2023 Isovalent */ 3 4 #include <sys/random.h> 5 #include <argp.h> 6 #include "bench.h" 7 #include "bpf_hashmap_lookup.skel.h" 8 #include "bpf_util.h" 9 10 /* BPF triggering benchmarks */ 11 static struct ctx { 12 struct bpf_hashmap_lookup *skel; 13 } ctx; 14 15 /* only available to kernel, so define it here */ 16 #define BPF_MAX_LOOPS (1<<23) 17 18 #define MAX_KEY_SIZE 1024 /* the size of the key map */ 19 20 static struct { 21 __u32 key_size; 22 __u32 map_flags; 23 __u32 max_entries; 24 __u32 nr_entries; 25 __u32 nr_loops; 26 } args = { 27 .key_size = 4, 28 .map_flags = 0, 29 .max_entries = 1000, 30 .nr_entries = 500, 31 .nr_loops = 1000000, 32 }; 33 34 enum { 35 ARG_KEY_SIZE = 8001, 36 ARG_MAP_FLAGS, 37 ARG_MAX_ENTRIES, 38 ARG_NR_ENTRIES, 39 ARG_NR_LOOPS, 40 }; 41 42 static const struct argp_option opts[] = { 43 { "key_size", ARG_KEY_SIZE, "KEY_SIZE", 0, 44 "The hashmap key size (max 1024)"}, 45 { "map_flags", ARG_MAP_FLAGS, "MAP_FLAGS", 0, 46 "The hashmap flags passed to BPF_MAP_CREATE"}, 47 { "max_entries", ARG_MAX_ENTRIES, "MAX_ENTRIES", 0, 48 "The hashmap max entries"}, 49 { "nr_entries", ARG_NR_ENTRIES, "NR_ENTRIES", 0, 50 "The number of entries to insert/lookup"}, 51 { "nr_loops", ARG_NR_LOOPS, "NR_LOOPS", 0, 52 "The number of loops for the benchmark"}, 53 {}, 54 }; 55 56 static error_t parse_arg(int key, char *arg, struct argp_state *state) 57 { 58 long ret; 59 60 switch (key) { 61 case ARG_KEY_SIZE: 62 ret = strtol(arg, NULL, 10); 63 if (ret < 1 || ret > MAX_KEY_SIZE) { 64 fprintf(stderr, "invalid key_size"); 65 argp_usage(state); 66 } 67 args.key_size = ret; 68 break; 69 case ARG_MAP_FLAGS: 70 ret = strtol(arg, NULL, 0); 71 if (ret < 0 || ret > UINT_MAX) { 72 fprintf(stderr, "invalid map_flags"); 73 argp_usage(state); 74 } 75 args.map_flags = ret; 76 break; 77 case ARG_MAX_ENTRIES: 78 ret = strtol(arg, NULL, 10); 79 if (ret < 1 || ret > UINT_MAX) { 80 fprintf(stderr, "invalid max_entries"); 81 argp_usage(state); 82 } 83 args.max_entries = ret; 84 break; 85 case ARG_NR_ENTRIES: 86 ret = strtol(arg, NULL, 10); 87 if (ret < 1 || ret > UINT_MAX) { 88 fprintf(stderr, "invalid nr_entries"); 89 argp_usage(state); 90 } 91 args.nr_entries = ret; 92 break; 93 case ARG_NR_LOOPS: 94 ret = strtol(arg, NULL, 10); 95 if (ret < 1 || ret > BPF_MAX_LOOPS) { 96 fprintf(stderr, "invalid nr_loops: %ld (min=1 max=%u)\n", 97 ret, BPF_MAX_LOOPS); 98 argp_usage(state); 99 } 100 args.nr_loops = ret; 101 break; 102 default: 103 return ARGP_ERR_UNKNOWN; 104 } 105 106 return 0; 107 } 108 109 const struct argp bench_hashmap_lookup_argp = { 110 .options = opts, 111 .parser = parse_arg, 112 }; 113 114 static void validate(void) 115 { 116 if (env.consumer_cnt != 0) { 117 fprintf(stderr, "benchmark doesn't support consumer!\n"); 118 exit(1); 119 } 120 121 if (args.nr_entries > args.max_entries) { 122 fprintf(stderr, "args.nr_entries is too big! (max %u, got %u)\n", 123 args.max_entries, args.nr_entries); 124 exit(1); 125 } 126 } 127 128 static void *producer(void *input) 129 { 130 while (true) { 131 /* trigger the bpf program */ 132 syscall(__NR_getpgid); 133 } 134 return NULL; 135 } 136 137 static void measure(struct bench_res *res) 138 { 139 } 140 141 static inline void patch_key(u32 i, u32 *key) 142 { 143 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 144 *key = i + 1; 145 #else 146 *key = __builtin_bswap32(i + 1); 147 #endif 148 /* the rest of key is random */ 149 } 150 151 static void setup(void) 152 { 153 struct bpf_link *link; 154 int map_fd; 155 int ret; 156 int i; 157 158 setup_libbpf(); 159 160 ctx.skel = bpf_hashmap_lookup__open(); 161 if (!ctx.skel) { 162 fprintf(stderr, "failed to open skeleton\n"); 163 exit(1); 164 } 165 166 bpf_map__set_max_entries(ctx.skel->maps.hash_map_bench, args.max_entries); 167 bpf_map__set_key_size(ctx.skel->maps.hash_map_bench, args.key_size); 168 bpf_map__set_value_size(ctx.skel->maps.hash_map_bench, 8); 169 bpf_map__set_map_flags(ctx.skel->maps.hash_map_bench, args.map_flags); 170 171 ctx.skel->bss->nr_entries = args.nr_entries; 172 ctx.skel->bss->nr_loops = args.nr_loops / args.nr_entries; 173 174 if (args.key_size > 4) { 175 for (i = 1; i < args.key_size/4; i++) 176 ctx.skel->bss->key[i] = 2654435761 * i; 177 } 178 179 ret = bpf_hashmap_lookup__load(ctx.skel); 180 if (ret) { 181 bpf_hashmap_lookup__destroy(ctx.skel); 182 fprintf(stderr, "failed to load map: %s", strerror(-ret)); 183 exit(1); 184 } 185 186 /* fill in the hash_map */ 187 map_fd = bpf_map__fd(ctx.skel->maps.hash_map_bench); 188 for (u64 i = 0; i < args.nr_entries; i++) { 189 patch_key(i, ctx.skel->bss->key); 190 bpf_map_update_elem(map_fd, ctx.skel->bss->key, &i, BPF_ANY); 191 } 192 193 link = bpf_program__attach(ctx.skel->progs.benchmark); 194 if (!link) { 195 fprintf(stderr, "failed to attach program!\n"); 196 exit(1); 197 } 198 } 199 200 static inline double events_from_time(u64 time) 201 { 202 if (time) 203 return args.nr_loops * 1000000000llu / time / 1000000.0L; 204 205 return 0; 206 } 207 208 static int compute_events(u64 *times, double *events_mean, double *events_stddev, u64 *mean_time) 209 { 210 int i, n = 0; 211 212 *events_mean = 0; 213 *events_stddev = 0; 214 *mean_time = 0; 215 216 for (i = 0; i < 32; i++) { 217 if (!times[i]) 218 break; 219 *mean_time += times[i]; 220 *events_mean += events_from_time(times[i]); 221 n += 1; 222 } 223 if (!n) 224 return 0; 225 226 *mean_time /= n; 227 *events_mean /= n; 228 229 if (n > 1) { 230 for (i = 0; i < n; i++) { 231 double events_i = *events_mean - events_from_time(times[i]); 232 *events_stddev += events_i * events_i / (n - 1); 233 } 234 *events_stddev = sqrt(*events_stddev); 235 } 236 237 return n; 238 } 239 240 static void hashmap_report_final(struct bench_res res[], int res_cnt) 241 { 242 unsigned int nr_cpus = bpf_num_possible_cpus(); 243 double events_mean, events_stddev; 244 u64 mean_time; 245 int i, n; 246 247 for (i = 0; i < nr_cpus; i++) { 248 n = compute_events(ctx.skel->bss->percpu_times[i], &events_mean, 249 &events_stddev, &mean_time); 250 if (n == 0) 251 continue; 252 253 if (env.quiet) { 254 /* we expect only one cpu to be present */ 255 if (env.affinity) 256 printf("%.3lf\n", events_mean); 257 else 258 printf("cpu%02d %.3lf\n", i, events_mean); 259 } else { 260 printf("cpu%02d: lookup %.3lfM ± %.3lfM events/sec" 261 " (approximated from %d samples of ~%lums)\n", 262 i, events_mean, 2*events_stddev, 263 n, mean_time / 1000000); 264 } 265 } 266 } 267 268 const struct bench bench_bpf_hashmap_lookup = { 269 .name = "bpf-hashmap-lookup", 270 .argp = &bench_hashmap_lookup_argp, 271 .validate = validate, 272 .setup = setup, 273 .producer_thread = producer, 274 .measure = measure, 275 .report_progress = NULL, 276 .report_final = hashmap_report_final, 277 }; 278