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 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 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 
 113         if (MAJOR(dev) != 2)
 114                 return;
 115         if (!floppy_change(dev & 0x03))
 116                 return;
 117         for (i=0 ; i<NR_SUPER ; i++)
 118                 if (super_block[i].s_dev == dev)
 119                         put_super(super_block[i].s_dev);
 120         invalidate_inodes(dev);
 121         invalidate_buffers(dev);
 122 }
 123 
 124 #define _hashfn(dev,block) (((unsigned)(dev^block))%NR_HASH)
 125 #define hash(dev,block) hash_table[_hashfn(dev,block)]
 126 
 127 static inline void remove_from_hash_queue(struct buffer_head * bh)
     /* [previous][next][first][last][top][bottom][index][help] */
 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;
 135         bh->b_next = bh->b_prev = NULL;
 136 }
 137 
 138 static inline void remove_from_free_list(struct buffer_head * bh)
     /* [previous][next][first][last][top][bottom][index][help] */
 139 {
 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;
 146         bh->b_next_free = bh->b_prev_free = NULL;
 147 }
 148 
 149 static inline void remove_from_queues(struct buffer_head * bh)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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;
 182 }
 183 
 184 static inline void insert_into_queues(struct buffer_head * bh)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 219 {
 220         struct buffer_head * bh;
 221 
 222         for (;;) {
 223                 if (!(bh=find_buffer(dev,block)))
 224                         return NULL;
 225                 bh->b_count++;
 226                 wait_on_buffer(bh);
 227                 if (bh->b_dev == dev && bh->b_blocknr == block) {
 228                         put_last_free(bh);
 229                         return bh;
 230                 }
 231                 bh->b_count--;
 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.
 239  *
 240  * The algoritm is changed: hopefully better, and an elusive bug removed.
 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).
 244  */
 245 #define BADNESS(bh) (((bh)->b_dirt<<1)+(bh)->b_lock)
 246 struct buffer_head * getblk(int dev,int block)
     /* [previous][next][first][last][top][bottom][index][help] */
 247 {
 248         struct buffer_head * bh, * tmp;
 249         int buffers;
 250 
 251 repeat:
 252         if (bh = get_hash_table(dev,block))
 253                 return bh;
 254         buffers = NR_BUFFERS;
 255         tmp = free_list;
 256         do {
 257                 tmp = tmp->b_next_free;
 258                 if (tmp->b_count)
 259                         continue;
 260                 if (!bh || BADNESS(tmp)<BADNESS(bh)) {
 261                         bh = tmp;
 262                         if (!BADNESS(tmp))
 263                                 break;
 264                 }
 265                 if (tmp->b_dirt)
 266                         ll_rw_block(WRITEA,tmp);
 267 /* and repeat until we find something good */
 268         } while (buffers--);
 269         if (!bh) {
 270                 sleep_on(&buffer_wait);
 271                 goto repeat;
 272         }
 273         wait_on_buffer(bh);
 274         if (bh->b_count)
 275                 goto repeat;
 276         while (bh->b_dirt) {
 277                 sync_dev(bh->b_dev);
 278                 wait_on_buffer(bh);
 279                 if (bh->b_count)
 280                         goto repeat;
 281         }
 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;
 296 }
 297 
 298 void brelse(struct buffer_head * buf)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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);
 321         wait_on_buffer(bh);
 322         if (bh->b_uptodate)
 323                 return bh;
 324         brelse(bh);
 325         return NULL;
 326 }
 327 
 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])
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 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, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
 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);
 387         if (bh->b_uptodate)
 388                 return bh;
 389         brelse(bh);
 390         return (NULL);
 391 }
 392 
 393 void buffer_init(long buffer_end)
     /* [previous][next][first][last][top][bottom][index][help] */
 394 {
 395         struct buffer_head * h = start_buffer;
 396         void * b;
 397         int i;
 398 
 399         if (buffer_end == 1<<20)
 400                 b = (void *) (640*1024);
 401         else
 402                 b = (void *) buffer_end;
 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++)
 425                 hash_table[i] = NULL;
 426 }       

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