ethernet: add sanity checks to p2p_ethernet_add/del
[vpp.git] / src / vppinfra / test_spinlock.c
1 /*
2  * Copyright (c) 2019 Arm Limited.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #ifndef _GNU_SOURCE
17 #define _GNU_SOURCE
18 #endif
19
20 #include <vppinfra/mem.h>
21 #include <vppinfra/cache.h>
22 #include <vppinfra/lock.h>
23 #include <pthread.h>
24 #include <vppinfra/format.h>
25 #include <vppinfra/error.h>
26 #include <vppinfra/time.h>
27 #include <sched.h>
28 #include <stdlib.h>
29 #include <vppinfra/atomics.h>
30
31 static u32 all_threads_online = 0;
32
33 typedef struct
34 {
35   uword threads_per_core;
36   uword cpu_mask;
37   uword num_cores;
38   uword increment_per_thread;
39   clib_spinlock_t slock;
40   uword shared_count;
41   uword iterations;
42 } spinlock_test_main_t;
43
44 void *
45 inc_shared_counter (void *arg)
46 {
47   f64 *time = vec_new (f64, 1);
48   *time = 0;
49   spinlock_test_main_t *stm = arg;
50
51   /* Wait for all threads to be created */
52   while (!clib_atomic_load_acq_n (&all_threads_online));
53
54   f64 start = clib_cpu_time_now ();
55   for (uword i = 0; i < stm->increment_per_thread; i++)
56     {
57       clib_spinlock_lock (&stm->slock);
58       stm->shared_count++;
59       clib_spinlock_unlock (&stm->slock);
60     }
61   *time = clib_cpu_time_now () - start;
62   return time;
63 }
64
65 unsigned
66 test_spinlock (spinlock_test_main_t * stm, f64 * elapse_time)
67 {
68   int error;
69   uword num_threads = stm->num_cores * stm->threads_per_core;
70   pthread_t pthread[num_threads];
71
72   cpu_set_t cpuset;
73   unsigned cores_set = 0, cpu_id = 0;
74   for (unsigned cpu_mask = stm->cpu_mask; cpu_mask; cpu_mask >>= 1)
75     {
76       if (!(cpu_mask & 1))
77         {
78           cpu_id++;
79           continue;
80         }
81
82       CPU_ZERO (&cpuset);
83       CPU_SET (cpu_id, &cpuset);
84       for (uword t_num = 0; t_num < stm->threads_per_core; t_num++)
85         {
86           uword t_index = cores_set * stm->threads_per_core + t_num;
87           if ((error = pthread_create (&pthread[t_index], NULL,
88                                        &inc_shared_counter, stm)))
89             clib_unix_warning ("pthread_create failed with %d", error);
90
91           if ((error = pthread_setaffinity_np (pthread[t_index],
92                                                sizeof (cpu_set_t), &cpuset)))
93             clib_unix_warning ("pthread_setaffinity_np failed with %d",
94                                error);
95         }
96       cores_set++;
97       cpu_id++;
98     }
99
100   /* Launch all threads */
101   clib_atomic_store_rel_n (&all_threads_online, 1);
102
103   for (uword thread_num = 0; thread_num < num_threads; thread_num++)
104     {
105       f64 *time;
106       if ((error = pthread_join (pthread[thread_num], (void *) &time)))
107         clib_unix_warning ("pthread_join failed with %d", error);
108       *elapse_time += *time;
109       vec_free (time);
110     }
111
112   fformat (stdout, "Time elapsed: %.4e cycles\n", *elapse_time);
113   return stm->shared_count;
114 }
115
116 uword
117 num_cores_in_cpu_mask (uword mask)
118 {
119   uword num_cores = 0;
120   for (uword cpu_mask = mask; cpu_mask; cpu_mask >>= 1)
121     num_cores += (cpu_mask & 1);
122   return num_cores;
123 }
124
125 int
126 test_spinlock_main (unformat_input_t * i)
127 {
128   spinlock_test_main_t _stm, *stm = &_stm;
129   clib_memset (stm, 0, sizeof (spinlock_test_main_t));
130
131   while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
132     {
133       if (0 == unformat (i, "threads/core %d", &stm->threads_per_core)
134           && 0 == unformat (i, "cpu_mask %x", &stm->cpu_mask)
135           && 0 == unformat (i, "increment %d", &stm->increment_per_thread)
136           && 0 == unformat (i, "iterations %d", &stm->iterations))
137         {
138           clib_unix_warning ("unknown input '%U'", format_unformat_error, i);
139           return 1;
140         }
141     }
142
143   stm->num_cores = num_cores_in_cpu_mask (stm->cpu_mask);
144
145   uword total_increment = stm->threads_per_core * stm->num_cores *
146     stm->increment_per_thread;
147
148   clib_spinlock_init (&stm->slock);
149
150   f64 average_time = 0;
151   for (uword trial = 0; trial < stm->iterations; trial++)
152     {
153       stm->shared_count = 0;
154       f64 elapse_time = 0;
155       if (test_spinlock (stm, &elapse_time) != total_increment)
156         {
157           clib_spinlock_free (&stm->slock);
158           fformat (stdout, "FAILED: expected count: %d, actual count: %d\n",
159                    total_increment, stm->shared_count);
160           return 1;
161         }
162
163       fformat (stdout, "Trial %d SUCCESS: %d = %d\n",
164                trial, stm->shared_count, total_increment);
165       average_time = (average_time * trial + elapse_time) / (trial + 1);
166       fformat (stdout, "Average lock/unlock cycles %.4e\n", average_time);
167     }
168   clib_spinlock_free (&stm->slock);
169   return 0;
170 }
171
172 #ifdef CLIB_UNIX
173 /** Launches a number of threads to simultaneously increment a global
174     counter, and records timestamps for spinlock performance benchmarking
175
176     @param "threads/core [# threads/core]" - number of threads per core
177     @param "cpu_mask [cpu_mask]" - cpu hex string e.g. input ff sets cpus 0 - 7
178     @param "increment [# increments]" - number of increments per threads
179     @param "iterations [# iterations]" - number of iterations
180     @returns exit code
181 */
182 int
183 main (int argc, char *argv[])
184 {
185   unformat_input_t i;
186   i32 ret;
187   clib_time_t time;
188
189   clib_mem_init (0, 3ULL << 30);
190   clib_time_init (&time);
191
192   unformat_init_command_line (&i, argv);
193   ret = test_spinlock_main (&i);
194   unformat_free (&i);
195
196   return ret;
197 }
198 #endif /* CLIB_UNIX */
199
200 /*
201  * fd.io coding-style-patch-verification: ON
202  *
203  * Local Variables:
204  * eval: (c-set-style "gnu")
205  * End:
206  */