Merge tag 'v3.14.25' into backport/v3.14.24-ltsi-rc1+v3.14.25/snapshot-merge.wip
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / staging / ktap / test / benchmark / sembench.c
1 /*
2  * copyright Oracle 2007.  Licensed under GPLv2
3  * To compile: gcc -Wall -o sembench sembench.c -lpthread
4  *
5  * usage: sembench -t thread count -w wakenum -r runtime -o op
6  * op can be: 0 (ipc sem) 1 (nanosleep) 2 (futexes)
7  *
8  * example:
9  *      sembench -t 1024 -w 512 -r 60 -o 2
10  * runs 1024 threads, waking up 512 at a time, running for 60 seconds using
11  * futex locking.
12  *
13  */
14 #define  _GNU_SOURCE
15 #define _POSIX_C_SOURCE 199309
16 #include <fcntl.h>
17 #include <sched.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <sys/sem.h>
21 #include <sys/ipc.h>
22 #include <sys/types.h>
23 #include <sys/mman.h>
24 #include <pthread.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <time.h>
28 #include <sys/time.h>
29 #include <sys/syscall.h>
30 #include <errno.h>
31
32 #define VERSION "0.2"
33
34 /* futexes have been around since 2.5.something, but it still seems I
35  * need to make my own syscall.  Sigh.
36  */
37 #define FUTEX_WAIT              0
38 #define FUTEX_WAKE              1
39 #define FUTEX_FD                2
40 #define FUTEX_REQUEUE           3
41 #define FUTEX_CMP_REQUEUE       4
42 #define FUTEX_WAKE_OP           5
43 static inline int futex (int *uaddr, int op, int val,
44                          const struct timespec *timeout,
45                          int *uaddr2, int val3)
46 {
47         return syscall(__NR_futex, uaddr, op, val, timeout, uaddr2, val3);
48 }
49
50 static void smp_mb(void)
51 {
52         __sync_synchronize();
53 }
54
55 static int all_done = 0;
56 static int timeout_test = 0;
57
58 #define SEMS_PERID 250
59
60 struct sem_operations;
61
62 struct lockinfo {
63         unsigned long id;
64         unsigned long index;
65         int data;
66         pthread_t tid;
67         struct lockinfo *next;
68         struct sem_operations *ops;
69         unsigned long ready;
70 };
71
72 struct sem_wakeup_info {
73         int wakeup_count;
74         struct sembuf sb[SEMS_PERID];
75 };
76
77 struct sem_operations {
78         void (*wait)(struct lockinfo *l);
79         int (*wake)(struct sem_wakeup_info *wi, int num_semids, int num);
80         void (*setup)(struct sem_wakeup_info **wi, int num_semids);
81         void (*cleanup)(int num_semids);
82         char *name;
83 };
84
85 int *semid_lookup = NULL;
86
87 pthread_mutex_t worklist_mutex = PTHREAD_MUTEX_INITIALIZER;
88 static unsigned long total_burns = 0;
89 static unsigned long min_burns = ~0UL;
90 static unsigned long max_burns = 0;
91
92 /* currently running threads */
93 static int thread_count = 0;
94
95 struct lockinfo *worklist = NULL;
96 static int workers_started = 0;
97
98 /* total threads started */
99 static int num_threads = 2048;
100
101 static void worklist_add(struct lockinfo *l)
102 {
103         smp_mb();
104         l->ready = 1;
105 }
106
107 static struct lockinfo *worklist_rm(void)
108 {
109         static int last_index = 0;
110         int i;
111         struct lockinfo *l;
112
113         for (i = 0; i < num_threads; i++) {
114                 int test = (last_index + i) % num_threads;
115
116                 l = worklist + test;
117                 smp_mb();
118                 if (l->ready) {
119                         l->ready = 0;
120                         last_index = test;
121                         return l;
122                 }
123         }
124         return NULL;
125 }
126
127 /* ipc semaphore post& wait */
128 void wait_ipc_sem(struct lockinfo *l)
129 {
130         struct sembuf sb;
131         int ret;
132         struct timespec *tvp = NULL;
133         struct timespec tv = { 0, 1 };
134
135         sb.sem_num = l->index;
136         sb.sem_flg = 0;
137
138         sb.sem_op = -1;
139         l->data = 1;
140
141         if (timeout_test && (l->id % 5) == 0)
142                 tvp = &tv;
143
144         worklist_add(l);
145         ret = semtimedop(semid_lookup[l->id], &sb, 1, tvp);
146
147         while(l->data != 0 && tvp) {
148                 struct timespec tv2 = { 0, 500 };
149                 nanosleep(&tv2, NULL);
150         }
151
152         if (l->data != 0) {
153                 if (tvp)
154                         return;
155                 fprintf(stderr, "wakeup without data update\n");
156                 exit(1);
157         }
158         if (ret) {
159                 if (errno == EAGAIN && tvp)
160                         return;
161                 perror("semtimed op");
162                 exit(1);
163         }
164 }
165
166 int ipc_wake_some(struct sem_wakeup_info *wi, int num_semids, int num)
167 {
168         int i;
169         int ret;
170         struct lockinfo *l;
171         int found = 0;
172
173         for (i = 0; i < num_semids; i++) {
174                 wi[i].wakeup_count = 0;
175         }
176         while(num > 0) {
177                 struct sembuf *sb;
178                 l = worklist_rm();
179                 if (!l)
180                         break;
181                 if (l->data != 1)
182                         fprintf(stderr, "warning, lockinfo data was %d\n",
183                                 l->data);
184                 l->data = 0;
185                 sb = wi[l->id].sb + wi[l->id].wakeup_count;
186                 sb->sem_num = l->index;
187                 sb->sem_op = 1;
188                 sb->sem_flg = IPC_NOWAIT;
189                 wi[l->id].wakeup_count++;
190                 found++;
191                 num--;
192         }
193         if (!found)
194                 return 0;
195         for (i = 0; i < num_semids; i++) {
196                 int wakeup_total;
197                 int cur;
198                 int offset = 0;
199                 if (!wi[i].wakeup_count)
200                         continue;
201                 wakeup_total = wi[i].wakeup_count;
202                 while(wakeup_total > 0) {
203                         cur = wakeup_total > 64 ? 64 : wakeup_total;
204                         ret = semtimedop(semid_lookup[i], wi[i].sb + offset,
205                                          cur, NULL);
206                         if (ret) {
207                                 perror("semtimedop");
208                                 exit(1);
209                         }
210                         offset += cur;
211                         wakeup_total -= cur;
212                 }
213         }
214         return found;
215 }
216
217 void setup_ipc_sems(struct sem_wakeup_info **wi, int num_semids)
218 {
219         int i;
220         *wi = malloc(sizeof(**wi) * num_semids);
221         semid_lookup = malloc(num_semids * sizeof(int));
222         for(i = 0; i < num_semids; i++) {
223                 semid_lookup[i] = semget(IPC_PRIVATE, SEMS_PERID,
224                                          IPC_CREAT | 0777);
225                 if (semid_lookup[i] < 0) {
226                         perror("semget");
227                         exit(1);
228                 }
229         }
230         sleep(10);
231 }
232
233 void cleanup_ipc_sems(int num)
234 {
235         int i;
236         for (i = 0; i < num; i++) {
237                 semctl(semid_lookup[i], 0, IPC_RMID);
238         }
239 }
240
241 struct sem_operations ipc_sem_ops = {
242         .wait = wait_ipc_sem,
243         .wake = ipc_wake_some,
244         .setup = setup_ipc_sems,
245         .cleanup = cleanup_ipc_sems,
246         .name = "ipc sem operations",
247 };
248
249 /* futex post & wait */
250 void wait_futex_sem(struct lockinfo *l)
251 {
252         int ret;
253         l->data = 1;
254         worklist_add(l);
255         while(l->data == 1) {
256                 ret = futex(&l->data, FUTEX_WAIT, 1, NULL, NULL, 0);
257                 /*
258                 if (ret && ret != EWOULDBLOCK) {
259                         perror("futex wait");
260                         exit(1);
261                 }*/
262         }
263 }
264
265 int futex_wake_some(struct sem_wakeup_info *wi, int num_semids, int num)
266 {
267         int i;
268         int ret;
269         struct lockinfo *l;
270         int found = 0;
271
272         for (i = 0; i < num; i++) {
273                 l = worklist_rm();
274                 if (!l)
275                         break;
276                 if (l->data != 1)
277                         fprintf(stderr, "warning, lockinfo data was %d\n",
278                                 l->data);
279                 l->data = 0;
280                 ret = futex(&l->data, FUTEX_WAKE, 1, NULL, NULL, 0);
281                 if (ret < 0) {
282                         perror("futex wake");
283                         exit(1);
284                 }
285                 found++;
286         }
287         return found;
288 }
289
290 void setup_futex_sems(struct sem_wakeup_info **wi, int num_semids)
291 {
292         return;
293 }
294
295 void cleanup_futex_sems(int num)
296 {
297         return;
298 }
299
300 struct sem_operations futex_sem_ops = {
301         .wait = wait_futex_sem,
302         .wake = futex_wake_some,
303         .setup = setup_futex_sems,
304         .cleanup = cleanup_futex_sems,
305         .name = "futex sem operations",
306 };
307
308 /* nanosleep sems here */
309 void wait_nanosleep_sem(struct lockinfo *l)
310 {
311         int ret;
312         struct timespec tv = { 0, 1000000 };
313         int count = 0;
314
315         l->data = 1;
316         worklist_add(l);
317         while(l->data) {
318                 ret = nanosleep(&tv, NULL);
319                 if (ret) {
320                         perror("nanosleep");
321                         exit(1);
322                 }
323                 count++;
324         }
325 }
326
327 int nanosleep_wake_some(struct sem_wakeup_info *wi, int num_semids, int num)
328 {
329         int i;
330         struct lockinfo *l;
331
332         for (i = 0; i < num; i++) {
333                 l = worklist_rm();
334                 if (!l)
335                         break;
336                 if (l->data != 1)
337                         fprintf(stderr, "warning, lockinfo data was %d\n",
338                                 l->data);
339                 l->data = 0;
340         }
341         return i;
342 }
343
344 void setup_nanosleep_sems(struct sem_wakeup_info **wi, int num_semids)
345 {
346         return;
347 }
348
349 void cleanup_nanosleep_sems(int num)
350 {
351         return;
352 }
353
354 struct sem_operations nanosleep_sem_ops = {
355         .wait = wait_nanosleep_sem,
356         .wake = nanosleep_wake_some,
357         .setup = setup_nanosleep_sems,
358         .cleanup = cleanup_nanosleep_sems,
359         .name = "nano sleep sem operations",
360 };
361
362 void *worker(void *arg)
363 {
364         struct lockinfo *l = (struct lockinfo *)arg;
365         int burn_count = 0;
366         pthread_t tid = pthread_self();
367         size_t pagesize = getpagesize();
368         char *buf = malloc(pagesize);
369
370         if (!buf) {
371                 perror("malloc");
372                 exit(1);
373         }
374
375         l->tid = tid;
376         workers_started = 1;
377         smp_mb();
378
379         while(!all_done) {
380                 l->ops->wait(l);
381                 if (all_done)
382                         break;
383                 burn_count++;
384         }
385         pthread_mutex_lock(&worklist_mutex);
386         total_burns += burn_count;
387         if (burn_count < min_burns)
388                 min_burns = burn_count;
389         if (burn_count > max_burns)
390                 max_burns = burn_count;
391         thread_count--;
392         pthread_mutex_unlock(&worklist_mutex);
393         return (void *)0;
394 }
395
396 void print_usage(void)
397 {
398         printf("usage: sembench [-t threads] [-w wake incr] [-r runtime]");
399         printf("                [-o num] (0=ipc, 1=nanosleep, 2=futex)\n");
400         exit(1);
401 }
402
403 #define NUM_OPERATIONS 3
404 struct sem_operations *allops[NUM_OPERATIONS] = { &ipc_sem_ops,
405                                                 &nanosleep_sem_ops,
406                                                 &futex_sem_ops};
407
408 int main(int ac, char **av) {
409         int ret;
410         int i;
411         int semid = 0;
412         int sem_num = 0;
413         int burn_count = 0;
414         struct sem_wakeup_info *wi = NULL;
415         struct timeval start;
416         struct timeval now;
417         int num_semids = 0;
418         int wake_num = 256;
419         int run_secs = 30;
420         int pagesize = getpagesize();
421         char *buf = malloc(pagesize);
422         struct sem_operations *ops = allops[0];
423         cpu_set_t cpu_mask;
424         cpu_set_t target_mask;
425         int target_cpu = 0;
426         int max_cpu = -1;
427
428         if (!buf) {
429                 perror("malloc");
430                 exit(1);
431         }
432         for (i = 1; i < ac; i++) {
433                 if (strcmp(av[i], "-t") == 0) {
434                         if (i == ac -1)
435                                 print_usage();
436                         num_threads = atoi(av[i+1]);
437                         i++;
438                 } else if (strcmp(av[i], "-w") == 0) {
439                         if (i == ac -1)
440                                 print_usage();
441                         wake_num = atoi(av[i+1]);
442                         i++;
443                 } else if (strcmp(av[i], "-r") == 0) {
444                         if (i == ac -1)
445                                 print_usage();
446                         run_secs = atoi(av[i+1]);
447                         i++;
448                 } else if (strcmp(av[i], "-o") == 0) {
449                         int index;
450                         if (i == ac -1)
451                                 print_usage();
452                         index = atoi(av[i+1]);
453                         if (index >= NUM_OPERATIONS) {
454                                 fprintf(stderr, "invalid operations %d\n",
455                                         index);
456                                 exit(1);
457                         }
458                         ops = allops[index];
459                         i++;
460                 } else if (strcmp(av[i], "-T") == 0) {
461                         timeout_test = 1;
462                 } else if (strcmp(av[i], "-h") == 0) {
463                         print_usage();
464                 }
465         }
466         num_semids = (num_threads + SEMS_PERID - 1) / SEMS_PERID;
467         ops->setup(&wi, num_semids);
468
469         ret = sched_getaffinity(0, sizeof(cpu_set_t), &cpu_mask);
470         if (ret) {
471                 perror("sched_getaffinity");
472                 exit(1);
473         }
474         for (i = 0; i < CPU_SETSIZE; i++)
475                 if (CPU_ISSET(i, &cpu_mask))
476                         max_cpu = i;
477         if (max_cpu == -1) {
478                 fprintf(stderr, "sched_getaffinity returned empty mask\n");
479                 exit(1);
480         }
481
482         CPU_ZERO(&target_mask);
483
484         worklist = malloc(sizeof(*worklist) * num_threads);
485         memset(worklist, 0, sizeof(*worklist) * num_threads);
486
487         for (i = 0; i < num_threads; i++) {
488                 struct lockinfo *l;
489                 pthread_t tid;
490                 thread_count++;
491                 l = worklist + i;
492                 if (!l) {
493                         perror("malloc");
494                         exit(1);
495                 }
496                 l->id = semid;
497                 l->index = sem_num++;
498                 l->ops = ops;
499                 if (sem_num >= SEMS_PERID) {
500                         semid++;
501                         sem_num = 0;
502                 }
503                 ret = pthread_create(&tid, NULL, worker, (void *)l);
504                 if (ret) {
505                         perror("pthread_create");
506                         exit(1);
507                 }
508
509                 while (!CPU_ISSET(target_cpu, &cpu_mask)) {
510                         target_cpu++;
511                         if (target_cpu > max_cpu)
512                                 target_cpu = 0;
513                 }
514                 CPU_SET(target_cpu, &target_mask);
515                 ret = pthread_setaffinity_np(tid, sizeof(cpu_set_t),
516                                              &target_mask);
517                 CPU_CLR(target_cpu, &target_mask);
518                 target_cpu++;
519
520                 ret = pthread_detach(tid);
521                 if (ret) {
522                         perror("pthread_detach");
523                         exit(1);
524                 }
525         }
526         while(!workers_started) {
527                 smp_mb();
528                 usleep(200);
529         }
530         gettimeofday(&start, NULL);
531         fprintf(stderr, "main loop going\n");
532         while(1) {
533                 ops->wake(wi, num_semids, wake_num);
534                 burn_count++;
535                 gettimeofday(&now, NULL);
536                 if (now.tv_sec - start.tv_sec >= run_secs)
537                         break;
538         }
539         fprintf(stderr, "all done\n");
540         all_done = 1;
541         while(thread_count > 0) {
542                 ops->wake(wi, num_semids, wake_num);
543                 usleep(200);
544         }
545         printf("%d threads, waking %d at a time\n", num_threads, wake_num);
546         printf("using %s\n", ops->name);
547         printf("main thread burns: %d\n", burn_count);
548         printf("worker burn count total %lu min %lu max %lu avg %lu\n",
549                total_burns, min_burns, max_burns, total_burns / num_threads);
550         printf("run time %d seconds %lu worker burns per second\n",
551                 (int)(now.tv_sec - start.tv_sec),
552                 total_burns / (now.tv_sec - start.tv_sec));
553         ops->cleanup(num_semids);
554         return 0;
555 }
556