tests/linux: use Akaros's CFLAGS
[akaros.git] / kern / drivers / dev / regress.c
1 /*
2  * This file is part of the UCB release of Plan 9. It is subject to the license
3  * terms in the LICENSE file found in the top-level directory of this
4  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
5  * part of the UCB release of Plan 9, including this file, may be copied,
6  * modified, propagated, or distributed except according to the terms contained
7  * in the LICENSE file.
8  */
9
10 // regression device.
11 // Currently, has only one file, monitor, which is used to send
12 // commands to the monitor.
13 // TODO: read them back :-)
14
15 #include <slab.h>
16 #include <kmalloc.h>
17 #include <kref.h>
18 #include <string.h>
19 #include <stdio.h>
20 #include <assert.h>
21 #include <error.h>
22 #include <cpio.h>
23 #include <pmap.h>
24 #include <smp.h>
25 #include <net/ip.h>
26 #include <monitor.h>
27 #include <ktest.h>
28
29 struct dev regressdevtab;
30
31 static char *devname(void)
32 {
33         return regressdevtab.name;
34 }
35
36 struct regress
37 {
38         spinlock_t lock;
39         struct queue *monitor;
40 };
41 struct regress regress;
42
43 enum{
44         Monitordirqid = 0,
45         Monitordataqid,
46         Monitorctlqid,
47 };
48
49 struct dirtab regresstab[]={
50         {".",           {Monitordirqid, 0, QTDIR},      0,      DMDIR|0550},
51         {"mondata",     {Monitordataqid},               0,      0600},
52         {"monctl",      {Monitorctlqid},                0,      0600},
53 };
54
55 static char *ctlcommands = "ktest";
56
57 static struct chan *regressattach(char *spec)
58 {
59         uint32_t n;
60
61         regress.monitor = qopen(2 << 20, 0, 0, 0);
62         if (! regress.monitor) {
63                 printk("monitor allocate failed. No monitor output\n");
64         }
65         return devattach(devname(), spec);
66 }
67
68 static void regressinit(void)
69 {
70 }
71
72 static struct walkqid *regresswalk(struct chan *c, struct chan *nc, char **name,
73                                    unsigned int nname)
74 {
75         return devwalk(c, nc, name, nname, regresstab, ARRAY_SIZE(regresstab),
76                        devgen);
77 }
78
79 static size_t regressstat(struct chan *c, uint8_t *db, size_t n)
80 {
81         if (regress.monitor)
82                 regresstab[Monitordataqid].length = qlen(regress.monitor);
83         else
84                 regresstab[Monitordataqid].length = 0;
85
86         return devstat(c, db, n, regresstab, ARRAY_SIZE(regresstab), devgen);
87 }
88
89 static struct chan *regressopen(struct chan *c, int omode)
90 {
91         if (c->qid.type & QTDIR) {
92                 if (openmode(omode) != O_READ)
93                         error(EPERM, ERROR_FIXME);
94         }
95         c->mode = openmode(omode);
96         c->flag |= COPEN;
97         c->offset = 0;
98         return c;
99 }
100
101 static void regressclose(struct chan *unused)
102 {
103 }
104
105 static size_t regressread(struct chan *c, void *va, size_t n, off64_t off)
106 {
107         uint64_t w, *bp;
108         char *a, *ea;
109         uintptr_t offset = off;
110         uint64_t pc;
111         int snp_ret, ret = 0;
112
113         switch((int)c->qid.path){
114         case Monitordirqid:
115                 n = devdirread(c, va, n, regresstab, ARRAY_SIZE(regresstab),
116                                devgen);
117                 break;
118
119         case Monitorctlqid:
120                 n = readstr(off, va, n, ctlcommands);
121                 break;
122
123         case Monitordataqid:
124                 if (regress.monitor) {
125                         printd("monitordataqid: regress.monitor %p len %p\n",
126                                regress.monitor, qlen(kprof.monitor));
127                         if (qlen(regress.monitor) > 0)
128                                 n = qread(regress.monitor, va, n);
129                         else
130                                 n = 0;
131                 } else
132                         error(EFAIL, "no monitor queue");
133                 break;
134         default:
135                 n = 0;
136                 break;
137         }
138         return n;
139 }
140
141 int __tlb_bench_x;
142
143 static void __tlb_s(void)
144 {
145         tlbflush();
146         cmb();  /* tlbflush is asm volatile, but it can still be reordered. */
147         WRITE_ONCE(__tlb_bench_x, 1);
148 }
149
150 static void __tlb_s_ipi(struct hw_trapframe *hw_tf, void *data)
151 {
152         __tlb_s();
153 }
154
155 static void __tlb_s_kmsg(uint32_t srcid, long a0, long a1, long a2)
156 {
157         __tlb_s();
158 }
159
160 /* This runs the test from the calling core, which is typically core 0 if you
161  * are running from the shell.  If you run from another core, note that
162  * deregister_irq() will synchronize_rcu, which moves this thread to core 0 at
163  * the end of the function. */
164 static void __tlb_shootdown_bench(int target_core, int mode)
165 {
166         ERRSTACK(1);
167         uint64_t s, *d;
168         const char *str = NULL;
169         struct irq_handler *irqh;
170         int tbdf = MKBUS(BusIPI, 0, 0, 0);
171         #define ITERS 10
172
173         if (target_core == core_id())
174                 error(EINVAL, "TLB bench: Aborting, we are core %d",
175                       target_core);
176         if (target_core < 0 || target_core >= num_cores)
177                 error(EINVAL,
178                       "TLB bench: Aborting, target_core %d out of range",
179                       target_core);
180         irqh = register_irq(I_TESTING, __tlb_s_ipi, NULL, tbdf);
181         if (!irqh)
182                 error(EFAIL,
183                       "TLB bench: Oh crap, we couldn't register the IRQ!");
184         d = kmalloc(sizeof(uint64_t) * ITERS, MEM_WAIT);
185         if (waserror()) {
186                 deregister_irq(irqh->apic_vector, tbdf);
187                 kfree(d);
188                 nexterror();
189         }
190         for (int i = 0; i < ITERS; i++) {
191                 __tlb_bench_x = 0;
192                 s = start_timing();
193                 switch (mode) {
194                 case 1:
195                         str = "NOOP";
196                         __tlb_bench_x = 1;
197                         break;
198                 case 2:
199                         tlbflush();
200                         str = "LOCAL";
201                         __tlb_bench_x = 1;
202                         break;
203                 case 3:
204                         /* To run this test, you need to hacked this into
205                          * POKE_HANDLER.  If not, you'll wedge the machine.
206                                 mov %cr3,%rax;\
207                                 mov %rax,%cr3;\
208                                 incl __tlb_bench_x;\
209                         * And comment out the error(). */
210                         error(EFAIL, "TLB bench: hack the POKE_HANDLER");
211
212                         send_ipi(target_core, I_POKE_CORE);
213                         str = "POKE";
214                         while (!READ_ONCE(__tlb_bench_x))
215                                 cpu_relax();
216                         break;
217                 case 4:
218                         send_ipi(target_core, I_TESTING);
219                         str = "IPI";
220                         while (!READ_ONCE(__tlb_bench_x))
221                                 cpu_relax();
222                         break;
223                 case 5:
224                         send_kernel_message(target_core, __tlb_s_kmsg, 0, 0, 0,
225                                             KMSG_IMMEDIATE);
226                         str = "KMSG";
227                         while (!READ_ONCE(__tlb_bench_x))
228                                 cpu_relax();
229                         break;
230                 case 6:
231                         send_kernel_message(target_core, __tlb_s_kmsg, 0, 0, 0,
232                                             KMSG_IMMEDIATE);
233                         str = "NOACK-KMSG";
234                         break;
235                 case 7:
236                         send_ipi(target_core, I_TESTING);
237                         str = "NOACK-IPI";
238                         break;
239                 default:
240                         error(EINVAL, "TLB bench: bad mode %d", mode);
241                 }
242                 d[i] = stop_timing(s);
243                 /* The NOACKs still need to wait, so we don't race with the
244                  * remote core and our *next* loop. */
245                 while (!READ_ONCE(__tlb_bench_x))
246                         cpu_relax();
247                 /* The remote core has signalled it did the TLB flush, but it
248                  * takes a little while for it to halt or otherwise get back to
249                  * idle.  Wait a little to get a more stable measurement.
250                  * Without this delay (or something similar), I've seen extra
251                  * delays of close to 400ns.  Note that in real usage, the
252                  * remote core won't always be ready to handle the IRQ, so this
253                  * test is best case. */
254                 udelay(1000);
255         }
256         for (int i = 0; i < ITERS; i++)
257                 printk("%02d: TLB %s shootdown: %llu ns\n", i, str,
258                        tsc2nsec(d[i]));
259         deregister_irq(irqh->apic_vector, tbdf);
260         kfree(d);
261         poperror();
262 }
263
264 static size_t regresswrite(struct chan *c, void *a, size_t n, off64_t unused)
265 {
266         ERRSTACK(1);
267         uintptr_t pc;
268         struct cmdbuf *cb;
269         cb = parsecmd(a, n);
270
271         if (waserror()) {
272                 kfree(cb);
273                 nexterror();
274         }
275
276         switch ((int)(c->qid.path)) {
277         case Monitorctlqid:
278                 if (cb->nf < 1)
279                         error(EFAIL, "%s no command, need %s", __func__,
280                               ctlcommands);
281                 if (!strcmp(cb->f[0], "ktest")) {
282                         run_registered_ktest_suites();
283                 } else if (!strcmp(cb->f[0], "tlb")) {
284                         if (cb->nf < 3)
285                                 error(EFAIL,
286                                       "TLB bench: need core and mode (ints)");
287                         __tlb_shootdown_bench(strtol(cb->f[1], NULL, 10),
288                                               strtol(cb->f[2], NULL, 10));
289                 } else {
290                         error(EFAIL, "regresswrite: only commands are %s",
291                               ctlcommands);
292                 }
293                 break;
294
295         case Monitordataqid:
296                 if (onecmd(cb->nf, cb->f, NULL) < 0)
297                         n = -1;
298                 break;
299         default:
300                 error(EBADFD, ERROR_FIXME);
301         }
302         kfree(cb);
303         poperror();
304         return n;
305 }
306
307 struct dev regressdevtab __devtab = {
308         .name = "regress",
309
310         .reset = devreset,
311         .init = regressinit,
312         .shutdown = devshutdown,
313         .attach = regressattach,
314         .walk = regresswalk,
315         .stat = regressstat,
316         .open = regressopen,
317         .create = devcreate,
318         .close = regressclose,
319         .read = regressread,
320         .bread = devbread,
321         .write = regresswrite,
322         .bwrite = devbwrite,
323         .remove = devremove,
324         .wstat = devwstat,
325 };