diff options
author | Paul Mackerras <paulus@samba.org> | 2006-03-29 13:24:50 +1100 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2006-03-29 13:24:50 +1100 |
commit | bac30d1a78d0f11c613968fc8b351a91ed465386 (patch) | |
tree | e52f3c876522a2f6047a6ec1c27df2e8a79486b8 /fs | |
parent | e8222502ee6157e2713da9e0792c21f4ad458d50 (diff) | |
parent | ca9ba4471c1203bb6e759b76e83167fec54fe590 (diff) |
Merge ../linux-2.6
Diffstat (limited to 'fs')
145 files changed, 1549 insertions, 755 deletions
diff --git a/fs/9p/v9fs_vfs.h b/fs/9p/v9fs_vfs.h index 43c9f7de0314..f867b8d3e973 100644 --- a/fs/9p/v9fs_vfs.h +++ b/fs/9p/v9fs_vfs.h @@ -39,8 +39,8 @@ extern struct file_system_type v9fs_fs_type; extern struct address_space_operations v9fs_addr_operations; -extern struct file_operations v9fs_file_operations; -extern struct file_operations v9fs_dir_operations; +extern const struct file_operations v9fs_file_operations; +extern const struct file_operations v9fs_dir_operations; extern struct dentry_operations v9fs_dentry_operations; struct inode *v9fs_get_inode(struct super_block *sb, int mode); diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c index 766f11f1215c..e32d5971039b 100644 --- a/fs/9p/vfs_dir.c +++ b/fs/9p/vfs_dir.c @@ -204,7 +204,7 @@ int v9fs_dir_release(struct inode *inode, struct file *filp) return 0; } -struct file_operations v9fs_dir_operations = { +const struct file_operations v9fs_dir_operations = { .read = generic_read_dir, .readdir = v9fs_dir_readdir, .open = v9fs_file_open, diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index 59e744163407..083dcfcd158e 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -266,7 +266,7 @@ v9fs_file_write(struct file *filp, const char __user * data, return total; } -struct file_operations v9fs_file_operations = { +const struct file_operations v9fs_file_operations = { .llseek = generic_file_llseek, .read = v9fs_file_read, .write = v9fs_file_write, diff --git a/fs/adfs/adfs.h b/fs/adfs/adfs.h index f6cd01352cc8..29217ff36d44 100644 --- a/fs/adfs/adfs.h +++ b/fs/adfs/adfs.h @@ -85,7 +85,7 @@ void __adfs_error(struct super_block *sb, const char *function, /* dir_*.c */ extern struct inode_operations adfs_dir_inode_operations; -extern struct file_operations adfs_dir_operations; +extern const struct file_operations adfs_dir_operations; extern struct dentry_operations adfs_dentry_operations; extern struct adfs_dir_ops adfs_f_dir_ops; extern struct adfs_dir_ops adfs_fplus_dir_ops; @@ -94,7 +94,7 @@ extern int adfs_dir_update(struct super_block *sb, struct object_info *obj); /* file.c */ extern struct inode_operations adfs_file_inode_operations; -extern struct file_operations adfs_file_operations; +extern const struct file_operations adfs_file_operations; static inline __u32 signed_asl(__u32 val, signed int shift) { diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index 0b4c3a028076..7b075fc397da 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -196,7 +196,7 @@ out: return ret; } -struct file_operations adfs_dir_operations = { +const struct file_operations adfs_dir_operations = { .read = generic_read_dir, .readdir = adfs_readdir, .fsync = file_fsync, diff --git a/fs/adfs/file.c b/fs/adfs/file.c index 6af10885f9d6..1014b9f2117b 100644 --- a/fs/adfs/file.c +++ b/fs/adfs/file.c @@ -25,7 +25,7 @@ #include "adfs.h" -struct file_operations adfs_file_operations = { +const struct file_operations adfs_file_operations = { .llseek = generic_file_llseek, .read = generic_file_read, .mmap = generic_file_mmap, diff --git a/fs/affs/affs.h b/fs/affs/affs.h index 0c6799f2137a..a43a876742b8 100644 --- a/fs/affs/affs.h +++ b/fs/affs/affs.h @@ -192,9 +192,9 @@ extern void affs_dir_truncate(struct inode *); extern struct inode_operations affs_file_inode_operations; extern struct inode_operations affs_dir_inode_operations; extern struct inode_operations affs_symlink_inode_operations; -extern struct file_operations affs_file_operations; -extern struct file_operations affs_file_operations_ofs; -extern struct file_operations affs_dir_operations; +extern const struct file_operations affs_file_operations; +extern const struct file_operations affs_file_operations_ofs; +extern const struct file_operations affs_dir_operations; extern struct address_space_operations affs_symlink_aops; extern struct address_space_operations affs_aops; extern struct address_space_operations affs_aops_ofs; diff --git a/fs/affs/dir.c b/fs/affs/dir.c index 548efd0ee98c..5d9649fa1814 100644 --- a/fs/affs/dir.c +++ b/fs/affs/dir.c @@ -17,7 +17,7 @@ static int affs_readdir(struct file *, void *, filldir_t); -struct file_operations affs_dir_operations = { +const struct file_operations affs_dir_operations = { .read = generic_read_dir, .readdir = affs_readdir, .fsync = file_fsync, diff --git a/fs/affs/file.c b/fs/affs/file.c index f72fb776ecdf..7076262af39b 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -25,7 +25,7 @@ static struct buffer_head *affs_get_extblock_slow(struct inode *inode, u32 ext); static int affs_file_open(struct inode *inode, struct file *filp); static int affs_file_release(struct inode *inode, struct file *filp); -struct file_operations affs_file_operations = { +const struct file_operations affs_file_operations = { .llseek = generic_file_llseek, .read = generic_file_read, .write = generic_file_write, diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 5c61c24dab2a..a6dff6a4f204 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -32,7 +32,7 @@ static int afs_d_delete(struct dentry *dentry); static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen, loff_t fpos, ino_t ino, unsigned dtype); -struct file_operations afs_dir_file_operations = { +const struct file_operations afs_dir_file_operations = { .open = afs_dir_open, .readdir = afs_dir_readdir, }; diff --git a/fs/afs/internal.h b/fs/afs/internal.h index ab8f87c66319..72febdf9a35a 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -64,7 +64,7 @@ extern struct cachefs_index_def afs_cache_cell_index_def; * dir.c */ extern struct inode_operations afs_dir_inode_operations; -extern struct file_operations afs_dir_file_operations; +extern const struct file_operations afs_dir_file_operations; /* * file.c @@ -105,7 +105,7 @@ extern struct cachefs_netfs afs_cache_netfs; * mntpt.c */ extern struct inode_operations afs_mntpt_inode_operations; -extern struct file_operations afs_mntpt_file_operations; +extern const struct file_operations afs_mntpt_file_operations; extern struct afs_timer afs_mntpt_expiry_timer; extern struct afs_timer_ops afs_mntpt_expiry_timer_ops; extern unsigned long afs_mntpt_expiry_timeout; diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c index 31ee06590de5..4e6eeb59b83c 100644 --- a/fs/afs/mntpt.c +++ b/fs/afs/mntpt.c @@ -32,7 +32,7 @@ static struct dentry *afs_mntpt_lookup(struct inode *dir, static int afs_mntpt_open(struct inode *inode, struct file *file); static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd); -struct file_operations afs_mntpt_file_operations = { +const struct file_operations afs_mntpt_file_operations = { .open = afs_mntpt_open, }; diff --git a/fs/afs/proc.c b/fs/afs/proc.c index 9c81b8f7eef0..101d21b6c037 100644 --- a/fs/afs/proc.c +++ b/fs/afs/proc.c @@ -37,7 +37,7 @@ static struct seq_operations afs_proc_cells_ops = { .show = afs_proc_cells_show, }; -static struct file_operations afs_proc_cells_fops = { +static const struct file_operations afs_proc_cells_fops = { .open = afs_proc_cells_open, .read = seq_read, .write = afs_proc_cells_write, @@ -53,7 +53,7 @@ static ssize_t afs_proc_rootcell_write(struct file *file, const char __user *buf, size_t size, loff_t *_pos); -static struct file_operations afs_proc_rootcell_fops = { +static const struct file_operations afs_proc_rootcell_fops = { .open = afs_proc_rootcell_open, .read = afs_proc_rootcell_read, .write = afs_proc_rootcell_write, @@ -77,7 +77,7 @@ static struct seq_operations afs_proc_cell_volumes_ops = { .show = afs_proc_cell_volumes_show, }; -static struct file_operations afs_proc_cell_volumes_fops = { +static const struct file_operations afs_proc_cell_volumes_fops = { .open = afs_proc_cell_volumes_open, .read = seq_read, .llseek = seq_lseek, @@ -101,7 +101,7 @@ static struct seq_operations afs_proc_cell_vlservers_ops = { .show = afs_proc_cell_vlservers_show, }; -static struct file_operations afs_proc_cell_vlservers_fops = { +static const struct file_operations afs_proc_cell_vlservers_fops = { .open = afs_proc_cell_vlservers_open, .read = seq_read, .llseek = seq_lseek, @@ -124,7 +124,7 @@ static struct seq_operations afs_proc_cell_servers_ops = { .show = afs_proc_cell_servers_show, }; -static struct file_operations afs_proc_cell_servers_fops = { +static const struct file_operations afs_proc_cell_servers_fops = { .open = afs_proc_cell_servers_open, .read = seq_read, .llseek = seq_lseek, diff --git a/fs/autofs/autofs_i.h b/fs/autofs/autofs_i.h index 990c28da5aec..a62327f1bdff 100644 --- a/fs/autofs/autofs_i.h +++ b/fs/autofs/autofs_i.h @@ -146,7 +146,7 @@ struct autofs_dir_ent *autofs_expire(struct super_block *,struct autofs_sb_info extern struct inode_operations autofs_root_inode_operations; extern struct inode_operations autofs_symlink_inode_operations; -extern struct file_operations autofs_root_operations; +extern const struct file_operations autofs_root_operations; /* Initializing function */ diff --git a/fs/autofs/dirhash.c b/fs/autofs/dirhash.c index 5ccfcf26310d..3fded389d06b 100644 --- a/fs/autofs/dirhash.c +++ b/fs/autofs/dirhash.c @@ -92,7 +92,7 @@ struct autofs_dir_ent *autofs_expire(struct super_block *sb, ; dput(dentry); - if ( may_umount(mnt) == 0 ) { + if ( may_umount(mnt) ) { mntput(mnt); DPRINTK(("autofs: signaling expire on %s\n", ent->name)); return ent; /* Expirable! */ diff --git a/fs/autofs/root.c b/fs/autofs/root.c index 870e2cf33016..9cac08d6a873 100644 --- a/fs/autofs/root.c +++ b/fs/autofs/root.c @@ -26,7 +26,7 @@ static int autofs_root_rmdir(struct inode *,struct dentry *); static int autofs_root_mkdir(struct inode *,struct dentry *,int); static int autofs_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); -struct file_operations autofs_root_operations = { +const struct file_operations autofs_root_operations = { .read = generic_read_dir, .readdir = autofs_root_readdir, .ioctl = autofs_root_ioctl, diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index f54c5b21f876..57c4903614e5 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -3,6 +3,7 @@ * linux/fs/autofs/autofs_i.h * * Copyright 1997-1998 Transmeta Corporation - All Rights Reserved + * Copyright 2005-2006 Ian Kent <raven@themaw.net> * * This file is part of the Linux kernel and is made available under * the terms of the GNU General Public License, version 2, or at your @@ -41,14 +42,6 @@ #define AUTOFS_SUPER_MAGIC 0x0187 -/* - * If the daemon returns a negative response (AUTOFS_IOC_FAIL) then the - * kernel will keep the negative response cached for up to the time given - * here, although the time can be shorter if the kernel throws the dcache - * entry away. This probably should be settable from user space. - */ -#define AUTOFS_NEGATIVE_TIMEOUT (60*HZ) /* 1 minute */ - /* Unified info structure. This is pointed to by both the dentry and inode structures. Each file in the filesystem has an instance of this structure. It holds a reference to the dentry, so dentries are never @@ -63,6 +56,7 @@ struct autofs_info { struct autofs_sb_info *sbi; unsigned long last_used; + atomic_t count; mode_t mode; size_t size; @@ -83,23 +77,37 @@ struct autofs_wait_queue { int hash; int len; char *name; + u32 dev; + u64 ino; + uid_t uid; + gid_t gid; + pid_t pid; + pid_t tgid; /* This is for status reporting upon return */ int status; - atomic_t notified; + atomic_t notify; atomic_t wait_ctr; }; #define AUTOFS_SBI_MAGIC 0x6d4a556d +#define AUTOFS_TYPE_INDIRECT 0x0001 +#define AUTOFS_TYPE_DIRECT 0x0002 +#define AUTOFS_TYPE_OFFSET 0x0004 + struct autofs_sb_info { u32 magic; struct dentry *root; + int pipefd; struct file *pipe; pid_t oz_pgrp; int catatonic; int version; int sub_version; + int min_proto; + int max_proto; unsigned long exp_timeout; + unsigned int type; int reghost_enabled; int needs_reghost; struct super_block *sb; @@ -166,8 +174,10 @@ int autofs4_expire_multi(struct super_block *, struct vfsmount *, extern struct inode_operations autofs4_symlink_inode_operations; extern struct inode_operations autofs4_dir_inode_operations; extern struct inode_operations autofs4_root_inode_operations; -extern struct file_operations autofs4_dir_operations; -extern struct file_operations autofs4_root_operations; +extern struct inode_operations autofs4_indirect_root_inode_operations; +extern struct inode_operations autofs4_direct_root_inode_operations; +extern const struct file_operations autofs4_dir_operations; +extern const struct file_operations autofs4_root_operations; /* Initializing function */ @@ -176,13 +186,6 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *, struct autofs_sb_info /* Queue management functions */ -enum autofs_notify -{ - NFY_NONE, - NFY_MOUNT, - NFY_EXPIRE -}; - int autofs4_wait(struct autofs_sb_info *,struct dentry *, enum autofs_notify); int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int); void autofs4_catatonic_mode(struct autofs_sb_info *); @@ -200,12 +203,22 @@ static inline int autofs4_follow_mount(struct vfsmount **mnt, struct dentry **de return res; } +static inline u32 autofs4_get_dev(struct autofs_sb_info *sbi) +{ + return new_encode_dev(sbi->sb->s_dev); +} + +static inline u64 autofs4_get_ino(struct autofs_sb_info *sbi) +{ + return sbi->sb->s_root->d_inode->i_ino; +} + static inline int simple_positive(struct dentry *dentry) { return dentry->d_inode && !d_unhashed(dentry); } -static inline int simple_empty_nolock(struct dentry *dentry) +static inline int __simple_empty(struct dentry *dentry) { struct dentry *child; int ret = 0; @@ -217,3 +230,6 @@ static inline int simple_empty_nolock(struct dentry *dentry) out: return ret; } + +void autofs4_dentry_release(struct dentry *); + diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index dc39589df165..b8ce02607d66 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c @@ -4,7 +4,7 @@ * * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved * Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org> - * Copyright 2001-2003 Ian Kent <raven@themaw.net> + * Copyright 2001-2006 Ian Kent <raven@themaw.net> * * This file is part of the Linux kernel and is made available under * the terms of the GNU General Public License, version 2, or at your @@ -16,7 +16,7 @@ static unsigned long now; -/* Check if a dentry can be expired return 1 if it can else return 0 */ +/* Check if a dentry can be expired */ static inline int autofs4_can_expire(struct dentry *dentry, unsigned long timeout, int do_now) { @@ -41,14 +41,14 @@ static inline int autofs4_can_expire(struct dentry *dentry, attempts if expire fails the first time */ ino->last_used = now; } - return 1; } -/* Check a mount point for busyness return 1 if not busy, otherwise */ -static int autofs4_check_mount(struct vfsmount *mnt, struct dentry *dentry) +/* Check a mount point for busyness */ +static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry) { - int status = 0; + struct dentry *top = dentry; + int status = 1; DPRINTK("dentry %p %.*s", dentry, (int)dentry->d_name.len, dentry->d_name.name); @@ -63,9 +63,14 @@ static int autofs4_check_mount(struct vfsmount *mnt, struct dentry *dentry) if (is_autofs4_dentry(dentry)) goto done; - /* The big question */ - if (may_umount_tree(mnt) == 0) - status = 1; + /* Update the expiry counter if fs is busy */ + if (!may_umount_tree(mnt)) { + struct autofs_info *ino = autofs4_dentry_ino(top); + ino->last_used = jiffies; + goto done; + } + + status = 0; done: DPRINTK("returning = %d", status); mntput(mnt); @@ -73,78 +78,124 @@ done: return status; } +/* + * Calculate next entry in top down tree traversal. + * From next_mnt in namespace.c - elegant. + */ +static struct dentry *next_dentry(struct dentry *p, struct dentry *root) +{ + struct list_head *next = p->d_subdirs.next; + + if (next == &p->d_subdirs) { + while (1) { + if (p == root) + return NULL; + next = p->d_u.d_child.next; + if (next != &p->d_parent->d_subdirs) + break; + p = p->d_parent; + } + } + return list_entry(next, struct dentry, d_u.d_child); +} + +/* + * Check a direct mount point for busyness. + * Direct mounts have similar expiry semantics to tree mounts. + * The tree is not busy iff no mountpoints are busy and there are no + * autofs submounts. + */ +static int autofs4_direct_busy(struct vfsmount *mnt, + struct dentry *top, + unsigned long timeout, + int do_now) +{ + DPRINTK("top %p %.*s", + top, (int) top->d_name.len, top->d_name.name); + + /* If it's busy update the expiry counters */ + if (!may_umount_tree(mnt)) { + struct autofs_info *ino = autofs4_dentry_ino(top); + if (ino) + ino->last_used = jiffies; + return 1; + } + + /* Timeout of a direct mount is determined by its top dentry */ + if (!autofs4_can_expire(top, timeout, do_now)) + return 1; + + return 0; +} + /* Check a directory tree of mount points for busyness * The tree is not busy iff no mountpoints are busy - * Return 1 if the tree is busy or 0 otherwise */ -static int autofs4_check_tree(struct vfsmount *mnt, - struct dentry *top, - unsigned long timeout, - int do_now) +static int autofs4_tree_busy(struct vfsmount *mnt, + struct dentry *top, + unsigned long timeout, + int do_now) { - struct dentry *this_parent = top; - struct list_head *next; + struct autofs_info *top_ino = autofs4_dentry_ino(top); + struct dentry *p; - DPRINTK("parent %p %.*s", + DPRINTK("top %p %.*s", top, (int)top->d_name.len, top->d_name.name); /* Negative dentry - give up */ if (!simple_positive(top)) - return 0; - - /* Timeout of a tree mount is determined by its top dentry */ - if (!autofs4_can_expire(top, timeout, do_now)) - return 0; - - /* Is someone visiting anywhere in the tree ? */ - if (may_umount_tree(mnt)) - return 0; + return 1; spin_lock(&dcache_lock); -repeat: - next = this_parent->d_subdirs.next; -resume: - while (next != &this_parent->d_subdirs) { - struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); - + for (p = top; p; p = next_dentry(p, top)) { /* Negative dentry - give up */ - if (!simple_positive(dentry)) { - next = next->next; + if (!simple_positive(p)) continue; - } DPRINTK("dentry %p %.*s", - dentry, (int)dentry->d_name.len, dentry->d_name.name); - - if (!simple_empty_nolock(dentry)) { - this_parent = dentry; - goto repeat; - } + p, (int) p->d_name.len, p->d_name.name); - dentry = dget(dentry); + p = dget(p); spin_unlock(&dcache_lock); - if (d_mountpoint(dentry)) { - /* First busy => tree busy */ - if (!autofs4_check_mount(mnt, dentry)) { - dput(dentry); - return 0; + /* + * Is someone visiting anywhere in the subtree ? + * If there's no mount we need to check the usage + * count for the autofs dentry. + * If the fs is busy update the expiry counter. + */ + if (d_mountpoint(p)) { + if (autofs4_mount_busy(mnt, p)) { + top_ino->last_used = jiffies; + dput(p); + return 1; + } + } else { + struct autofs_info *ino = autofs4_dentry_ino(p); + unsigned int ino_count = atomic_read(&ino->count); + + /* allow for dget above and top is already dgot */ + if (p == top) + ino_count += 2; + else + ino_count++; + + if (atomic_read(&p->d_count) > ino_count) { + top_ino->last_used = jiffies; + dput(p); + return 1; } } - - dput(dentry); + dput(p); spin_lock(&dcache_lock); - next = next->next; - } - - if (this_parent != top) { - next = this_parent->d_u.d_child.next; - this_parent = this_parent->d_parent; - goto resume; } spin_unlock(&dcache_lock); - return 1; + /* Timeout of a tree mount is ultimately determined by its top dentry */ + if (!autofs4_can_expire(top, timeout, do_now)) + return 1; + + return 0; } static struct dentry *autofs4_check_leaves(struct vfsmount *mnt, @@ -152,58 +203,68 @@ static struct dentry *autofs4_check_leaves(struct vfsmount *mnt, unsigned long timeout, int do_now) { - struct dentry *this_parent = parent; - struct list_head *next; + struct dentry *p; DPRINTK("parent %p %.*s", parent, (int)parent->d_name.len, parent->d_name.name); spin_lock(&dcache_lock); -repeat: - next = this_parent->d_subdirs.next; -resume: - while (next != &this_parent->d_subdirs) { - struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); - + for (p = parent; p; p = next_dentry(p, parent)) { /* Negative dentry - give up */ - if (!simple_positive(dentry)) { - next = next->next; + if (!simple_positive(p)) continue; - } DPRINTK("dentry %p %.*s", - dentry, (int)dentry->d_name.len, dentry->d_name.name); - - if (!list_empty(&dentry->d_subdirs)) { - this_parent = dentry; - goto repeat; - } + p, (int) p->d_name.len, p->d_name.name); - dentry = dget(dentry); + p = dget(p); spin_unlock(&dcache_lock); - if (d_mountpoint(dentry)) { - /* Can we expire this guy */ - if (!autofs4_can_expire(dentry, timeout, do_now)) - goto cont; - + if (d_mountpoint(p)) { /* Can we umount this guy */ - if (autofs4_check_mount(mnt, dentry)) - return dentry; + if (autofs4_mount_busy(mnt, p)) + goto cont; + /* Can we expire this guy */ + if (autofs4_can_expire(p, timeout, do_now)) + return p; } cont: - dput(dentry); + dput(p); spin_lock(&dcache_lock); - next = next->next; } + spin_unlock(&dcache_lock); + return NULL; +} + +/* Check if we can expire a direct mount (possibly a tree) */ +static struct dentry *autofs4_expire_direct(struct super_block *sb, + struct vfsmount *mnt, + struct autofs_sb_info *sbi, + int how) +{ + unsigned long timeout; + struct dentry *root = dget(sb->s_root); + int do_now = how & AUTOFS_EXP_IMMEDIATE; + + if (!sbi->exp_timeout || !root) + return NULL; + + now = jiffies; + timeout = sbi->exp_timeout; + + /* Lock the tree as we must expire as a whole */ + spin_lock(&sbi->fs_lock); + if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { + struct autofs_info *ino = autofs4_dentry_ino(root); - if (this_parent != parent) { - next = this_parent->d_u.d_child.next; - this_parent = this_parent->d_parent; - goto resume; + /* Set this flag early to catch sys_chdir and the like */ + ino->flags |= AUTOFS_INF_EXPIRING; + spin_unlock(&sbi->fs_lock); + return root; } - spin_unlock(&dcache_lock); + spin_unlock(&sbi->fs_lock); + dput(root); return NULL; } @@ -214,10 +275,10 @@ cont: * - it is unused by any user process * - it has been unused for exp_timeout time */ -static struct dentry *autofs4_expire(struct super_block *sb, - struct vfsmount *mnt, - struct autofs_sb_info *sbi, - int how) +static struct dentry *autofs4_expire_indirect(struct super_block *sb, + struct vfsmount *mnt, + struct autofs_sb_info *sbi, + int how) { unsigned long timeout; struct dentry *root = sb->s_root; @@ -241,7 +302,7 @@ static struct dentry *autofs4_expire(struct super_block *sb, struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); /* Negative dentry - give up */ - if ( !simple_positive(dentry) ) { + if (!simple_positive(dentry)) { next = next->next; continue; } @@ -249,31 +310,36 @@ static struct dentry *autofs4_expire(struct super_block *sb, dentry = dget(dentry); spin_unlock(&dcache_lock); - /* Case 1: indirect mount or top level direct mount */ + /* + * Case 1: (i) indirect mount or top level pseudo direct mount + * (autofs-4.1). + * (ii) indirect mount with offset mount, check the "/" + * offset (autofs-5.0+). + */ if (d_mountpoint(dentry)) { DPRINTK("checking mountpoint %p %.*s", dentry, (int)dentry->d_name.len, dentry->d_name.name); - /* Can we expire this guy */ - if (!autofs4_can_expire(dentry, timeout, do_now)) + /* Can we umount this guy */ + if (autofs4_mount_busy(mnt, dentry)) goto next; - /* Can we umount this guy */ - if (autofs4_check_mount(mnt, dentry)) { + /* Can we expire this guy */ + if (autofs4_can_expire(dentry, timeout, do_now)) { expired = dentry; break; } goto next; } - if ( simple_empty(dentry) ) + if (simple_empty(dentry)) goto next; /* Case 2: tree mount, expire iff entire tree is not busy */ if (!exp_leaves) { /* Lock the tree as we must expire as a whole */ spin_lock(&sbi->fs_lock); - if (autofs4_check_tree(mnt, dentry, timeout, do_now)) { + if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { struct autofs_info *inf = autofs4_dentry_ino(dentry); /* Set this flag early to catch sys_chdir and the like */ @@ -283,7 +349,10 @@ static struct dentry *autofs4_expire(struct super_block *sb, break; } spin_unlock(&sbi->fs_lock); - /* Case 3: direct mount, expire individual leaves */ + /* + * Case 3: pseudo direct mount, expire individual leaves + * (autofs-4.1). + */ } else { expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); if (expired) { @@ -297,7 +366,7 @@ next: next = next->next; } - if ( expired ) { + if (expired) { DPRINTK("returning %p %.*s", expired, (int)expired->d_name.len, expired->d_name.name); spin_lock(&dcache_lock); @@ -325,7 +394,7 @@ int autofs4_expire_run(struct super_block *sb, pkt.hdr.proto_version = sbi->version; pkt.hdr.type = autofs_ptype_expire; - if ((dentry = autofs4_expire(sb, mnt, sbi, 0)) == NULL) + if ((dentry = autofs4_expire_indirect(sb, mnt, sbi, 0)) == NULL) return -EAGAIN; pkt.len = dentry->d_name.len; @@ -351,17 +420,22 @@ int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, if (arg && get_user(do_now, arg)) return -EFAULT; - if ((dentry = autofs4_expire(sb, mnt, sbi, do_now)) != NULL) { - struct autofs_info *de_info = autofs4_dentry_ino(dentry); + if (sbi->type & AUTOFS_TYPE_DIRECT) + dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); + else + dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); + + if (dentry) { + struct autofs_info *ino = autofs4_dentry_ino(dentry); /* This is synchronous because it makes the daemon a little easier */ - de_info->flags |= AUTOFS_INF_EXPIRING; + ino->flags |= AUTOFS_INF_EXPIRING; ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); - de_info->flags &= ~AUTOFS_INF_EXPIRING; + ino->flags &= ~AUTOFS_INF_EXPIRING; dput(dentry); } - + return ret; } diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index 1ad98d48e550..fde78b110ddd 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c @@ -3,6 +3,7 @@ * linux/fs/autofs/inode.c * * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved + * Copyright 2005-2006 Ian Kent <raven@themaw.net> * * This file is part of the Linux kernel and is made available under * the terms of the GNU General Public License, version 2, or at your @@ -13,6 +14,7 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/file.h> +#include <linux/seq_file.h> #include <linux/pagemap.h> #include <linux/parser.h> #include <linux/bitops.h> @@ -46,6 +48,7 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino, ino->size = 0; ino->last_used = jiffies; + atomic_set(&ino->count, 0); ino->sbi = sbi; @@ -64,10 +67,19 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino, void autofs4_free_ino(struct autofs_info *ino) { + struct autofs_info *p_ino; + if (ino->dentry) { ino->dentry->d_fsdata = NULL; - if (ino->dentry->d_inode) + if (ino->dentry->d_inode) { + struct dentry *parent = ino->dentry->d_parent; + if (atomic_dec_and_test(&ino->count)) { + p_ino = autofs4_dentry_ino(parent); + if (p_ino && parent != ino->dentry) + atomic_dec(&p_ino->count); + } dput(ino->dentry); + } ino->dentry = NULL; } if (ino->free) @@ -145,20 +157,44 @@ static void autofs4_put_super(struct super_block *sb) autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ /* Clean up and release dangling references */ - if (sbi) - autofs4_force_release(sbi); + autofs4_force_release(sbi); kfree(sbi); DPRINTK("shutting down"); } +static int autofs4_show_options(struct seq_file *m, struct vfsmount *mnt) +{ + struct autofs_sb_info *sbi = autofs4_sbi(mnt->mnt_sb); + + if (!sbi) + return 0; + + seq_printf(m, ",fd=%d", sbi->pipefd); + seq_printf(m, ",pgrp=%d", sbi->oz_pgrp); + seq_printf(m, ",timeout=%lu", sbi->exp_timeout/HZ); + seq_printf(m, ",minproto=%d", sbi->min_proto); + seq_printf(m, ",maxproto=%d", sbi->max_proto); + + if (sbi->type & AUTOFS_TYPE_OFFSET) + seq_printf(m, ",offset"); + else if (sbi->type & AUTOFS_TYPE_DIRECT) + seq_printf(m, ",direct"); + else + seq_printf(m, ",indirect"); + + return 0; +} + static struct super_operations autofs4_sops = { .put_super = autofs4_put_super, .statfs = simple_statfs, + .show_options = autofs4_show_options, }; -enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto}; +enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto, + Opt_indirect, Opt_direct, Opt_offset}; static match_table_t tokens = { {Opt_fd, "fd=%u"}, @@ -167,11 +203,15 @@ static match_table_t tokens = { {Opt_pgrp, "pgrp=%u"}, {Opt_minproto, "minproto=%u"}, {Opt_maxproto, "maxproto=%u"}, + {Opt_indirect, "indirect"}, + {Opt_direct, "direct"}, + {Opt_offset, "offset"}, {Opt_err, NULL} }; static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, - pid_t *pgrp, int *minproto, int *maxproto) + pid_t *pgrp, unsigned int *type, + int *minproto, int *maxproto) { char *p; substring_t args[MAX_OPT_ARGS]; @@ -225,6 +265,15 @@ static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, return 1; *maxproto = option; break; + case Opt_indirect: + *type = AUTOFS_TYPE_INDIRECT; + break; + case Opt_direct: + *type = AUTOFS_TYPE_DIRECT; + break; + case Opt_offset: + *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; + break; default: return 1; } @@ -243,6 +292,10 @@ static struct autofs_info *autofs4_mkroot(struct autofs_sb_info *sbi) return ino; } +static struct dentry_operations autofs4_sb_dentry_operations = { + .d_release = autofs4_dentry_release, +}; + int autofs4_fill_super(struct super_block *s, void *data, int silent) { struct inode * root_inode; @@ -251,7 +304,6 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) int pipefd; struct autofs_sb_info *sbi; struct autofs_info *ino; - int minproto, maxproto; sbi = (struct autofs_sb_info *) kmalloc(sizeof(*sbi), GFP_KERNEL); if ( !sbi ) @@ -263,12 +315,16 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) s->s_fs_info = sbi; sbi->magic = AUTOFS_SBI_MAGIC; sbi->root = NULL; + sbi->pipefd = -1; sbi->catatonic = 0; sbi->exp_timeout = 0; sbi->oz_pgrp = process_group(current); sbi->sb = s; sbi->version = 0; sbi->sub_version = 0; + sbi->type = 0; + sbi->min_proto = 0; + sbi->max_proto = 0; mutex_init(&sbi->wq_mutex); spin_lock_init(&sbi->fs_lock); sbi->queues = NULL; @@ -285,38 +341,46 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) if (!ino) goto fail_free; root_inode = autofs4_get_inode(s, ino); - kfree(ino); if (!root_inode) - goto fail_free; + goto fail_ino; - root_inode->i_op = &autofs4_root_inode_operations; - root_inode->i_fop = &autofs4_root_operations; root = d_alloc_root(root_inode); - pipe = NULL; - if (!root) goto fail_iput; + pipe = NULL; + + root->d_op = &autofs4_sb_dentry_operations; + root->d_fsdata = ino; /* Can this call block? */ if (parse_options(data, &pipefd, &root_inode->i_uid, &root_inode->i_gid, - &sbi->oz_pgrp, - &minproto, &maxproto)) { + &sbi->oz_pgrp, &sbi->type, + &sbi->min_proto, &sbi->max_proto)) { printk("autofs: called with bogus options\n"); goto fail_dput; } + root_inode->i_fop = &autofs4_root_operations; + root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? + &autofs4_direct_root_inode_operations : + &autofs4_indirect_root_inode_operations; + /* Couldn't this be tested earlier? */ - if (maxproto < AUTOFS_MIN_PROTO_VERSION || - minproto > AUTOFS_MAX_PROTO_VERSION) { + if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION || + sbi->min_proto > AUTOFS_MAX_PROTO_VERSION) { printk("autofs: kernel does not match daemon version " "daemon (%d, %d) kernel (%d, %d)\n", - minproto, maxproto, + sbi->min_proto, sbi->max_proto, AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION); goto fail_dput; } - sbi->version = maxproto > AUTOFS_MAX_PROTO_VERSION ? AUTOFS_MAX_PROTO_VERSION : maxproto; + /* Establish highest kernel protocol version */ + if (sbi->max_proto > AUTOFS_MAX_PROTO_VERSION) + sbi->version = AUTOFS_MAX_PROTO_VERSION; + else + sbi->version = sbi->max_proto; sbi->sub_version = AUTOFS_PROTO_SUBVERSION; DPRINTK("pipe fd = %d, pgrp = %u", pipefd, sbi->oz_pgrp); @@ -329,6 +393,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) if ( !pipe->f_op || !pipe->f_op->write ) goto fail_fput; sbi->pipe = pipe; + sbi->pipefd = pipefd; /* * Take a reference to the root dentry so we get a chance to @@ -356,6 +421,8 @@ fail_dput: fail_iput: printk("autofs: get root dentry failed\n"); iput(root_inode); +fail_ino: + kfree(ino); fail_free: kfree(sbi); fail_unlock: diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 62d8d4acb8bb..84e030c8ddd0 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -4,7 +4,7 @@ * * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved * Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org> - * Copyright 2001-2003 Ian Kent <raven@themaw.net> + * Copyright 2001-2006 Ian Kent <raven@themaw.net> * * This file is part of the Linux kernel and is made available under * the terms of the GNU General Public License, version 2, or at your @@ -30,9 +30,9 @@ static int autofs4_dir_close(struct inode *inode, struct file *file); static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); -static int autofs4_dcache_readdir(struct file *, void *, filldir_t); +static void *autofs4_follow_link(struct dentry *, struct nameidata *); -struct file_operations autofs4_root_operations = { +const struct file_operations autofs4_root_operations = { .open = dcache_dir_open, .release = dcache_dir_close, .read = generic_read_dir, @@ -40,14 +40,14 @@ struct file_operations autofs4_root_operations = { .ioctl = autofs4_root_ioctl, }; -struct file_operations autofs4_dir_operations = { +const struct file_operations autofs4_dir_operations = { .open = autofs4_dir_open, .release = autofs4_dir_close, .read = generic_read_dir, .readdir = autofs4_dir_readdir, }; -struct inode_operations autofs4_root_inode_operations = { +struct inode_operations autofs4_indirect_root_inode_operations = { .lookup = autofs4_lookup, .unlink = autofs4_dir_unlink, .symlink = autofs4_dir_symlink, @@ -55,6 +55,14 @@ struct inode_operations autofs4_root_inode_operations = { .rmdir = autofs4_dir_rmdir, }; +struct inode_operations autofs4_direct_root_inode_operations = { + .lookup = autofs4_lookup, + .unlink = autofs4_dir_unlink, + .mkdir = autofs4_dir_mkdir, + .rmdir = autofs4_dir_rmdir, + .follow_link = autofs4_follow_link, +}; + struct inode_operations autofs4_dir_inode_operations = { .lookup = autofs4_lookup, .unlink = autofs4_dir_unlink, @@ -82,87 +90,7 @@ static int autofs4_root_readdir(struct file *file, void *dirent, DPRINTK("needs_reghost = %d", sbi->needs_reghost); - return autofs4_dcache_readdir(file, dirent, filldir); -} - -/* Update usage from here to top of tree, so that scan of - top-level directories will give a useful result */ -static void autofs4_update_usage(struct vfsmount *mnt, struct dentry *dentry) -{ - struct dentry *top = dentry->d_sb->s_root; - - spin_lock(&dcache_lock); - for(; dentry != top; dentry = dentry->d_parent) { - struct autofs_info *ino = autofs4_dentry_ino(dentry); - - if (ino) { - touch_atime(mnt, dentry); - ino->last_used = jiffies; - } - } - spin_unlock(&dcache_lock); -} - -/* - * From 2.4 kernel readdir.c - */ -static int autofs4_dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) -{ - int i; - struct dentry *dentry = filp->f_dentry; - - i = filp->f_pos; - switch (i) { - case 0: - if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino, DT_DIR) < 0) - break; - i++; - filp->f_pos++; - /* fallthrough */ - case 1: - if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) - break; - i++; - filp->f_pos++; - /* fallthrough */ - default: { - struct list_head *list; - int j = i-2; - - spin_lock(&dcache_lock); - list = dentry->d_subdirs.next; - - for (;;) { - if (list == &dentry->d_subdirs) { - spin_unlock(&dcache_lock); - return 0; - } - if (!j) - break; - j--; - list = list->next; - } - - while(1) { - struct dentry *de = list_entry(list, - struct dentry, d_u.d_child); - - if (!d_unhashed(de) && de->d_inode) { - spin_unlock(&dcache_lock); - if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino, DT_UNKNOWN) < 0) - break; - spin_lock(&dcache_lock); - } - filp->f_pos++; - list = list->next; - if (list != &dentry->d_subdirs) - continue; - spin_unlock(&dcache_lock); - break; - } - } - } - return 0; + return dcache_readdir(file, dirent, filldir); } static int autofs4_dir_open(struct inode *inode, struct file *file) @@ -170,8 +98,16 @@ static int autofs4_dir_open(struct inode *inode, struct file *file) struct dentry *dentry = file->f_dentry; struct vfsmount *mnt = file->f_vfsmnt; struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); + struct dentry *cursor; int status; + status = dcache_dir_open(inode, file); + if (status) + goto out; + + cursor = file->private_data; + cursor->d_fsdata = NULL; + DPRINTK("file=%p dentry=%p %.*s", file, dentry, dentry->d_name.len, dentry->d_name.name); @@ -180,12 +116,15 @@ static int autofs4_dir_open(struct inode *inode, struct file *file) if (autofs4_ispending(dentry)) { DPRINTK("dentry busy"); - return -EBUSY; + dcache_dir_close(inode, file); + status = -EBUSY; + goto out; } + status = -ENOENT; if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { struct nameidata nd; - int empty; + int empty, ret; /* In case there are stale directory dentrys from a failed mount */ spin_lock(&dcache_lock); @@ -195,13 +134,13 @@ static int autofs4_dir_open(struct inode *inode, struct file *file) if (!empty) d_invalidate(dentry); - nd.dentry = dentry; - nd.mnt = mnt; nd.flags = LOOKUP_DIRECTORY; - status = (dentry->d_op->d_revalidate)(dentry, &nd); + ret = (dentry->d_op->d_revalidate)(dentry, &nd); - if (!status) - return -ENOENT; + if (!ret) { + dcache_dir_close(inode, file); + goto out; + } } if (d_mountpoint(dentry)) { @@ -212,25 +151,29 @@ static int autofs4_dir_open(struct inode *inode, struct file *file) if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { dput(fp_dentry); mntput(fp_mnt); - return -ENOENT; + dcache_dir_close(inode, file); + goto out; } fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); status = PTR_ERR(fp); if (IS_ERR(fp)) { - file->private_data = NULL; - return status; + dcache_dir_close(inode, file); + goto out; } - file->private_data = fp; + cursor->d_fsdata = fp; } -out: return 0; +out: + return status; } static int autofs4_dir_close(struct inode *inode, struct file *file) { struct dentry *dentry = file->f_dentry; struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); + struct dentry *cursor = file->private_data; + int status = 0; DPRINTK("file=%p dentry=%p %.*s", file, dentry, dentry->d_name.len, dentry->d_name.name); @@ -240,26 +183,28 @@ static int autofs4_dir_close(struct inode *inode, struct file *file) if (autofs4_ispending(dentry)) { DPRINTK("dentry busy"); - return -EBUSY; + status = -EBUSY; + goto out; } if (d_mountpoint(dentry)) { - struct file *fp = file->private_data; - - if (!fp) - return -ENOENT; - + struct file *fp = cursor->d_fsdata; + if (!fp) { + status = -ENOENT; + goto out; + } filp_close(fp, current->files); - file->private_data = NULL; } out: - return 0; + dcache_dir_close(inode, file); + return status; } static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) { struct dentry *dentry = file->f_dentry; struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); + struct dentry *cursor = file->private_data; int status; DPRINTK("file=%p dentry=%p %.*s", @@ -274,7 +219,7 @@ static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldi } if (d_mountpoint(dentry)) { - struct file *fp = file->private_data; + struct file *fp = cursor->d_fsdata; if (!fp) return -ENOENT; @@ -289,27 +234,26 @@ static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldi return status; } out: - return autofs4_dcache_readdir(file, dirent, filldir); + return dcache_readdir(file, dirent, filldir); } -static int try_to_fill_dentry(struct vfsmount *mnt, struct dentry *dentry, int flags) +static int try_to_fill_dentry(struct dentry *dentry, int flags) { - struct super_block *sb = mnt->mnt_sb; - struct autofs_sb_info *sbi = autofs4_sbi(sb); - struct autofs_info *de_info = autofs4_dentry_ino(dentry); + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); + struct autofs_info *ino = autofs4_dentry_ino(dentry); int status = 0; /* Block on any pending expiry here; invalidate the dentry when expiration is done to trigger mount request with a new dentry */ - if (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { + if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { DPRINTK("waiting for expire %p name=%.*s", dentry, dentry->d_name.len, dentry->d_name.name); status = autofs4_wait(sbi, dentry, NFY_NONE); - + DPRINTK("expire done status=%d", status); - + /* * If the directory still exists the mount request must * continue otherwise it can't be followed at the right @@ -317,34 +261,36 @@ static int try_to_fill_dentry(struct vfsmount *mnt, struct dentry *dentry, int f */ status = d_invalidate(dentry); if (status != -EBUSY) - return 0; + return -ENOENT; } DPRINTK("dentry=%p %.*s ino=%p", dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); - /* Wait for a pending mount, triggering one if there isn't one already */ + /* + * Wait for a pending mount, triggering one if there + * isn't one already + */ if (dentry->d_inode == NULL) { DPRINTK("waiting for mount name=%.*s", dentry->d_name.len, dentry->d_name.name); status = autofs4_wait(sbi, dentry, NFY_MOUNT); - + DPRINTK("mount done status=%d", status); if (status && dentry->d_inode) - return 0; /* Try to get the kernel to invalidate this dentry */ - + return status; /* Try to get the kernel to invalidate this dentry */ + /* Turn this into a real negative dentry? */ if (status == -ENOENT) { - dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT; spin_lock(&dentry->d_lock); dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; spin_unlock(&dentry->d_lock); - return 1; + return status; } else if (status) { /* Return a negative dentry, but leave it "pending" */ - return 1; + return status; } /* Trigger mount for path component or follow link */ } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || @@ -363,19 +309,87 @@ static int try_to_fill_dentry(struct vfsmount *mnt, struct dentry *dentry, int f spin_lock(&dentry->d_lock); dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; spin_unlock(&dentry->d_lock); - return 0; + return status; } } - /* We don't update the usages for the autofs daemon itself, this - is necessary for recursive autofs mounts */ - if (!autofs4_oz_mode(sbi)) - autofs4_update_usage(mnt, dentry); + /* Initialize expiry counter after successful mount */ + if (ino) + ino->last_used = jiffies; spin_lock(&dentry->d_lock); dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; spin_unlock(&dentry->d_lock); - return 1; + return status; +} + +/* For autofs direct mounts the follow link triggers the mount */ +static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); + int oz_mode = autofs4_oz_mode(sbi); + unsigned int lookup_type; + int status; + + DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", + dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, + nd->flags); + + /* If it's our master or we shouldn't trigger a mount we're done */ + lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); + if (oz_mode || !lookup_type) + goto done; + + /* + * If a request is pending wait for it. + * If it's a mount then it won't be expired till at least + * a liitle later and if it's an expire then we might need + * to mount it again. + */ + if (autofs4_ispending(dentry)) { + DPRINTK("waiting for active request %p name=%.*s", + dentry, dentry->d_name.len, dentry->d_name.name); + + status = autofs4_wait(sbi, dentry, NFY_NONE); + + DPRINTK("request done status=%d", status); + } + + /* + * If the dentry contains directories then it is an + * autofs multi-mount with no root mount offset. So + * don't try to mount it again. + */ + spin_lock(&dcache_lock); + if (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) { + spin_unlock(&dcache_lock); + + status = try_to_fill_dentry(dentry, 0); + if (status) + goto out_error; + + /* + * The mount succeeded but if there is no root mount + * it must be an autofs multi-mount with no root offset + * so we don't need to follow the mount. + */ + if (d_mountpoint(dentry)) { + if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { + status = -ENOENT; + goto out_error; + } + } + + goto done; + } + spin_unlock(&dcache_lock); + +done: + return NULL; + +out_error: + path_release(nd); + return ERR_PTR(status); } /* @@ -384,47 +398,43 @@ static int try_to_fill_dentry(struct vfsmount *mnt, struct dentry *dentry, int f * yet completely filled in, and revalidate has to delay such * lookups.. */ -static int autofs4_revalidate(struct dentry * dentry, struct nameidata *nd) +static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) { - struct inode * dir = dentry->d_parent->d_inode; + struct inode *dir = dentry->d_parent->d_inode; struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); int oz_mode = autofs4_oz_mode(sbi); int flags = nd ? nd->flags : 0; - int status = 1; + int status = 0; /* Pending dentry */ if (autofs4_ispending(dentry)) { if (!oz_mode) - status = try_to_fill_dentry(nd->mnt, dentry, flags); - return status; + status = try_to_fill_dentry(dentry, flags); + return !status; } /* Negative dentry.. invalidate if "old" */ if (dentry->d_inode == NULL) - return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT); + return 0; /* Check for a non-mountpoint directory with no contents */ spin_lock(&dcache_lock); if (S_ISDIR(dentry->d_inode->i_mode) && !d_mountpoint(dentry) && - list_empty(&dentry->d_subdirs)) { + __simple_empty(dentry)) { DPRINTK("dentry=%p %.*s, emptydir", dentry, dentry->d_name.len, dentry->d_name.name); spin_unlock(&dcache_lock); if (!oz_mode) - status = try_to_fill_dentry(nd->mnt, dentry, flags); - return status; + status = try_to_fill_dentry(dentry, flags); + return !status; } spin_unlock(&dcache_lock); - /* Update the usage list */ - if (!oz_mode) - autofs4_update_usage(nd->mnt, dentry); - return 1; } -static void autofs4_dentry_release(struct dentry *de) +void autofs4_dentry_release(struct dentry *de) { struct autofs_info *inf; @@ -462,12 +472,13 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s DPRINTK("name = %.*s", dentry->d_name.len, dentry->d_name.name); + /* File name too long to exist */ if (dentry->d_name.len > NAME_MAX) - return ERR_PTR(-ENAMETOOLONG);/* File name too long to exist */ + return ERR_PTR(-ENAMETOOLONG); sbi = autofs4_sbi(dir->i_sb); - oz_mode = autofs4_oz_mode(sbi); + DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", current->pid, process_group(current), sbi->catatonic, oz_mode); @@ -519,7 +530,7 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s * doesn't do the right thing for all system calls, but it should * be OK for the operations we permit from an autofs. */ - if ( dentry->d_inode && d_unhashed(dentry) ) + if (dentry->d_inode && d_unhashed(dentry)) return ERR_PTR(-ENOENT); return NULL; @@ -531,6 +542,7 @@ static int autofs4_dir_symlink(struct inode *dir, { struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); struct autofs_info *ino = autofs4_dentry_ino(dentry); + struct autofs_info *p_ino; struct inode *inode; char *cp; @@ -564,6 +576,10 @@ static int autofs4_dir_symlink(struct inode *dir, dentry->d_fsdata = ino; ino->dentry = dget(dentry); + atomic_inc(&ino->count); + p_ino = autofs4_dentry_ino(dentry->d_parent); + if (p_ino && dentry->d_parent != dentry) + atomic_inc(&p_ino->count); ino->inode = inode; dir->i_mtime = CURRENT_TIME; @@ -590,11 +606,17 @@ static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry) { struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); struct autofs_info *ino = autofs4_dentry_ino(dentry); + struct autofs_info *p_ino; /* This allows root to remove symlinks */ if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) return -EACCES; + if (atomic_dec_and_test(&ino->count)) { + p_ino = autofs4_dentry_ino(dentry->d_parent); + if (p_ino && dentry->d_parent != dentry) + atomic_dec(&p_ino->count); + } dput(ino->dentry); dentry->d_inode->i_size = 0; @@ -611,6 +633,7 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry) { struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); struct autofs_info *ino = autofs4_dentry_ino(dentry); + struct autofs_info *p_ino; if (!autofs4_oz_mode(sbi)) return -EACCES; @@ -625,8 +648,12 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry) spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); + if (atomic_dec_and_test(&ino->count)) { + p_ino = autofs4_dentry_ino(dentry->d_parent); + if (p_ino && dentry->d_parent != dentry) + atomic_dec(&p_ino->count); + } dput(ino->dentry); - dentry->d_inode->i_size = 0; dentry->d_inode->i_nlink = 0; @@ -640,6 +667,7 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode) { struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); struct autofs_info *ino = autofs4_dentry_ino(dentry); + struct autofs_info *p_ino; struct inode *inode; if ( !autofs4_oz_mode(sbi) ) @@ -662,6 +690,10 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode) dentry->d_fsdata = ino; ino->dentry = dget(dentry); + atomic_inc(&ino->count); + p_ino = autofs4_dentry_ino(dentry->d_parent); + if (p_ino && dentry->d_parent != dentry) + atomic_inc(&p_ino->count); ino->inode = inode; dir->i_nlink++; dir->i_mtime = CURRENT_TIME; @@ -745,7 +777,7 @@ static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) { int status = 0; - if (may_umount(mnt) == 0) + if (may_umount(mnt)) status = 1; DPRINTK("returning %d", status); diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index be78e9378c03..142ab6aa2aa1 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c @@ -3,7 +3,7 @@ * linux/fs/autofs/waitq.c * * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * Copyright 2001-2003 Ian Kent <raven@themaw.net> + * Copyright 2001-2006 Ian Kent <raven@themaw.net> * * This file is part of the Linux kernel and is made available under * the terms of the GNU General Public License, version 2, or at your @@ -33,7 +33,7 @@ void autofs4_catatonic_mode(struct autofs_sb_info *sbi) sbi->catatonic = 1; wq = sbi->queues; sbi->queues = NULL; /* Erase all wait queues */ - while ( wq ) { + while (wq) { nwq = wq->next; wq->status = -ENOENT; /* Magic is gone - report failure */ kfree(wq->name); @@ -45,7 +45,6 @@ void autofs4_catatonic_mode(struct autofs_sb_info *sbi) fput(sbi->pipe); /* Close the pipe */ sbi->pipe = NULL; } - shrink_dcache_sb(sbi->sb); } @@ -98,7 +97,10 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi, pkt.hdr.proto_version = sbi->version; pkt.hdr.type = type; - if (type == autofs_ptype_missing) { + switch (type) { + /* Kernel protocol v4 missing and expire packets */ + case autofs_ptype_missing: + { struct autofs_packet_missing *mp = &pkt.missing; pktsz = sizeof(*mp); @@ -107,7 +109,10 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi, mp->len = wq->len; memcpy(mp->name, wq->name, wq->len); mp->name[wq->len] = '\0'; - } else if (type == autofs_ptype_expire_multi) { + break; + } + case autofs_ptype_expire_multi: + { struct autofs_packet_expire_multi *ep = &pkt.expire_multi; pktsz = sizeof(*ep); @@ -116,7 +121,34 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi, ep->len = wq->len; memcpy(ep->name, wq->name, wq->len); ep->name[wq->len] = '\0'; - } else { + break; + } + /* + * Kernel protocol v5 packet for handling indirect and direct + * mount missing and expire requests + */ + case autofs_ptype_missing_indirect: + case autofs_ptype_expire_indirect: + case autofs_ptype_missing_direct: + case autofs_ptype_expire_direct: + { + struct autofs_v5_packet *packet = &pkt.v5_packet; + + pktsz = sizeof(*packet); + + packet->wait_queue_token = wq->wait_queue_token; + packet->len = wq->len; + memcpy(packet->name, wq->name, wq->len); + packet->name[wq->len] = '\0'; + packet->dev = wq->dev; + packet->ino = wq->ino; + packet->uid = wq->uid; + packet->gid = wq->gid; + packet->pid = wq->pid; + packet->tgid = wq->tgid; + break; + } + default: printk("autofs4_notify_daemon: bad type %d!\n", type); return; } @@ -162,21 +194,29 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, { struct autofs_wait_queue *wq; char *name; - int len, status; + unsigned int len = 0; + unsigned int hash = 0; + int status; /* In catatonic mode, we don't wait for nobody */ - if ( sbi->catatonic ) + if (sbi->catatonic) return -ENOENT; name = kmalloc(NAME_MAX + 1, GFP_KERNEL); if (!name) return -ENOMEM; - len = autofs4_getpath(sbi, dentry, &name); - if (!len) { - kfree(name); - return -ENOENT; + /* If this is a direct mount request create a dummy name */ + if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) + len = sprintf(name, "%p", dentry); + else { + len = autofs4_getpath(sbi, dentry, &name); + if (!len) { + kfree(name); + return -ENOENT; + } } + hash = full_name_hash(name, len); if (mutex_lock_interruptible(&sbi->wq_mutex)) { kfree(name); @@ -190,7 +230,7 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, break; } - if ( !wq ) { + if (!wq) { /* Can't wait for an expire if there's no mount */ if (notify == NFY_NONE && !d_mountpoint(dentry)) { kfree(name); @@ -200,7 +240,7 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, /* Create a new wait queue */ wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); - if ( !wq ) { + if (!wq) { kfree(name); mutex_unlock(&sbi->wq_mutex); return -ENOMEM; @@ -212,12 +252,18 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, wq->next = sbi->queues; sbi->queues = wq; init_waitqueue_head(&wq->queue); - wq->hash = dentry->d_name.hash; + wq->hash = hash; wq->name = name; wq->len = len; + wq->dev = autofs4_get_dev(sbi); + wq->ino = autofs4_get_ino(sbi); + wq->uid = current->uid; + wq->gid = current->gid; + wq->pid = current->pid; + wq->tgid = current->tgid; wq->status = -EINTR; /* Status return if interrupted */ atomic_set(&wq->wait_ctr, 2); - atomic_set(&wq->notified, 1); + atomic_set(&wq->notify, 1); mutex_unlock(&sbi->wq_mutex); } else { atomic_inc(&wq->wait_ctr); @@ -227,9 +273,26 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); } - if (notify != NFY_NONE && atomic_dec_and_test(&wq->notified)) { - int type = (notify == NFY_MOUNT ? - autofs_ptype_missing : autofs_ptype_expire_multi); + if (notify != NFY_NONE && atomic_read(&wq->notify)) { + int type; + + atomic_dec(&wq->notify); + + if (sbi->version < 5) { + if (notify == NFY_MOUNT) + type = autofs_ptype_missing; + else + type = autofs_ptype_expire_multi; + } else { + if (notify == NFY_MOUNT) + type = (sbi->type & AUTOFS_TYPE_DIRECT) ? + autofs_ptype_missing_direct : + autofs_ptype_missing_indirect; + else + type = (sbi->type & AUTOFS_TYPE_DIRECT) ? + autofs_ptype_expire_direct : + autofs_ptype_expire_indirect; + } DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); @@ -240,14 +303,14 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, /* wq->name is NULL if and only if the lock is already released */ - if ( sbi->catatonic ) { + if (sbi->catatonic) { /* We might have slept, so check again for catatonic mode */ wq->status = -ENOENT; kfree(wq->name); wq->name = NULL; } - if ( wq->name ) { + if (wq->name) { /* Block all but "shutdown" signals while waiting */ sigset_t oldset; unsigned long irqflags; @@ -283,12 +346,12 @@ int autofs4_wait_release(struct autofs_sb_info *sbi, autofs_wqt_t wait_queue_tok struct autofs_wait_queue *wq, **wql; mutex_lock(&sbi->wq_mutex); - for ( wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next ) { - if ( wq->wait_queue_token == wait_queue_token ) + for (wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next) { + if (wq->wait_queue_token == wait_queue_token) break; } - if ( !wq ) { + if (!wq) { mutex_unlock(&sbi->wq_mutex); return -EINVAL; } diff --git a/fs/bad_inode.c b/fs/bad_inode.c index e172180a1d8c..80599ae33966 100644 --- a/fs/bad_inode.c +++ b/fs/bad_inode.c @@ -22,7 +22,7 @@ static int return_EIO(void) #define EIO_ERROR ((void *) (return_EIO)) -static struct file_operations bad_file_ops = +static const struct file_operations bad_file_ops = { .llseek = EIO_ERROR, .aio_read = EIO_ERROR, diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c index 044a59587829..68ebd10f345d 100644 --- a/fs/befs/linuxvfs.c +++ b/fs/befs/linuxvfs.c @@ -64,7 +64,7 @@ static const struct super_operations befs_sops = { /* slab cache for befs_inode_info objects */ static kmem_cache_t *befs_inode_cachep; -static struct file_operations befs_dir_operations = { +static const struct file_operations befs_dir_operations = { .read = generic_read_dir, .readdir = befs_readdir, }; diff --git a/fs/bfs/bfs.h b/fs/bfs/bfs.h index 1fbc53f14aba..9d791004b21c 100644 --- a/fs/bfs/bfs.h +++ b/fs/bfs/bfs.h @@ -49,11 +49,11 @@ static inline struct bfs_inode_info *BFS_I(struct inode *inode) /* file.c */ extern struct inode_operations bfs_file_inops; -extern struct file_operations bfs_file_operations; +extern const struct file_operations bfs_file_operations; extern struct address_space_operations bfs_aops; /* dir.c */ extern struct inode_operations bfs_dir_inops; -extern struct file_operations bfs_dir_operations; +extern const struct file_operations bfs_dir_operations; #endif /* _FS_BFS_BFS_H */ diff --git a/fs/bfs/dir.c b/fs/bfs/dir.c index 5af928fa0449..26fad9621738 100644 --- a/fs/bfs/dir.c +++ b/fs/bfs/dir.c @@ -70,7 +70,7 @@ static int bfs_readdir(struct file * f, void * dirent, filldir_t filldir) return 0; } -struct file_operations bfs_dir_operations = { +const struct file_operations bfs_dir_operations = { .read = generic_read_dir, .readdir = bfs_readdir, .fsync = file_fsync, diff --git a/fs/bfs/file.c b/fs/bfs/file.c index 807723b65daf..d83cd74a2e4e 100644 --- a/fs/bfs/file.c +++ b/fs/bfs/file.c @@ -17,7 +17,7 @@ #define dprintf(x...) #endif -struct file_operations bfs_file_operations = { +const struct file_operations bfs_file_operations = { .llseek = generic_file_llseek, .read = generic_file_read, .write = generic_file_write, diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 6a7b730c206b..d73d75591a39 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -600,7 +600,7 @@ static ssize_t bm_entry_write(struct file *file, const char __user *buffer, return count; } -static struct file_operations bm_entry_operations = { +static const struct file_operations bm_entry_operations = { .read = bm_entry_read, .write = bm_entry_write, }; @@ -668,7 +668,7 @@ out: return count; } -static struct file_operations bm_register_operations = { +static const struct file_operations bm_register_operations = { .write = bm_register_write, }; @@ -715,7 +715,7 @@ static ssize_t bm_status_write(struct file * file, const char __user * buffer, return count; } -static struct file_operations bm_status_operations = { +static const struct file_operations bm_status_operations = { .read = bm_status_read, .write = bm_status_write, }; diff --git a/fs/block_dev.c b/fs/block_dev.c index 5983d42df015..af88c43043d5 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -266,6 +266,9 @@ static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) mutex_init(&bdev->bd_mount_mutex); INIT_LIST_HEAD(&bdev->bd_inodes); INIT_LIST_HEAD(&bdev->bd_list); +#ifdef CONFIG_SYSFS + INIT_LIST_HEAD(&bdev->bd_holder_list); +#endif inode_init_once(&ei->vfs_inode); } } @@ -490,6 +493,300 @@ void bd_release(struct block_device *bdev) EXPORT_SYMBOL(bd_release); +#ifdef CONFIG_SYSFS +/* + * Functions for bd_claim_by_kobject / bd_release_from_kobject + * + * If a kobject is passed to bd_claim_by_kobject() + * and the kobject has a parent directory, + * following symlinks are created: + * o from the kobject to the claimed bdev + * o from "holders" directory of the bdev to the parent of the kobject + * bd_release_from_kobject() removes these symlinks. + * + * Example: + * If /dev/dm-0 maps to /dev/sda, kobject corresponding to + * /sys/block/dm-0/slaves is passed to bd_claim_by_kobject(), then: + * /sys/block/dm-0/slaves/sda --> /sys/block/sda + * /sys/block/sda/holders/dm-0 --> /sys/block/dm-0 + */ + +static struct kobject *bdev_get_kobj(struct block_device *bdev) +{ + if (bdev->bd_contains != bdev) + return kobject_get(&bdev->bd_part->kobj); + else + return kobject_get(&bdev->bd_disk->kobj); +} + +static struct kobject *bdev_get_holder(struct block_device *bdev) +{ + if (bdev->bd_contains != bdev) + return kobject_get(bdev->bd_part->holder_dir); + else + return kobject_get(bdev->bd_disk->holder_dir); +} + +static void add_symlink(struct kobject *from, struct kobject *to) +{ + if (!from || !to) + return; + sysfs_create_link(from, to, kobject_name(to)); +} + +static void del_symlink(struct kobject *from, struct kobject *to) +{ + if (!from || !to) + return; + sysfs_remove_link(from, kobject_name(to)); +} + +/* + * 'struct bd_holder' contains pointers to kobjects symlinked by + * bd_claim_by_kobject. + * It's connected to bd_holder_list which is protected by bdev->bd_sem. + */ +struct bd_holder { + struct list_head list; /* chain of holders of the bdev */ + int count; /* references from the holder */ + struct kobject *sdir; /* holder object, e.g. "/block/dm-0/slaves" */ + struct kobject *hdev; /* e.g. "/block/dm-0" */ + struct kobject *hdir; /* e.g. "/block/sda/holders" */ + struct kobject *sdev; /* e.g. "/block/sda" */ +}; + +/* + * Get references of related kobjects at once. + * Returns 1 on success. 0 on failure. + * + * Should call bd_holder_release_dirs() after successful use. + */ +static int bd_holder_grab_dirs(struct block_device *bdev, + struct bd_holder *bo) +{ + if (!bdev || !bo) + return 0; + + bo->sdir = kobject_get(bo->sdir); + if (!bo->sdir) + return 0; + + bo->hdev = kobject_get(bo->sdir->parent); + if (!bo->hdev) + goto fail_put_sdir; + + bo->sdev = bdev_get_kobj(bdev); + if (!bo->sdev) + goto fail_put_hdev; + + bo->hdir = bdev_get_holder(bdev); + if (!bo->hdir) + goto fail_put_sdev; + + return 1; + +fail_put_sdev: + kobject_put(bo->sdev); +fail_put_hdev: + kobject_put(bo->hdev); +fail_put_sdir: + kobject_put(bo->sdir); + + return 0; +} + +/* Put references of related kobjects at once. */ +static void bd_holder_release_dirs(struct bd_holder *bo) +{ + kobject_put(bo->hdir); + kobject_put(bo->sdev); + kobject_put(bo->hdev); + kobject_put(bo->sdir); +} + +static struct bd_holder *alloc_bd_holder(struct kobject *kobj) +{ + struct bd_holder *bo; + + bo = kzalloc(sizeof(*bo), GFP_KERNEL); + if (!bo) + return NULL; + + bo->count = 1; + bo->sdir = kobj; + + return bo; +} + +static void free_bd_holder(struct bd_holder *bo) +{ + kfree(bo); +} + +/** + * add_bd_holder - create sysfs symlinks for bd_claim() relationship + * + * @bdev: block device to be bd_claimed + * @bo: preallocated and initialized by alloc_bd_holder() + * + * If there is no matching entry with @bo in @bdev->bd_holder_list, + * add @bo to the list, create symlinks. + * + * Returns 1 if @bo was added to the list. + * Returns 0 if @bo wasn't used by any reason and should be freed. + */ +static int add_bd_holder(struct block_device *bdev, struct bd_holder *bo) +{ + struct bd_holder *tmp; + + if (!bo) + return 0; + + list_for_each_entry(tmp, &bdev->bd_holder_list, list) { + if (tmp->sdir == bo->sdir) { + tmp->count++; + return 0; + } + } + + if (!bd_holder_grab_dirs(bdev, bo)) + return 0; + + add_symlink(bo->sdir, bo->sdev); + add_symlink(bo->hdir, bo->hdev); + list_add_tail(&bo->list, &bdev->bd_holder_list); + return 1; +} + +/** + * del_bd_holder - delete sysfs symlinks for bd_claim() relationship + * + * @bdev: block device to be bd_claimed + * @kobj: holder's kobject + * + * If there is matching entry with @kobj in @bdev->bd_holder_list + * and no other bd_claim() from the same kobject, + * remove the struct bd_holder from the list, delete symlinks for it. + * + * Returns a pointer to the struct bd_holder when it's removed from the list + * and ready to be freed. + * Returns NULL if matching claim isn't found or there is other bd_claim() + * by the same kobject. + */ +static struct bd_holder *del_bd_holder(struct block_device *bdev, + struct kobject *kobj) +{ + struct bd_holder *bo; + + list_for_each_entry(bo, &bdev->bd_holder_list, list) { + if (bo->sdir == kobj) { + bo->count--; + BUG_ON(bo->count < 0); + if (!bo->count) { + list_del(&bo->list); + del_symlink(bo->sdir, bo->sdev); + del_symlink(bo->hdir, bo->hdev); + bd_holder_release_dirs(bo); + return bo; + } + break; + } + } + + return NULL; +} + +/** + * bd_claim_by_kobject - bd_claim() with additional kobject signature + * + * @bdev: block device to be claimed + * @holder: holder's signature + * @kobj: holder's kobject + * + * Do bd_claim() and if it succeeds, create sysfs symlinks between + * the bdev and the holder's kobject. + * Use bd_release_from_kobject() when relesing the claimed bdev. + * + * Returns 0 on success. (same as bd_claim()) + * Returns errno on failure. + */ +static int bd_claim_by_kobject(struct block_device *bdev, void *holder, + struct kobject *kobj) +{ + int res; + struct bd_holder *bo; + + if (!kobj) + return -EINVAL; + + bo = alloc_bd_holder(kobj); + if (!bo) + return -ENOMEM; + + mutex_lock(&bdev->bd_mutex); + res = bd_claim(bdev, holder); + if (res || !add_bd_holder(bdev, bo)) + free_bd_holder(bo); + mutex_unlock(&bdev->bd_mutex); + + return res; +} + +/** + * bd_release_from_kobject - bd_release() with additional kobject signature + * + * @bdev: block device to be released + * @kobj: holder's kobject + * + * Do bd_release() and remove sysfs symlinks created by bd_claim_by_kobject(). + */ +static void bd_release_from_kobject(struct block_device *bdev, + struct kobject *kobj) +{ + struct bd_holder *bo; + + if (!kobj) + return; + + mutex_lock(&bdev->bd_mutex); + bd_release(bdev); + if ((bo = del_bd_holder(bdev, kobj))) + free_bd_holder(bo); + mutex_unlock(&bdev->bd_mutex); +} + +/** + * bd_claim_by_disk - wrapper function for bd_claim_by_kobject() + * + * @bdev: block device to be claimed + * @holder: holder's signature + * @disk: holder's gendisk + * + * Call bd_claim_by_kobject() with getting @disk->slave_dir. + */ +int bd_claim_by_disk(struct block_device *bdev, void *holder, + struct gendisk *disk) +{ + return bd_claim_by_kobject(bdev, holder, kobject_get(disk->slave_dir)); +} +EXPORT_SYMBOL_GPL(bd_claim_by_disk); + +/** + * bd_release_from_disk - wrapper function for bd_release_from_kobject() + * + * @bdev: block device to be claimed + * @disk: holder's gendisk + * + * Call bd_release_from_kobject() and put @disk->slave_dir. + */ +void bd_release_from_disk(struct block_device *bdev, struct gendisk *disk) +{ + bd_release_from_kobject(bdev, disk->slave_dir); + kobject_put(disk->slave_dir); +} +EXPORT_SYMBOL_GPL(bd_release_from_disk); +#endif + /* * Tries to open block device by device number. Use it ONLY if you * really do not have anything better - i.e. when you are behind a @@ -790,7 +1087,7 @@ struct address_space_operations def_blk_aops = { .direct_IO = blkdev_direct_IO, }; -struct file_operations def_blk_fops = { +const struct file_operations def_blk_fops = { .open = blkdev_open, .release = blkdev_close, .llseek = block_llseek, diff --git a/fs/buffer.c b/fs/buffer.c index d597758dd129..23f1f3a68077 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -493,7 +493,7 @@ static void free_more_memory(void) wakeup_pdflush(1024); yield(); - for_each_pgdat(pgdat) { + for_each_online_pgdat(pgdat) { zones = pgdat->node_zonelists[gfp_zone(GFP_NOFS)].zones; if (*zones) try_to_free_pages(zones, GFP_NOFS); diff --git a/fs/char_dev.c b/fs/char_dev.c index 8c6eb04d31e2..4e1b849f912f 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c @@ -250,7 +250,7 @@ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, } int register_chrdev(unsigned int major, const char *name, - struct file_operations *fops) + const struct file_operations *fops) { struct char_device_struct *cd; struct cdev *cdev; @@ -406,7 +406,7 @@ static void cdev_purge(struct cdev *cdev) * is contain the open that then fills in the correct operations * depending on the special file... */ -struct file_operations def_chr_fops = { +const struct file_operations def_chr_fops = { .open = chrdev_open, }; @@ -473,7 +473,7 @@ struct cdev *cdev_alloc(void) return p; } -void cdev_init(struct cdev *cdev, struct file_operations *fops) +void cdev_init(struct cdev *cdev, const struct file_operations *fops) { memset(cdev, 0, sizeof *cdev); INIT_LIST_HEAD(&cdev->list); diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 6b99b51d6694..4bbc544857bc 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -583,7 +583,7 @@ struct inode_operations cifs_symlink_inode_ops = { #endif }; -struct file_operations cifs_file_ops = { +const struct file_operations cifs_file_ops = { .read = do_sync_read, .write = do_sync_write, .readv = generic_file_readv, @@ -607,7 +607,7 @@ struct file_operations cifs_file_ops = { #endif /* CONFIG_CIFS_EXPERIMENTAL */ }; -struct file_operations cifs_file_direct_ops = { +const struct file_operations cifs_file_direct_ops = { /* no mmap, no aio, no readv - BB reevaluate whether they can be done with directio, no cache */ .read = cifs_user_read, @@ -626,7 +626,7 @@ struct file_operations cifs_file_direct_ops = { .dir_notify = cifs_dir_notify, #endif /* CONFIG_CIFS_EXPERIMENTAL */ }; -struct file_operations cifs_file_nobrl_ops = { +const struct file_operations cifs_file_nobrl_ops = { .read = do_sync_read, .write = do_sync_write, .readv = generic_file_readv, @@ -649,7 +649,7 @@ struct file_operations cifs_file_nobrl_ops = { #endif /* CONFIG_CIFS_EXPERIMENTAL */ }; -struct file_operations cifs_file_direct_nobrl_ops = { +const struct file_operations cifs_file_direct_nobrl_ops = { /* no mmap, no aio, no readv - BB reevaluate whether they can be done with directio, no cache */ .read = cifs_user_read, @@ -668,7 +668,7 @@ struct file_operations cifs_file_direct_nobrl_ops = { #endif /* CONFIG_CIFS_EXPERIMENTAL */ }; -struct file_operations cifs_dir_ops = { +const struct file_operations cifs_dir_ops = { .readdir = cifs_readdir, .release = cifs_closedir, .read = generic_read_dir, diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 821a8eb22559..74f405ae4da3 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -61,10 +61,10 @@ extern struct inode_operations cifs_file_inode_ops; extern struct inode_operations cifs_symlink_inode_ops; /* Functions related to files and directories */ -extern struct file_operations cifs_file_ops; -extern struct file_operations cifs_file_direct_ops; /* if directio mount */ -extern struct file_operations cifs_file_nobrl_ops; -extern struct file_operations cifs_file_direct_nobrl_ops; /* if directio mount */ +extern const struct file_operations cifs_file_ops; +extern const struct file_operations cifs_file_direct_ops; /* if directio mount */ +extern const struct file_operations cifs_file_nobrl_ops; +extern const struct file_operations cifs_file_direct_nobrl_ops; /* if directio mount */ extern int cifs_open(struct inode *inode, struct file *file); extern int cifs_close(struct inode *inode, struct file *file); extern int cifs_closedir(struct inode *inode, struct file *file); @@ -76,7 +76,7 @@ extern int cifs_lock(struct file *, int, struct file_lock *); extern int cifs_fsync(struct file *, struct dentry *, int); extern int cifs_flush(struct file *); extern int cifs_file_mmap(struct file * , struct vm_area_struct *); -extern struct file_operations cifs_dir_ops; +extern const struct file_operations cifs_dir_ops; extern int cifs_dir_open(struct inode *inode, struct file *file); extern int cifs_readdir(struct file *file, void *direntry, filldir_t filldir); extern int cifs_dir_notify(struct file *, unsigned long arg); diff --git a/fs/coda/dir.c b/fs/coda/dir.c index 54f76de8a686..71f2ea632e53 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -82,7 +82,7 @@ struct inode_operations coda_dir_inode_operations = .setattr = coda_setattr, }; -struct file_operations coda_dir_operations = { +const struct file_operations coda_dir_operations = { .llseek = generic_file_llseek, .read = generic_read_dir, .readdir = coda_readdir, diff --git a/fs/coda/file.c b/fs/coda/file.c index 146a991d6eb5..7c2642431fa5 100644 --- a/fs/coda/file.c +++ b/fs/coda/file.c @@ -288,7 +288,7 @@ int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync) return err; } -struct file_operations coda_file_operations = { +const struct file_operations coda_file_operations = { .llseek = generic_file_llseek, .read = coda_file_read, .write = coda_file_write, diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c index 127714936c66..214822be87bd 100644 --- a/fs/coda/pioctl.c +++ b/fs/coda/pioctl.c @@ -36,7 +36,7 @@ struct inode_operations coda_ioctl_inode_operations = .setattr = coda_setattr, }; -struct file_operations coda_ioctl_operations = { +const struct file_operations coda_ioctl_operations = { .owner = THIS_MODULE, .ioctl = coda_pioctl, }; diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index 98c74fe2e139..6c6771db36da 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -342,7 +342,7 @@ static int coda_psdev_release(struct inode * inode, struct file * file) } -static struct file_operations coda_psdev_fops = { +static const struct file_operations coda_psdev_fops = { .owner = THIS_MODULE, .read = coda_psdev_read, .write = coda_psdev_write, diff --git a/fs/compat.c b/fs/compat.c index ef5a0771592d..7f8e26ea427c 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -1639,15 +1639,6 @@ void compat_set_fd_set(unsigned long nr, compat_ulong_t __user *ufdset, * This is a virtual copy of sys_select from fs/select.c and probably * should be compared to it from time to time */ -static void *select_bits_alloc(int size) -{ - return kmalloc(6 * size, GFP_KERNEL); -} - -static void select_bits_free(void *bits, int size) -{ - kfree(bits); -} /* * We can actually return ERESTARTSYS instead of EINTR, but I'd @@ -1686,7 +1677,7 @@ int compat_core_sys_select(int n, compat_ulong_t __user *inp, */ ret = -ENOMEM; size = FDS_BYTES(n); - bits = select_bits_alloc(size); + bits = kmalloc(6 * size, GFP_KERNEL); if (!bits) goto out_nofds; fds.in = (unsigned long *) bits; @@ -1720,7 +1711,7 @@ int compat_core_sys_select(int n, compat_ulong_t __user *inp, compat_set_fd_set(n, exp, fds.res_ex); out: - select_bits_free(bits, size); + kfree(bits); out_nofds: return ret; } diff --git a/fs/configfs/configfs_internal.h b/fs/configfs/configfs_internal.h index f70e46951b37..3f4ff7a242b9 100644 --- a/fs/configfs/configfs_internal.h +++ b/fs/configfs/configfs_internal.h @@ -72,9 +72,9 @@ extern void configfs_release_fs(void); extern struct rw_semaphore configfs_rename_sem; extern struct super_block * configfs_sb; -extern struct file_operations configfs_dir_operations; -extern struct file_operations configfs_file_operations; -extern struct file_operations bin_fops; +extern const struct file_operations configfs_dir_operations; +extern const struct file_operations configfs_file_operations; +extern const struct file_operations bin_fops; extern struct inode_operations configfs_dir_inode_operations; extern struct inode_operations configfs_symlink_inode_operations; diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index ca60e3abef45..8ed9b06a9828 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -1027,7 +1027,7 @@ static loff_t configfs_dir_lseek(struct file * file, loff_t offset, int origin) return offset; } -struct file_operations configfs_dir_operations = { +const struct file_operations configfs_dir_operations = { .open = configfs_dir_open, .release = configfs_dir_close, .llseek = configfs_dir_lseek, diff --git a/fs/configfs/file.c b/fs/configfs/file.c index 3921920d8716..f499803743e0 100644 --- a/fs/configfs/file.c +++ b/fs/configfs/file.c @@ -322,7 +322,7 @@ static int configfs_release(struct inode * inode, struct file * filp) return 0; } -struct file_operations configfs_file_operations = { +const struct file_operations configfs_file_operations = { .read = configfs_read_file, .write = configfs_write_file, .llseek = generic_file_llseek, diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c index acc1b2c10a86..9efcc3a164e8 100644 --- a/fs/cramfs/inode.c +++ b/fs/cramfs/inode.c @@ -29,7 +29,7 @@ static struct super_operations cramfs_ops; static struct inode_operations cramfs_dir_inode_operations; -static struct file_operations cramfs_directory_operations; +static const struct file_operations cramfs_directory_operations; static struct address_space_operations cramfs_aops; static DEFINE_MUTEX(read_mutex); @@ -512,7 +512,7 @@ static struct address_space_operations cramfs_aops = { /* * A directory can only readdir */ -static struct file_operations cramfs_directory_operations = { +static const struct file_operations cramfs_directory_operations = { .llseek = generic_file_llseek, .read = generic_read_dir, .readdir = cramfs_readdir, diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 40c4fc973fad..66a505422e5c 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -39,7 +39,7 @@ static int default_open(struct inode *inode, struct file *file) return 0; } -struct file_operations debugfs_file_operations = { +const struct file_operations debugfs_file_operations = { .read = default_read_file, .write = default_write_file, .open = default_open, @@ -213,7 +213,7 @@ static ssize_t write_file_bool(struct file *file, const char __user *user_buf, return count; } -static struct file_operations fops_bool = { +static const struct file_operations fops_bool = { .read = read_file_bool, .write = write_file_bool, .open = default_open, diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index d4f1a2cddd47..85d166cdcae4 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -191,7 +191,7 @@ static int debugfs_create_by_name(const char *name, mode_t mode, */ struct dentry *debugfs_create_file(const char *name, mode_t mode, struct dentry *parent, void *data, - struct file_operations *fops) + const struct file_operations *fops) { struct dentry *dentry = NULL; int error; diff --git a/fs/devfs/base.c b/fs/devfs/base.c index b621521e09d4..52f5059c4f31 100644 --- a/fs/devfs/base.c +++ b/fs/devfs/base.c @@ -856,14 +856,14 @@ static int devfsd_close(struct inode *inode, struct file *file); #ifdef CONFIG_DEVFS_DEBUG static ssize_t stat_read(struct file *file, char __user *buf, size_t len, loff_t * ppos); -static struct file_operations stat_fops = { +static const struct file_operations stat_fops = { .open = nonseekable_open, .read = stat_read, }; #endif /* Devfs daemon file operations */ -static struct file_operations devfsd_fops = { +static const struct file_operations devfsd_fops = { .open = nonseekable_open, .read = devfsd_read, .ioctl = devfsd_ioctl, @@ -1842,8 +1842,8 @@ static int try_modload(struct devfs_entry *parent, struct fs_info *fs_info, static struct inode_operations devfs_iops; static struct inode_operations devfs_dir_iops; -static struct file_operations devfs_fops; -static struct file_operations devfs_dir_fops; +static const struct file_operations devfs_fops; +static const struct file_operations devfs_dir_fops; static struct inode_operations devfs_symlink_iops; static int devfs_notify_change(struct dentry *dentry, struct iattr *iattr) @@ -2061,11 +2061,11 @@ static int devfs_open(struct inode *inode, struct file *file) return err; } /* End Function devfs_open */ -static struct file_operations devfs_fops = { +static const struct file_operations devfs_fops = { .open = devfs_open, }; -static struct file_operations devfs_dir_fops = { +static const struct file_operations devfs_dir_fops = { .read = generic_read_dir, .readdir = devfs_readdir, }; diff --git a/fs/efs/dir.c b/fs/efs/dir.c index 777c614ff360..17f5b2d3c16a 100644 --- a/fs/efs/dir.c +++ b/fs/efs/dir.c @@ -10,7 +10,7 @@ static int efs_readdir(struct file *, void *, filldir_t); -struct file_operations efs_dir_operations = { +const struct file_operations efs_dir_operations = { .read = generic_read_dir, .readdir = efs_readdir, }; diff --git a/fs/eventpoll.c b/fs/eventpoll.c index e067a06c6464..242fe1a66ce5 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -290,7 +290,7 @@ static kmem_cache_t *pwq_cache __read_mostly; static struct vfsmount *eventpoll_mnt __read_mostly; /* File callbacks that implement the eventpoll file behaviour */ -static struct file_operations eventpoll_fops = { +static const struct file_operations eventpoll_fops = { .release = ep_eventpoll_close, .poll = ep_eventpoll_poll }; diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index 0165388c425c..d672aa9f4061 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -658,7 +658,7 @@ not_empty: return 0; } -struct file_operations ext2_dir_operations = { +const struct file_operations ext2_dir_operations = { .llseek = generic_file_llseek, .read = generic_read_dir, .readdir = ext2_readdir, diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index 11035ac7986f..9f74a62be555 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -154,12 +154,12 @@ extern void ext2_write_super (struct super_block *); */ /* dir.c */ -extern struct file_operations ext2_dir_operations; +extern const struct file_operations ext2_dir_operations; /* file.c */ extern struct inode_operations ext2_file_inode_operations; -extern struct file_operations ext2_file_operations; -extern struct file_operations ext2_xip_file_operations; +extern const struct file_operations ext2_file_operations; +extern const struct file_operations ext2_xip_file_operations; /* inode.c */ extern struct address_space_operations ext2_aops; diff --git a/fs/ext2/file.c b/fs/ext2/file.c index a484412fc782..509cceca04db 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -39,7 +39,7 @@ static int ext2_release_file (struct inode * inode, struct file * filp) * We have mostly NULL's here: the current defaults are ok for * the ext2 filesystem. */ -struct file_operations ext2_file_operations = { +const struct file_operations ext2_file_operations = { .llseek = generic_file_llseek, .read = generic_file_read, .write = generic_file_write, @@ -56,7 +56,7 @@ struct file_operations ext2_file_operations = { }; #ifdef CONFIG_EXT2_FS_XIP -struct file_operations ext2_xip_file_operations = { +const struct file_operations ext2_xip_file_operations = { .llseek = generic_file_llseek, .read = xip_file_read, .write = xip_file_write, diff --git a/fs/ext3/dir.c b/fs/ext3/dir.c index 38bd3f6ec147..f37528ed222e 100644 --- a/fs/ext3/dir.c +++ b/fs/ext3/dir.c @@ -39,7 +39,7 @@ static int ext3_dx_readdir(struct file * filp, static int ext3_release_dir (struct inode * inode, struct file * filp); -struct file_operations ext3_dir_operations = { +const struct file_operations ext3_dir_operations = { .llseek = generic_file_llseek, .read = generic_read_dir, .readdir = ext3_readdir, /* we take BKL. needed?*/ diff --git a/fs/ext3/file.c b/fs/ext3/file.c index 59098ea56711..783a796220bb 100644 --- a/fs/ext3/file.c +++ b/fs/ext3/file.c @@ -105,7 +105,7 @@ force_commit: return ret; } -struct file_operations ext3_file_operations = { +const struct file_operations ext3_file_operations = { .llseek = generic_file_llseek, .read = do_sync_read, .write = do_sync_write, diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 4095bc149eb1..698b85bb1dd4 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -741,7 +741,7 @@ static int fat_dir_ioctl(struct inode * inode, struct file * filp, return ret; } -struct file_operations fat_dir_operations = { +const struct file_operations fat_dir_operations = { .read = generic_read_dir, .readdir = fat_readdir, .ioctl = fat_dir_ioctl, diff --git a/fs/fat/file.c b/fs/fat/file.c index 88aa1ae13f9f..1ee25232e6af 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -112,7 +112,7 @@ int fat_generic_ioctl(struct inode *inode, struct file *filp, } } -struct file_operations fat_file_operations = { +const struct file_operations fat_file_operations = { .llseek = generic_file_llseek, .read = do_sync_read, .write = do_sync_write, diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 404bfc9f7385..c1ce284f8a94 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -1435,9 +1435,6 @@ out_fail: EXPORT_SYMBOL_GPL(fat_fill_super); -int __init fat_cache_init(void); -void fat_cache_destroy(void); - static int __init init_fat_fs(void) { int err; diff --git a/fs/fifo.c b/fs/fifo.c index d13fcd3ec803..889f722ee36d 100644 --- a/fs/fifo.c +++ b/fs/fifo.c @@ -145,6 +145,6 @@ err_nocleanup: * is contain the open that then fills in the correct operations * depending on the access mode of the file... */ -struct file_operations def_fifo_fops = { +const struct file_operations def_fifo_fops = { .open = fifo_open, /* will set read or write pipe_fops */ }; diff --git a/fs/file.c b/fs/file.c index bbc743314730..55f4e7022563 100644 --- a/fs/file.c +++ b/fs/file.c @@ -373,6 +373,6 @@ static void __devinit fdtable_defer_list_init(int cpu) void __init files_defer_init(void) { int i; - for_each_cpu(i) + for_each_possible_cpu(i) fdtable_defer_list_init(i); } diff --git a/fs/freevxfs/vxfs_extern.h b/fs/freevxfs/vxfs_extern.h index 927acf70c591..1cf1fe8466a2 100644 --- a/fs/freevxfs/vxfs_extern.h +++ b/fs/freevxfs/vxfs_extern.h @@ -63,7 +63,7 @@ extern void vxfs_clear_inode(struct inode *); /* vxfs_lookup.c */ extern struct inode_operations vxfs_dir_inode_ops; -extern struct file_operations vxfs_dir_operations; +extern const struct file_operations vxfs_dir_operations; /* vxfs_olt.c */ extern int vxfs_read_olt(struct super_block *, u_long); diff --git a/fs/freevxfs/vxfs_lookup.c b/fs/freevxfs/vxfs_lookup.c index 554eb455722c..29cce456c7ce 100644 --- a/fs/freevxfs/vxfs_lookup.c +++ b/fs/freevxfs/vxfs_lookup.c @@ -56,7 +56,7 @@ struct inode_operations vxfs_dir_inode_ops = { .lookup = vxfs_lookup, }; -struct file_operations vxfs_dir_operations = { +const struct file_operations vxfs_dir_operations = { .readdir = vxfs_readdir, }; diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 0c9a2ee54c91..23d1f52eb1b8 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -922,7 +922,7 @@ static int fuse_dev_release(struct inode *inode, struct file *file) return 0; } -struct file_operations fuse_dev_operations = { +const struct file_operations fuse_dev_operations = { .owner = THIS_MODULE, .llseek = no_llseek, .read = fuse_dev_read, diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index c72a8a97935c..256355b80256 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1170,7 +1170,7 @@ static struct inode_operations fuse_dir_inode_operations = { .removexattr = fuse_removexattr, }; -static struct file_operations fuse_dir_operations = { +static const struct file_operations fuse_dir_operations = { .llseek = generic_file_llseek, .read = generic_read_dir, .readdir = fuse_readdir, diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 6f05379b0a0d..975f2697e866 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -12,7 +12,7 @@ #include <linux/slab.h> #include <linux/kernel.h> -static struct file_operations fuse_direct_io_file_operations; +static const struct file_operations fuse_direct_io_file_operations; static int fuse_send_open(struct inode *inode, struct file *file, int isdir, struct fuse_open_out *outargp) @@ -611,7 +611,7 @@ static int fuse_set_page_dirty(struct page *page) return 0; } -static struct file_operations fuse_file_operations = { +static const struct file_operations fuse_file_operations = { .llseek = generic_file_llseek, .read = generic_file_read, .write = generic_file_write, @@ -623,7 +623,7 @@ static struct file_operations fuse_file_operations = { .sendfile = generic_file_sendfile, }; -static struct file_operations fuse_direct_io_file_operations = { +static const struct file_operations fuse_direct_io_file_operations = { .llseek = generic_file_llseek, .read = fuse_direct_read, .write = fuse_direct_write, diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 4a83adfec968..a16a04fcf41e 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -346,7 +346,7 @@ static inline u64 get_node_id(struct inode *inode) } /** Device operations */ -extern struct file_operations fuse_dev_operations; +extern const struct file_operations fuse_dev_operations; /** * This is the single global spinlock which protects FUSE's structures diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c index 534e5a7480ef..7cd8cc03aea7 100644 --- a/fs/hfs/dir.c +++ b/fs/hfs/dir.c @@ -313,7 +313,7 @@ static int hfs_rename(struct inode *old_dir, struct dentry *old_dentry, return res; } -struct file_operations hfs_dir_operations = { +const struct file_operations hfs_dir_operations = { .read = generic_read_dir, .readdir = hfs_readdir, .llseek = generic_file_llseek, diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h index 18ce47ab1b71..3ed8663a8db1 100644 --- a/fs/hfs/hfs_fs.h +++ b/fs/hfs/hfs_fs.h @@ -169,7 +169,7 @@ extern int hfs_cat_move(u32, struct inode *, struct qstr *, extern void hfs_cat_build_key(struct super_block *, btree_key *, u32, struct qstr *); /* dir.c */ -extern struct file_operations hfs_dir_operations; +extern const struct file_operations hfs_dir_operations; extern struct inode_operations hfs_dir_inode_operations; /* extent.c */ diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 2c564701724f..2d4ced22201b 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -17,7 +17,7 @@ #include "hfs_fs.h" #include "btree.h" -static struct file_operations hfs_file_operations; +static const struct file_operations hfs_file_operations; static struct inode_operations hfs_file_inode_operations; /*================ Variable-like macros ================*/ @@ -601,7 +601,7 @@ int hfs_inode_setattr(struct dentry *dentry, struct iattr * attr) } -static struct file_operations hfs_file_operations = { +static const struct file_operations hfs_file_operations = { .llseek = generic_file_llseek, .read = generic_file_read, .write = generic_file_write, diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index 01a6fe3a395c..1f9ece0de326 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c @@ -483,7 +483,7 @@ struct inode_operations hfsplus_dir_inode_operations = { .rename = hfsplus_rename, }; -struct file_operations hfsplus_dir_operations = { +const struct file_operations hfsplus_dir_operations = { .read = generic_read_dir, .readdir = hfsplus_readdir, .ioctl = hfsplus_ioctl, diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 9fbe4d2aeece..acf66dba3e01 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -280,7 +280,7 @@ static struct inode_operations hfsplus_file_inode_operations = { .listxattr = hfsplus_listxattr, }; -static struct file_operations hfsplus_file_operations = { +static const struct file_operations hfsplus_file_operations = { .llseek = generic_file_llseek, .read = generic_file_read, .write = generic_file_write, diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index b3ad0bd0312f..bf0f8e16e433 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -384,7 +384,7 @@ int hostfs_fsync(struct file *file, struct dentry *dentry, int datasync) return fsync_file(HOSTFS_I(dentry->d_inode)->fd, datasync); } -static struct file_operations hostfs_file_fops = { +static const struct file_operations hostfs_file_fops = { .llseek = generic_file_llseek, .read = generic_file_read, .sendfile = generic_file_sendfile, @@ -399,7 +399,7 @@ static struct file_operations hostfs_file_fops = { .fsync = hostfs_fsync, }; -static struct file_operations hostfs_dir_fops = { +static const struct file_operations hostfs_dir_fops = { .llseek = generic_file_llseek, .readdir = hostfs_readdir, .read = generic_read_dir, diff --git a/fs/hostfs/hostfs_user.c b/fs/hostfs/hostfs_user.c index b97809deba66..23b7cee72123 100644 --- a/fs/hostfs/hostfs_user.c +++ b/fs/hostfs/hostfs_user.c @@ -360,7 +360,6 @@ int do_statfs(char *root, long *bsize_out, long long *blocks_out, spare_out[2] = buf.f_spare[2]; spare_out[3] = buf.f_spare[3]; spare_out[4] = buf.f_spare[4]; - spare_out[5] = buf.f_spare[5]; return(0); } diff --git a/fs/hpfs/dir.c b/fs/hpfs/dir.c index 5591f9623aa2..ecc9180645ae 100644 --- a/fs/hpfs/dir.c +++ b/fs/hpfs/dir.c @@ -310,7 +310,7 @@ struct dentry *hpfs_lookup(struct inode *dir, struct dentry *dentry, struct name return ERR_PTR(-ENOENT); } -struct file_operations hpfs_dir_ops = +const struct file_operations hpfs_dir_ops = { .llseek = hpfs_dir_lseek, .read = generic_read_dir, diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c index 7c995ac4081b..d3b9fffe45a1 100644 --- a/fs/hpfs/file.c +++ b/fs/hpfs/file.c @@ -119,7 +119,7 @@ static ssize_t hpfs_file_write(struct file *file, const char __user *buf, return retval; } -struct file_operations hpfs_file_ops = +const struct file_operations hpfs_file_ops = { .llseek = generic_file_llseek, .read = generic_file_read, diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h index 4c6473ab3b34..29b7a3e55173 100644 --- a/fs/hpfs/hpfs_fn.h +++ b/fs/hpfs/hpfs_fn.h @@ -240,7 +240,7 @@ void hpfs_set_dentry_operations(struct dentry *); /* dir.c */ struct dentry *hpfs_lookup(struct inode *, struct dentry *, struct nameidata *); -extern struct file_operations hpfs_dir_ops; +extern const struct file_operations hpfs_dir_ops; /* dnode.c */ @@ -266,7 +266,7 @@ void hpfs_set_ea(struct inode *, struct fnode *, char *, char *, int); /* file.c */ int hpfs_file_fsync(struct file *, struct dentry *, int); -extern struct file_operations hpfs_file_ops; +extern const struct file_operations hpfs_file_ops; extern struct inode_operations hpfs_file_iops; extern struct address_space_operations hpfs_aops; diff --git a/fs/hppfs/hppfs_kern.c b/fs/hppfs/hppfs_kern.c index a44dc5897399..2ba20cdb5baa 100644 --- a/fs/hppfs/hppfs_kern.c +++ b/fs/hppfs/hppfs_kern.c @@ -558,7 +558,7 @@ static loff_t hppfs_llseek(struct file *file, loff_t off, int where) return(default_llseek(file, off, where)); } -static struct file_operations hppfs_file_fops = { +static const struct file_operations hppfs_file_fops = { .owner = NULL, .llseek = hppfs_llseek, .read = hppfs_read, @@ -609,7 +609,7 @@ static int hppfs_fsync(struct file *file, struct dentry *dentry, int datasync) return(0); } -static struct file_operations hppfs_dir_fops = { +static const struct file_operations hppfs_dir_fops = { .owner = NULL, .readdir = hppfs_readdir, .open = hppfs_dir_open, diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 25fa8bba8cb5..3a5b4e923455 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -35,7 +35,7 @@ static struct super_operations hugetlbfs_ops; static struct address_space_operations hugetlbfs_aops; -struct file_operations hugetlbfs_file_operations; +const struct file_operations hugetlbfs_file_operations; static struct inode_operations hugetlbfs_dir_inode_operations; static struct inode_operations hugetlbfs_inode_operations; @@ -566,7 +566,7 @@ static void init_once(void *foo, kmem_cache_t *cachep, unsigned long flags) inode_init_once(&ei->vfs_inode); } -struct file_operations hugetlbfs_file_operations = { +const struct file_operations hugetlbfs_file_operations = { .mmap = hugetlbfs_file_mmap, .fsync = simple_sync_file, .get_unmapped_area = hugetlb_get_unmapped_area, diff --git a/fs/inode.c b/fs/inode.c index 1fddf2803af8..32b7c3375021 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -104,7 +104,7 @@ static struct inode *alloc_inode(struct super_block *sb) { static struct address_space_operations empty_aops; static struct inode_operations empty_iops; - static struct file_operations empty_fops; + static const struct file_operations empty_fops; struct inode *inode; if (sb->s_op->alloc_inode) diff --git a/fs/inotify.c b/fs/inotify.c index f48a3dae0712..367c487c014b 100644 --- a/fs/inotify.c +++ b/fs/inotify.c @@ -920,7 +920,7 @@ static long inotify_ioctl(struct file *file, unsigned int cmd, return ret; } -static struct file_operations inotify_fops = { +static const struct file_operations inotify_fops = { .poll = inotify_poll, .read = inotify_read, .release = inotify_release, diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index 7901ac9f97ab..5440ea292c69 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -16,7 +16,7 @@ static int isofs_readdir(struct file *, void *, filldir_t); -struct file_operations isofs_dir_operations = +const struct file_operations isofs_dir_operations = { .read = generic_read_dir, .readdir = isofs_readdir, diff --git a/fs/isofs/isofs.h b/fs/isofs/isofs.h index 439a19b1bf3e..b87ba066f5e7 100644 --- a/fs/isofs/isofs.h +++ b/fs/isofs/isofs.h @@ -175,6 +175,6 @@ isofs_normalize_block_and_offset(struct iso_directory_record* de, } extern struct inode_operations isofs_dir_inode_operations; -extern struct file_operations isofs_dir_operations; +extern const struct file_operations isofs_dir_operations; extern struct address_space_operations isofs_symlink_aops; extern struct export_operations isofs_export_ops; diff --git a/fs/jffs/inode-v23.c b/fs/jffs/inode-v23.c index 5a4519e834da..020cc097c539 100644 --- a/fs/jffs/inode-v23.c +++ b/fs/jffs/inode-v23.c @@ -55,9 +55,9 @@ static int jffs_remove(struct inode *dir, struct dentry *dentry, int type); static struct super_operations jffs_ops; -static struct file_operations jffs_file_operations; +static const struct file_operations jffs_file_operations; static struct inode_operations jffs_file_inode_operations; -static struct file_operations jffs_dir_operations; +static const struct file_operations jffs_dir_operations; static struct inode_operations jffs_dir_inode_operations; static struct address_space_operations jffs_address_operations; @@ -1629,7 +1629,7 @@ static int jffs_fsync(struct file *f, struct dentry *d, int datasync) } -static struct file_operations jffs_file_operations = +static const struct file_operations jffs_file_operations = { .open = generic_file_open, .llseek = generic_file_llseek, @@ -1649,7 +1649,7 @@ static struct inode_operations jffs_file_inode_operations = }; -static struct file_operations jffs_dir_operations = +static const struct file_operations jffs_dir_operations = { .readdir = jffs_readdir, }; diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index a7bf9cb2567f..8bc7a5018e40 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -37,7 +37,7 @@ static int jffs2_mknod (struct inode *,struct dentry *,int,dev_t); static int jffs2_rename (struct inode *, struct dentry *, struct inode *, struct dentry *); -struct file_operations jffs2_dir_operations = +const struct file_operations jffs2_dir_operations = { .read = generic_read_dir, .readdir = jffs2_readdir, diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index 935f273dc57b..9f4171213e58 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -38,7 +38,7 @@ int jffs2_fsync(struct file *filp, struct dentry *dentry, int datasync) return 0; } -struct file_operations jffs2_file_operations = +const struct file_operations jffs2_file_operations = { .llseek = generic_file_llseek, .open = generic_file_open, diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index 59e7a393200c..d307cf548625 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -159,11 +159,11 @@ void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c); void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c); /* dir.c */ -extern struct file_operations jffs2_dir_operations; +extern const struct file_operations jffs2_dir_operations; extern struct inode_operations jffs2_dir_inode_operations; /* file.c */ -extern struct file_operations jffs2_file_operations; +extern const struct file_operations jffs2_file_operations; extern struct inode_operations jffs2_file_inode_operations; extern struct address_space_operations jffs2_file_address_operations; int jffs2_fsync(struct file *, struct dentry *, int); diff --git a/fs/jfs/file.c b/fs/jfs/file.c index e1ac6e497e2b..1c9745be5ada 100644 --- a/fs/jfs/file.c +++ b/fs/jfs/file.c @@ -100,7 +100,7 @@ struct inode_operations jfs_file_inode_operations = { #endif }; -struct file_operations jfs_file_operations = { +const struct file_operations jfs_file_operations = { .open = jfs_open, .llseek = generic_file_llseek, .write = generic_file_write, diff --git a/fs/jfs/jfs_inode.h b/fs/jfs/jfs_inode.h index 095d471b9f9a..c30072674464 100644 --- a/fs/jfs/jfs_inode.h +++ b/fs/jfs/jfs_inode.h @@ -35,9 +35,9 @@ extern void jfs_set_inode_flags(struct inode *); extern struct address_space_operations jfs_aops; extern struct inode_operations jfs_dir_inode_operations; -extern struct file_operations jfs_dir_operations; +extern const struct file_operations jfs_dir_operations; extern struct inode_operations jfs_file_inode_operations; -extern struct file_operations jfs_file_operations; +extern const struct file_operations jfs_file_operations; extern struct inode_operations jfs_symlink_inode_operations; extern struct dentry_operations jfs_ci_dentry_operations; #endif /* _H_JFS_INODE */ diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index 309cee575f7d..09ea03f62277 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -1519,7 +1519,7 @@ struct inode_operations jfs_dir_inode_operations = { #endif }; -struct file_operations jfs_dir_operations = { +const struct file_operations jfs_dir_operations = { .read = generic_read_dir, .readdir = jfs_readdir, .fsync = jfs_fsync, diff --git a/fs/libfs.c b/fs/libfs.c index 4fdeaceb892c..7145ba7a48d0 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -179,7 +179,7 @@ ssize_t generic_read_dir(struct file *filp, char __user *buf, size_t siz, loff_t return -EISDIR; } -struct file_operations simple_dir_operations = { +const struct file_operations simple_dir_operations = { .open = dcache_dir_open, .release = dcache_dir_close, .llseek = dcache_dir_lseek, diff --git a/fs/mbcache.c b/fs/mbcache.c index 73e754fea2d8..e4fde1ab22cd 100644 --- a/fs/mbcache.c +++ b/fs/mbcache.c @@ -311,7 +311,7 @@ fail: /* * mb_cache_shrink() * - * Removes all cache entires of a device from the cache. All cache entries + * Removes all cache entries of a device from the cache. All cache entries * currently in use cannot be freed, and thus remain in the cache. All others * are freed. * diff --git a/fs/minix/dir.c b/fs/minix/dir.c index 732502aabc05..69224d1fe043 100644 --- a/fs/minix/dir.c +++ b/fs/minix/dir.c @@ -14,7 +14,7 @@ typedef struct minix_dir_entry minix_dirent; static int minix_readdir(struct file *, void *, filldir_t); -struct file_operations minix_dir_operations = { +const struct file_operations minix_dir_operations = { .read = generic_read_dir, .readdir = minix_readdir, .fsync = minix_sync_file, diff --git a/fs/minix/file.c b/fs/minix/file.c index f1d77acb3f01..420b32882a10 100644 --- a/fs/minix/file.c +++ b/fs/minix/file.c @@ -15,7 +15,7 @@ */ int minix_sync_file(struct file *, struct dentry *, int); -struct file_operations minix_file_operations = { +const struct file_operations minix_file_operations = { .llseek = generic_file_llseek, .read = generic_file_read, .write = generic_file_write, diff --git a/fs/minix/minix.h b/fs/minix/minix.h index e42a8bb89001..c55b77cdcc8e 100644 --- a/fs/minix/minix.h +++ b/fs/minix/minix.h @@ -81,8 +81,8 @@ extern int minix_sync_file(struct file *, struct dentry *, int); extern struct inode_operations minix_file_inode_operations; extern struct inode_operations minix_dir_inode_operations; -extern struct file_operations minix_file_operations; -extern struct file_operations minix_dir_operations; +extern const struct file_operations minix_file_operations; +extern const struct file_operations minix_dir_operations; extern struct dentry_operations minix_dentry_operations; static inline struct minix_sb_info *minix_sb(struct super_block *sb) diff --git a/fs/namei.c b/fs/namei.c index 98dc2e134362..22f6e8d16aa8 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -546,6 +546,22 @@ struct path { struct dentry *dentry; }; +static inline void dput_path(struct path *path, struct nameidata *nd) +{ + dput(path->dentry); + if (path->mnt != nd->mnt) + mntput(path->mnt); +} + +static inline void path_to_nameidata(struct path *path, struct nameidata *nd) +{ + dput(nd->dentry); + if (nd->mnt != path->mnt) + mntput(nd->mnt); + nd->mnt = path->mnt; + nd->dentry = path->dentry; +} + static __always_inline int __do_follow_link(struct path *path, struct nameidata *nd) { int error; @@ -555,8 +571,11 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata touch_atime(path->mnt, dentry); nd_set_link(nd, NULL); - if (path->mnt == nd->mnt) - mntget(path->mnt); + if (path->mnt != nd->mnt) { + path_to_nameidata(path, nd); + dget(dentry); + } + mntget(path->mnt); cookie = dentry->d_inode->i_op->follow_link(dentry, nd); error = PTR_ERR(cookie); if (!IS_ERR(cookie)) { @@ -573,22 +592,6 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata return error; } -static inline void dput_path(struct path *path, struct nameidata *nd) -{ - dput(path->dentry); - if (path->mnt != nd->mnt) - mntput(path->mnt); -} - -static inline void path_to_nameidata(struct path *path, struct nameidata *nd) -{ - dput(nd->dentry); - if (nd->mnt != path->mnt) - mntput(nd->mnt); - nd->mnt = path->mnt; - nd->dentry = path->dentry; -} - /* * This limits recursive symlink follows to 8, while * limiting consecutive symlinks to 40. diff --git a/fs/namespace.c b/fs/namespace.c index e069a4c5e389..bf478addb852 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -459,9 +459,9 @@ int may_umount_tree(struct vfsmount *mnt) spin_unlock(&vfsmount_lock); if (actual_refs > minimum_refs) - return -EBUSY; + return 0; - return 0; + return 1; } EXPORT_SYMBOL(may_umount_tree); @@ -481,10 +481,10 @@ EXPORT_SYMBOL(may_umount_tree); */ int may_umount(struct vfsmount *mnt) { - int ret = 0; + int ret = 1; spin_lock(&vfsmount_lock); if (propagate_mount_busy(mnt, 2)) - ret = -EBUSY; + ret = 0; spin_unlock(&vfsmount_lock); return ret; } diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index cfd76f431dc0..f0860c602d8b 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -49,7 +49,7 @@ extern int ncp_symlink(struct inode *, struct dentry *, const char *); #define ncp_symlink NULL #endif -struct file_operations ncp_dir_operations = +const struct file_operations ncp_dir_operations = { .read = generic_read_dir, .readdir = ncp_readdir, diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c index ebdad8f6398f..e6b7c67cf057 100644 --- a/fs/ncpfs/file.c +++ b/fs/ncpfs/file.c @@ -283,7 +283,7 @@ static int ncp_release(struct inode *inode, struct file *file) { return 0; } -struct file_operations ncp_file_operations = +const struct file_operations ncp_file_operations = { .llseek = remote_llseek, .read = ncp_file_read, diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 06c48b385c94..a23f34894167 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -54,7 +54,7 @@ static int nfs_rename(struct inode *, struct dentry *, static int nfs_fsync_dir(struct file *, struct dentry *, int); static loff_t nfs_llseek_dir(struct file *, loff_t, int); -struct file_operations nfs_dir_operations = { +const struct file_operations nfs_dir_operations = { .llseek = nfs_llseek_dir, .read = generic_read_dir, .readdir = nfs_readdir, diff --git a/fs/nfs/file.c b/fs/nfs/file.c index dee49a0cb995..f1df2c8d9259 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -49,7 +49,7 @@ static int nfs_check_flags(int flags); static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl); static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl); -struct file_operations nfs_file_operations = { +const struct file_operations nfs_file_operations = { .llseek = nfs_file_llseek, .read = do_sync_read, .write = do_sync_write, diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 417ec02df44f..c340be0a3f59 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -57,27 +57,17 @@ static int exp_verify_string(char *cp, int max); #define EXPKEY_HASHMASK (EXPKEY_HASHMAX -1) static struct cache_head *expkey_table[EXPKEY_HASHMAX]; -static inline int svc_expkey_hash(struct svc_expkey *item) +static void expkey_put(struct kref *ref) { - int hash = item->ek_fsidtype; - char * cp = (char*)item->ek_fsid; - int len = key_len(item->ek_fsidtype); + struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref); - hash ^= hash_mem(cp, len, EXPKEY_HASHBITS); - hash ^= hash_ptr(item->ek_client, EXPKEY_HASHBITS); - return hash & EXPKEY_HASHMASK; -} - -void expkey_put(struct cache_head *item, struct cache_detail *cd) -{ - if (cache_put(item, cd)) { - struct svc_expkey *key = container_of(item, struct svc_expkey, h); - if (test_bit(CACHE_VALID, &item->flags) && - !test_bit(CACHE_NEGATIVE, &item->flags)) - exp_put(key->ek_export); - auth_domain_put(key->ek_client); - kfree(key); + if (test_bit(CACHE_VALID, &key->h.flags) && + !test_bit(CACHE_NEGATIVE, &key->h.flags)) { + dput(key->ek_dentry); + mntput(key->ek_mnt); } + auth_domain_put(key->ek_client); + kfree(key); } static void expkey_request(struct cache_detail *cd, @@ -95,7 +85,10 @@ static void expkey_request(struct cache_detail *cd, (*bpp)[-1] = '\n'; } -static struct svc_expkey *svc_expkey_lookup(struct svc_expkey *, int); +static struct svc_expkey *svc_expkey_update(struct svc_expkey *new, struct svc_expkey *old); +static struct svc_expkey *svc_expkey_lookup(struct svc_expkey *); +static struct cache_detail svc_expkey_cache; + static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen) { /* client fsidtype fsid [path] */ @@ -106,6 +99,7 @@ static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen) int fsidtype; char *ep; struct svc_expkey key; + struct svc_expkey *ek; if (mesg[mlen-1] != '\n') return -EINVAL; @@ -150,40 +144,38 @@ static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen) key.ek_fsidtype = fsidtype; memcpy(key.ek_fsid, buf, len); + ek = svc_expkey_lookup(&key); + err = -ENOMEM; + if (!ek) + goto out; + /* now we want a pathname, or empty meaning NEGATIVE */ + err = -EINVAL; if ((len=qword_get(&mesg, buf, PAGE_SIZE)) < 0) goto out; dprintk("Path seems to be <%s>\n", buf); err = 0; if (len == 0) { - struct svc_expkey *ek; set_bit(CACHE_NEGATIVE, &key.h.flags); - ek = svc_expkey_lookup(&key, 1); + ek = svc_expkey_update(&key, ek); if (ek) - expkey_put(&ek->h, &svc_expkey_cache); + cache_put(&ek->h, &svc_expkey_cache); + else err = -ENOMEM; } else { struct nameidata nd; - struct svc_expkey *ek; - struct svc_export *exp; err = path_lookup(buf, 0, &nd); if (err) goto out; dprintk("Found the path %s\n", buf); - exp = exp_get_by_name(dom, nd.mnt, nd.dentry, NULL); - - err = -ENOENT; - if (!exp) - goto out_nd; - key.ek_export = exp; - dprintk("And found export\n"); + key.ek_mnt = nd.mnt; + key.ek_dentry = nd.dentry; - ek = svc_expkey_lookup(&key, 1); + ek = svc_expkey_update(&key, ek); if (ek) - expkey_put(&ek->h, &svc_expkey_cache); - exp_put(exp); - err = 0; - out_nd: + cache_put(&ek->h, &svc_expkey_cache); + else + err = -ENOMEM; path_release(&nd); } cache_flush(); @@ -214,35 +206,31 @@ static int expkey_show(struct seq_file *m, if (test_bit(CACHE_VALID, &h->flags) && !test_bit(CACHE_NEGATIVE, &h->flags)) { seq_printf(m, " "); - seq_path(m, ek->ek_export->ex_mnt, ek->ek_export->ex_dentry, "\\ \t\n"); + seq_path(m, ek->ek_mnt, ek->ek_dentry, "\\ \t\n"); } seq_printf(m, "\n"); return 0; } - -struct cache_detail svc_expkey_cache = { - .owner = THIS_MODULE, - .hash_size = EXPKEY_HASHMAX, - .hash_table = expkey_table, - .name = "nfsd.fh", - .cache_put = expkey_put, - .cache_request = expkey_request, - .cache_parse = expkey_parse, - .cache_show = expkey_show, -}; -static inline int svc_expkey_match (struct svc_expkey *a, struct svc_expkey *b) +static inline int expkey_match (struct cache_head *a, struct cache_head *b) { - if (a->ek_fsidtype != b->ek_fsidtype || - a->ek_client != b->ek_client || - memcmp(a->ek_fsid, b->ek_fsid, key_len(a->ek_fsidtype)) != 0) + struct svc_expkey *orig = container_of(a, struct svc_expkey, h); + struct svc_expkey *new = container_of(b, struct svc_expkey, h); + + if (orig->ek_fsidtype != new->ek_fsidtype || + orig->ek_client != new->ek_client || + memcmp(orig->ek_fsid, new->ek_fsid, key_len(orig->ek_fsidtype)) != 0) return 0; return 1; } -static inline void svc_expkey_init(struct svc_expkey *new, struct svc_expkey *item) +static inline void expkey_init(struct cache_head *cnew, + struct cache_head *citem) { - cache_get(&item->ek_client->h); + struct svc_expkey *new = container_of(cnew, struct svc_expkey, h); + struct svc_expkey *item = container_of(citem, struct svc_expkey, h); + + kref_get(&item->ek_client->ref); new->ek_client = item->ek_client; new->ek_fsidtype = item->ek_fsidtype; new->ek_fsid[0] = item->ek_fsid[0]; @@ -250,39 +238,94 @@ static inline void svc_expkey_init(struct svc_expkey *new, struct svc_expkey *it new->ek_fsid[2] = item->ek_fsid[2]; } -static inline void svc_expkey_update(struct svc_expkey *new, struct svc_expkey *item) +static inline void expkey_update(struct cache_head *cnew, + struct cache_head *citem) +{ + struct svc_expkey *new = container_of(cnew, struct svc_expkey, h); + struct svc_expkey *item = container_of(citem, struct svc_expkey, h); + + new->ek_mnt = mntget(item->ek_mnt); + new->ek_dentry = dget(item->ek_dentry); +} + +static struct cache_head *expkey_alloc(void) { - cache_get(&item->ek_export->h); - new->ek_export = item->ek_export; + struct svc_expkey *i = kmalloc(sizeof(*i), GFP_KERNEL); + if (i) + return &i->h; + else + return NULL; } -static DefineSimpleCacheLookup(svc_expkey,0) /* no inplace updates */ +static struct cache_detail svc_expkey_cache = { + .owner = THIS_MODULE, + .hash_size = EXPKEY_HASHMAX, + .hash_table = expkey_table, + .name = "nfsd.fh", + .cache_put = expkey_put, + .cache_request = expkey_request, + .cache_parse = expkey_parse, + .cache_show = expkey_show, + .match = expkey_match, + .init = expkey_init, + .update = expkey_update, + .alloc = expkey_alloc, +}; -#define EXPORT_HASHBITS 8 -#define EXPORT_HASHMAX (1<< EXPORT_HASHBITS) -#define EXPORT_HASHMASK (EXPORT_HASHMAX -1) +static struct svc_expkey * +svc_expkey_lookup(struct svc_expkey *item) +{ + struct cache_head *ch; + int hash = item->ek_fsidtype; + char * cp = (char*)item->ek_fsid; + int len = key_len(item->ek_fsidtype); -static struct cache_head *export_table[EXPORT_HASHMAX]; + hash ^= hash_mem(cp, len, EXPKEY_HASHBITS); + hash ^= hash_ptr(item->ek_client, EXPKEY_HASHBITS); + hash &= EXPKEY_HASHMASK; -static inline int svc_export_hash(struct svc_export *item) + ch = sunrpc_cache_lookup(&svc_expkey_cache, &item->h, + hash); + if (ch) + return container_of(ch, struct svc_expkey, h); + else + return NULL; +} + +static struct svc_expkey * +svc_expkey_update(struct svc_expkey *new, struct svc_expkey *old) { - int rv; + struct cache_head *ch; + int hash = new->ek_fsidtype; + char * cp = (char*)new->ek_fsid; + int len = key_len(new->ek_fsidtype); - rv = hash_ptr(item->ex_client, EXPORT_HASHBITS); - rv ^= hash_ptr(item->ex_dentry, EXPORT_HASHBITS); - rv ^= hash_ptr(item->ex_mnt, EXPORT_HASHBITS); - return rv; + hash ^= hash_mem(cp, len, EXPKEY_HASHBITS); + hash ^= hash_ptr(new->ek_client, EXPKEY_HASHBITS); + hash &= EXPKEY_HASHMASK; + + ch = sunrpc_cache_update(&svc_expkey_cache, &new->h, + &old->h, hash); + if (ch) + return container_of(ch, struct svc_expkey, h); + else + return NULL; } -void svc_export_put(struct cache_head *item, struct cache_detail *cd) + +#define EXPORT_HASHBITS 8 +#define EXPORT_HASHMAX (1<< EXPORT_HASHBITS) +#define EXPORT_HASHMASK (EXPORT_HASHMAX -1) + +static struct cache_head *export_table[EXPORT_HASHMAX]; + +static void svc_export_put(struct kref *ref) { - if (cache_put(item, cd)) { - struct svc_export *exp = container_of(item, struct svc_export, h); - dput(exp->ex_dentry); - mntput(exp->ex_mnt); - auth_domain_put(exp->ex_client); - kfree(exp); - } + struct svc_export *exp = container_of(ref, struct svc_export, h.ref); + dput(exp->ex_dentry); + mntput(exp->ex_mnt); + auth_domain_put(exp->ex_client); + kfree(exp); } static void svc_export_request(struct cache_detail *cd, @@ -304,7 +347,9 @@ static void svc_export_request(struct cache_detail *cd, (*bpp)[-1] = '\n'; } -static struct svc_export *svc_export_lookup(struct svc_export *, int); +static struct svc_export *svc_export_update(struct svc_export *new, + struct svc_export *old); +static struct svc_export *svc_export_lookup(struct svc_export *); static int check_export(struct inode *inode, int flags) { @@ -417,11 +462,16 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) if (err) goto out; } - expp = svc_export_lookup(&exp, 1); + expp = svc_export_lookup(&exp); if (expp) - exp_put(expp); - err = 0; + expp = svc_export_update(&exp, expp); + else + err = -ENOMEM; cache_flush(); + if (expp == NULL) + err = -ENOMEM; + else + exp_put(expp); out: if (nd.dentry) path_release(&nd); @@ -455,6 +505,46 @@ static int svc_export_show(struct seq_file *m, seq_puts(m, ")\n"); return 0; } +static int svc_export_match(struct cache_head *a, struct cache_head *b) +{ + struct svc_export *orig = container_of(a, struct svc_export, h); + struct svc_export *new = container_of(b, struct svc_export, h); + return orig->ex_client == new->ex_client && + orig->ex_dentry == new->ex_dentry && + orig->ex_mnt == new->ex_mnt; +} + +static void svc_export_init(struct cache_head *cnew, struct cache_head *citem) +{ + struct svc_export *new = container_of(cnew, struct svc_export, h); + struct svc_export *item = container_of(citem, struct svc_export, h); + + kref_get(&item->ex_client->ref); + new->ex_client = item->ex_client; + new->ex_dentry = dget(item->ex_dentry); + new->ex_mnt = mntget(item->ex_mnt); +} + +static void export_update(struct cache_head *cnew, struct cache_head *citem) +{ + struct svc_export *new = container_of(cnew, struct svc_export, h); + struct svc_export *item = container_of(citem, struct svc_export, h); + + new->ex_flags = item->ex_flags; + new->ex_anon_uid = item->ex_anon_uid; + new->ex_anon_gid = item->ex_anon_gid; + new->ex_fsid = item->ex_fsid; +} + +static struct cache_head *svc_export_alloc(void) +{ + struct svc_export *i = kmalloc(sizeof(*i), GFP_KERNEL); + if (i) + return &i->h; + else + return NULL; +} + struct cache_detail svc_export_cache = { .owner = THIS_MODULE, .hash_size = EXPORT_HASHMAX, @@ -464,34 +554,49 @@ struct cache_detail svc_export_cache = { .cache_request = svc_export_request, .cache_parse = svc_export_parse, .cache_show = svc_export_show, + .match = svc_export_match, + .init = svc_export_init, + .update = export_update, + .alloc = svc_export_alloc, }; -static inline int svc_export_match(struct svc_export *a, struct svc_export *b) +static struct svc_export * +svc_export_lookup(struct svc_export *exp) { - return a->ex_client == b->ex_client && - a->ex_dentry == b->ex_dentry && - a->ex_mnt == b->ex_mnt; -} -static inline void svc_export_init(struct svc_export *new, struct svc_export *item) -{ - cache_get(&item->ex_client->h); - new->ex_client = item->ex_client; - new->ex_dentry = dget(item->ex_dentry); - new->ex_mnt = mntget(item->ex_mnt); + struct cache_head *ch; + int hash; + hash = hash_ptr(exp->ex_client, EXPORT_HASHBITS); + hash ^= hash_ptr(exp->ex_dentry, EXPORT_HASHBITS); + hash ^= hash_ptr(exp->ex_mnt, EXPORT_HASHBITS); + + ch = sunrpc_cache_lookup(&svc_export_cache, &exp->h, + hash); + if (ch) + return container_of(ch, struct svc_export, h); + else + return NULL; } -static inline void svc_export_update(struct svc_export *new, struct svc_export *item) +static struct svc_export * +svc_export_update(struct svc_export *new, struct svc_export *old) { - new->ex_flags = item->ex_flags; - new->ex_anon_uid = item->ex_anon_uid; - new->ex_anon_gid = item->ex_anon_gid; - new->ex_fsid = item->ex_fsid; + struct cache_head *ch; + int hash; + hash = hash_ptr(old->ex_client, EXPORT_HASHBITS); + hash ^= hash_ptr(old->ex_dentry, EXPORT_HASHBITS); + hash ^= hash_ptr(old->ex_mnt, EXPORT_HASHBITS); + + ch = sunrpc_cache_update(&svc_export_cache, &new->h, + &old->h, + hash); + if (ch) + return container_of(ch, struct svc_export, h); + else + return NULL; } -static DefineSimpleCacheLookup(svc_export,1) /* allow inplace updates */ - -struct svc_expkey * +static struct svc_expkey * exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp) { struct svc_expkey key, *ek; @@ -504,7 +609,7 @@ exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp) key.ek_fsidtype = fsid_type; memcpy(key.ek_fsid, fsidv, key_len(fsid_type)); - ek = svc_expkey_lookup(&key, 0); + ek = svc_expkey_lookup(&key); if (ek != NULL) if ((err = cache_check(&svc_expkey_cache, &ek->h, reqp))) ek = ERR_PTR(err); @@ -519,13 +624,16 @@ static int exp_set_key(svc_client *clp, int fsid_type, u32 *fsidv, key.ek_client = clp; key.ek_fsidtype = fsid_type; memcpy(key.ek_fsid, fsidv, key_len(fsid_type)); - key.ek_export = exp; + key.ek_mnt = exp->ex_mnt; + key.ek_dentry = exp->ex_dentry; key.h.expiry_time = NEVER; key.h.flags = 0; - ek = svc_expkey_lookup(&key, 1); + ek = svc_expkey_lookup(&key); + if (ek) + ek = svc_expkey_update(&key,ek); if (ek) { - expkey_put(&ek->h, &svc_expkey_cache); + cache_put(&ek->h, &svc_expkey_cache); return 0; } return -ENOMEM; @@ -573,7 +681,7 @@ exp_get_by_name(svc_client *clp, struct vfsmount *mnt, struct dentry *dentry, key.ex_mnt = mnt; key.ex_dentry = dentry; - exp = svc_export_lookup(&key, 0); + exp = svc_export_lookup(&key); if (exp != NULL) switch (cache_check(&svc_export_cache, &exp->h, reqp)) { case 0: break; @@ -654,7 +762,7 @@ static void exp_fsid_unhash(struct svc_export *exp) ek = exp_get_fsid_key(exp->ex_client, exp->ex_fsid); if (ek && !IS_ERR(ek)) { ek->h.expiry_time = get_seconds()-1; - expkey_put(&ek->h, &svc_expkey_cache); + cache_put(&ek->h, &svc_expkey_cache); } svc_expkey_cache.nextcheck = get_seconds(); } @@ -692,7 +800,7 @@ static void exp_unhash(struct svc_export *exp) ek = exp_get_key(exp->ex_client, inode->i_sb->s_dev, inode->i_ino); if (ek && !IS_ERR(ek)) { ek->h.expiry_time = get_seconds()-1; - expkey_put(&ek->h, &svc_expkey_cache); + cache_put(&ek->h, &svc_expkey_cache); } svc_expkey_cache.nextcheck = get_seconds(); } @@ -741,8 +849,8 @@ exp_export(struct nfsctl_export *nxp) if ((nxp->ex_flags & NFSEXP_FSID) && (fsid_key = exp_get_fsid_key(clp, nxp->ex_dev)) && !IS_ERR(fsid_key) && - fsid_key->ek_export && - fsid_key->ek_export != exp) + fsid_key->ek_mnt && + (fsid_key->ek_mnt != nd.mnt || fsid_key->ek_dentry != nd.dentry) ) goto finish; if (exp) { @@ -775,13 +883,13 @@ exp_export(struct nfsctl_export *nxp) new.ex_anon_gid = nxp->ex_anon_gid; new.ex_fsid = nxp->ex_dev; - exp = svc_export_lookup(&new, 1); + exp = svc_export_lookup(&new); + if (exp) + exp = svc_export_update(&new, exp); - if (exp == NULL) + if (!exp) goto finish; - err = 0; - if (exp_hash(clp, exp) || exp_fsid_hash(clp, exp)) { /* failed to create at least one index */ @@ -794,7 +902,7 @@ finish: if (exp) exp_put(exp); if (fsid_key && !IS_ERR(fsid_key)) - expkey_put(&fsid_key->h, &svc_expkey_cache); + cache_put(&fsid_key->h, &svc_expkey_cache); if (clp) auth_domain_put(clp); path_release(&nd); @@ -912,6 +1020,24 @@ out: return err; } +struct svc_export * +exp_find(struct auth_domain *clp, int fsid_type, u32 *fsidv, + struct cache_req *reqp) +{ + struct svc_export *exp; + struct svc_expkey *ek = exp_find_key(clp, fsid_type, fsidv, reqp); + if (!ek || IS_ERR(ek)) + return ERR_PTR(PTR_ERR(ek)); + + exp = exp_get_by_name(clp, ek->ek_mnt, ek->ek_dentry, reqp); + cache_put(&ek->h, &svc_expkey_cache); + + if (!exp || IS_ERR(exp)) + return ERR_PTR(PTR_ERR(exp)); + return exp; +} + + /* * Called when we need the filehandle for the root of the pseudofs, * for a given NFSv4 client. The root is defined to be the @@ -922,6 +1048,7 @@ exp_pseudoroot(struct auth_domain *clp, struct svc_fh *fhp, struct cache_req *creq) { struct svc_expkey *fsid_key; + struct svc_export *exp; int rv; u32 fsidv[2]; @@ -933,9 +1060,15 @@ exp_pseudoroot(struct auth_domain *clp, struct svc_fh *fhp, if (!fsid_key || IS_ERR(fsid_key)) return nfserr_perm; - rv = fh_compose(fhp, fsid_key->ek_export, - fsid_key->ek_export->ex_dentry, NULL); - expkey_put(&fsid_key->h, &svc_expkey_cache); + exp = exp_get_by_name(clp, fsid_key->ek_mnt, fsid_key->ek_dentry, creq); + if (exp == NULL) + rv = nfserr_perm; + else if (IS_ERR(exp)) + rv = nfserrno(PTR_ERR(exp)); + else + rv = fh_compose(fhp, exp, + fsid_key->ek_dentry, NULL); + cache_put(&fsid_key->h, &svc_expkey_cache); return rv; } @@ -1054,7 +1187,7 @@ static int e_show(struct seq_file *m, void *p) cache_get(&exp->h); if (cache_check(&svc_export_cache, &exp->h, NULL)) return 0; - if (cache_put(&exp->h, &svc_export_cache)) BUG(); + cache_put(&exp->h, &svc_export_cache); return svc_export_show(m, &svc_export_cache, cp); } @@ -1129,7 +1262,6 @@ exp_delclient(struct nfsctl_client *ncp) */ if (dom) { err = auth_unix_forget_old(dom); - dom->h.expiry_time = get_seconds(); auth_domain_put(dom); } diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c index 13369650cdf9..4b6aa60dfceb 100644 --- a/fs/nfsd/nfs4idmap.c +++ b/fs/nfsd/nfs4idmap.c @@ -76,21 +76,18 @@ struct ent { char authname[IDMAP_NAMESZ]; }; -#define DefineSimpleCacheLookupMap(STRUCT, FUNC) \ - DefineCacheLookup(struct STRUCT, h, FUNC##_lookup, \ - (struct STRUCT *item, int set), /*no setup */, \ - & FUNC##_cache, FUNC##_hash(item), FUNC##_match(item, tmp), \ - STRUCT##_init(new, item), STRUCT##_update(tmp, item), 0) - /* Common entry handling */ #define ENT_HASHBITS 8 #define ENT_HASHMAX (1 << ENT_HASHBITS) #define ENT_HASHMASK (ENT_HASHMAX - 1) -static inline void -ent_init(struct ent *new, struct ent *itm) +static void +ent_init(struct cache_head *cnew, struct cache_head *citm) { + struct ent *new = container_of(cnew, struct ent, h); + struct ent *itm = container_of(citm, struct ent, h); + new->id = itm->id; new->type = itm->type; @@ -98,19 +95,21 @@ ent_init(struct ent *new, struct ent *itm) strlcpy(new->authname, itm->authname, sizeof(new->name)); } -static inline void -ent_update(struct ent *new, struct ent *itm) +static void +ent_put(struct kref *ref) { - ent_init(new, itm); + struct ent *map = container_of(ref, struct ent, h.ref); + kfree(map); } -static void -ent_put(struct cache_head *ch, struct cache_detail *cd) +static struct cache_head * +ent_alloc(void) { - if (cache_put(ch, cd)) { - struct ent *map = container_of(ch, struct ent, h); - kfree(map); - } + struct ent *e = kmalloc(sizeof(*e), GFP_KERNEL); + if (e) + return &e->h; + else + return NULL; } /* @@ -149,9 +148,12 @@ idtoname_request(struct cache_detail *cd, struct cache_head *ch, char **bpp, (*bpp)[-1] = '\n'; } -static inline int -idtoname_match(struct ent *a, struct ent *b) +static int +idtoname_match(struct cache_head *ca, struct cache_head *cb) { + struct ent *a = container_of(ca, struct ent, h); + struct ent *b = container_of(cb, struct ent, h); + return (a->id == b->id && a->type == b->type && strcmp(a->authname, b->authname) == 0); } @@ -184,7 +186,8 @@ warn_no_idmapd(struct cache_detail *detail) static int idtoname_parse(struct cache_detail *, char *, int); -static struct ent *idtoname_lookup(struct ent *, int); +static struct ent *idtoname_lookup(struct ent *); +static struct ent *idtoname_update(struct ent *, struct ent *); static struct cache_detail idtoname_cache = { .owner = THIS_MODULE, @@ -196,6 +199,10 @@ static struct cache_detail idtoname_cache = { .cache_parse = idtoname_parse, .cache_show = idtoname_show, .warn_no_listener = warn_no_idmapd, + .match = idtoname_match, + .init = ent_init, + .update = ent_init, + .alloc = ent_alloc, }; int @@ -238,6 +245,11 @@ idtoname_parse(struct cache_detail *cd, char *buf, int buflen) if (ent.h.expiry_time == 0) goto out; + error = -ENOMEM; + res = idtoname_lookup(&ent); + if (!res) + goto out; + /* Name */ error = qword_get(&buf, buf1, PAGE_SIZE); if (error == -EINVAL) @@ -252,10 +264,11 @@ idtoname_parse(struct cache_detail *cd, char *buf, int buflen) memcpy(ent.name, buf1, sizeof(ent.name)); } error = -ENOMEM; - if ((res = idtoname_lookup(&ent, 1)) == NULL) + res = idtoname_update(&ent, res); + if (res == NULL) goto out; - ent_put(&res->h, &idtoname_cache); + cache_put(&res->h, &idtoname_cache); error = 0; out: @@ -264,7 +277,31 @@ out: return error; } -static DefineSimpleCacheLookupMap(ent, idtoname); + +static struct ent * +idtoname_lookup(struct ent *item) +{ + struct cache_head *ch = sunrpc_cache_lookup(&idtoname_cache, + &item->h, + idtoname_hash(item)); + if (ch) + return container_of(ch, struct ent, h); + else + return NULL; +} + +static struct ent * +idtoname_update(struct ent *new, struct ent *old) +{ + struct cache_head *ch = sunrpc_cache_update(&idtoname_cache, + &new->h, &old->h, + idtoname_hash(new)); + if (ch) + return container_of(ch, struct ent, h); + else + return NULL; +} + /* * Name -> ID cache @@ -291,9 +328,12 @@ nametoid_request(struct cache_detail *cd, struct cache_head *ch, char **bpp, (*bpp)[-1] = '\n'; } -static inline int -nametoid_match(struct ent *a, struct ent *b) +static int +nametoid_match(struct cache_head *ca, struct cache_head *cb) { + struct ent *a = container_of(ca, struct ent, h); + struct ent *b = container_of(cb, struct ent, h); + return (a->type == b->type && strcmp(a->name, b->name) == 0 && strcmp(a->authname, b->authname) == 0); } @@ -317,7 +357,8 @@ nametoid_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h) return 0; } -static struct ent *nametoid_lookup(struct ent *, int); +static struct ent *nametoid_lookup(struct ent *); +static struct ent *nametoid_update(struct ent *, struct ent *); static int nametoid_parse(struct cache_detail *, char *, int); static struct cache_detail nametoid_cache = { @@ -330,6 +371,10 @@ static struct cache_detail nametoid_cache = { .cache_parse = nametoid_parse, .cache_show = nametoid_show, .warn_no_listener = warn_no_idmapd, + .match = nametoid_match, + .init = ent_init, + .update = ent_init, + .alloc = ent_alloc, }; static int @@ -379,10 +424,14 @@ nametoid_parse(struct cache_detail *cd, char *buf, int buflen) set_bit(CACHE_NEGATIVE, &ent.h.flags); error = -ENOMEM; - if ((res = nametoid_lookup(&ent, 1)) == NULL) + res = nametoid_lookup(&ent); + if (res == NULL) + goto out; + res = nametoid_update(&ent, res); + if (res == NULL) goto out; - ent_put(&res->h, &nametoid_cache); + cache_put(&res->h, &nametoid_cache); error = 0; out: kfree(buf1); @@ -390,7 +439,30 @@ out: return (error); } -static DefineSimpleCacheLookupMap(ent, nametoid); + +static struct ent * +nametoid_lookup(struct ent *item) +{ + struct cache_head *ch = sunrpc_cache_lookup(&nametoid_cache, + &item->h, + nametoid_hash(item)); + if (ch) + return container_of(ch, struct ent, h); + else + return NULL; +} + +static struct ent * +nametoid_update(struct ent *new, struct ent *old) +{ + struct cache_head *ch = sunrpc_cache_update(&nametoid_cache, + &new->h, &old->h, + nametoid_hash(new)); + if (ch) + return container_of(ch, struct ent, h); + else + return NULL; +} /* * Exported API @@ -458,24 +530,24 @@ idmap_defer(struct cache_req *req) } static inline int -do_idmap_lookup(struct ent *(*lookup_fn)(struct ent *, int), struct ent *key, +do_idmap_lookup(struct ent *(*lookup_fn)(struct ent *), struct ent *key, struct cache_detail *detail, struct ent **item, struct idmap_defer_req *mdr) { - *item = lookup_fn(key, 0); + *item = lookup_fn(key); if (!*item) return -ENOMEM; return cache_check(detail, &(*item)->h, &mdr->req); } static inline int -do_idmap_lookup_nowait(struct ent *(*lookup_fn)(struct ent *, int), +do_idmap_lookup_nowait(struct ent *(*lookup_fn)(struct ent *), struct ent *key, struct cache_detail *detail, struct ent **item) { int ret = -ENOMEM; - *item = lookup_fn(key, 0); + *item = lookup_fn(key); if (!*item) goto out_err; ret = -ETIMEDOUT; @@ -488,7 +560,7 @@ do_idmap_lookup_nowait(struct ent *(*lookup_fn)(struct ent *, int), goto out_put; return 0; out_put: - ent_put(&(*item)->h, detail); + cache_put(&(*item)->h, detail); out_err: *item = NULL; return ret; @@ -496,7 +568,7 @@ out_err: static int idmap_lookup(struct svc_rqst *rqstp, - struct ent *(*lookup_fn)(struct ent *, int), struct ent *key, + struct ent *(*lookup_fn)(struct ent *), struct ent *key, struct cache_detail *detail, struct ent **item) { struct idmap_defer_req *mdr; @@ -539,7 +611,7 @@ idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen if (ret) return ret; *id = item->id; - ent_put(&item->h, &nametoid_cache); + cache_put(&item->h, &nametoid_cache); return 0; } @@ -561,7 +633,7 @@ idmap_id_to_name(struct svc_rqst *rqstp, int type, uid_t id, char *name) ret = strlen(item->name); BUG_ON(ret > IDMAP_NAMESZ); memcpy(name, item->name, ret); - ent_put(&item->h, &idtoname_cache); + cache_put(&item->h, &idtoname_cache); return ret; } diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index c8960aff0968..3ef017b3b5bd 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -134,7 +134,7 @@ static ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size return simple_transaction_read(file, buf, size, pos); } -static struct file_operations transaction_ops = { +static const struct file_operations transaction_ops = { .write = nfsctl_transaction_write, .read = nfsctl_transaction_read, .release = simple_transaction_release, @@ -146,7 +146,7 @@ static int exports_open(struct inode *inode, struct file *file) return seq_open(file, &nfs_exports_op); } -static struct file_operations exports_operations = { +static const struct file_operations exports_operations = { .open = exports_open, .read = seq_read, .llseek = seq_lseek, diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 7a3e397b4ed3..3f2ec2e6d06c 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -506,7 +506,7 @@ fh_put(struct svc_fh *fhp) nfsd_nr_put++; } if (exp) { - svc_export_put(&exp->h, &svc_export_cache); + cache_put(&exp->h, &svc_export_cache); fhp->fh_export = NULL; } return; diff --git a/fs/nfsd/stats.c b/fs/nfsd/stats.c index 1cf955bcc526..57265d563804 100644 --- a/fs/nfsd/stats.c +++ b/fs/nfsd/stats.c @@ -80,7 +80,7 @@ static int nfsd_proc_open(struct inode *inode, struct file *file) return single_open(file, nfsd_proc_show, NULL); } -static struct file_operations nfsd_proc_fops = { +static const struct file_operations nfsd_proc_fops = { .owner = THIS_MODULE, .open = nfsd_proc_open, .read = seq_read, diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 5320e5afaddb..31018333dc38 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -706,7 +706,7 @@ nfsd_close(struct file *filp) * after it. */ static inline int nfsd_dosync(struct file *filp, struct dentry *dp, - struct file_operations *fop) + const struct file_operations *fop) { struct inode *inode = dp->d_inode; int (*fsync) (struct file *, struct dentry *, int); diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c index 9d9ed3fe371d..d1e2c6f9f05e 100644 --- a/fs/ntfs/dir.c +++ b/fs/ntfs/dir.c @@ -1553,7 +1553,7 @@ static int ntfs_dir_fsync(struct file *filp, struct dentry *dentry, #endif /* NTFS_RW */ -struct file_operations ntfs_dir_ops = { +const struct file_operations ntfs_dir_ops = { .llseek = generic_file_llseek, /* Seek inside directory. */ .read = generic_read_dir, /* Return -EISDIR. */ .readdir = ntfs_readdir, /* Read directory contents. */ diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c index f5d057e4acc2..c63a83e8da98 100644 --- a/fs/ntfs/file.c +++ b/fs/ntfs/file.c @@ -2294,7 +2294,7 @@ static int ntfs_file_fsync(struct file *filp, struct dentry *dentry, #endif /* NTFS_RW */ -struct file_operations ntfs_file_ops = { +const struct file_operations ntfs_file_ops = { .llseek = generic_file_llseek, /* Seek inside file. */ .read = generic_file_read, /* Read from file. */ .aio_read = generic_file_aio_read, /* Async read from file. */ @@ -2337,6 +2337,6 @@ struct inode_operations ntfs_file_inode_ops = { #endif /* NTFS_RW */ }; -struct file_operations ntfs_empty_file_ops = {}; +const struct file_operations ntfs_empty_file_ops = {}; struct inode_operations ntfs_empty_inode_ops = {}; diff --git a/fs/ntfs/ntfs.h b/fs/ntfs/ntfs.h index 166142960b53..bf7b3d7c0930 100644 --- a/fs/ntfs/ntfs.h +++ b/fs/ntfs/ntfs.h @@ -60,13 +60,13 @@ extern struct kmem_cache *ntfs_index_ctx_cache; extern struct address_space_operations ntfs_aops; extern struct address_space_operations ntfs_mst_aops; -extern struct file_operations ntfs_file_ops; +extern const struct file_operations ntfs_file_ops; extern struct inode_operations ntfs_file_inode_ops; -extern struct file_operations ntfs_dir_ops; +extern const struct file_operations ntfs_dir_ops; extern struct inode_operations ntfs_dir_inode_ops; -extern struct file_operations ntfs_empty_file_ops; +extern const struct file_operations ntfs_empty_file_ops; extern struct inode_operations ntfs_empty_inode_ops; extern struct export_operations ntfs_export_ops; diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index 84f153aca692..64cd52860c87 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -2017,7 +2017,7 @@ out: return ret; } -static struct file_operations ocfs2_dlm_debug_fops = { +static const struct file_operations ocfs2_dlm_debug_fops = { .open = ocfs2_dlm_debug_open, .release = ocfs2_dlm_debug_release, .read = seq_read, diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 4b4cbadd5838..34e903a6a46b 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -1176,7 +1176,7 @@ struct inode_operations ocfs2_special_file_iops = { .getattr = ocfs2_getattr, }; -struct file_operations ocfs2_fops = { +const struct file_operations ocfs2_fops = { .read = do_sync_read, .write = do_sync_write, .sendfile = generic_file_sendfile, @@ -1188,7 +1188,7 @@ struct file_operations ocfs2_fops = { .aio_write = ocfs2_file_aio_write, }; -struct file_operations ocfs2_dops = { +const struct file_operations ocfs2_dops = { .read = generic_read_dir, .readdir = ocfs2_readdir, .fsync = ocfs2_sync_file, diff --git a/fs/ocfs2/file.h b/fs/ocfs2/file.h index a5ea33b24060..740c9e7ca599 100644 --- a/fs/ocfs2/file.h +++ b/fs/ocfs2/file.h @@ -26,8 +26,8 @@ #ifndef OCFS2_FILE_H #define OCFS2_FILE_H -extern struct file_operations ocfs2_fops; -extern struct file_operations ocfs2_dops; +extern const struct file_operations ocfs2_fops; +extern const struct file_operations ocfs2_dops; extern struct inode_operations ocfs2_file_iops; extern struct inode_operations ocfs2_special_file_iops; struct ocfs2_alloc_context; diff --git a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c index aeb0106890e4..0f14276a2e51 100644 --- a/fs/openpromfs/inode.c +++ b/fs/openpromfs/inode.c @@ -581,17 +581,17 @@ int property_release (struct inode *inode, struct file *filp) return 0; } -static struct file_operations openpromfs_prop_ops = { +static const struct file_operations openpromfs_prop_ops = { .read = property_read, .write = property_write, .release = property_release, }; -static struct file_operations openpromfs_nodenum_ops = { +static const struct file_operations openpromfs_nodenum_ops = { .read = nodenum_read, }; -static struct file_operations openprom_operations = { +static const struct file_operations openprom_operations = { .read = generic_read_dir, .readdir = openpromfs_readdir, }; diff --git a/fs/partitions/check.c b/fs/partitions/check.c index f924f459bdb8..af0cb4b9e784 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -297,6 +297,25 @@ struct kobj_type ktype_part = { .sysfs_ops = &part_sysfs_ops, }; +static inline void partition_sysfs_add_subdir(struct hd_struct *p) +{ + struct kobject *k; + + k = kobject_get(&p->kobj); + p->holder_dir = kobject_add_dir(k, "holders"); + kobject_put(k); +} + +static inline void disk_sysfs_add_subdirs(struct gendisk *disk) +{ + struct kobject *k; + + k = kobject_get(&disk->kobj); + disk->holder_dir = kobject_add_dir(k, "holders"); + disk->slave_dir = kobject_add_dir(k, "slaves"); + kobject_put(k); +} + void delete_partition(struct gendisk *disk, int part) { struct hd_struct *p = disk->part[part-1]; @@ -310,6 +329,8 @@ void delete_partition(struct gendisk *disk, int part) p->ios[0] = p->ios[1] = 0; p->sectors[0] = p->sectors[1] = 0; devfs_remove("%s/part%d", disk->devfs_name, part); + if (p->holder_dir) + kobject_unregister(p->holder_dir); kobject_unregister(&p->kobj); } @@ -337,6 +358,7 @@ void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len) p->kobj.parent = &disk->kobj; p->kobj.ktype = &ktype_part; kobject_register(&p->kobj); + partition_sysfs_add_subdir(p); disk->part[part-1] = p; } @@ -383,6 +405,7 @@ void register_disk(struct gendisk *disk) if ((err = kobject_add(&disk->kobj))) return; disk_sysfs_symlinks(disk); + disk_sysfs_add_subdirs(disk); kobject_uevent(&disk->kobj, KOBJ_ADD); /* No minors to use for partitions */ @@ -483,6 +506,10 @@ void del_gendisk(struct gendisk *disk) devfs_remove_disk(disk); + if (disk->holder_dir) + kobject_unregister(disk->holder_dir); + if (disk->slave_dir) + kobject_unregister(disk->slave_dir); if (disk->driverfs_dev) { char *disk_name = make_block_name(disk); sysfs_remove_link(&disk->kobj, "device"); diff --git a/fs/pipe.c b/fs/pipe.c index 4384c9290943..e2f4f1d9ffc2 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -568,7 +568,7 @@ pipe_rdwr_open(struct inode *inode, struct file *filp) * The file_operations structs are not static because they * are also used in linux/fs/fifo.c to do operations on FIFOs. */ -struct file_operations read_fifo_fops = { +const struct file_operations read_fifo_fops = { .llseek = no_llseek, .read = pipe_read, .readv = pipe_readv, @@ -580,7 +580,7 @@ struct file_operations read_fifo_fops = { .fasync = pipe_read_fasync, }; -struct file_operations write_fifo_fops = { +const struct file_operations write_fifo_fops = { .llseek = no_llseek, .read = bad_pipe_r, .write = pipe_write, @@ -592,7 +592,7 @@ struct file_operations write_fifo_fops = { .fasync = pipe_write_fasync, }; -struct file_operations rdwr_fifo_fops = { +const struct file_operations rdwr_fifo_fops = { .llseek = no_llseek, .read = pipe_read, .readv = pipe_readv, diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 47b7a20d45eb..4ba03009cf72 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -560,7 +560,7 @@ static void proc_kill_inodes(struct proc_dir_entry *de) struct file * filp = list_entry(p, struct file, f_u.fu_list); struct dentry * dentry = filp->f_dentry; struct inode * inode; - struct file_operations *fops; + const struct file_operations *fops; if (dentry->d_op != &proc_dentry_operations) continue; diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 95a1cf32b838..0502f17b860d 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -30,7 +30,7 @@ do { \ #endif -extern void create_seq_entry(char *name, mode_t mode, struct file_operations *f); +extern void create_seq_entry(char *name, mode_t mode, const struct file_operations *f); extern int proc_exe_link(struct inode *, struct dentry **, struct vfsmount **); extern int proc_tid_stat(struct task_struct *, char *); extern int proc_tgid_stat(struct task_struct *, char *); diff --git a/fs/proc/kcore.c b/fs/proc/kcore.c index adc2cd95169a..17f6e8fa1397 100644 --- a/fs/proc/kcore.c +++ b/fs/proc/kcore.c @@ -31,7 +31,7 @@ static int open_kcore(struct inode * inode, struct file * filp) static ssize_t read_kcore(struct file *, char __user *, size_t, loff_t *); -struct file_operations proc_kcore_operations = { +const struct file_operations proc_kcore_operations = { .read = read_kcore, .open = open_kcore, }; diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c index 10d37bf25206..ff3b90b56e9d 100644 --- a/fs/proc/kmsg.c +++ b/fs/proc/kmsg.c @@ -47,7 +47,7 @@ static unsigned int kmsg_poll(struct file *file, poll_table *wait) } -struct file_operations proc_kmsg_operations = { +const struct file_operations proc_kmsg_operations = { .read = kmsg_read, .poll = kmsg_poll, .open = kmsg_open, diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index 1e9ea37d457e..ef5a3323f4b5 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c @@ -534,7 +534,7 @@ static int show_stat(struct seq_file *p, void *v) if (wall_to_monotonic.tv_nsec) --jif; - for_each_cpu(i) { + for_each_possible_cpu(i) { int j; user = cputime64_add(user, kstat_cpu(i).cpustat.user); @@ -731,7 +731,7 @@ static struct file_operations proc_sysrq_trigger_operations = { struct proc_dir_entry *proc_root_kcore; -void create_seq_entry(char *name, mode_t mode, struct file_operations *f) +void create_seq_entry(char *name, mode_t mode, const struct file_operations *f) { struct proc_dir_entry *entry; entry = create_proc_entry(name, mode, NULL); diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c index 4063fb32f78c..7efa73d44c9a 100644 --- a/fs/proc/vmcore.c +++ b/fs/proc/vmcore.c @@ -172,7 +172,7 @@ static int open_vmcore(struct inode *inode, struct file *filp) return 0; } -struct file_operations proc_vmcore_operations = { +const struct file_operations proc_vmcore_operations = { .read = read_vmcore, .open = open_vmcore, }; diff --git a/fs/qnx4/dir.c b/fs/qnx4/dir.c index 7a8f5595c26f..9031948fefd0 100644 --- a/fs/qnx4/dir.c +++ b/fs/qnx4/dir.c @@ -81,7 +81,7 @@ out: return 0; } -struct file_operations qnx4_dir_operations = +const struct file_operations qnx4_dir_operations = { .read = generic_read_dir, .readdir = qnx4_readdir, diff --git a/fs/qnx4/file.c b/fs/qnx4/file.c index c33963fded9e..62af4b1348bd 100644 --- a/fs/qnx4/file.c +++ b/fs/qnx4/file.c @@ -19,7 +19,7 @@ * We have mostly NULL's here: the current defaults are ok for * the qnx4 filesystem. */ -struct file_operations qnx4_file_operations = +const struct file_operations qnx4_file_operations = { .llseek = generic_file_llseek, .read = generic_file_read, diff --git a/fs/ramfs/file-mmu.c b/fs/ramfs/file-mmu.c index 6ada2095b9ac..00a933eb820c 100644 --- a/fs/ramfs/file-mmu.c +++ b/fs/ramfs/file-mmu.c @@ -32,7 +32,7 @@ struct address_space_operations ramfs_aops = { .commit_write = simple_commit_write }; -struct file_operations ramfs_file_operations = { +const struct file_operations ramfs_file_operations = { .read = generic_file_read, .write = generic_file_write, .mmap = generic_file_mmap, diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c index b1ca234068f6..f443a84b98a5 100644 --- a/fs/ramfs/file-nommu.c +++ b/fs/ramfs/file-nommu.c @@ -33,7 +33,7 @@ struct address_space_operations ramfs_aops = { .commit_write = simple_commit_write }; -struct file_operations ramfs_file_operations = { +const struct file_operations ramfs_file_operations = { .mmap = ramfs_nommu_mmap, .get_unmapped_area = ramfs_nommu_get_unmapped_area, .read = generic_file_read, diff --git a/fs/ramfs/internal.h b/fs/ramfs/internal.h index 272c8a7120b0..313237631b49 100644 --- a/fs/ramfs/internal.h +++ b/fs/ramfs/internal.h @@ -11,5 +11,5 @@ extern struct address_space_operations ramfs_aops; -extern struct file_operations ramfs_file_operations; +extern const struct file_operations ramfs_file_operations; extern struct inode_operations ramfs_file_inode_operations; diff --git a/fs/read_write.c b/fs/read_write.c index 34b1bf259efd..6256ca81a718 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -19,7 +19,7 @@ #include <asm/uaccess.h> #include <asm/unistd.h> -struct file_operations generic_ro_fops = { +const struct file_operations generic_ro_fops = { .llseek = generic_file_llseek, .read = generic_file_read, .mmap = generic_file_readonly_mmap, diff --git a/fs/reiserfs/dir.c b/fs/reiserfs/dir.c index d71ac6579289..973c819f8033 100644 --- a/fs/reiserfs/dir.c +++ b/fs/reiserfs/dir.c @@ -18,7 +18,7 @@ static int reiserfs_readdir(struct file *, void *, filldir_t); static int reiserfs_dir_fsync(struct file *filp, struct dentry *dentry, int datasync); -struct file_operations reiserfs_dir_operations = { +const struct file_operations reiserfs_dir_operations = { .read = generic_read_dir, .readdir = reiserfs_readdir, .fsync = reiserfs_dir_fsync, diff --git a/fs/reiserfs/file.c b/fs/reiserfs/file.c index d0c1e865963e..010094d14da6 100644 --- a/fs/reiserfs/file.c +++ b/fs/reiserfs/file.c @@ -1566,7 +1566,7 @@ static ssize_t reiserfs_aio_write(struct kiocb *iocb, const char __user * buf, return generic_file_aio_write(iocb, buf, count, pos); } -struct file_operations reiserfs_file_operations = { +const struct file_operations reiserfs_file_operations = { .read = generic_file_read, .write = reiserfs_file_write, .ioctl = reiserfs_ioctl, diff --git a/fs/reiserfs/procfs.c b/fs/reiserfs/procfs.c index ef6caed9336b..731688e1cfe3 100644 --- a/fs/reiserfs/procfs.c +++ b/fs/reiserfs/procfs.c @@ -470,7 +470,7 @@ static int r_open(struct inode *inode, struct file *file) return ret; } -static struct file_operations r_file_operations = { +static const struct file_operations r_file_operations = { .open = r_open, .read = seq_read, .llseek = seq_lseek, diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c index c2fc424d7d5c..9b9eda7b335c 100644 --- a/fs/romfs/inode.c +++ b/fs/romfs/inode.c @@ -463,7 +463,7 @@ static struct address_space_operations romfs_aops = { .readpage = romfs_readpage }; -static struct file_operations romfs_dir_operations = { +static const struct file_operations romfs_dir_operations = { .read = generic_read_dir, .readdir = romfs_readdir, }; diff --git a/fs/select.c b/fs/select.c index 1815a57d2255..b3a3a1326af6 100644 --- a/fs/select.c +++ b/fs/select.c @@ -29,12 +29,6 @@ #define ROUND_UP(x,y) (((x)+(y)-1)/(y)) #define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM) -struct poll_table_entry { - struct file * filp; - wait_queue_t wait; - wait_queue_head_t * wait_address; -}; - struct poll_table_page { struct poll_table_page * next; struct poll_table_entry * entry; @@ -64,13 +58,23 @@ void poll_initwait(struct poll_wqueues *pwq) init_poll_funcptr(&pwq->pt, __pollwait); pwq->error = 0; pwq->table = NULL; + pwq->inline_index = 0; } EXPORT_SYMBOL(poll_initwait); +static void free_poll_entry(struct poll_table_entry *entry) +{ + remove_wait_queue(entry->wait_address,&entry->wait); + fput(entry->filp); +} + void poll_freewait(struct poll_wqueues *pwq) { struct poll_table_page * p = pwq->table; + int i; + for (i = 0; i < pwq->inline_index; i++) + free_poll_entry(pwq->inline_entries + i); while (p) { struct poll_table_entry * entry; struct poll_table_page *old; @@ -78,8 +82,7 @@ void poll_freewait(struct poll_wqueues *pwq) entry = p->entry; do { entry--; - remove_wait_queue(entry->wait_address,&entry->wait); - fput(entry->filp); + free_poll_entry(entry); } while (entry > p->entries); old = p; p = p->next; @@ -89,12 +92,14 @@ void poll_freewait(struct poll_wqueues *pwq) EXPORT_SYMBOL(poll_freewait); -static void __pollwait(struct file *filp, wait_queue_head_t *wait_address, - poll_table *_p) +static struct poll_table_entry *poll_get_entry(poll_table *_p) { struct poll_wqueues *p = container_of(_p, struct poll_wqueues, pt); struct poll_table_page *table = p->table; + if (p->inline_index < N_INLINE_POLL_ENTRIES) + return p->inline_entries + p->inline_index++; + if (!table || POLL_TABLE_FULL(table)) { struct poll_table_page *new_table; @@ -102,7 +107,7 @@ static void __pollwait(struct file *filp, wait_queue_head_t *wait_address, if (!new_table) { p->error = -ENOMEM; __set_current_state(TASK_RUNNING); - return; + return NULL; } new_table->entry = new_table->entries; new_table->next = table; @@ -110,16 +115,21 @@ static void __pollwait(struct file *filp, wait_queue_head_t *wait_address, table = new_table; } - /* Add a new entry */ - { - struct poll_table_entry * entry = table->entry; - table->entry = entry+1; - get_file(filp); - entry->filp = filp; - entry->wait_address = wait_address; - init_waitqueue_entry(&entry->wait, current); - add_wait_queue(wait_address,&entry->wait); - } + return table->entry++; +} + +/* Add a new entry */ +static void __pollwait(struct file *filp, wait_queue_head_t *wait_address, + poll_table *p) +{ + struct poll_table_entry *entry = poll_get_entry(p); + if (!entry) + return; + get_file(filp); + entry->filp = filp; + entry->wait_address = wait_address; + init_waitqueue_entry(&entry->wait, current); + add_wait_queue(wait_address,&entry->wait); } #define FDS_IN(fds, n) (fds->in + n) @@ -210,7 +220,7 @@ int do_select(int n, fd_set_bits *fds, s64 *timeout) for (i = 0; i < n; ++rinp, ++routp, ++rexp) { unsigned long in, out, ex, all_bits, bit = 1, mask, j; unsigned long res_in = 0, res_out = 0, res_ex = 0; - struct file_operations *f_op = NULL; + const struct file_operations *f_op = NULL; struct file *file = NULL; in = *inp++; out = *outp++; ex = *exp++; @@ -221,17 +231,18 @@ int do_select(int n, fd_set_bits *fds, s64 *timeout) } for (j = 0; j < __NFDBITS; ++j, ++i, bit <<= 1) { + int fput_needed; if (i >= n) break; if (!(bit & all_bits)) continue; - file = fget(i); + file = fget_light(i, &fput_needed); if (file) { f_op = file->f_op; mask = DEFAULT_POLLMASK; if (f_op && f_op->poll) mask = (*f_op->poll)(file, retval ? NULL : wait); - fput(file); + fput_light(file, fput_needed); if ((mask & POLLIN_SET) && (in & bit)) { res_in |= bit; retval++; @@ -284,16 +295,6 @@ int do_select(int n, fd_set_bits *fds, s64 *timeout) return retval; } -static void *select_bits_alloc(int size) -{ - return kmalloc(6 * size, GFP_KERNEL); -} - -static void select_bits_free(void *bits, int size) -{ - kfree(bits); -} - /* * We can actually return ERESTARTSYS instead of EINTR, but I'd * like to be certain this leads to no problems. So I return @@ -312,6 +313,8 @@ static int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp, char *bits; int ret, size, max_fdset; struct fdtable *fdt; + /* Allocate small arguments on the stack to save memory and be faster */ + char stack_fds[SELECT_STACK_ALLOC]; ret = -EINVAL; if (n < 0) @@ -332,7 +335,10 @@ static int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp, */ ret = -ENOMEM; size = FDS_BYTES(n); - bits = select_bits_alloc(size); + if (6*size < SELECT_STACK_ALLOC) + bits = stack_fds; + else + bits = kmalloc(6 * size, GFP_KERNEL); if (!bits) goto out_nofds; fds.in = (unsigned long *) bits; @@ -367,7 +373,8 @@ static int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp, ret = -EFAULT; out: - select_bits_free(bits, size); + if (bits != stack_fds) + kfree(bits); out_nofds: return ret; } @@ -551,14 +558,15 @@ static void do_pollfd(unsigned int num, struct pollfd * fdpage, fdp = fdpage+i; fd = fdp->fd; if (fd >= 0) { - struct file * file = fget(fd); + int fput_needed; + struct file * file = fget_light(fd, &fput_needed); mask = POLLNVAL; if (file != NULL) { mask = DEFAULT_POLLMASK; if (file->f_op && file->f_op->poll) mask = file->f_op->poll(file, *pwait); mask &= fdp->events | POLLERR | POLLHUP; - fput(file); + fput_light(file, fput_needed); } if (mask) { *pwait = NULL; @@ -619,6 +627,9 @@ static int do_poll(unsigned int nfds, struct poll_list *list, return count; } +#define N_STACK_PPS ((sizeof(stack_pps) - sizeof(struct poll_list)) / \ + sizeof(struct pollfd)) + int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout) { struct poll_wqueues table; @@ -628,6 +639,9 @@ int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout) struct poll_list *walk; struct fdtable *fdt; int max_fdset; + /* Allocate small arguments on the stack to save memory and be faster */ + char stack_pps[POLL_STACK_ALLOC]; + struct poll_list *stack_pp = NULL; /* Do a sanity check on nfds ... */ rcu_read_lock(); @@ -645,14 +659,23 @@ int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout) err = -ENOMEM; while(i!=0) { struct poll_list *pp; - pp = kmalloc(sizeof(struct poll_list)+ - sizeof(struct pollfd)* - (i>POLLFD_PER_PAGE?POLLFD_PER_PAGE:i), - GFP_KERNEL); - if(pp==NULL) - goto out_fds; + int num, size; + if (stack_pp == NULL) + num = N_STACK_PPS; + else + num = POLLFD_PER_PAGE; + if (num > i) + num = i; + size = sizeof(struct poll_list) + sizeof(struct pollfd)*num; + if (!stack_pp) + stack_pp = pp = (struct poll_list *)stack_pps; + else { + pp = kmalloc(size, GFP_KERNEL); + if (!pp) + goto out_fds; + } pp->next=NULL; - pp->len = (i>POLLFD_PER_PAGE?POLLFD_PER_PAGE:i); + pp->len = num; if (head == NULL) head = pp; else @@ -660,7 +683,7 @@ int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout) walk = pp; if (copy_from_user(pp->entries, ufds + nfds-i, - sizeof(struct pollfd)*pp->len)) { + sizeof(struct pollfd)*num)) { err = -EFAULT; goto out_fds; } @@ -689,7 +712,8 @@ out_fds: walk = head; while(walk!=NULL) { struct poll_list *pp = walk->next; - kfree(walk); + if (walk != stack_pp) + kfree(walk); walk = pp; } poll_freewait(&table); diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c index 0424d06b147e..34c7a11d91f0 100644 --- a/fs/smbfs/dir.c +++ b/fs/smbfs/dir.c @@ -34,7 +34,7 @@ static int smb_rename(struct inode *, struct dentry *, static int smb_make_node(struct inode *,struct dentry *,int,dev_t); static int smb_link(struct dentry *, struct inode *, struct dentry *); -struct file_operations smb_dir_operations = +const struct file_operations smb_dir_operations = { .read = generic_read_dir, .readdir = smb_readdir, diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index 7042e62726a4..c56bd99a9701 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -401,7 +401,7 @@ smb_file_permission(struct inode *inode, int mask, struct nameidata *nd) return error; } -struct file_operations smb_file_operations = +const struct file_operations smb_file_operations = { .llseek = remote_llseek, .read = smb_file_read, diff --git a/fs/smbfs/proto.h b/fs/smbfs/proto.h index e866ec8660d0..47664597e6b1 100644 --- a/fs/smbfs/proto.h +++ b/fs/smbfs/proto.h @@ -35,7 +35,7 @@ extern int smb_proc_symlink(struct smb_sb_info *server, struct dentry *d, const extern int smb_proc_link(struct smb_sb_info *server, struct dentry *dentry, struct dentry *new_dentry); extern void smb_install_null_ops(struct smb_ops *ops); /* dir.c */ -extern struct file_operations smb_dir_operations; +extern const struct file_operations smb_dir_operations; extern struct inode_operations smb_dir_inode_operations; extern struct inode_operations smb_dir_inode_operations_unix; extern void smb_new_dentry(struct dentry *dentry); @@ -64,7 +64,7 @@ extern int smb_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat extern int smb_notify_change(struct dentry *dentry, struct iattr *attr); /* file.c */ extern struct address_space_operations smb_file_aops; -extern struct file_operations smb_file_operations; +extern const struct file_operations smb_file_operations; extern struct inode_operations smb_file_inode_operations; /* ioctl.c */ extern int smb_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c index 78899eeab974..c16a93c353c0 100644 --- a/fs/sysfs/bin.c +++ b/fs/sysfs/bin.c @@ -163,7 +163,7 @@ static int release(struct inode * inode, struct file * file) return 0; } -struct file_operations bin_fops = { +const struct file_operations bin_fops = { .read = read, .write = write, .mmap = mmap, diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 9ee956864445..f26880a4785e 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -503,7 +503,7 @@ static loff_t sysfs_dir_lseek(struct file * file, loff_t offset, int origin) return offset; } -struct file_operations sysfs_dir_operations = { +const struct file_operations sysfs_dir_operations = { .open = sysfs_dir_open, .release = sysfs_dir_close, .llseek = sysfs_dir_lseek, diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 5e83e7246788..830f76fa098c 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -348,7 +348,7 @@ static int sysfs_release(struct inode * inode, struct file * filp) return 0; } -struct file_operations sysfs_file_operations = { +const struct file_operations sysfs_file_operations = { .read = sysfs_read_file, .write = sysfs_write_file, .llseek = generic_file_llseek, diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index cf11d5b789d9..32958a7c50e9 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -21,9 +21,9 @@ extern int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); extern struct rw_semaphore sysfs_rename_sem; extern struct super_block * sysfs_sb; -extern struct file_operations sysfs_dir_operations; -extern struct file_operations sysfs_file_operations; -extern struct file_operations bin_fops; +extern const struct file_operations sysfs_dir_operations; +extern const struct file_operations sysfs_file_operations; +extern const struct file_operations bin_fops; extern struct inode_operations sysfs_dir_inode_operations; extern struct inode_operations sysfs_symlink_inode_operations; diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c index cce8b05cba5a..8c66e9270dd6 100644 --- a/fs/sysv/dir.c +++ b/fs/sysv/dir.c @@ -20,7 +20,7 @@ static int sysv_readdir(struct file *, void *, filldir_t); -struct file_operations sysv_dir_operations = { +const struct file_operations sysv_dir_operations = { .read = generic_read_dir, .readdir = sysv_readdir, .fsync = sysv_sync_file, diff --git a/fs/sysv/file.c b/fs/sysv/file.c index da69abc06240..a59e303135fa 100644 --- a/fs/sysv/file.c +++ b/fs/sysv/file.c @@ -19,7 +19,7 @@ * We have mostly NULLs here: the current defaults are OK for * the coh filesystem. */ -struct file_operations sysv_file_operations = { +const struct file_operations sysv_file_operations = { .llseek = generic_file_llseek, .read = generic_file_read, .write = generic_file_write, diff --git a/fs/sysv/sysv.h b/fs/sysv/sysv.h index b7f9b4a42aab..393a480e4deb 100644 --- a/fs/sysv/sysv.h +++ b/fs/sysv/sysv.h @@ -159,8 +159,8 @@ extern ino_t sysv_inode_by_name(struct dentry *); extern struct inode_operations sysv_file_inode_operations; extern struct inode_operations sysv_dir_inode_operations; extern struct inode_operations sysv_fast_symlink_inode_operations; -extern struct file_operations sysv_file_operations; -extern struct file_operations sysv_dir_operations; +extern const struct file_operations sysv_file_operations; +extern const struct file_operations sysv_dir_operations; extern struct address_space_operations sysv_aops; extern struct super_operations sysv_sops; extern struct dentry_operations sysv_dentry_operations; diff --git a/fs/udf/dir.c b/fs/udf/dir.c index f5222527fe39..8c28efa3b8ff 100644 --- a/fs/udf/dir.c +++ b/fs/udf/dir.c @@ -42,7 +42,7 @@ static int do_udf_readdir(struct inode *, struct file *, filldir_t, void *); /* readdir and lookup functions */ -struct file_operations udf_dir_operations = { +const struct file_operations udf_dir_operations = { .read = generic_read_dir, .readdir = udf_readdir, .ioctl = udf_ioctl, diff --git a/fs/udf/file.c b/fs/udf/file.c index a6f2acc1f15c..e34b00e303f1 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -248,7 +248,7 @@ static int udf_release_file(struct inode * inode, struct file * filp) return 0; } -struct file_operations udf_file_operations = { +const struct file_operations udf_file_operations = { .read = generic_file_read, .ioctl = udf_ioctl, .open = generic_file_open, diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h index 1d5800e0cbe7..023e19ba5a2e 100644 --- a/fs/udf/udfdecl.h +++ b/fs/udf/udfdecl.h @@ -44,9 +44,9 @@ struct buffer_head; struct super_block; extern struct inode_operations udf_dir_inode_operations; -extern struct file_operations udf_dir_operations; +extern const struct file_operations udf_dir_operations; extern struct inode_operations udf_file_inode_operations; -extern struct file_operations udf_file_operations; +extern const struct file_operations udf_file_operations; extern struct address_space_operations udf_aops; extern struct address_space_operations udf_adinicb_aops; extern struct address_space_operations udf_symlink_aops; diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c index 7c10c68902ae..1a561202d3f4 100644 --- a/fs/ufs/dir.c +++ b/fs/ufs/dir.c @@ -620,7 +620,7 @@ int ufs_empty_dir (struct inode * inode) return 1; } -struct file_operations ufs_dir_operations = { +const struct file_operations ufs_dir_operations = { .read = generic_read_dir, .readdir = ufs_readdir, .fsync = file_fsync, diff --git a/fs/ufs/file.c b/fs/ufs/file.c index 62ad481810ef..312fd3f86313 100644 --- a/fs/ufs/file.c +++ b/fs/ufs/file.c @@ -31,7 +31,7 @@ * the ufs filesystem. */ -struct file_operations ufs_file_operations = { +const struct file_operations ufs_file_operations = { .llseek = generic_file_llseek, .read = generic_file_read, .write = generic_file_write, diff --git a/fs/xfs/linux-2.6/xfs_file.c b/fs/xfs/linux-2.6/xfs_file.c index 185567a6a561..85997b1205f5 100644 --- a/fs/xfs/linux-2.6/xfs_file.c +++ b/fs/xfs/linux-2.6/xfs_file.c @@ -528,7 +528,7 @@ open_exec_out: } #endif /* HAVE_FOP_OPEN_EXEC */ -struct file_operations xfs_file_operations = { +const struct file_operations xfs_file_operations = { .llseek = generic_file_llseek, .read = do_sync_read, .write = do_sync_write, @@ -550,7 +550,7 @@ struct file_operations xfs_file_operations = { #endif }; -struct file_operations xfs_invis_file_operations = { +const struct file_operations xfs_invis_file_operations = { .llseek = generic_file_llseek, .read = do_sync_read, .write = do_sync_write, @@ -570,7 +570,7 @@ struct file_operations xfs_invis_file_operations = { }; -struct file_operations xfs_dir_file_operations = { +const struct file_operations xfs_dir_file_operations = { .read = generic_read_dir, .readdir = xfs_file_readdir, .unlocked_ioctl = xfs_file_ioctl, diff --git a/fs/xfs/linux-2.6/xfs_iops.h b/fs/xfs/linux-2.6/xfs_iops.h index a8417d7af5f9..ad6173da5678 100644 --- a/fs/xfs/linux-2.6/xfs_iops.h +++ b/fs/xfs/linux-2.6/xfs_iops.h @@ -22,9 +22,9 @@ extern struct inode_operations xfs_inode_operations; extern struct inode_operations xfs_dir_inode_operations; extern struct inode_operations xfs_symlink_inode_operations; -extern struct file_operations xfs_file_operations; -extern struct file_operations xfs_dir_file_operations; -extern struct file_operations xfs_invis_file_operations; +extern const struct file_operations xfs_file_operations; +extern const struct file_operations xfs_dir_file_operations; +extern const struct file_operations xfs_invis_file_operations; extern int xfs_ioctl(struct bhv_desc *, struct inode *, struct file *, int, unsigned int, void __user *); |