📄 buffer.c
字号:
/* * linux/fs/buffer.c * * Copyright (C) 1991, 1992 Linus Torvalds * * OSKit support added by the University of Utah, 1997 *//* * 'buffer.c' implements the buffer-cache functions. Race-conditions have * been avoided by NEVER letting an interrupt change a buffer (except for the * data, of course), but instead letting the caller do it. *//* Start bdflush() with kernel_thread not syscall - Paul Gortmaker, 12/95 *//* Removed a lot of unnecessary code and simplified things now that * the buffer cache isn't our primary cache - Andrew Tridgell 12/96 *//* Speed up hash, lru, and free list operations. Use gfp() for allocating * hash table, use SLAB cache for buffer heads. -DaveM *//* Added 32k buffer block sizes - these are required older ARM systems. * - RMK */#include <linux/malloc.h>#include <linux/locks.h>#include <linux/errno.h>#include <linux/swap.h>#include <linux/swapctl.h>#include <linux/smp_lock.h>#include <linux/vmalloc.h>#include <linux/blkdev.h>#include <linux/sysrq.h>#include <linux/file.h>#include <linux/init.h>#include <linux/quotaops.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/bitops.h>#define NR_SIZES 7static char buffersize_index[65] ={-1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, -1, -1, 5, -1, -1, -1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, -1, -1, 6};#define BUFSIZE_INDEX(X) ((int) buffersize_index[(X)>>9])#define MAX_BUF_PER_PAGE (PAGE_SIZE / 512)#define NR_RESERVED (2*MAX_BUF_PER_PAGE)#define MAX_UNUSED_BUFFERS NR_RESERVED+20 /* don't ever have more than this number of unused buffer heads *//* * Hash table mask.. */static unsigned long bh_hash_mask = 0;static int grow_buffers(int size);static struct buffer_head ** hash_table;static struct buffer_head * lru_list[NR_LIST] = {NULL, };static struct buffer_head * free_list[NR_SIZES] = {NULL, };static kmem_cache_t *bh_cachep;static struct buffer_head * unused_list = NULL;static struct buffer_head * reuse_list = NULL;static struct wait_queue * buffer_wait = NULL;static int nr_buffers = 0;static int nr_buffers_type[NR_LIST] = {0,};static int nr_buffer_heads = 0;static int nr_unused_buffer_heads = 0;static int nr_hashed_buffers = 0;/* This is used by some architectures to estimate available memory. */int buffermem = 0;/* Here is the parameter block for the bdflush process. If you add or * remove any of the parameters, make sure to update kernel/sysctl.c. */#define N_PARAM 9/* The dummy values in this structure are left in there for compatibility * with old programs that play with the /proc entries. */union bdflush_param{ struct { int nfract; /* Percentage of buffer cache dirty to activate bdflush */ int ndirty; /* Maximum number of dirty blocks to write out per wake-cycle */ int nrefill; /* Number of clean buffers to try to obtain each time we call refill */ int nref_dirt; /* Dirty buffer threshold for activating bdflush when trying to refill buffers. */ int interval; /* jiffies delay between kupdate flushes */ int age_buffer; /* Time for normal buffer to age before we flush it */ int age_super; /* Time for superblock to age before we flush it */ int dummy2; /* unused */ int dummy3; /* unused */ } b_un; unsigned int data[N_PARAM];} bdf_prm = {{40, 500, 64, 256, 5*HZ, 30*HZ, 5*HZ, 1884, 2}};/* These are the min and max parameter values that we will allow to be assigned */int bdflush_min[N_PARAM] = { 0, 10, 5, 25, 0, 1*HZ, 1*HZ, 1, 1};int bdflush_max[N_PARAM] = {100,5000, 2000, 2000,60*HZ, 600*HZ, 600*HZ, 2047, 5};void wakeup_bdflush(int);#ifndef OSKIT/* * Rewrote the wait-routines to use the "new" wait-queue functionality, * and getting rid of the cli-sti pairs. The wait-queue routines still * need cli-sti, but now it's just a couple of 386 instructions or so. * * Note that the real wait_on_buffer() is an inline function that checks * if 'b_wait' is set before calling this, so that the queues aren't set * up unnecessarily. */void __wait_on_buffer(struct buffer_head * bh){ struct task_struct *tsk = current; struct wait_queue wait; bh->b_count++; wait.task = tsk; add_wait_queue(&bh->b_wait, &wait);repeat: tsk->state = TASK_UNINTERRUPTIBLE; run_task_queue(&tq_disk); if (buffer_locked(bh)) { schedule(); goto repeat; } tsk->state = TASK_RUNNING; remove_wait_queue(&bh->b_wait, &wait); bh->b_count--;}#endif /* OSKIT *//* Call sync_buffers with wait!=0 to ensure that the call does not * return until all buffer writes have completed. Sync() may return * before the writes have finished; fsync() may not. *//* Godamity-damn. Some buffers (bitmaps for filesystems) * spontaneously dirty themselves without ever brelse being called. * We will ultimately want to put these in a separate list, but for * now we search all of the lists for dirty buffers. */static int sync_buffers(kdev_t dev, int wait){ int i, retry, pass = 0, err = 0; struct buffer_head * bh, *next; /* One pass for no-wait, three for wait: * 0) write out all dirty, unlocked buffers; * 1) write out all dirty buffers, waiting if locked; * 2) wait for completion by waiting for all buffers to unlock. */ do { retry = 0;repeat: /* We search all lists as a failsafe mechanism, not because we expect * there to be dirty buffers on any of the other lists. */ bh = lru_list[BUF_DIRTY]; if (!bh) goto repeat2; for (i = nr_buffers_type[BUF_DIRTY]*2 ; i-- > 0 ; bh = next) { if (bh->b_list != BUF_DIRTY) goto repeat; next = bh->b_next_free; if (!lru_list[BUF_DIRTY]) break; if (dev && bh->b_dev != dev) continue; if (buffer_locked(bh)) { /* Buffer is locked; skip it unless wait is * requested AND pass > 0. */ if (!wait || !pass) { retry = 1; continue; } wait_on_buffer (bh); goto repeat; } /* If an unlocked buffer is not uptodate, there has * been an IO error. Skip it. */ if (wait && buffer_req(bh) && !buffer_locked(bh) && !buffer_dirty(bh) && !buffer_uptodate(bh)) { err = -EIO; continue; } /* Don't write clean buffers. Don't write ANY buffers * on the third pass. */ if (!buffer_dirty(bh) || pass >= 2) continue; /* Don't bother about locked buffers. * * XXX We checked if it was locked above and there is no * XXX way we could have slept in between. -DaveM */ if (buffer_locked(bh)) continue; bh->b_count++; next->b_count++; bh->b_flushtime = 0; ll_rw_block(WRITE, 1, &bh); bh->b_count--; next->b_count--; retry = 1; } repeat2: bh = lru_list[BUF_LOCKED]; if (!bh) break; for (i = nr_buffers_type[BUF_LOCKED]*2 ; i-- > 0 ; bh = next) { if (bh->b_list != BUF_LOCKED) goto repeat2; next = bh->b_next_free; if (!lru_list[BUF_LOCKED]) break; if (dev && bh->b_dev != dev) continue; if (buffer_locked(bh)) { /* Buffer is locked; skip it unless wait is * requested AND pass > 0. */ if (!wait || !pass) { retry = 1; continue; } wait_on_buffer (bh); goto repeat2; } } /* If we are waiting for the sync to succeed, and if any dirty * blocks were written, then repeat; on the second pass, only * wait for buffers being written (do not pass to write any * more buffers on the second pass). */ } while (wait && retry && ++pass<=2); return err;}void sync_dev(kdev_t dev){ sync_buffers(dev, 0); sync_supers(dev); sync_inodes(dev); sync_buffers(dev, 0); DQUOT_SYNC(dev); /* * FIXME(eric) we need to sync the physical devices here. * This is because some (scsi) controllers have huge amounts of * cache onboard (hundreds of Mb), and we need to instruct * them to commit all of the dirty memory to disk, and we should * not return until this has happened. * * This would need to get implemented by going through the assorted * layers so that each block major number can be synced, and this * would call down into the upper and mid-layer scsi. */}int fsync_dev(kdev_t dev){ sync_buffers(dev, 0); sync_supers(dev); sync_inodes(dev); DQUOT_SYNC(dev); return sync_buffers(dev, 1);}asmlinkage int sys_sync(void){ lock_kernel(); fsync_dev(0); unlock_kernel(); return 0;}/* * filp may be NULL if called via the msync of a vma. */ int file_fsync(struct file *filp, struct dentry *dentry){ struct inode * inode = dentry->d_inode; struct super_block * sb; kdev_t dev; /* sync the inode to buffers */ write_inode_now(inode); /* sync the superblock to buffers */ sb = inode->i_sb; wait_on_super(sb); if (sb->s_op && sb->s_op->write_super) sb->s_op->write_super(sb); /* .. finally sync the buffers to disk */ dev = inode->i_dev; return sync_buffers(dev, 1);}#ifndef OSKITasmlinkage int sys_fsync(unsigned int fd){ struct file * file; struct dentry * dentry; struct inode * inode; int err; lock_kernel(); err = -EBADF; file = fget(fd); if (!file) goto out; dentry = file->f_dentry; if (!dentry) goto out_putf; inode = dentry->d_inode; if (!inode) goto out_putf; err = -EINVAL; if (!file->f_op || !file->f_op->fsync) goto out_putf; /* We need to protect against concurrent writers.. */ down(&inode->i_sem); err = file->f_op->fsync(file, dentry); up(&inode->i_sem);out_putf: fput(file);out: unlock_kernel(); return err;}asmlinkage int sys_fdatasync(unsigned int fd){ struct file * file; struct dentry * dentry; struct inode * inode; int err; lock_kernel(); err = -EBADF; file = fget(fd); if (!file) goto out; dentry = file->f_dentry; if (!dentry) goto out_putf; inode = dentry->d_inode; if (!inode) goto out_putf; err = -EINVAL; if (!file->f_op || !file->f_op->fsync) goto out_putf; /* this needs further work, at the moment it is identical to fsync() */ down(&inode->i_sem); err = file->f_op->fsync(file, dentry); up(&inode->i_sem);out_putf: fput(file);out: unlock_kernel(); return err;}#endif /* OSKIT */void invalidate_buffers(kdev_t dev){ int i; int nlist; struct buffer_head * bh; for(nlist = 0; nlist < NR_LIST; nlist++) { bh = lru_list[nlist]; for (i = nr_buffers_type[nlist]*2 ; --i > 0 ; bh = bh->b_next_free) { if (bh->b_dev != dev) continue; wait_on_buffer(bh); if (bh->b_dev != dev) continue; if (bh->b_count) continue; bh->b_flushtime = 0; clear_bit(BH_Protected, &bh->b_state); clear_bit(BH_Uptodate, &bh->b_state); clear_bit(BH_Dirty, &bh->b_state); clear_bit(BH_Req, &bh->b_state); } }}#define _hashfn(dev,block) (((unsigned)(HASHDEV(dev)^block)) & bh_hash_mask)#define hash(dev,block) hash_table[_hashfn(dev,block)]static inline void remove_from_hash_queue(struct buffer_head * bh){ struct buffer_head **pprev = bh->b_pprev; if (pprev) { struct buffer_head * next = bh->b_next; if (next) { next->b_pprev = pprev; bh->b_next = NULL; } *pprev = next; bh->b_pprev = NULL; } nr_hashed_buffers--;}static inline void remove_from_lru_list(struct buffer_head * bh){ if (!(bh->b_prev_free) || !(bh->b_next_free)) panic("VFS: LRU block list corrupted"); if (bh->b_dev == B_FREE) panic("LRU list corrupted"); bh->b_prev_free->b_next_free = bh->b_next_free; bh->b_next_free->b_prev_free = bh->b_prev_free; if (lru_list[bh->b_list] == bh) lru_list[bh->b_list] = bh->b_next_free; if (lru_list[bh->b_list] == bh) lru_list[bh->b_list] = NULL; bh->b_next_free = bh->b_prev_free = NULL;}static inline void remove_from_free_list(struct buffer_head * bh){ int isize = BUFSIZE_INDEX(bh->b_size); if (!(bh->b_prev_free) || !(bh->b_next_free)) panic("VFS: Free block list corrupted"); if(bh->b_dev != B_FREE) panic("Free list corrupted"); if(!free_list[isize]) panic("Free list empty"); if(bh->b_next_free == bh) free_list[isize] = NULL; else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -