1 /* 2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include <errno.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <sys/wait.h> 11 #include <unistd.h> 12 13 #include <SupportDefs.h> 14 15 #include <syscalls.h> 16 #include <spinlock_contention.h> 17 18 19 #define panic printf 20 21 22 struct dummy_spinlock { 23 vint32 lock; 24 vint32 count_low; 25 vint32 count_high; 26 }; 27 28 29 struct dummy_smp_msg { 30 dummy_smp_msg* next; 31 }; 32 33 34 static int sNumCPUs = 2; 35 static bool sICIEnabled = true; 36 static dummy_spinlock cpu_msg_spinlock[SMP_MAX_CPUS]; 37 static dummy_smp_msg* smp_msgs[SMP_MAX_CPUS]; 38 static dummy_spinlock broadcast_msg_spinlock; 39 static dummy_smp_msg* smp_broadcast_msgs; 40 41 42 bool 43 dummy_are_interrupts_enabled() 44 { 45 return false; 46 } 47 48 49 void 50 dummy_acquire_spinlock_nocheck(dummy_spinlock* lock) 51 { 52 if (sNumCPUs > 1) { 53 if (dummy_are_interrupts_enabled()) 54 panic("acquire_spinlock_nocheck: attempt to acquire lock %p with interrupts enabled\n", lock); 55 56 while (atomic_add(&lock->lock, 1) != 0) { 57 } 58 } else { 59 if (dummy_are_interrupts_enabled()) 60 panic("acquire_spinlock_nocheck: attempt to acquire lock %p with interrupts enabled\n", lock); 61 if (atomic_set((int32 *)lock, 1) != 0) 62 panic("acquire_spinlock_nocheck: attempt to acquire lock %p twice on non-SMP system\n", lock); 63 } 64 } 65 66 67 void 68 dummy_release_spinlock(dummy_spinlock* lock) 69 { 70 if (sNumCPUs > 1) { 71 if (dummy_are_interrupts_enabled()) 72 panic("release_spinlock: attempt to release lock %p with interrupts enabled\n", lock); 73 74 { 75 int32 count = atomic_set(&lock->lock, 0) - 1; 76 if (count < 0) { 77 panic("release_spinlock: lock %p was already released\n", lock); 78 } else { 79 // add to the total count -- deal with carry manually 80 if ((uint32)atomic_add(&lock->count_low, count) + count 81 < (uint32)count) { 82 atomic_add(&lock->count_high, 1); 83 } 84 } 85 } 86 } else { 87 if (dummy_are_interrupts_enabled()) 88 panic("release_spinlock: attempt to release lock %p with interrupts enabled\n", lock); 89 if (atomic_set((int32 *)lock, 0) != 1) 90 panic("release_spinlock: lock %p was already released\n", lock); 91 } 92 } 93 94 95 struct dummy_smp_msg * 96 dummy_check_for_message(int currentCPU, int *source_mailbox) 97 { 98 struct dummy_smp_msg *msg; 99 100 if (!sICIEnabled) 101 return NULL; 102 103 dummy_acquire_spinlock_nocheck(&cpu_msg_spinlock[currentCPU]); 104 msg = smp_msgs[currentCPU]; 105 if (msg != NULL) { 106 printf("yeah\n"); 107 dummy_release_spinlock(&cpu_msg_spinlock[currentCPU]); 108 *source_mailbox = 1; 109 } else { 110 // try getting one from the broadcast mailbox 111 112 dummy_release_spinlock(&cpu_msg_spinlock[currentCPU]); 113 dummy_acquire_spinlock_nocheck(&broadcast_msg_spinlock); 114 115 msg = smp_broadcast_msgs; 116 while (msg != NULL) { 117 printf("yeah\n"); 118 msg = msg->next; 119 } 120 dummy_release_spinlock(&broadcast_msg_spinlock); 121 } 122 return msg; 123 } 124 125 126 int32 127 dummy_process_pending_ici(int32 currentCPU) 128 { 129 int retval = 42; 130 int sourceMailbox = 0; 131 132 dummy_smp_msg* msg = dummy_check_for_message(currentCPU, &sourceMailbox); 133 if (msg == NULL) 134 return retval; 135 136 switch ((addr_t)msg) { 137 case 0: 138 printf("foo\n"); 139 break; 140 case 1: 141 printf("foo\n"); 142 break; 143 case 2: 144 printf("foo\n"); 145 break; 146 } 147 148 return 9; 149 } 150 151 152 static void 153 test_spinlock(dummy_spinlock* lock) 154 { 155 while (atomic_add(&lock->lock, -1) != 0) 156 dummy_process_pending_ici(0); 157 } 158 159 160 static double 161 estimate_spinlock_tick_time() 162 { 163 // time the spinlock 164 int32 count = (INT_MAX >> 16) + 1; 165 while (true) { 166 bigtime_t startTime = system_time(); 167 168 dummy_spinlock lock; 169 lock.lock = count; 170 test_spinlock(&lock); 171 172 bigtime_t totalTime = system_time() - startTime; 173 double tickTime = (double)totalTime / count; 174 175 if (totalTime > 1000000 || INT_MAX >> 2 < count) 176 return tickTime; 177 178 count <<= 1; 179 } 180 } 181 182 183 static const char* 184 time_string(double timeInUsecs, char* buffer) 185 { 186 static const char* const kUnits[] = { "us", "ms", "s ", NULL }; 187 188 double time = timeInUsecs; 189 190 int32 i = 0; 191 while (time > 1000 && kUnits[i + 1] != NULL) { 192 time /= 1000; 193 i++; 194 } 195 196 sprintf(buffer, "%.3f %s", time, kUnits[i]); 197 198 return buffer; 199 } 200 201 202 int 203 main(int argc, char** argv) 204 { 205 // get the initial contention info 206 spinlock_contention_info startInfo; 207 status_t error = _kern_generic_syscall(SPINLOCK_CONTENTION, 208 GET_SPINLOCK_CONTENTION_INFO, &startInfo, sizeof(startInfo)); 209 if (error != B_OK) { 210 fprintf(stderr, "Error: Failed to get spinlock contention info: %s\n", 211 strerror(error)); 212 exit(1); 213 } 214 bigtime_t startTime = system_time(); 215 216 pid_t child = fork(); 217 if (child < 0) { 218 fprintf(stderr, "Error: fork() failed: %s\n", strerror(errno)); 219 exit(1); 220 } 221 222 if (child == 0) { 223 execvp(argv[1], argv + 1); 224 fprintf(stderr, "Error: exec() failed: %s\n", strerror(errno)); 225 exit(1); 226 } else { 227 int status; 228 wait(&status); 229 } 230 231 // get the final contention info 232 spinlock_contention_info endInfo; 233 error = _kern_generic_syscall(SPINLOCK_CONTENTION, 234 GET_SPINLOCK_CONTENTION_INFO, &endInfo, sizeof(endInfo)); 235 if (error != B_OK) { 236 fprintf(stderr, "Error: Failed to get spinlock contention info: %s\n", 237 strerror(error)); 238 exit(1); 239 } 240 bigtime_t totalTime = system_time() - startTime; 241 242 char buffer[128]; 243 printf("\ntotal run time: %s\n", time_string(totalTime, buffer)); 244 245 // estimate spinlock tick time 246 printf("estimating time per spinlock tick...\n"); 247 double tickTime = estimate_spinlock_tick_time(); 248 printf("time per spinlock tick: %s\n", 249 time_string(tickTime, buffer)); 250 251 // print results 252 static const char* const kLockNames[] = { "thread creation", NULL }; 253 bigtime_t lockCounts[] = { 254 endInfo.thread_creation_spinlock - startInfo.thread_creation_spinlock, 255 }; 256 257 printf("\nlock counter time wasted %% CPU\n"); 258 printf("-------------------------------------------------------\n"); 259 for (int32 i = 0; kLockNames[i] != NULL; i++) { 260 double wastedUsecs = lockCounts[i] * tickTime; 261 printf("%-10s %12llu %14s %12.4f\n", kLockNames[i], lockCounts[i], 262 time_string(wastedUsecs, buffer), wastedUsecs / totalTime * 100); 263 } 264 265 return 0; 266 } 267