akaros/kern/drivers/dev/regress.c
<<
>>
Prefs
   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
  29struct dev regressdevtab;
  30
  31static char *devname(void)
  32{
  33        return regressdevtab.name;
  34}
  35
  36struct regress
  37{
  38        spinlock_t lock;
  39        struct queue *monitor;
  40};
  41struct regress regress;
  42
  43enum{
  44        Monitordirqid = 0,
  45        Monitordataqid,
  46        Monitorctlqid,
  47};
  48
  49struct dirtab regresstab[]={
  50        {".",           {Monitordirqid, 0, QTDIR},      0,      DMDIR|0550},
  51        {"mondata",     {Monitordataqid},               0,      0600},
  52        {"monctl",      {Monitorctlqid},                0,      0600},
  53};
  54
  55static char *ctlcommands = "ktest";
  56
  57static 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
  68static void regressinit(void)
  69{
  70}
  71
  72static 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
  79static 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
  89static 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
 101static void regressclose(struct chan *unused)
 102{
 103}
 104
 105static 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
 141int __tlb_bench_x;
 142
 143static 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
 150static void __tlb_s_ipi(struct hw_trapframe *hw_tf, void *data)
 151{
 152        __tlb_s();
 153}
 154
 155static 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. */
 164static 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
 264static 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
 307struct 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};
 326