root/fs/buffer.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. wait_on_buffer
  2. sys_sync
  3. sync_dev
  4. invalidate_buffers
  5. check_disk_change
  6. remove_from_queues
  7. insert_into_queues
  8. find_buffer
  9. get_hash_table
  10. getblk
  11. brelse
  12. bread
  13. bread_page
  14. breada
  15. buffer_init

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

/* [previous][next][first][last][top][bottom][index][help] */