Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

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

/*
 *  read the inode blocks and make sure they
 *  haven't been trashed.
 *
 *  make the in-core table of qid to inode mappings.
 *      N.B. this is just an array. we need a linear search to find
 *           a particular inode. this could be done faster.
 *
 *  nab is the first inode block.
 */
int
iinit(Icache *ic, int f, int psize, char* name)
{
        Ibuf *b;
        Imap *m;
        ulong ino;
        Bbuf *bb;
        Dinode *bi;

        /*
         *  get basic sizes and allocation info from disk
         */
        if(dinit(ic, f, psize, name) < 0)
                return -1;

        /*
         *  read first inode block to get number of inodes
         */
        bb = bcread(ic, ic->nab);
        if(bb == 0){
                fprint(2, "iinit: can't read disk\n");
                return -1;
        }
        bi = (Dinode*)bb->data;
        if(bi->nino==0 || bi->nino>2048){
                fprint(2, "iinit: bad nino\n");
                return -1;
        }
        ic->nino = bi->nino;

        /*
         *  set up sizing constants
         */
        ic->i2b = (ic->bsize - sizeof(Dihdr))/sizeof(Inode);
        ic->nib = (ic->nino + ic->i2b - 1)/ic->i2b;

        /*
         *  allocate the in-core qid/inode map, build it's lru
         */
        if(ic->map)
                free(ic->map);
        ic->map = malloc(sizeof(Imap)*ic->nino);
        if(ic->map == 0){
                fprint(2, "iinit: can't alloc map\n");
                return -1;
        }
        lruinit(&ic->mlru);
        for(m = ic->map; m < &ic->map[ic->nino]; m++){
                m->inuse = 0;
                m->b = 0;
                lruadd(&ic->mlru, m);
        }

        /*
         *  mark all cache buffers as empty, put them on the lru list
         */
        lruinit(&ic->blru);
        for(b = ic->ib; b < &ic->ib[Nicache]; b++){
                b->inuse = 0;
                lruadd(&ic->blru, b);
        }

        /*
         *  Read all inodes and
         *  build the in-core qid/inode map
         */
        for(ino = 0; ino < ic->nino; ino++){
                b = iread(ic, ino);
                if(b == 0){
                        fprint(2, "iinit: can't read inode %ld\n", ino);
                        return -1;
                }
                if(b->inode.inuse){
                        m = &ic->map[ino];
                        m->inuse = 1;
                        m->qid = b->inode.qid;
                        lruref(&ic->mlru, m);
                }
        }
        return 0;
}

/*
 *  format the inode blocks
 */
int
iformat(Icache *ic, int f, ulong nino, char *name, int bsize, int psize)
{
        int nib;
        ulong bno, i2b, i;
        Bbuf *bb;
        Dinode *bi;

        /*
         *  first format disk allocation
         */
        if(dformat(ic, f, name, bsize, psize) < 0)
                return -1;

        fprint(2, "formatting inodes\n");

        i2b = (bsize - sizeof(Dihdr))/sizeof(Inode);
        nib = (nino + i2b - 1)/i2b;

        for(bno = ic->nab; bno < ic->nab + nib; bno++){
                if(dalloc(ic, 0) == Notabno){
                        fprint(2, "iformat: balloc failed\n");
                        return -1;
                }
                bb = bcalloc(ic, bno);
                if(bb == 0){
                        fprint(2, "iformat: bcalloc failed\n");
                        return -1;
                }
                bi = (Dinode*)bb->data;
                bi->magic = Imagic;
                bi->nino = nino;
                for(i = 0; i < i2b; i++)
                        bi->inode[i].inuse = 0;
                bcmark(ic, bb);
        }

        bcsync(ic);

        return iinit(ic, f, psize, name);
}

/*
 *  allocate a cache buffer, use least recently used
 */
Ibuf*
ialloc(Icache *ic, ulong ino)
{
        Imap *m;
        Ibuf *b;

        b = (Ibuf*)ic->blru.lnext;
        if(b->inuse)
                ic->map[b->ino].b = 0;
        b->ino = ino;
        b->inuse = 1;
        m = &ic->map[ino];
        m->b = b;
        return b;
}

/*
 *  free a cache buffer
 */
void
ifree(Icache *ic, Ibuf *b)
{
        b->inuse = 0;
        if(b->inuse)
                ic->map[b->ino].b = 0;
        lruderef(&ic->blru, b);
}

/*
 *  get an inode into the cache.  if no inode exists for this qid, create one
 *  from an unused qid/inode map.
 */
Ibuf *
iget(Icache *ic, Qid qid)
{
        Imap *m, *me;
        Ibuf *b;

        /*
         *  find map entry with same qid.path
         */
        for(m = ic->map, me = &ic->map[ic->nino]; m < me; m++)
                if(m->inuse && m->qid.path==qid.path){
                        if(m->qid.vers != qid.vers){
                                /*
                                 *  our info is old, forget it
                                 */
                                DPRINT(2, "updating old file %llud.%lud\n",
                                        qid.path, qid.vers);
                                m->qid = qid;
                                iupdate(ic, m - ic->map, qid);
                        }
                        break;
                }

        /*
         *  if an already existing inode, just get it
         */
        if(m != me)
                return iread(ic, m - ic->map);

        /*
         *  create a new inode, throw out the least recently used inode
         *  if necessary
         */
        m = (Imap*)ic->mlru.lnext;
        if(m->inuse){
                DPRINT(2, "superceding file %llud.%ld by %llud.%ld\n",
                        m->qid.path, m->qid.vers, qid.path, qid.vers);
                if(iremove(ic, m - ic->map) < 0)
                        return 0;
        }

        if(statson)
                cfsstat.ninsert++;
        /*
         *  init inode and write to disk
         */
        DPRINT(2, "new file %llud.%ld ino %ld\n",
                qid.path, qid.vers, m - ic->map);
        b = ialloc(ic, m - ic->map);
        b->inode.inuse = m->inuse = 1;
        b->inode.qid = qid;
        b->inode.length = 0x7fffffffffffffffLL;
        m->qid = qid;
        b->inode.ptr.bno = Notabno;
        iwrite(ic, b);
        return b;
}

/*
 *  read an inode into the cache
 *
 *  ASSUMPTION: the inode is valid
 */
Ibuf*
iread(Icache *ic, ulong ino)
{
        Ibuf *b;
        Imap *m;
        ulong bno;
        Bbuf *bb;
        Dinode *bi;

        /*
         *  first see if we already have it in a cache entry
         */
        m = &ic->map[ino];
        if(m->inuse && m->b){
                b = m->b;
                goto out;
        }

        /*
         *  read it
         */
        b = ialloc(ic, ino);
        bno = ic->nab + ino/ic->i2b;
        bb = bcread(ic, bno);
        if(bb == 0){
                ifree(ic, b);
                return 0;
        }
        bi = (Dinode*)bb->data;
        b->inode = bi->inode[ino % ic->i2b];

        /*
         *  consistency check
         */
        if(bi->nino!=ic->nino || bi->magic!=Imagic){
                fprint(2, "iread: inconsistent inode block\n");
                ifree(ic, b);
                return 0;
        }
out:
        b->inuse = 1;
        m->b = b;
        if(b->inode.inuse)
                lruref(&ic->mlru, m);
        lruref(&ic->blru, b);
        return b;
}

/*
 *  write an inode back to disk
 */
int
iwrite(Icache *ic, Ibuf *b)
{
        ulong bno;
        Bbuf *bb;
        Dinode *bi;

        bno = ic->nab + b->ino/ic->i2b;
        bb = bcread(ic, bno);
        if(bb == 0)
                return 0;
        bi = (Dinode*)bb->data;
        bi->inode[b->ino % ic->i2b] = b->inode;
        bcmark(ic, bb);
        lruref(&ic->mlru, &ic->map[b->ino]);
        lruref(&ic->blru, b);
        return 0;
}

/*
 *  Forget what we know about an inode without removing it
 *
 *      N.B: ordering of iwrite and dfree is important
 */
int
iupdate(Icache *ic, ulong ino, Qid qid)
{
        Ibuf *b;
        Imap *m;
        Dptr d;

        if(statson)
                cfsstat.nupdate++;
        b = iread(ic, ino);
        if(b == 0)
                return -1;

        /*
         *  update inode and map
         */
        b->inode.qid = qid;
        b->inode.length = 0x7fffffffffffffffLL; /* Set to maximum */
        m = &ic->map[ino];
        m->qid = qid;

        /*
         *  the free is not done if the write fails!
         *  this is important
         */
        d = b->inode.ptr;
        b->inode.ptr.bno = Notabno;
        if(iwrite(ic, b) < 0)
                return -1;
        dfree(ic, &d);
        return 0;
}

/*
 *  remove an inode
 *
 *      N.B: ordering of iwrite and dfree is important
 */
int
iremove(Icache *ic, ulong ino)
{
        Ibuf *b;
        Imap *m;

        if(statson)
                cfsstat.ndelete++;
        m = &ic->map[ino];

        /*
         *  read in inode
         */
        b = iread(ic, ino);
        if(b == 0)
                return -1;

        /*
         *  mark it unused on disk
         */
        b->inode.inuse = 0;
        if(iwrite(ic, b) < 0)
                return -1;

        /*
         *  throw out it's data pages
         */
        dfree(ic, &b->inode.ptr);

        /*
         *  free the inode buffer
         */
        ifree(ic, b);

        /*
         *  make map entry least recently used
         */
        lruderef(&ic->mlru, m);
        return 0;
}

/*
 *  increment our version number
 */
void
iinc(Icache *ic, Ibuf *b)
{
        b->inode.qid.vers++;
        ic->map[b->ino].qid = b->inode.qid;
        iwrite(ic, b);
}