Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.h>
#include "cformat.h"
#include "lru.h"
#include "bcache.h"

int
bcinit(Bcache *bc, int f, int bsize)
{
        Bbuf *b;

        /*
         *  allocate space for all buffers
         *  point all buffers into outer space
         */
        bc->dfirst = 0;
        bc->bsize = bsize;
        bc->f = f;
        lruinit(bc);
        for(b = bc->bb; b < &bc->bb[Nbcache]; b++){
                b->inuse = 0;
                b->next = 0;
                b->dirty = 0;
                if(b->data == 0)
                        b->data = (char *)malloc(bc->bsize);
                if(b->data == 0)
                        return -1;
                lruadd(bc, b);
        }

        return 0;
}

/*
 *  Find a buffer for block b.  If it's dirty, write it out.
 */
Bbuf *
bcfind(Bcache *bc, ulong bno)
{
        Bbuf *b;

        if(bno == Notabno)
                error("bcfind: Notabno");
        bno &= ~Indbno;

        /*
         *  if we already have a buffer for this bno, use it
         */
        for(b = bc->bb; b < &bc->bb[Nbcache]; b++)
                if(b->inuse && b->bno==bno)
                        goto out;

        /*
         *  get least recently used block
         */
        b = (Bbuf*)bc->lnext;
out:
        /*
         *  if dirty, write it out
         */
        if(b->dirty)
                if(bcwrite(bc, b) < 0)
                        warning("writing dirty page");
        lruref(bc, b);
        return b;
}

/*
 *  allocate a buffer block for a block.  it's guaranteed to be there till
 *  the next Nbcache bcread's.
 */
Bbuf *
bcalloc(Bcache *bc, ulong bno)
{
        Bbuf *b;

        b = bcfind(bc, bno);
        bno &= ~Indbno;
        b->bno = bno;
        b->inuse = 1;
        return b;
}

/*
 *  read a block into a buffer cache.  it's guaranteed to be there till
 *  the next Nbcache bcread's.
 */
Bbuf *
bcread(Bcache *bc, ulong bno)
{
        Bbuf *b;

        b = bcfind(bc, bno);
        bno &= ~Indbno;
        if(b->bno!=bno || !b->inuse)
                /*
                 *  read in the one we really want
                 */
                if(bread(bc, bno, b->data) < 0){
                        b->inuse = 0;
                        return 0;
                }
        b->bno = bno;
        b->inuse = 1;
        return b;
}

/*
 *  mark a page dirty, if it's already dirty force a write
 *
 *      N.B: ordering is important.
 */
void
bcmark(Bcache *bc, Bbuf *b)
{
        lruref(bc, b);

        if(b->dirty){
                bcwrite(bc, b);
                return;
        }

        b->dirty = 1;
        if(bc->dfirst)
                bc->dlast->next = b;
        else
                bc->dfirst = b;
        bc->dlast = b;
}

/*
 *  write out a page (and all preceding dirty ones)
 */
int
bcwrite(Bcache *bc, Bbuf *b)
{
        Bbuf *nb;

        /*
         *  write out all preceding pages
         */
        while(nb = bc->dfirst){
                if(bwrite(bc, nb->bno, nb->data) < 0)
                        return -1;
                nb->dirty = 0;
                bc->dfirst = nb->next;
                nb->next = 0;
                if(nb == b)
                        return 0;
        }

        /*
         *  write out this page
         */
        if(bwrite(bc, b->bno, b->data) < 0)
                return -1;
        b->dirty = 0;
        b->next = 0;
        return 0;
}

/*
 *  write out all dirty pages (in order)
 */
int
bcsync(Bcache *bc)
{
        if(bc->dfirst)
                return bcwrite(bc, bc->dlast);
        return 0;
}

/*
 *  read a block from disk
 */
int
bread(Bcache *bc, ulong bno, void *buf)
{
        uvlong x = (uvlong)bno * bc->bsize;

        if(pread(bc->f, buf, bc->bsize, x) != bc->bsize)
                return -1;
        return 0;
}

/*
 *  write a block to disk
 */
int
bwrite(Bcache *bc, ulong bno, void *buf)
{
        uvlong x = (uvlong)bno * bc->bsize;

        if(pwrite(bc->f, buf, bc->bsize, x) != bc->bsize)
                return -1;
        return 0;
}