diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-07-05 23:09:37 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-07-05 23:09:37 +0000 |
commit | aba344fdfed81b2c03d6114c54cfd73a486aa10b (patch) | |
tree | d032d8430bf1234c3ecc6f6330d6de6e887e5963 /fs | |
parent | 40c138bfc6d37dbff5339f84575db1e3cec6e34e (diff) |
Merge with Linux 2.3.9.
Diffstat (limited to 'fs')
116 files changed, 2334 insertions, 2483 deletions
diff --git a/fs/Makefile b/fs/Makefile index b94509a03..069b1a2a4 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -263,14 +263,6 @@ else endif endif -ifeq ($(CONFIG_BINFMT_JAVA),y) -BINFMTS += binfmt_java.o -else - ifeq ($(CONFIG_BINFMT_JAVA),m) - M_OBJS += binfmt_java.o - endif -endif - ifeq ($(CONFIG_BINFMT_EM86),y) BINFMTS += binfmt_em86.o else diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index 738bb40b8..0d0d11a06 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -51,12 +51,14 @@ struct inode_operations adfs_dir_inode_operations = { NULL, /* rename */ NULL, /* read link */ NULL, /* follow link */ + NULL, /* get_block */ NULL, /* read page */ NULL, /* write page */ - NULL, /* bmap */ + NULL, /* flush page */ NULL, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; unsigned int adfs_val (unsigned char *p, int len) diff --git a/fs/adfs/file.c b/fs/adfs/file.c index a886c68ce..c415597e3 100644 --- a/fs/adfs/file.c +++ b/fs/adfs/file.c @@ -61,10 +61,12 @@ struct inode_operations adfs_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - generic_readpage, /* readpage */ + adfs_bmap, /* get_block */ + block_read_full_page, /* readpage */ NULL, /* writepage */ - adfs_bmap, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; diff --git a/fs/affs/dir.c b/fs/affs/dir.c index ee08ff451..b88eac770 100644 --- a/fs/affs/dir.c +++ b/fs/affs/dir.c @@ -57,9 +57,10 @@ struct inode_operations affs_dir_inode_operations = { affs_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permissions */ NULL, /* smap */ diff --git a/fs/affs/file.c b/fs/affs/file.c index bb1ce69c8..358c1c56c 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -74,9 +74,10 @@ struct inode_operations affs_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - generic_readpage, /* readpage */ + affs_bmap, /* get_block */ + block_read_full_page, /* readpage */ NULL, /* writepage */ - affs_bmap, /* bmap */ + NULL, /* flushpage */ affs_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ @@ -114,9 +115,10 @@ struct inode_operations affs_file_inode_operations_ofs = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ affs_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c index c3da66d40..18c03730f 100644 --- a/fs/affs/symlink.c +++ b/fs/affs/symlink.c @@ -35,12 +35,14 @@ struct inode_operations affs_symlink_inode_operations = { NULL, /* rename */ affs_readlink, /* readlink */ affs_follow_link, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; static int diff --git a/fs/autofs/dir.c b/fs/autofs/dir.c index f6ccf8419..a8262e701 100644 --- a/fs/autofs/dir.c +++ b/fs/autofs/dir.c @@ -73,9 +73,10 @@ struct inode_operations autofs_dir_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ diff --git a/fs/autofs/root.c b/fs/autofs/root.c index 011e3286f..c6034c754 100644 --- a/fs/autofs/root.c +++ b/fs/autofs/root.c @@ -54,9 +54,10 @@ struct inode_operations autofs_root_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ diff --git a/fs/autofs/symlink.c b/fs/autofs/symlink.c index 0e46db365..6ca720527 100644 --- a/fs/autofs/symlink.c +++ b/fs/autofs/symlink.c @@ -49,9 +49,10 @@ struct inode_operations autofs_symlink_inode_operations = { NULL, /* rename */ autofs_readlink, /* readlink */ autofs_follow_link, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ diff --git a/fs/bad_inode.c b/fs/bad_inode.c index 89711607b..2691953f2 100644 --- a/fs/bad_inode.c +++ b/fs/bad_inode.c @@ -60,7 +60,7 @@ struct inode_operations bad_inode_ops = EIO_ERROR, /* rename */ EIO_ERROR, /* readlink */ bad_follow_link, /* follow_link */ - EIO_ERROR, /* bmap */ + EIO_ERROR, /* get_block */ EIO_ERROR, /* readpage */ EIO_ERROR, /* writepage */ EIO_ERROR, /* flushpage */ diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index 2b886a147..4723c6802 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -323,7 +323,7 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs if (N_MAGIC(ex) == ZMAGIC && ex.a_text && bprm->dentry->d_inode->i_op && - bprm->dentry->d_inode->i_op->bmap && + bprm->dentry->d_inode->i_op->get_block && (fd_offset < bprm->dentry->d_inode->i_sb->s_blocksize)) { printk(KERN_NOTICE "N_TXTOFF < BLOCK_SIZE. Please convert binary.\n"); return -ENOEXEC; @@ -396,7 +396,7 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs fd = open_dentry(bprm->dentry, O_RDONLY); if (fd < 0) return fd; - file = fcheck(fd); + file = fget(fd); if ((fd_offset & ~PAGE_MASK) != 0) { printk(KERN_WARNING @@ -406,6 +406,7 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs } if (!file->f_op || !file->f_op->mmap || ((fd_offset & ~PAGE_MASK) != 0)) { + fput(file); sys_close(fd); do_brk(0, ex.a_text+ex.a_data); read_exec(bprm->dentry, fd_offset, @@ -422,6 +423,7 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs fd_offset); if (error != N_TXTADDR(ex)) { + fput(file); sys_close(fd); send_sig(SIGKILL, current, 0); return error; @@ -431,6 +433,7 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE, fd_offset + ex.a_text); + fput(file); sys_close(fd); if (error != N_DATADDR(ex)) { send_sig(SIGKILL, current, 0); diff --git a/fs/binfmt_java.c b/fs/binfmt_java.c deleted file mode 100644 index 2bd036d98..000000000 --- a/fs/binfmt_java.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * linux/fs/binfmt_java.c - * - * Copyright (C) 1996 Brian A. Lantz - * derived from binfmt_script.c - * - * Simplified and modified to support binary java interpreters - * by Tom May <ftom@netcom.com>. - */ - -#include <linux/module.h> -#include <linux/string.h> -#include <linux/stat.h> -#include <linux/malloc.h> -#include <linux/binfmts.h> -#include <linux/init.h> - -#define _PATH_JAVA "/usr/bin/java" -#define _PATH_APPLET "/usr/bin/appletviewer" - -/* These paths can be modified with sysctl(). */ - -char binfmt_java_interpreter[65] = _PATH_JAVA; -char binfmt_java_appletviewer[65] = _PATH_APPLET; - -static int do_load_java(struct linux_binprm *bprm,struct pt_regs *regs) -{ - char *i_name; - int len; - int retval; - struct dentry * dentry; - unsigned char *ucp = (unsigned char *) bprm->buf; - - if ((ucp[0] != 0xca) || (ucp[1] != 0xfe) || (ucp[2] != 0xba) || (ucp[3] != 0xbe)) - return -ENOEXEC; - - /* - * Fail if we're called recursively, e.g., the Java interpreter - * is a java binary. - */ - - if (bprm->java) - return -ENOEXEC; - - bprm->java = 1; - - dput(bprm->dentry); - bprm->dentry = NULL; - - /* - * Set args: [0] the name of the java interpreter - * [1] name of java class to execute, which is the - * filename without the path and without trailing - * ".class". Note that the interpreter will use - * its own way to found the class file (typically using - * environment variable CLASSPATH), and may in fact - * execute a different file from the one we want. - * - * This is done in reverse order, because of how the - * user environment and arguments are stored. - */ - remove_arg_zero(bprm); - len = strlen (bprm->filename); - if (len >= 6 && !strcmp (bprm->filename + len - 6, ".class")) - bprm->filename[len - 6] = 0; - if ((i_name = strrchr (bprm->filename, '/')) != NULL) - i_name++; - else - i_name = bprm->filename; - - retval = copy_strings_kernel(1, &i_name, bprm); - if (retval < 0) - return retval; - bprm->argc++; - - i_name = binfmt_java_interpreter; - retval = copy_strings_kernel(1, &i_name, bprm); - if (retval < 0) - return retval; - bprm->argc++; - - /* - * OK, now restart the process with the interpreter's dentry. - */ - bprm->filename = binfmt_java_interpreter; - dentry = open_namei(binfmt_java_interpreter, 0, 0); - retval = PTR_ERR(dentry); - if (IS_ERR(dentry)) - return retval; - - bprm->dentry = dentry; - retval = prepare_binprm(bprm); - if (retval < 0) - return retval; - - return search_binary_handler(bprm,regs); -} - -static int do_load_applet(struct linux_binprm *bprm,struct pt_regs *regs) -{ - char *i_name; - struct dentry * dentry; - int retval; - - if (strncmp (bprm->buf, "<!--applet", 10)) - return -ENOEXEC; - - dput(bprm->dentry); - bprm->dentry = NULL; - - /* - * Set args: [0] the name of the appletviewer - * [1] filename of html file - * - * This is done in reverse order, because of how the - * user environment and arguments are stored. - */ - remove_arg_zero(bprm); - i_name = bprm->filename; - retval = copy_strings_kernel(1, &i_name, bprm); - if (retval < 0) return retval; - bprm->argc++; - - i_name = binfmt_java_appletviewer; - retval = copy_strings_kernel(1, &i_name, bprm); - if (retval < 0) return retval; - bprm->argc++; - - /* - * OK, now restart the process with the interpreter's dentry. - */ - bprm->filename = binfmt_java_appletviewer; - dentry = open_namei(binfmt_java_appletviewer, 0, 0); - retval = PTR_ERR(dentry); - if (IS_ERR(dentry)) - return retval; - - bprm->dentry = dentry; - retval = prepare_binprm(bprm); - if (retval < 0) - return retval; - - return search_binary_handler(bprm,regs); -} - -static int load_java(struct linux_binprm *bprm,struct pt_regs *regs) -{ - int retval; - MOD_INC_USE_COUNT; - retval = do_load_java(bprm,regs); - MOD_DEC_USE_COUNT; - return retval; -} - -static struct linux_binfmt java_format = { -#ifndef MODULE - NULL, 0, load_java, NULL, NULL -#else - NULL, &__this_module, load_java, NULL, NULL -#endif -}; - -static int load_applet(struct linux_binprm *bprm,struct pt_regs *regs) -{ - int retval; - MOD_INC_USE_COUNT; - retval = do_load_applet(bprm,regs); - MOD_DEC_USE_COUNT; - return retval; -} - -static struct linux_binfmt applet_format = { -#ifndef MODULE - NULL, 0, load_applet, NULL, NULL -#else - NULL, &__this_module, load_applet, NULL, NULL -#endif -}; - -int __init init_java_binfmt(void) -{ - register_binfmt(&java_format); - return register_binfmt(&applet_format); -} - -#ifdef MODULE -int init_module(void) -{ - return init_java_binfmt(); -} - -void cleanup_module( void) { - unregister_binfmt(&java_format); - unregister_binfmt(&applet_format); -} -#endif diff --git a/fs/buffer.c b/fs/buffer.c index 9ffb8556a..108b385ea 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -24,6 +24,8 @@ * - RMK */ +/* Thread it... -DaveM */ + #include <linux/sched.h> #include <linux/fs.h> #include <linux/malloc.h> @@ -57,31 +59,39 @@ static char buffersize_index[65] = #define MAX_UNUSED_BUFFERS NR_RESERVED+20 /* don't ever have more than this number of unused buffer heads */ -/* - * Hash table mask.. +/* Anti-deadlock ordering: + * lru_list_lock > hash_table_lock > free_list_lock > unused_list_lock */ -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, }; +/* + * Hash table gook.. + */ +static unsigned int bh_hash_mask = 0; +static unsigned int bh_hash_shift = 0; +static struct buffer_head **hash_table; +static rwlock_t hash_table_lock = RW_LOCK_UNLOCKED; -static kmem_cache_t *bh_cachep; +static struct buffer_head *lru_list[NR_LIST]; +static spinlock_t lru_list_lock = SPIN_LOCK_UNLOCKED; +static int nr_buffers_type[NR_LIST] = {0,}; static struct buffer_head * unused_list = NULL; -static struct buffer_head * reuse_list = NULL; +static int nr_unused_buffer_heads = 0; +static spinlock_t unused_list_lock = SPIN_LOCK_UNLOCKED; static DECLARE_WAIT_QUEUE_HEAD(buffer_wait); -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; +struct bh_free_head { + struct buffer_head *list; + spinlock_t lock; +}; +static struct bh_free_head free_list[NR_SIZES]; + +static kmem_cache_t *bh_cachep; + +static int grow_buffers(int size); /* This is used by some architectures to estimate available memory. */ -int buffermem = 0; +atomic_t buffermem = ATOMIC_INIT(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. @@ -131,7 +141,7 @@ void __wait_on_buffer(struct buffer_head * bh) struct task_struct *tsk = current; DECLARE_WAITQUEUE(wait, tsk); - bh->b_count++; + atomic_inc(&bh->b_count); add_wait_queue(&bh->b_wait, &wait); repeat: tsk->state = TASK_UNINTERRUPTIBLE; @@ -142,7 +152,7 @@ repeat: } tsk->state = TASK_RUNNING; remove_wait_queue(&bh->b_wait, &wait); - bh->b_count--; + atomic_dec(&bh->b_count); } /* Call sync_buffers with wait!=0 to ensure that the call does not @@ -167,17 +177,19 @@ static int sync_buffers(kdev_t dev, int wait) */ 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. */ +repeat: + spin_lock(&lru_list_lock); 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) @@ -190,7 +202,10 @@ repeat: retry = 1; continue; } + atomic_inc(&bh->b_count); + spin_unlock(&lru_list_lock); wait_on_buffer (bh); + atomic_dec(&bh->b_count); goto repeat; } @@ -209,30 +224,24 @@ repeat: 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++; + atomic_inc(&bh->b_count); bh->b_flushtime = 0; + spin_unlock(&lru_list_lock); ll_rw_block(WRITE, 1, &bh); - bh->b_count--; - next->b_count--; + atomic_dec(&bh->b_count); retry = 1; + goto repeat; } repeat2: bh = lru_list[BUF_LOCKED]; - if (!bh) + if (!bh) { + spin_unlock(&lru_list_lock); 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) @@ -245,10 +254,15 @@ repeat: retry = 1; continue; } + atomic_inc(&bh->b_count); + spin_unlock(&lru_list_lock); wait_on_buffer (bh); + spin_lock(&lru_list_lock); + atomic_dec(&bh->b_count); goto repeat2; } } + spin_unlock(&lru_list_lock); /* If we are waiting for the sync to succeed, and if any dirty * blocks were written, then repeat; on the second pass, only @@ -282,17 +296,19 @@ void sync_dev(kdev_t dev) int fsync_dev(kdev_t dev) { sync_buffers(dev, 0); + + lock_kernel(); sync_supers(dev); sync_inodes(dev); DQUOT_SYNC(dev); + unlock_kernel(); + return sync_buffers(dev, 1); } asmlinkage int sys_sync(void) { - lock_kernel(); fsync_dev(0); - unlock_kernel(); return 0; } @@ -396,19 +412,28 @@ out: void invalidate_buffers(kdev_t dev) { - int i; int nlist; - struct buffer_head * bh; + spin_lock(&lru_list_lock); for(nlist = 0; nlist < NR_LIST; nlist++) { + struct buffer_head * bh; + int i; + retry: bh = lru_list[nlist]; + if (!bh) + continue; 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) + if (buffer_locked(bh)) { + atomic_inc(&bh->b_count); + spin_unlock(&lru_list_lock); + wait_on_buffer(bh); + spin_lock(&lru_list_lock); + atomic_dec(&bh->b_count); + goto retry; + } + if (atomic_read(&bh->b_count)) continue; bh->b_flushtime = 0; clear_bit(BH_Protected, &bh->b_state); @@ -417,157 +442,119 @@ void invalidate_buffers(kdev_t dev) clear_bit(BH_Req, &bh->b_state); } } + spin_unlock(&lru_list_lock); } -#define _hashfn(dev,block) (((unsigned)(HASHDEV(dev)^block)) & bh_hash_mask) -#define hash(dev,block) hash_table[_hashfn(dev,block)] +/* After several hours of tedious analysis, the following hash + * function won. Do not mess with it... -DaveM + */ +#define _hashfn(dev,block) \ + ((((dev)<<(bh_hash_shift - 6)) ^ ((dev)<<(bh_hash_shift - 9))) ^ \ + (((block)<<(bh_hash_shift - 6)) ^ ((block) >> 13) ^ ((block) << (bh_hash_shift - 12)))) +#define hash(dev,block) hash_table[(_hashfn(dev,block) & bh_hash_mask)] -static void insert_into_hash_list(struct buffer_head * bh) +static __inline__ void __hash_link(struct buffer_head *bh, struct buffer_head **head) { - bh->b_next = NULL; - bh->b_pprev = NULL; - if (bh->b_dev) { - struct buffer_head **bhp = &hash(bh->b_dev, bh->b_blocknr); - struct buffer_head *next = *bhp; - - if (next) { - bh->b_next = next; - next->b_pprev = &bh->b_next; - } - *bhp = bh; - bh->b_pprev = bhp; - nr_hashed_buffers++; - } + if ((bh->b_next = *head) != NULL) + bh->b_next->b_pprev = &bh->b_next; + *head = bh; + bh->b_pprev = head; } -static void remove_from_hash_queue(struct buffer_head * bh) +static __inline__ void __hash_unlink(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--; - } + if (bh->b_next) + bh->b_next->b_pprev = bh->b_pprev; + *(bh->b_pprev) = bh->b_next; + bh->b_pprev = NULL; } -static void insert_into_lru_list(struct buffer_head * bh) +static void __insert_into_lru_list(struct buffer_head * bh, int blist) { - struct buffer_head **bhp = &lru_list[bh->b_list]; - - if (bh->b_dev == B_FREE) - BUG(); + struct buffer_head **bhp = &lru_list[blist]; if(!*bhp) { *bhp = bh; bh->b_prev_free = bh; } - - if (bh->b_next_free) - panic("VFS: buffer LRU pointers corrupted"); - bh->b_next_free = *bhp; bh->b_prev_free = (*bhp)->b_prev_free; (*bhp)->b_prev_free->b_next_free = bh; (*bhp)->b_prev_free = bh; - - nr_buffers++; - nr_buffers_type[bh->b_list]++; + nr_buffers_type[blist]++; } -static void remove_from_lru_list(struct buffer_head * bh) +static void __remove_from_lru_list(struct buffer_head * bh, int blist) { - if (!(bh->b_prev_free) || !(bh->b_next_free)) - return; - - if (bh->b_dev == B_FREE) { - printk("LRU list corrupted"); - *(int*)0 = 0; + if (bh->b_prev_free || bh->b_next_free) { + 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[blist] == bh) + lru_list[blist] = bh->b_next_free; + if (lru_list[blist] == bh) + lru_list[blist] = NULL; + bh->b_next_free = bh->b_prev_free = NULL; + nr_buffers_type[blist]--; } - 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; - - nr_buffers--; - nr_buffers_type[bh->b_list]--; } -static void remove_from_free_list(struct buffer_head * bh) +static void __remove_from_free_list(struct buffer_head * bh, int index) { - 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; + free_list[index].list = NULL; else { bh->b_prev_free->b_next_free = bh->b_next_free; bh->b_next_free->b_prev_free = bh->b_prev_free; - if (free_list[isize] == bh) - free_list[isize] = bh->b_next_free; + if (free_list[index].list == bh) + free_list[index].list = bh->b_next_free; } bh->b_next_free = bh->b_prev_free = NULL; } -static void remove_from_queues(struct buffer_head * bh) +/* The following two functions must operate atomically + * because they control the visibility of a buffer head + * to the rest of the kernel. + */ +static __inline__ void __remove_from_queues(struct buffer_head *bh) { - if (bh->b_dev == B_FREE) - BUG(); - remove_from_hash_queue(bh); - remove_from_lru_list(bh); + write_lock(&hash_table_lock); + if (bh->b_pprev) + __hash_unlink(bh); + __remove_from_lru_list(bh, bh->b_list); + write_unlock(&hash_table_lock); } -static void put_last_free(struct buffer_head * bh) +static void insert_into_queues(struct buffer_head *bh) { - if (bh) { - struct buffer_head **bhp = &free_list[BUFSIZE_INDEX(bh->b_size)]; - - if (bh->b_count) - BUG(); - - bh->b_dev = B_FREE; /* So it is obvious we are on the free list. */ - - /* Add to back of free list. */ - if(!*bhp) { - *bhp = bh; - bh->b_prev_free = bh; - } - - bh->b_next_free = *bhp; - bh->b_prev_free = (*bhp)->b_prev_free; - (*bhp)->b_prev_free->b_next_free = bh; - (*bhp)->b_prev_free = bh; - } + struct buffer_head **head = &hash(bh->b_dev, bh->b_blocknr); + + spin_lock(&lru_list_lock); + write_lock(&hash_table_lock); + __hash_link(bh, head); + __insert_into_lru_list(bh, bh->b_list); + write_unlock(&hash_table_lock); + spin_unlock(&lru_list_lock); } -struct buffer_head * find_buffer(kdev_t dev, int block, int size) -{ - struct buffer_head * next; +/* This function must only run if there are no other + * references _anywhere_ to this buffer head. + */ +static void put_last_free(struct buffer_head * bh) +{ + struct bh_free_head *head = &free_list[BUFSIZE_INDEX(bh->b_size)]; + struct buffer_head **bhp = &head->list; - next = hash(dev,block); - for (;;) { - struct buffer_head *tmp = next; - if (!next) - break; - next = tmp->b_next; - if (tmp->b_blocknr != block || tmp->b_size != size || tmp->b_dev != dev) - continue; - next = tmp; - break; + spin_lock(&head->lock); + bh->b_dev = B_FREE; + if(!*bhp) { + *bhp = bh; + bh->b_prev_free = bh; } - return next; + bh->b_next_free = *bhp; + bh->b_prev_free = (*bhp)->b_prev_free; + (*bhp)->b_prev_free->b_next_free = bh; + (*bhp)->b_prev_free = bh; + spin_unlock(&head->lock); } /* @@ -579,10 +566,19 @@ struct buffer_head * find_buffer(kdev_t dev, int block, int size) */ struct buffer_head * get_hash_table(kdev_t dev, int block, int size) { - struct buffer_head * bh; - bh = find_buffer(dev,block,size); + struct buffer_head **head = &hash(dev, block); + struct buffer_head *bh; + + read_lock(&hash_table_lock); + for(bh = *head; bh; bh = bh->b_next) + if (bh->b_blocknr == block && + bh->b_size == size && + bh->b_dev == dev) + break; if (bh) - bh->b_count++; + atomic_inc(&bh->b_count); + read_unlock(&hash_table_lock); + return bh; } @@ -631,6 +627,8 @@ void set_blocksize(kdev_t dev, int size) * around on the free list, and we can get in a loop if we are not careful. */ for(nlist = 0; nlist < NR_LIST; nlist++) { + repeat: + spin_lock(&lru_list_lock); bh = lru_list[nlist]; for (i = nr_buffers_type[nlist]*2 ; --i > 0 ; bh = bhnext) { if(!bh) @@ -641,21 +639,25 @@ void set_blocksize(kdev_t dev, int size) continue; if (bh->b_size == size) continue; - bhnext->b_count++; - bh->b_count++; - wait_on_buffer(bh); - bhnext->b_count--; + if (buffer_locked(bh)) { + atomic_inc(&bh->b_count); + spin_unlock(&lru_list_lock); + wait_on_buffer(bh); + atomic_dec(&bh->b_count); + goto repeat; + } if (bh->b_dev == dev && bh->b_size != size) { clear_bit(BH_Dirty, &bh->b_state); clear_bit(BH_Uptodate, &bh->b_state); clear_bit(BH_Req, &bh->b_state); bh->b_flushtime = 0; } - if (--bh->b_count) - continue; - remove_from_queues(bh); - put_last_free(bh); + if (atomic_read(&bh->b_count) == 0) { + __remove_from_queues(bh); + put_last_free(bh); + } } + spin_unlock(&lru_list_lock); } } @@ -671,13 +673,10 @@ static void refill_freelist(int size) } } -void init_buffer(struct buffer_head *bh, kdev_t dev, int block, - bh_end_io_t *handler, void *dev_id) +void init_buffer(struct buffer_head *bh, bh_end_io_t *handler, void *dev_id) { bh->b_list = BUF_CLEAN; bh->b_flushtime = 0; - bh->b_dev = dev; - bh->b_blocknr = block; bh->b_end_io = handler; bh->b_dev_id = dev_id; } @@ -688,6 +687,92 @@ static void end_buffer_io_sync(struct buffer_head *bh, int uptodate) unlock_buffer(bh); } +static void end_buffer_io_bad(struct buffer_head *bh, int uptodate) +{ + mark_buffer_uptodate(bh, uptodate); + unlock_buffer(bh); + BUG(); +} + +static void end_buffer_io_async(struct buffer_head * bh, int uptodate) +{ + static spinlock_t page_uptodate_lock = SPIN_LOCK_UNLOCKED; + unsigned long flags; + struct buffer_head *tmp; + struct page *page; + int free; + + mark_buffer_uptodate(bh, uptodate); + + /* This is a temporary buffer used for page I/O. */ + page = mem_map + MAP_NR(bh->b_data); + + if (!uptodate) + SetPageError(page); + + /* + * Be _very_ careful from here on. Bad things can happen if + * two buffer heads end IO at almost the same time and both + * decide that the page is now completely done. + * + * Async buffer_heads are here only as labels for IO, and get + * thrown away once the IO for this page is complete. IO is + * deemed complete once all buffers have been visited + * (b_count==0) and are now unlocked. We must make sure that + * only the _last_ buffer that decrements its count is the one + * that free's the page.. + */ + spin_lock_irqsave(&page_uptodate_lock, flags); + unlock_buffer(bh); + atomic_dec(&bh->b_count); + tmp = bh->b_this_page; + while (tmp != bh) { + if (atomic_read(&tmp->b_count) && + (tmp->b_end_io == end_buffer_io_async)) + goto still_busy; + tmp = tmp->b_this_page; + } + + /* OK, the async IO on this page is complete. */ + spin_unlock_irqrestore(&page_uptodate_lock, flags); + + /* + * if none of the buffers had errors then we can set the + * page uptodate: + */ + if (!PageError(page)) + SetPageUptodate(page); + + /* + * Run the hooks that have to be done when a page I/O has completed. + * + * Note - we need to test the flags before we unlock the page, but + * we must not actually free the page until after the unlock! + */ + if (test_and_clear_bit(PG_decr_after, &page->flags)) + atomic_dec(&nr_async_pages); + + if (test_and_clear_bit(PG_free_swap_after, &page->flags)) + swap_free(page->offset); + + free = test_and_clear_bit(PG_free_after, &page->flags); + + if (page->owner != -1) + PAGE_BUG(page); + page->owner = (int)current; + UnlockPage(page); + + if (free) + __free_page(page); + + return; + +still_busy: + spin_unlock_irqrestore(&page_uptodate_lock, flags); + return; +} + + /* * Ok, this is getblk, and it isn't very clear, again to hinder * race-conditions. Most of the code is seldom used, (ie repeating), @@ -713,22 +798,26 @@ repeat: } isize = BUFSIZE_INDEX(size); -get_free: - bh = free_list[isize]; + spin_lock(&free_list[isize].lock); + bh = free_list[isize].list; + if (bh) { + __remove_from_free_list(bh, isize); + atomic_set(&bh->b_count, 1); + } + spin_unlock(&free_list[isize].lock); if (!bh) goto refill; - remove_from_free_list(bh); /* OK, FINALLY we know that this buffer is the only one of its kind, - * and that it's unused (b_count=0), unlocked, and clean. + * we hold a reference (b_count>0), it is unlocked, and it is clean. */ - init_buffer(bh, dev, block, end_buffer_io_sync, NULL); - bh->b_count = 1; - bh->b_state = 0; + init_buffer(bh, end_buffer_io_sync, NULL); + bh->b_dev = dev; + bh->b_blocknr = block; + bh->b_state = 1 << BH_Mapped; /* Insert the buffer into the regular lists */ - insert_into_lru_list(bh); - insert_into_hash_list(bh); + insert_into_queues(bh); goto out; /* @@ -737,24 +826,12 @@ get_free: */ refill: refill_freelist(size); - if (!find_buffer(dev,block,size)) - goto get_free; goto repeat; out: return bh; } /* - * Put a buffer into the appropriate list, without side-effects. - */ -static void file_buffer(struct buffer_head *bh, int list) -{ - remove_from_lru_list(bh); - bh->b_list = list; - insert_into_lru_list(bh); -} - -/* * if a new dirty buffer is created we need to balance bdflush. * * in the future we might want to make bdflush aware of different @@ -783,6 +860,7 @@ void balance_dirty(kdev_t dev) static inline void __mark_dirty(struct buffer_head *bh, int flag) { bh->b_flushtime = jiffies + (flag ? bdf_prm.b_un.age_super : bdf_prm.b_un.age_buffer); + clear_bit(BH_New, &bh->b_state); refile_buffer(bh); } @@ -791,34 +869,29 @@ void __mark_buffer_dirty(struct buffer_head *bh, int flag) __mark_dirty(bh, flag); } -void __atomic_mark_buffer_dirty(struct buffer_head *bh, int flag) -{ - lock_kernel(); - __mark_dirty(bh, flag); - unlock_kernel(); -} - /* * A buffer may need to be moved from one buffer list to another * (e.g. in case it is not shared any more). Handle this. */ -void refile_buffer(struct buffer_head * buf) +static __inline__ void __refile_buffer(struct buffer_head *bh) { - int dispose; - - if (buf->b_dev == B_FREE) { - printk("Attempt to refile free buffer\n"); - return; - } - - dispose = BUF_CLEAN; - if (buffer_locked(buf)) + int dispose = BUF_CLEAN; + if (buffer_locked(bh)) dispose = BUF_LOCKED; - if (buffer_dirty(buf)) + if (buffer_dirty(bh)) dispose = BUF_DIRTY; + if (dispose != bh->b_list) { + __remove_from_lru_list(bh, bh->b_list); + bh->b_list = dispose; + __insert_into_lru_list(bh, dispose); + } +} - if (dispose != buf->b_list) - file_buffer(buf, dispose); +void refile_buffer(struct buffer_head *bh) +{ + spin_lock(&lru_list_lock); + __refile_buffer(bh); + spin_unlock(&lru_list_lock); } /* @@ -828,9 +901,8 @@ void __brelse(struct buffer_head * buf) { touch_buffer(buf); - if (buf->b_count) { - buf->b_count--; - wake_up(&buffer_wait); + if (atomic_read(&buf->b_count)) { + atomic_dec(&buf->b_count); return; } printk("VFS: brelse: Trying to free free buffer\n"); @@ -844,14 +916,21 @@ void __brelse(struct buffer_head * buf) */ void __bforget(struct buffer_head * buf) { - if (buf->b_count != 1 || buffer_locked(buf)) { - __brelse(buf); - return; + spin_lock(&lru_list_lock); + write_lock(&hash_table_lock); + if (atomic_read(&buf->b_count) != 1 || buffer_locked(buf)) { + touch_buffer(buf); + atomic_dec(&buf->b_count); + } else { + atomic_set(&buf->b_count, 0); + buf->b_state = 0; + if (buf->b_pprev) + __hash_unlink(buf); + __remove_from_lru_list(buf, buf->b_list); + put_last_free(buf); } - buf->b_count = 0; - buf->b_state = 0; - remove_from_queues(buf); - put_last_free(buf); + write_unlock(&hash_table_lock); + spin_unlock(&lru_list_lock); } /* @@ -941,49 +1020,25 @@ struct buffer_head * breada(kdev_t dev, int block, int bufsize, /* * Note: the caller should wake up the buffer_wait list if needed. */ -static void put_unused_buffer_head(struct buffer_head * bh) +static __inline__ void __put_unused_buffer_head(struct buffer_head * bh) { if (nr_unused_buffer_heads >= MAX_UNUSED_BUFFERS) { - nr_buffer_heads--; kmem_cache_free(bh_cachep, bh); - return; + } else { + bh->b_blocknr = -1; + init_waitqueue_head(&bh->b_wait); + nr_unused_buffer_heads++; + bh->b_next_free = unused_list; + bh->b_this_page = NULL; + unused_list = bh; } - -// memset(bh, 0, sizeof(*bh)); - bh->b_blocknr = -1; - init_waitqueue_head(&bh->b_wait); - nr_unused_buffer_heads++; - bh->b_next_free = unused_list; - unused_list = bh; } -/* - * We can't put completed temporary IO buffer_heads directly onto the - * unused_list when they become unlocked, since the device driver - * end_request routines still expect access to the buffer_head's - * fields after the final unlock. So, the device driver puts them on - * the reuse_list instead once IO completes, and we recover these to - * the unused_list here. - * - * Note that we don't do a wakeup here, but return a flag indicating - * whether we got any buffer heads. A task ready to sleep can check - * the returned value, and any tasks already sleeping will have been - * awakened when the buffer heads were added to the reuse list. - */ -static inline int recover_reusable_buffer_heads(void) +static void put_unused_buffer_head(struct buffer_head *bh) { - struct buffer_head *head = xchg(&reuse_list, NULL); - int found = 0; - - if (head) { - do { - struct buffer_head *bh = head; - head = head->b_next_free; - put_unused_buffer_head(bh); - } while (head); - found = 1; - } - return found; + spin_lock(&unused_list_lock); + __put_unused_buffer_head(bh); + spin_unlock(&unused_list_lock); } /* @@ -995,13 +1050,15 @@ static struct buffer_head * get_unused_buffer_head(int async) { struct buffer_head * bh; - recover_reusable_buffer_heads(); + spin_lock(&unused_list_lock); if (nr_unused_buffer_heads > NR_RESERVED) { bh = unused_list; unused_list = bh->b_next_free; nr_unused_buffer_heads--; + spin_unlock(&unused_list_lock); return bh; } + spin_unlock(&unused_list_lock); /* This is critical. We can't swap out pages to get * more buffer heads, because the swap-out may need @@ -1010,20 +1067,23 @@ static struct buffer_head * get_unused_buffer_head(int async) if((bh = kmem_cache_alloc(bh_cachep, SLAB_BUFFER)) != NULL) { memset(bh, 0, sizeof(*bh)); init_waitqueue_head(&bh->b_wait); - nr_buffer_heads++; return bh; } /* * If we need an async buffer, use the reserved buffer heads. */ - if (async && unused_list) { - bh = unused_list; - unused_list = bh->b_next_free; - nr_unused_buffer_heads--; - return bh; + if (async) { + spin_lock(&unused_list_lock); + if (unused_list) { + bh = unused_list; + unused_list = bh->b_next_free; + nr_unused_buffer_heads--; + spin_unlock(&unused_list_lock); + return bh; + } + spin_unlock(&unused_list_lock); } - #if 0 /* * (Pending further analysis ...) @@ -1035,7 +1095,6 @@ static struct buffer_head * get_unused_buffer_head(int async) (bh = kmem_cache_alloc(bh_cachep, SLAB_KERNEL)) != NULL) { memset(bh, 0, sizeof(*bh)); init_waitqueue_head(&bh->b_wait); - nr_buffer_heads++; return bh; } #endif @@ -1052,8 +1111,7 @@ static struct buffer_head * get_unused_buffer_head(int async) * from ordinary buffer allocations, and only async requests are allowed * to sleep waiting for buffer heads. */ -static struct buffer_head * create_buffers(unsigned long page, - unsigned long size, int async) +static struct buffer_head * create_buffers(unsigned long page, unsigned long size, int async) { DECLARE_WAITQUEUE(wait, current); struct buffer_head *bh, *head; @@ -1073,11 +1131,14 @@ try_again: bh->b_state = 0; bh->b_next_free = NULL; - bh->b_count = 0; + bh->b_pprev = NULL; + atomic_set(&bh->b_count, 0); bh->b_size = size; bh->b_data = (char *) (page+offset); - bh->b_list = 0; + bh->b_list = BUF_CLEAN; + bh->b_flushtime = 0; + bh->b_end_io = end_buffer_io_bad; } return head; /* @@ -1118,115 +1179,16 @@ no_grow: */ add_wait_queue(&buffer_wait, &wait); current->state = TASK_UNINTERRUPTIBLE; - if (!recover_reusable_buffer_heads()) + if (nr_unused_buffer_heads < MAX_BUF_PER_PAGE) { + current->policy |= SCHED_YIELD; schedule(); + } remove_wait_queue(&buffer_wait, &wait); current->state = TASK_RUNNING; goto try_again; } -/* Run the hooks that have to be done when a page I/O has completed. */ -static inline void after_unlock_page (struct page * page) -{ - if (test_and_clear_bit(PG_decr_after, &page->flags)) { - atomic_dec(&nr_async_pages); -#ifdef DEBUG_SWAP - printk ("DebugVM: Finished IO on page %p, nr_async_pages %d\n", - (char *) page_address(page), - atomic_read(&nr_async_pages)); -#endif - } - if (test_and_clear_bit(PG_swap_unlock_after, &page->flags)) - swap_after_unlock_page(page->offset); - if (test_and_clear_bit(PG_free_after, &page->flags)) - __free_page(page); -} - -/* - * Free all temporary buffers belonging to a page. - * This needs to be called with interrupts disabled. - */ -static inline void free_async_buffers (struct buffer_head * bh) -{ - struct buffer_head *tmp, *tail; - - /* - * Link all the buffers into the b_next_free list, - * so we only have to do one xchg() operation ... - */ - tail = bh; - while ((tmp = tail->b_this_page) != bh) { - tail->b_next_free = tmp; - tail = tmp; - }; - - /* Update the reuse list */ - tail->b_next_free = xchg(&reuse_list, NULL); - reuse_list = bh; - - /* Wake up any waiters ... */ - wake_up(&buffer_wait); -} - -static void end_buffer_io_async(struct buffer_head * bh, int uptodate) -{ - unsigned long flags; - struct buffer_head *tmp; - struct page *page; - - mark_buffer_uptodate(bh, uptodate); - - /* This is a temporary buffer used for page I/O. */ - page = mem_map + MAP_NR(bh->b_data); - - if (!uptodate) - SetPageError(page); - - /* - * Be _very_ careful from here on. Bad things can happen if - * two buffer heads end IO at almost the same time and both - * decide that the page is now completely done. - * - * Async buffer_heads are here only as labels for IO, and get - * thrown away once the IO for this page is complete. IO is - * deemed complete once all buffers have been visited - * (b_count==0) and are now unlocked. We must make sure that - * only the _last_ buffer that decrements its count is the one - * that free's the page.. - */ - save_flags(flags); - cli(); - unlock_buffer(bh); - tmp = bh->b_this_page; - while (tmp != bh) { - if (buffer_locked(tmp)) - goto still_busy; - tmp = tmp->b_this_page; - } - - /* OK, the async IO on this page is complete. */ - restore_flags(flags); - - after_unlock_page(page); - /* - * if none of the buffers had errors then we can set the - * page uptodate: - */ - if (!PageError(page)) - SetPageUptodate(page); - if (page->owner != -1) - PAGE_BUG(page); - page->owner = (int)current; - UnlockPage(page); - - return; - -still_busy: - restore_flags(flags); - return; -} - -static int create_page_buffers (int rw, struct page *page, kdev_t dev, int b[], int size, int bmap) +static int create_page_buffers(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap) { struct buffer_head *head, *bh, *tail; int block; @@ -1240,9 +1202,7 @@ static int create_page_buffers (int rw, struct page *page, kdev_t dev, int b[], * They show up in the buffer hash table and are registered in * page->buffers. */ - lock_kernel(); head = create_buffers(page_address(page), size, 1); - unlock_kernel(); if (page->buffers) BUG(); if (!head) @@ -1252,7 +1212,9 @@ static int create_page_buffers (int rw, struct page *page, kdev_t dev, int b[], block = *(b++); tail = bh; - init_buffer(bh, dev, block, end_buffer_io_async, NULL); + init_buffer(bh, end_buffer_io_async, NULL); + bh->b_dev = dev; + bh->b_blocknr = block; /* * When we use bmap, we define block zero to represent @@ -1261,9 +1223,11 @@ static int create_page_buffers (int rw, struct page *page, kdev_t dev, int b[], * two cases. */ if (bmap && !block) { - set_bit(BH_Uptodate, &bh->b_state); memset(bh->b_data, 0, size); + set_bit(BH_Uptodate, &bh->b_state); + continue; } + set_bit(BH_Mapped, &bh->b_state); } tail->b_this_page = head; get_page(page); @@ -1287,7 +1251,6 @@ int block_flushpage(struct inode *inode, struct page *page, unsigned long offset BUG(); if (!page->buffers) return 0; - lock_kernel(); head = page->buffers; bh = head; @@ -1299,14 +1262,16 @@ int block_flushpage(struct inode *inode, struct page *page, unsigned long offset * is this block fully flushed? */ if (offset <= curr_off) { - if (bh->b_blocknr) { - bh->b_count++; + if (buffer_mapped(bh)) { + atomic_inc(&bh->b_count); wait_on_buffer(bh); if (bh->b_dev == B_FREE) BUG(); mark_buffer_clean(bh); + clear_bit(BH_Uptodate, &bh->b_state); + clear_bit(BH_Mapped, &bh->b_state); bh->b_blocknr = 0; - bh->b_count--; + atomic_dec(&bh->b_count); } } curr_off = next_off; @@ -1318,22 +1283,24 @@ int block_flushpage(struct inode *inode, struct page *page, unsigned long offset * the 'final' flushpage. We have invalidated the bmap * cached value unconditionally, so real IO is not * possible anymore. + * + * If the free doesn't work out, the buffers can be + * left around - they just turn into anonymous buffers + * instead. */ - if (!offset) - try_to_free_buffers(page); + if (!offset) { + if (!try_to_free_buffers(page)) + atomic_add(PAGE_CACHE_SIZE, &buffermem); + } - unlock_kernel(); return 0; } -static void create_empty_buffers (struct page *page, - struct inode *inode, unsigned long blocksize) +static void create_empty_buffers(struct page *page, struct inode *inode, unsigned long blocksize) { struct buffer_head *bh, *head, *tail; - lock_kernel(); head = create_buffers(page_address(page), blocksize, 1); - unlock_kernel(); if (page->buffers) BUG(); @@ -1341,6 +1308,7 @@ static void create_empty_buffers (struct page *page, do { bh->b_dev = inode->i_dev; bh->b_blocknr = 0; + bh->b_end_io = end_buffer_io_bad; tail = bh; bh = bh->b_this_page; } while (bh); @@ -1353,12 +1321,12 @@ static void create_empty_buffers (struct page *page, * block_write_full_page() is SMP-safe - currently it's still * being called with the kernel lock held, but the code is ready. */ -int block_write_full_page (struct file *file, struct page *page, fs_getblock_t fs_get_block) +int block_write_full_page(struct file *file, struct page *page) { struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; - int err, created, i; - unsigned long block, phys, offset; + int err, i; + unsigned long block, offset; struct buffer_head *bh, *head; if (!PageLocked(page)) @@ -1381,23 +1349,22 @@ int block_write_full_page (struct file *file, struct page *page, fs_getblock_t f if (!bh) BUG(); - if (!bh->b_blocknr) { - err = -EIO; - phys = fs_get_block (inode, block, 1, &err, &created); - if (!phys) + /* + * If the buffer isn't up-to-date, we can't be sure + * that the buffer has been initialized with the proper + * block number information etc.. + * + * Leave it to the low-level FS to make all those + * decisions (block #0 may actually be a valid block) + */ + bh->b_end_io = end_buffer_io_sync; + if (!buffer_mapped(bh)) { + err = inode->i_op->get_block(inode, block, bh, 1); + if (err) goto out; - - init_buffer(bh, inode->i_dev, phys, end_buffer_io_sync, NULL); - bh->b_state = (1<<BH_Uptodate); - } else { - /* - * block already exists, just mark it uptodate and - * dirty: - */ - bh->b_end_io = end_buffer_io_sync; - set_bit(BH_Uptodate, &bh->b_state); } - atomic_mark_buffer_dirty(bh,0); + set_bit(BH_Uptodate, &bh->b_state); + mark_buffer_dirty(bh,0); bh = bh->b_this_page; block++; @@ -1410,15 +1377,15 @@ out: return err; } -int block_write_partial_page (struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf, fs_getblock_t fs_get_block) +int block_write_partial_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) { struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; unsigned long block; - int err, created, partial; + int err, partial; unsigned long blocksize, start_block, end_block; unsigned long start_offset, start_bytes, end_bytes; - unsigned long bbits, phys, blocks, i, len; + unsigned long bbits, blocks, i, len; struct buffer_head *bh, *head; char * target_buf; @@ -1469,46 +1436,35 @@ int block_write_partial_page (struct file *file, struct page *page, unsigned lon partial = 1; goto skip; } - if (!bh->b_blocknr) { - err = -EIO; - phys = fs_get_block (inode, block, 1, &err, &created); - if (!phys) - goto out; - init_buffer(bh, inode->i_dev, phys, end_buffer_io_sync, NULL); + /* + * If the buffer is not up-to-date, we need to ask the low-level + * FS to do something for us (we used to have assumptions about + * the meaning of b_blocknr etc, that's bad). + * + * If "update" is set, that means that the low-level FS should + * try to make sure that the block is up-to-date because we're + * not going to fill it completely. + */ + bh->b_end_io = end_buffer_io_sync; + if (!buffer_mapped(bh)) { + err = inode->i_op->get_block(inode, block, bh, 1); + if (err) + goto out; + } - /* - * if partially written block which has contents on - * disk, then we have to read it first. - * We also rely on the fact that filesystem holes - * cannot be written. - */ - if (start_offset || (end_bytes && (i == end_block))) { - if (created) { - memset(bh->b_data, 0, bh->b_size); - } else { - bh->b_state = 0; - ll_rw_block(READ, 1, &bh); - lock_kernel(); - wait_on_buffer(bh); - unlock_kernel(); - err = -EIO; - if (!buffer_uptodate(bh)) - goto out; - } + if (!buffer_uptodate(bh) && (start_offset || (end_bytes && (i == end_block)))) { + if (buffer_new(bh)) { + memset(bh->b_data, 0, bh->b_size); + } else { + ll_rw_block(READ, 1, &bh); + wait_on_buffer(bh); + err = -EIO; + if (!buffer_uptodate(bh)) + goto out; } - - bh->b_state = (1<<BH_Uptodate); - } else { - /* - * block already exists, just mark it uptodate: - */ - bh->b_end_io = end_buffer_io_sync; - set_bit(BH_Uptodate, &bh->b_state); - created = 0; } - err = -EFAULT; len = blocksize; if (start_offset) { len = start_bytes; @@ -1517,8 +1473,7 @@ int block_write_partial_page (struct file *file, struct page *page, unsigned lon len = end_bytes; end_bytes = 0; } - if (copy_from_user(target_buf, buf, len)) - goto out; + err = copy_from_user(target_buf, buf, len); target_buf += len; buf += len; @@ -1538,12 +1493,18 @@ int block_write_partial_page (struct file *file, struct page *page, unsigned lon * should not penalize them for somebody else writing * lots of dirty pages. */ + set_bit(BH_Uptodate, &bh->b_state); if (!test_and_set_bit(BH_Dirty, &bh->b_state)) { - __atomic_mark_buffer_dirty(bh, bdf_prm.b_un.age_buffer); + __mark_dirty(bh, 0); if (too_many_dirty_buffers) balance_dirty(bh->b_dev); } + if (err) { + err = -EFAULT; + goto out; + } + skip: i++; block++; @@ -1572,6 +1533,9 @@ out: * * brw_page() is SMP-safe, although it's being called with the * kernel lock held - but the code is ready. + * + * FIXME: we need a swapper_inode->get_block function to remove + * some of the bmap kludges and interface ugliness here. */ int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap) { @@ -1600,7 +1564,7 @@ int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap) do { block = *(b++); - if (fresh && (bh->b_count != 0)) + if (fresh && (atomic_read(&bh->b_count) != 0)) BUG(); if (rw == READ) { if (!fresh) @@ -1613,6 +1577,7 @@ int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap) BUG(); if (!buffer_uptodate(bh)) { arr[nr++] = bh; + atomic_inc(&bh->b_count); } } } else { /* WRITE */ @@ -1625,8 +1590,9 @@ int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap) BUG(); } set_bit(BH_Uptodate, &bh->b_state); - atomic_mark_buffer_dirty(bh, 0); + set_bit(BH_Dirty, &bh->b_state); arr[nr++] = bh; + atomic_inc(&bh->b_count); } bh = bh->b_this_page; } while (bh != head); @@ -1649,30 +1615,7 @@ int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap) } /* - * This is called by end_request() when I/O has completed. - */ -void mark_buffer_uptodate(struct buffer_head * bh, int on) -{ - if (on) { - struct buffer_head *tmp = bh; - struct page *page; - set_bit(BH_Uptodate, &bh->b_state); - /* If a page has buffers and all these buffers are uptodate, - * then the page is uptodate. */ - do { - if (!test_bit(BH_Uptodate, &tmp->b_state)) - return; - tmp=tmp->b_this_page; - } while (tmp && tmp != bh); - page = mem_map + MAP_NR(bh->b_data); - SetPageUptodate(page); - return; - } - clear_bit(BH_Uptodate, &bh->b_state); -} - -/* - * Generic "readpage" function for block devices that have the normal + * Generic "read page" function for block devices that have the normal * bmap functionality. This is most of the block device filesystems. * Reads the page asynchronously --- the unlock_buffer() and * mark_buffer_uptodate() functions propagate buffer state into the @@ -1682,7 +1625,7 @@ int block_read_full_page(struct file * file, struct page * page) { struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; - unsigned long iblock, phys_block; + unsigned long iblock; struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE]; unsigned int blocksize, blocks; int nr; @@ -1700,33 +1643,25 @@ int block_read_full_page(struct file * file, struct page * page) head = page->buffers; bh = head; nr = 0; + do { - phys_block = bh->b_blocknr; - /* - * important, we have to retry buffers that already have - * their bnr cached but had an IO error! - */ - if (!buffer_uptodate(bh)) { - phys_block = inode->i_op->bmap(inode, iblock); - /* - * this is safe to do because we hold the page lock: - */ - if (phys_block) { - init_buffer(bh, inode->i_dev, phys_block, - end_buffer_io_async, NULL); - arr[nr] = bh; - nr++; - } else { - /* - * filesystem 'hole' represents zero-contents: - */ + if (buffer_uptodate(bh)) + continue; + + if (!buffer_mapped(bh)) { + inode->i_op->get_block(inode, iblock, bh, 0); + if (!buffer_mapped(bh)) { memset(bh->b_data, 0, blocksize); set_bit(BH_Uptodate, &bh->b_state); + continue; } } - iblock++; - bh = bh->b_this_page; - } while (bh != head); + + init_buffer(bh, end_buffer_io_async, NULL); + atomic_inc(&bh->b_count); + arr[nr] = bh; + nr++; + } while (iblock++, (bh = bh->b_this_page) != head); ++current->maj_flt; if (nr) { @@ -1770,8 +1705,9 @@ static int grow_buffers(int size) } isize = BUFSIZE_INDEX(size); - insert_point = free_list[isize]; + spin_lock(&free_list[isize].lock); + insert_point = free_list[isize].list; tmp = bh; while (1) { if (insert_point) { @@ -1790,9 +1726,11 @@ static int grow_buffers(int size) break; } tmp->b_this_page = bh; - free_list[isize] = bh; + free_list[isize].list = bh; + spin_unlock(&free_list[isize].lock); + mem_map[MAP_NR(page)].buffers = bh; - buffermem += PAGE_SIZE; + atomic_add(PAGE_SIZE, &buffermem); return 1; } @@ -1800,7 +1738,7 @@ static int grow_buffers(int size) * Can the buffer be thrown out? */ #define BUFFER_BUSY_BITS ((1<<BH_Dirty) | (1<<BH_Lock) | (1<<BH_Protected)) -#define buffer_busy(bh) ((bh)->b_count || ((bh)->b_state & BUFFER_BUSY_BITS)) +#define buffer_busy(bh) (atomic_read(&(bh)->b_count) | ((bh)->b_state & BUFFER_BUSY_BITS)) /* * try_to_free_buffers() checks if all the buffers on this particular page @@ -1808,90 +1746,70 @@ static int grow_buffers(int size) * * Wake up bdflush() if this fails - if we're running low on memory due * to dirty buffers, we need to flush them out as quickly as possible. + * + * NOTE: There are quite a number of ways that threads of control can + * obtain a reference to a buffer head within a page. So we must + * lock out all of these paths to cleanly toss the page. */ int try_to_free_buffers(struct page * page) { struct buffer_head * tmp, * bh = page->buffers; + int index = BUFSIZE_INDEX(bh->b_size); + int ret; + spin_lock(&lru_list_lock); + write_lock(&hash_table_lock); + spin_lock(&free_list[index].lock); tmp = bh; do { struct buffer_head * p = tmp; tmp = tmp->b_this_page; - if (!buffer_busy(p)) - continue; - - too_many_dirty_buffers = 1; - wakeup_bdflush(0); - return 0; + if (buffer_busy(p)) + goto busy_buffer_page; } while (tmp != bh); + spin_lock(&unused_list_lock); tmp = bh; do { struct buffer_head * p = tmp; tmp = tmp->b_this_page; - /* The buffer can be either on the regular queues or on the free list.. */ - if (p->b_dev == B_FREE) - remove_from_free_list(p); - else - remove_from_queues(p); - - put_unused_buffer_head(p); + /* The buffer can be either on the regular + * queues or on the free list.. + */ + if (p->b_dev == B_FREE) { + __remove_from_free_list(p, index); + } else { + if (p->b_pprev) + __hash_unlink(p); + __remove_from_lru_list(p, p->b_list); + } + __put_unused_buffer_head(p); } while (tmp != bh); + spin_unlock(&unused_list_lock); /* Wake up anyone waiting for buffer heads */ wake_up(&buffer_wait); /* And free the page */ page->buffers = NULL; - if (__free_page(page)) { - buffermem -= PAGE_SIZE; - return 1; - } - return 0; -} - -/* ================== Debugging =================== */ - -void show_buffers(void) -{ - struct buffer_head * bh; - int found = 0, locked = 0, dirty = 0, used = 0, lastused = 0; - int protected = 0; - int nlist; - static char *buf_types[NR_LIST] = {"CLEAN","LOCKED","DIRTY"}; - - printk("Buffer memory: %6dkB\n",buffermem>>10); - printk("Buffer heads: %6d\n",nr_buffer_heads); - printk("Buffer blocks: %6d\n",nr_buffers); - printk("Buffer hashed: %6d\n",nr_hashed_buffers); - - for(nlist = 0; nlist < NR_LIST; nlist++) { - found = locked = dirty = used = lastused = protected = 0; - bh = lru_list[nlist]; - if(!bh) continue; - - do { - found++; - if (buffer_locked(bh)) - locked++; - if (buffer_protected(bh)) - protected++; - if (buffer_dirty(bh)) - dirty++; - if (bh->b_count) - used++, lastused = found; - bh = bh->b_next_free; - } while (bh != lru_list[nlist]); - printk("%8s: %d buffers, %d used (last=%d), " - "%d locked, %d protected, %d dirty\n", - buf_types[nlist], found, used, lastused, - locked, protected, dirty); - }; + __free_page(page); + ret = 1; +out: + spin_unlock(&free_list[index].lock); + write_unlock(&hash_table_lock); + spin_unlock(&lru_list_lock); + return ret; + +busy_buffer_page: + /* Uhhuh, start writeback so that we don't end up with all dirty pages */ + too_many_dirty_buffers = 1; + wakeup_bdflush(0); + ret = 0; + goto out; } - /* ===================== Init ======================= */ /* @@ -1901,31 +1819,53 @@ void show_buffers(void) */ void __init buffer_init(unsigned long memory_size) { - int order; + int order, i; unsigned int nr_hash; - /* we need to guess at the right sort of size for a buffer cache. - the heuristic from working with large databases and getting - fsync times (ext2) manageable, is the following */ - - memory_size >>= 22; - for (order = 5; (1UL << order) < memory_size; order++); + /* The buffer cache hash table is less important these days, + * trim it a bit. + */ + memory_size >>= 14; + memory_size *= sizeof(struct buffer_head *); + for (order = 0; (PAGE_SIZE << order) < memory_size; order++) + ; /* try to allocate something until we get it or we're asking for something that is really too small */ do { - nr_hash = (1UL << order) * PAGE_SIZE / - sizeof(struct buffer_head *); + unsigned long tmp; + + nr_hash = (PAGE_SIZE << order) / sizeof(struct buffer_head *); + bh_hash_mask = (nr_hash - 1); + + tmp = nr_hash; + bh_hash_shift = 0; + while((tmp >>= 1UL) != 0UL) + bh_hash_shift++; + hash_table = (struct buffer_head **) __get_free_pages(GFP_ATOMIC, order); - } while (hash_table == NULL && --order > 4); - printk("buffer-cache hash table entries: %d (order: %d, %ld bytes)\n", nr_hash, order, (1UL<<order) * PAGE_SIZE); - + } while (hash_table == NULL && --order > 0); + printk("Buffer-cache hash table entries: %d (order: %d, %ld bytes)\n", + nr_hash, order, (1UL<<order) * PAGE_SIZE); + if (!hash_table) panic("Failed to allocate buffer hash table\n"); - memset(hash_table, 0, nr_hash * sizeof(struct buffer_head *)); - bh_hash_mask = nr_hash-1; + + /* Setup hash chains. */ + for(i = 0; i < nr_hash; i++) + hash_table[i] = NULL; + + /* Setup free lists. */ + for(i = 0; i < NR_SIZES; i++) { + free_list[i].list = NULL; + free_list[i].lock = SPIN_LOCK_UNLOCKED; + } + + /* Setup lru lists. */ + for(i = 0; i < NR_LIST; i++) + lru_list[i] = NULL; bh_cachep = kmem_cache_create("buffer_head", sizeof(struct buffer_head), @@ -1933,21 +1873,6 @@ void __init buffer_init(unsigned long memory_size) SLAB_HWCACHE_ALIGN, NULL, NULL); if(!bh_cachep) panic("Cannot create buffer head SLAB cache\n"); - /* - * Allocate the reserved buffer heads. - */ - while (nr_buffer_heads < NR_RESERVED) { - struct buffer_head * bh; - - bh = kmem_cache_alloc(bh_cachep, SLAB_ATOMIC); - if (!bh) - break; - put_unused_buffer_head(bh); - nr_buffer_heads++; - } - - lru_list[BUF_CLEAN] = 0; - grow_buffers(BLOCK_SIZE); } @@ -1983,70 +1908,49 @@ void wakeup_bdflush(int wait) static int sync_old_buffers(void) { - int i; - int ndirty, nwritten; int nlist; - int ncount; - struct buffer_head * bh, *next; + lock_kernel(); sync_supers(0); sync_inodes(0); + unlock_kernel(); - ncount = 0; -#ifdef DEBUG - for(nlist = 0; nlist < NR_LIST; nlist++) -#else - for(nlist = BUF_LOCKED; nlist <= BUF_DIRTY; nlist++) -#endif - { - ndirty = 0; - nwritten = 0; + for(nlist = BUF_LOCKED; nlist <= BUF_DIRTY; nlist++) { + struct buffer_head *bh; repeat: - + spin_lock(&lru_list_lock); bh = lru_list[nlist]; - if(bh) - for (i = nr_buffers_type[nlist]; i-- > 0; bh = next) { - /* We may have stalled while waiting for I/O to complete. */ - if(bh->b_list != nlist) goto repeat; - next = bh->b_next_free; - if(!lru_list[nlist]) { - printk("Dirty list empty %d\n", i); - break; - } - - /* Clean buffer on dirty list? Refile it */ - if (nlist == BUF_DIRTY && !buffer_dirty(bh) && !buffer_locked(bh)) { - refile_buffer(bh); - continue; - } - - /* Unlocked buffer on locked list? Refile it */ - if (nlist == BUF_LOCKED && !buffer_locked(bh)) { - refile_buffer(bh); - continue; - } + if(bh) { + struct buffer_head *next; + int i; + for (i = nr_buffers_type[nlist]; i-- > 0; bh = next) { + next = bh->b_next_free; + + /* If the buffer is not on the proper list, + * then refile it. + */ + if ((nlist == BUF_DIRTY && + (!buffer_dirty(bh) && !buffer_locked(bh))) || + (nlist == BUF_LOCKED && !buffer_locked(bh))) { + __refile_buffer(bh); + continue; + } - if (buffer_locked(bh) || !buffer_dirty(bh)) - continue; - ndirty++; - nwritten++; - next->b_count++; - bh->b_count++; - bh->b_flushtime = 0; -#ifdef DEBUG - if(nlist != BUF_DIRTY) ncount++; -#endif - ll_rw_block(WRITE, 1, &bh); - bh->b_count--; - next->b_count--; - } + if (buffer_locked(bh) || !buffer_dirty(bh)) + continue; + + /* OK, now we are committed to write it out. */ + bh->b_flushtime = 0; + atomic_inc(&bh->b_count); + spin_unlock(&lru_list_lock); + ll_rw_block(WRITE, 1, &bh); + atomic_dec(&bh->b_count); + goto repeat; + } + } + spin_unlock(&lru_list_lock); } run_task_queue(&tq_disk); -#ifdef DEBUG - if (ncount) printk("sync_old_buffers: %d dirty buffers not on dirty list\n", ncount); - printk("Wrote %d/%d buffers\n", nwritten, ndirty); -#endif - run_task_queue(&tq_disk); return 0; } @@ -2060,7 +1964,6 @@ asmlinkage int sys_bdflush(int func, long data) { int i, error = -EPERM; - lock_kernel(); if (!capable(CAP_SYS_ADMIN)) goto out; @@ -2092,7 +1995,6 @@ asmlinkage int sys_bdflush(int func, long data) */ error = 0; out: - unlock_kernel(); return error; } @@ -2114,52 +2016,37 @@ int bdflush(void * unused) sprintf(current->comm, "kflushd"); bdflush_tsk = current; - /* - * As a kernel thread we want to tamper with system buffers - * and other internals and thus be subject to the SMP locking - * rules. (On a uniprocessor box this does nothing). - */ - lock_kernel(); - for (;;) { int nlist; CHECK_EMERGENCY_SYNC - for(nlist = BUF_LOCKED; nlist <= BUF_DIRTY; nlist++) - { - int nr; - int written = 0; + for(nlist = BUF_LOCKED; nlist <= BUF_DIRTY; nlist++) { + int nr, major, written = 0; struct buffer_head *next; - int major; repeat: + spin_lock(&lru_list_lock); next = lru_list[nlist]; nr = nr_buffers_type[nlist]; - while (nr-- > 0) { struct buffer_head *bh = next; - /* We may have stalled while waiting for I/O to complete. */ - if (next->b_list != nlist) - goto repeat; + next = next->b_next_free; - /* Clean buffer on dirty list? Refile it */ - if (nlist == BUF_DIRTY && !buffer_dirty(bh)) { - refile_buffer(bh); - continue; - } - - /* Unlocked buffer on locked list? Refile it */ - if (nlist == BUF_LOCKED && !buffer_locked(bh)) { - refile_buffer(bh); + /* If the buffer is not on the correct list, + * then refile it. + */ + if ((nlist == BUF_DIRTY && + (!buffer_dirty(bh) && !buffer_locked(bh))) || + (nlist == BUF_LOCKED && !buffer_locked(bh))) { + __refile_buffer(bh); continue; } - /* - * If we aren't in panic mode, don't write out too much - * at a time. Also, don't write out buffers we don't really - * have to write out yet.. + /* If we aren't in panic mode, don't write out too much + * at a time. Also, don't write out buffers we don't + * really have to write out yet.. */ if (!too_many_dirty_buffers) { if (written > bdf_prm.b_un.ndirty) @@ -2172,9 +2059,6 @@ int bdflush(void * unused) continue; major = MAJOR(bh->b_dev); - if (next) - next->b_count++; - bh->b_count++; written++; bh->b_flushtime = 0; @@ -2182,18 +2066,18 @@ int bdflush(void * unused) * For the loop major we can try to do asynchronous writes, * but we have to guarantee that we're making some progress.. */ + atomic_inc(&bh->b_count); + spin_unlock(&lru_list_lock); if (major == LOOP_MAJOR && written > 1) { ll_rw_block(WRITEA, 1, &bh); if (buffer_dirty(bh)) --written; } else ll_rw_block(WRITE, 1, &bh); - - bh->b_count--; - if (next) - next->b_count--; - wake_up(&buffer_wait); + atomic_dec(&bh->b_count); + goto repeat; } + spin_unlock(&lru_list_lock); } run_task_queue(&tq_disk); wake_up(&bdflush_done); @@ -2206,7 +2090,10 @@ int bdflush(void * unused) */ if (!too_many_dirty_buffers || nr_buffers_type[BUF_DIRTY] < bdf_prm.b_un.ndirty) { too_many_dirty_buffers = 0; - sleep_on_timeout(&bdflush_wait, 5*HZ); + spin_lock_irq(¤t->sigmask_lock); + flush_signals(current); + spin_unlock_irq(¤t->sigmask_lock); + interruptible_sleep_on_timeout(&bdflush_wait, 5*HZ); } } } diff --git a/fs/coda/dir.c b/fs/coda/dir.c index b3c0195a3..4313edb69 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -77,9 +77,10 @@ struct inode_operations coda_dir_inode_operations = coda_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ coda_permission, /* permission */ NULL, /* smap */ diff --git a/fs/coda/file.c b/fs/coda/file.c index cbc81542f..7e83ea954 100644 --- a/fs/coda/file.c +++ b/fs/coda/file.c @@ -47,13 +47,13 @@ struct inode_operations coda_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ coda_readpage, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ coda_permission, /* permission */ NULL, /* smap */ - NULL, /* update page */ coda_revalidate_inode /* revalidate */ }; @@ -101,7 +101,7 @@ static int coda_readpage(struct file * coda_file, struct page * page) CDEBUG(D_INODE, "coda ino: %ld, cached ino %ld, page offset: %lx\n", coda_inode->i_ino, cii->c_ovp->i_ino, page->offset); - generic_readpage(&cont_file, page); + block_read_full_page(&cont_file, page); EXIT; return 0; } @@ -256,7 +256,7 @@ void coda_prepare_openfile(struct inode *i, struct file *coda_file, cont_file->f_pos = coda_file->f_pos; cont_file->f_mode = coda_file->f_mode; cont_file->f_flags = coda_file->f_flags; - cont_file->f_count = coda_file->f_count; + atomic_set(&cont_file->f_count, atomic_read(&coda_file->f_count)); cont_file->f_owner = coda_file->f_owner; cont_file->f_op = cont_inode->i_op->default_file_ops; cont_file->f_dentry = cont_dentry; diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c index 4a98361aa..3e6924f15 100644 --- a/fs/coda/pioctl.c +++ b/fs/coda/pioctl.c @@ -46,13 +46,13 @@ struct inode_operations coda_ioctl_inode_operations = NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ coda_ioctl_permission, /* permission */ NULL, /* smap */ - NULL, /* update page */ NULL /* revalidate */ }; diff --git a/fs/coda/symlink.c b/fs/coda/symlink.c index b42555811..cfe0dbcb7 100644 --- a/fs/coda/symlink.c +++ b/fs/coda/symlink.c @@ -42,13 +42,13 @@ struct inode_operations coda_symlink_inode_operations = { NULL, /* rename */ coda_readlink, /* readlink */ coda_follow_link, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* update page */ NULL /* revalidate */ }; diff --git a/fs/devices.c b/fs/devices.c index 934fe290f..7bdadd5fb 100644 --- a/fs/devices.c +++ b/fs/devices.c @@ -277,7 +277,7 @@ struct inode_operations blkdev_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ @@ -333,11 +333,14 @@ struct inode_operations chrdev_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; /* diff --git a/fs/devpts/root.c b/fs/devpts/root.c index c1c1a6000..9b5408235 100644 --- a/fs/devpts/root.c +++ b/fs/devpts/root.c @@ -51,9 +51,10 @@ struct inode_operations devpts_root_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ diff --git a/fs/efs/dir.c b/fs/efs/dir.c index ea484dab4..29f2ebbc3 100644 --- a/fs/efs/dir.c +++ b/fs/efs/dir.c @@ -25,6 +25,8 @@ static struct file_operations efs_dir_operations = { NULL /* revalidate */ }; +extern int efs_get_block(struct inode *, long, struct buffer_head *, int); + struct inode_operations efs_dir_inode_operations = { &efs_dir_operations, /* default directory file-ops */ NULL, /* create */ @@ -38,12 +40,14 @@ struct inode_operations efs_dir_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + efs_get_block, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - efs_bmap, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; static int efs_readdir(struct file *filp, void *dirent, filldir_t filldir) { diff --git a/fs/efs/file.c b/fs/efs/file.c index d97a420c5..b86965f8f 100644 --- a/fs/efs/file.c +++ b/fs/efs/file.c @@ -8,6 +8,59 @@ #include <linux/efs_fs.h> +int efs_get_block(struct inode *inode, long iblock, + struct buffer_head *bh_result, int create) +{ + int error = -EROFS; + long phys; + + if (create) + return error; + if (iblock >= inode->i_blocks) { +#ifdef DEBUG + /* + * i have no idea why this happens as often as it does + */ + printk(KERN_WARNING "EFS: bmap(): block %d >= %ld (filesize %ld)\n", + block, + inode->i_blocks, + inode->i_size); +#endif + return 0; + } + phys = efs_map_block(inode, iblock); + if (phys) { + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = phys; + bh_result->b_state |= (1UL << BH_Mapped); + } + return 0; +} + +int efs_bmap(struct inode *inode, efs_block_t block) { + + if (block < 0) { + printk(KERN_WARNING "EFS: bmap(): block < 0\n"); + return 0; + } + + /* are we about to read past the end of a file ? */ + if (!(block < inode->i_blocks)) { +#ifdef DEBUG + /* + * i have no idea why this happens as often as it does + */ + printk(KERN_WARNING "EFS: bmap(): block %d >= %ld (filesize %ld)\n", + block, + inode->i_blocks, + inode->i_size); +#endif + return 0; + } + + return efs_map_block(inode, block); +} + static struct file_operations efs_file_operations = { NULL, /* lseek */ generic_file_read, /* read */ @@ -38,35 +91,12 @@ struct inode_operations efs_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - generic_readpage, /* readpage */ + efs_get_block, /* get_block */ + block_read_full_page, /* readpage */ NULL, /* writepage */ - efs_bmap, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; - -int efs_bmap(struct inode *inode, efs_block_t block) { - - if (block < 0) { - printk(KERN_WARNING "EFS: bmap(): block < 0\n"); - return 0; - } - - /* are we about to read past the end of a file ? */ - if (!(block < inode->i_blocks)) { -#ifdef DEBUG - /* - * i have no idea why this happens as often as it does - */ - printk(KERN_WARNING "EFS: bmap(): block %d >= %ld (filesize %ld)\n", - block, - inode->i_blocks, - inode->i_size); -#endif - return 0; - } - - return efs_map_block(inode, block); -} - diff --git a/fs/efs/symlink.c b/fs/efs/symlink.c index 705599eb0..69ddda45b 100644 --- a/fs/efs/symlink.c +++ b/fs/efs/symlink.c @@ -26,12 +26,14 @@ struct inode_operations efs_symlink_inode_operations = { NULL, /* rename */ efs_readlink, /* readlink */ efs_follow_link, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; static char *efs_linktarget(struct inode *in, int *len) { @@ -72,10 +72,6 @@ void __init binfmt_setup(void) init_aout32_binfmt(); #endif -#ifdef CONFIG_BINFMT_JAVA - init_java_binfmt(); -#endif - #ifdef CONFIG_BINFMT_EM86 init_em86_binfmt(); #endif @@ -324,7 +320,7 @@ int setup_arg_pages(struct linux_binprm *bprm) /* * Read in the complete executable. This is used for "-N" files * that aren't on a block boundary, and for files on filesystems - * without bmap support. + * without get_block support. */ int read_exec(struct dentry *dentry, unsigned long offset, char * addr, unsigned long count, int to_kmem) @@ -804,7 +800,6 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs bprm.dentry = dentry; bprm.filename = filename; bprm.sh_bang = 0; - bprm.java = 0; bprm.loader = 0; bprm.exec = 0; if ((bprm.argc = count(argv)) < 0) { diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index 59f068b5e..cf9e615bd 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -67,7 +67,7 @@ struct inode_operations ext2_dir_inode_operations = { ext2_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ diff --git a/fs/ext2/file.c b/fs/ext2/file.c index c90419ce3..e223ce277 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -37,7 +37,6 @@ #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) -static int ext2_writepage (struct file * file, struct page * page); static long long ext2_file_lseek(struct file *, long long, int); #if BITS_PER_LONG < 64 static int ext2_open_file (struct inode *, struct file *); @@ -106,23 +105,16 @@ static inline void remove_suid(struct inode *inode) } } -static int ext2_writepage (struct file * file, struct page * page) -{ - return block_write_full_page(file, page, ext2_getblk_block); -} - -static long ext2_write_one_page (struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) -{ - return block_write_partial_page(file, page, offset, bytes, buf, ext2_getblk_block); -} - /* * Write to a file (through the page cache). */ static ssize_t ext2_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { - ssize_t retval = generic_file_write(file, buf, count, ppos, ext2_write_one_page); + ssize_t retval; + + retval = generic_file_write(file, buf, count, + ppos, block_write_partial_page); if (retval > 0) { struct inode *inode = file->f_dentry->d_inode; remove_suid(inode); @@ -195,9 +187,9 @@ struct inode_operations ext2_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - ext2_bmap, /* bmap */ + ext2_get_block, /* get_block */ block_read_full_page, /* readpage */ - ext2_writepage, /* writepage */ + block_write_full_page, /* writepage */ block_flushpage, /* flushpage */ ext2_truncate, /* truncate */ ext2_permission, /* permission */ diff --git a/fs/ext2/fsync.c b/fs/ext2/fsync.c index 8ae361e73..3969e17e9 100644 --- a/fs/ext2/fsync.c +++ b/fs/ext2/fsync.c @@ -56,7 +56,7 @@ static int sync_indirect(struct inode * inode, u32 * block, int wait) return 0; } ll_rw_block(WRITE, 1, &bh); - bh->b_count--; + atomic_dec(&bh->b_count); return 0; } diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 02fb5b7b7..171f34a16 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -129,23 +129,22 @@ static int ext2_alloc_block (struct inode * inode, unsigned long goal, int *err) return result; } - -int ext2_bmap (struct inode * inode, int block) +static inline long ext2_block_map (struct inode * inode, long block) { int i, ret; - int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); - int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb); + int ptrs = EXT2_ADDR_PER_BLOCK(inode->i_sb); + int ptrs_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb); ret = 0; lock_kernel(); if (block < 0) { - ext2_warning (inode->i_sb, "ext2_bmap", "block < 0"); + ext2_warning (inode->i_sb, "ext2_block_map", "block < 0"); goto out; } - if (block >= EXT2_NDIR_BLOCKS + addr_per_block + - (1 << (addr_per_block_bits * 2)) + - ((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) { - ext2_warning (inode->i_sb, "ext2_bmap", "block > big"); + if (block >= EXT2_NDIR_BLOCKS + ptrs + + (1 << (ptrs_bits * 2)) + + ((1 << (ptrs_bits * 2)) << ptrs_bits)) { + ext2_warning (inode->i_sb, "ext2_block_map", "block > big"); goto out; } if (block < EXT2_NDIR_BLOCKS) { @@ -153,7 +152,7 @@ int ext2_bmap (struct inode * inode, int block) goto out; } block -= EXT2_NDIR_BLOCKS; - if (block < addr_per_block) { + if (block < ptrs) { i = inode_bmap (inode, EXT2_IND_BLOCK); if (!i) goto out; @@ -161,122 +160,64 @@ int ext2_bmap (struct inode * inode, int block) inode->i_sb->s_blocksize), block); goto out; } - block -= addr_per_block; - if (block < (1 << (addr_per_block_bits * 2))) { + block -= ptrs; + if (block < (1 << (ptrs_bits * 2))) { i = inode_bmap (inode, EXT2_DIND_BLOCK); if (!i) goto out; i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize), - block >> addr_per_block_bits); + block >> ptrs_bits); if (!i) goto out; ret = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize), - block & (addr_per_block - 1)); + block & (ptrs - 1)); + goto out; } - block -= (1 << (addr_per_block_bits * 2)); + block -= (1 << (ptrs_bits * 2)); i = inode_bmap (inode, EXT2_TIND_BLOCK); if (!i) goto out; i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize), - block >> (addr_per_block_bits * 2)); + block >> (ptrs_bits * 2)); if (!i) goto out; i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize), - (block >> addr_per_block_bits) & (addr_per_block - 1)); + (block >> ptrs_bits) & (ptrs - 1)); if (!i) goto out; ret = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize), - block & (addr_per_block - 1)); + block & (ptrs - 1)); out: unlock_kernel(); return ret; } -int ext2_bmap_create (struct inode * inode, int block) -{ - int i; - int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); - int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb); - - if (block < 0) { - ext2_warning (inode->i_sb, "ext2_bmap", "block < 0"); - return 0; - } - if (block >= EXT2_NDIR_BLOCKS + addr_per_block + - (1 << (addr_per_block_bits * 2)) + - ((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) { - ext2_warning (inode->i_sb, "ext2_bmap", "block > big"); - return 0; - } - if (block < EXT2_NDIR_BLOCKS) - return inode_bmap (inode, block); - block -= EXT2_NDIR_BLOCKS; - if (block < addr_per_block) { - i = inode_bmap (inode, EXT2_IND_BLOCK); - if (!i) - return 0; - return block_bmap (bread (inode->i_dev, i, - inode->i_sb->s_blocksize), block); - } - block -= addr_per_block; - if (block < (1 << (addr_per_block_bits * 2))) { - i = inode_bmap (inode, EXT2_DIND_BLOCK); - if (!i) - return 0; - i = block_bmap (bread (inode->i_dev, i, - inode->i_sb->s_blocksize), - block >> addr_per_block_bits); - if (!i) - return 0; - return block_bmap (bread (inode->i_dev, i, - inode->i_sb->s_blocksize), - block & (addr_per_block - 1)); - } - block -= (1 << (addr_per_block_bits * 2)); - i = inode_bmap (inode, EXT2_TIND_BLOCK); - if (!i) - return 0; - i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize), - block >> (addr_per_block_bits * 2)); - if (!i) - return 0; - i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize), - (block >> addr_per_block_bits) & (addr_per_block - 1)); - if (!i) - return 0; - return block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize), - block & (addr_per_block - 1)); -} - static struct buffer_head * inode_getblk (struct inode * inode, int nr, - int create, int new_block, int * err, int metadata, - int *phys_block, int *created) + int new_block, int * err, int metadata, long *phys, int *new) { u32 * p; int tmp, goal = 0; struct buffer_head * result; - int blocks = inode->i_sb->s_blocksize / 512; + int blocksize = inode->i_sb->s_blocksize; p = inode->u.ext2_i.i_data + nr; repeat: tmp = le32_to_cpu(*p); if (tmp) { if (metadata) { - struct buffer_head * result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize); + result = getblk (inode->i_dev, tmp, blocksize); if (tmp == le32_to_cpu(*p)) return result; brelse (result); goto repeat; } else { - *phys_block = tmp; + *phys = tmp; return NULL; } } *err = -EFBIG; - if (!create) - goto dont_create; /* Check file limits.. */ { @@ -285,7 +226,6 @@ repeat: limit >>= EXT2_BLOCK_SIZE_BITS(inode->i_sb); if (new_block >= limit) { send_sig(SIGXFSZ, current, 0); -dont_create: *err = -EFBIG; return NULL; } @@ -313,34 +253,41 @@ dont_create: ext2_debug ("goal = %d.\n", goal); tmp = ext2_alloc_block (inode, goal, err); - if (!tmp) + if (!tmp) { + *err = -ENOSPC; return NULL; + } if (metadata) { - result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize); + result = getblk (inode->i_dev, tmp, blocksize); if (*p) { ext2_free_blocks (inode, tmp, 1); brelse (result); goto repeat; } - memset(result->b_data, 0, inode->i_sb->s_blocksize); + memset(result->b_data, 0, blocksize); mark_buffer_uptodate(result, 1); mark_buffer_dirty(result, 1); } else { if (*p) { + /* + * Nobody is allowed to change block allocation + * state from under us: + */ + BUG(); ext2_free_blocks (inode, tmp, 1); goto repeat; } - *phys_block = tmp; + *phys = tmp; result = NULL; *err = 0; - *created = 1; + *new = 1; } *p = cpu_to_le32(tmp); inode->u.ext2_i.i_next_alloc_block = new_block; inode->u.ext2_i.i_next_alloc_goal = tmp; inode->i_ctime = CURRENT_TIME; - inode->i_blocks += blocks; + inode->i_blocks += blocksize/512; if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) ext2_sync_inode (inode); else @@ -357,24 +304,23 @@ dont_create: * NULL return in the data case is mandatory. */ static struct buffer_head * block_getblk (struct inode * inode, - struct buffer_head * bh, int nr, int create, int blocksize, - int new_block, int * err, int metadata, int *phys_block, int *created) + struct buffer_head * bh, int nr, + int new_block, int * err, int metadata, long *phys, int *new) { int tmp, goal = 0; u32 * p; struct buffer_head * result; - int blocks = inode->i_sb->s_blocksize / 512; + int blocksize = inode->i_sb->s_blocksize; unsigned long limit; - + + result = NULL; if (!bh) - return NULL; + goto out; if (!buffer_uptodate(bh)) { ll_rw_block (READ, 1, &bh); wait_on_buffer (bh); - if (!buffer_uptodate(bh)) { - brelse (bh); - return NULL; - } + if (!buffer_uptodate(bh)) + goto out; } p = (u32 *) bh->b_data + nr; repeat: @@ -382,31 +328,24 @@ repeat: if (tmp) { if (metadata) { result = getblk (bh->b_dev, tmp, blocksize); - if (tmp == le32_to_cpu(*p)) { - brelse (bh); - return result; - } + if (tmp == le32_to_cpu(*p)) + goto out; brelse (result); goto repeat; } else { - *phys_block = tmp; - brelse (bh); - return NULL; + *phys = tmp; + /* result == NULL */ + goto out; } } *err = -EFBIG; - if (!create) { - brelse (bh); - return NULL; - } limit = current->rlim[RLIMIT_FSIZE].rlim_cur; if (limit < RLIM_INFINITY) { limit >>= EXT2_BLOCK_SIZE_BITS(inode->i_sb); if (new_block >= limit) { - brelse (bh); send_sig(SIGXFSZ, current, 0); - return NULL; + goto out; } } @@ -423,10 +362,8 @@ repeat: goal = bh->b_blocknr; } tmp = ext2_alloc_block (inode, goal, err); - if (!tmp) { - brelse (bh); - return NULL; - } + if (!tmp) + goto out; if (metadata) { result = getblk (bh->b_dev, tmp, blocksize); if (*p) { @@ -438,12 +375,10 @@ repeat: mark_buffer_uptodate(result, 1); mark_buffer_dirty(result, 1); } else { - *phys_block = tmp; - result = NULL; - *err = 0; - *created = 1; + *phys = tmp; + *new = 1; } - if (le32_to_cpu(*p)) { + if (*p) { ext2_free_blocks (inode, tmp, 1); brelse (result); goto repeat; @@ -455,117 +390,162 @@ repeat: wait_on_buffer (bh); } inode->i_ctime = CURRENT_TIME; - inode->i_blocks += blocks; + inode->i_blocks += blocksize/512; mark_inode_dirty(inode); inode->u.ext2_i.i_next_alloc_block = new_block; inode->u.ext2_i.i_next_alloc_goal = tmp; + *err = 0; +out: brelse (bh); return result; } -int ext2_getblk_block (struct inode * inode, long block, - int create, int * err, int * created) +int ext2_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create) { - struct buffer_head * bh, *tmp; - unsigned long b; - unsigned long addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); - int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb); - int phys_block, ret; + int ret, err, new; + struct buffer_head *bh; + unsigned long ptr, phys; + /* + * block pointers per block + */ + unsigned long ptrs = EXT2_ADDR_PER_BLOCK(inode->i_sb); + int ptrs_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb); + const int direct_blocks = EXT2_NDIR_BLOCKS, + indirect_blocks = ptrs, + double_blocks = (1 << (ptrs_bits * 2)), + triple_blocks = (1 << (ptrs_bits * 3)); - lock_kernel(); - ret = 0; - *err = -EIO; - if (block < 0) { - ext2_warning (inode->i_sb, "ext2_getblk", "block < 0"); - goto abort; - } - if (block > EXT2_NDIR_BLOCKS + addr_per_block + - (1 << (addr_per_block_bits * 2)) + - ((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) { - ext2_warning (inode->i_sb, "ext2_getblk", "block > big"); - goto abort; + if (!create) { + /* + * Will clean this up further, ext2_block_map() should use the + * bh instead of an integer block-number interface. + */ + phys = ext2_block_map(inode, iblock); + if (phys) { + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = phys; + bh_result->b_state |= (1UL << BH_Mapped); + } + return 0; } + + err = -EIO; + new = 0; + ret = 0; + bh = NULL; + + lock_kernel(); + + if (iblock < 0) + goto abort_negative; + if (iblock > direct_blocks + indirect_blocks + + double_blocks + triple_blocks) + goto abort_too_big; + /* * If this is a sequential block allocation, set the next_alloc_block * to this block now so that all the indblock and data block * allocations use the same goal zone */ - ext2_debug ("block %lu, next %lu, goal %lu.\n", block, + ext2_debug ("block %lu, next %lu, goal %lu.\n", iblock, inode->u.ext2_i.i_next_alloc_block, inode->u.ext2_i.i_next_alloc_goal); - if (block == inode->u.ext2_i.i_next_alloc_block + 1) { + if (iblock == inode->u.ext2_i.i_next_alloc_block + 1) { inode->u.ext2_i.i_next_alloc_block++; inode->u.ext2_i.i_next_alloc_goal++; } - *err = 0; // -ENOSPC; - b = block; - *created = 0; - if (block < EXT2_NDIR_BLOCKS) { - /* - * data page. - */ - tmp = inode_getblk (inode, block, create, b, - err, 0, &phys_block, created); - goto out; - } - block -= EXT2_NDIR_BLOCKS; - if (block < addr_per_block) { - bh = inode_getblk (inode, EXT2_IND_BLOCK, create, b, err, 1, NULL, NULL); - tmp = block_getblk (inode, bh, block, create, - inode->i_sb->s_blocksize, b, err, 0, &phys_block, created); - goto out; - } - block -= addr_per_block; - if (block < (1 << (addr_per_block_bits * 2))) { - bh = inode_getblk (inode, EXT2_DIND_BLOCK, create, b, err, 1, NULL, NULL); - bh = block_getblk (inode, bh, block >> addr_per_block_bits, - create, inode->i_sb->s_blocksize, b, err, 1, NULL, NULL); - tmp = block_getblk (inode, bh, block & (addr_per_block - 1), - create, inode->i_sb->s_blocksize, b, err, 0, &phys_block, created); + err = 0; + ptr = iblock; + + /* + * ok, these macros clean the logic up a bit and make + * it much more readable: + */ +#define GET_INODE_DATABLOCK(x) \ + inode_getblk(inode, x, iblock, &err, 0, &phys, &new) +#define GET_INODE_PTR(x) \ + inode_getblk(inode, x, iblock, &err, 1, NULL, NULL) +#define GET_INDIRECT_DATABLOCK(x) \ + block_getblk (inode, bh, x, iblock, &err, 0, &phys, &new); +#define GET_INDIRECT_PTR(x) \ + block_getblk (inode, bh, x, iblock, &err, 1, NULL, NULL); + + if (ptr < direct_blocks) { + bh = GET_INODE_DATABLOCK(ptr); goto out; } - block -= (1 << (addr_per_block_bits * 2)); - bh = inode_getblk (inode, EXT2_TIND_BLOCK, create, b, err, 1, NULL,NULL); - bh = block_getblk (inode, bh, block >> (addr_per_block_bits * 2), - create, inode->i_sb->s_blocksize, b, err, 1, NULL,NULL); - bh = block_getblk (inode, bh, (block >> addr_per_block_bits) & - (addr_per_block - 1), create, inode->i_sb->s_blocksize, - b, err, 1, NULL,NULL); - tmp = block_getblk (inode, bh, block & (addr_per_block - 1), create, - inode->i_sb->s_blocksize, b, err, 0, &phys_block, created); + ptr -= direct_blocks; + if (ptr < indirect_blocks) { + bh = GET_INODE_PTR(EXT2_IND_BLOCK); + goto get_indirect; + } + ptr -= indirect_blocks; + if (ptr < double_blocks) { + bh = GET_INODE_PTR(EXT2_DIND_BLOCK); + goto get_double; + } + ptr -= double_blocks; + bh = GET_INODE_PTR(EXT2_TIND_BLOCK); + bh = GET_INDIRECT_PTR(ptr >> (ptrs_bits * 2)); +get_double: + bh = GET_INDIRECT_PTR((ptr >> ptrs_bits) & (ptrs - 1)); +get_indirect: + bh = GET_INDIRECT_DATABLOCK(ptr & (ptrs - 1)); + +#undef GET_INODE_DATABLOCK +#undef GET_INODE_PTR +#undef GET_INDIRECT_DATABLOCK +#undef GET_INDIRECT_PTR out: - if (!phys_block) - goto abort; - if (*err) + if (bh) + BUG(); // temporary debugging check + if (err) goto abort; - ret = phys_block; + if (!phys) + BUG(); // must not happen either + + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = phys; + bh_result->b_state |= (1UL << BH_Mapped); /* safe */ + if (new) + bh_result->b_state |= (1UL << BH_New); abort: unlock_kernel(); - return ret; + return err; + +abort_negative: + ext2_warning (inode->i_sb, "ext2_get_block", "block < 0"); + goto abort; + +abort_too_big: + ext2_warning (inode->i_sb, "ext2_get_block", "block > big"); + goto abort; } -struct buffer_head * ext2_getblk (struct inode * inode, long block, - int create, int * err) +struct buffer_head * ext2_getblk(struct inode * inode, long block, int create, int * err) { - struct buffer_head *tmp = NULL; - int phys_block; - int created; - - phys_block = ext2_getblk_block (inode, block, create, err, &created); - - if (phys_block) { - tmp = getblk (inode->i_dev, phys_block, inode->i_sb->s_blocksize); - if (created) { - memset(tmp->b_data, 0, inode->i_sb->s_blocksize); - mark_buffer_uptodate(tmp, 1); - mark_buffer_dirty(tmp, 1); + struct buffer_head dummy; + int error; + + dummy.b_state = 0; + dummy.b_blocknr = -1000; + error = ext2_get_block(inode, block, &dummy, create); + *err = error; + if (!error && buffer_mapped(&dummy)) { + struct buffer_head *bh; + bh = getblk(dummy.b_dev, dummy.b_blocknr, inode->i_sb->s_blocksize); + if (buffer_new(&dummy)) { + memset(bh->b_data, 0, inode->i_sb->s_blocksize); + mark_buffer_uptodate(bh, 1); + mark_buffer_dirty(bh, 1); } + return bh; } - return tmp; + return NULL; } struct buffer_head * ext2_bread (struct inode * inode, int block, diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 885929f70..9666118ee 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -555,7 +555,7 @@ static int empty_dir (struct inode * inode) while (offset < inode->i_size ) { if (!bh || (void *) de >= (void *) (bh->b_data + sb->s_blocksize)) { brelse (bh); - bh = ext2_bread (inode, offset >> EXT2_BLOCK_SIZE_BITS(sb), 1, &err); + bh = ext2_bread (inode, offset >> EXT2_BLOCK_SIZE_BITS(sb), 0, &err); if (!bh) { #if 0 ext2_error (sb, "empty_dir", diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c index b0ebcb91b..5f73159c3 100644 --- a/fs/ext2/symlink.c +++ b/fs/ext2/symlink.c @@ -43,7 +43,7 @@ struct inode_operations ext2_symlink_inode_operations = { NULL, /* rename */ ext2_readlink, /* readlink */ ext2_follow_link, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c index b6f57efb0..d824edb0b 100644 --- a/fs/ext2/truncate.c +++ b/fs/ext2/truncate.c @@ -129,7 +129,7 @@ static int check_block_empty(struct inode *inode, struct buffer_head *bh, if (*(ind++)) goto in_use; - if (bh->b_count == 1) { + if (atomic_read(&bh->b_count) == 1) { int tmp; tmp = le32_to_cpu(*p); *p = 0; @@ -141,7 +141,7 @@ static int check_block_empty(struct inode *inode, struct buffer_head *bh, bforget(bh); if (ind_bh) mark_buffer_dirty(ind_bh, 1); - ext2_free_blocks (inode, tmp, 1); + ext2_free_blocks(inode, tmp, 1); goto out; } retry = 1; @@ -158,11 +158,10 @@ out: } #define DATA_BUFFER_USED(bh) \ - (bh->b_count || buffer_locked(bh)) + (atomic_read(&bh->b_count) || buffer_locked(bh)) static int trunc_direct (struct inode * inode) { - struct buffer_head * bh; int i, retry = 0; unsigned long block_to_free = 0, free_count = 0; int blocks = inode->i_sb->s_blocksize / 512; @@ -175,19 +174,9 @@ static int trunc_direct (struct inode * inode) if (!tmp) continue; - bh = find_buffer(inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (bh) { - if (DATA_BUFFER_USED(bh)) { - retry = 1; - continue; - } - bh->b_count++; - } - *p = 0; inode->i_blocks -= blocks; mark_inode_dirty(inode); - bforget(bh); /* accumulate blocks to free if they're contiguous */ if (free_count == 0) @@ -206,8 +195,7 @@ static int trunc_direct (struct inode * inode) return retry; } -static int trunc_indirect (struct inode * inode, int offset, u32 * p, - struct buffer_head *dind_bh) +static int trunc_indirect (struct inode * inode, int offset, u32 * p, struct buffer_head *dind_bh) { struct buffer_head * ind_bh; int i, tmp, retry = 0; @@ -242,28 +230,15 @@ static int trunc_indirect (struct inode * inode, int offset, u32 * p, indirect_block = 0; for (i = indirect_block ; i < addr_per_block ; i++) { u32 * ind = i + (u32 *) ind_bh->b_data; - struct buffer_head * bh; wait_on_buffer(ind_bh); tmp = le32_to_cpu(*ind); if (!tmp) continue; - /* - * Use find_buffer so we don't block here. - */ - bh = find_buffer(inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (bh) { - if (DATA_BUFFER_USED(bh)) { - retry = 1; - continue; - } - bh->b_count++; - } *ind = 0; inode->i_blocks -= blocks; mark_inode_dirty(inode); - bforget(bh); mark_buffer_dirty(ind_bh, 1); /* accumulate blocks to free if they're contiguous */ diff --git a/fs/fat/file.c b/fs/fat/file.c index 7c9518181..5d964da2b 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -57,12 +57,14 @@ struct inode_operations fat_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - generic_readpage, /* readpage */ + fat_bmap, /* get_block */ + block_read_full_page, /* readpage */ NULL, /* writepage */ - fat_bmap, /* bmap */ + NULL, /* flushpage */ fat_truncate, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; /* #Specification: msdos / special devices / mmap @@ -107,12 +109,14 @@ struct inode_operations fat_file_inode_operations_1024 = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - generic_readpage, /* readpage */ + NULL, /* get_block */ + block_read_full_page, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ fat_truncate, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; static struct file_operations fat_file_operations_readpage = { @@ -142,12 +146,14 @@ struct inode_operations fat_file_inode_operations_readpage = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ fat_readpage, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ fat_truncate, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; #define MSDOS_PREFETCH 32 @@ -179,7 +179,7 @@ struct inode_operations fifo_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ diff --git a/fs/file_table.c b/fs/file_table.c index 45b68daec..80c3a08ba 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -80,7 +80,7 @@ struct file * get_empty_filp(void) nr_free_files--; new_one: memset(f, 0, sizeof(*f)); - f->f_count = 1; + atomic_set(&f->f_count, 1); f->f_version = ++event; f->f_uid = current->fsuid; f->f_gid = current->fsgid; @@ -120,7 +120,7 @@ int init_private_file(struct file *filp, struct dentry *dentry, int mode) { memset(filp, 0, sizeof(*filp)); filp->f_mode = mode; - filp->f_count = 1; + atomic_set(&filp->f_count, 1); filp->f_dentry = dentry; filp->f_uid = current->fsuid; filp->f_gid = current->fsgid; @@ -133,21 +133,17 @@ int init_private_file(struct file *filp, struct dentry *dentry, int mode) void fput(struct file *file) { - int count = file->f_count-1; - - if (!count) { + if (atomic_dec_and_test(&file->f_count)) { locks_remove_flock(file); __fput(file); - file->f_count = 0; remove_filp(file); insert_file_free(file); - } else - file->f_count = count; + } } void put_filp(struct file *file) { - if(--file->f_count == 0) { + if (atomic_dec_and_test(&file->f_count)) { remove_filp(file); insert_file_free(file); } diff --git a/fs/hfs/dir_cap.c b/fs/hfs/dir_cap.c index 189b0107e..a5ce908a7 100644 --- a/fs/hfs/dir_cap.c +++ b/fs/hfs/dir_cap.c @@ -87,12 +87,14 @@ struct inode_operations hfs_cap_ndir_inode_operations = { hfs_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; struct inode_operations hfs_cap_fdir_inode_operations = { @@ -108,12 +110,14 @@ struct inode_operations hfs_cap_fdir_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; struct inode_operations hfs_cap_rdir_inode_operations = { @@ -129,12 +133,14 @@ struct inode_operations hfs_cap_rdir_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; /*================ File-local functions ================*/ diff --git a/fs/hfs/dir_dbl.c b/fs/hfs/dir_dbl.c index 17e53798a..d68f7ed6a 100644 --- a/fs/hfs/dir_dbl.c +++ b/fs/hfs/dir_dbl.c @@ -86,12 +86,14 @@ struct inode_operations hfs_dbl_dir_inode_operations = { dbl_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; diff --git a/fs/hfs/dir_nat.c b/fs/hfs/dir_nat.c index 21d4ca9af..aa8a90220 100644 --- a/fs/hfs/dir_nat.c +++ b/fs/hfs/dir_nat.c @@ -93,9 +93,10 @@ struct inode_operations hfs_nat_ndir_inode_operations = { hfs_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ @@ -115,9 +116,10 @@ struct inode_operations hfs_nat_hdir_inode_operations = { nat_hdr_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ diff --git a/fs/hfs/file.c b/fs/hfs/file.c index d3796e275..6b01cd9e0 100644 --- a/fs/hfs/file.c +++ b/fs/hfs/file.c @@ -63,9 +63,10 @@ struct inode_operations hfs_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - generic_readpage, /* readpage */ + hfs_bmap, /* get_block */ + block_read_full_page, /* readpage */ NULL, /* writepage */ - hfs_bmap, /* bmap */ + NULL, /* flushpage */ hfs_file_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ diff --git a/fs/hfs/file_cap.c b/fs/hfs/file_cap.c index 789073d19..e96dd7f20 100644 --- a/fs/hfs/file_cap.c +++ b/fs/hfs/file_cap.c @@ -77,9 +77,10 @@ struct inode_operations hfs_cap_info_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block - none */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap - none */ + NULL, /* flushpage */ cap_info_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ diff --git a/fs/hfs/file_hdr.c b/fs/hfs/file_hdr.c index d112b3498..58a12133a 100644 --- a/fs/hfs/file_hdr.c +++ b/fs/hfs/file_hdr.c @@ -78,10 +78,11 @@ struct inode_operations hfs_hdr_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block - XXX: not available since + header part has no disk block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap - XXX: not available since - header part has no disk block */ + NULL, /* flushpage */ hdr_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ diff --git a/fs/hpfs/Makefile b/fs/hpfs/Makefile index 46f1ffae0..2ebaece02 100644 --- a/fs/hpfs/Makefile +++ b/fs/hpfs/Makefile @@ -1,5 +1,5 @@ O_TARGET := hpfs.o -O_OBJS := alloc.o anode.o buffer.o dentry.o dir.o dnode.o ea.o file.o inode.o map.o mmap.o name.o namei.o super.o +O_OBJS := alloc.o anode.o buffer.o dentry.o dir.o dnode.o ea.o file.o inode.o map.o name.o namei.o super.o M_OBJS := $(O_TARGET) include $(TOPDIR)/Rules.make diff --git a/fs/hpfs/anode.c b/fs/hpfs/anode.c index f2c4a3532..62410ca26 100644 --- a/fs/hpfs/anode.c +++ b/fs/hpfs/anode.c @@ -301,13 +301,20 @@ void hpfs_remove_btree(struct super_block *s, struct bplus_header *btree) anode = hpfs_map_anode(s, ano, &bh); btree1 = &anode->btree; } else btree1 = btree; - for (i = 0; i < btree1->n_used_nodes; i++) - if (btree1->u.internal[i].down == oano) - if ((pos = i + 1) < btree1->n_used_nodes) goto go_down; - else goto go_up; - hpfs_error(s, "reference to anode %08x not found in anode %08x (probably bad up pointer)", - oano, level ? ano : -1); - if (level) brelse(bh); + for (i = 0; i < btree1->n_used_nodes; i++) { + if (btree1->u.internal[i].down == oano) { + if ((pos = i + 1) < btree1->n_used_nodes) + goto go_down; + else + goto go_up; + } + } + hpfs_error(s, + "reference to anode %08x not found in anode %08x " + "(probably bad up pointer)", + oano, level ? ano : -1); + if (level) + brelse(bh); } /* Just a wrapper around hpfs_bplus_lookup .. used for reading eas */ diff --git a/fs/hpfs/ea.c b/fs/hpfs/ea.c index 6dac91fd8..d7abdc991 100644 --- a/fs/hpfs/ea.c +++ b/fs/hpfs/ea.c @@ -233,10 +233,10 @@ void hpfs_set_ea(struct inode *inode, struct fnode *fnode, char *key, char *data goto bail; fnode->ea_anode = 0; len++; - } - else if (!fnode->ea_anode) - if (hpfs_alloc_if_possible(s, fnode->ea_secno + len)) len++; - else { + } else if (!fnode->ea_anode) { + if (hpfs_alloc_if_possible(s, fnode->ea_secno + len)) { + len++; + } else { /* Aargh... don't know how to create ea anodes :-( */ /*struct buffer_head *bh; struct anode *anode; @@ -280,10 +280,15 @@ void hpfs_set_ea(struct inode *inode, struct fnode *fnode, char *key, char *data fnode->ea_secno = new_sec; len = (pos + 511) >> 9; } - if (fnode->ea_anode) - if (hpfs_add_sector_to_btree(s, fnode->ea_secno, 0, len) != -1) + } + if (fnode->ea_anode) { + if (hpfs_add_sector_to_btree(s, fnode->ea_secno, + 0, len) != -1) { len++; - else goto bail; + } else { + goto bail; + } + } } h[0] = 0; h[1] = strlen(key); diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c index 7cea8dcdc..fbb1f2f6c 100644 --- a/fs/hpfs/file.c +++ b/fs/hpfs/file.c @@ -53,143 +53,101 @@ void hpfs_truncate(struct inode *i) { if (IS_IMMUTABLE(i)) return /*-EPERM*/; i->i_hpfs_n_secs = 0; - hpfs_truncate_btree(i->i_sb, i->i_ino, 1, ((i->i_size + 511) >> 9)); i->i_blocks = 1 + ((i->i_size + 511) >> 9); + hpfs_truncate_btree(i->i_sb, i->i_ino, 1, ((i->i_size + 511) >> 9)); hpfs_write_inode(i); } -ssize_t hpfs_file_read(struct file *filp, char *buf, size_t count, loff_t *ppos) +int hpfs_getblk_block(struct inode *inode, long block, int create, int *err, int *created) { - struct inode *inode = filp->f_dentry->d_inode; - int i,j; - int a = generic_file_read(filp, buf, count, ppos); - if (inode->i_hpfs_conv != CONV_TEXT || a < 0) { - return a; - } - for (i = 0, j = 0; i < a; i++) { - char c; - int error; - if ((error = get_user(c, buf + i))) return error; - if (c != '\r') { - if (i != j) put_user(c, buf + j); - j++; + int add; + int sec = 0; + down(&inode->i_sem); + if (err) *err = 0; + if (created) *created = 0; + if (!inode->i_blocks) { + hpfs_error(inode->i_sb, "hpfs_get_block: inode %08x has no blocks", inode->i_ino); + if (err) *err = -EFSERROR; + up(&inode->i_sem); + return 0; + } + if (block < ((add = inode->i_blocks - 1))) { + int bm; + if (!(bm = hpfs_bmap(inode, block))) { + hpfs_error(inode->i_sb, "hpfs_get_block: cound not bmap block %08x, inode %08x, size %08x", (int)block, inode->i_ino, (int)inode->i_size); + *err = -EFSERROR; } + up(&inode->i_sem); + return bm; } - return j; + if (!create) { + if (err) *err = -EFBIG; + up(&inode->i_sem); + return 0; + } + if (created) *created = 1; + while (add <= block) { + if ((sec = hpfs_add_sector_to_btree(inode->i_sb, inode->i_ino, 1, add)) == -1) { + if (err) *err = -ENOSPC; + hpfs_truncate_btree(inode->i_sb, inode->i_ino, 1, inode->i_blocks - 1); + return 0; + } /* FIXME: clear block */ + add++; + } + inode->i_blocks = add + 1; + up(&inode->i_sem); + return sec; } -ssize_t hpfs_file_write(struct file *filp, const char *buf, size_t count, - loff_t *ppos) +/* copied from ext2fs */ +static int hpfs_get_block(struct inode *inode, unsigned long block, struct buffer_head *bh, int update) { - struct inode *i = filp->f_dentry->d_inode; - int carry, error = 0; - const char *start = buf; - if (!i) return -EINVAL; - if (!S_ISREG(i->i_mode)) return -EINVAL; - if (IS_IMMUTABLE(i)) return -EPERM; - if (filp->f_flags & O_APPEND) *ppos = i->i_size; - if (count <= 0) return 0; - if ((unsigned)(*ppos+count) >= 0x80000000U || (unsigned)count >= 0x80000000U) return -EFBIG; - carry = 0; - while (count || carry) { - int ii, add = 0; - secno sec = 0; /* Go away, uninitialized variable warning */ - int offset, size, written; - char ch; - struct buffer_head *bh; - char *data; - offset = *ppos & 0x1ff; - size = count > 0x200 - offset ? 0x200 - offset : count; - if ((*ppos >> 9) < ((i->i_size + 0x1ff) >> 9)) { - i->i_hpfs_n_secs = 0; - if (!(sec = hpfs_bmap(i, *ppos >> 9))) { - hpfs_error(i->i_sb, "bmap failed, file %08x, fsec %08x", - i->i_ino, *ppos >> 9); - error =- EFSERROR; - break; - } - } else for (ii = (i->i_size + 0x1ff) >> 9, add = 1; ii <= *ppos >> 9; ii++) { - if ((sec = hpfs_add_sector_to_btree(i->i_sb, i->i_ino, 1, ii)) == -1) { - hpfs_truncate(i); - return -ENOSPC; - } - if (*ppos != i->i_size) - if ((data = hpfs_get_sector(i->i_sb, sec, &bh))) { - memset(data, 0, 512); - mark_buffer_dirty(bh, 0); - brelse(bh); - } - i->i_size = 0x200 * ii + 1; - i->i_blocks++; - /*mark_inode_dirty(i);*/i->i_hpfs_dirty = 1; - if (i->i_sb->s_hpfs_chk >= 2) { - secno bsec; - bsec = hpfs_bmap(i, ii); - if (sec != bsec) { - hpfs_error(i->i_sb, "sec == %08x, bmap returns %08x", sec, bsec); - error = -EFSERROR; - break; - } - } - PRINTK(("file_write: added %08x\n", sec)); - } - if (!sec || sec == 15) { - hpfs_error(i->i_sb, "bmap returned empty sector"); - error = -EFSERROR; - break; - } - if (i->i_sb->s_hpfs_chk) - if (hpfs_chk_sectors(i->i_sb, sec, 1, "data")) { - error = -EFSERROR; - break; - } - if ((!offset && size == 0x200) || add) - data = hpfs_get_sector(i->i_sb, sec, &bh); - else data = hpfs_map_sector(i->i_sb, sec, &bh, 0); - if (!data) { - error = -EIO; - break; - } - if (i->i_hpfs_conv != CONV_TEXT) { - memcpy_fromfs(data + offset, buf, written = size); - buf += size; - } else { - int left; - char *to; - /* LF->CR/LF conversion, stolen from fat fs */ - written = left = 0x200 - offset; - to = (char *) bh->b_data + (*ppos & 0x1ff); - if (carry) { - *to++ = '\n'; - left--; - carry = 0; - } - for (size = 0; size < count && left; size++) { - if ((error = get_user(ch, buf++))) break; - if (ch == '\n') { - *to++ = '\r'; - left--; - } - if (!left) carry = 1; - else { - *to++ = ch; - left--; - } - } - written -= left; + if (!bh->b_blocknr) { + int error, created; + unsigned long blocknr; + + blocknr = hpfs_getblk_block(inode, block, 1, &error, &created); + if (!blocknr) { + if (!error) + error = -ENOSPC; + return error; } - update_vm_cache(i, *ppos, bh->b_data + (*ppos & 0x1ff), written); - *ppos += written; - if (*ppos > i->i_size) { - i->i_size = *ppos; - /*mark_inode_dirty(i);*/i->i_hpfs_dirty = 1; + + bh->b_dev = inode->i_dev; + bh->b_blocknr = blocknr; + + if (!update) + return 0; + + if (created) { + memset(bh->b_data, 0, bh->b_size); + set_bit(BH_Uptodate, &bh->b_state); + return 0; } - mark_buffer_dirty(bh, 0); - brelse(bh); - count -= size; } - if (start == buf) return error; - i->i_mtime = CURRENT_TIME; - /*mark_inode_dirty(i);*/i->i_hpfs_dirty = 1; - return buf - start; + + if (!update) + return 0; + + lock_kernel(); + ll_rw_block(READ, 1, &bh); + wait_on_buffer(bh); + unlock_kernel(); + + return buffer_uptodate(bh) ? 0 : -EIO; +} + +ssize_t hpfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + ssize_t retval; + + retval = generic_file_write(file, buf, count, + ppos, block_write_partial_page); + if (retval > 0) { + struct inode *inode = file->f_dentry->d_inode; + inode->i_mtime = CURRENT_TIME; + inode->i_hpfs_dirty = 1; + } + return retval; } + diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h index cdb2c709e..050b63597 100644 --- a/fs/hpfs/hpfs_fn.h +++ b/fs/hpfs/hpfs_fn.h @@ -21,6 +21,7 @@ #include <asm/bitops.h> #include <asm/segment.h> #include <asm/uaccess.h> +#include <linux/smp_lock.h> #include <stdarg.h> @@ -259,6 +260,7 @@ secno hpfs_bmap(struct inode *, unsigned); void hpfs_truncate(struct inode *); ssize_t hpfs_file_read(struct file *, char *, size_t, loff_t *); ssize_t hpfs_file_write(struct file *, const char *, size_t, loff_t *); +int hpfs_writepage (struct file *, struct page *); /* inode.c */ diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c index 17984d667..efc776218 100644 --- a/fs/hpfs/inode.c +++ b/fs/hpfs/inode.c @@ -11,7 +11,7 @@ static const struct file_operations hpfs_file_ops = { NULL, /* lseek - default */ - hpfs_file_read, /* read */ + generic_file_read, /* read */ hpfs_file_write, /* write */ NULL, /* readdir - bad */ NULL, /* poll - default */ @@ -41,11 +41,13 @@ static const struct inode_operations hpfs_file_iops = NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - generic_readpage, /* readpage */ - NULL, /* writepage */ (int (*)(struct inode *, int)) - &hpfs_bmap, /* bmap */ - &hpfs_truncate, /* truncate */ +#warning Someone needs to code up hpfs_get_block properly... -DaveM + &hpfs_bmap, /* get_block */ + block_read_full_page, /* readpage */ + hpfs_writepage, /* writepage */ + block_flushpage, /* flushpage */ + hpfs_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ NULL, /* revalidate */ @@ -84,13 +86,14 @@ static const struct inode_operations hpfs_dir_iops = hpfs_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* revalidate */ + NULL /* revalidate */ }; const struct inode_operations hpfs_symlink_iops = @@ -107,13 +110,14 @@ const struct inode_operations hpfs_symlink_iops = NULL, /* rename */ hpfs_readlink, /* readlink */ hpfs_follow_link, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* revalidate */ + NULL /* revalidate */ }; @@ -125,7 +129,6 @@ void hpfs_read_inode(struct inode *i) unsigned char *ea; int ea_size; i->i_op = 0; - /*i->i_hpfs_sem = MUTEX;*/ init_MUTEX(&i->i_hpfs_sem); i->i_uid = sb->s_hpfs_uid; i->i_gid = sb->s_hpfs_gid; diff --git a/fs/hpfs/mmap.c b/fs/hpfs/mmap.c deleted file mode 100644 index 3fd544664..000000000 --- a/fs/hpfs/mmap.c +++ /dev/null @@ -1,128 +0,0 @@ -/* - * linux/fs/hpfs/mmap.c - * - * taken from fat filesystem - * - * Written by Jacques Gelinas (jacques@solucorp.qc.ca) - * Inspired by fs/nfs/mmap.c (Jon Tombs 15 Aug 1993) - * - * Modified for HPFS by Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz) - * - * mmap handling for hpfs filesystem - * (generic_file_mmap may be used only on filesystems that keep zeros - * in last file sector beyond end) - */ - -/* - * generic_file_mmap doesn't erase the space beyond file end in last sector. :-( - * Hpfs doesn't keep zeros in last sector. This causes problems with kernel - * mkdep.c and probably other programs. Additionally this could be a security - * hole - some interesting data, like pieces of /etc/shadow could be found - * beyond file end. - * - * So, I can't use generic mmap. mmap from fat filesystem looks good, so I used - * it. - * - * BTW. fat uses generic mmap on normal disks. Doesn't it also have above bugs? - * I don't think Msdos erases space in last sector. - * - * If you fix generic_file_mmap, you can remove this file and use it. - */ - -#include "hpfs_fn.h" - -/* - * Fill in the supplied page for mmap - */ - -static unsigned long hpfs_file_mmap_nopage( - struct vm_area_struct * area, - unsigned long address, - int error_code) -{ - /*struct inode * inode = area->vm_inode;*/ - struct inode * inode = area->vm_file->f_dentry->d_inode; - unsigned long page; - unsigned int clear; - loff_t pos; - long gap; /* distance from eof to pos */ - - page = __get_free_page(GFP_KERNEL); - if (!page) - return page; - address &= PAGE_MASK; - pos = address - area->vm_start + area->vm_offset; - - clear = 0; - gap = inode->i_size - pos; - if (gap <= 0){ - /* mmaping beyond end of file */ - clear = PAGE_SIZE; - }else{ - int cur_read; - int need_read; - /*struct file *filp = area->vm_file;*/ - struct file filp; - if (gap < PAGE_SIZE){ - clear = PAGE_SIZE - gap; - } - filp.f_reada = 0; - filp.f_pos = pos; - filp.f_dentry=area->vm_file->f_dentry; - need_read = PAGE_SIZE - clear; - { - mm_segment_t cur_fs = get_fs(); - set_fs (KERNEL_DS); - cur_read = generic_file_read (&filp,(char*)page - ,need_read,&pos); - set_fs (cur_fs); - } - if (cur_read != need_read){ - hpfs_error(inode->i_sb, "Error while reading an mmap file %08x", inode->i_ino); - } - } - if (clear > 0){ - memset ((char*)page+PAGE_SIZE-clear,0,clear); - } - return page; -} - -struct vm_operations_struct hpfs_file_mmap = { - NULL, /* open */ - NULL, /* close */ - NULL, /* unmap */ - NULL, /* protect */ - NULL, /* sync */ - NULL, /* advise */ - hpfs_file_mmap_nopage, /* nopage */ - NULL, /* wppage */ - NULL, /* swapout */ - NULL, /* swapin */ -}; - -/* - * This is used for a general mmap of an msdos file - * Returns 0 if ok, or a negative error code if not. - */ -int hpfs_mmap(struct file * file, struct vm_area_struct * vma) -{ - struct inode *inode = file->f_dentry->d_inode; - /*printk("start mmap\n");*/ - if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */ - return -EINVAL; - if (vma->vm_offset & (inode->i_sb->s_blocksize - 1)) - return -EINVAL; - if (!inode->i_sb || !S_ISREG(inode->i_mode)) - return -EACCES; - /*if (!IS_RDONLY(inode)) { - inode->i_atime = CURRENT_TIME; - mark_inode_dirty(inode); - }*/ - - vma->vm_file = file; - /*inode->i_count++;*/ - file->f_count++; - vma->vm_ops = &hpfs_file_mmap; - /*printk("end mmap\n");*/ - return 0; -} diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c index a50f2a49b..c6291c83a 100644 --- a/fs/hpfs/super.c +++ b/fs/hpfs/super.c @@ -283,7 +283,7 @@ int parse_opts(char *opts, uid_t *uid, gid_t *gid, umode_t *umask, return 1; } -static inline void hpfs_help() +static inline void hpfs_help(void) { printk("\n\ HPFS filesystem options:\n\ diff --git a/fs/inode.c b/fs/inode.c index ba9cc7a78..01fc64d23 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -231,7 +231,7 @@ void write_inode_now(struct inode *inode) void clear_inode(struct inode *inode) { if (inode->i_nrpages) - truncate_inode_pages(inode, 0); + BUG(); wait_on_inode(inode); if (IS_QUOTAINIT(inode)) DQUOT_DROP(inode); @@ -261,6 +261,8 @@ static void dispose_list(struct list_head * head) if (tmp == head) break; inode = list_entry(tmp, struct inode, i_list); + if (inode->i_nrpages) + truncate_inode_pages(inode, 0); clear_inode(inode); count++; } @@ -735,6 +737,8 @@ void iput(struct inode *inode) if (op && op->delete_inode) { void (*delete)(struct inode *) = op->delete_inode; spin_unlock(&inode_lock); + if (inode->i_nrpages) + truncate_inode_pages(inode, 0); delete(inode); spin_lock(&inode_lock); } @@ -778,8 +782,14 @@ kdevname(inode->i_dev), inode->i_ino, atomic_read(&inode->i_sem.count)); int bmap(struct inode * inode, int block) { - if (inode->i_op && inode->i_op->bmap) - return inode->i_op->bmap(inode, block); + struct buffer_head tmp; + + if (inode->i_op && inode->i_op->get_block) { + tmp.b_state = 0; + tmp.b_blocknr = 0; + inode->i_op->get_block(inode, block, &tmp, 0); + return tmp.b_blocknr; + } return 0; } diff --git a/fs/ioctl.c b/fs/ioctl.c index 8d18e453a..e9f8e09b6 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -18,14 +18,21 @@ static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg) switch (cmd) { case FIBMAP: + { + struct buffer_head tmp; + if (inode->i_op == NULL) return -EBADF; - if (inode->i_op->bmap == NULL) + if (inode->i_op->get_block == NULL) return -EINVAL; if ((error = get_user(block, (int *) arg)) != 0) return error; - block = inode->i_op->bmap(inode,block); - return put_user(block, (int *) arg); + + tmp.b_state = 0; + tmp.b_blocknr = 0; + inode->i_op->get_block(inode, block, &tmp, 0); + return put_user(tmp.b_blocknr, (int *) arg); + } case FIGETBSZ: if (inode->i_sb == NULL) return -EBADF; diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index 23ca159c7..bc64dfdd5 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -57,11 +57,14 @@ struct inode_operations isofs_dir_inode_operations = NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; static int isofs_name_translate(char * old, int len, char * new) diff --git a/fs/isofs/file.c b/fs/isofs/file.c index ce85b367a..fd9b124ed 100644 --- a/fs/isofs/file.c +++ b/fs/isofs/file.c @@ -48,10 +48,12 @@ struct inode_operations isofs_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - isofs_bmap, /* bmap */ + isofs_get_block, /* get_block */ block_read_full_page, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 01d37a849..1cf86ae63 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -55,7 +55,7 @@ static int isofs_cmpi_ms(struct dentry *dentry, struct qstr *a, struct qstr *b); static int isofs_cmp_ms(struct dentry *dentry, struct qstr *a, struct qstr *b); #endif -void isofs_put_super(struct super_block *sb) +static void isofs_put_super(struct super_block *sb) { #ifdef CONFIG_JOLIET if (sb->u.isofs_sb.s_nls_iocharset) { @@ -73,6 +73,9 @@ void isofs_put_super(struct super_block *sb) return; } +static void isofs_read_inode(struct inode *); +static int isofs_statfs (struct super_block *, struct statfs *, int); + static struct super_operations isofs_sops = { isofs_read_inode, NULL, /* write_inode */ @@ -487,8 +490,8 @@ static unsigned int isofs_get_last_session(kdev_t dev,s32 session ) * Note: a check_disk_change() has been done immediately prior * to this call, so we don't need to check again. */ -struct super_block *isofs_read_super(struct super_block *s, void *data, - int silent) +static struct super_block *isofs_read_super(struct super_block *s, void *data, + int silent) { kdev_t dev = s->s_dev; struct buffer_head * bh = NULL, *pri_bh = NULL; @@ -894,7 +897,7 @@ out_unlock: return NULL; } -int isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz) +static int isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz) { struct statfs tmp; @@ -910,96 +913,115 @@ int isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz) return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } -static int do_isofs_bmap(struct inode * inode,int block) +/* Life is simpler than for other filesystem since we never + * have to create a new block, only find an existing one. + */ +int isofs_get_block(struct inode *inode, long iblock, + struct buffer_head *bh_result, int create) { - off_t b_off, offset, size; - struct inode *ino; + off_t b_off, offset, sect_size; unsigned int firstext; unsigned long nextino; - int i; + int i, err; - if (block<0) { - printk("_isofs_bmap: block<0"); - return 0; - } + lock_kernel(); - b_off = block << ISOFS_BUFFER_BITS(inode); + err = -EROFS; + if (create) + goto abort_create_attempted; - /* - * If we are beyond the end of this file, don't give out any + err = -EIO; + if (iblock < 0) + goto abort_negative; + + b_off = iblock << ISOFS_BUFFER_BITS(inode); + + /* If we are beyond the end of this file, don't give out any * blocks. */ - if( b_off > inode->i_size ) - { - off_t max_legal_read_offset; - - /* - * If we are *way* beyond the end of the file, print a message. - * Access beyond the end of the file up to the next page boundary - * is normal, however because of the way the page cache works. - * In this case, we just return 0 so that we can properly fill - * the page with useless information without generating any - * I/O errors. - */ - max_legal_read_offset = (inode->i_size + PAGE_SIZE - 1) - & ~(PAGE_SIZE - 1); - if( b_off >= max_legal_read_offset ) - { - - printk("_isofs_bmap: block>= EOF(%d, %ld)\n", block, - inode->i_size); - } - return 0; - } + if (b_off > inode->i_size) { + off_t max_legal_read_offset; + + /* If we are *way* beyond the end of the file, print a message. + * Access beyond the end of the file up to the next page boundary + * is normal, however because of the way the page cache works. + * In this case, we just return 0 so that we can properly fill + * the page with useless information without generating any + * I/O errors. + */ + max_legal_read_offset = (inode->i_size + PAGE_SIZE - 1) + & ~(PAGE_SIZE - 1); + if (b_off >= max_legal_read_offset) + goto abort_beyond_end; + } + + offset = 0; + firstext = inode->u.isofs_i.i_first_extent; + sect_size = inode->u.isofs_i.i_section_size; + nextino = inode->u.isofs_i.i_next_section_ino; - offset = 0; - firstext = inode->u.isofs_i.i_first_extent; - size = inode->u.isofs_i.i_section_size; - nextino = inode->u.isofs_i.i_next_section_ino; -#ifdef DEBUG - printk("first inode: inode=%x nextino=%x firstext=%u size=%lu\n", - inode->i_ino, nextino, firstext, size); -#endif i = 0; if (nextino) { - while(b_off >= offset + size) { - offset += size; - - if(nextino == 0) return 0; - ino = iget(inode->i_sb, nextino); - if(!ino) return 0; - firstext = ino->u.isofs_i.i_first_extent; - size = ino->u.isofs_i.i_section_size; -#ifdef DEBUG - printk("read inode: inode=%lu ino=%lu nextino=%lu firstext=%u size=%lu\n", - inode->i_ino, nextino, ino->u.isofs_i.i_next_section_ino, firstext, size); -#endif - nextino = ino->u.isofs_i.i_next_section_ino; - iput(ino); - - if(++i > 100) { - printk("isofs_bmap: More than 100 file sections ?!?, aborting...\n"); - printk("isofs_bmap: ino=%lu block=%d firstext=%u size=%u nextino=%lu\n", - inode->i_ino, block, firstext, (unsigned)size, nextino); - return 0; - } + while (b_off >= (offset + sect_size)) { + struct inode *ninode; + + offset += sect_size; + if (nextino == 0) + goto abort; + ninode = iget(inode->i_sb, nextino); + if (!ninode) + goto abort; + firstext = ninode->u.isofs_i.i_first_extent; + sect_size = ninode->u.isofs_i.i_section_size; + nextino = ninode->u.isofs_i.i_next_section_ino; + iput(ninode); + + if (++i > 100) + goto abort_too_many_sections; } } -#ifdef DEBUG - printk("isofs_bmap: mapped inode:block %x:%d to block %lu\n", - inode->i_ino, block, (b_off - offset + firstext) >> ISOFS_BUFFER_BITS(inode)); -#endif - return (b_off - offset + firstext) >> ISOFS_BUFFER_BITS(inode); -} -int isofs_bmap(struct inode * inode,int block) -{ - int retval; + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = + (b_off - offset + firstext) >> ISOFS_BUFFER_BITS(inode); + bh_result->b_state |= (1UL << BH_Mapped); + err = 0; - lock_kernel(); - retval = do_isofs_bmap(inode, block); +abort: unlock_kernel(); - return retval; + return err; + +abort_create_attempted: + printk("_isofs_bmap: Kernel tries to allocate a block\n"); + goto abort; + +abort_negative: + printk("_isofs_bmap: block < 0\n"); + goto abort; + +abort_beyond_end: + printk("_isofs_bmap: block >= EOF (%ld, %ld)\n", + iblock, inode->i_size); + goto abort; + +abort_too_many_sections: + printk("isofs_bmap: More than 100 file sections ?!?, aborting...\n"); + printk("isofs_bmap: ino=%lu block=%ld firstext=%u sect_size=%u nextino=%lu\n", + inode->i_ino, iblock, firstext, (unsigned) sect_size, nextino); + goto abort; +} + +int isofs_bmap(struct inode *inode, int block) +{ + struct buffer_head dummy; + int error; + + dummy.b_state = 0; + dummy.b_blocknr = -1000; + error = isofs_get_block(inode, block, &dummy, 0); + if (!error) + return dummy.b_blocknr; + return 0; } static void test_and_set_uid(uid_t *p, uid_t value) @@ -1101,7 +1123,7 @@ out_toomany: goto out; } -void isofs_read_inode(struct inode * inode) +static void isofs_read_inode(struct inode * inode) { struct super_block *sb = inode->i_sb; unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); diff --git a/fs/isofs/symlink.c b/fs/isofs/symlink.c index 5de4a8748..e5a7a2c72 100644 --- a/fs/isofs/symlink.c +++ b/fs/isofs/symlink.c @@ -38,11 +38,14 @@ struct inode_operations isofs_symlink_inode_operations = { NULL, /* rename */ isofs_readlink, /* readlink */ isofs_follow_link, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; static int isofs_readlink(struct dentry * dentry, char * buffer, int buflen) diff --git a/fs/lockd/host.c b/fs/lockd/host.c index e8b208f65..02cfb07f4 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -140,6 +140,7 @@ nlm_lookup_host(struct svc_client *clnt, struct sockaddr_in *sin, host->h_nextrebind = jiffies + NLM_HOST_REBIND; host->h_expires = jiffies + NLM_HOST_EXPIRE; host->h_count = 1; + init_waitqueue_head(&host->h_gracewait); host->h_state = 0; /* pseudo NSM state */ host->h_nsmstate = 0; /* real NSM state */ host->h_exportent = clnt; diff --git a/fs/locks.c b/fs/locks.c index 301b92c9e..728985c34 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -563,11 +563,14 @@ int locks_verify_area(int read_write, struct inode *inode, struct file *filp, /* Candidates for mandatory locking have the setgid bit set * but no group execute bit - an otherwise meaningless combination. */ - if (IS_MANDLOCK(inode) && - (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) - return (locks_mandatory_area(read_write, inode, filp, offset, - count)); - return (0); + if (IS_MANDLOCK(inode) && (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) { + int retval; + lock_kernel(); + retval = locks_mandatory_area(read_write, inode, filp, offset, count); + unlock_kernel(); + return retval; + } + return 0; } int locks_mandatory_locked(struct inode *inode) diff --git a/fs/minix/dir.c b/fs/minix/dir.c index 187925903..a44d5d69d 100644 --- a/fs/minix/dir.c +++ b/fs/minix/dir.c @@ -52,11 +52,14 @@ struct inode_operations minix_dir_inode_operations = { minix_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; static int minix_readdir(struct file * filp, diff --git a/fs/minix/file.c b/fs/minix/file.c index d6b7ecb17..14d478bde 100644 --- a/fs/minix/file.c +++ b/fs/minix/file.c @@ -27,50 +27,14 @@ #include <linux/fs.h> #include <linux/minix_fs.h> -static int minix_writepage(struct file *file, struct page *page) -{ - struct dentry *dentry = file->f_dentry; - struct inode *inode = dentry->d_inode; - unsigned long block; - int *p, nr[PAGE_SIZE/BLOCK_SIZE]; - int i, err, created; - struct buffer_head *bh; - - i = PAGE_SIZE / BLOCK_SIZE; - block = page->offset / BLOCK_SIZE; - p = nr; - bh = page->buffers; - do { - if (bh && bh->b_blocknr) - *p = bh->b_blocknr; - else - *p = minix_getblk_block(inode, block, 1, &err, &created); - if (!*p) - return -EIO; - i--; - block++; - p++; - if (bh) - bh = bh->b_this_page; - } while(i > 0); - - /* IO start */ - brw_page(WRITE, page, inode->i_dev, nr, BLOCK_SIZE, 1); - return 0; -} - -static long minix_write_one_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char *buf) -{ - return block_write_one_page(file, page, offset, bytes, buf, minix_getblk_block); -} - /* * Write to a file (through the page cache). */ static ssize_t minix_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { - return generic_file_write(file, buf, count, ppos, minix_write_one_page); + return generic_file_write(file, buf, count, + ppos, block_write_partial_page); } /* @@ -88,7 +52,10 @@ static struct file_operations minix_file_operations = { NULL, /* no special open is needed */ NULL, /* flush */ NULL, /* release */ - minix_sync_file /* fsync */ + minix_sync_file, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL /* revalidate */ }; struct inode_operations minix_file_inode_operations = { @@ -104,12 +71,12 @@ struct inode_operations minix_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - generic_readpage, /* readpage */ - minix_writepage, /* writepage */ - minix_bmap, /* bmap */ + minix_get_block, /* get_block */ + block_read_full_page, /* readpage */ + block_write_full_page, /* writepage */ + block_flushpage, /* flushpage */ minix_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ NULL, /* revalidate */ - block_flushpage, /* flushpage */ }; diff --git a/fs/minix/fsync.c b/fs/minix/fsync.c index ef3d15db9..2fcdddf15 100644 --- a/fs/minix/fsync.c +++ b/fs/minix/fsync.c @@ -53,7 +53,7 @@ static int V1_sync_block (struct inode * inode, unsigned short * block, int wait return 0; } ll_rw_block(WRITE, 1, &bh); - bh->b_count--; + atomic_dec(&bh->b_count); return 0; } @@ -190,7 +190,7 @@ static int V2_sync_block (struct inode * inode, unsigned long * block, int wait) return 0; } ll_rw_block(WRITE, 1, &bh); - bh->b_count--; + atomic_dec(&bh->b_count); return 0; } diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 088de42dc..e5352090d 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -19,6 +19,7 @@ #include <linux/stat.h> #include <linux/locks.h> #include <linux/init.h> +#include <linux/smp_lock.h> #include <asm/system.h> #include <asm/uaccess.h> @@ -361,112 +362,127 @@ static int V1_block_bmap(struct buffer_head * bh, int nr) return tmp; } -static int V1_minix_bmap(struct inode * inode,int block) +static int V1_minix_block_map(struct inode * inode, long block) { - int i; + int i, ret; - if (block<0) { + ret = 0; + lock_kernel(); + if (block < 0) { printk("minix_bmap: block<0"); - return 0; + goto out; } if (block >= (inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE)) { printk("minix_bmap: block>big"); - return 0; + goto out; + } + if (block < 7) { + ret = V1_inode_bmap(inode,block); + goto out; } - if (block < 7) - return V1_inode_bmap(inode,block); block -= 7; if (block < 512) { i = V1_inode_bmap(inode,7); if (!i) - return 0; - return V1_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block); + goto out; + ret = V1_block_bmap(bread(inode->i_dev, i, + BLOCK_SIZE), block); + goto out; } block -= 512; i = V1_inode_bmap(inode,8); if (!i) - return 0; + goto out; i = V1_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block>>9); if (!i) - return 0; - return V1_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block & 511); + goto out; + ret = V1_block_bmap(bread(inode->i_dev, i, BLOCK_SIZE), + block & 511); +out: + unlock_kernel(); + return ret; } /* * The minix V2 fs bmap functions. */ -#define V2_inode_bmap(inode,nr) (((unsigned long *)(inode)->u.minix_i.u.i2_data)[(nr)]) +#define V2_inode_bmap(inode,nr) (((unsigned int *)(inode)->u.minix_i.u.i2_data)[(nr)]) static int V2_block_bmap(struct buffer_head * bh, int nr) { int tmp; if (!bh) return 0; - tmp = ((unsigned long *) bh->b_data)[nr]; + tmp = ((unsigned int *) bh->b_data)[nr]; brelse(bh); return tmp; } -static int V2_minix_bmap(struct inode * inode, int block) +static int V2_minix_block_map(struct inode * inode, int block) { - int i; + int i, ret; - if (block<0) { + ret = 0; + lock_kernel(); + if (block < 0) { printk("minix_bmap: block<0"); - return 0; + goto out; } if (block >= (inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE)) { printk("minix_bmap: block>big"); - return 0; + goto out; + } + if (block < 7) { + ret = V2_inode_bmap(inode,block); + goto out; } - if (block < 7) - return V2_inode_bmap(inode,block); block -= 7; if (block < 256) { - i = V2_inode_bmap(inode,7); + i = V2_inode_bmap(inode, 7); if (!i) - return 0; - return V2_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block); + goto out; + ret = V2_block_bmap(bread(inode->i_dev, i, + BLOCK_SIZE), block); + goto out; } block -= 256; - if (block < 256*256) { - i = V2_inode_bmap(inode,8); + if (block < (256 * 256)) { + i = V2_inode_bmap(inode, 8); if (!i) - return 0; - i = V2_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block >> 8); + goto out; + i = V2_block_bmap(bread(inode->i_dev, i, BLOCK_SIZE), + block >> 8); if (!i) - return 0; - return V2_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block & 255); + goto out; + ret = V2_block_bmap(bread(inode->i_dev, i, BLOCK_SIZE), + block & 255); + goto out; } - block -= 256*256; - i = V2_inode_bmap(inode,9); + block -= (256 * 256); + i = V2_inode_bmap(inode, 9); if (!i) - return 0; - i = V2_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block >> 16); + goto out; + i = V2_block_bmap(bread(inode->i_dev, i, BLOCK_SIZE), + block >> 16); if (!i) - return 0; - i = V2_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),(block >> 8) & 255); + goto out; + i = V2_block_bmap(bread(inode->i_dev, i, BLOCK_SIZE), + (block >> 8) & 255); if (!i) - return 0; - return V2_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block & 255); -} - -/* - * The global minix fs bmap function. - */ -int minix_bmap(struct inode * inode, int block) -{ - if (INODE_VERSION(inode) == MINIX_V1) - return V1_minix_bmap(inode, block); - else - return V2_minix_bmap(inode, block); + goto out; + ret = V2_block_bmap(bread(inode->i_dev, i, BLOCK_SIZE), + block & 255); +out: + unlock_kernel(); + return ret; } /* * The minix V1 fs getblk functions. */ -static struct buffer_head * V1_inode_getblk(struct inode * inode, int nr, int create, - int metadata, int *phys_block, int *created) +static struct buffer_head * V1_inode_getblk(struct inode * inode, int nr, + int new_block, int *err, + int metadata, int *phys, int *new) { int tmp; unsigned short *p; @@ -483,15 +499,30 @@ repeat: brelse(result); goto repeat; } else { - *phys_block = tmp; + *phys = tmp; return NULL; } } - if (!create) - return NULL; + *err = -EFBIG; + + /* Check file limits.. */ + { + unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur; + if (limit < RLIM_INFINITY) { + limit >>= BLOCK_SIZE_BITS; + if (new_block >= limit) { + send_sig(SIGXFSZ, current, 0); + *err = -EFBIG; + return NULL; + } + } + } + tmp = minix_new_block(inode->i_sb); - if (!tmp) + if (!tmp) { + *err = -ENOSPC; return NULL; + } if (metadata) { result = getblk(inode->i_dev, tmp, BLOCK_SIZE); if (*p) { @@ -504,12 +535,18 @@ repeat: mark_buffer_dirty(result, 1); } else { if (*p) { + /* + * Nobody is allowed to change block allocation + * state from under us: + */ + BUG(); minix_free_block(inode->i_sb, tmp); goto repeat; } - *phys_block = tmp; + *phys = tmp; result = NULL; - *created = 1; + *err = 0; + *new = 1; } *p = tmp; @@ -519,22 +556,22 @@ repeat: } static struct buffer_head * V1_block_getblk(struct inode * inode, - struct buffer_head * bh, int nr, int create, - int metadata, int *phys_block, int *created) + struct buffer_head * bh, int nr, int new_block, int *err, + int metadata, int *phys, int *new) { int tmp; unsigned short *p; struct buffer_head * result; + unsigned long limit; + result = NULL; if (!bh) - return NULL; + goto out; if (!buffer_uptodate(bh)) { ll_rw_block(READ, 1, &bh); wait_on_buffer(bh); - if (!buffer_uptodate(bh)) { - brelse(bh); - return NULL; - } + if (!buffer_uptodate(bh)) + goto out; } p = nr + (unsigned short *) bh->b_data; repeat: @@ -542,27 +579,29 @@ repeat: if (tmp) { if (metadata) { result = getblk(bh->b_dev, tmp, BLOCK_SIZE); - if (tmp == *p) { - brelse(bh); - return result; - } + if (tmp == *p) + goto out; brelse(result); goto repeat; } else { - *phys_block = tmp; - brelse(bh); - return NULL; + *phys = tmp; + goto out; } } - if (!create) { - brelse(bh); - return NULL; + *err = -EFBIG; + + limit = current->rlim[RLIMIT_FSIZE].rlim_cur; + if (limit < RLIM_INFINITY) { + limit >>= BLOCK_SIZE_BITS; + if (new_block >= limit) { + send_sig(SIGXFSZ, current, 0); + goto out; + } } + tmp = minix_new_block(inode->i_sb); - if (!tmp) { - brelse(bh); - return NULL; - } + if (!tmp) + goto out; if (metadata) { result = getblk(bh->b_dev, tmp, BLOCK_SIZE); if (*p) { @@ -574,69 +613,118 @@ repeat: mark_buffer_uptodate(result, 1); mark_buffer_dirty(result, 1); } else { - if (*p) { - minix_free_block(inode->i_sb, tmp); - goto repeat; - } - *phys_block = tmp; - result = NULL; - *created = 1; + *phys = tmp; + *new = 1; + } + if (*p) { + minix_free_block(inode->i_sb, tmp); + brelse(result); + goto repeat; } *p = tmp; mark_buffer_dirty(bh, 1); + *err = 0; +out: brelse(bh); return result; } -int V1_getblk_block(struct inode * inode, long block, int create, int *err, int *created) +static int V1_get_block(struct inode * inode, long block, + struct buffer_head *bh_result, int create) { - struct buffer_head *bh, *tmp; - int phys_block; + int ret, err, new, phys, ptr; + struct buffer_head *bh; - *err = -EIO; - if (block < 0) { - printk("minix_getblk: block<0"); - return 0; - } - if (block >= inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE) { - printk("minix_getblk: block>big"); + if (!create) { + phys = V1_minix_block_map(inode, block); + if (phys) { + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = phys; + bh_result->b_state |= (1UL << BH_Mapped); + } return 0; } - *created = 0; - if (block < 7) { - tmp = V1_inode_getblk(inode, block, create, - 0, &phys_block, created); + + err = -EIO; + new = 0; + ret = 0; + bh = NULL; + + lock_kernel(); + if (block < 0) + goto abort_negative; + if (block >= inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE) + goto abort_too_big; + + err = 0; + ptr = block; + /* + * ok, these macros clean the logic up a bit and make + * it much more readable: + */ +#define GET_INODE_DATABLOCK(x) \ + V1_inode_getblk(inode, x, block, &err, 0, &phys, &new) +#define GET_INODE_PTR(x) \ + V1_inode_getblk(inode, x, block, &err, 1, NULL, NULL) +#define GET_INDIRECT_DATABLOCK(x) \ + V1_block_getblk(inode, bh, x, block, &err, 0, &phys, &new) +#define GET_INDIRECT_PTR(x) \ + V1_block_getblk(inode, bh, x, block, &err, 1, NULL, NULL) + + if (ptr < 7) { + bh = GET_INODE_DATABLOCK(ptr); goto out; } - block -= 7; - if (block < 512) { - bh = V1_inode_getblk(inode, 7, create, 1, NULL, NULL); - tmp = V1_block_getblk(inode, bh, block, create, - 0, &phys_block, created); - goto out; + ptr -= 7; + if (ptr < 512) { + bh = GET_INODE_PTR(7); + goto get_indirect; } - block -= 512; - bh = V1_inode_getblk(inode, 8, create, 1, NULL, NULL); - bh = V1_block_getblk(inode, bh, (block>>9) & 511, create, 1, NULL, NULL); - tmp = V1_block_getblk(inode, bh, block & 511, create, 0, &phys_block, created); + ptr -= 512; + bh = GET_INODE_PTR(8); + bh = GET_INDIRECT_PTR((ptr >> 9) & 511); +get_indirect: + bh = GET_INDIRECT_DATABLOCK(ptr & 511); + +#undef GET_INODE_DATABLOCK +#undef GET_INODE_PTR +#undef GET_INDIRECT_DATABLOCK +#undef GET_INDIRECT_PTR out: - *err = 0; - return phys_block; + if (err) + goto abort; + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = phys; + bh_result->b_state |= (1UL << BH_Mapped); + if (new) + bh_result->b_state |= (1UL << BH_New); +abort: + unlock_kernel(); + return err; + +abort_negative: + printk("minix_getblk: block<0"); + goto abort; + +abort_too_big: + printk("minix_getblk: block>big"); + goto abort; } /* * The minix V2 fs getblk functions. */ -static struct buffer_head * V2_inode_getblk(struct inode * inode, int nr, int create, - int metadata, int *phys_block, int *created) +static struct buffer_head * V2_inode_getblk(struct inode * inode, int nr, + int new_block, int *err, + int metadata, int *phys, int *new) { int tmp; - unsigned long *p; + unsigned int *p; struct buffer_head * result; - p = (unsigned long *) inode->u.minix_i.u.i2_data + nr; + p = (unsigned int *) inode->u.minix_i.u.i2_data + nr; repeat: tmp = *p; if (tmp) { @@ -647,15 +735,30 @@ repeat: brelse(result); goto repeat; } else { - *phys_block = tmp; + *phys = tmp; return NULL; } } - if (!create) - return NULL; + *err = -EFBIG; + + /* Check file limits.. */ + { + unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur; + if (limit < RLIM_INFINITY) { + limit >>= BLOCK_SIZE_BITS; + if (new_block >= limit) { + send_sig(SIGXFSZ, current, 0); + *err = -EFBIG; + return NULL; + } + } + } + tmp = minix_new_block(inode->i_sb); - if (!tmp) + if (!tmp) { + *err = -ENOSPC; return NULL; + } if (metadata) { result = getblk(inode->i_dev, tmp, BLOCK_SIZE); if (*p) { @@ -668,12 +771,18 @@ repeat: mark_buffer_dirty(result, 1); } else { if (*p) { + /* + * Nobody is allowed to change block allocation + * state from under us: + */ + BUG(); minix_free_block(inode->i_sb, tmp); goto repeat; } - *phys_block = tmp; + *phys = tmp; result = NULL; - *created = 1; + *err = 0; + *new = 1; } *p = tmp; @@ -683,50 +792,52 @@ repeat: } static struct buffer_head * V2_block_getblk(struct inode * inode, - struct buffer_head * bh, int nr, int create, - int metadata, int *phys_block, int *created) + struct buffer_head * bh, int nr, int new_block, int *err, + int metadata, int *phys, int *new) { int tmp; - unsigned long *p; + unsigned int *p; struct buffer_head * result; + unsigned long limit; + result = NULL; if (!bh) - return NULL; + goto out; if (!buffer_uptodate(bh)) { ll_rw_block(READ, 1, &bh); wait_on_buffer(bh); - if (!buffer_uptodate(bh)) { - brelse(bh); - return NULL; - } + if (!buffer_uptodate(bh)) + goto out; } - p = nr + (unsigned long *) bh->b_data; + p = nr + (unsigned int *) bh->b_data; repeat: tmp = *p; if (tmp) { if (metadata) { result = getblk(bh->b_dev, tmp, BLOCK_SIZE); - if (tmp == *p) { - brelse(bh); - return result; - } + if (tmp == *p) + goto out; brelse(result); goto repeat; } else { - *phys_block = tmp; - brelse(bh); - return NULL; + *phys = tmp; + goto out; } } - if (!create) { - brelse(bh); - return NULL; + *err = -EFBIG; + + limit = current->rlim[RLIMIT_FSIZE].rlim_cur; + if (limit < RLIM_INFINITY) { + limit >>= BLOCK_SIZE_BITS; + if (new_block >= limit) { + send_sig(SIGXFSZ, current, 0); + goto out; + } } + tmp = minix_new_block(inode->i_sb); - if (!tmp) { - brelse(bh); - return NULL; - } + if (!tmp) + goto out; if (metadata) { result = getblk(bh->b_dev, tmp, BLOCK_SIZE); if (*p) { @@ -738,103 +849,151 @@ repeat: mark_buffer_uptodate(result, 1); mark_buffer_dirty(result, 1); } else { - if (*p) { - minix_free_block(inode->i_sb, tmp); - goto repeat; - } - *phys_block = tmp; - result = NULL; - *created = 1; + *phys = tmp; + *new = 1; + } + if (*p) { + minix_free_block(inode->i_sb, tmp); + brelse(result); + goto repeat; } *p = tmp; mark_buffer_dirty(bh, 1); + *err = 0; +out: brelse(bh); return result; } -int V2_getblk_block(struct inode * inode, int block, int create, int *err, int *created) +static int V2_get_block(struct inode * inode, long block, + struct buffer_head *bh_result, int create) { - struct buffer_head * bh, *tmp; - int phys_block; + int ret, err, new, phys, ptr; + struct buffer_head * bh; - *err = -EIO; - if (block < 0) { - printk("minix_getblk: block<0"); - return 0; - } - if (block >= inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE) { - printk("minix_getblk: block>big"); + if (!create) { + phys = V2_minix_block_map(inode, block); + if (phys) { + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = phys; + bh_result->b_state |= (1UL << BH_Mapped); + } return 0; } - *created = 0; - if (block < 7) { - tmp = V2_inode_getblk(inode, block, create, - 0, &phys_block, created); + + err = -EIO; + new = 0; + ret = 0; + bh = NULL; + + lock_kernel(); + if (block < 0) + goto abort_negative; + if (block >= inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE) + goto abort_too_big; + + err = 0; + ptr = block; + /* + * ok, these macros clean the logic up a bit and make + * it much more readable: + */ +#define GET_INODE_DATABLOCK(x) \ + V2_inode_getblk(inode, x, block, &err, 0, &phys, &new) +#define GET_INODE_PTR(x) \ + V2_inode_getblk(inode, x, block, &err, 1, NULL, NULL) +#define GET_INDIRECT_DATABLOCK(x) \ + V2_block_getblk(inode, bh, x, block, &err, 0, &phys, &new) +#define GET_INDIRECT_PTR(x) \ + V2_block_getblk(inode, bh, x, block, &err, 1, NULL, NULL) + + if (ptr < 7) { + bh = GET_INODE_DATABLOCK(ptr); goto out; } - block -= 7; - if (block < 256) { - bh = V2_inode_getblk(inode, 7, create, 1, NULL, NULL); - tmp = V2_block_getblk(inode, bh, block, create, - 0, &phys_block, created); - goto out; + ptr -= 7; + if (ptr < 256) { + bh = GET_INODE_PTR(7); + goto get_indirect; } - block -= 256; - if (block < 256*256) { - bh = V2_inode_getblk(inode, 8, create, 1, NULL, NULL); - bh = V2_block_getblk(inode, bh, (block>>8) & 255, create, - 1, NULL, NULL); - tmp = V2_block_getblk(inode, bh, block & 255, create, - 0, &phys_block, created); - goto out; + ptr -= 256; + if (ptr < 256*256) { + bh = GET_INODE_PTR(8); + goto get_double; } - block -= 256*256; - bh = V2_inode_getblk(inode, 9, create, 1, NULL, NULL); - bh = V2_block_getblk(inode, bh, (block >> 16) & 255, create, 1, NULL, NULL); - bh = V2_block_getblk(inode, bh, (block >> 8) & 255, create, 1, NULL, NULL); - tmp = V2_block_getblk(inode, bh, block & 255, create, 0, &phys_block, created); + ptr -= 256*256; + bh = GET_INODE_PTR(9); + bh = GET_INDIRECT_PTR((ptr >> 16) & 255); +get_double: + bh = GET_INDIRECT_PTR((ptr >> 8) & 255); +get_indirect: + bh = GET_INDIRECT_DATABLOCK(ptr & 255); + +#undef GET_INODE_DATABLOCK +#undef GET_INODE_PTR +#undef GET_INDIRECT_DATABLOCK +#undef GET_INDIRECT_PTR out: - *err = 0; - return phys_block; + if (err) + goto abort; + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = phys; + bh_result->b_state |= (1UL << BH_Mapped); + if (new) + bh_result->b_state |= (1UL << BH_New); +abort: + unlock_kernel(); + return err; + +abort_negative: + printk("minix_getblk: block<0"); + goto abort; + +abort_too_big: + printk("minix_getblk: block>big"); + goto abort; } -int minix_getblk_block (struct inode *inode, long block, - int create, int *err, int *created) +int minix_get_block(struct inode *inode, long block, + struct buffer_head *bh_result, int create) { if (INODE_VERSION(inode) == MINIX_V1) - return V1_getblk_block(inode, block, create, err, created); + return V1_get_block(inode, block, bh_result, create); else - return V2_getblk_block(inode, block, create, err, created); + return V2_get_block(inode, block, bh_result, create); } /* * the global minix fs getblk function. */ -struct buffer_head *minix_getblk (struct inode *inode, int block, int create) +struct buffer_head *minix_getblk(struct inode *inode, int block, int create) { - struct buffer_head *tmp = NULL; - int phys_block; - int err, created; - - phys_block = minix_getblk_block(inode, block, create, &err, &created); - if (phys_block) { - tmp = getblk(inode->i_dev, phys_block, BLOCK_SIZE); - if (created) { - memset(tmp->b_data, 0, BLOCK_SIZE); - mark_buffer_uptodate(tmp, 1); - mark_buffer_dirty(tmp, 1); + struct buffer_head dummy; + int error; + + dummy.b_state = 0; + dummy.b_blocknr = -1000; + error = minix_get_block(inode, block, &dummy, create); + if (!error && buffer_mapped(&dummy)) { + struct buffer_head *bh; + bh = getblk(dummy.b_dev, dummy.b_blocknr, BLOCK_SIZE); + if (buffer_new(&dummy)) { + memset(bh->b_data, 0, BLOCK_SIZE); + mark_buffer_uptodate(bh, 1); + mark_buffer_dirty(bh, 1); } + return bh; } - return tmp; + return NULL; } struct buffer_head * minix_bread(struct inode * inode, int block, int create) { struct buffer_head * bh; - bh = minix_getblk(inode,block,create); + bh = minix_getblk(inode, block, create); if (!bh || buffer_uptodate(bh)) return bh; ll_rw_block(READ, 1, &bh); diff --git a/fs/minix/symlink.c b/fs/minix/symlink.c index 4f3661105..3a8951b09 100644 --- a/fs/minix/symlink.c +++ b/fs/minix/symlink.c @@ -33,11 +33,14 @@ struct inode_operations minix_symlink_inode_operations = { NULL, /* rename */ minix_readlink, /* readlink */ minix_follow_link, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; static struct dentry * minix_follow_link(struct dentry * dentry, diff --git a/fs/minix/truncate.c b/fs/minix/truncate.c index 4718e092e..6724d0064 100644 --- a/fs/minix/truncate.c +++ b/fs/minix/truncate.c @@ -33,7 +33,7 @@ */ #define DATA_BUFFER_USED(bh) \ - ((bh->b_count > 1) || buffer_locked(bh)) + (atomic_read(&bh->b_count) || buffer_locked(bh)) /* * The functions for minix V1 fs truncation. @@ -121,7 +121,7 @@ repeat: if (*(ind++)) break; if (i >= 512) { - if (ind_bh->b_count != 1) + if (atomic_read(&ind_bh->b_count) != 1) retry = 1; else { tmp = *p; @@ -166,7 +166,7 @@ repeat: if (*(dind++)) break; if (i >= 512) { - if (dind_bh->b_count != 1) + if (atomic_read(&dind_bh->b_count) != 1) retry = 1; else { tmp = *p; @@ -285,7 +285,7 @@ repeat: if (*(ind++)) break; if (i >= 256) { - if (ind_bh->b_count != 1) + if (atomic_read(&ind_bh->b_count) != 1) retry = 1; else { tmp = *p; @@ -330,7 +330,7 @@ repeat: if (*(dind++)) break; if (i >= 256) { - if (dind_bh->b_count != 1) + if (atomic_read(&dind_bh->b_count) != 1) retry = 1; else { tmp = *p; @@ -376,7 +376,7 @@ repeat: if (*(tind++)) break; if (i >= 256) { - if (tind_bh->b_count != 1) + if (atomic_read(&tind_bh->b_count) != 1) retry = 1; else { tmp = *p; diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index 8175724d7..fa5e4aed5 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -627,9 +627,10 @@ struct inode_operations msdos_dir_inode_operations = { msdos_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index 680f011a1..1f92a89fb 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -92,9 +92,10 @@ struct inode_operations ncp_dir_inode_operations = ncp_rename, /* rename */ NULL, /* readlink */ NULL, /* follow link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c index bb61e2464..3e16cf408 100644 --- a/fs/ncpfs/file.c +++ b/fs/ncpfs/file.c @@ -295,6 +295,13 @@ struct inode_operations ncp_file_inode_operations = NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ - NULL, /* bmap */ - NULL /* truncate */ + NULL, /* follow_link */ + NULL, /* get_block */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* flushpage */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; diff --git a/fs/ncpfs/symlink.c b/fs/ncpfs/symlink.c index a923c218c..578b2b985 100644 --- a/fs/ncpfs/symlink.c +++ b/fs/ncpfs/symlink.c @@ -59,12 +59,14 @@ struct inode_operations ncp_symlink_inode_operations={ NULL, /* rename */ ncp_readlink, /* readlink */ ncp_follow_link, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; /* ----- follow a symbolic link ------------------------------------------ */ diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index d41505862..31d76e5c6 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -78,7 +78,7 @@ struct inode_operations nfs_dir_inode_operations = { nfs_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ @@ -884,6 +884,8 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) nfs_flush_dircache(dir); error = nfs_proc_mkdir(NFS_DSERVER(dentry), NFS_FH(dentry->d_parent), dentry->d_name.name, &sattr, &fhandle, &fattr); + if (!error) + dir->i_nlink++; return error; } @@ -909,8 +911,8 @@ dentry->d_inode->i_count, dentry->d_inode->i_nlink); /* Update i_nlink and invalidate dentry. */ if (!error) { d_drop(dentry); - if (dentry->d_inode->i_nlink) - dentry->d_inode->i_nlink --; + if (dir->i_nlink) + dir->i_nlink--; } return error; diff --git a/fs/nfs/file.c b/fs/nfs/file.c index d3066f4cd..2fed65b38 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -71,7 +71,7 @@ struct inode_operations nfs_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ nfs_readpage, /* readpage */ nfs_writepage, /* writepage */ NULL, /* flushpage */ @@ -167,7 +167,7 @@ nfs_fsync(struct file *file, struct dentry *dentry) * If the writer ends up delaying the write, the writer needs to * increment the page use counts until he is done with the page. */ -static long nfs_write_one_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) +static int nfs_write_one_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) { long status; diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index c6fc4d685..6cd892740 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -43,7 +43,7 @@ struct inode_operations nfs_symlink_inode_operations = { NULL, /* rename */ nfs_readlink, /* readlink */ nfs_follow_link, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 911a5261e..8b63cbf66 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -467,7 +467,7 @@ nfs_updatepage(struct file *file, struct page *page, unsigned long offset, unsig * The IO completion will then free the page and the dentry. */ get_page(page); - file->f_count++; + atomic_inc(&file->f_count); /* Schedule request */ synchronous = schedule_write_request(req, synchronous); diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 582b1854f..63294ae29 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -342,7 +342,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, memset(filp, 0, sizeof(*filp)); filp->f_op = inode->i_op->default_file_ops; - filp->f_count = 1; + atomic_set(&filp->f_count, 1); filp->f_flags = wflag? O_WRONLY : O_RDONLY; filp->f_mode = wflag? FMODE_WRITE : FMODE_READ; filp->f_dentry = dentry; @@ -360,7 +360,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, /* I nearly added put_filp() call here, but this filp * is really on callers stack frame. -DaveM */ - filp->f_count--; + atomic_dec(&filp->f_count); } } out_nfserr: diff --git a/fs/ntfs/fs.c b/fs/ntfs/fs.c index d9430c2cc..cc6bfe2e3 100644 --- a/fs/ntfs/fs.c +++ b/fs/ntfs/fs.c @@ -439,9 +439,10 @@ static struct inode_operations ntfs_inode_operations_nobmap = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ @@ -621,9 +622,10 @@ static struct inode_operations ntfs_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - generic_readpage, + ntfs_bmap, /* get_block */ + block_read_full_page, /* readpage */ NULL, /* writepage */ - ntfs_bmap, + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ @@ -669,9 +671,10 @@ static struct inode_operations ntfs_dir_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ @@ -790,7 +790,7 @@ int filp_close(struct file *filp, fl_owner_t id) int retval; struct dentry *dentry = filp->f_dentry; - if (filp->f_count == 0) { + if (!atomic_read(&filp->f_count)) { printk("VFS: Close: file count is 0\n"); return 0; } @@ -8,6 +8,7 @@ #include <linux/file.h> #include <linux/poll.h> #include <linux/malloc.h> +#include <linux/smp_lock.h> #include <asm/uaccess.h> @@ -28,16 +29,12 @@ /* in case of paging and multiple read/write on the same pipe. (FGC) */ -static ssize_t pipe_read(struct file * filp, char * buf, - size_t count, loff_t *ppos) +static ssize_t do_pipe_read(struct file * filp, char * buf, size_t count) { struct inode * inode = filp->f_dentry->d_inode; ssize_t chars = 0, size = 0, read = 0; char *pipebuf; - if (ppos != &filp->f_pos) - return -ESPIPE; - if (filp->f_flags & O_NONBLOCK) { if (PIPE_LOCK(*inode)) return -EAGAIN; @@ -82,17 +79,13 @@ static ssize_t pipe_read(struct file * filp, char * buf, return -EAGAIN; return 0; } - -static ssize_t pipe_write(struct file * filp, const char * buf, - size_t count, loff_t *ppos) + +static ssize_t do_pipe_write(struct file * filp, const char * buf, size_t count) { struct inode * inode = filp->f_dentry->d_inode; ssize_t chars = 0, free = 0, written = 0, err=0; char *pipebuf; - if (ppos != &filp->f_pos) - return -ESPIPE; - if (!PIPE_READERS(*inode)) { /* no readers */ send_sig(SIGPIPE,current,0); return -EPIPE; @@ -147,6 +140,32 @@ errout: return written ? written : err; } +static ssize_t pipe_read(struct file * filp, char * buf, size_t count, loff_t *ppos) +{ + ssize_t retval; + + if (ppos != &filp->f_pos) + return -ESPIPE; + + lock_kernel(); + retval = do_pipe_read(filp, buf, count); + unlock_kernel(); + return retval; +} + +static ssize_t pipe_write(struct file * filp, const char * buf, size_t count, loff_t *ppos) +{ + ssize_t retval; + + if (ppos != &filp->f_pos) + return -ESPIPE; + + lock_kernel(); + retval = do_pipe_write(filp, buf, count); + unlock_kernel(); + return retval; +} + static long long pipe_lseek(struct file * file, long long offset, int orig) { return -ESPIPE; @@ -461,7 +480,7 @@ struct inode_operations pipe_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ diff --git a/fs/proc/array.c b/fs/proc/array.c index 33717830f..f108511fb 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -1545,7 +1545,7 @@ struct inode_operations proc_array_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ @@ -1596,7 +1596,7 @@ struct inode_operations proc_arraylong_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ diff --git a/fs/proc/base.c b/fs/proc/base.c index 8579dd8c5..118e94956 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -45,7 +45,7 @@ static struct inode_operations proc_base_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 2bbb51d28..a900d01bf 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -51,7 +51,7 @@ struct inode_operations proc_fd_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 4e59fed73..1a2fe0f6e 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -60,7 +60,7 @@ struct inode_operations proc_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ @@ -86,7 +86,7 @@ struct inode_operations proc_net_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c index 3cfccab96..bfe6c8c2e 100644 --- a/fs/proc/kmsg.c +++ b/fs/proc/kmsg.c @@ -72,7 +72,7 @@ struct inode_operations proc_kmsg_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ diff --git a/fs/proc/link.c b/fs/proc/link.c index 3a5639825..647dc339f 100644 --- a/fs/proc/link.c +++ b/fs/proc/link.c @@ -49,7 +49,7 @@ struct inode_operations proc_link_inode_operations = { NULL, /* rename */ proc_readlink, /* readlink */ proc_follow_link, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ diff --git a/fs/proc/mem.c b/fs/proc/mem.c index 0e89f7645..4d599c77b 100644 --- a/fs/proc/mem.c +++ b/fs/proc/mem.c @@ -336,7 +336,7 @@ struct inode_operations proc_mem_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ diff --git a/fs/proc/net.c b/fs/proc/net.c index 1ad226de0..b4e18bc49 100644 --- a/fs/proc/net.c +++ b/fs/proc/net.c @@ -113,7 +113,7 @@ struct inode_operations proc_net_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ diff --git a/fs/proc/omirr.c b/fs/proc/omirr.c index 562aa11c5..f738827a7 100644 --- a/fs/proc/omirr.c +++ b/fs/proc/omirr.c @@ -289,7 +289,7 @@ struct inode_operations proc_omirr_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ diff --git a/fs/proc/openpromfs.c b/fs/proc/openpromfs.c index dcb007f11..72bb194cf 100644 --- a/fs/proc/openpromfs.c +++ b/fs/proc/openpromfs.c @@ -577,11 +577,14 @@ static struct inode_operations openpromfs_prop_inode_ops = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; static struct file_operations openpromfs_nodenum_ops = { @@ -611,11 +614,14 @@ static struct inode_operations openpromfs_nodenum_inode_ops = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; static struct file_operations openprom_alias_operations = { @@ -645,11 +651,14 @@ static struct inode_operations openprom_alias_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; static int lookup_children(u16 n, const char * name, int len) diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c index 594f00858..140281015 100644 --- a/fs/proc/proc_devtree.c +++ b/fs/proc/proc_devtree.c @@ -57,7 +57,7 @@ struct inode_operations devtree_symlink_inode_operations = { NULL, /* rename */ devtree_readlink, /* readlink */ devtree_follow_link, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ diff --git a/fs/proc/root.c b/fs/proc/root.c index 67171fa07..d08860c26 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -71,7 +71,7 @@ struct inode_operations proc_dir_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ @@ -97,7 +97,7 @@ struct inode_operations proc_dyna_dir_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ @@ -142,7 +142,7 @@ static struct inode_operations proc_root_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ @@ -302,7 +302,7 @@ struct inode_operations proc_openprom_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ @@ -490,7 +490,7 @@ static struct inode_operations proc_self_inode_operations = { NULL, /* rename */ proc_self_readlink, /* readlink */ proc_self_follow_link, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ @@ -513,7 +513,7 @@ static struct inode_operations proc_link_inode_operations = { NULL, /* rename */ proc_readlink, /* readlink */ proc_follow_link, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ diff --git a/fs/proc/scsi.c b/fs/proc/scsi.c index ae2679b6d..679fa383f 100644 --- a/fs/proc/scsi.c +++ b/fs/proc/scsi.c @@ -71,7 +71,7 @@ struct inode_operations proc_scsi_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ diff --git a/fs/proc/sysvipc.c b/fs/proc/sysvipc.c index c6e32894d..7fff0ed03 100644 --- a/fs/proc/sysvipc.c +++ b/fs/proc/sysvipc.c @@ -130,7 +130,7 @@ struct inode_operations proc_sysvipc_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ diff --git a/fs/qnx4/dir.c b/fs/qnx4/dir.c index 029fd9061..8b27142a3 100644 --- a/fs/qnx4/dir.c +++ b/fs/qnx4/dir.c @@ -114,10 +114,12 @@ struct inode_operations qnx4_dir_inode_operations = NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; diff --git a/fs/qnx4/file.c b/fs/qnx4/file.c index ab923b7f0..2e2b9c2fb 100644 --- a/fs/qnx4/file.c +++ b/fs/qnx4/file.c @@ -189,16 +189,18 @@ struct inode_operations qnx4_file_inode_operations = NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + qnx4_bmap, /* get_block */ qnx4_readpage, /* readpage */ NULL, /* writepage */ - qnx4_bmap, /* bmap */ + NULL, /* flushpage */ #ifdef CONFIG_QNX4FS_RW qnx4_truncate, /* truncate */ #else NULL, #endif NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; static int qnx4_readpage(struct file *file, struct page *page) diff --git a/fs/qnx4/fsync.c b/fs/qnx4/fsync.c index 826ee8a09..4cea74fe5 100644 --- a/fs/qnx4/fsync.c +++ b/fs/qnx4/fsync.c @@ -55,7 +55,7 @@ static int sync_block(struct inode *inode, unsigned short *block, int wait) return 0; } ll_rw_block(WRITE, 1, &bh); - bh->b_count--; + atomic_dec(&bh->b_count); return 0; } diff --git a/fs/qnx4/symlinks.c b/fs/qnx4/symlinks.c index 457258670..c360d8b72 100644 --- a/fs/qnx4/symlinks.c +++ b/fs/qnx4/symlinks.c @@ -43,11 +43,14 @@ struct inode_operations qnx4_symlink_inode_operations = NULL, /* rename */ qnx4_readlink, /* readlink */ qnx4_follow_link, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; static struct dentry *qnx4_follow_link(struct dentry *dentry, diff --git a/fs/read_write.c b/fs/read_write.c index c7ea90a69..cf207fed0 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -117,28 +117,22 @@ asmlinkage ssize_t sys_read(unsigned int fd, char * buf, size_t count) { ssize_t ret; struct file * file; - ssize_t (*read)(struct file *, char *, size_t, loff_t *); - - lock_kernel(); ret = -EBADF; file = fget(fd); - if (!file) - goto bad_file; - if (!(file->f_mode & FMODE_READ)) - goto out; - ret = locks_verify_area(FLOCK_VERIFY_READ, file->f_dentry->d_inode, - file, file->f_pos, count); - if (ret) - goto out; - ret = -EINVAL; - if (!file->f_op || !(read = file->f_op->read)) - goto out; - ret = read(file, buf, count, &file->f_pos); -out: - fput(file); -bad_file: - unlock_kernel(); + if (file) { + if (file->f_mode & FMODE_READ) { + ret = locks_verify_area(FLOCK_VERIFY_READ, file->f_dentry->d_inode, + file, file->f_pos, count); + if (!ret) { + ssize_t (*read)(struct file *, char *, size_t, loff_t *); + ret = -EINVAL; + if (file->f_op && (read = file->f_op->read) != NULL) + ret = read(file, buf, count, &file->f_pos); + } + } + fput(file); + } return ret; } @@ -146,31 +140,23 @@ asmlinkage ssize_t sys_write(unsigned int fd, const char * buf, size_t count) { ssize_t ret; struct file * file; - struct inode * inode; - ssize_t (*write)(struct file *, const char *, size_t, loff_t *); - - lock_kernel(); ret = -EBADF; file = fget(fd); - if (!file) - goto bad_file; - if (!(file->f_mode & FMODE_WRITE)) - goto out; - inode = file->f_dentry->d_inode; - ret = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file, + if (file) { + if (file->f_mode & FMODE_WRITE) { + struct inode *inode = file->f_dentry->d_inode; + ret = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file, file->f_pos, count); - if (ret) - goto out; - ret = -EINVAL; - if (!file->f_op || !(write = file->f_op->write)) - goto out; - - ret = write(file, buf, count, &file->f_pos); -out: - fput(file); -bad_file: - unlock_kernel(); + if (!ret) { + ssize_t (*write)(struct file *, const char *, size_t, loff_t *); + ret = -EINVAL; + if (file->f_op && (write = file->f_op->write) != NULL) + ret = write(file, buf, count, &file->f_pos); + } + } + fput(file); + } return ret; } diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c index d35b0d130..e16802e97 100644 --- a/fs/romfs/inode.c +++ b/fs/romfs/inode.c @@ -1,7 +1,7 @@ /* * ROMFS file system, Linux implementation * - * Copyright (C) 1997 Janos Farkas <chexum@shadow.banki.hu> + * Copyright (C) 1997-1999 Janos Farkas <chexum@shadow.banki.hu> * * Using parts of the minix filesystem * Copyright (C) 1991, 1992 Linus Torvalds @@ -57,6 +57,7 @@ #include <linux/fs.h> #include <linux/locks.h> #include <linux/init.h> +#include <linux/smp_lock.h> #include <asm/uaccess.h> @@ -320,7 +321,7 @@ romfs_lookup(struct inode *dir, struct dentry *dentry) const char *name; /* got from dentry */ int len; - res = 0; /* instead of ENOENT */ + res = -EACCES; /* placeholder for "no data here" */ offset = dir->i_ino & ROMFH_MASK; if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) goto out; @@ -335,8 +336,9 @@ romfs_lookup(struct inode *dir, struct dentry *dentry) len = dentry->d_name.len; for(;;) { - if (!offset || offset >= maxoff - || romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) + if (!offset || offset >= maxoff) + goto out0; + if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) goto out; /* try to match the first 16 bytes of name */ @@ -365,19 +367,28 @@ romfs_lookup(struct inode *dir, struct dentry *dentry) if ((ntohl(ri.next) & ROMFH_TYPE) == ROMFH_HRD) offset = ntohl(ri.spec) & ROMFH_MASK; - if ((inode = iget(dir->i_sb, offset))==NULL) { - res = -EACCES; - } else { - d_add(dentry, inode); - } + if ((inode = iget(dir->i_sb, offset))) + goto outi; -out: - return ERR_PTR(res); + /* + * it's a bit funky, _lookup needs to return an error code + * (negative) or a NULL, both as a dentry. ENOENT should not + * be returned, instead we need to create a negative dentry by + * d_add(dentry, NULL); and return 0 as no error. + * (Although as I see, it only matters on writable file + * systems). + */ + +out0: inode = NULL; +outi: res = 0; + d_add (dentry, inode); + +out: return ERR_PTR(res); } /* * Ok, we do readpage, to be able to execute programs. Unfortunately, - * we can't use bmap, since we have looser alignments. + * we can't use bmap, since we may have looser alignments. */ static int @@ -389,12 +400,13 @@ romfs_readpage(struct file * file, struct page * page) unsigned long offset, avail, readlen; int result = -EIO; - atomic_inc(&page->count); - set_bit(PG_locked, &page->flags); - + lock_kernel(); + get_page(page); buf = page_address(page); - clear_bit(PG_uptodate, &page->flags); - clear_bit(PG_error, &page->flags); + + /* hack? */ + page->owner = (int)current; + offset = page->offset; if (offset < inode->i_size) { avail = inode->i_size-offset; @@ -403,18 +415,19 @@ romfs_readpage(struct file * file, struct page * page) if (readlen < PAGE_SIZE) { memset((void *)(buf+readlen),0,PAGE_SIZE-readlen); } - set_bit(PG_uptodate, &page->flags); + SetPageUptodate(page); result = 0; } } if (result) { - set_bit(PG_error, &page->flags); memset((void *)buf, 0, PAGE_SIZE); + SetPageError(page); } - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); + UnlockPage(page); + free_page(buf); + unlock_kernel(); return result; } @@ -508,12 +521,14 @@ static struct inode_operations romfs_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block -- not really */ romfs_readpage, /* readpage */ NULL, /* writepage */ - NULL, /* bmap -- not really */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ + NULL /* revalidate */ }; static struct file_operations romfs_dir_operations = { @@ -550,12 +565,14 @@ static struct inode_operations romfs_dir_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ + NULL /* revalidate */ }; static struct inode_operations romfs_link_inode_operations = { @@ -571,18 +588,20 @@ static struct inode_operations romfs_link_inode_operations = { NULL, /* rename */ romfs_readlink, /* readlink */ romfs_follow_link, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ + NULL /* revalidate */ }; static mode_t romfs_modemap[] = { - 0, S_IFDIR, S_IFREG, S_IFLNK+0777, - S_IFBLK, S_IFCHR, S_IFSOCK, S_IFIFO + 0, S_IFDIR+0644, S_IFREG+0644, S_IFLNK+0777, + S_IFBLK+0600, S_IFCHR+0600, S_IFSOCK+0644, S_IFIFO+0644 }; static struct inode_operations *romfs_inoops[] = @@ -591,10 +610,10 @@ static struct inode_operations *romfs_inoops[] = &romfs_dir_inode_operations, &romfs_file_inode_operations, &romfs_link_inode_operations, - &blkdev_inode_operations, /* standard handlers */ - &chrdev_inode_operations, - NULL, /* socket */ - NULL, /* fifo */ + NULL, /* device/fifo/socket nodes, */ + NULL, /* set by init_special_inode */ + NULL, + NULL, }; static void @@ -627,34 +646,30 @@ romfs_read_inode(struct inode *i) i->i_mtime = i->i_atime = i->i_ctime = 0; i->i_uid = i->i_gid = 0; - i->i_op = romfs_inoops[nextfh & ROMFH_TYPE]; - - /* Precalculate the data offset */ - ino = romfs_strnlen(i, ino+ROMFH_SIZE, ROMFS_MAXFN); - if (ino >= 0) - ino = ((ROMFH_SIZE+ino+1+ROMFH_PAD)&ROMFH_MASK); - else - ino = 0; - - i->u.romfs_i.i_metasize = ino; - i->u.romfs_i.i_dataoffset = ino+(i->i_ino&ROMFH_MASK); - - /* Compute permissions */ - ino = S_IRUGO|S_IWUSR; - ino |= romfs_modemap[nextfh & ROMFH_TYPE]; - if (nextfh & ROMFH_EXEC) { - ino |= S_IXUGO; - } - i->i_mode = ino; - - if (S_ISFIFO(ino)) - init_fifo(i); - else if (S_ISDIR(ino)) - i->i_size = i->u.romfs_i.i_metasize; - else if (S_ISBLK(ino) || S_ISCHR(ino)) { - i->i_mode &= ~(S_IRWXG|S_IRWXO); - ino = ntohl(ri.spec); - i->i_rdev = MKDEV(ino>>16,ino&0xffff); + /* Precalculate the data offset */ + ino = romfs_strnlen(i, ino+ROMFH_SIZE, ROMFS_MAXFN); + if (ino >= 0) + ino = ((ROMFH_SIZE+ino+1+ROMFH_PAD)&ROMFH_MASK); + else + ino = 0; + + i->u.romfs_i.i_metasize = ino; + i->u.romfs_i.i_dataoffset = ino+(i->i_ino&ROMFH_MASK); + + /* Compute permissions */ + ino = romfs_modemap[nextfh & ROMFH_TYPE]; + /* only "normal" files have ops */ + if ((i->i_op = romfs_inoops[nextfh & ROMFH_TYPE])) { + if (nextfh & ROMFH_EXEC) + ino |= S_IXUGO; + i->i_mode = ino; + if (S_ISDIR(ino)) + i->i_size = i->u.romfs_i.i_metasize; + } else { + /* depending on MBZ for sock/fifos */ + nextfh = ntohl(ri.spec); + nextfh = kdev_t_to_nr(MKDEV(nextfh>>16,nextfh&0xffff)); + init_special_inode(i, ino, nextfh); } } diff --git a/fs/select.c b/fs/select.c index e47e3b0b4..4cb1a6d55 100644 --- a/fs/select.c +++ b/fs/select.c @@ -64,9 +64,9 @@ void __pollwait(struct file * filp, wait_queue_head_t * wait_address, poll_table if (p->nr < __MAX_POLL_TABLE_ENTRIES) { struct poll_table_entry * entry; ok_table: - entry = p->entry + p->nr; - entry->filp = filp; - filp->f_count++; + entry = p->entry + p->nr; + entry->filp = filp; + atomic_inc(&filp->f_count); entry->wait_address = wait_address; init_waitqueue_entry(&entry->wait, current); add_wait_queue(wait_address,&entry->wait); @@ -181,17 +181,13 @@ int do_select(int n, fd_set_bits *fds, long *timeout) off = i / __NFDBITS; if (!(bit & BITS(fds, off))) continue; - /* - * The poll_wait routine will increment f_count if - * the file is added to the wait table, so we don't - * need to increment it now. - */ - file = fcheck(i); + file = fget(i); mask = POLLNVAL; if (file) { mask = DEFAULT_POLLMASK; if (file->f_op && file->f_op->poll) mask = file->f_op->poll(file, wait); + fput(file); } if ((mask & POLLIN_SET) && ISSET(bit, __IN(fds,off))) { SET(bit, __RES_IN(fds,off)); @@ -347,14 +343,14 @@ static int do_poll(unsigned int nfds, struct pollfd *fds, poll_table *wait, mask = 0; fd = fdpnt->fd; if (fd >= 0) { - /* poll_wait increments f_count if needed */ - struct file * file = fcheck(fd); + struct file * file = fget(fd); mask = POLLNVAL; if (file != NULL) { mask = DEFAULT_POLLMASK; if (file->f_op && file->f_op->poll) mask = file->f_op->poll(file, wait); mask &= fdpnt->events | POLLERR | POLLHUP; + fput(file); } if (mask) { wait = NULL; diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c index 870ebac74..cbaa646b3 100644 --- a/fs/smbfs/dir.c +++ b/fs/smbfs/dir.c @@ -59,9 +59,10 @@ struct inode_operations smb_dir_inode_operations = smb_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index cfb2d82da..682b511f6 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -404,9 +404,10 @@ struct inode_operations smb_file_inode_operations = NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ smb_readpage, /* readpage */ smb_writepage, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ smb_file_permission, /* permission */ NULL, /* smap */ diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index 627186cae..cf3539823 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -6,9 +6,7 @@ * */ -#include <linux/config.h> #include <linux/module.h> - #include <linux/sched.h> #include <linux/kernel.h> #include <linux/mm.h> diff --git a/fs/sysv/balloc.c b/fs/sysv/balloc.c index 375ff6f75..f5240a75c 100644 --- a/fs/sysv/balloc.c +++ b/fs/sysv/balloc.c @@ -201,7 +201,7 @@ int sysv_new_block(struct super_block * sb) unlock_super(sb); return 0; } - if (bh->b_count != 1) { + if (atomic_read(&bh->b_count) != 1) { printk("sysv_new_block: block already in use\n"); unlock_super(sb); return 0; diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c index a28a7ba8e..d76f1d6b3 100644 --- a/fs/sysv/dir.c +++ b/fs/sysv/dir.c @@ -59,11 +59,14 @@ struct inode_operations sysv_dir_inode_operations = { sysv_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; static int sysv_readdir(struct file * filp, void * dirent, filldir_t filldir) diff --git a/fs/sysv/file.c b/fs/sysv/file.c index 19443f289..3019c913f 100644 --- a/fs/sysv/file.c +++ b/fs/sysv/file.c @@ -33,50 +33,14 @@ #include <linux/fs.h> #include <linux/sysv_fs.h> -static int sysv_writepage (struct file * file, struct page * page) -{ - struct dentry *dentry = file->f_dentry; - struct inode *inode = dentry->d_inode; - unsigned long block; - int *p, nr[PAGE_SIZE/512]; - int i, err, created; - struct buffer_head *bh; - - i = PAGE_SIZE >> inode->i_sb->sv_block_size_bits; - block = page->offset >> inode->i_sb->sv_block_size_bits; - p = nr; - bh = page->buffers; - do { - if (bh && bh->b_blocknr) - *p = bh->b_blocknr; - else - *p = sysv_getblk_block (inode, block, 1, &err, &created); - if (!*p) - return -EIO; - i--; - block++; - p++; - if (bh) - bh = bh->b_this_page; - } while (i > 0); - - /* IO start */ - brw_page(WRITE, page, inode->i_dev, nr, inode->i_sb->sv_block_size, 1); - return 0; -} - -static long sysv_write_one_page (struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) -{ - return block_write_one_page(file, page, offset, bytes, buf, sysv_getblk_block); -} - /* * Write to a file (through the page cache). */ static ssize_t sysv_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { - return generic_file_write(file, buf, count, ppos, sysv_write_one_page); + return generic_file_write(file, buf, count, + ppos, block_write_partial_page); } /* @@ -113,12 +77,12 @@ struct inode_operations sysv_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - generic_readpage, /* readpage */ - sysv_writepage, /* writepage */ - sysv_bmap, /* bmap */ + sysv_get_block, /* get_block */ + block_read_full_page, /* readpage */ + block_write_full_page, /* writepage */ + block_flushpage, /* flushpage */ sysv_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* revalidate */ - block_flushpage, /* flushpage */ + NULL /* revalidate */ }; diff --git a/fs/sysv/fsync.c b/fs/sysv/fsync.c index ddffd7de2..b0e1138c7 100644 --- a/fs/sysv/fsync.c +++ b/fs/sysv/fsync.c @@ -54,7 +54,7 @@ static int sync_block (struct inode * inode, u32 *blockp, int convert, int wait) return 0; } ll_rw_block(WRITE, 1, &bh); - bh->b_count--; + atomic_dec(&bh->b_count); return 0; } diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index d335b5b50..ff10e778d 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -31,6 +31,7 @@ #include <linux/string.h> #include <linux/locks.h> #include <linux/init.h> +#include <linux/smp_lock.h> #include <asm/byteorder.h> #include <asm/uaccess.h> @@ -62,6 +63,11 @@ static void sysv_delete_inode(struct inode *inode) sysv_free_inode(inode); } +static void sysv_put_super(struct super_block *); +static void sysv_write_super(struct super_block *); +static void sysv_read_inode(struct inode *); +static int sysv_notify_change(struct dentry *, struct iattr *); +static int sysv_statfs(struct super_block *, struct statfs *, int); static struct super_operations sysv_sops = { sysv_read_inode, @@ -338,8 +344,8 @@ static struct super_block * detected_coherent (struct super_block *sb, struct bu return sb; } -struct super_block *sysv_read_super(struct super_block *sb,void *data, - int silent) +static struct super_block *sysv_read_super(struct super_block *sb, + void *data, int silent) { struct buffer_head *bh; const char *found; @@ -519,7 +525,7 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data, } /* This is only called on sync() and umount(), when s_dirt=1. */ -void sysv_write_super (struct super_block *sb) +static void sysv_write_super(struct super_block *sb) { lock_super(sb); if (buffer_dirty(sb->sv_bh1) || buffer_dirty(sb->sv_bh2)) { @@ -542,7 +548,7 @@ void sysv_write_super (struct super_block *sb) unlock_super(sb); } -void sysv_put_super(struct super_block *sb) +static void sysv_put_super(struct super_block *sb) { /* we can assume sysv_write_super() has already been called, and that the superblock is locked */ @@ -555,7 +561,7 @@ void sysv_put_super(struct super_block *sb) MOD_DEC_USE_COUNT; } -int sysv_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) +static int sysv_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { struct statfs tmp; @@ -597,59 +603,72 @@ static int block_bmap(struct super_block * sb, struct buffer_head * bh, int nr, return tmp + sb->sv_block_base; } -int sysv_bmap(struct inode * inode,int block_nr) +static unsigned int sysv_block_map(struct inode *inode, unsigned int block) { - unsigned int block = block_nr; - struct super_block * sb = inode->i_sb; - int convert; - int i; - struct buffer_head * bh; + struct super_block *sb; + int i, ret, convert; - if (block < 10) - return inode_bmap(sb,inode,block); + ret = 0; + lock_kernel(); + sb = inode->i_sb; + if (block < 10) { + ret = inode_bmap(sb, inode, block); + goto out; + } block -= 10; convert = sb->sv_convert; if (block < sb->sv_ind_per_block) { - i = inode_bmap(sb,inode,10); + i = inode_bmap(sb, inode, 10); if (!i) - return 0; - bh = bread(inode->i_dev,i,sb->sv_block_size); - return block_bmap(sb, bh, block, convert); + goto out; + ret = block_bmap(sb, + bread(inode->i_dev, i, sb->sv_block_size), + block, convert); + goto out; } block -= sb->sv_ind_per_block; if (block < sb->sv_ind_per_block_2) { - i = inode_bmap(sb,inode,11); + i = inode_bmap(sb, inode, 11); if (!i) - return 0; - bh = bread(inode->i_dev,i,sb->sv_block_size); - i = block_bmap(sb, bh, block >> sb->sv_ind_per_block_bits, convert); + goto out; + i = block_bmap(sb, + bread(inode->i_dev, i, sb->sv_block_size), + (block >> sb->sv_ind_per_block_bits), convert); if (!i) - return 0; - bh = bread(inode->i_dev,i,sb->sv_block_size); - return block_bmap(sb, bh, block & sb->sv_ind_per_block_1, convert); + goto out; + ret = block_bmap(sb, + bread(inode->i_dev, i, sb->sv_block_size), + (block & sb->sv_ind_per_block_1), convert); + goto out; } block -= sb->sv_ind_per_block_2; if (block < sb->sv_ind_per_block_3) { - i = inode_bmap(sb,inode,12); + i = inode_bmap(sb, inode, 12); if (!i) - return 0; - bh = bread(inode->i_dev,i,sb->sv_block_size); - i = block_bmap(sb, bh, block >> sb->sv_ind_per_block_2_bits, convert); + goto out; + i = block_bmap(sb, + bread(inode->i_dev, i, sb->sv_block_size), + (block >> sb->sv_ind_per_block_2_bits), convert); if (!i) - return 0; - bh = bread(inode->i_dev,i,sb->sv_block_size); - i = block_bmap(sb, bh, (block >> sb->sv_ind_per_block_bits) & sb->sv_ind_per_block_1,convert); + goto out; + ret = block_bmap(sb, + bread(inode->i_dev, i, sb->sv_block_size), + ((block >> sb->sv_ind_per_block_bits) & + sb->sv_ind_per_block_1), convert); if (!i) - return 0; - bh = bread(inode->i_dev,i,sb->sv_block_size); - return block_bmap(sb, bh, block & sb->sv_ind_per_block_1, convert); - } - if ((int)block<0) { - printk("sysv_bmap: block<0"); - return 0; + goto out; + ret = block_bmap(sb, + bread(inode->i_dev, i, sb->sv_block_size), + (block & sb->sv_ind_per_block_1), convert); + goto out; } - printk("sysv_bmap: block>big"); - return 0; + if ((int)block < 0) + printk("sysv_block_map: block < 0\n"); + else + printk("sysv_block_map: block > big\n"); +out: + unlock_kernel(); + return ret; } /* End of bmap support. */ @@ -657,8 +676,8 @@ int sysv_bmap(struct inode * inode,int block_nr) /* Access selected blocks of regular files (or directories) */ -static struct buffer_head * inode_getblk(struct inode * inode, int nr, int create, - int metadata, int *phys_block, int *created) +static struct buffer_head *inode_getblk(struct inode *inode, int nr, int new_block, + int *err, int metadata, long *phys, int *new) { struct super_block *sb; u32 tmp; @@ -677,15 +696,30 @@ repeat: brelse(result); goto repeat; } else { - *phys_block = tmp; + *phys = tmp; return NULL; } } - if (!create) - return NULL; + *err = -EFBIG; + + /* Check file limits.. */ + { + unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur; + if (limit < RLIM_INFINITY) { + limit >>= sb->sv_block_size_bits; + if (new_block >= limit) { + send_sig(SIGXFSZ, current, 0); + *err = -EFBIG; + return NULL; + } + } + } + tmp = sysv_new_block(sb); - if (!tmp) + if (!tmp) { + *err = -ENOSPC; return NULL; + } if (metadata) { result = sv_getblk(sb, inode->i_dev, tmp); if (*p) { @@ -695,12 +729,18 @@ repeat: } } else { if (*p) { + /* + * Nobody is allowed to change block allocation + * state from under us: + */ + BUG(); sysv_free_block(sb, tmp); goto repeat; } - *phys_block = tmp; + *phys = tmp; result = NULL; - *created = 1; + *err = 0; + *new = 1; } *p = tmp; @@ -709,24 +749,24 @@ repeat: return result; } -static struct buffer_head * block_getblk(struct inode * inode, - struct buffer_head * bh, int nr, int create, - int metadata, int *phys_block, int *created) +static struct buffer_head *block_getblk(struct inode *inode, + struct buffer_head *bh, int nr, int new_block, int *err, + int metadata, long *phys, int *new) { struct super_block *sb; u32 tmp, block; sysv_zone_t *p; struct buffer_head * result; + unsigned long limit; + result = NULL; if (!bh) - return NULL; + goto out; if (!buffer_uptodate(bh)) { ll_rw_block(READ, 1, &bh); wait_on_buffer(bh); - if (!buffer_uptodate(bh)) { - brelse(bh); - return NULL; - } + if (!buffer_uptodate(bh)) + goto out; } sb = inode->i_sb; p = nr + (sysv_zone_t *) bh->b_data; @@ -737,120 +777,175 @@ repeat: if (tmp) { if (metadata) { result = sv_getblk(sb, bh->b_dev, block); - if (tmp == *p) { - brelse(bh); - return result; - } + if (tmp == *p) + goto out; brelse(result); goto repeat; } else { - *phys_block = tmp; - brelse(bh); - return NULL; + *phys = tmp; + goto out; } } - if (!create) { - brelse(bh); - return NULL; + *err = -EFBIG; + + limit = current->rlim[RLIMIT_FSIZE].rlim_cur; + if (limit < RLIM_INFINITY) { + limit >>= sb->sv_block_size_bits; + if (new_block >= limit) { + send_sig(SIGXFSZ, current, 0); + goto out; + } } + block = sysv_new_block(sb); - if (!block) { - brelse(bh); - return NULL; - } + if (!block) + goto out; if (metadata) { result = sv_getblk(sb, bh->b_dev, block); if (*p) { - sysv_free_block(sb,block); + sysv_free_block(sb, block); brelse(result); goto repeat; } + memset(result->b_data, 0, sb->sv_block_size); + mark_buffer_uptodate(result, 1); + mark_buffer_dirty(result, 1); } else { - *phys_block = tmp; - result = NULL; - *created = 1; + *phys = tmp; + *new = 1; + } + if (*p) { + sysv_free_block(sb, block); + brelse(result); + goto repeat; } *p = (sb->sv_convert ? to_coh_ulong(block) : block); mark_buffer_dirty(bh, 1); + *err = 0; +out: brelse(bh); return result; } -int sysv_getblk_block(struct inode *inode, long block, int create, - int *err, int *created) +int sysv_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create) { - struct super_block *sb = inode->i_sb; - struct buffer_head *bh, *tmp; - int phys_block; + struct super_block *sb; + int ret, err, new; + struct buffer_head *bh; + unsigned long ptr, phys; - *err = -EIO; - if (block < 0) { - printk("sysv_getblk: block<0"); - return 0; - } - if (block > sb->sv_ind_per_block_3) { - printk("sysv_getblk: block>big"); + if (!create) { + phys = sysv_block_map(inode, iblock); + if (phys) { + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = phys; + bh_result->b_state |= (1UL << BH_Mapped); + } return 0; } - if (block < 10) { - tmp = inode_getblk(inode, block, create, - 0, &phys_block, created); + + err = -EIO; + new = 0; + ret = 0; + bh = NULL; + + lock_kernel(); + sb = inode->i_sb; + if (iblock < 0) + goto abort_negative; + if (iblock > sb->sv_ind_per_block_3) + goto abort_too_big; + + err = 0; + ptr = iblock; + + /* + * ok, these macros clean the logic up a bit and make + * it much more readable: + */ +#define GET_INODE_DATABLOCK(x) \ + inode_getblk(inode, x, iblock, &err, 0, &phys, &new) +#define GET_INODE_PTR(x) \ + inode_getblk(inode, x, iblock, &err, 1, NULL, NULL) +#define GET_INDIRECT_DATABLOCK(x) \ + block_getblk (inode, bh, x, iblock, &err, 0, &phys, &new); +#define GET_INDIRECT_PTR(x) \ + block_getblk (inode, bh, x, iblock, &err, 1, NULL, NULL); + + if (ptr < 10) { + bh = GET_INODE_DATABLOCK(ptr); goto out; } - block -= 10; - if (block < sb->sv_ind_per_block) { - bh = inode_getblk(inode, 10, create, 1, NULL, NULL); - tmp = block_getblk(inode, bh, block, create, - 0, &phys_block, created); - goto out; + ptr -= 10; + if (ptr < sb->sv_ind_per_block) { + bh = GET_INODE_PTR(10); + goto get_indirect; } - block -= sb->sv_ind_per_block; - if (block < sb->sv_ind_per_block_2) { - bh = inode_getblk(inode, 11, create, 1, NULL, NULL); - bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_bits, create, - 1, NULL, NULL); - tmp = block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create, - 0, &phys_block, created); - goto out; + ptr -= sb->sv_ind_per_block; + if (ptr < sb->sv_ind_per_block_2) { + bh = GET_INODE_PTR(11); + goto get_double; } - block -= sb->sv_ind_per_block_2; - bh = inode_getblk(inode, 12, create, 1, NULL, NULL); - bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_2_bits, create, - 1, NULL, NULL); - bh = block_getblk(inode, bh, - (block >> sb->sv_ind_per_block_bits) & sb->sv_ind_per_block_1, - create, 1, NULL, NULL); - tmp = block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create, - 0, &phys_block, created); + ptr -= sb->sv_ind_per_block_2; + bh = GET_INODE_PTR(12); + bh = GET_INDIRECT_PTR(ptr >> sb->sv_ind_per_block_2_bits); +get_double: + bh = GET_INDIRECT_PTR((ptr >> sb->sv_ind_per_block_bits) & sb->sv_ind_per_block_1); +get_indirect: + bh = GET_INDIRECT_DATABLOCK(ptr & sb->sv_ind_per_block_1); + +#undef GET_INODE_DATABLOCK +#undef GET_INODE_PTR +#undef GET_INDIRECT_DATABLOCK +#undef GET_INDIRECT_PTR out: - *err = 0; - return phys_block; + if (err) + goto abort; + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = phys; + bh_result->b_state |= (1UL << BH_Mapped); + if (new) + bh_result->b_state |= (1UL << BH_New); +abort: + unlock_kernel(); + return err; + +abort_negative: + printk("sysv_getblk: block < 0\n"); + goto abort; + +abort_too_big: + printk("sysv_getblk: block > big\n"); + goto abort; } -struct buffer_head *sysv_getblk (struct inode *inode, unsigned int block, int create) +struct buffer_head *sysv_getblk(struct inode *inode, unsigned int block, int create) { - struct buffer_head *tmp = NULL; - int phys_block; - int err, created; - - phys_block = sysv_getblk_block(inode, block, create, &err, &created); - if (phys_block) { - tmp = getblk(inode->i_dev, phys_block, BLOCK_SIZE); - if (created) { - memset(tmp->b_data, 0, BLOCK_SIZE); - mark_buffer_uptodate(tmp, 1); - mark_buffer_dirty(tmp, 1); + struct buffer_head dummy; + int error; + + dummy.b_state = 0; + dummy.b_blocknr = -1000; + error = sysv_get_block(inode, block, &dummy, create); + if (!error && buffer_mapped(&dummy)) { + struct buffer_head *bh; + bh = getblk(dummy.b_dev, dummy.b_blocknr, BLOCK_SIZE); + if (buffer_new(&dummy)) { + memset(bh->b_data, 0, BLOCK_SIZE); + mark_buffer_uptodate(bh, 1); + mark_buffer_dirty(bh, 1); } + return bh; } - return tmp; + return NULL; } -struct buffer_head * sysv_file_bread(struct inode * inode, int block, int create) +struct buffer_head *sysv_file_bread(struct inode *inode, int block, int create) { - struct buffer_head * bh; + struct buffer_head *bh; - bh = sysv_getblk(inode,block,create); + bh = sysv_getblk(inode, block, create); if (!bh || buffer_uptodate(bh)) return bh; ll_rw_block(READ, 1, &bh); @@ -903,7 +998,7 @@ static inline void coh_write3byte (unsigned char * p, unsigned long val) *(unsigned short *)(p+1) = (unsigned short) val; } -void sysv_read_inode(struct inode * inode) +static void sysv_read_inode(struct inode *inode) { struct super_block * sb = inode->i_sb; struct buffer_head * bh; @@ -971,7 +1066,7 @@ void sysv_read_inode(struct inode * inode) } /* To avoid inconsistencies between inodes in memory and inodes on disk. */ -int sysv_notify_change(struct dentry *dentry, struct iattr *attr) +static int sysv_notify_change(struct dentry *dentry, struct iattr *attr) { struct inode *inode = dentry->d_inode; int error; diff --git a/fs/sysv/symlink.c b/fs/sysv/symlink.c index 5ebaec7cc..df611d589 100644 --- a/fs/sysv/symlink.c +++ b/fs/sysv/symlink.c @@ -39,11 +39,14 @@ struct inode_operations sysv_symlink_inode_operations = { NULL, /* rename */ sysv_readlink, /* readlink */ sysv_follow_link, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; static struct dentry *sysv_follow_link(struct dentry * dentry, diff --git a/fs/sysv/truncate.c b/fs/sysv/truncate.c index a8c0e0745..db0f72506 100644 --- a/fs/sysv/truncate.c +++ b/fs/sysv/truncate.c @@ -36,7 +36,7 @@ */ #define DATA_BUFFER_USED(bh) \ - ((bh->b_count > 1) || buffer_locked(bh)) + (atomic_read(&bh->b_count) || buffer_locked(bh)) /* We throw away any data beyond inode->i_size. */ diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c index 6a79c4a5e..b93d04f0b 100644 --- a/fs/ufs/dir.c +++ b/fs/ufs/dir.c @@ -55,10 +55,8 @@ ufs_readdir (struct file * filp, void * dirent, filldir_t filldir) while (!error && !stored && filp->f_pos < inode->i_size) { lblk = (filp->f_pos) >> sb->s_blocksize_bits; - /* XXX - ufs_bmap() call needs error checking */ - blk = ufs_bmap(inode, lblk); - bh = bread (sb->s_dev, blk, sb->s_blocksize); - if (!bh) { + blk = ufs_frag_map(inode, lblk); + if (!blk || !(bh = bread (sb->s_dev, blk, sb->s_blocksize))) { /* XXX - error - skip to the next block */ printk("ufs_readdir: " "dir inode %lu has a hole at offset %lu\n", @@ -209,10 +207,12 @@ struct inode_operations ufs_dir_inode_operations = { ufs_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ ufs_permission, /* permission */ NULL, /* smap */ + NULL /* revalidate */ }; diff --git a/fs/ufs/file.c b/fs/ufs/file.c index 9e027cfc3..33d68ba5d 100644 --- a/fs/ufs/file.c +++ b/fs/ufs/file.c @@ -87,49 +87,23 @@ static inline void remove_suid(struct inode *inode) } } -static int ufs_writepage (struct file *file, struct page *page) -{ - struct dentry *dentry = file->f_dentry; - struct inode *inode = dentry->d_inode; - unsigned long block; - int *p, nr[PAGE_SIZE/512]; - int i, err, created; - struct buffer_head *bh; - - i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits; - block = page->offset >> inode->i_sb->s_blocksize_bits; - p = nr; - bh = page->buffers; - do { - if (bh && bh->b_blocknr) - *p = bh->b_blocknr; - else - *p = ufs_getfrag_block(inode, block, 1, &err, &created); - if (!*p) - return -EIO; - i--; - block++; - p++; - if (bh) - bh = bh->b_this_page; - } while (i > 0); - - brw_page(WRITE, page, inode->i_dev, nr, inode->i_sb->s_blocksize, 1); - return 0; -} - -static long ufs_write_one_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char *buf) -{ - return block_write_one_page(file, page, offset, bytes, buf, ufs_getfrag_block); -} - /* * Write to a file (through the page cache). */ static ssize_t ufs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { - return generic_file_write(file, buf, count, ppos, ufs_write_one_page); + ssize_t retval; + + retval = generic_file_write(file, buf, count, + ppos, block_write_partial_page); + if (retval > 0) { + struct inode *inode = file->f_dentry->d_inode; + remove_suid(inode); + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + mark_inode_dirty(inode); + } + return retval; } /* @@ -176,12 +150,12 @@ struct inode_operations ufs_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - generic_readpage, /* readpage */ - ufs_writepage, /* writepage */ - ufs_bmap, /* bmap */ + ufs_getfrag_block, /* get_block */ + block_read_full_page, /* readpage */ + block_write_full_page, /* writepage */ + block_flushpage, /* flushpage */ ufs_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* revalidate */ - block_flushpage, /* flushpage */ + NULL /* revalidate */ }; diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 636b0aabd..3d9c8f602 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -36,6 +36,7 @@ #include <linux/string.h> #include <linux/locks.h> #include <linux/mm.h> +#include <linux/smp_lock.h> #include "swab.h" #include "util.h" @@ -82,10 +83,10 @@ static void ufs_print_inode(struct inode * inode) #define ufs_inode_bmap(inode, nr) \ (SWAB32((inode)->u.ufs_i.i_u1.i_data[(nr) >> uspi->s_fpbshift]) + ((nr) & uspi->s_fpbmask)) -static inline unsigned ufs_block_bmap (struct buffer_head * bh, unsigned nr, +static inline unsigned int ufs_block_bmap (struct buffer_head * bh, unsigned nr, struct ufs_sb_private_info * uspi, unsigned swab) { - unsigned tmp; + unsigned int tmp; UFSD(("ENTER, nr %u\n", nr)) if (!bh) @@ -96,86 +97,90 @@ static inline unsigned ufs_block_bmap (struct buffer_head * bh, unsigned nr, return tmp; } -int ufs_bmap (struct inode * inode, int fragment) +int ufs_frag_map(struct inode *inode, int frag) { - struct super_block * sb; - struct ufs_sb_private_info * uspi; - unsigned tmp; - unsigned swab; + struct super_block *sb; + struct ufs_sb_private_info *uspi; + unsigned int swab; + int i, ret; + + ret = 0; + lock_kernel(); sb = inode->i_sb; uspi = sb->u.ufs_sb.s_uspi; swab = sb->u.ufs_sb.s_swab; - - UFSD(("ENTER, ino %lu, fragment %u\n", inode->i_ino, fragment)) - - if (fragment >= ((UFS_NDADDR + uspi->s_apb + uspi->s_2apb + uspi->s_3apb) << uspi->s_fpbshift)) { - ufs_warning (sb, "ufs_bmap", "block > big"); - return 0; + if (frag < 0) { + ufs_warning(sb, "ufs_frag_map", "frag < 0"); + goto out; } - - /* - * direct fragment - */ - if (fragment < UFS_NDIR_FRAGMENT) - return (uspi->s_sbbase + ufs_inode_bmap (inode, fragment)); - - /* - * indirect fragment - */ - fragment -= UFS_NDIR_FRAGMENT; - if (fragment < (1 << (uspi->s_apbshift + uspi->s_fpbshift))) { - tmp = ufs_inode_bmap (inode, - UFS_IND_FRAGMENT + (fragment >> uspi->s_apbshift)); - if (!tmp) - return 0; - return (uspi->s_sbbase + - ufs_block_bmap (bread (sb->s_dev, uspi->s_sbbase + tmp, sb->s_blocksize), - fragment & uspi->s_apbmask, uspi, swab)); + if (frag >= + ((UFS_NDADDR + uspi->s_apb + uspi->s_2apb + uspi->s_3apb) + << uspi->s_fpbshift)) { + ufs_warning(sb, "ufs_frag_map", "frag > big"); + goto out; } - /* - * dindirect fragment - */ - fragment -= 1 << (uspi->s_apbshift + uspi->s_fpbshift); - if (fragment < (1 << (uspi->s_2apbshift + uspi->s_fpbshift))) { - tmp = ufs_inode_bmap (inode, - UFS_DIND_FRAGMENT + (fragment >> uspi->s_2apbshift)); - if (!tmp) - return 0; - tmp = ufs_block_bmap (bread (sb->s_dev, uspi->s_sbbase + tmp, sb->s_blocksize), - (fragment >> uspi->s_apbshift) & uspi->s_apbmask, uspi, swab); - if (!tmp) - return 0; - return (uspi->s_sbbase + - ufs_block_bmap (bread (sb->s_dev, uspi->s_sbbase + tmp, sb->s_blocksize), - fragment & uspi->s_apbmask, uspi, swab)); + if (frag < UFS_NDIR_FRAGMENT) { + ret = uspi->s_sbbase + ufs_inode_bmap(inode, frag); + goto out; } - /* - * tindirect fragment - */ - fragment -= 1 << (uspi->s_2apbshift + uspi->s_fpbshift); - tmp = ufs_inode_bmap (inode, - UFS_TIND_FRAGMENT + (fragment >> uspi->s_3apbshift)); - if (!tmp) - return 0; - tmp = ufs_block_bmap (bread (sb->s_dev, uspi->s_sbbase + tmp, sb->s_blocksize), - (fragment >> uspi->s_2apbshift) & uspi->s_apbmask, uspi, swab); - if (!tmp) - return 0; - tmp = ufs_block_bmap (bread (sb->s_dev, uspi->s_sbbase + tmp, sb->s_blocksize), - (fragment >> uspi->s_apbshift) & uspi->s_apbmask, uspi, swab); - if (!tmp) - return 0; - return (uspi->s_sbbase + - ufs_block_bmap (bread (sb->s_dev, uspi->s_sbbase + tmp, sb->s_blocksize), - fragment & uspi->s_apbmask, uspi, swab)); + frag -= UFS_NDIR_FRAGMENT; + if (frag < (1 << (uspi->s_apbshift + uspi->s_fpbshift))) { + i = ufs_inode_bmap(inode, + UFS_IND_FRAGMENT + (frag >> uspi->s_apbshift)); + if (!i) + goto out; + ret = (uspi->s_sbbase + + ufs_block_bmap(bread(sb->s_dev, uspi->s_sbbase + i, + sb->s_blocksize), + frag & uspi->s_apbmask, uspi, swab)); + } + frag -= 1 << (uspi->s_apbshift + uspi->s_fpbshift); + if (frag < (1 << (uspi->s_2apbshift + uspi->s_fpbshift))) { + i = ufs_inode_bmap (inode, + UFS_DIND_FRAGMENT + (frag >> uspi->s_2apbshift)); + if (!i) + goto out; + i = ufs_block_bmap(bread(sb->s_dev, uspi->s_sbbase + i, + sb->s_blocksize), + (frag >> uspi->s_apbshift) & uspi->s_apbmask, + uspi, swab); + if (!i) + goto out; + ret = (uspi->s_sbbase + + ufs_block_bmap(bread(sb->s_dev, uspi->s_sbbase + i, + sb->s_blocksize), + (frag & uspi->s_apbmask), uspi, swab)); + goto out; + } + frag -= 1 << (uspi->s_2apbshift + uspi->s_fpbshift); + i = ufs_inode_bmap(inode, + UFS_TIND_FRAGMENT + (frag >> uspi->s_3apbshift)); + if (!i) + goto out; + i = ufs_block_bmap(bread(sb->s_dev, uspi->s_sbbase + i, sb->s_blocksize), + (frag >> uspi->s_2apbshift) & uspi->s_apbmask, + uspi, swab); + if (!i) + goto out; + i = ufs_block_bmap(bread(sb->s_dev, uspi->s_sbbase + i, sb->s_blocksize), + (frag >> uspi->s_apbshift) & uspi->s_apbmask, + uspi, swab); + if (!i) + goto out; + ret = (uspi->s_sbbase + + ufs_block_bmap(bread(sb->s_dev, uspi->s_sbbase + i, sb->s_blocksize), + (frag & uspi->s_apbmask), uspi, swab)); +out: + unlock_kernel(); + return ret; } -static struct buffer_head * ufs_inode_getfrag (struct inode * inode, - unsigned fragment, unsigned new_fragment, int create, - unsigned required, int *err, int metadata, int *phys_block, int *created) +static struct buffer_head * ufs_inode_getfrag (struct inode *inode, + unsigned int fragment, unsigned int new_fragment, + unsigned int required, int *err, int metadata, long *phys, int *new) { struct super_block * sb; struct ufs_sb_private_info * uspi; @@ -184,7 +189,7 @@ static struct buffer_head * ufs_inode_getfrag (struct inode * inode, unsigned block, blockoff, lastfrag, lastblock, lastblockoff; unsigned tmp, goal; u32 * p, * p2; - unsigned swab; + unsigned int swab; UFSD(("ENTER, ino %lu, fragment %u, new_fragment %u, required %u\n", inode->i_ino, fragment, new_fragment, required)) @@ -206,18 +211,17 @@ repeat: sb->s_blocksize); if (tmp == SWAB32(*p)) { UFSD(("EXIT, result %u\n", tmp + blockoff)) - return result; + return result; } brelse (result); goto repeat; } else { - *phys_block = tmp; + *phys = tmp; return NULL; } } *err = -EFBIG; - if (!create) - return NULL; + limit = current->rlim[RLIMIT_FSIZE].rlim_cur; if (limit < RLIM_INFINITY) { limit >>= sb->s_blocksize_bits; @@ -226,6 +230,7 @@ repeat: return NULL; } } + lastblock = ufs_fragstoblks (lastfrag); lastblockoff = ufs_fragnum (lastfrag); /* @@ -270,10 +275,10 @@ repeat: } if (!tmp) { if ((!blockoff && SWAB32(*p)) || - (blockoff && lastfrag != inode->u.ufs_i.i_lastfrag)) + (blockoff && lastfrag != inode->u.ufs_i.i_lastfrag)) goto repeat; - else - return NULL; + *err = -ENOSPC; + return NULL; } /* The nullification of framgents done in ufs/balloc.c is @@ -283,10 +288,10 @@ repeat: if (metadata) { result = getblk (inode->i_dev, tmp + blockoff, sb->s_blocksize); } else { - *phys_block = tmp; + *phys = tmp; result = NULL; *err = 0; - *created = 1; + *new = 1; } inode->i_ctime = CURRENT_TIME; @@ -297,16 +302,16 @@ repeat: return result; } -static struct buffer_head * ufs_block_getfrag (struct inode * inode, - struct buffer_head * bh, unsigned fragment, unsigned new_fragment, - int create, unsigned blocksize, int * err, int metadata, int *phys_block, int *created) +static struct buffer_head * ufs_block_getfrag (struct inode *inode, + struct buffer_head *bh, unsigned int fragment, unsigned int new_fragment, + unsigned int blocksize, int * err, int metadata, long *phys, int *new) { struct super_block * sb; struct ufs_sb_private_info * uspi; struct buffer_head * result; unsigned tmp, goal, block, blockoff; u32 * p; - unsigned swab; + unsigned int swab; sb = inode->i_sb; swab = sb->u.ufs_sb.s_swab; @@ -316,15 +321,14 @@ static struct buffer_head * ufs_block_getfrag (struct inode * inode, UFSD(("ENTER, ino %lu, fragment %u, new_fragment %u\n", inode->i_ino, fragment, new_fragment)) + result = NULL; if (!bh) - return NULL; + goto out; if (!buffer_uptodate(bh)) { ll_rw_block (READ, 1, &bh); wait_on_buffer (bh); - if (!buffer_uptodate(bh)) { - brelse (bh); - return NULL; - } + if (!buffer_uptodate(bh)) + goto out; } p = (u32 *) bh->b_data + block; @@ -334,24 +338,18 @@ repeat: if (metadata) { result = getblk (bh->b_dev, uspi->s_sbbase + tmp + blockoff, sb->s_blocksize); - if (tmp == SWAB32(*p)) { - brelse (bh); - UFSD(("EXIT, result %u\n", tmp + blockoff)) - return result; - } + if (tmp == SWAB32(*p)) + goto out; brelse (result); goto repeat; } else { - *phys_block = tmp; - brelse (bh); - return NULL; + *phys = tmp; + goto out; } } *err = -EFBIG; - if (!create) { - brelse (bh); - return NULL; - } else { + + { unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur; if (limit < RLIM_INFINITY) { limit >>= sb->s_blocksize_bits; @@ -368,12 +366,9 @@ repeat: goal = bh->b_blocknr + uspi->s_fpb; tmp = ufs_new_fragments (inode, p, ufs_blknum(new_fragment), goal, uspi->s_fpb, err); if (!tmp) { - if (SWAB32(*p)) { + if (SWAB32(*p)) goto repeat; - } else { - brelse (bh); - return NULL; - } + goto out; } /* The nullification of framgents done in ufs/balloc.c is @@ -383,10 +378,8 @@ repeat: if (metadata) { result = getblk (bh->b_dev, tmp + blockoff, sb->s_blocksize); } else { - *phys_block = tmp; - result = NULL; - *err = 0; - *created = 1; + *phys = tmp; + *new = 1; } mark_buffer_dirty(bh, 1); @@ -396,122 +389,137 @@ repeat: } inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); +out: brelse (bh); UFSD(("EXIT, result %u\n", tmp + blockoff)) return result; } -int ufs_getfrag_block (struct inode * inode, long fragment, - int create, int * err, int *created) +int ufs_getfrag_block (struct inode *inode, long fragment, struct buffer_head *bh_result, int create) { struct super_block * sb; struct ufs_sb_private_info * uspi; - struct buffer_head * bh, * tmp; - unsigned f; - unsigned swab; - int phys_block; + struct buffer_head * bh; + unsigned int swab; + int ret, err, new; + unsigned long ptr, phys; sb = inode->i_sb; uspi = sb->u.ufs_sb.s_uspi; swab = sb->u.ufs_sb.s_swab; - *err = -EIO; - UFSD(("ENTER, ino %lu, fragment %u\n", inode->i_ino, fragment)) - if (fragment < 0) { - ufs_warning (sb, "ufs_getblk", "block < 0"); - return 0; - } - if (fragment > ((UFS_NDADDR + uspi->s_apb + uspi->s_2apb + uspi->s_3apb) << uspi->s_fpbshift)) { - ufs_warning (sb, "ufs_getblk", "block > big"); + if (!create) { + phys = ufs_frag_map(inode, fragment); + if (phys) { + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = phys; + bh_result->b_state |= (1UL << BH_Mapped); + } return 0; } - *err = -ENOSPC; - f = fragment; - *created = 0; + err = -EIO; + new = 0; + ret = 0; + bh = NULL; + + lock_kernel(); + + UFSD(("ENTER, ino %lu, fragment %u\n", inode->i_ino, fragment)) + if (fragment < 0) + goto abort_negative; + if (fragment > + ((UFS_NDADDR + uspi->s_apb + uspi->s_2apb + uspi->s_3apb) + << uspi->s_fpbshift)) + goto abort_too_big; + + err = 0; + ptr = fragment; /* - * Direct fragment - */ - if (fragment < UFS_NDIR_FRAGMENT) { - tmp = ufs_inode_getfrag (inode, fragment, fragment, create, 1, - err, 0, &phys_block, created); - goto out; - } - /* - * Indirect fragment - */ - fragment -= UFS_NDIR_FRAGMENT; - if (fragment < (1 << (uspi->s_apbshift + uspi->s_fpbshift))) { - bh = ufs_inode_getfrag (inode, - UFS_IND_FRAGMENT + (fragment >> uspi->s_apbshift), - f, create, uspi->s_fpb, err, 1, NULL, NULL); - tmp = ufs_block_getfrag (inode, bh, - fragment & uspi->s_apbmask, - f, create, sb->s_blocksize, - err, 0, &phys_block, created); - goto out; - } - /* - * Dindirect fragment + * ok, these macros clean the logic up a bit and make + * it much more readable: */ - fragment -= 1 << (uspi->s_apbshift + uspi->s_fpbshift); - if ( fragment < (1 << (uspi->s_2apbshift + uspi->s_fpbshift))) { - bh = ufs_inode_getfrag (inode, - UFS_DIND_FRAGMENT + (fragment >> uspi->s_2apbshift), - f, create, uspi->s_fpb, err, - 1, NULL, NULL); - bh = ufs_block_getfrag (inode, bh, - (fragment >> uspi->s_apbshift) & uspi->s_apbmask, - f, create, sb->s_blocksize, err, - 1, NULL, NULL); - tmp = ufs_block_getfrag (inode, bh, - fragment & uspi->s_apbmask, - f, create, sb->s_blocksize, err, - 0, &phys_block, created); +#define GET_INODE_DATABLOCK(x) \ + ufs_inode_getfrag(inode, x, fragment, 1, &err, 0, &phys, &new) +#define GET_INODE_PTR(x) \ + ufs_inode_getfrag(inode, x, fragment, uspi->s_fpb, &err, 1, NULL, NULL) +#define GET_INDIRECT_DATABLOCK(x) \ + ufs_block_getfrag(inode, bh, x, fragment, sb->s_blocksize, \ + &err, 0, &phys, &new); +#define GET_INDIRECT_PTR(x) \ + ufs_block_getfrag(inode, bh, x, fragment, sb->s_blocksize, \ + &err, 1, NULL, NULL); + + if (ptr < UFS_NDIR_FRAGMENT) { + bh = GET_INODE_DATABLOCK(ptr); goto out; } - /* - * Tindirect fragment - */ - fragment -= 1 << (uspi->s_2apbshift + uspi->s_fpbshift); - bh = ufs_inode_getfrag (inode, - UFS_TIND_FRAGMENT + (fragment >> uspi->s_3apbshift), - f, create, uspi->s_fpb, err, 1, NULL, NULL); - bh = ufs_block_getfrag (inode, bh, - (fragment >> uspi->s_2apbshift) & uspi->s_apbmask, - f, create, sb->s_blocksize, err, 1, NULL, NULL); - bh = ufs_block_getfrag (inode, bh, - (fragment >> uspi->s_apbshift) & uspi->s_apbmask, - f, create, sb->s_blocksize, err, 1, NULL, NULL); - tmp = ufs_block_getfrag (inode, bh, - fragment & uspi->s_apbmask, - f, create, sb->s_blocksize, err, 0, &phys_block, created); + ptr -= UFS_NDIR_FRAGMENT; + if (ptr < (1 << (uspi->s_apbshift + uspi->s_fpbshift))) { + bh = GET_INODE_PTR(UFS_IND_FRAGMENT + (ptr >> uspi->s_apbshift)); + goto get_indirect; + } + ptr -= 1 << (uspi->s_apbshift + uspi->s_fpbshift); + if (ptr < (1 << (uspi->s_2apbshift + uspi->s_fpbshift))) { + bh = GET_INODE_PTR(UFS_DIND_FRAGMENT + (ptr >> uspi->s_2apbshift)); + goto get_double; + } + ptr -= 1 << (uspi->s_2apbshift + uspi->s_fpbshift); + bh = GET_INODE_PTR(UFS_TIND_FRAGMENT + (ptr >> uspi->s_3apbshift)); + bh = GET_INDIRECT_PTR((ptr >> uspi->s_2apbshift) & uspi->s_apbmask); +get_double: + bh = GET_INDIRECT_PTR((ptr >> uspi->s_apbshift) & uspi->s_apbmask); +get_indirect: + bh = GET_INDIRECT_DATABLOCK(ptr & uspi->s_apbmask); + +#undef GET_INODE_DATABLOCK +#undef GET_INODE_PTR +#undef GET_INDIRECT_DATABLOCK +#undef GET_INDIRECT_PTR out: - if (!phys_block) - return 0; - if (*err) - return 0; - return phys_block; + if (err) + goto abort; + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = phys; + bh_result->b_state |= (1UL << BH_Mapped); + if (new) + bh_result->b_state |= (1UL << BH_New); +abort: + unlock_kernel(); + return err; + +abort_negative: + ufs_warning(sb, "ufs_get_block", "block < 0"); + goto abort; + +abort_too_big: + ufs_warning(sb, "ufs_get_block", "block > big"); + goto abort; } struct buffer_head *ufs_getfrag(struct inode *inode, unsigned int fragment, int create, int *err) { - struct buffer_head *tmp = NULL; - int phys_block, created; - - phys_block = ufs_getfrag_block(inode, fragment, create, err, &created); - if (phys_block) { - tmp = getblk(inode->i_dev, phys_block, inode->i_sb->s_blocksize); - if (created) { - memset(tmp->b_data, 0, inode->i_sb->s_blocksize); - mark_buffer_uptodate(tmp, 1); - mark_buffer_dirty(tmp, 1); + struct buffer_head dummy; + int error; + + dummy.b_state = 0; + dummy.b_blocknr = -1000; + error = ufs_getfrag_block(inode, fragment, &dummy, create); + *err = error; + if (!error && buffer_mapped(&dummy)) { + struct buffer_head *bh; + bh = getblk(dummy.b_dev, dummy.b_blocknr, inode->i_sb->s_blocksize); + if (buffer_new(&dummy)) { + memset(bh->b_data, 0, inode->i_sb->s_blocksize); + mark_buffer_uptodate(bh, 1); + mark_buffer_dirty(bh, 1); } + return bh; } - return tmp; + return NULL; } struct buffer_head * ufs_bread (struct inode * inode, unsigned fragment, diff --git a/fs/ufs/super.c b/fs/ufs/super.c index db3f11f23..12937adcf 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -271,6 +271,8 @@ static int ufs_parse_options (char * options, unsigned * mount_options) ufs_set_opt (*mount_options, UFSTYPE_44BSD); else if (!strcmp (value, "nextstep")) ufs_set_opt (*mount_options, UFSTYPE_NEXTSTEP); + else if (!strcmp (value, "nextstep-cd")) + ufs_set_opt (*mount_options, UFSTYPE_NEXTSTEP_CD); else if (!strcmp (value, "openstep")) ufs_set_opt (*mount_options, UFSTYPE_OPENSTEP); else if (!strcmp (value, "sunx86")) @@ -465,7 +467,7 @@ struct super_block * ufs_read_super (struct super_block * sb, void * data, } if (!(sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE)) { printk("You didn't specify the type of your ufs filesystem\n\n" - " mount -t ufs -o ufstype=sun|sunx86|44bsd|old|nextstep|openstep ....\n\n" + " mount -t ufs -o ufstype=sun|sunx86|44bsd|old|nextstep|netxstep-cd|openstep ...\n\n" ">>>WARNING<<< Wrong ufstype may corrupt your filesystem, " "default is ufstype=old\n"); ufs_set_opt (sb->u.ufs_sb.s_mount_opt, UFSTYPE_OLD); @@ -535,6 +537,20 @@ struct super_block * ufs_read_super (struct super_block * sb, void * data, } break; + case UFS_MOUNT_UFSTYPE_NEXTSTEP_CD: + UFSD(("ufstype=nextstep-cd\n")) + uspi->s_fsize = block_size = 2048; + uspi->s_fmask = ~(2048 - 1); + uspi->s_fshift = 11; + uspi->s_sbsize = super_block_size = 2048; + uspi->s_sbbase = 0; + flags |= UFS_DE_OLD | UFS_UID_OLD | UFS_ST_OLD | UFS_CG_OLD; + if (!(sb->s_flags & MS_RDONLY)) { + printk(KERN_INFO "ufstype=nextstep-cd is supported read-only\n"); + sb->s_flags |= MS_RDONLY; + } + break; + case UFS_MOUNT_UFSTYPE_OPENSTEP: UFSD(("ufstype=openstep\n")) uspi->s_fsize = block_size = 1024; @@ -592,6 +608,7 @@ again: #endif if ((((sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE) == UFS_MOUNT_UFSTYPE_NEXTSTEP) + || ((sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE) == UFS_MOUNT_UFSTYPE_NEXTSTEP_CD) || ((sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE) == UFS_MOUNT_UFSTYPE_OPENSTEP)) && uspi->s_sbbase < 256) { ubh_brelse_uspi(uspi); @@ -616,8 +633,8 @@ magic_found: printk("ufs_read_super: fs_bsize %u != {4096, 8192}\n", uspi->s_bsize); goto failed; } - if (uspi->s_fsize != 512 && uspi->s_fsize != 1024) { - printk("ufs_read_super: fs_fsize %u != {512, 1024}\n", uspi->s_fsize); + if (uspi->s_fsize != 512 && uspi->s_fsize != 1024 && uspi->s_fsize != 2048) { + printk("ufs_read_super: fs_fsize %u != {512, 1024, 2048}\n", uspi->s_fsize); goto failed; } if (uspi->s_fsize != block_size || uspi->s_sbsize != super_block_size) { diff --git a/fs/ufs/symlink.c b/fs/ufs/symlink.c index 39b54e05d..3df5c40a6 100644 --- a/fs/ufs/symlink.c +++ b/fs/ufs/symlink.c @@ -129,10 +129,12 @@ struct inode_operations ufs_symlink_inode_operations = { NULL, /* rename */ ufs_readlink, /* readlink */ ufs_follow_link, /* follow_link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; diff --git a/fs/ufs/truncate.c b/fs/ufs/truncate.c index 4649a4253..b7214fc49 100644 --- a/fs/ufs/truncate.c +++ b/fs/ufs/truncate.c @@ -63,7 +63,7 @@ #define DIRECT_FRAGMENT howmany (inode->i_size, uspi->s_fsize) #define DATA_BUFFER_USED(bh) \ - ((bh->b_count > 1) || buffer_locked(bh)) + (atomic_read(&bh->b_count) || buffer_locked(bh)) static int ufs_trunc_direct (struct inode * inode) { diff --git a/fs/ufs/util.c b/fs/ufs/util.c index 11978a752..e6d5f3a5b 100644 --- a/fs/ufs/util.c +++ b/fs/ufs/util.c @@ -137,8 +137,8 @@ unsigned ubh_max_bcount (struct ufs_buffer_head * ubh) if (!ubh) return 0; for ( i = 0; i < ubh->count; i++ ) - if ( ubh->bh[i]->b_count > max ) - max = ubh->bh[i]->b_count; + if ( atomic_read(&ubh->bh[i]->b_count) > max ) + max = atomic_read(&ubh->bh[i]->b_count); return max; } diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c index 0f26103c4..34fe302d7 100644 --- a/fs/umsdos/dir.c +++ b/fs/umsdos/dir.c @@ -832,9 +832,10 @@ struct inode_operations umsdos_dir_inode_operations = UMSDOS_rename, /* rename */ NULL, /* readlink */ NULL, /* followlink */ - generic_readpage, /* readpage */ + fat_bmap, /* get_block */ + block_read_full_page, /* readpage */ NULL, /* writepage */ - fat_bmap, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ diff --git a/fs/umsdos/file.c b/fs/umsdos/file.c index 339d82801..d3d8a74e6 100644 --- a/fs/umsdos/file.c +++ b/fs/umsdos/file.c @@ -99,12 +99,14 @@ struct inode_operations umsdos_file_inode_operations = NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - generic_readpage, /* readpage */ + fat_bmap, /* get_block */ + block_read_full_page, /* readpage */ NULL, /* writepage */ - fat_bmap, /* bmap */ + NULL, /* flushpage */ UMSDOS_truncate, /* truncate */ NULL, /* permission */ - fat_smap /* smap */ + fat_smap, /* smap */ + NULL /* revalidate */ }; /* For other with larger and unaligned file system */ @@ -137,12 +139,14 @@ struct inode_operations umsdos_file_inode_operations_no_bmap = NULL, /* rename */ NULL, /* readlink */ NULL, /* follow link */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ UMSDOS_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ + NULL /* revalidate */ }; /* For other with larger and unaligned file system with readpage */ @@ -175,10 +179,12 @@ struct inode_operations umsdos_file_inode_operations_readpage = NULL, /* rename */ NULL, /* readlink */ NULL, /* follow link */ + NULL, /* get_block */ fat_readpage, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ UMSDOS_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ + NULL /* revalidate */ }; diff --git a/fs/umsdos/rdir.c b/fs/umsdos/rdir.c index 7951bb8f8..c7bb8fb1b 100644 --- a/fs/umsdos/rdir.c +++ b/fs/umsdos/rdir.c @@ -249,7 +249,7 @@ struct inode_operations umsdos_rdir_inode_operations = NULL, /* followlink */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* get_block */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ diff --git a/fs/umsdos/symlink.c b/fs/umsdos/symlink.c index 97ea2da41..2d08a35d8 100644 --- a/fs/umsdos/symlink.c +++ b/fs/umsdos/symlink.c @@ -135,9 +135,10 @@ struct inode_operations umsdos_symlink_inode_operations = NULL, /* rename */ UMSDOS_readlink, /* readlink */ UMSDOS_followlink, /* followlink */ - generic_readpage, /* readpage */ + fat_bmap, /* get_block */ + block_read_full_page, /* readpage */ NULL, /* writepage */ - fat_bmap, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c index d372ead15..ad241c3cd 100644 --- a/fs/vfat/namei.c +++ b/fs/vfat/namei.c @@ -1232,11 +1232,14 @@ struct inode_operations vfat_dir_inode_operations = { vfat_rename, /* rename */ NULL, /* readlink */ NULL, /* followlink */ + NULL, /* get_block */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; struct super_block *vfat_read_super(struct super_block *sb,void *data, |