[source navigation] [diff markup] [identifier search] [freetext search] [file search]

Oldlinux Cross Reference
Linux/fs/buffer.c

Version: [1.0] [0.99.11] [0.99] [0.98] [0.97] [0.96a] [0.95] [0.12] [0.11] [0.01]
Architecture: [i386]

  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)
 37 {
 38         cli();
 39         while (bh->b_lock)
 40                 sleep_on(&bh->b_wait);
 41         sti();
 42 }
 43 
 44 int sys_sync(void)
 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)
 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)
 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)
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)
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)
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)
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)
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)
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)
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)
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])
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, ...)
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)
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 }       
382 

[source navigation] [diff markup] [identifier search] [freetext search] [file search]

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.