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 > 1) { 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 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 140 static 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 146 static void *consumer(void *input) 147 { 148 return NULL; 149 } 150 151 static void *sk_producer(void *input) 152 { 153 struct thread *t = &threads[(long)(input)]; 154 int *fds = t->fds; 155 int i; 156 157 while (true) { 158 for (i = 0; i < batch_sz; i++) { 159 fds[i] = socket(AF_INET6, SOCK_DGRAM, 0); 160 if (fds[i] == -1) 161 atomic_inc(&create_owner_errs); 162 } 163 164 for (i = 0; i < batch_sz; i++) { 165 if (fds[i] != -1) 166 close(fds[i]); 167 } 168 } 169 170 return NULL; 171 } 172 173 static void *thread_func(void *arg) 174 { 175 return NULL; 176 } 177 178 static void *task_producer(void *input) 179 { 180 struct thread *t = &threads[(long)(input)]; 181 pthread_t *pthds = t->pthds; 182 int *pthd_results = t->pthd_results; 183 int i; 184 185 while (true) { 186 for (i = 0; i < batch_sz; i++) { 187 pthd_results[i] = pthread_create(&pthds[i], NULL, thread_func, NULL); 188 if (pthd_results[i]) 189 atomic_inc(&create_owner_errs); 190 } 191 192 for (i = 0; i < batch_sz; i++) { 193 if (!pthd_results[i]) 194 pthread_join(pthds[i], NULL);; 195 } 196 } 197 198 return NULL; 199 } 200 201 static void *producer(void *input) 202 { 203 if (storage_type == BPF_MAP_TYPE_SK_STORAGE) 204 return sk_producer(input); 205 else 206 return task_producer(input); 207 } 208 209 static void report_progress(int iter, struct bench_res *res, long delta_ns) 210 { 211 double creates_per_sec, kmallocs_per_create; 212 213 creates_per_sec = res->hits / 1000.0 / (delta_ns / 1000000000.0); 214 kmallocs_per_create = (double)res->drops / res->hits; 215 216 printf("Iter %3d (%7.3lfus): ", 217 iter, (delta_ns - 1000000000) / 1000.0); 218 printf("creates %8.3lfk/s (%7.3lfk/prod), ", 219 creates_per_sec, creates_per_sec / env.producer_cnt); 220 printf("%3.2lf kmallocs/create\n", kmallocs_per_create); 221 } 222 223 static void report_final(struct bench_res res[], int res_cnt) 224 { 225 double creates_mean = 0.0, creates_stddev = 0.0; 226 long total_creates = 0, total_kmallocs = 0; 227 int i; 228 229 for (i = 0; i < res_cnt; i++) { 230 creates_mean += res[i].hits / 1000.0 / (0.0 + res_cnt); 231 total_creates += res[i].hits; 232 total_kmallocs += res[i].drops; 233 } 234 235 if (res_cnt > 1) { 236 for (i = 0; i < res_cnt; i++) 237 creates_stddev += (creates_mean - res[i].hits / 1000.0) * 238 (creates_mean - res[i].hits / 1000.0) / 239 (res_cnt - 1.0); 240 creates_stddev = sqrt(creates_stddev); 241 } 242 printf("Summary: creates %8.3lf \u00B1 %5.3lfk/s (%7.3lfk/prod), ", 243 creates_mean, creates_stddev, creates_mean / env.producer_cnt); 244 printf("%4.2lf kmallocs/create\n", (double)total_kmallocs / total_creates); 245 if (create_owner_errs || skel->bss->create_errs) 246 printf("%s() errors %ld create_errs %ld\n", 247 storage_type == BPF_MAP_TYPE_SK_STORAGE ? 248 "socket" : "pthread_create", 249 create_owner_errs, 250 skel->bss->create_errs); 251 } 252 253 /* Benchmark performance of creating bpf local storage */ 254 const struct bench bench_local_storage_create = { 255 .name = "local-storage-create", 256 .argp = &bench_local_storage_create_argp, 257 .validate = validate, 258 .setup = setup, 259 .producer_thread = producer, 260 .consumer_thread = consumer, 261 .measure = measure, 262 .report_progress = report_progress, 263 .report_final = report_final, 264 }; 265