|
|
1.1 root 1: /*
1.1.1.2 root 2: * linux/fs/buffer.c
3: *
4: * (C) 1991 Linus Torvalds
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>
26: #include <asm/system.h>
1.1.1.2 root 27: #include <asm/io.h>
1.1 root 28:
29: extern int end;
1.1.1.6 root 30: static struct buffer_head * start_buffer = (struct buffer_head *) &end;
31: static struct buffer_head * hash_table[NR_HASH];
1.1 root 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:
1.1.1.4 root 44: static void sync_buffers(int dev)
1.1 root 45: {
46: int i;
47: struct buffer_head * bh;
48:
1.1.1.4 root 49: bh = free_list;
1.1.1.7 ! root 50: for (i = NR_BUFFERS*2 ; i-- > 0 ; bh = bh->b_next_free) {
! 51: if (bh->b_lock)
1.1.1.4 root 52: continue;
1.1.1.7 ! root 53: if (!bh->b_dirt)
1.1.1.4 root 54: continue;
1.1.1.7 ! root 55: ll_rw_block(WRITE,bh);
1.1 root 56: }
1.1.1.4 root 57: }
58:
59: int sys_sync(void)
60: {
61: sync_inodes(); /* write out inodes into buffers */
62: sync_buffers(0);
1.1 root 63: return 0;
64: }
65:
1.1.1.2 root 66: int sync_dev(int dev)
1.1 root 67: {
1.1.1.4 root 68: sync_buffers(dev);
1.1.1.2 root 69: sync_inodes();
1.1.1.4 root 70: sync_buffers(dev);
1.1 root 71: return 0;
72: }
73:
1.1.1.3 root 74: void inline invalidate_buffers(int dev)
75: {
76: int i;
77: struct buffer_head * bh;
78:
79: bh = start_buffer;
80: for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
81: if (bh->b_dev != dev)
82: continue;
83: wait_on_buffer(bh);
84: if (bh->b_dev == dev)
85: bh->b_uptodate = bh->b_dirt = 0;
86: }
87: }
88:
1.1.1.2 root 89: /*
90: * This routine checks whether a floppy has been changed, and
91: * invalidates all buffer-cache-entries in that case. This
92: * is a relatively slow routine, so we have to try to minimize using
93: * it. Thus it is called only upon a 'mount' or 'open'. This
94: * is the best way of combining speed and utility, I think.
95: * People changing diskettes in the middle of an operation deserve
96: * to loose :-)
97: *
98: * NOTE! Although currently this is only for floppies, the idea is
99: * that any additional removable block-device will use this routine,
100: * and that mount/open needn't know that floppies/whatever are
101: * special.
102: */
103: void check_disk_change(int dev)
104: {
105: int i;
1.1.1.5 root 106: struct buffer_head * bh;
1.1.1.2 root 107:
108: if (MAJOR(dev) != 2)
109: return;
1.1.1.5 root 110: if (!(bh = getblk(dev,0)))
111: return;
112: i = floppy_change(bh);
113: brelse(bh);
114: if (!i)
1.1.1.2 root 115: return;
116: for (i=0 ; i<NR_SUPER ; i++)
1.1.1.3 root 117: if (super_block[i].s_dev == dev)
1.1.1.2 root 118: put_super(super_block[i].s_dev);
1.1.1.3 root 119: invalidate_inodes(dev);
120: invalidate_buffers(dev);
1.1.1.2 root 121: }
122:
1.1 root 123: #define _hashfn(dev,block) (((unsigned)(dev^block))%NR_HASH)
124: #define hash(dev,block) hash_table[_hashfn(dev,block)]
125:
1.1.1.4 root 126: static inline void remove_from_hash_queue(struct buffer_head * bh)
1.1 root 127: {
128: if (bh->b_next)
129: bh->b_next->b_prev = bh->b_prev;
130: if (bh->b_prev)
131: bh->b_prev->b_next = bh->b_next;
132: if (hash(bh->b_dev,bh->b_blocknr) == bh)
133: hash(bh->b_dev,bh->b_blocknr) = bh->b_next;
1.1.1.4 root 134: bh->b_next = bh->b_prev = NULL;
135: }
136:
137: static inline void remove_from_free_list(struct buffer_head * bh)
138: {
1.1 root 139: if (!(bh->b_prev_free) || !(bh->b_next_free))
140: panic("Free block list corrupted");
141: bh->b_prev_free->b_next_free = bh->b_next_free;
142: bh->b_next_free->b_prev_free = bh->b_prev_free;
143: if (free_list == bh)
144: free_list = bh->b_next_free;
1.1.1.4 root 145: bh->b_next_free = bh->b_prev_free = NULL;
146: }
147:
148: static inline void remove_from_queues(struct buffer_head * bh)
149: {
150: remove_from_hash_queue(bh);
151: remove_from_free_list(bh);
152: }
153:
154: static inline void put_first_free(struct buffer_head * bh)
155: {
156: if (!bh || (bh == free_list))
157: return;
158: remove_from_free_list(bh);
159: /* add to front of free list */
160: bh->b_next_free = free_list;
161: bh->b_prev_free = free_list->b_prev_free;
162: free_list->b_prev_free->b_next_free = bh;
163: free_list->b_prev_free = bh;
164: free_list = bh;
165: }
166:
167: static inline void put_last_free(struct buffer_head * bh)
168: {
169: if (!bh)
170: return;
171: if (bh == free_list) {
172: free_list = bh->b_next_free;
173: return;
174: }
175: remove_from_free_list(bh);
176: /* add to back of free list */
177: bh->b_next_free = free_list;
178: bh->b_prev_free = free_list->b_prev_free;
179: free_list->b_prev_free->b_next_free = bh;
180: free_list->b_prev_free = bh;
1.1 root 181: }
182:
183: static inline void insert_into_queues(struct buffer_head * bh)
184: {
185: /* put at end of free list */
186: bh->b_next_free = free_list;
187: bh->b_prev_free = free_list->b_prev_free;
188: free_list->b_prev_free->b_next_free = bh;
189: free_list->b_prev_free = bh;
190: /* put the buffer in new hash-queue if it has a device */
191: bh->b_prev = NULL;
192: bh->b_next = NULL;
193: if (!bh->b_dev)
194: return;
195: bh->b_next = hash(bh->b_dev,bh->b_blocknr);
196: hash(bh->b_dev,bh->b_blocknr) = bh;
1.1.1.7 ! root 197: if (bh->b_next)
! 198: bh->b_next->b_prev = bh;
1.1 root 199: }
200:
201: static struct buffer_head * find_buffer(int dev, int block)
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)
219: {
220: struct buffer_head * bh;
221:
1.1.1.2 root 222: for (;;) {
223: if (!(bh=find_buffer(dev,block)))
224: return NULL;
225: bh->b_count++;
226: wait_on_buffer(bh);
1.1.1.4 root 227: if (bh->b_dev == dev && bh->b_blocknr == block) {
228: put_last_free(bh);
1.1.1.2 root 229: return bh;
1.1.1.4 root 230: }
1.1.1.2 root 231: bh->b_count--;
1.1 root 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.
1.1.1.2 root 239: *
1.1.1.3 root 240: * The algoritm is changed: hopefully better, and an elusive bug removed.
1.1.1.4 root 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).
1.1 root 244: */
1.1.1.2 root 245: #define BADNESS(bh) (((bh)->b_dirt<<1)+(bh)->b_lock)
1.1 root 246: struct buffer_head * getblk(int dev,int block)
247: {
1.1.1.4 root 248: struct buffer_head * bh, * tmp;
249: int buffers;
1.1 root 250:
251: repeat:
1.1.1.2 root 252: if (bh = get_hash_table(dev,block))
253: return bh;
1.1.1.4 root 254: buffers = NR_BUFFERS;
1.1.1.7 ! root 255: for (tmp = free_list ; buffers-- > 0 ; tmp = tmp->b_next_free) {
1.1.1.2 root 256: if (tmp->b_count)
257: continue;
258: if (!bh || BADNESS(tmp)<BADNESS(bh)) {
259: bh = tmp;
260: if (!BADNESS(tmp))
1.1 root 261: break;
262: }
1.1.1.7 ! root 263: #if 0
1.1.1.4 root 264: if (tmp->b_dirt)
265: ll_rw_block(WRITEA,tmp);
1.1.1.7 ! root 266: #endif
! 267: }
1.1.1.3 root 268: /* and repeat until we find something good */
1.1.1.2 root 269: if (!bh) {
1.1 root 270: sleep_on(&buffer_wait);
271: goto repeat;
272: }
1.1.1.2 root 273: wait_on_buffer(bh);
274: if (bh->b_count)
1.1 root 275: goto repeat;
1.1.1.2 root 276: while (bh->b_dirt) {
1.1.1.7 ! root 277: sync_buffers(bh->b_dev);
1.1.1.2 root 278: wait_on_buffer(bh);
279: if (bh->b_count)
280: goto repeat;
1.1 root 281: }
1.1.1.2 root 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;
1.1 root 296: }
297:
298: void brelse(struct buffer_head * buf)
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)
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);
1.1.1.2 root 321: wait_on_buffer(bh);
322: if (bh->b_uptodate)
323: return bh;
324: brelse(bh);
325: return NULL;
326: }
327:
1.1.1.3 root 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])
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:
1.1.1.2 root 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, ...)
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)
1.1.1.7 ! root 381: ll_rw_block(READA,tmp);
1.1.1.2 root 382: tmp->b_count--;
383: }
384: }
385: va_end(args);
386: wait_on_buffer(bh);
1.1 root 387: if (bh->b_uptodate)
388: return bh;
389: brelse(bh);
390: return (NULL);
391: }
392:
1.1.1.2 root 393: void buffer_init(long buffer_end)
1.1 root 394: {
395: struct buffer_head * h = start_buffer;
1.1.1.2 root 396: void * b;
1.1 root 397: int i;
398:
1.1.1.2 root 399: if (buffer_end == 1<<20)
400: b = (void *) (640*1024);
401: else
402: b = (void *) buffer_end;
1.1 root 403: while ( (b -= BLOCK_SIZE) >= ((void *) (h+1)) ) {
1.1.1.6 root 404: if (((unsigned long) (h+1)) > 0xA0000) {
405: printk("buffer-list doesn't fit in low meg - contact Linus\n");
406: break;
407: }
1.1 root 408: h->b_dev = 0;
409: h->b_dirt = 0;
410: h->b_count = 0;
411: h->b_lock = 0;
412: h->b_uptodate = 0;
413: h->b_wait = NULL;
414: h->b_next = NULL;
415: h->b_prev = NULL;
416: h->b_data = (char *) b;
1.1.1.7 ! root 417: h->b_reqnext = NULL;
1.1 root 418: h->b_prev_free = h-1;
419: h->b_next_free = h+1;
420: h++;
421: NR_BUFFERS++;
422: if (b == (void *) 0x100000)
423: b = (void *) 0xA0000;
424: }
425: h--;
426: free_list = start_buffer;
427: free_list->b_prev_free = h;
428: h->b_next_free = free_list;
429: for (i=0;i<NR_HASH;i++)
1.1.1.4 root 430: hash_table[i] = NULL;
1.1 root 431: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.