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

1.1       root        1: /*
1.1.1.2   root        2:  *  linux/fs/buffer.c
                      3:  *
1.1.1.10! root        4:  *  Copyright (C) 1991, 1992  Linus Torvalds
1.1.1.2   root        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>
1.1.1.10! root       26: #include <linux/string.h>
        !            27: 
1.1       root       28: #include <asm/system.h>
1.1.1.2   root       29: #include <asm/io.h>
1.1       root       30: 
1.1.1.6   root       31: static struct buffer_head * hash_table[NR_HASH];
1.1.1.10! root       32: static struct buffer_head * free_list = NULL;
        !            33: static struct buffer_head * unused_list = NULL;
        !            34: static struct wait_queue * buffer_wait = NULL;
        !            35: 
        !            36: int nr_buffers = 0;
        !            37: int nr_buffer_heads = 0;
1.1       root       38: 
                     39: static inline void wait_on_buffer(struct buffer_head * bh)
                     40: {
                     41:        cli();
                     42:        while (bh->b_lock)
                     43:                sleep_on(&bh->b_wait);
                     44:        sti();
                     45: }
                     46: 
1.1.1.4   root       47: static void sync_buffers(int dev)
1.1       root       48: {
                     49:        int i;
                     50:        struct buffer_head * bh;
                     51: 
1.1.1.4   root       52:        bh = free_list;
1.1.1.10! root       53:        for (i = nr_buffers*2 ; i-- > 0 ; bh = bh->b_next_free) {
1.1.1.7   root       54:                if (bh->b_lock)
1.1.1.4   root       55:                        continue;
1.1.1.7   root       56:                if (!bh->b_dirt)
1.1.1.4   root       57:                        continue;
1.1.1.7   root       58:                ll_rw_block(WRITE,bh);
1.1       root       59:        }
1.1.1.4   root       60: }
                     61: 
                     62: int sys_sync(void)
                     63: {
1.1.1.9   root       64:        int i;
                     65: 
                     66:        for (i=0 ; i<NR_SUPER ; i++)
                     67:                if (super_block[i].s_dev
                     68:                    && super_block[i].s_op 
                     69:                    && super_block[i].s_op->write_super 
                     70:                    && super_block[i].s_dirt)
                     71:                        super_block[i].s_op->write_super(&super_block[i]);
1.1.1.4   root       72:        sync_inodes();          /* write out inodes into buffers */
                     73:        sync_buffers(0);
1.1       root       74:        return 0;
                     75: }
                     76: 
1.1.1.2   root       77: int sync_dev(int dev)
1.1       root       78: {
1.1.1.9   root       79:        struct super_block * sb;
                     80: 
                     81:        if (sb = get_super (dev))
                     82:                if (sb->s_op && sb->s_op->write_super && sb->s_dirt)
                     83:                        sb->s_op->write_super (sb);
1.1.1.4   root       84:        sync_buffers(dev);
1.1.1.2   root       85:        sync_inodes();
1.1.1.4   root       86:        sync_buffers(dev);
1.1       root       87:        return 0;
                     88: }
                     89: 
1.1.1.3   root       90: void inline invalidate_buffers(int dev)
                     91: {
                     92:        int i;
                     93:        struct buffer_head * bh;
                     94: 
1.1.1.10! root       95:        bh = free_list;
        !            96:        for (i = nr_buffers*2 ; --i > 0 ; bh = bh->b_next_free) {
1.1.1.3   root       97:                if (bh->b_dev != dev)
                     98:                        continue;
                     99:                wait_on_buffer(bh);
                    100:                if (bh->b_dev == dev)
                    101:                        bh->b_uptodate = bh->b_dirt = 0;
                    102:        }
                    103: }
                    104: 
1.1.1.2   root      105: /*
                    106:  * This routine checks whether a floppy has been changed, and
                    107:  * invalidates all buffer-cache-entries in that case. This
                    108:  * is a relatively slow routine, so we have to try to minimize using
                    109:  * it. Thus it is called only upon a 'mount' or 'open'. This
                    110:  * is the best way of combining speed and utility, I think.
                    111:  * People changing diskettes in the middle of an operation deserve
                    112:  * to loose :-)
                    113:  *
                    114:  * NOTE! Although currently this is only for floppies, the idea is
                    115:  * that any additional removable block-device will use this routine,
                    116:  * and that mount/open needn't know that floppies/whatever are
                    117:  * special.
                    118:  */
                    119: void check_disk_change(int dev)
                    120: {
                    121:        int i;
1.1.1.5   root      122:        struct buffer_head * bh;
1.1.1.2   root      123: 
                    124:        if (MAJOR(dev) != 2)
                    125:                return;
1.1.1.10! root      126:        if (!(bh = getblk(dev,0,1024)))
1.1.1.5   root      127:                return;
                    128:        i = floppy_change(bh);
                    129:        brelse(bh);
                    130:        if (!i)
1.1.1.2   root      131:                return;
                    132:        for (i=0 ; i<NR_SUPER ; i++)
1.1.1.3   root      133:                if (super_block[i].s_dev == dev)
1.1.1.2   root      134:                        put_super(super_block[i].s_dev);
1.1.1.3   root      135:        invalidate_inodes(dev);
                    136:        invalidate_buffers(dev);
1.1.1.2   root      137: }
                    138: 
1.1       root      139: #define _hashfn(dev,block) (((unsigned)(dev^block))%NR_HASH)
                    140: #define hash(dev,block) hash_table[_hashfn(dev,block)]
                    141: 
1.1.1.4   root      142: static inline void remove_from_hash_queue(struct buffer_head * bh)
1.1       root      143: {
                    144:        if (bh->b_next)
                    145:                bh->b_next->b_prev = bh->b_prev;
                    146:        if (bh->b_prev)
                    147:                bh->b_prev->b_next = bh->b_next;
                    148:        if (hash(bh->b_dev,bh->b_blocknr) == bh)
                    149:                hash(bh->b_dev,bh->b_blocknr) = bh->b_next;
1.1.1.4   root      150:        bh->b_next = bh->b_prev = NULL;
                    151: }
                    152: 
                    153: static inline void remove_from_free_list(struct buffer_head * bh)
                    154: {
1.1       root      155:        if (!(bh->b_prev_free) || !(bh->b_next_free))
                    156:                panic("Free block list corrupted");
                    157:        bh->b_prev_free->b_next_free = bh->b_next_free;
                    158:        bh->b_next_free->b_prev_free = bh->b_prev_free;
                    159:        if (free_list == bh)
                    160:                free_list = bh->b_next_free;
1.1.1.4   root      161:        bh->b_next_free = bh->b_prev_free = NULL;
                    162: }
                    163: 
                    164: static inline void remove_from_queues(struct buffer_head * bh)
                    165: {
                    166:        remove_from_hash_queue(bh);
                    167:        remove_from_free_list(bh);
                    168: }
                    169: 
                    170: static inline void put_first_free(struct buffer_head * bh)
                    171: {
                    172:        if (!bh || (bh == free_list))
                    173:                return;
                    174:        remove_from_free_list(bh);
                    175: /* add to front of free list */
                    176:        bh->b_next_free = free_list;
                    177:        bh->b_prev_free = free_list->b_prev_free;
                    178:        free_list->b_prev_free->b_next_free = bh;
                    179:        free_list->b_prev_free = bh;
                    180:        free_list = bh;
                    181: }
                    182: 
                    183: static inline void put_last_free(struct buffer_head * bh)
                    184: {
                    185:        if (!bh)
                    186:                return;
                    187:        if (bh == free_list) {
                    188:                free_list = bh->b_next_free;
                    189:                return;
                    190:        }
                    191:        remove_from_free_list(bh);
                    192: /* add to back of free list */
                    193:        bh->b_next_free = free_list;
                    194:        bh->b_prev_free = free_list->b_prev_free;
                    195:        free_list->b_prev_free->b_next_free = bh;
                    196:        free_list->b_prev_free = bh;
1.1       root      197: }
                    198: 
                    199: static inline void insert_into_queues(struct buffer_head * bh)
                    200: {
                    201: /* put at end of free list */
                    202:        bh->b_next_free = free_list;
                    203:        bh->b_prev_free = free_list->b_prev_free;
                    204:        free_list->b_prev_free->b_next_free = bh;
                    205:        free_list->b_prev_free = bh;
                    206: /* put the buffer in new hash-queue if it has a device */
                    207:        bh->b_prev = NULL;
                    208:        bh->b_next = NULL;
                    209:        if (!bh->b_dev)
                    210:                return;
                    211:        bh->b_next = hash(bh->b_dev,bh->b_blocknr);
                    212:        hash(bh->b_dev,bh->b_blocknr) = bh;
1.1.1.7   root      213:        if (bh->b_next)
                    214:                bh->b_next->b_prev = bh;
1.1       root      215: }
                    216: 
1.1.1.10! root      217: static struct buffer_head * find_buffer(int dev, int block, int size)
1.1       root      218: {              
                    219:        struct buffer_head * tmp;
                    220: 
                    221:        for (tmp = hash(dev,block) ; tmp != NULL ; tmp = tmp->b_next)
                    222:                if (tmp->b_dev==dev && tmp->b_blocknr==block)
1.1.1.10! root      223:                        if (tmp->b_size == size)
        !           224:                                return tmp;
        !           225:                        else {
        !           226:                                printk("wrong block-size on device %04x\n",dev);
        !           227:                                return NULL;
        !           228:                        }
1.1       root      229:        return NULL;
                    230: }
                    231: 
                    232: /*
                    233:  * Why like this, I hear you say... The reason is race-conditions.
                    234:  * As we don't lock buffers (unless we are readint them, that is),
                    235:  * something might happen to it while we sleep (ie a read-error
                    236:  * will force it bad). This shouldn't really happen currently, but
                    237:  * the code is ready.
                    238:  */
1.1.1.10! root      239: struct buffer_head * get_hash_table(int dev, int block, int size)
1.1       root      240: {
                    241:        struct buffer_head * bh;
                    242: 
1.1.1.2   root      243:        for (;;) {
1.1.1.10! root      244:                if (!(bh=find_buffer(dev,block,size)))
1.1.1.2   root      245:                        return NULL;
                    246:                bh->b_count++;
                    247:                wait_on_buffer(bh);
1.1.1.10! root      248:                if (bh->b_dev == dev && bh->b_blocknr == block && bh->b_size == size) {
1.1.1.4   root      249:                        put_last_free(bh);
1.1.1.2   root      250:                        return bh;
1.1.1.4   root      251:                }
1.1.1.2   root      252:                bh->b_count--;
1.1       root      253:        }
                    254: }
                    255: 
                    256: /*
                    257:  * Ok, this is getblk, and it isn't very clear, again to hinder
                    258:  * race-conditions. Most of the code is seldom used, (ie repeating),
                    259:  * so it should be much more efficient than it looks.
1.1.1.2   root      260:  *
1.1.1.3   root      261:  * The algoritm is changed: hopefully better, and an elusive bug removed.
1.1.1.4   root      262:  *
                    263:  * 14.02.92: changed it to sync dirty buffers a bit: better performance
                    264:  * when the filesystem starts to get full of dirty blocks (I hope).
1.1       root      265:  */
1.1.1.2   root      266: #define BADNESS(bh) (((bh)->b_dirt<<1)+(bh)->b_lock)
1.1.1.10! root      267: struct buffer_head * getblk(int dev, int block, int size)
1.1       root      268: {
1.1.1.4   root      269:        struct buffer_head * bh, * tmp;
                    270:        int buffers;
1.1       root      271: 
                    272: repeat:
1.1.1.10! root      273:        if (bh = get_hash_table(dev, block, size))
1.1.1.2   root      274:                return bh;
1.1.1.10! root      275: 
        !           276:        if (nr_free_pages > 30)
        !           277:                grow_buffers(size);
        !           278: 
        !           279:        buffers = nr_buffers;
        !           280:        bh = NULL;
        !           281: 
        !           282:        for (tmp = free_list; buffers-- > 0 ; tmp = tmp->b_next_free) {
        !           283:                if (tmp->b_count || tmp->b_size != size)
1.1.1.2   root      284:                        continue;
                    285:                if (!bh || BADNESS(tmp)<BADNESS(bh)) {
                    286:                        bh = tmp;
                    287:                        if (!BADNESS(tmp))
1.1       root      288:                                break;
                    289:                }
1.1.1.7   root      290: #if 0
1.1.1.4   root      291:                if (tmp->b_dirt)
                    292:                        ll_rw_block(WRITEA,tmp);
1.1.1.7   root      293: #endif
                    294:        }
1.1.1.10! root      295: 
        !           296:        if (!bh && nr_free_pages > 5) {
        !           297:                grow_buffers(size);
        !           298:                goto repeat;
        !           299:        }
        !           300:        
1.1.1.3   root      301: /* and repeat until we find something good */
1.1.1.2   root      302:        if (!bh) {
1.1       root      303:                sleep_on(&buffer_wait);
                    304:                goto repeat;
                    305:        }
1.1.1.2   root      306:        wait_on_buffer(bh);
1.1.1.10! root      307:        if (bh->b_count || bh->b_size != size)
1.1       root      308:                goto repeat;
1.1.1.8   root      309:        if (bh->b_dirt) {
1.1.1.7   root      310:                sync_buffers(bh->b_dev);
1.1.1.8   root      311:                goto repeat;
1.1       root      312:        }
1.1.1.2   root      313: /* NOTE!! While we slept waiting for this block, somebody else might */
                    314: /* already have added "this" block to the cache. check it */
1.1.1.10! root      315:        if (find_buffer(dev,block,size))
1.1.1.2   root      316:                goto repeat;
                    317: /* OK, FINALLY we know that this buffer is the only one of it's kind, */
                    318: /* and that it's unused (b_count=0), unlocked (b_lock=0), and clean */
                    319:        bh->b_count=1;
                    320:        bh->b_dirt=0;
                    321:        bh->b_uptodate=0;
                    322:        remove_from_queues(bh);
                    323:        bh->b_dev=dev;
                    324:        bh->b_blocknr=block;
                    325:        insert_into_queues(bh);
                    326:        return bh;
1.1       root      327: }
                    328: 
                    329: void brelse(struct buffer_head * buf)
                    330: {
                    331:        if (!buf)
                    332:                return;
                    333:        wait_on_buffer(buf);
                    334:        if (!(buf->b_count--))
                    335:                panic("Trying to free free buffer");
                    336:        wake_up(&buffer_wait);
                    337: }
                    338: 
                    339: /*
                    340:  * bread() reads a specified block and returns the buffer that contains
                    341:  * it. It returns NULL if the block was unreadable.
                    342:  */
1.1.1.10! root      343: struct buffer_head * bread(int dev, int block, int size)
1.1       root      344: {
                    345:        struct buffer_head * bh;
                    346: 
1.1.1.10! root      347:        if (!(bh = getblk(dev, block, size))) {
        !           348:                printk("bread: getblk returned NULL\n");
        !           349:                return NULL;
        !           350:        }
1.1       root      351:        if (bh->b_uptodate)
                    352:                return bh;
                    353:        ll_rw_block(READ,bh);
1.1.1.2   root      354:        wait_on_buffer(bh);
                    355:        if (bh->b_uptodate)
                    356:                return bh;
                    357:        brelse(bh);
                    358:        return NULL;
                    359: }
                    360: 
1.1.1.3   root      361: #define COPYBLK(from,to) \
                    362: __asm__("cld\n\t" \
                    363:        "rep\n\t" \
                    364:        "movsl\n\t" \
                    365:        ::"c" (BLOCK_SIZE/4),"S" (from),"D" (to) \
                    366:        :"cx","di","si")
                    367: 
                    368: /*
                    369:  * bread_page reads four buffers into memory at the desired address. It's
                    370:  * a function of its own, as there is some speed to be got by reading them
                    371:  * all at the same time, not waiting for one to be read, and then another
                    372:  * etc.
                    373:  */
                    374: void bread_page(unsigned long address,int dev,int b[4])
                    375: {
                    376:        struct buffer_head * bh[4];
                    377:        int i;
                    378: 
                    379:        for (i=0 ; i<4 ; i++)
                    380:                if (b[i]) {
1.1.1.10! root      381:                        if (bh[i] = getblk(dev, b[i], 1024))
1.1.1.3   root      382:                                if (!bh[i]->b_uptodate)
                    383:                                        ll_rw_block(READ,bh[i]);
                    384:                } else
                    385:                        bh[i] = NULL;
                    386:        for (i=0 ; i<4 ; i++,address += BLOCK_SIZE)
                    387:                if (bh[i]) {
                    388:                        wait_on_buffer(bh[i]);
                    389:                        if (bh[i]->b_uptodate)
                    390:                                COPYBLK((unsigned long) bh[i]->b_data,address);
                    391:                        brelse(bh[i]);
                    392:                }
                    393: }
                    394: 
1.1.1.2   root      395: /*
                    396:  * Ok, breada can be used as bread, but additionally to mark other
                    397:  * blocks for reading as well. End the argument list with a negative
                    398:  * number.
                    399:  */
                    400: struct buffer_head * breada(int dev,int first, ...)
                    401: {
                    402:        va_list args;
                    403:        struct buffer_head * bh, *tmp;
                    404: 
                    405:        va_start(args,first);
1.1.1.10! root      406:        if (!(bh = getblk(dev, first, 1024))) {
        !           407:                printk("breada: getblk returned NULL\n");
        !           408:                return NULL;
        !           409:        }
1.1.1.2   root      410:        if (!bh->b_uptodate)
                    411:                ll_rw_block(READ,bh);
                    412:        while ((first=va_arg(args,int))>=0) {
1.1.1.10! root      413:                tmp = getblk(dev, first, 1024);
1.1.1.2   root      414:                if (tmp) {
                    415:                        if (!tmp->b_uptodate)
1.1.1.7   root      416:                                ll_rw_block(READA,tmp);
1.1.1.2   root      417:                        tmp->b_count--;
                    418:                }
                    419:        }
                    420:        va_end(args);
                    421:        wait_on_buffer(bh);
1.1       root      422:        if (bh->b_uptodate)
                    423:                return bh;
                    424:        brelse(bh);
                    425:        return (NULL);
                    426: }
                    427: 
1.1.1.10! root      428: static void put_unused_buffer_head(struct buffer_head * bh)
        !           429: {
        !           430:        memset((void *) bh,0,sizeof(*bh));
        !           431:        bh->b_next_free = unused_list;
        !           432:        unused_list = bh;
        !           433: }
        !           434: 
        !           435: static void get_more_buffer_heads(void)
        !           436: {
        !           437:        unsigned long page;
        !           438:        struct buffer_head * bh;
        !           439: 
        !           440:        if (unused_list)
        !           441:                return;
        !           442:        page = get_free_page(GFP_KERNEL);
        !           443:        if (!page)
        !           444:                return;
        !           445:        bh = (struct buffer_head *) page;
        !           446:        while ((unsigned long) (bh+1) <= page+4096) {
        !           447:                put_unused_buffer_head(bh);
        !           448:                bh++;
        !           449:                nr_buffer_heads++;
        !           450:        }
        !           451: }
        !           452: 
        !           453: static struct buffer_head * get_unused_buffer_head(void)
1.1       root      454: {
1.1.1.10! root      455:        struct buffer_head * bh;
        !           456: 
        !           457:        get_more_buffer_heads();
        !           458:        if (!unused_list)
        !           459:                return NULL;
        !           460:        bh = unused_list;
        !           461:        unused_list = bh->b_next_free;
        !           462:        bh->b_next_free = NULL;
        !           463:        bh->b_data = NULL;
        !           464:        bh->b_size = 0;
        !           465:        return bh;
        !           466: }
        !           467: 
        !           468: /*
        !           469:  * Try to increase the number of buffers available: the size argument
        !           470:  * is used to determine what kind of buffers we want. Currently only
        !           471:  * 1024-byte buffers are supported by the rest of the system, but I
        !           472:  * think this will change eventually.
        !           473:  */
        !           474: void grow_buffers(int size)
        !           475: {
        !           476:        unsigned long page;
1.1       root      477:        int i;
1.1.1.10! root      478:        struct buffer_head *bh, *tmp;
1.1       root      479: 
1.1.1.10! root      480:        if ((size & 511) || (size > 4096)) {
        !           481:                printk("grow_buffers: size = %d\n",size);
        !           482:                return;
        !           483:        }
        !           484:        page = get_free_page(GFP_BUFFER);
        !           485:        if (!page)
        !           486:                return;
        !           487:        tmp = NULL;
        !           488:        i = 0;
        !           489:        for (i = 0 ; i+size <= 4096 ; i += size) {
        !           490:                bh = get_unused_buffer_head();
        !           491:                if (!bh)
        !           492:                        goto no_grow;
        !           493:                bh->b_this_page = tmp;
        !           494:                tmp = bh;
        !           495:                bh->b_data = (char * ) (page+i);
        !           496:                bh->b_size = size;
        !           497:                i += size;
        !           498:        }
        !           499:        tmp = bh;
        !           500:        while (1) {
        !           501:                tmp->b_next_free = free_list;
        !           502:                tmp->b_prev_free = free_list->b_prev_free;
        !           503:                free_list->b_prev_free->b_next_free = tmp;
        !           504:                free_list->b_prev_free = tmp;
        !           505:                free_list = tmp;
        !           506:                ++nr_buffers;
        !           507:                if (tmp->b_this_page)
        !           508:                        tmp = tmp->b_this_page;
        !           509:                else
1.1.1.6   root      510:                        break;
1.1.1.10! root      511:        }
        !           512:        tmp->b_this_page = bh;
        !           513:        return;
        !           514: /*
        !           515:  * In case anything failed, we just free everything we got.
        !           516:  */
        !           517: no_grow:
        !           518:        bh = tmp;
        !           519:        while (bh) {
        !           520:                tmp = bh;
        !           521:                bh = bh->b_this_page;
        !           522:                put_unused_buffer_head(tmp);
        !           523:        }       
        !           524:        free_page(page);
        !           525: }
        !           526: 
        !           527: /*
        !           528:  * try_to_free() checks if all the buffers on this particular page
        !           529:  * are unused, and free's the page if so.
        !           530:  */
        !           531: static int try_to_free(struct buffer_head * bh)
        !           532: {
        !           533:        unsigned long page;
        !           534:        struct buffer_head * tmp, * p;
        !           535: 
        !           536:        tmp = bh;
        !           537:        do {
        !           538:                if (!tmp)
        !           539:                        return 0;
        !           540:                if (tmp->b_count || tmp->b_dirt || tmp->b_lock)
        !           541:                        return 0;
        !           542:                tmp = tmp->b_this_page;
        !           543:        } while (tmp != bh);
        !           544:        page = (unsigned long) bh->b_data;
        !           545:        page &= 0xfffff000;
        !           546:        tmp = bh;
        !           547:        do {
        !           548:                p = tmp;
        !           549:                tmp = tmp->b_this_page;
        !           550:                nr_buffers--;
        !           551:                remove_from_queues(p);
        !           552:                put_unused_buffer_head(p);
        !           553:        } while (tmp != bh);
        !           554:        free_page(page);
        !           555:        return 1;
        !           556: }
        !           557: 
        !           558: /*
        !           559:  * Try to free up some pages by shrinking the buffer-cache
        !           560:  */
        !           561: int shrink_buffers(void)
        !           562: {
        !           563:        struct buffer_head *bh;
        !           564:        int i;
        !           565: 
        !           566:        bh = free_list;
        !           567:        for (i = nr_buffers*2 ; i-- > 0 ; bh = bh->b_next_free) {
        !           568:                wait_on_buffer(bh);
        !           569:                if (bh->b_count || !bh->b_this_page)
        !           570:                        continue;
        !           571:                if (bh->b_dirt) {
        !           572:                        ll_rw_block(WRITEA,bh);
        !           573:                        continue;
1.1.1.6   root      574:                }
1.1.1.10! root      575:                if (try_to_free(bh))
        !           576:                        return 1;
        !           577:        }
        !           578:        return 0;
        !           579: }
        !           580: 
        !           581: /*
        !           582:  * This initializes the low 1M that isn't used by the kernel to buffer
        !           583:  * cache. It should really be used for paging memory, but it takes a lot
        !           584:  * of special-casing, which I don't want to do.
        !           585:  *
        !           586:  * The biggest problem with this approach is that all low-mem buffers
        !           587:  * have a fixed size of 1024 chars: not good if/when the other sizes
        !           588:  * are implemented.
        !           589:  */
        !           590: void buffer_init(void)
        !           591: {
        !           592:        struct buffer_head * bh;
        !           593:        extern int end;
        !           594:        unsigned long mem;
        !           595:        int i;
        !           596: 
        !           597:        for (i = 0 ; i < NR_HASH ; i++)
1.1.1.4   root      598:                hash_table[i] = NULL;
1.1.1.10! root      599:        mem = (unsigned long) & end;
        !           600:        mem += BLOCK_SIZE-1;
        !           601:        mem &= ~(BLOCK_SIZE-1);
        !           602:        free_list = get_unused_buffer_head();
        !           603:        if (!free_list)
        !           604:                panic("unable to get a single buffer-head");
        !           605:        free_list->b_prev_free = free_list;
        !           606:        free_list->b_next_free = free_list;
        !           607:        free_list->b_data = (char *) mem;
        !           608:        free_list->b_size = BLOCK_SIZE;
        !           609:        mem += BLOCK_SIZE;
        !           610:        while (mem + 1024 < 0xA0000) {
        !           611:                bh = get_unused_buffer_head();
        !           612:                if (!bh)
        !           613:                        break;
        !           614:                bh->b_data = (char *) mem;
        !           615:                bh->b_size = BLOCK_SIZE;
        !           616:                mem += BLOCK_SIZE;
        !           617:                bh->b_next_free = free_list;
        !           618:                bh->b_prev_free = free_list->b_prev_free;
        !           619:                free_list->b_prev_free->b_next_free = bh;
        !           620:                free_list->b_prev_free = bh;
        !           621:                free_list = bh;
        !           622:                ++nr_buffers;
        !           623:        }
        !           624:        return;
        !           625: }

unix.superglobalmegacorp.com

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