From affb88d7f440590ba0249ed56830d8dbd3550500 Mon Sep 17 00:00:00 2001 From: Will Sowerbutts Date: Mon, 26 Jan 2015 21:31:23 +0000 Subject: [PATCH] Kernel: Change bf_free to tristate -- free, busy, superblock. Superblocks are never freed by bfree(). This allows the superblock to be read by user space from the raw block device without being freed until the filesystem is later unmounted. --- Kernel/devio.c | 62 ++++++++++++++++++++++------------------- Kernel/filesys.c | 6 ++-- Kernel/include/kernel.h | 7 ++++- Kernel/start.c | 2 +- Kernel/syscall_other.c | 1 + 5 files changed, 45 insertions(+), 33 deletions(-) diff --git a/Kernel/devio.c b/Kernel/devio.c index f17285a0..d5843871 100644 --- a/Kernel/devio.c +++ b/Kernel/devio.c @@ -34,25 +34,27 @@ uint8_t *bread(uint16_t dev, blkno_t blk, bool rewrite) register bufptr bp; if ((bp = bfind(dev, blk)) != NULL) { - if (bp->bf_busy) + if (bp->bf_busy == BF_BUSY) panic("want busy block"); - goto done; - } - bp = freebuf(); - bp->bf_dev = dev; - bp->bf_blk = blk; - - /* If rewrite is set, we are about to write over the entire block, - so we don't need the previous contents */ - if (!rewrite) { - if (bdread(bp) == -1) { - udata.u_error = EIO; - return (NULL); + else if (bp->bf_busy == BF_FREE) + bp->bf_busy = BF_BUSY; + /* BF_SUPERBLOCK is fine */ + } else { + bp = freebuf(); + bp->bf_dev = dev; + bp->bf_blk = blk; + bp->bf_busy = BF_BUSY; + + /* If rewrite is set, we are about to write over the entire block, + so we don't need the previous contents */ + if (!rewrite) { + if (bdread(bp) == -1) { + udata.u_error = EIO; + return (NULL); + } } } - done: - bp->bf_busy = 1; bp->bf_time = ++bufclock; /* Time stamp it */ return (bp->bf_data); } @@ -74,7 +76,9 @@ int bfree(bufptr bp, uint8_t dirty) { /* dirty: 0=clean, 1=dirty (write back), 2=dirty+immediate write */ if (dirty) bp->bf_dirty = true; - bp->bf_busy = false; + + if(bp->bf_busy == BF_BUSY) /* do not free BF_SUPERBLOCK */ + bp->bf_busy = BF_FREE; if (dirty > 1) { // immediate writeback if (bdwrite(bp) == -1) @@ -96,7 +100,7 @@ void *tmpbuf(void) bp = freebuf(); bp->bf_dev = NO_DEVICE; - bp->bf_busy = true; + bp->bf_busy = BF_BUSY; bp->bf_time = ++bufclock; /* Time stamp it */ return bp->bf_data; } @@ -120,7 +124,7 @@ void bufsync(void) for (bp = bufpool; bp < bufpool + NBUFS; ++bp) { if ((bp->bf_dev != NO_DEVICE) && bp->bf_dirty) { bdwrite(bp); - if (!bp->bf_busy) + if (bp->bf_busy == BF_FREE) bp->bf_dirty = false; d_flush(bp->bf_dev); } @@ -149,7 +153,7 @@ bufptr freebuf(void) oldest = NULL; oldtime = 0; for (bp = bufpool; bp < bufpool + NBUFS; ++bp) { - if (bufclock - bp->bf_time >= oldtime && !bp->bf_busy) { + if (bufclock - bp->bf_time >= oldtime && bp->bf_busy == BF_FREE) { oldest = bp; oldtime = bufclock - bp->bf_time; } @@ -178,16 +182,6 @@ void bufdiscard(bufptr bp) bp->bf_time = bufclock - 1000; } -void bufdump(void) -{ - bufptr j; - - kprintf("\ndev\tblock\tdirty\tbusy\ttime clock %d\n", bufclock); - for (j = bufpool; j < bufpool + NBUFS; ++j) - kprintf("%d\t%u\t%d\t%d\t%u\n", j->bf_dev, j->bf_blk, - j->bf_dirty, j->bf_busy, j->bf_time); -} - /********************************************************************* Bdread() and bdwrite() are the block device interface routines. They @@ -532,6 +526,16 @@ void kprintf(const char *fmt, ...) #ifdef CONFIG_IDUMP +void bufdump(void) +{ + bufptr j; + + kprintf("\ndev\tblock\tdirty\tbusy\ttime clock %d\n", bufclock); + for (j = bufpool; j < bufpool + NBUFS; ++j) + kprintf("%d\t%u\t%d\t%d\t%u\n", j->bf_dev, j->bf_blk, + j->bf_dirty, j->bf_busy, j->bf_time); +} + void idump(void) { inoptr ip; diff --git a/Kernel/filesys.c b/Kernel/filesys.c index da1c81f2..8c78bb49 100644 --- a/Kernel/filesys.c +++ b/Kernel/filesys.c @@ -1071,9 +1071,11 @@ bool fmount(uint16_t dev, inoptr ino, uint16_t flags) udata.u_error = EMFILE; return true; /* Table is full */ } - /* Pin the buffer at dev 0 blk 1, it will only be released - by umount */ + + /* Pin the buffer with the superblock (block 1), it + * will only be released by umount */ fp = (filesys *)bread(dev, 1, 0); + ((bufptr)fp)->bf_busy = BF_SUPERBLOCK; /* really really busy */ // kprintf("fp->s_mounted=0x%x, fp->s_isize=0x%x, fp->s_fsize=0x%x\n", // fp->s_mounted, fp->s_isize, fp->s_fsize); diff --git a/Kernel/include/kernel.h b/Kernel/include/kernel.h index 7d09e34c..8f43f70b 100644 --- a/Kernel/include/kernel.h +++ b/Kernel/include/kernel.h @@ -93,6 +93,11 @@ typedef uint16_t blkno_t; /* Can have 65536 512-byte blocks in filesystem */ #define BLKSHIFT 9 #define BLKMASK 511 +/* we need a busier-than-busy state for superblocks, so that if those blocks + * are read by userspace through bread() they are not subsequently freed by + * bfree() until the filesystem is unmounted */ +typedef enum { BF_FREE=0, BF_BUSY, BF_SUPERBLOCK } bf_busy_t; + /* FIXME: if we could split the data and the header we could keep blocks outside of our kernel data (as ELKS does) which would be a win, but need some more care on copies, block indexes and directory ops */ @@ -101,7 +106,7 @@ typedef struct blkbuf { uint16_t bf_dev; blkno_t bf_blk; bool bf_dirty; - bool bf_busy; + bf_busy_t bf_busy; uint16_t bf_time; /* LRU time stamp */ } blkbuf, *bufptr; diff --git a/Kernel/start.c b/Kernel/start.c index ec9d4765..e96a7b00 100644 --- a/Kernel/start.c +++ b/Kernel/start.c @@ -37,7 +37,7 @@ void bufinit(void) for (bp = bufpool; bp < bufpool + NBUFS; ++bp) { bp->bf_dev = NO_DEVICE; - bp->bf_busy = false; + bp->bf_busy = BF_FREE; } } diff --git a/Kernel/syscall_other.c b/Kernel/syscall_other.c index 50c4ac2d..1bb36074 100644 --- a/Kernel/syscall_other.c +++ b/Kernel/syscall_other.c @@ -352,6 +352,7 @@ int16_t _umount(void) i_deref(mnt->m_fs->s_mntpt); /* Give back the buffer we pinned at mount time */ + ((bufptr)mnt->m_fs)->bf_busy = BF_BUSY; /* downgrade from BF_SUPERBLOCK */ bfree((bufptr)mnt->m_fs, 2); /* Vanish the entry */ mnt->m_dev = NO_DEVICE; -- 2.34.1