1// SPDX-License-Identifier: GPL-2.0 2/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ 3 4#include <sys/types.h> 5#include <sys/socket.h> 6#include <pthread.h> 7#include <argp.h> 8 9#include "bench.h" 10#include "bench_local_storage_create.skel.h" 11 12struct thread { 13 int *fds; 14 pthread_t *pthds; 15 int *pthd_results; 16}; 17 18static struct bench_local_storage_create *skel; 19static struct thread *threads; 20static long create_owner_errs; 21static int storage_type = BPF_MAP_TYPE_SK_STORAGE; 22static int batch_sz = 32; 23 24enum { 25 ARG_BATCH_SZ = 9000, 26 ARG_STORAGE_TYPE = 9001, 27}; 28 29static const struct argp_option opts[] = { 30 { "batch-size", ARG_BATCH_SZ, "BATCH_SIZE", 0, 31 "The number of storage creations in each batch" }, 32 { "storage-type", ARG_STORAGE_TYPE, "STORAGE_TYPE", 0, 33 "The type of local storage to test (socket or task)" }, 34 {}, 35}; 36 37static error_t parse_arg(int key, char *arg, struct argp_state *state) 38{ 39 int ret; 40 41 switch (key) { 42 case ARG_BATCH_SZ: 43 ret = atoi(arg); 44 if (ret < 1) { 45 fprintf(stderr, "invalid batch-size\n"); 46 argp_usage(state); 47 } 48 batch_sz = ret; 49 break; 50 case ARG_STORAGE_TYPE: 51 if (!strcmp(arg, "task")) { 52 storage_type = BPF_MAP_TYPE_TASK_STORAGE; 53 } else if (!strcmp(arg, "socket")) { 54 storage_type = BPF_MAP_TYPE_SK_STORAGE; 55 } else { 56 fprintf(stderr, "invalid storage-type (socket or task)\n"); 57 argp_usage(state); 58 } 59 break; 60 default: 61 return ARGP_ERR_UNKNOWN; 62 } 63 64 return 0; 65} 66 67const struct argp bench_local_storage_create_argp = { 68 .options = opts, 69 .parser = parse_arg, 70}; 71 72static void validate(void) 73{ 74 if (env.consumer_cnt != 0) { 75 fprintf(stderr, 76 "local-storage-create benchmark does not need consumer\n"); 77 exit(1); 78 } 79} 80 81static void setup(void) 82{ 83 int i; 84 85 skel = bench_local_storage_create__open_and_load(); 86 if (!skel) { 87 fprintf(stderr, "error loading skel\n"); 88 exit(1); 89 } 90 91 skel->bss->bench_pid = getpid(); 92 if (storage_type == BPF_MAP_TYPE_SK_STORAGE) { 93 if (!bpf_program__attach(skel->progs.socket_post_create)) { 94 fprintf(stderr, "Error attaching bpf program\n"); 95 exit(1); 96 } 97 } else { 98 if (!bpf_program__attach(skel->progs.sched_process_fork)) { 99 fprintf(stderr, "Error attaching bpf program\n"); 100 exit(1); 101 } 102 } 103 104 if (!bpf_program__attach(skel->progs.kmalloc)) { 105 fprintf(stderr, "Error attaching bpf program\n"); 106 exit(1); 107 } 108 109 threads = calloc(env.producer_cnt, sizeof(*threads)); 110 111 if (!threads) { 112 fprintf(stderr, "cannot alloc thread_res\n"); 113 exit(1); 114 } 115 116 for (i = 0; i < env.producer_cnt; i++) { 117 struct thread *t = &threads[i]; 118 119 if (storage_type == BPF_MAP_TYPE_SK_STORAGE) { 120 t->fds = malloc(batch_sz * sizeof(*t->fds)); 121 if (!t->fds) { 122 fprintf(stderr, "cannot alloc t->fds\n"); 123 exit(1); 124 } 125 } else { 126 t->pthds = malloc(batch_sz * sizeof(*t->pthds)); 127 if (!t->pthds) { 128 fprintf(stderr, "cannot alloc t->pthds\n"); 129 exit(1); 130 } 131 t->pthd_results = malloc(batch_sz * sizeof(*t->pthd_results)); 132 if (!t->pthd_results) { 133 fprintf(stderr, "cannot alloc t->pthd_results\n"); 134 exit(1); 135 } 136 } 137 } 138} 139 140static void measure(struct bench_res *res) 141{ 142 res->hits = atomic_swap(&skel->bss->create_cnts, 0); 143 res->drops = atomic_swap(&skel->bss->kmalloc_cnts, 0); 144} 145 146static void *sk_producer(void *input) 147{ 148 struct thread *t = &threads[(long)(input)]; 149 int *fds = t->fds; 150 int i; 151 152 while (true) { 153 for (i = 0; i < batch_sz; i++) { 154 fds[i] = socket(AF_INET6, SOCK_DGRAM, 0); 155 if (fds[i] == -1) 156 atomic_inc(&create_owner_errs); 157 } 158 159 for (i = 0; i < batch_sz; i++) { 160 if (fds[i] != -1) 161 close(fds[i]); 162 } 163 } 164 165 return NULL; 166} 167 168static void *thread_func(void *arg) 169{ 170 return NULL; 171} 172 173static void *task_producer(void *input) 174{ 175 struct thread *t = &threads[(long)(input)]; 176 pthread_t *pthds = t->pthds; 177 int *pthd_results = t->pthd_results; 178 int i; 179 180 while (true) { 181 for (i = 0; i < batch_sz; i++) { 182 pthd_results[i] = pthread_create(&pthds[i], NULL, thread_func, NULL); 183 if (pthd_results[i]) 184 atomic_inc(&create_owner_errs); 185 } 186 187 for (i = 0; i < batch_sz; i++) { 188 if (!pthd_results[i]) 189 pthread_join(pthds[i], NULL);; 190 } 191 } 192 193 return NULL; 194} 195 196static void *producer(void *input) 197{ 198 if (storage_type == BPF_MAP_TYPE_SK_STORAGE) 199 return sk_producer(input); 200 else 201 return task_producer(input); 202} 203 204static void report_progress(int iter, struct bench_res *res, long delta_ns) 205{ 206 double creates_per_sec, kmallocs_per_create; 207 208 creates_per_sec = res->hits / 1000.0 / (delta_ns / 1000000000.0); 209 kmallocs_per_create = (double)res->drops / res->hits; 210 211 printf("Iter %3d (%7.3lfus): ", 212 iter, (delta_ns - 1000000000) / 1000.0); 213 printf("creates %8.3lfk/s (%7.3lfk/prod), ", 214 creates_per_sec, creates_per_sec / env.producer_cnt); 215 printf("%3.2lf kmallocs/create\n", kmallocs_per_create); 216} 217 218static void report_final(struct bench_res res[], int res_cnt) 219{ 220 double creates_mean = 0.0, creates_stddev = 0.0; 221 long total_creates = 0, total_kmallocs = 0; 222 int i; 223 224 for (i = 0; i < res_cnt; i++) { 225 creates_mean += res[i].hits / 1000.0 / (0.0 + res_cnt); 226 total_creates += res[i].hits; 227 total_kmallocs += res[i].drops; 228 } 229 230 if (res_cnt > 1) { 231 for (i = 0; i < res_cnt; i++) 232 creates_stddev += (creates_mean - res[i].hits / 1000.0) * 233 (creates_mean - res[i].hits / 1000.0) / 234 (res_cnt - 1.0); 235 creates_stddev = sqrt(creates_stddev); 236 } 237 printf("Summary: creates %8.3lf \u00B1 %5.3lfk/s (%7.3lfk/prod), ", 238 creates_mean, creates_stddev, creates_mean / env.producer_cnt); 239 printf("%4.2lf kmallocs/create\n", (double)total_kmallocs / total_creates); 240 if (create_owner_errs || skel->bss->create_errs) 241 printf("%s() errors %ld create_errs %ld\n", 242 storage_type == BPF_MAP_TYPE_SK_STORAGE ? 243 "socket" : "pthread_create", 244 create_owner_errs, 245 skel->bss->create_errs); 246} 247 248/* Benchmark performance of creating bpf local storage */ 249const struct bench bench_local_storage_create = { 250 .name = "local-storage-create", 251 .argp = &bench_local_storage_create_argp, 252 .validate = validate, 253 .setup = setup, 254 .producer_thread = producer, 255 .measure = measure, 256 .report_progress = report_progress, 257 .report_final = report_final, 258}; 259