|
|
1.1 root 1: /* 1.1.1.2 ! root 2: * linux/fs/buffer.c ! 3: * ! 4: * (C) 1991 Linus Torvalds ! 5: */ ! 6: ! 7: /* 1.1 root 8: * 'buffer.c' implements the buffer-cache functions. Race-conditions have 9: * been avoided by NEVER letting a interrupt change a buffer (except for the 10: * data, of course), but instead letting the caller do it. NOTE! As interrupts 11: * can wake up a caller, some cli-sti sequences are needed to check for 12: * sleep-on-calls. These should be extremely quick, though (I hope). 13: */ 14: 1.1.1.2 ! root 15: /* ! 16: * NOTE! There is one discordant note here: checking floppies for ! 17: * disk change. This is where it fits best, I think, as it should ! 18: * invalidate changed floppy-disk-caches. ! 19: */ ! 20: ! 21: #include <stdarg.h> ! 22: 1.1 root 23: #include <linux/config.h> 24: #include <linux/sched.h> 25: #include <linux/kernel.h> 26: #include <asm/system.h> 1.1.1.2 ! root 27: #include <asm/io.h> 1.1 root 28: 29: extern int end; 30: struct buffer_head * start_buffer = (struct buffer_head *) &end; 31: struct buffer_head * hash_table[NR_HASH]; 32: static struct buffer_head * free_list; 33: static struct task_struct * buffer_wait = NULL; 34: int NR_BUFFERS = 0; 35: 36: static inline void wait_on_buffer(struct buffer_head * bh) 37: { 38: cli(); 39: while (bh->b_lock) 40: sleep_on(&bh->b_wait); 41: sti(); 42: } 43: 44: int sys_sync(void) 45: { 46: int i; 47: struct buffer_head * bh; 48: 49: sync_inodes(); /* write out inodes into buffers */ 50: bh = start_buffer; 51: for (i=0 ; i<NR_BUFFERS ; i++,bh++) { 52: wait_on_buffer(bh); 53: if (bh->b_dirt) 54: ll_rw_block(WRITE,bh); 55: } 56: return 0; 57: } 58: 1.1.1.2 ! root 59: int sync_dev(int dev) 1.1 root 60: { 61: int i; 62: struct buffer_head * bh; 63: 64: bh = start_buffer; 65: for (i=0 ; i<NR_BUFFERS ; i++,bh++) { 66: if (bh->b_dev != dev) 67: continue; 68: wait_on_buffer(bh); 1.1.1.2 ! root 69: if (bh->b_dev == dev && bh->b_dirt) ! 70: ll_rw_block(WRITE,bh); ! 71: } ! 72: sync_inodes(); ! 73: bh = start_buffer; ! 74: for (i=0 ; i<NR_BUFFERS ; i++,bh++) { ! 75: if (bh->b_dev != dev) ! 76: continue; ! 77: wait_on_buffer(bh); ! 78: if (bh->b_dev == dev && bh->b_dirt) 1.1 root 79: ll_rw_block(WRITE,bh); 80: } 81: return 0; 82: } 83: 1.1.1.2 ! root 84: /* ! 85: * This routine checks whether a floppy has been changed, and ! 86: * invalidates all buffer-cache-entries in that case. This ! 87: * is a relatively slow routine, so we have to try to minimize using ! 88: * it. Thus it is called only upon a 'mount' or 'open'. This ! 89: * is the best way of combining speed and utility, I think. ! 90: * People changing diskettes in the middle of an operation deserve ! 91: * to loose :-) ! 92: * ! 93: * NOTE! Although currently this is only for floppies, the idea is ! 94: * that any additional removable block-device will use this routine, ! 95: * and that mount/open needn't know that floppies/whatever are ! 96: * special. ! 97: */ ! 98: void check_disk_change(int dev) ! 99: { ! 100: int i; ! 101: struct buffer_head * bh; ! 102: ! 103: if (MAJOR(dev) != 2) ! 104: return; ! 105: dev=MINOR(dev) & 0x03; /* which floppy is it? */ ! 106: if (!floppy_change(dev)) ! 107: return; ! 108: dev |= 0x200; ! 109: for (i=0 ; i<NR_SUPER ; i++) ! 110: if ((super_block[i].s_dev & 0xff03)==dev) ! 111: put_super(super_block[i].s_dev); ! 112: bh = start_buffer; ! 113: for (i=0 ; i<NR_BUFFERS ; i++,bh++) { ! 114: if ((bh->b_dev & 0xff03) != dev) ! 115: continue; ! 116: wait_on_buffer(bh); ! 117: if ((bh->b_dev & 0xff03) == dev) ! 118: bh->b_uptodate = bh->b_dirt = 0; ! 119: } ! 120: } ! 121: 1.1 root 122: #define _hashfn(dev,block) (((unsigned)(dev^block))%NR_HASH) 123: #define hash(dev,block) hash_table[_hashfn(dev,block)] 124: 125: static inline void remove_from_queues(struct buffer_head * bh) 126: { 127: /* remove from hash-queue */ 128: if (bh->b_next) 129: bh->b_next->b_prev = bh->b_prev; 130: if (bh->b_prev) 131: bh->b_prev->b_next = bh->b_next; 132: if (hash(bh->b_dev,bh->b_blocknr) == bh) 133: hash(bh->b_dev,bh->b_blocknr) = bh->b_next; 134: /* remove from free list */ 135: if (!(bh->b_prev_free) || !(bh->b_next_free)) 136: panic("Free block list corrupted"); 137: bh->b_prev_free->b_next_free = bh->b_next_free; 138: bh->b_next_free->b_prev_free = bh->b_prev_free; 139: if (free_list == bh) 140: free_list = bh->b_next_free; 141: } 142: 143: static inline void insert_into_queues(struct buffer_head * bh) 144: { 145: /* put at end of free list */ 146: bh->b_next_free = free_list; 147: bh->b_prev_free = free_list->b_prev_free; 148: free_list->b_prev_free->b_next_free = bh; 149: free_list->b_prev_free = bh; 150: /* put the buffer in new hash-queue if it has a device */ 151: bh->b_prev = NULL; 152: bh->b_next = NULL; 153: if (!bh->b_dev) 154: return; 155: bh->b_next = hash(bh->b_dev,bh->b_blocknr); 156: hash(bh->b_dev,bh->b_blocknr) = bh; 157: bh->b_next->b_prev = bh; 158: } 159: 160: static struct buffer_head * find_buffer(int dev, int block) 161: { 162: struct buffer_head * tmp; 163: 164: for (tmp = hash(dev,block) ; tmp != NULL ; tmp = tmp->b_next) 165: if (tmp->b_dev==dev && tmp->b_blocknr==block) 166: return tmp; 167: return NULL; 168: } 169: 170: /* 171: * Why like this, I hear you say... The reason is race-conditions. 172: * As we don't lock buffers (unless we are readint them, that is), 173: * something might happen to it while we sleep (ie a read-error 174: * will force it bad). This shouldn't really happen currently, but 175: * the code is ready. 176: */ 177: struct buffer_head * get_hash_table(int dev, int block) 178: { 179: struct buffer_head * bh; 180: 1.1.1.2 ! root 181: for (;;) { ! 182: if (!(bh=find_buffer(dev,block))) ! 183: return NULL; ! 184: bh->b_count++; ! 185: wait_on_buffer(bh); ! 186: if (bh->b_dev == dev && bh->b_blocknr == block) ! 187: return bh; ! 188: bh->b_count--; 1.1 root 189: } 190: } 191: 192: /* 193: * Ok, this is getblk, and it isn't very clear, again to hinder 194: * race-conditions. Most of the code is seldom used, (ie repeating), 195: * so it should be much more efficient than it looks. 1.1.1.2 ! root 196: * ! 197: * The algoritm is changed: better, and an elusive bug removed. ! 198: * LBT 11.11.91 1.1 root 199: */ 1.1.1.2 ! root 200: #define BADNESS(bh) (((bh)->b_dirt<<1)+(bh)->b_lock) 1.1 root 201: struct buffer_head * getblk(int dev,int block) 202: { 1.1.1.2 ! root 203: struct buffer_head * tmp, * bh; 1.1 root 204: 205: repeat: 1.1.1.2 ! root 206: if (bh = get_hash_table(dev,block)) ! 207: return bh; 1.1 root 208: tmp = free_list; 209: do { 1.1.1.2 ! root 210: if (tmp->b_count) ! 211: continue; ! 212: if (!bh || BADNESS(tmp)<BADNESS(bh)) { ! 213: bh = tmp; ! 214: if (!BADNESS(tmp)) 1.1 root 215: break; 216: } 1.1.1.2 ! root 217: } while ((tmp = tmp->b_next_free) != free_list); ! 218: if (!bh) { 1.1 root 219: sleep_on(&buffer_wait); 220: goto repeat; 221: } 1.1.1.2 ! root 222: wait_on_buffer(bh); ! 223: if (bh->b_count) 1.1 root 224: goto repeat; 1.1.1.2 ! root 225: while (bh->b_dirt) { ! 226: sync_dev(bh->b_dev); ! 227: wait_on_buffer(bh); ! 228: if (bh->b_count) ! 229: goto repeat; 1.1 root 230: } 1.1.1.2 ! root 231: /* NOTE!! While we slept waiting for this block, somebody else might */ ! 232: /* already have added "this" block to the cache. check it */ ! 233: if (find_buffer(dev,block)) ! 234: goto repeat; ! 235: /* OK, FINALLY we know that this buffer is the only one of it's kind, */ ! 236: /* and that it's unused (b_count=0), unlocked (b_lock=0), and clean */ ! 237: bh->b_count=1; ! 238: bh->b_dirt=0; ! 239: bh->b_uptodate=0; ! 240: remove_from_queues(bh); ! 241: bh->b_dev=dev; ! 242: bh->b_blocknr=block; ! 243: insert_into_queues(bh); ! 244: return bh; 1.1 root 245: } 246: 247: void brelse(struct buffer_head * buf) 248: { 249: if (!buf) 250: return; 251: wait_on_buffer(buf); 252: if (!(buf->b_count--)) 253: panic("Trying to free free buffer"); 254: wake_up(&buffer_wait); 255: } 256: 257: /* 258: * bread() reads a specified block and returns the buffer that contains 259: * it. It returns NULL if the block was unreadable. 260: */ 261: struct buffer_head * bread(int dev,int block) 262: { 263: struct buffer_head * bh; 264: 265: if (!(bh=getblk(dev,block))) 266: panic("bread: getblk returned NULL\n"); 267: if (bh->b_uptodate) 268: return bh; 269: ll_rw_block(READ,bh); 1.1.1.2 ! root 270: wait_on_buffer(bh); ! 271: if (bh->b_uptodate) ! 272: return bh; ! 273: brelse(bh); ! 274: return NULL; ! 275: } ! 276: ! 277: /* ! 278: * Ok, breada can be used as bread, but additionally to mark other ! 279: * blocks for reading as well. End the argument list with a negative ! 280: * number. ! 281: */ ! 282: struct buffer_head * breada(int dev,int first, ...) ! 283: { ! 284: va_list args; ! 285: struct buffer_head * bh, *tmp; ! 286: ! 287: va_start(args,first); ! 288: if (!(bh=getblk(dev,first))) ! 289: panic("bread: getblk returned NULL\n"); ! 290: if (!bh->b_uptodate) ! 291: ll_rw_block(READ,bh); ! 292: while ((first=va_arg(args,int))>=0) { ! 293: tmp=getblk(dev,first); ! 294: if (tmp) { ! 295: if (!tmp->b_uptodate) ! 296: ll_rw_block(READA,bh); ! 297: tmp->b_count--; ! 298: } ! 299: } ! 300: va_end(args); ! 301: wait_on_buffer(bh); 1.1 root 302: if (bh->b_uptodate) 303: return bh; 304: brelse(bh); 305: return (NULL); 306: } 307: 1.1.1.2 ! root 308: void buffer_init(long buffer_end) 1.1 root 309: { 310: struct buffer_head * h = start_buffer; 1.1.1.2 ! root 311: void * b; 1.1 root 312: int i; 313: 1.1.1.2 ! root 314: if (buffer_end == 1<<20) ! 315: b = (void *) (640*1024); ! 316: else ! 317: b = (void *) buffer_end; 1.1 root 318: while ( (b -= BLOCK_SIZE) >= ((void *) (h+1)) ) { 319: h->b_dev = 0; 320: h->b_dirt = 0; 321: h->b_count = 0; 322: h->b_lock = 0; 323: h->b_uptodate = 0; 324: h->b_wait = NULL; 325: h->b_next = NULL; 326: h->b_prev = NULL; 327: h->b_data = (char *) b; 328: h->b_prev_free = h-1; 329: h->b_next_free = h+1; 330: h++; 331: NR_BUFFERS++; 332: if (b == (void *) 0x100000) 333: b = (void *) 0xA0000; 334: } 335: h--; 336: free_list = start_buffer; 337: free_list->b_prev_free = h; 338: h->b_next_free = free_list; 339: for (i=0;i<NR_HASH;i++) 340: hash_table[i]=NULL; 341: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.