diff options
59 files changed, 3065 insertions, 1513 deletions
diff --git a/Documentation/filesystems/automount-support.txt b/Documentation/filesystems/automount-support.txt index 58c65a1713e..7cac200e2a8 100644 --- a/Documentation/filesystems/automount-support.txt +++ b/Documentation/filesystems/automount-support.txt @@ -19,7 +19,7 @@ following procedure: (2) Have the follow_link() op do the following steps: - (a) Call do_kern_mount() to call the appropriate filesystem to set up a + (a) Call vfs_kern_mount() to call the appropriate filesystem to set up a superblock and gain a vfsmount structure representing it. (b) Copy the nameidata provided as an argument and substitute the dentry diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index 95f5ad923b0..bfc9b28a724 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -569,7 +569,7 @@ static int create_special_files (void) ignore_mount = 1; /* create the devices special file */ - retval = simple_pin_fs("usbfs", &usbfs_mount, &usbfs_mount_count); + retval = simple_pin_fs(&usb_fs_type, &usbfs_mount, &usbfs_mount_count); if (retval) { err ("Unable to get usbfs mount"); goto exit; diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index 872943004e5..8b15bb22cac 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -256,11 +256,12 @@ static int v9fs_show_options(struct seq_file *m, struct vfsmount *mnt) } static void -v9fs_umount_begin(struct super_block *sb) +v9fs_umount_begin(struct vfsmount *vfsmnt, int flags) { - struct v9fs_session_info *v9ses = sb->s_fs_info; + struct v9fs_session_info *v9ses = vfsmnt->mnt_sb->s_fs_info; - v9fs_session_cancel(v9ses); + if (flags & MNT_FORCE) + v9fs_session_cancel(v9ses); } static struct super_operations v9fs_super_ops = { diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c index b5cf9e1205a..99785a79d04 100644 --- a/fs/afs/mntpt.c +++ b/fs/afs/mntpt.c @@ -203,7 +203,7 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt) /* try and do the mount */ kdebug("--- attempting mount %s -o %s ---", devname, options); - mnt = do_kern_mount("afs", 0, devname, options); + mnt = vfs_kern_mount(&afs_fs_type, 0, devname, options); kdebug("--- mount result %p ---", mnt); free_page((unsigned long) devname); diff --git a/fs/afs/super.c b/fs/afs/super.c index 82468df0ba5..67d1f5c819e 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -48,7 +48,7 @@ static void afs_put_super(struct super_block *sb); static void afs_destroy_inode(struct inode *inode); -static struct file_system_type afs_fs_type = { +struct file_system_type afs_fs_type = { .owner = THIS_MODULE, .name = "afs", .get_sb = afs_get_sb, diff --git a/fs/afs/super.h b/fs/afs/super.h index ac11362f4e9..32de8cc6fae 100644 --- a/fs/afs/super.h +++ b/fs/afs/super.h @@ -38,6 +38,8 @@ static inline struct afs_super_info *AFS_FS_S(struct super_block *sb) return sb->s_fs_info; } +extern struct file_system_type afs_fs_type; + #endif /* __KERNEL__ */ #endif /* _LINUX_AFS_SUPER_H */ diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 07a4996cca3..34ebbc191e4 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -55,6 +55,7 @@ typedef struct { } Node; static DEFINE_RWLOCK(entries_lock); +static struct file_system_type bm_fs_type; static struct vfsmount *bm_mnt; static int entry_count; @@ -637,7 +638,7 @@ static ssize_t bm_register_write(struct file *file, const char __user *buffer, if (!inode) goto out2; - err = simple_pin_fs("binfmt_misc", &bm_mnt, &entry_count); + err = simple_pin_fs(&bm_fs_type, &bm_mnt, &entry_count); if (err) { iput(inode); inode = NULL; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 7520f468715..8b4de6eaabd 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -403,12 +403,14 @@ static struct quotactl_ops cifs_quotactl_ops = { #endif #ifdef CONFIG_CIFS_EXPERIMENTAL -static void cifs_umount_begin(struct super_block * sblock) +static void cifs_umount_begin(struct vfsmount * vfsmnt, int flags) { struct cifs_sb_info *cifs_sb; struct cifsTconInfo * tcon; - cifs_sb = CIFS_SB(sblock); + if (!(flags & MNT_FORCE)) + return; + cifs_sb = CIFS_SB(vfsmnt->mnt_sb); if(cifs_sb == NULL) return; diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c index 94dab7bdd85..3e5fe843e1d 100644 --- a/fs/configfs/mount.c +++ b/fs/configfs/mount.c @@ -118,7 +118,7 @@ static struct file_system_type configfs_fs_type = { int configfs_pin_fs(void) { - return simple_pin_fs("configfs", &configfs_mount, + return simple_pin_fs(&configfs_fs_type, &configfs_mount, &configfs_mnt_count); } diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 440128ebef3..6fa1e04f841 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -199,7 +199,7 @@ struct dentry *debugfs_create_file(const char *name, mode_t mode, pr_debug("debugfs: creating file '%s'\n",name); - error = simple_pin_fs("debugfs", &debugfs_mount, &debugfs_mount_count); + error = simple_pin_fs(&debug_fs_type, &debugfs_mount, &debugfs_mount_count); if (error) goto exit; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 5ceb8bd7a18..dcaaabd3b9c 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -198,9 +198,10 @@ struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid, return inode; } -static void fuse_umount_begin(struct super_block *sb) +static void fuse_umount_begin(struct vfsmount *vfsmnt, int flags) { - fuse_abort_conn(get_fuse_conn_super(sb)); + if (flags & MNT_FORCE) + fuse_abort_conn(get_fuse_conn_super(vfsmnt->mnt_sb)); } static void fuse_put_super(struct super_block *sb) diff --git a/fs/libfs.c b/fs/libfs.c index 1b115638178..fc785d8befb 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -424,13 +424,13 @@ out: static DEFINE_SPINLOCK(pin_fs_lock); -int simple_pin_fs(char *name, struct vfsmount **mount, int *count) +int simple_pin_fs(struct file_system_type *type, struct vfsmount **mount, int *count) { struct vfsmount *mnt = NULL; spin_lock(&pin_fs_lock); if (unlikely(!*mount)) { spin_unlock(&pin_fs_lock); - mnt = do_kern_mount(name, 0, name, NULL); + mnt = vfs_kern_mount(type, 0, type->name, NULL); if (IS_ERR(mnt)) return PTR_ERR(mnt); spin_lock(&pin_fs_lock); diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index bce74446870..52774feab93 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -147,11 +147,10 @@ u32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *lock) * Someone has sent us an SM_NOTIFY. Ensure we bind to the new port number, * that we mark locks for reclaiming, and that we bump the pseudo NSM state. */ -static inline -void nlmclnt_prepare_reclaim(struct nlm_host *host, u32 newstate) +static void nlmclnt_prepare_reclaim(struct nlm_host *host) { + down_write(&host->h_rwsem); host->h_monitored = 0; - host->h_nsmstate = newstate; host->h_state++; host->h_nextrebind = 0; nlm_rebind_host(host); @@ -164,6 +163,13 @@ void nlmclnt_prepare_reclaim(struct nlm_host *host, u32 newstate) dprintk("NLM: reclaiming locks for host %s", host->h_name); } +static void nlmclnt_finish_reclaim(struct nlm_host *host) +{ + host->h_reclaiming = 0; + up_write(&host->h_rwsem); + dprintk("NLM: done reclaiming locks for host %s", host->h_name); +} + /* * Reclaim all locks on server host. We do this by spawning a separate * reclaimer thread. @@ -171,12 +177,10 @@ void nlmclnt_prepare_reclaim(struct nlm_host *host, u32 newstate) void nlmclnt_recovery(struct nlm_host *host, u32 newstate) { - if (host->h_reclaiming++) { - if (host->h_nsmstate == newstate) - return; - nlmclnt_prepare_reclaim(host, newstate); - } else { - nlmclnt_prepare_reclaim(host, newstate); + if (host->h_nsmstate == newstate) + return; + host->h_nsmstate = newstate; + if (!host->h_reclaiming++) { nlm_get_host(host); __module_get(THIS_MODULE); if (kernel_thread(reclaimer, host, CLONE_KERNEL) < 0) @@ -190,6 +194,7 @@ reclaimer(void *ptr) struct nlm_host *host = (struct nlm_host *) ptr; struct nlm_wait *block; struct file_lock *fl, *next; + u32 nsmstate; daemonize("%s-reclaim", host->h_name); allow_signal(SIGKILL); @@ -199,19 +204,25 @@ reclaimer(void *ptr) lock_kernel(); lockd_up(); + nlmclnt_prepare_reclaim(host); /* First, reclaim all locks that have been marked. */ restart: + nsmstate = host->h_nsmstate; list_for_each_entry_safe(fl, next, &host->h_reclaim, fl_u.nfs_fl.list) { list_del_init(&fl->fl_u.nfs_fl.list); if (signalled()) continue; - if (nlmclnt_reclaim(host, fl) == 0) - list_add_tail(&fl->fl_u.nfs_fl.list, &host->h_granted); - goto restart; + if (nlmclnt_reclaim(host, fl) != 0) + continue; + list_add_tail(&fl->fl_u.nfs_fl.list, &host->h_granted); + if (host->h_nsmstate != nsmstate) { + /* Argh! The server rebooted again! */ + list_splice_init(&host->h_granted, &host->h_reclaim); + goto restart; + } } - - host->h_reclaiming = 0; + nlmclnt_finish_reclaim(host); /* Now, wake up all processes that sleep on a blocked lock */ list_for_each_entry(block, &nlm_blocked, b_list) { diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index f96e38155b5..4db62098d3f 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -508,7 +508,10 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) } block = nlmclnt_prepare_block(host, fl); +again: for(;;) { + /* Reboot protection */ + fl->fl_u.nfs_fl.state = host->h_state; status = nlmclnt_call(req, NLMPROC_LOCK); if (status < 0) goto out_unblock; @@ -531,10 +534,16 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) } if (resp->status == NLM_LCK_GRANTED) { - fl->fl_u.nfs_fl.state = host->h_state; + down_read(&host->h_rwsem); + /* Check whether or not the server has rebooted */ + if (fl->fl_u.nfs_fl.state != host->h_state) { + up_read(&host->h_rwsem); + goto again; + } fl->fl_flags |= FL_SLEEP; /* Ensure the resulting lock will get added to granted list */ do_vfs_lock(fl); + up_read(&host->h_rwsem); } status = nlm_stat_to_errno(resp->status); out_unblock: @@ -596,6 +605,7 @@ nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl) static int nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl) { + struct nlm_host *host = req->a_host; struct nlm_res *resp = &req->a_res; int status; @@ -604,7 +614,9 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl) * request, or to deny it with NLM_LCK_DENIED_GRACE_PERIOD. In either * case, we want to unlock. */ + down_read(&host->h_rwsem); do_vfs_lock(fl); + up_read(&host->h_rwsem); if (req->a_flags & RPC_TASK_ASYNC) return nlm_async_call(req, NLMPROC_UNLOCK, &nlmclnt_unlock_ops); diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 729ac427d35..38b0e8a1aec 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -112,11 +112,12 @@ nlm_lookup_host(int server, struct sockaddr_in *sin, host->h_version = version; host->h_proto = proto; host->h_rpcclnt = NULL; - init_MUTEX(&host->h_sema); + mutex_init(&host->h_mutex); host->h_nextrebind = jiffies + NLM_HOST_REBIND; host->h_expires = jiffies + NLM_HOST_EXPIRE; atomic_set(&host->h_count, 1); init_waitqueue_head(&host->h_gracewait); + init_rwsem(&host->h_rwsem); host->h_state = 0; /* pseudo NSM state */ host->h_nsmstate = 0; /* real NSM state */ host->h_server = server; @@ -172,7 +173,7 @@ nlm_bind_host(struct nlm_host *host) (unsigned)ntohl(host->h_addr.sin_addr.s_addr)); /* Lock host handle */ - down(&host->h_sema); + mutex_lock(&host->h_mutex); /* If we've already created an RPC client, check whether * RPC rebind is required @@ -204,12 +205,12 @@ nlm_bind_host(struct nlm_host *host) host->h_rpcclnt = clnt; } - up(&host->h_sema); + mutex_unlock(&host->h_mutex); return clnt; forgetit: printk("lockd: couldn't create RPC handle for %s\n", host->h_name); - up(&host->h_sema); + mutex_unlock(&host->h_mutex); return NULL; } diff --git a/fs/namespace.c b/fs/namespace.c index c13072a5f1e..866430bb024 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -585,8 +585,8 @@ static int do_umount(struct vfsmount *mnt, int flags) */ lock_kernel(); - if ((flags & MNT_FORCE) && sb->s_op->umount_begin) - sb->s_op->umount_begin(sb); + if (sb->s_op->umount_begin) + sb->s_op->umount_begin(mnt, flags); unlock_kernel(); /* @@ -1172,13 +1172,46 @@ static void expire_mount(struct vfsmount *mnt, struct list_head *mounts, } /* + * go through the vfsmounts we've just consigned to the graveyard to + * - check that they're still dead + * - delete the vfsmount from the appropriate namespace under lock + * - dispose of the corpse + */ +static void expire_mount_list(struct list_head *graveyard, struct list_head *mounts) +{ + struct namespace *namespace; + struct vfsmount *mnt; + + while (!list_empty(graveyard)) { + LIST_HEAD(umounts); + mnt = list_entry(graveyard->next, struct vfsmount, mnt_expire); + list_del_init(&mnt->mnt_expire); + + /* don't do anything if the namespace is dead - all the + * vfsmounts from it are going away anyway */ + namespace = mnt->mnt_namespace; + if (!namespace || !namespace->root) + continue; + get_namespace(namespace); + + spin_unlock(&vfsmount_lock); + down_write(&namespace_sem); + expire_mount(mnt, mounts, &umounts); + up_write(&namespace_sem); + release_mounts(&umounts); + mntput(mnt); + put_namespace(namespace); + spin_lock(&vfsmount_lock); + } +} + +/* * process a list of expirable mountpoints with the intent of discarding any * mountpoints that aren't in use and haven't been touched since last we came * here */ void mark_mounts_for_expiry(struct list_head *mounts) { - struct namespace *namespace; struct vfsmount *mnt, *next; LIST_HEAD(graveyard); @@ -1202,38 +1235,79 @@ void mark_mounts_for_expiry(struct list_head *mounts) list_move(&mnt->mnt_expire, &graveyard); } - /* - * go through the vfsmounts we've just consigned to the graveyard to - * - check that they're still dead - * - delete the vfsmount from the appropriate namespace under lock - * - dispose of the corpse - */ - while (!list_empty(&graveyard)) { - LIST_HEAD(umounts); - mnt = list_entry(graveyard.next, struct vfsmount, mnt_expire); - list_del_init(&mnt->mnt_expire); + expire_mount_list(&graveyard, mounts); - /* don't do anything if the namespace is dead - all the - * vfsmounts from it are going away anyway */ - namespace = mnt->mnt_namespace; - if (!namespace || !namespace->root) + spin_unlock(&vfsmount_lock); +} + +EXPORT_SYMBOL_GPL(mark_mounts_for_expiry); + +/* + * Ripoff of 'select_parent()' + * + * search the list of submounts for a given mountpoint, and move any + * shrinkable submounts to the 'graveyard' list. + */ +static int select_submounts(struct vfsmount *parent, struct list_head *graveyard) +{ + struct vfsmount *this_parent = parent; + struct list_head *next; + int found = 0; + +repeat: + next = this_parent->mnt_mounts.next; +resume: + while (next != &this_parent->mnt_mounts) { + struct list_head *tmp = next; + struct vfsmount *mnt = list_entry(tmp, struct vfsmount, mnt_child); + + next = tmp->next; + if (!(mnt->mnt_flags & MNT_SHRINKABLE)) continue; - get_namespace(namespace); + /* + * Descend a level if the d_mounts list is non-empty. + */ + if (!list_empty(&mnt->mnt_mounts)) { + this_parent = mnt; + goto repeat; + } - spin_unlock(&vfsmount_lock); - down_write(&namespace_sem); - expire_mount(mnt, mounts, &umounts); - up_write(&namespace_sem); - release_mounts(&umounts); - mntput(mnt); - put_namespace(namespace); - spin_lock(&vfsmount_lock); + if (!propagate_mount_busy(mnt, 1)) { + mntget(mnt); + list_move_tail(&mnt->mnt_expire, graveyard); + found++; + } } + /* + * All done at this level ... ascend and resume the search + */ + if (this_parent != parent) { + next = this_parent->mnt_child.next; + this_parent = this_parent->mnt_parent; + goto resume; + } + return found; +} + +/* + * process a list of expirable mountpoints with the intent of discarding any + * submounts of a specific parent mountpoint + */ +void shrink_submounts(struct vfsmount *mountpoint, struct list_head *mounts) +{ + LIST_HEAD(graveyard); + int found; + + spin_lock(&vfsmount_lock); + + /* extract submounts of 'mountpoint' from the expiration list */ + while ((found = select_submounts(mountpoint, &graveyard)) != 0) + expire_mount_list(&graveyard, mounts); spin_unlock(&vfsmount_lock); } -EXPORT_SYMBOL_GPL(mark_mounts_for_expiry); +EXPORT_SYMBOL_GPL(shrink_submounts); /* * Some copy_from_user() implementations do not return the exact number of diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index ec61fd56a1a..0b572a0c196 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -4,14 +4,16 @@ obj-$(CONFIG_NFS_FS) += nfs.o -nfs-y := dir.o file.o inode.o nfs2xdr.o pagelist.o \ - proc.o read.o symlink.o unlink.o write.o +nfs-y := dir.o file.o inode.o super.o nfs2xdr.o pagelist.o \ + proc.o read.o symlink.o unlink.o write.o \ + namespace.o nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \ delegation.o idmap.o \ - callback.o callback_xdr.o callback_proc.o + callback.o callback_xdr.o callback_proc.o \ + nfs4namespace.o nfs-$(CONFIG_NFS_DIRECTIO) += direct.o nfs-$(CONFIG_SYSCTL) += sysctl.o nfs-objs := $(nfs-y) diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index 90c95adc8c1..d53f8c6a9ec 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -182,8 +182,6 @@ static int nfs_callback_authenticate(struct svc_rqst *rqstp) /* * Define NFS4 callback program */ -extern struct svc_version nfs4_callback_version1; - static struct svc_version *nfs4_callback_version[] = { [1] = &nfs4_callback_version1, }; diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index 05c38cf40b6..c92991328d9 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -202,7 +202,7 @@ static unsigned decode_recall_args(struct svc_rqst *rqstp, struct xdr_stream *xd status = decode_fh(xdr, &args->fh); out: dprintk("%s: exit with status = %d\n", __FUNCTION__, status); - return 0; + return status; } static unsigned encode_string(struct xdr_stream *xdr, unsigned int len, const char *str) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index cae74dd4c7f..3ddda6f7ecc 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -528,7 +528,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) lock_kernel(); - res = nfs_revalidate_inode(NFS_SERVER(inode), inode); + res = nfs_revalidate_mapping(inode, filp->f_mapping); if (res < 0) { unlock_kernel(); return res; @@ -868,6 +868,17 @@ int nfs_is_exclusive_create(struct inode *dir, struct nameidata *nd) return (nd->intent.open.flags & O_EXCL) != 0; } +static inline int nfs_reval_fsid(struct inode *dir, + struct nfs_fh *fh, struct nfs_fattr *fattr) +{ + struct nfs_server *server = NFS_SERVER(dir); + + if (!nfs_fsid_equal(&server->fsid, &fattr->fsid)) + /* Revalidate fsid on root dir */ + return __nfs_revalidate_inode(server, dir->i_sb->s_root->d_inode); + return 0; +} + static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) { struct dentry *res; @@ -900,6 +911,11 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru res = ERR_PTR(error); goto out_unlock; } + error = nfs_reval_fsid(dir, &fhandle, &fattr); + if (error < |