1 // SPDX-FileCopyrightText: 2009 Mathieu Desnoyers <mathieu.desnoyers (at) efficios.com> 2 // 3 // SPDX-License-Identifier: GPL-2.0-or-later 4 5 /* 6 * Userspace RCU library - test program 7 */ 8 9 #include <stdio.h> 10 #include <pthread.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <sys/types.h> 14 #include <sys/wait.h> 15 #include <unistd.h> 16 #include <stdio.h> 17 #include <errno.h> 18 19 #include <urcu/arch.h> 20 #include <urcu/assert.h> 21 #include <urcu/tls-compat.h> 22 #include "thread-id.h" 23 24 /* hardcoded number of CPUs */ 25 #define NR_CPUS 16384 26 27 #ifndef DYNAMIC_LINK_TEST 28 #define _LGPL_SOURCE 29 #endif 30 #include <urcu.h> 31 32 struct test_array { 33 int a; 34 }; 35 36 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 37 38 static unsigned long wdelay; 39 40 static volatile struct test_array test_array = { 8 }; 41 42 static unsigned long duration; 43 44 /* read-side C.S. duration, in loops */ 45 static unsigned long rduration; 46 47 /* write-side C.S. duration, in loops */ 48 static unsigned long wduration; 49 50 static inline void loop_sleep(unsigned long loops) 51 { 52 while (loops-- != 0) 53 caa_cpu_relax(); 54 } 55 56 static int verbose_mode; 57 58 #define printf_verbose(fmt, args...) \ 59 do { \ 60 if (verbose_mode) \ 61 printf(fmt, args); \ 62 } while (0) 63 64 static unsigned int cpu_affinities[NR_CPUS]; 65 static unsigned int next_aff = 0; 66 static int use_affinity = 0; 67 68 pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; 69 70 static void set_affinity(void) 71 { 72 #ifdef HAVE_SCHED_SETAFFINITY 73 cpu_set_t mask; 74 int cpu, ret; 75 #endif /* HAVE_SCHED_SETAFFINITY */ 76 77 if (!use_affinity) 78 return; 79 80 #ifdef HAVE_SCHED_SETAFFINITY 81 ret = pthread_mutex_lock(&affinity_mutex); 82 if (ret) { 83 perror("Error in pthread mutex lock"); 84 exit(-1); 85 } 86 cpu = cpu_affinities[next_aff++]; 87 ret = pthread_mutex_unlock(&affinity_mutex); 88 if (ret) { 89 perror("Error in pthread mutex unlock"); 90 exit(-1); 91 } 92 CPU_ZERO(&mask); 93 CPU_SET(cpu, &mask); 94 sched_setaffinity(0, sizeof(mask), &mask); 95 #endif /* HAVE_SCHED_SETAFFINITY */ 96 } 97 98 static DEFINE_URCU_TLS(unsigned long long, nr_writes); 99 static DEFINE_URCU_TLS(unsigned long long, nr_reads); 100 101 static 102 unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_writes; 103 static 104 unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_reads; 105 106 static unsigned int nr_readers; 107 static unsigned int nr_writers; 108 109 pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; 110 111 static 112 void *thr_reader(void *data) 113 { 114 unsigned long tidx = (unsigned long)data; 115 116 printf_verbose("thread_begin %s, tid %lu\n", 117 "reader", urcu_get_thread_id()); 118 119 set_affinity(); 120 121 wait_until_go(); 122 123 for (;;) { 124 int v; 125 126 pthread_mutex_lock(&lock); 127 v = test_array.a; 128 urcu_posix_assert(v == 8); 129 if (caa_unlikely(rduration)) 130 loop_sleep(rduration); 131 pthread_mutex_unlock(&lock); 132 URCU_TLS(nr_reads)++; 133 if (caa_unlikely(!test_duration_read())) 134 break; 135 } 136 137 tot_nr_reads[tidx] = URCU_TLS(nr_reads); 138 printf_verbose("thread_end %s, tid %lu\n", 139 "reader", urcu_get_thread_id()); 140 return ((void*)1); 141 142 } 143 144 static 145 void *thr_writer(void *data) 146 { 147 unsigned long wtidx = (unsigned long)data; 148 149 printf_verbose("thread_begin %s, tid %lu\n", 150 "writer", urcu_get_thread_id()); 151 152 set_affinity(); 153 154 wait_until_go(); 155 156 for (;;) { 157 pthread_mutex_lock(&lock); 158 test_array.a = 0; 159 test_array.a = 8; 160 if (caa_unlikely(wduration)) 161 loop_sleep(wduration); 162 pthread_mutex_unlock(&lock); 163 URCU_TLS(nr_writes)++; 164 if (caa_unlikely(!test_duration_write())) 165 break; 166 if (caa_unlikely(wdelay)) 167 loop_sleep(wdelay); 168 } 169 170 printf_verbose("thread_end %s, tid %lu\n", 171 "writer", urcu_get_thread_id()); 172 tot_nr_writes[wtidx] = URCU_TLS(nr_writes); 173 return ((void*)2); 174 } 175 176 static 177 void show_usage(char **argv) 178 { 179 printf("Usage : %s nr_readers nr_writers duration (s) <OPTIONS>\n", 180 argv[0]); 181 printf("OPTIONS:\n"); 182 printf(" [-d delay] (writer period (us))\n"); 183 printf(" [-c duration] (reader C.S. duration (in loops))\n"); 184 printf(" [-e duration] (writer C.S. duration (in loops))\n"); 185 printf(" [-v] (verbose output)\n"); 186 printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); 187 printf("\n"); 188 } 189 190 int main(int argc, char **argv) 191 { 192 int err; 193 pthread_t *tid_reader, *tid_writer; 194 void *tret; 195 unsigned long long *count_reader, *count_writer; 196 unsigned long long tot_reads = 0, tot_writes = 0; 197 int i, a; 198 unsigned int i_thr; 199 200 if (argc < 4) { 201 show_usage(argv); 202 return -1; 203 } 204 cmm_smp_mb(); 205 206 err = sscanf(argv[1], "%u", &nr_readers); 207 if (err != 1) { 208 show_usage(argv); 209 return -1; 210 } 211 212 err = sscanf(argv[2], "%u", &nr_writers); 213 if (err != 1) { 214 show_usage(argv); 215 return -1; 216 } 217 218 err = sscanf(argv[3], "%lu", &duration); 219 if (err != 1) { 220 show_usage(argv); 221 return -1; 222 } 223 224 for (i = 4; i < argc; i++) { 225 if (argv[i][0] != '-') 226 continue; 227 switch (argv[i][1]) { 228 case 'a': 229 if (argc < i + 2) { 230 show_usage(argv); 231 return -1; 232 } 233 a = atoi(argv[++i]); 234 cpu_affinities[next_aff++] = a; 235 use_affinity = 1; 236 printf_verbose("Adding CPU %d affinity\n", a); 237 break; 238 case 'c': 239 if (argc < i + 2) { 240 show_usage(argv); 241 return -1; 242 } 243 rduration = atol(argv[++i]); 244 break; 245 case 'd': 246 if (argc < i + 2) { 247 show_usage(argv); 248 return -1; 249 } 250 wdelay = atol(argv[++i]); 251 break; 252 case 'e': 253 if (argc < i + 2) { 254 show_usage(argv); 255 return -1; 256 } 257 wduration = atol(argv[++i]); 258 break; 259 case 'v': 260 verbose_mode = 1; 261 break; 262 } 263 } 264 265 printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", 266 duration, nr_readers, nr_writers); 267 printf_verbose("Writer delay : %lu loops.\n", wdelay); 268 printf_verbose("Reader duration : %lu loops.\n", rduration); 269 printf_verbose("thread %-6s, tid %lu\n", 270 "main", urcu_get_thread_id()); 271 272 tid_reader = calloc(nr_readers, sizeof(*tid_reader)); 273 tid_writer = calloc(nr_writers, sizeof(*tid_writer)); 274 count_reader = calloc(nr_readers, sizeof(*count_reader)); 275 count_writer = calloc(nr_writers, sizeof(*count_writer)); 276 tot_nr_reads = calloc(nr_readers, sizeof(*tot_nr_reads)); 277 tot_nr_writes = calloc(nr_writers, sizeof(*tot_nr_writes)); 278 279 next_aff = 0; 280 281 for (i_thr = 0; i_thr < nr_readers; i_thr++) { 282 err = pthread_create(&tid_reader[i_thr], NULL, thr_reader, 283 (void *)(long)i_thr); 284 if (err != 0) 285 exit(1); 286 } 287 for (i_thr = 0; i_thr < nr_writers; i_thr++) { 288 err = pthread_create(&tid_writer[i_thr], NULL, thr_writer, 289 (void *)(long)i_thr); 290 if (err != 0) 291 exit(1); 292 } 293 294 test_for(duration); 295 296 for (i_thr = 0; i_thr < nr_readers; i_thr++) { 297 err = pthread_join(tid_reader[i_thr], &tret); 298 if (err != 0) 299 exit(1); 300 tot_reads += tot_nr_reads[i_thr]; 301 } 302 for (i_thr = 0; i_thr < nr_writers; i_thr++) { 303 err = pthread_join(tid_writer[i_thr], &tret); 304 if (err != 0) 305 exit(1); 306 tot_writes += tot_nr_writes[i_thr]; 307 } 308 309 printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, 310 tot_writes); 311 printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu " 312 "nr_writers %3u " 313 "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n", 314 argv[0], duration, nr_readers, rduration, wduration, 315 nr_writers, wdelay, tot_reads, tot_writes, 316 tot_reads + tot_writes); 317 318 free(tid_reader); 319 free(tid_writer); 320 free(count_reader); 321 free(count_writer); 322 free(tot_nr_reads); 323 free(tot_nr_writes); 324 return 0; 325 } 326