summaryrefslogtreecommitdiff
path: root/kernel/fs/buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/fs/buffer.c')
-rw-r--r--kernel/fs/buffer.c102
1 files changed, 61 insertions, 41 deletions
diff --git a/kernel/fs/buffer.c b/kernel/fs/buffer.c
index 7405738..99c3161 100644
--- a/kernel/fs/buffer.c
+++ b/kernel/fs/buffer.c
@@ -1,16 +1,76 @@
+#include <asm/interrupt.h>
+#include <errno.h>
#include <kernel/con.h>
#include <kernel/fs.h>
#include <stdint.h>
#include <string.h>
/* memory dedicated to the block cache */
+/* TODO: start buffers at kernel end not 512K */
#define BSTART 0x80000
#define BEND 0xA0000
static struct buffer *lru;
static struct buffer *mru;
-struct buffer *buffer_get_block(uint16_t device, uint16_t block);
+struct buffer *buffer_get_block(uint16_t device, uint16_t block) {
+ struct buffer **p = &mru;
+ struct buffer *b;
+
+ if(!device)
+ return NULL;
+
+ /* check if the requested block is already in the cache */
+ while(b = *p) {
+ if(b->b_device != device || b->b_block != block)
+ goto next;
+ /* remove the buffer from the list */
+ if(lru == b && b->b_next) lru = b->b_next;
+ if(b->b_next) b->b_next->b_prev = b->b_prev;
+ if(b->b_prev) b->b_prev->b_next = b->b_next;
+ /* put the buffer back at the MRU end of the list */
+ b->b_next = NULL;
+ b->b_prev = mru;
+ if(mru != b)
+ mru->b_next = b;
+ mru = b;
+ cli();
+ /* if the buffer exists but isn't populated. wait on it */
+ if(!b->b_present) {
+ sleep_on(&b->b_wait);
+ printk("[buf] Waiting for buffer %04x:%04x to be populated...\n", b->b_device, b->b_block);
+ }
+ sti();
+ printk("[buf] Fetched block %04x:%04x from cache\n", b->b_device, b->b_block);
+ return b->b_device ? b : NULL;
+next:
+ p = &b->b_prev;
+ }
+
+ /*
+ * otherwise, iterate through the least recently used
+ * buffers, to find a buffer that isn't pending an
+ * I/O operation, and read into it.
+ */
+
+ p = &lru;
+ while(b = *p) {
+ if(b->b_present || !b->b_device)
+ break;
+ p = &(*p)->b_next;
+ }
+
+ if(!b)
+ return NULL;
+
+ b->b_device = device;
+ b->b_block = block;
+ b->b_present = 0;
+ block_read(b);
+ wake_up(&b->b_wait);
+ printk("[buf] Called driver to read block %04x:%04x\n", b->b_device, b->b_block);
+ return b->b_device ? b : NULL;
+}
void buffer_init(void) {
int c = 0;
@@ -40,43 +100,3 @@ void buffer_init(void) {
printk("[buf] Buffers initialized (0x%x buffers in pool)\n", c);
}
-
-struct buffer *buffer_get_block(uint16_t device, uint16_t block) {
- int ret;
- struct buffer **p = &mru;
- struct buffer *b;
-
- /* check if the requested block is already in the cache */
- while(*p) {
- if((*p)->b_device == device && (*p)->b_block == block) {
- /* put the buffer back at the MRU end of the list */
- (*p)->b_next = NULL;
- (*p)->b_prev = mru;
- mru = *p;
- return *p;
- }
- p = &(*p)->b_prev;
- }
-
- /* find a buffer at the end of the LRU list */
- b = lru;
- lru = lru->b_next;
- lru->b_prev = NULL;
- ret = block_read(b->b_data, block);
- /* abort if we failed to read in the block */
- if(ret < 0) {
- /* release the allocated buffer to the pool */
- lru->b_prev = b;
- b->b_next = lru;
- b->b_prev = NULL;
- lru = b;
- return NULL;
- }
- b->b_device = device;
- b->b_block = block;
- /* put the buffer back at the MRU end of the list */
- b->b_next = NULL;
- b->b_prev = mru;
- mru = b;
- return b;
-}