|
|
1.1 ! root 1: /* ! 2: * 'buffer.c' implements the buffer-cache functions. Race-conditions have ! 3: * been avoided by NEVER letting a interrupt change a buffer (except for the ! 4: * data, of course), but instead letting the caller do it. NOTE! As interrupts ! 5: * can wake up a caller, some cli-sti sequences are needed to check for ! 6: * sleep-on-calls. These should be extremely quick, though (I hope). ! 7: */ ! 8: ! 9: #include <linux/config.h> ! 10: #include <linux/sched.h> ! 11: #include <linux/kernel.h> ! 12: #include <asm/system.h> ! 13: ! 14: #if (BUFFER_END & 0xfff) ! 15: #error "Bad BUFFER_END value" ! 16: #endif ! 17: ! 18: #if (BUFFER_END > 0xA0000 && BUFFER_END <= 0x100000) ! 19: #error "Bad BUFFER_END value" ! 20: #endif ! 21: ! 22: extern int end; ! 23: struct buffer_head * start_buffer = (struct buffer_head *) &end; ! 24: struct buffer_head * hash_table[NR_HASH]; ! 25: static struct buffer_head * free_list; ! 26: static struct task_struct * buffer_wait = NULL; ! 27: int NR_BUFFERS = 0; ! 28: ! 29: static inline void wait_on_buffer(struct buffer_head * bh) ! 30: { ! 31: cli(); ! 32: while (bh->b_lock) ! 33: sleep_on(&bh->b_wait); ! 34: sti(); ! 35: } ! 36: ! 37: int sys_sync(void) ! 38: { ! 39: int i; ! 40: struct buffer_head * bh; ! 41: ! 42: sync_inodes(); /* write out inodes into buffers */ ! 43: bh = start_buffer; ! 44: for (i=0 ; i<NR_BUFFERS ; i++,bh++) { ! 45: wait_on_buffer(bh); ! 46: if (bh->b_dirt) ! 47: ll_rw_block(WRITE,bh); ! 48: } ! 49: return 0; ! 50: } ! 51: ! 52: static int sync_dev(int dev) ! 53: { ! 54: int i; ! 55: struct buffer_head * bh; ! 56: ! 57: bh = start_buffer; ! 58: for (i=0 ; i<NR_BUFFERS ; i++,bh++) { ! 59: if (bh->b_dev != dev) ! 60: continue; ! 61: wait_on_buffer(bh); ! 62: if (bh->b_dirt) ! 63: ll_rw_block(WRITE,bh); ! 64: } ! 65: return 0; ! 66: } ! 67: ! 68: #define _hashfn(dev,block) (((unsigned)(dev^block))%NR_HASH) ! 69: #define hash(dev,block) hash_table[_hashfn(dev,block)] ! 70: ! 71: static inline void remove_from_queues(struct buffer_head * bh) ! 72: { ! 73: /* remove from hash-queue */ ! 74: if (bh->b_next) ! 75: bh->b_next->b_prev = bh->b_prev; ! 76: if (bh->b_prev) ! 77: bh->b_prev->b_next = bh->b_next; ! 78: if (hash(bh->b_dev,bh->b_blocknr) == bh) ! 79: hash(bh->b_dev,bh->b_blocknr) = bh->b_next; ! 80: /* remove from free list */ ! 81: if (!(bh->b_prev_free) || !(bh->b_next_free)) ! 82: panic("Free block list corrupted"); ! 83: bh->b_prev_free->b_next_free = bh->b_next_free; ! 84: bh->b_next_free->b_prev_free = bh->b_prev_free; ! 85: if (free_list == bh) ! 86: free_list = bh->b_next_free; ! 87: } ! 88: ! 89: static inline void insert_into_queues(struct buffer_head * bh) ! 90: { ! 91: /* put at end of free list */ ! 92: bh->b_next_free = free_list; ! 93: bh->b_prev_free = free_list->b_prev_free; ! 94: free_list->b_prev_free->b_next_free = bh; ! 95: free_list->b_prev_free = bh; ! 96: /* put the buffer in new hash-queue if it has a device */ ! 97: bh->b_prev = NULL; ! 98: bh->b_next = NULL; ! 99: if (!bh->b_dev) ! 100: return; ! 101: bh->b_next = hash(bh->b_dev,bh->b_blocknr); ! 102: hash(bh->b_dev,bh->b_blocknr) = bh; ! 103: bh->b_next->b_prev = bh; ! 104: } ! 105: ! 106: static struct buffer_head * find_buffer(int dev, int block) ! 107: { ! 108: struct buffer_head * tmp; ! 109: ! 110: for (tmp = hash(dev,block) ; tmp != NULL ; tmp = tmp->b_next) ! 111: if (tmp->b_dev==dev && tmp->b_blocknr==block) ! 112: return tmp; ! 113: return NULL; ! 114: } ! 115: ! 116: /* ! 117: * Why like this, I hear you say... The reason is race-conditions. ! 118: * As we don't lock buffers (unless we are readint them, that is), ! 119: * something might happen to it while we sleep (ie a read-error ! 120: * will force it bad). This shouldn't really happen currently, but ! 121: * the code is ready. ! 122: */ ! 123: struct buffer_head * get_hash_table(int dev, int block) ! 124: { ! 125: struct buffer_head * bh; ! 126: ! 127: repeat: ! 128: if (!(bh=find_buffer(dev,block))) ! 129: return NULL; ! 130: bh->b_count++; ! 131: wait_on_buffer(bh); ! 132: if (bh->b_dev != dev || bh->b_blocknr != block) { ! 133: brelse(bh); ! 134: goto repeat; ! 135: } ! 136: return bh; ! 137: } ! 138: ! 139: /* ! 140: * Ok, this is getblk, and it isn't very clear, again to hinder ! 141: * race-conditions. Most of the code is seldom used, (ie repeating), ! 142: * so it should be much more efficient than it looks. ! 143: */ ! 144: struct buffer_head * getblk(int dev,int block) ! 145: { ! 146: struct buffer_head * tmp; ! 147: ! 148: repeat: ! 149: if (tmp=get_hash_table(dev,block)) ! 150: return tmp; ! 151: tmp = free_list; ! 152: do { ! 153: if (!tmp->b_count) { ! 154: wait_on_buffer(tmp); /* we still have to wait */ ! 155: if (!tmp->b_count) /* on it, it might be dirty */ ! 156: break; ! 157: } ! 158: tmp = tmp->b_next_free; ! 159: } while (tmp != free_list || (tmp=NULL)); ! 160: /* Kids, don't try THIS at home ^^^^^. Magic */ ! 161: if (!tmp) { ! 162: printk("Sleeping on free buffer .."); ! 163: sleep_on(&buffer_wait); ! 164: printk("ok\n"); ! 165: goto repeat; ! 166: } ! 167: tmp->b_count++; ! 168: remove_from_queues(tmp); ! 169: /* ! 170: * Now, when we know nobody can get to this node (as it's removed from the ! 171: * free list), we write it out. We can sleep here without fear of race- ! 172: * conditions. ! 173: */ ! 174: if (tmp->b_dirt) ! 175: sync_dev(tmp->b_dev); ! 176: /* update buffer contents */ ! 177: tmp->b_dev=dev; ! 178: tmp->b_blocknr=block; ! 179: tmp->b_dirt=0; ! 180: tmp->b_uptodate=0; ! 181: /* NOTE!! While we possibly slept in sync_dev(), somebody else might have ! 182: * added "this" block already, so check for that. Thank God for goto's. ! 183: */ ! 184: if (find_buffer(dev,block)) { ! 185: tmp->b_dev=0; /* ok, someone else has beaten us */ ! 186: tmp->b_blocknr=0; /* to it - free this block and */ ! 187: tmp->b_count=0; /* try again */ ! 188: insert_into_queues(tmp); ! 189: goto repeat; ! 190: } ! 191: /* and then insert into correct position */ ! 192: insert_into_queues(tmp); ! 193: return tmp; ! 194: } ! 195: ! 196: void brelse(struct buffer_head * buf) ! 197: { ! 198: if (!buf) ! 199: return; ! 200: wait_on_buffer(buf); ! 201: if (!(buf->b_count--)) ! 202: panic("Trying to free free buffer"); ! 203: wake_up(&buffer_wait); ! 204: } ! 205: ! 206: /* ! 207: * bread() reads a specified block and returns the buffer that contains ! 208: * it. It returns NULL if the block was unreadable. ! 209: */ ! 210: struct buffer_head * bread(int dev,int block) ! 211: { ! 212: struct buffer_head * bh; ! 213: ! 214: if (!(bh=getblk(dev,block))) ! 215: panic("bread: getblk returned NULL\n"); ! 216: if (bh->b_uptodate) ! 217: return bh; ! 218: ll_rw_block(READ,bh); ! 219: if (bh->b_uptodate) ! 220: return bh; ! 221: brelse(bh); ! 222: return (NULL); ! 223: } ! 224: ! 225: void buffer_init(void) ! 226: { ! 227: struct buffer_head * h = start_buffer; ! 228: void * b = (void *) BUFFER_END; ! 229: int i; ! 230: ! 231: while ( (b -= BLOCK_SIZE) >= ((void *) (h+1)) ) { ! 232: h->b_dev = 0; ! 233: h->b_dirt = 0; ! 234: h->b_count = 0; ! 235: h->b_lock = 0; ! 236: h->b_uptodate = 0; ! 237: h->b_wait = NULL; ! 238: h->b_next = NULL; ! 239: h->b_prev = NULL; ! 240: h->b_data = (char *) b; ! 241: h->b_prev_free = h-1; ! 242: h->b_next_free = h+1; ! 243: h++; ! 244: NR_BUFFERS++; ! 245: if (b == (void *) 0x100000) ! 246: b = (void *) 0xA0000; ! 247: } ! 248: h--; ! 249: free_list = start_buffer; ! 250: free_list->b_prev_free = h; ! 251: h->b_next_free = free_list; ! 252: for (i=0;i<NR_HASH;i++) ! 253: hash_table[i]=NULL; ! 254: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.