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