/*
* allocate uncached memory
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"

#include <pool.h>

typedef struct Private Private;
struct Private {
       Lock;
       char    msg[256];
       char*   cur;
};

static Private ucprivate;

static void
ucpoolpanic(Pool* p, char* fmt, ...)
{
       va_list v;
       Private *pv;
       char msg[sizeof pv->msg];

       pv = p->private;
       va_start(v, fmt);
       vseprint(pv->cur, &pv->msg[sizeof pv->msg], fmt, v);
       va_end(v);
       memmove(msg, pv->msg, sizeof msg);
       iunlock(pv);
       panic("%s", msg);
}

static void
ucpoolprint(Pool* p, char* fmt, ...)
{
       va_list v;
       Private *pv;

       pv = p->private;
       va_start(v, fmt);
       pv->cur = vseprint(pv->cur, &pv->msg[sizeof pv->msg], fmt, v);
       va_end(v);
}

static void
ucpoolunlock(Pool* p)
{
       Private *pv;
       char msg[sizeof pv->msg];

       pv = p->private;
       if(pv->cur == pv->msg){
               iunlock(pv);
               return;
       }

       memmove(msg, pv->msg, sizeof msg);
       pv->cur = pv->msg;
       iunlock(pv);

       iprint("%.*s", sizeof pv->msg, msg);
}

static void
ucpoollock(Pool* p)
{
       Private *pv;

       pv = p->private;
       ilock(pv);
       pv->pc = getcallerpc(&p);
       pv->cur = pv->msg;
}

static void*
ucarena(usize size)
{
       void *uv, *v;

       assert(size == 1*MiB);

       mainmem->maxsize += 1*MiB;
       if((v = mallocalign(1*MiB, 1*MiB, 0, 0)) == nil ||
           (uv = mmuuncache(v, 1*MiB)) == nil){
               free(v);
               mainmem->maxsize -= 1*MiB;
               return nil;
       }
       return uv;
}

static Pool ucpool = {
       .name           = "Uncached",
       .maxsize        = 4*MiB,
       .minarena       = 1*MiB-32,
       .quantum        = 32,
       .alloc          = ucarena,
       .merge          = nil,
       .flags          = /*POOL_TOLERANCE|POOL_ANTAGONISM|POOL_PARANOIA|*/0,

       .lock           = ucpoollock,
       .unlock         = ucpoolunlock,
       .print          = ucpoolprint,
       .panic          = ucpoolpanic,

       .private        = &ucprivate,
};

void
ucfree(void* v)
{
       if(v == nil)
               return;
       poolfree(&ucpool, v);
}

void*
ucallocalign(usize size, int align, int span)
{
       void *v;

       assert(size < ucpool.minarena-128);
       v = poolallocalign(&ucpool, size, align, 0, span);
       if(v)
               memset(v, 0, size);
       return v;
}

void*
ucalloc(usize size)
{
       return ucallocalign(size, 32, 0);
}