|
|
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;
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:
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;
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
1.1 root 55: wait_on_buffer(bh);
1.1.1.4 root 56: #if 0
57: if (dev && (bh->b_dev != dev))
58: continue;
59: #endif
1.1 root 60: if (bh->b_dirt)
61: ll_rw_block(WRITE,bh);
62: }
1.1.1.4 root 63: }
64:
65: int sys_sync(void)
66: {
67: sync_inodes(); /* write out inodes into buffers */
68: sync_buffers(0);
1.1 root 69: return 0;
70: }
71:
1.1.1.2 root 72: int sync_dev(int dev)
1.1 root 73: {
1.1.1.4 root 74: sync_buffers(dev);
1.1.1.2 root 75: sync_inodes();
1.1.1.4 root 76: sync_buffers(dev);
1.1 root 77: return 0;
78: }
79:
1.1.1.3 root 80: void inline invalidate_buffers(int dev)
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:
1.1.1.2 root 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)
110: {
111: int i;
1.1.1.5 ! root 112: struct buffer_head * bh;
1.1.1.2 root 113:
114: if (MAJOR(dev) != 2)
115: return;
1.1.1.5 ! root 116: if (!(bh = getblk(dev,0)))
! 117: return;
! 118: i = floppy_change(bh);
! 119: brelse(bh);
! 120: if (!i)
1.1.1.2 root 121: return;
122: for (i=0 ; i<NR_SUPER ; i++)
1.1.1.3 root 123: if (super_block[i].s_dev == dev)
1.1.1.2 root 124: put_super(super_block[i].s_dev);
1.1.1.3 root 125: invalidate_inodes(dev);
126: invalidate_buffers(dev);
1.1.1.2 root 127: }
128:
1.1 root 129: #define _hashfn(dev,block) (((unsigned)(dev^block))%NR_HASH)
130: #define hash(dev,block) hash_table[_hashfn(dev,block)]
131:
1.1.1.4 root 132: static inline void remove_from_hash_queue(struct buffer_head * bh)
1.1 root 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;
1.1.1.4 root 140: bh->b_next = bh->b_prev = NULL;
141: }
142:
143: static inline void remove_from_free_list(struct buffer_head * bh)
144: {
1.1 root 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;
1.1.1.4 root 151: bh->b_next_free = bh->b_prev_free = NULL;
152: }
153:
154: static inline void remove_from_queues(struct buffer_head * bh)
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)
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)
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;
1.1 root 187: }
188:
189: static inline void insert_into_queues(struct buffer_head * bh)
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)
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)
224: {
225: struct buffer_head * bh;
226:
1.1.1.2 root 227: for (;;) {
228: if (!(bh=find_buffer(dev,block)))
229: return NULL;
230: bh->b_count++;
231: wait_on_buffer(bh);
1.1.1.4 root 232: if (bh->b_dev == dev && bh->b_blocknr == block) {
233: put_last_free(bh);
1.1.1.2 root 234: return bh;
1.1.1.4 root 235: }
1.1.1.2 root 236: bh->b_count--;
1.1 root 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.
1.1.1.2 root 244: *
1.1.1.3 root 245: * The algoritm is changed: hopefully better, and an elusive bug removed.
1.1.1.4 root 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).
1.1 root 249: */
1.1.1.2 root 250: #define BADNESS(bh) (((bh)->b_dirt<<1)+(bh)->b_lock)
1.1 root 251: struct buffer_head * getblk(int dev,int block)
252: {
1.1.1.4 root 253: struct buffer_head * bh, * tmp;
254: int buffers;
1.1 root 255:
256: repeat:
1.1.1.2 root 257: if (bh = get_hash_table(dev,block))
258: return bh;
1.1.1.4 root 259: buffers = NR_BUFFERS;
1.1 root 260: tmp = free_list;
261: do {
1.1.1.4 root 262: tmp = tmp->b_next_free;
1.1.1.2 root 263: if (tmp->b_count)
264: continue;
265: if (!bh || BADNESS(tmp)<BADNESS(bh)) {
266: bh = tmp;
267: if (!BADNESS(tmp))
1.1 root 268: break;
269: }
1.1.1.4 root 270: if (tmp->b_dirt)
271: ll_rw_block(WRITEA,tmp);
1.1.1.3 root 272: /* and repeat until we find something good */
1.1.1.4 root 273: } while (buffers--);
1.1.1.2 root 274: if (!bh) {
1.1 root 275: sleep_on(&buffer_wait);
276: goto repeat;
277: }
1.1.1.2 root 278: wait_on_buffer(bh);
279: if (bh->b_count)
1.1 root 280: goto repeat;
1.1.1.2 root 281: while (bh->b_dirt) {
282: sync_dev(bh->b_dev);
283: wait_on_buffer(bh);
284: if (bh->b_count)
285: goto repeat;
1.1 root 286: }
1.1.1.2 root 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;
1.1 root 301: }
302:
303: void brelse(struct buffer_head * buf)
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)
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);
1.1.1.2 root 326: wait_on_buffer(bh);
327: if (bh->b_uptodate)
328: return bh;
329: brelse(bh);
330: return NULL;
331: }
332:
1.1.1.3 root 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])
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:
1.1.1.2 root 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, ...)
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);
1.1 root 392: if (bh->b_uptodate)
393: return bh;
394: brelse(bh);
395: return (NULL);
396: }
397:
1.1.1.2 root 398: void buffer_init(long buffer_end)
1.1 root 399: {
400: struct buffer_head * h = start_buffer;
1.1.1.2 root 401: void * b;
1.1 root 402: int i;
403:
1.1.1.2 root 404: if (buffer_end == 1<<20)
405: b = (void *) (640*1024);
406: else
407: b = (void *) buffer_end;
1.1 root 408: while ( (b -= BLOCK_SIZE) >= ((void *) (h+1)) ) {
409: h->b_dev = 0;
410: h->b_dirt = 0;
411: h->b_count = 0;
412: h->b_lock = 0;
413: h->b_uptodate = 0;
414: h->b_wait = NULL;
415: h->b_next = NULL;
416: h->b_prev = NULL;
417: h->b_data = (char *) b;
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.