@@ -0,0 +1,272 @@
1// stridetest: is the CPU shared in proportion to tickets?
2//
3// stridetest run the checks
4// stridetest t1 t2 ... measure only: one spinning process per
5// ticket count; prints shares, checks nothing
6//
7// A spinning process asks the kernel for its CPU time (cputime) when
8// it first sees the clock (uptime) inside the measurement window and
9// again when the window has ended. Its share of the CPU time of all
10// spinners is its share of the CPU; the checks use that.
11//
12// It also counts "chunks" of a busy loop finished inside the window.
13// They are printed for information only: on QEMU a chunk takes more
14// or less time depending on how fast the host runs each hart.
15
16#include "kernel/types.h"
17#include "user/user.h"
18
19#define CHUNK 50000 // loop iterations between two looks at the clock
20#define MAXSPIN 8
21
22struct result {
23 int i; // which spinner
24 int chunks; // chunks finished inside the window
25 uint64 cpu; // CPU time used inside the window (time CSR units) 26};
27
28int wstart, wend; // the measurement window, in ticks
29
30// Spin until tick `wend`. Measure the CPU time used and count the
31// chunks finished inside [wstart, wend). Send the result to fd and
32// exit.
33void
35{
36 struct result r = {i, 0, 0};
37 volatile int x = 0;
38 uint64 t0 = 0;
39 int now, inside = 0;
40
41 for (;;) {
42 for (int k = 0; k < CHUNK; k++)
43 x++;
45 if (now >= wend)
46 break;
47 if (now >= wstart) {
48 if (!inside) {
49 inside = 1;
50 t0 = cputime();
51 }
52 r.chunks++;
53 }
54 }
55 if (inside)
56 r.cpu = cputime() - t0; 57 write(fd, &r, sizeof(r)); 59}
60
61int
62xfork(void)
63{
66 printf("stridetest: fork failed\n"); 68 }
70}
71
72// Run n spinners with the given tickets. The window starts `warm`
73// ticks from now and lasts `len` ticks. Spinner `sleeper` (or -1)
74// sleeps until the window starts; spinner `newcomer` (or -1) is not
75// forked until then. Fill in res[].
76//
77// Both really sleep, in read() on a pipe. (pause() would not do:
78// it wakes up at every tick to look at the clock.) An alarm process
79// writes to the pipe when the window starts.
80void
81measure(int n, int *tickets, int warm, int len, int sleeper, int newcomer, 82 struct result *res)
83{
84 int out[2], go[2];
85 struct result r;
86 char c;
87
89 printf("stridetest: pipe failed\n"); 91 }
94 for (int i = 0; i < n; i++) {
95 if (i == newcomer)
96 continue;
97 if (xfork() == 0) {
98 settickets(tickets[i]);
99 if (i == sleeper)
102 }
103 }
104 if (sleeper >= 0 || newcomer >= 0) {
105 if (xfork() == 0) {
107 write(go[1], "g", 1); 109 }
110 }
111 if (newcomer >= 0) {
113 if (xfork() == 0) {
114 settickets(tickets[newcomer]);
115 spin(newcomer, out[1]); 116 }
117 }
119 for (int i = 0; i < n; i++) {
120 if (read(out[0], &r, sizeof(r)) != sizeof(r)) { 121 printf("stridetest: short read\n"); 123 }
124 res[r.i] = r;
125 }
130 ;
131}
132
133// settickets checks its argument and returns the old value;
134// fork and exec keep the tickets.
135int
136ticketstest(void)
137{
138 int ok = 1, old, st;
139 char *argv[] = {"stridetest", "-t", 0}; 140
141 if (settickets(0) != -1 || settickets(-3) != -1 || settickets(101) != -1) {
142 printf("stridetest: tickets: a bad count was accepted\n"); 143 ok = 0;
144 }
145 old = settickets(7);
146 if (settickets(7) != 7) {
147 printf("stridetest: tickets: settickets did not return the old count\n"); 148 ok = 0;
149 }
153 if (st != 7) {
154 printf("stridetest: tickets: fork child had %d tickets, not 7\n", st); 155 ok = 0;
156 }
160 }
162 if (st != 7) {
163 printf("stridetest: tickets: after exec %d tickets, not 7\n", st); 164 ok = 0;
165 }
166 settickets(old);
167 printf("stridetest: tickets: %s\n", ok ? "OK" : "FAIL"); 168 return ok;
169}
170
171// Per mille of `part` in `total`.
172int
173permille(uint64 part, uint64 total)
174{
175 return total ? part * 1000 / total : 0;
176}
177
178// Six processes with 1, 1, 2, 2, 3 and 3 tickets. However many harts
179// (up to 4), each process wants more CPU than its share, so the
180// shares should be 1/6, 2/6 and 3/6 for the three ticket counts.
181int
182sharetest(void)
183{
184 int tickets[6] = {1, 1, 2, 2, 3, 3};
185 struct result res[6];
186 uint64 cpu = 0, chunks = 0; 187 int ok = 1;
188
189 measure(6, tickets, 5, 50, -1, -1, res);
190 for (int i = 0; i < 6; i++) {
192 chunks += res[i].chunks;
193 }
194 printf("stridetest: share: 6 processes with 1 1 2 2 3 3 tickets, 50 ticks\n"); 195 for (int t = 1; t <= 3; t++) {
196 struct result *a = &res[2 * t - 2], *b = &res[2 * t - 1];
197 int share = permille(a->cpu + b->cpu, cpu); 198 int ideal = (t * 1000 + 3) / 6; // rounded
199 printf("stridetest: share: tickets %d: cpu %d per mille (ideal %d); " 200 "chunks %d per mille\n",
201 t, share, ideal, permille(a->chunks + b->chunks, chunks));
202 // within 20% of the ideal share
203 if (5 * (share - ideal) > ideal || 5 * (ideal - share) > ideal)
204 ok = 0;
205 }
206 printf("stridetest: share: %s\n", ok ? "OK" : "FAIL"); 207 return ok;
208}
209
210// Six processes with equal tickets; process 0 either sleeps through
211// the first 30 ticks (sleeper) or is only created after them
212// (newcomer). In the next 30 ticks it should get about as much CPU
213// as each of the others, not a burst to make up for lost time.
214int
215latetest(char *what, int sleeper, int newcomer)
216{
217 int tickets[6] = {5, 5, 5, 5, 5, 5};
218 struct result res[6];
219 uint64 others = 0;
220 int ratio, ok;
221
222 measure(6, tickets, 30, 30, sleeper, newcomer, res);
223 for (int i = 1; i < 6; i++)
224 others += res[i].cpu; 225 ratio = others ? res[0].cpu * 5 * 100 / others : 0; 226 ok = ratio >= 67 && ratio <= 150;
227 printf("stridetest: %s: cpu %d ms, the others %d ms on average " 228 "(ratio %d/100)\n",
229 what, (int)(res[0].cpu / 10000), (int)(others / 5 / 10000), ratio); 230 printf("stridetest: %s: %s\n", what, ok ? "OK" : "FAIL"); 231 return ok;
232}
233
234int
236{
237 int tickets[MAXSPIN], n, sum = 0, ok = 1;
238 struct result res[MAXSPIN];
239 uint64 cpu = 0, chunks = 0; 240
242 exit(settickets(1)); // the exec part of ticketstest 243
244 if (argc > 1) {
245 n = argc - 1;
246 if (n > MAXSPIN)
247 n = MAXSPIN;
248 for (int i = 0; i < n; i++) {
250 sum += tickets[i];
251 }
252 measure(n, tickets, 5, 50, -1, -1, res);
253 for (int i = 0; i < n; i++) {
255 chunks += res[i].chunks;
256 }
257 for (int i = 0; i < n; i++)
258 printf("stridetest: %d tickets: cpu %d ms, %d per mille " 259 "(%d if shared by tickets); chunks %d per mille\n",
260 tickets[i], (int)(res[i].cpu / 10000), permille(res[i].cpu, cpu), 261 tickets[i] * 1000 / sum, permille(res[i].chunks, chunks));
262 printf("stridetest: measured only, nothing checked\n"); 264 }
265
266 ok &= ticketstest();
267 ok &= sharetest();
268 ok &= latetest("sleeper", 0, -1);
269 ok &= latetest("newcomer", -1, 0);
270 printf("stridetest: %s\n", ok ? "ALL OK" : "SOME FAILED"); 272}