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
dummy_are_interrupts_enabled()43 dummy_are_interrupts_enabled()
44 {
45 return false;
46 }
47
48
49 void
dummy_acquire_spinlock_nocheck(dummy_spinlock * lock)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
dummy_release_spinlock(dummy_spinlock * lock)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 *
dummy_check_for_message(int currentCPU,int * source_mailbox)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
dummy_process_pending_ici(int32 currentCPU)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
test_spinlock(dummy_spinlock * lock)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
estimate_spinlock_tick_time()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*
time_string(double timeInUsecs,char * buffer)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
main(int argc,char ** argv)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