diff --git a/fs/buffer.c b/fs/buffer.c index f5c8deab461a2f4d0a777897d8f38405a4ce6e96..ceb30e9b76d5f45188e35a1d9651f4746e304f20 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -10,6 +10,7 @@ extern int end; struct buffer_head * start_buffer = (struct buffer_head *) &end; struct buffer_head * hash_table[NR_HASH]; static struct buffer_head * free_list; +static struct task_struct * buffer_wait = NULL; int NR_BUFFERS = 0; inline void wait_on_buffer(struct buffer_head * bh) { @@ -20,6 +21,8 @@ inline void wait_on_buffer(struct buffer_head * bh) { sti(); } +#define _hashfn(dev,block) (((unsigned)(dev^block))%NR_HASH) +#define hash(dev,block) hash_table[_hashfn(dev,block)] static inline void remove_from_queues(struct buffer_head * bh) { if (bh->b_next) @@ -27,25 +30,98 @@ static inline void remove_from_queues(struct buffer_head * bh) { if (bh->b_prev) bh->b_prev->b_next = bh->b_next; + if (hash(bh->b_dev,bh->b_blocknr) == bh) + hash(bh->b_dev,bh->b_blocknr) = bh->b_next; + + if (!(bh->b_prev_free) || !(bh->b_next_free)) + printk("Free block list corrupted"); + bh->b_prev_free->b_next_free = bh->b_next_free; bh->b_next_free->b_prev_free = bh->b_prev_free; if (free_list == bh) free_list = bh->b_next_free; } +static inline void insert_into_queues(struct buffer_head * bh) { + bh->b_next_free = free_list; + bh->b_prev_free = free_list->b_prev_free; + free_list->b_prev_free->b_next_free = bh; + free_list->b_prev_free = bh; + + bh->b_prev = NULL; + bh->b_next = NULL; + if (!bh->b_dev) + return; + bh->b_next = hash(bh->b_dev,bh->b_blocknr); + hash(bh->b_dev,bh->b_blocknr) = bh; + if (bh->b_next) + bh->b_next->b_prev = bh; +} + +static struct buffer_head * find_buffer(int dev, int block) { + struct buffer_head * tmp; + for (tmp = hash(dev,block); tmp != NULL ; tmp = tmp->b_next) { + if (tmp->b_dev==dev && tmp->b_blocknr==block) + return tmp; + } + + return NULL; +} + +struct buffer_head * get_hash_table(int dev, int block) { + struct buffer_head * bh; + for (;;) { + if (!(bh=find_buffer(dev,block))) + return NULL; + bh->b_count++; + wait_on_buffer(bh); + if (bh->b_dev == dev && bh->b_blocknr == block) + return bh; + bh->b_count--; + } +} +#define BADNESS(bh) (((bh)->b_dirt<<1)+(bh)->b_lock) struct buffer_head * getblk(int dev,int block) { struct buffer_head * tmp, * bh; +repeat: + bh = get_hash_table(dev, block); + if (bh) return bh; + tmp = free_list; do { if (tmp->b_count) continue; - else - break; + + if (!bh || BADNESS(tmp)b_next_free) != free_list); + if (!bh) { + sleep_on(&buffer_wait); + goto repeat; + } + + wait_on_buffer(bh); + if (bh->b_count) + goto repeat; + + /* + while (bh->b_dirt) { + sync_dev(bh->b_dev); + wait_on_buffer(bh); + if (bh->b_count) + goto repeat; + }*/ + + if (find_buffer(dev,block)) + goto repeat; + bh = tmp; bh->b_count=1; bh->b_dirt=0; @@ -53,17 +129,33 @@ struct buffer_head * getblk(int dev,int block) { remove_from_queues(bh); bh->b_dev=dev; bh->b_blocknr=block; + insert_into_queues(bh); return bh; } +void brelse(struct buffer_head * buf) { + if (!buf) + return; + + wait_on_buffer(buf); + if (!(buf->b_count--)) + printk("Trying to free free buffer"); + wake_up(&buffer_wait); +} + struct buffer_head * bread(int dev,int block) { struct buffer_head * bh; - bh = getblk(dev,block); + if (!(bh=getblk(dev,block))) { + printk("bread: getblk returned NULL\n"); + } + if (bh->b_uptodate) + return bh; ll_rw_block(READ,bh); wait_on_buffer(bh); if (bh->b_uptodate) return bh; + brelse(bh); return NULL; } diff --git a/kernel/blk_drv/ll_rw_blk.c b/kernel/blk_drv/ll_rw_blk.c index bb699da95ede75c9cb267ac6458827cf97e1b4da..313c81c1e74f40a9ad2037e10c9b4ed596f206ec 100644 --- a/kernel/blk_drv/ll_rw_blk.c +++ b/kernel/blk_drv/ll_rw_blk.c @@ -8,7 +8,6 @@ struct request request[NR_REQUEST]; inline void lock_buffer(struct buffer_head * bh) { - printk("lock buffer\n"); cli(); while (bh->b_lock) sleep_on(&bh->b_wait); @@ -17,7 +16,6 @@ inline void lock_buffer(struct buffer_head * bh) { } inline void unlock_buffer(struct buffer_head * bh) { - printk("unlock buffer\n"); if (!bh->b_lock) printk("ll_rw_block.c: buffer not locked\n\r"); bh->b_lock = 0;