root/regress/sys/uvm/pdsim/pdsim.c
/*      $NetBSD: pdsim.c,v 1.2 2006/10/14 04:43:41 yamt Exp $   */

/*-
 * Copyright (c)2006 YAMAMOTO Takashi,
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "pdsim.h"

#define SHOWFAULT
#if defined(SHOWQLEN) || defined(SHOWIRR)
#undef SHOWFAULT
#endif

#undef  READAHEAD

struct vm_page *pages;

struct uvmexp uvmexp;

int npagein;
int nfault;
int raio;
int rahit;

int lastacc[MAXID];
int irr[MAXID];
int ts;

struct {
        int fault;
        int hit;
} stats[MAXID];

TAILQ_HEAD(, vm_page) freeq;

struct vm_page *
pdsim_pagealloc(struct uvm_object *obj, int idx)
{
        struct vm_page *pg;

        pg = TAILQ_FIRST(&freeq);
        if (pg == NULL) {
                return NULL;
        }
        TAILQ_REMOVE(&freeq, pg, pageq);
        pg->offset = idx << PAGE_SHIFT;
        pg->uanon = NULL;
        pg->uobject = obj;
        pg->pqflags = 0;
        obj->pages[idx] = pg;
        uvmexp.free--;
        uvmexp.filepages++;

        return pg;
}

void
pdsim_pagefree(struct vm_page *pg)
{
        struct uvm_object *obj;

        KASSERT(pg != NULL);

#if defined(SHOWFREE)
        if (pg->offset != -1) {
                int idx = pg->offset >> PAGE_SHIFT;
                printf("%d %d   # FREE IRR\n", idx, irr[idx]);
        }
#endif /* defined(SHOWFREE) */

        uvmpdpol_pagedequeue(pg);

        KASSERT(pg->uanon == NULL);
        obj = pg->uobject;
        if (obj != NULL) {
                int idx;

                idx = pg->offset >> PAGE_SHIFT;
                KASSERT(obj->pages[idx] == pg);
                obj->pages[idx] = NULL;
                uvmexp.filepages--;
        }
        TAILQ_INSERT_HEAD(&freeq, pg, pageq);
        uvmexp.free++;
}

static struct vm_page *
pdsim_pagelookup(struct uvm_object *obj, int index)
{
        struct vm_page *pg;

        pg = obj->pages[index];

        return pg;
}

static void
pdsim_pagemarkreferenced(struct vm_page *pg)
{

        pg->_mdflags |= MDPG_REFERENCED;
}

boolean_t
pmap_is_referenced(struct vm_page *pg)
{

        return pg->_mdflags & MDPG_REFERENCED;
}

boolean_t
pmap_clear_reference(struct vm_page *pg)
{
        boolean_t referenced = pmap_is_referenced(pg);

        pg->_mdflags &= ~MDPG_REFERENCED;

        return referenced;
}

static void
pdsim_init(int n)
{
        struct vm_page *pg;
        int i;

        uvmpdpol_init();
        uvmexp.npages = n;
        uvmpdpol_reinit();

        TAILQ_INIT(&freeq);
        pages = calloc(n, sizeof(*pg));
        for (i = 0; i < n; i++) {
                pg = &pages[i];
                pg->offset = -1;
                pdsim_pagefree(pg);
        }
}

static void
pdsim_reclaimone(void)
{
        struct vm_page *pg;

        uvmexp.freetarg = 1;
        while (uvmexp.free < uvmexp.freetarg) {
                uvmpdpol_tune();
                uvmpdpol_scaninit();
                pg = uvmpdpol_selectvictim();
                if (pg != NULL) {
                        pdsim_pagefree(pg);
                }
                uvmpdpol_balancequeue(0);
        }
}

static void
fault(struct uvm_object *obj, int index)
{
        struct vm_page *pg;

        DPRINTF("fault: %d -> ", index);
        nfault++;
        ts++;
        if (lastacc[index]) {
                irr[index] = ts - lastacc[index];
        }
        lastacc[index] = ts;
        stats[index].fault++;
        pg = pdsim_pagelookup(obj, index);
        if (pg) {
                DPRINTF("cached\n");
                pdsim_pagemarkreferenced(pg);
                stats[index].hit++;
                if ((pg->_mdflags & MDPG_SPECULATIVE) != 0) {
                        pg->_mdflags &= ~MDPG_SPECULATIVE;
                        rahit++;
                }
                return;
        }
        DPRINTF("miss\n");
retry:
        pg = pdsim_pagealloc(obj, index);
        if (pg == NULL) {
                pdsim_reclaimone();
                goto retry;
        }
        npagein++;
#if defined(SHOWFAULT)
        printf("%d      # FLT\n", index);
#endif
        pdsim_pagemarkreferenced(pg);
        uvmpdpol_pageactivate(pg);
        uvmpdpol_pageactivate(pg);
        dump("fault");
#if defined(READAHEAD)
        pg = pdsim_pagelookup(obj, index + 1);
        if (pg == NULL) {
ra_retry:
                pg = pdsim_pagealloc(obj, index + 1);
                if (pg == NULL) {
                        pdsim_reclaimone();
                        goto ra_retry;
                }
                raio++;
                pg->_mdflags |= MDPG_SPECULATIVE;
#if defined(SHOWFAULT)
                printf("%d      # READ-AHEAD\n", index + 1);
#endif
        }
        uvmpdpol_pageenqueue(pg);
        dump("read-ahead");
#endif /* defined(READAHEAD) */
}

struct uvm_object obj;

static void
test(void)
{
        memset(&obj, 0, sizeof(obj));
        char *ln;

        for (;; free(ln)) {
                int i;
                int ch;

                ln = fparseln(stdin, NULL, NULL, NULL, 0); 
                if (ln == NULL) {
                        break;
                }
                ch = *ln;
                if (ch == '\0') {
                        break;
                }
                if (ch == 'd') {
                        dump("test");
                        continue;
                }
                i = atoi(ln);
                fault(&obj, i);
#if defined(SHOWQLEN)
                showqlen();
#endif
        }
}

#if defined(DEBUG)
static void
dumpstats(void)
{
        int i;
        for (i = 0; i < MAXID; i++) {
                if (stats[i].fault == 0) {
                        continue;
                }
                DPRINTF("[%d] %d/%d %d\n", i,
                    stats[i].hit, stats[i].fault, irr[i]);
        }
}
#endif /* defined(DEBUG) */

int
main(int argc, char *argv[])
{

        setvbuf(stderr, NULL, _IOFBF, 0); /* XXX */

        pdsim_init(atoi(argv[1]));
        test();
        DPRINTF("io %d (%d + ra %d) / flt %d\n",
            npagein + raio, npagein, raio, nfault);
        DPRINTF("rahit / raio= %d / %d\n", rahit, raio);
#if defined(DEBUG)
        dumpstats();
#endif
        exit(0);
}