Annotation of linux/fs/buffer.c, revision 1.1.1.4

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: 
1.1.1.4 ! root       44: static void sync_buffers(int dev)
1.1       root       45: {
                     46:        int i;
                     47:        struct buffer_head * bh;
                     48: 
1.1.1.4 ! root       49:        bh = free_list;
        !            50:        for (i=0 ; i<NR_BUFFERS ; i++,bh = bh->b_next_free) {
        !            51: #if 0
        !            52:                if (dev && (bh->b_dev != dev))
        !            53:                        continue;
        !            54: #endif
1.1       root       55:                wait_on_buffer(bh);
1.1.1.4 ! root       56: #if 0
        !            57:                if (dev && (bh->b_dev != dev))
        !            58:                        continue;
        !            59: #endif
1.1       root       60:                if (bh->b_dirt)
                     61:                        ll_rw_block(WRITE,bh);
                     62:        }
1.1.1.4 ! root       63: }
        !            64: 
        !            65: int sys_sync(void)
        !            66: {
        !            67:        sync_inodes();          /* write out inodes into buffers */
        !            68:        sync_buffers(0);
1.1       root       69:        return 0;
                     70: }
                     71: 
1.1.1.2   root       72: int sync_dev(int dev)
1.1       root       73: {
1.1.1.4 ! root       74:        sync_buffers(dev);
1.1.1.2   root       75:        sync_inodes();
1.1.1.4 ! root       76:        sync_buffers(dev);
1.1       root       77:        return 0;
                     78: }
                     79: 
1.1.1.3   root       80: void inline invalidate_buffers(int dev)
                     81: {
                     82:        int i;
                     83:        struct buffer_head * bh;
                     84: 
                     85:        bh = start_buffer;
                     86:        for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
                     87:                if (bh->b_dev != dev)
                     88:                        continue;
                     89:                wait_on_buffer(bh);
                     90:                if (bh->b_dev == dev)
                     91:                        bh->b_uptodate = bh->b_dirt = 0;
                     92:        }
                     93: }
                     94: 
1.1.1.2   root       95: /*
                     96:  * This routine checks whether a floppy has been changed, and
                     97:  * invalidates all buffer-cache-entries in that case. This
                     98:  * is a relatively slow routine, so we have to try to minimize using
                     99:  * it. Thus it is called only upon a 'mount' or 'open'. This
                    100:  * is the best way of combining speed and utility, I think.
                    101:  * People changing diskettes in the middle of an operation deserve
                    102:  * to loose :-)
                    103:  *
                    104:  * NOTE! Although currently this is only for floppies, the idea is
                    105:  * that any additional removable block-device will use this routine,
                    106:  * and that mount/open needn't know that floppies/whatever are
                    107:  * special.
                    108:  */
                    109: void check_disk_change(int dev)
                    110: {
                    111:        int i;
                    112: 
                    113:        if (MAJOR(dev) != 2)
                    114:                return;
1.1.1.3   root      115:        if (!floppy_change(dev & 0x03))
1.1.1.2   root      116:                return;
                    117:        for (i=0 ; i<NR_SUPER ; i++)
1.1.1.3   root      118:                if (super_block[i].s_dev == dev)
1.1.1.2   root      119:                        put_super(super_block[i].s_dev);
1.1.1.3   root      120:        invalidate_inodes(dev);
                    121:        invalidate_buffers(dev);
1.1.1.2   root      122: }
                    123: 
1.1       root      124: #define _hashfn(dev,block) (((unsigned)(dev^block))%NR_HASH)
                    125: #define hash(dev,block) hash_table[_hashfn(dev,block)]
                    126: 
1.1.1.4 ! root      127: static inline void remove_from_hash_queue(struct buffer_head * bh)
1.1       root      128: {
                    129:        if (bh->b_next)
                    130:                bh->b_next->b_prev = bh->b_prev;
                    131:        if (bh->b_prev)
                    132:                bh->b_prev->b_next = bh->b_next;
                    133:        if (hash(bh->b_dev,bh->b_blocknr) == bh)
                    134:                hash(bh->b_dev,bh->b_blocknr) = bh->b_next;
1.1.1.4 ! root      135:        bh->b_next = bh->b_prev = NULL;
        !           136: }
        !           137: 
        !           138: static inline void remove_from_free_list(struct buffer_head * bh)
        !           139: {
1.1       root      140:        if (!(bh->b_prev_free) || !(bh->b_next_free))
                    141:                panic("Free block list corrupted");
                    142:        bh->b_prev_free->b_next_free = bh->b_next_free;
                    143:        bh->b_next_free->b_prev_free = bh->b_prev_free;
                    144:        if (free_list == bh)
                    145:                free_list = bh->b_next_free;
1.1.1.4 ! root      146:        bh->b_next_free = bh->b_prev_free = NULL;
        !           147: }
        !           148: 
        !           149: static inline void remove_from_queues(struct buffer_head * bh)
        !           150: {
        !           151:        remove_from_hash_queue(bh);
        !           152:        remove_from_free_list(bh);
        !           153: }
        !           154: 
        !           155: static inline void put_first_free(struct buffer_head * bh)
        !           156: {
        !           157:        if (!bh || (bh == free_list))
        !           158:                return;
        !           159:        remove_from_free_list(bh);
        !           160: /* add to front of free list */
        !           161:        bh->b_next_free = free_list;
        !           162:        bh->b_prev_free = free_list->b_prev_free;
        !           163:        free_list->b_prev_free->b_next_free = bh;
        !           164:        free_list->b_prev_free = bh;
        !           165:        free_list = bh;
        !           166: }
        !           167: 
        !           168: static inline void put_last_free(struct buffer_head * bh)
        !           169: {
        !           170:        if (!bh)
        !           171:                return;
        !           172:        if (bh == free_list) {
        !           173:                free_list = bh->b_next_free;
        !           174:                return;
        !           175:        }
        !           176:        remove_from_free_list(bh);
        !           177: /* add to back of free list */
        !           178:        bh->b_next_free = free_list;
        !           179:        bh->b_prev_free = free_list->b_prev_free;
        !           180:        free_list->b_prev_free->b_next_free = bh;
        !           181:        free_list->b_prev_free = bh;
1.1       root      182: }
                    183: 
                    184: static inline void insert_into_queues(struct buffer_head * bh)
                    185: {
                    186: /* put at end of free list */
                    187:        bh->b_next_free = free_list;
                    188:        bh->b_prev_free = free_list->b_prev_free;
                    189:        free_list->b_prev_free->b_next_free = bh;
                    190:        free_list->b_prev_free = bh;
                    191: /* put the buffer in new hash-queue if it has a device */
                    192:        bh->b_prev = NULL;
                    193:        bh->b_next = NULL;
                    194:        if (!bh->b_dev)
                    195:                return;
                    196:        bh->b_next = hash(bh->b_dev,bh->b_blocknr);
                    197:        hash(bh->b_dev,bh->b_blocknr) = bh;
                    198:        bh->b_next->b_prev = bh;
                    199: }
                    200: 
                    201: static struct buffer_head * find_buffer(int dev, int block)
                    202: {              
                    203:        struct buffer_head * tmp;
                    204: 
                    205:        for (tmp = hash(dev,block) ; tmp != NULL ; tmp = tmp->b_next)
                    206:                if (tmp->b_dev==dev && tmp->b_blocknr==block)
                    207:                        return tmp;
                    208:        return NULL;
                    209: }
                    210: 
                    211: /*
                    212:  * Why like this, I hear you say... The reason is race-conditions.
                    213:  * As we don't lock buffers (unless we are readint them, that is),
                    214:  * something might happen to it while we sleep (ie a read-error
                    215:  * will force it bad). This shouldn't really happen currently, but
                    216:  * the code is ready.
                    217:  */
                    218: struct buffer_head * get_hash_table(int dev, int block)
                    219: {
                    220:        struct buffer_head * bh;
                    221: 
1.1.1.2   root      222:        for (;;) {
                    223:                if (!(bh=find_buffer(dev,block)))
                    224:                        return NULL;
                    225:                bh->b_count++;
                    226:                wait_on_buffer(bh);
1.1.1.4 ! root      227:                if (bh->b_dev == dev && bh->b_blocknr == block) {
        !           228:                        put_last_free(bh);
1.1.1.2   root      229:                        return bh;
1.1.1.4 ! root      230:                }
1.1.1.2   root      231:                bh->b_count--;
1.1       root      232:        }
                    233: }
                    234: 
                    235: /*
                    236:  * Ok, this is getblk, and it isn't very clear, again to hinder
                    237:  * race-conditions. Most of the code is seldom used, (ie repeating),
                    238:  * so it should be much more efficient than it looks.
1.1.1.2   root      239:  *
1.1.1.3   root      240:  * The algoritm is changed: hopefully better, and an elusive bug removed.
1.1.1.4 ! root      241:  *
        !           242:  * 14.02.92: changed it to sync dirty buffers a bit: better performance
        !           243:  * when the filesystem starts to get full of dirty blocks (I hope).
1.1       root      244:  */
1.1.1.2   root      245: #define BADNESS(bh) (((bh)->b_dirt<<1)+(bh)->b_lock)
1.1       root      246: struct buffer_head * getblk(int dev,int block)
                    247: {
1.1.1.4 ! root      248:        struct buffer_head * bh, * tmp;
        !           249:        int buffers;
1.1       root      250: 
                    251: repeat:
1.1.1.2   root      252:        if (bh = get_hash_table(dev,block))
                    253:                return bh;
1.1.1.4 ! root      254:        buffers = NR_BUFFERS;
1.1       root      255:        tmp = free_list;
                    256:        do {
1.1.1.4 ! root      257:                tmp = tmp->b_next_free;
1.1.1.2   root      258:                if (tmp->b_count)
                    259:                        continue;
                    260:                if (!bh || BADNESS(tmp)<BADNESS(bh)) {
                    261:                        bh = tmp;
                    262:                        if (!BADNESS(tmp))
1.1       root      263:                                break;
                    264:                }
1.1.1.4 ! root      265:                if (tmp->b_dirt)
        !           266:                        ll_rw_block(WRITEA,tmp);
1.1.1.3   root      267: /* and repeat until we find something good */
1.1.1.4 ! root      268:        } while (buffers--);
1.1.1.2   root      269:        if (!bh) {
1.1       root      270:                sleep_on(&buffer_wait);
                    271:                goto repeat;
                    272:        }
1.1.1.2   root      273:        wait_on_buffer(bh);
                    274:        if (bh->b_count)
1.1       root      275:                goto repeat;
1.1.1.2   root      276:        while (bh->b_dirt) {
                    277:                sync_dev(bh->b_dev);
                    278:                wait_on_buffer(bh);
                    279:                if (bh->b_count)
                    280:                        goto repeat;
1.1       root      281:        }
1.1.1.2   root      282: /* NOTE!! While we slept waiting for this block, somebody else might */
                    283: /* already have added "this" block to the cache. check it */
                    284:        if (find_buffer(dev,block))
                    285:                goto repeat;
                    286: /* OK, FINALLY we know that this buffer is the only one of it's kind, */
                    287: /* and that it's unused (b_count=0), unlocked (b_lock=0), and clean */
                    288:        bh->b_count=1;
                    289:        bh->b_dirt=0;
                    290:        bh->b_uptodate=0;
                    291:        remove_from_queues(bh);
                    292:        bh->b_dev=dev;
                    293:        bh->b_blocknr=block;
                    294:        insert_into_queues(bh);
                    295:        return bh;
1.1       root      296: }
                    297: 
                    298: void brelse(struct buffer_head * buf)
                    299: {
                    300:        if (!buf)
                    301:                return;
                    302:        wait_on_buffer(buf);
                    303:        if (!(buf->b_count--))
                    304:                panic("Trying to free free buffer");
                    305:        wake_up(&buffer_wait);
                    306: }
                    307: 
                    308: /*
                    309:  * bread() reads a specified block and returns the buffer that contains
                    310:  * it. It returns NULL if the block was unreadable.
                    311:  */
                    312: struct buffer_head * bread(int dev,int block)
                    313: {
                    314:        struct buffer_head * bh;
                    315: 
                    316:        if (!(bh=getblk(dev,block)))
                    317:                panic("bread: getblk returned NULL\n");
                    318:        if (bh->b_uptodate)
                    319:                return bh;
                    320:        ll_rw_block(READ,bh);
1.1.1.2   root      321:        wait_on_buffer(bh);
                    322:        if (bh->b_uptodate)
                    323:                return bh;
                    324:        brelse(bh);
                    325:        return NULL;
                    326: }
                    327: 
1.1.1.3   root      328: #define COPYBLK(from,to) \
                    329: __asm__("cld\n\t" \
                    330:        "rep\n\t" \
                    331:        "movsl\n\t" \
                    332:        ::"c" (BLOCK_SIZE/4),"S" (from),"D" (to) \
                    333:        :"cx","di","si")
                    334: 
                    335: /*
                    336:  * bread_page reads four buffers into memory at the desired address. It's
                    337:  * a function of its own, as there is some speed to be got by reading them
                    338:  * all at the same time, not waiting for one to be read, and then another
                    339:  * etc.
                    340:  */
                    341: void bread_page(unsigned long address,int dev,int b[4])
                    342: {
                    343:        struct buffer_head * bh[4];
                    344:        int i;
                    345: 
                    346:        for (i=0 ; i<4 ; i++)
                    347:                if (b[i]) {
                    348:                        if (bh[i] = getblk(dev,b[i]))
                    349:                                if (!bh[i]->b_uptodate)
                    350:                                        ll_rw_block(READ,bh[i]);
                    351:                } else
                    352:                        bh[i] = NULL;
                    353:        for (i=0 ; i<4 ; i++,address += BLOCK_SIZE)
                    354:                if (bh[i]) {
                    355:                        wait_on_buffer(bh[i]);
                    356:                        if (bh[i]->b_uptodate)
                    357:                                COPYBLK((unsigned long) bh[i]->b_data,address);
                    358:                        brelse(bh[i]);
                    359:                }
                    360: }
                    361: 
1.1.1.2   root      362: /*
                    363:  * Ok, breada can be used as bread, but additionally to mark other
                    364:  * blocks for reading as well. End the argument list with a negative
                    365:  * number.
                    366:  */
                    367: struct buffer_head * breada(int dev,int first, ...)
                    368: {
                    369:        va_list args;
                    370:        struct buffer_head * bh, *tmp;
                    371: 
                    372:        va_start(args,first);
                    373:        if (!(bh=getblk(dev,first)))
                    374:                panic("bread: getblk returned NULL\n");
                    375:        if (!bh->b_uptodate)
                    376:                ll_rw_block(READ,bh);
                    377:        while ((first=va_arg(args,int))>=0) {
                    378:                tmp=getblk(dev,first);
                    379:                if (tmp) {
                    380:                        if (!tmp->b_uptodate)
                    381:                                ll_rw_block(READA,bh);
                    382:                        tmp->b_count--;
                    383:                }
                    384:        }
                    385:        va_end(args);
                    386:        wait_on_buffer(bh);
1.1       root      387:        if (bh->b_uptodate)
                    388:                return bh;
                    389:        brelse(bh);
                    390:        return (NULL);
                    391: }
                    392: 
1.1.1.2   root      393: void buffer_init(long buffer_end)
1.1       root      394: {
                    395:        struct buffer_head * h = start_buffer;
1.1.1.2   root      396:        void * b;
1.1       root      397:        int i;
                    398: 
1.1.1.2   root      399:        if (buffer_end == 1<<20)
                    400:                b = (void *) (640*1024);
                    401:        else
                    402:                b = (void *) buffer_end;
1.1       root      403:        while ( (b -= BLOCK_SIZE) >= ((void *) (h+1)) ) {
                    404:                h->b_dev = 0;
                    405:                h->b_dirt = 0;
                    406:                h->b_count = 0;
                    407:                h->b_lock = 0;
                    408:                h->b_uptodate = 0;
                    409:                h->b_wait = NULL;
                    410:                h->b_next = NULL;
                    411:                h->b_prev = NULL;
                    412:                h->b_data = (char *) b;
                    413:                h->b_prev_free = h-1;
                    414:                h->b_next_free = h+1;
                    415:                h++;
                    416:                NR_BUFFERS++;
                    417:                if (b == (void *) 0x100000)
                    418:                        b = (void *) 0xA0000;
                    419:        }
                    420:        h--;
                    421:        free_list = start_buffer;
                    422:        free_list->b_prev_free = h;
                    423:        h->b_next_free = free_list;
                    424:        for (i=0;i<NR_HASH;i++)
1.1.1.4 ! root      425:                hash_table[i] = NULL;
1.1       root      426: }      

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.