root/fs/buffer.c

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

DEFINITIONS

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

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