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 12 struct thread { 13 int *fds; 14 pthread_t *pthds; 15 int *pthd_results; 16 }; 17 18 static struct bench_local_storage_create *skel; 19 static struct thread *threads; 20 static long create_owner_errs; 21 static int storage_type = BPF_MAP_TYPE_SK_STORAGE; 22 static int batch_sz = 32; 23 24 enum { 25 ARG_BATCH_SZ = 9000, 26 ARG_STORAGE_TYPE = 9001, 27 }; 28 29 static 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 37 static 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 67 const struct argp bench_local_storage_create_argp = { 68 .options = opts, 69 .parser = parse_arg, 70 }; 71 72 static 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 81 static 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 threads = calloc(env.producer_cnt, sizeof(*threads)); 105 106 if (!threads) { 107 fprintf(stderr, "cannot alloc thread_res\n"); 108 exit(1); 109 } 110 111 for (i = 0; i < env.producer_cnt; i++) { 112 struct thread *t = &threads[i]; 113 114 if (storage_type == BPF_MAP_TYPE_SK_STORAGE) { 115 t->fds = malloc(batch_sz * sizeof(*t->fds)); 116 if (!t->fds) { 117 fprintf(stderr, "cannot alloc t->fds\n"); 118 exit(1); 119 } 120 } else { 121 t->pthds = malloc(batch_sz * sizeof(*t->pthds)); 122 if (!t->pthds) { 123 fprintf(stderr, "cannot alloc t->pthds\n"); 124 exit(1); 125 } 126 t->pthd_results = malloc(batch_sz * sizeof(*t->pthd_results)); 127 if (!t->pthd_results) { 128 fprintf(stderr, "cannot alloc t->pthd_results\n"); 129 exit(1); 130 } 131 } 132 } 133 } 134 135 static void measure(struct bench_res *res) 136 { 137 res->hits = atomic_swap(&skel->bss->create_cnts, 0); 138 } 139 140 static void *sk_producer(void *input) 141 { 142 struct thread *t = &threads[(long)(input)]; 143 int *fds = t->fds; 144 int i; 145 146 while (true) { 147 for (i = 0; i < batch_sz; i++) { 148 fds[i] = socket(AF_INET6, SOCK_DGRAM, 0); 149 if (fds[i] == -1) 150 atomic_inc(&create_owner_errs); 151 } 152 153 for (i = 0; i < batch_sz; i++) { 154 if (fds[i] != -1) 155 close(fds[i]); 156 } 157 } 158 159 return NULL; 160 } 161 162 static void *thread_func(void *arg) 163 { 164 return NULL; 165 } 166 167 static void *task_producer(void *input) 168 { 169 struct thread *t = &threads[(long)(input)]; 170 pthread_t *pthds = t->pthds; 171 int *pthd_results = t->pthd_results; 172 int i; 173 174 while (true) { 175 for (i = 0; i < batch_sz; i++) { 176 pthd_results[i] = pthread_create(&pthds[i], NULL, thread_func, NULL); 177 if (pthd_results[i]) 178 atomic_inc(&create_owner_errs); 179 } 180 181 for (i = 0; i < batch_sz; i++) { 182 if (!pthd_results[i]) 183 pthread_join(pthds[i], NULL); 184 } 185 } 186 187 return NULL; 188 } 189 190 static void *producer(void *input) 191 { 192 if (storage_type == BPF_MAP_TYPE_SK_STORAGE) 193 return sk_producer(input); 194 else 195 return task_producer(input); 196 } 197 198 static void report_progress(int iter, struct bench_res *res, long delta_ns) 199 { 200 double creates_per_sec; 201 202 creates_per_sec = res->hits / 1000.0 / (delta_ns / 1000000000.0); 203 204 printf("Iter %3d (%7.3lfus): ", 205 iter, (delta_ns - 1000000000) / 1000.0); 206 printf("creates %8.3lfk/s (%7.3lfk/prod)\n", 207 creates_per_sec, creates_per_sec / env.producer_cnt); 208 } 209 210 static void report_final(struct bench_res res[], int res_cnt) 211 { 212 double creates_mean = 0.0, creates_stddev = 0.0; 213 long total_creates = 0; 214 int i; 215 216 for (i = 0; i < res_cnt; i++) { 217 creates_mean += res[i].hits / 1000.0 / (0.0 + res_cnt); 218 total_creates += res[i].hits; 219 } 220 221 if (res_cnt > 1) { 222 for (i = 0; i < res_cnt; i++) 223 creates_stddev += (creates_mean - res[i].hits / 1000.0) * 224 (creates_mean - res[i].hits / 1000.0) / 225 (res_cnt - 1.0); 226 creates_stddev = sqrt(creates_stddev); 227 } 228 printf("Summary: creates %8.3lf \u00B1 %5.3lfk/s (%7.3lfk/prod), %ld total\n", 229 creates_mean, creates_stddev, creates_mean / env.producer_cnt, 230 total_creates); 231 if (create_owner_errs || skel->bss->create_errs) 232 printf("%s() errors %ld create_errs %ld\n", 233 storage_type == BPF_MAP_TYPE_SK_STORAGE ? 234 "socket" : "pthread_create", 235 create_owner_errs, 236 skel->bss->create_errs); 237 } 238 239 /* Benchmark performance of creating bpf local storage */ 240 const struct bench bench_local_storage_create = { 241 .name = "local-storage-create", 242 .argp = &bench_local_storage_create_argp, 243 .validate = validate, 244 .setup = setup, 245 .producer_thread = producer, 246 .measure = measure, 247 .report_progress = report_progress, 248 .report_final = report_final, 249 }; 250