From: Thierry Reding Date: Thu, 24 Oct 2013 13:02:30 +0000 (+0200) Subject: Merge remote-tracking branch 'userns/for-next' X-Git-Tag: next-20131024~15 X-Git-Url: https://git.karo-electronics.de/?p=karo-tx-linux.git;a=commitdiff_plain;h=25691330eb62649401cfb18ef518983511f6412f;hp=-c Merge remote-tracking branch 'userns/for-next' Conflicts: fs/fuse/dir.c --- 25691330eb62649401cfb18ef518983511f6412f diff --combined fs/afs/dir.c index 529300327f45,7fb69d45f1b9..3756d4fe129f --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@@ -600,6 -600,9 +600,6 @@@ static int afs_d_revalidate(struct dent /* lock down the parent dentry so we can peer at it */ parent = dget_parent(dentry); - if (!parent->d_inode) - goto out_bad; - dir = AFS_FS_I(parent->d_inode); /* validate the parent directory */ @@@ -683,8 -686,7 +683,7 @@@ not_found out_bad: /* don't unhash if we have submounts */ - if (check_submounts_and_drop(dentry) != 0) - goto out_skip; + shrink_submounts_and_drop(dentry); _debug("dropping dentry %s/%s", parent->d_name.name, dentry->d_name.name); diff --combined fs/dcache.c index d70df2e0e0da,1e9bf96b0132..093a68d5fec5 --- a/fs/dcache.c +++ b/fs/dcache.c @@@ -1331,6 -1331,14 +1331,6 @@@ rename_retry * list is non-empty and continue searching. */ -/** - * have_submounts - check for mounts over a dentry - * @parent: dentry to check. - * - * Return true if the parent or its subdirectories contain - * a mount point - */ - static enum d_walk_ret check_mount(void *data, struct dentry *dentry) { int *ret = data; @@@ -1341,13 -1349,6 +1341,13 @@@ return D_WALK_CONTINUE; } +/** + * have_submounts - check for mounts over a dentry + * @parent: dentry to check. + * + * Return true if the parent or its subdirectories contain + * a mount point + */ int have_submounts(struct dentry *parent) { int ret = 0; @@@ -1372,7 -1373,7 +1372,7 @@@ int d_set_mounted(struct dentry *dentry int ret = -ENOENT; write_seqlock(&rename_lock); for (p = dentry->d_parent; !IS_ROOT(p); p = p->d_parent) { - /* Need exclusion wrt. check_submounts_and_drop() */ + /* Need exclusion wrt. shrink_submounts_and_drop() */ spin_lock(&p->d_lock); if (unlikely(d_unhashed(p))) { spin_unlock(&p->d_lock); @@@ -1477,70 -1478,56 +1477,56 @@@ void shrink_dcache_parent(struct dentr } EXPORT_SYMBOL(shrink_dcache_parent); - static enum d_walk_ret check_and_collect(void *_data, struct dentry *dentry) + struct detach_data { + struct dentry *found; + }; + static enum d_walk_ret do_detach_submounts(void *ptr, struct dentry *dentry) { - struct select_data *data = _data; + struct detach_data *data = ptr; - if (d_mountpoint(dentry)) { - data->found = -EBUSY; - return D_WALK_QUIT; - } + if (d_mountpoint(dentry)) + data->found = dentry; - return select_collect(_data, dentry); - } - - static void check_and_drop(void *_data) - { - struct select_data *data = _data; - - if (d_mountpoint(data->start)) - data->found = -EBUSY; - if (!data->found) - __d_drop(data->start); + return data->found ? D_WALK_QUIT : D_WALK_CONTINUE; } /** - * check_submounts_and_drop - prune dcache, check for submounts and drop + * detach_submounts - check for submounts and detach them. * - * All done as a single atomic operation relative to has_unlinked_ancestor(). - * Returns 0 if successfully unhashed @parent. If there were submounts then - * return -EBUSY. + * @dentry: dentry to find mount points under. * - * @dentry: dentry to prune and drop + * If dentry or any of it's children is a mount point detach those mounts. */ - int check_submounts_and_drop(struct dentry *dentry) + void detach_submounts(struct dentry *dentry) { - int ret = 0; - - /* Negative dentries can be dropped without further checks */ - if (!dentry->d_inode) { - d_drop(dentry); - goto out; - } - + struct detach_data data; for (;;) { - struct select_data data; - - INIT_LIST_HEAD(&data.dispose); - data.start = dentry; - data.found = 0; + data.found = NULL; + d_walk(dentry, &data, do_detach_submounts, NULL); - d_walk(dentry, &data, check_and_collect, check_and_drop); - ret = data.found; - - if (!list_empty(&data.dispose)) - shrink_dentry_list(&data.dispose); - - if (ret <= 0) + if (!data.found) break; + detach_mounts(data.found); cond_resched(); } + detach_mounts(dentry); + } - out: - return ret; + /** + * shrink_submounts_and_drop - detach submounts, prune dcache, and drop + * + * All done as a single atomic operation reletaive to d_set_mounted(). + * + * @dentry: dentry to detach, prune and drop + */ + void shrink_submounts_and_drop(struct dentry *dentry) + { + d_drop(dentry); + detach_submounts(dentry); + shrink_dcache_parent(dentry); } - EXPORT_SYMBOL(check_submounts_and_drop); + EXPORT_SYMBOL(shrink_submounts_and_drop); /** * __d_alloc - allocate a dcache entry @@@ -1800,32 -1787,6 +1786,32 @@@ struct dentry *d_instantiate_unique(str EXPORT_SYMBOL(d_instantiate_unique); +/** + * d_instantiate_no_diralias - instantiate a non-aliased dentry + * @entry: dentry to complete + * @inode: inode to attach to this dentry + * + * Fill in inode information in the entry. If a directory alias is found, then + * return an error. Together with d_materialise_unique() this guarantees that a + * directory inode may never have more than one alias. + */ +int d_instantiate_no_diralias(struct dentry *entry, struct inode *inode) +{ + BUG_ON(!hlist_unhashed(&entry->d_alias)); + + spin_lock(&inode->i_lock); + if (S_ISDIR(inode->i_mode) && !hlist_empty(&inode->i_dentry)) { + spin_unlock(&inode->i_lock); + return -EBUSY; + } + __d_instantiate(entry, inode); + spin_unlock(&inode->i_lock); + security_d_instantiate(entry, inode); + + return 0; +} +EXPORT_SYMBOL(d_instantiate_no_diralias); + struct dentry *d_make_root(struct inode *root_inode) { struct dentry *res = NULL; diff --combined fs/fuse/dir.c index 0747f6eed598,b1cd7b79a325..3959aa981f17 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@@ -182,7 -182,6 +182,7 @@@ static int fuse_dentry_revalidate(struc struct inode *inode; struct dentry *parent; struct fuse_conn *fc; + struct fuse_inode *fi; int ret; inode = ACCESS_ONCE(entry->d_inode); @@@ -229,7 -228,7 +229,7 @@@ if (!err && !outarg.nodeid) err = -ENOENT; if (!err) { - struct fuse_inode *fi = get_fuse_inode(inode); + fi = get_fuse_inode(inode); if (outarg.nodeid != get_node_id(inode)) { fuse_queue_forget(fc, forget, outarg.nodeid, 1); goto invalid; @@@ -247,11 -246,8 +247,11 @@@ attr_version); fuse_change_entry_timeout(entry, &outarg); } else if (inode) { - fc = get_fuse_conn(inode); - if (fc->readdirplus_auto) { + fi = get_fuse_inode(inode); + if (flags & LOOKUP_RCU) { + if (test_bit(FUSE_I_INIT_RDPLUS, &fi->state)) + return -ECHILD; + } else if (test_and_clear_bit(FUSE_I_INIT_RDPLUS, &fi->state)) { parent = dget_parent(entry); fuse_advise_use_readdirplus(parent->d_inode); dput(parent); @@@ -263,9 -259,7 +263,10 @@@ out invalid: ret = 0; - shrink_submounts_and_drop(entry); + - if (!(flags & LOOKUP_RCU) && check_submounts_and_drop(entry) != 0) - ret = 1; ++ if (!(flags & LOOKUP_RCU)) ++ shrink_submounts_and_drop(entry); ++ goto out; } @@@ -342,6 -336,24 +343,6 @@@ int fuse_lookup_name(struct super_bloc return err; } -static struct dentry *fuse_materialise_dentry(struct dentry *dentry, - struct inode *inode) -{ - struct dentry *newent; - - if (inode && S_ISDIR(inode->i_mode)) { - struct fuse_conn *fc = get_fuse_conn(inode); - - mutex_lock(&fc->inst_mutex); - newent = d_materialise_unique(dentry, inode); - mutex_unlock(&fc->inst_mutex); - } else { - newent = d_materialise_unique(dentry, inode); - } - - return newent; -} - static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, unsigned int flags) { @@@ -364,7 -376,7 +365,7 @@@ if (inode && get_node_id(inode) == FUSE_ROOT_ID) goto out_iput; - newent = fuse_materialise_dentry(entry, inode); + newent = d_materialise_unique(entry, inode); err = PTR_ERR(newent); if (IS_ERR(newent)) goto out_err; @@@ -583,11 -595,21 +584,11 @@@ static int create_new_entry(struct fuse } kfree(forget); - if (S_ISDIR(inode->i_mode)) { - struct dentry *alias; - mutex_lock(&fc->inst_mutex); - alias = d_find_alias(inode); - if (alias) { - /* New directory must have moved since mkdir */ - mutex_unlock(&fc->inst_mutex); - dput(alias); - iput(inode); - return -EBUSY; - } - d_instantiate(entry, inode); - mutex_unlock(&fc->inst_mutex); - } else - d_instantiate(entry, inode); + err = d_instantiate_no_diralias(entry, inode); + if (err) { + iput(inode); + return err; + } fuse_change_entry_timeout(entry, &outarg); fuse_invalidate_attr(dir); @@@ -1040,8 -1062,6 +1041,8 @@@ static int fuse_access(struct inode *in struct fuse_access_in inarg; int err; + BUG_ON(mask & MAY_NOT_BLOCK); + if (fc->no_access) return 0; @@@ -1129,6 -1149,9 +1130,6 @@@ static int fuse_permission(struct inod noticed immediately, only after the attribute timeout has expired */ } else if (mask & (MAY_ACCESS | MAY_CHDIR)) { - if (mask & MAY_NOT_BLOCK) - return -ECHILD; - err = fuse_access(inode, mask); } else if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) { if (!(inode->i_mode & S_IXUGO)) { @@@ -1256,7 -1279,7 +1257,7 @@@ static int fuse_direntplus_link(struct if (!inode) goto out; - alias = fuse_materialise_dentry(dentry, inode); + alias = d_materialise_unique(dentry, inode); err = PTR_ERR(alias); if (IS_ERR(alias)) goto out; @@@ -1267,8 -1290,6 +1268,8 @@@ } found: + if (fc->readdirplus_auto) + set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state); fuse_change_entry_timeout(dentry, o); err = 0; diff --combined fs/namei.c index caa28051e197,a12c1d31d4c8..f892ef9b3ead --- a/fs/namei.c +++ b/fs/namei.c @@@ -2294,11 -2294,10 +2294,11 @@@ out * path_mountpoint - look up a path to be umounted * @dfd: directory file descriptor to start walk from * @name: full pathname to walk + * @path: pointer to container for result * @flags: lookup flags * * Look up the given name, but don't attempt to revalidate the last component. - * Returns 0 and "path" will be valid on success; Retuns error otherwise. + * Returns 0 and "path" will be valid on success; Returns error otherwise. */ static int path_mountpoint(int dfd, const char *name, struct path *path, unsigned int flags) @@@ -3548,6 -3547,20 +3548,20 @@@ void dentry_unhash(struct dentry *dentr spin_unlock(&dentry->d_lock); } + static bool covered(struct vfsmount *mnt, struct dentry *dentry) + { + /* test to see if a dentry is covered with a mount in + * the current mount namespace. + */ + bool is_covered; + + rcu_read_lock(); + is_covered = d_mountpoint(dentry) && __lookup_mnt(mnt, dentry, 1); + rcu_read_unlock(); + + return is_covered; + } + int vfs_rmdir(struct inode *dir, struct dentry *dentry) { int error = may_delete(dir, dentry, 1); @@@ -3561,10 -3574,6 +3575,6 @@@ dget(dentry); mutex_lock(&dentry->d_inode->i_mutex); - error = -EBUSY; - if (d_mountpoint(dentry)) - goto out; - error = security_inode_rmdir(dir, dentry); if (error) goto out; @@@ -3576,6 -3585,7 +3586,7 @@@ dentry->d_inode->i_flags |= S_DEAD; dont_mount(dentry); + detach_mounts(dentry); out: mutex_unlock(&dentry->d_inode->i_mutex); @@@ -3623,6 -3633,9 +3634,9 @@@ retry error = -ENOENT; goto exit3; } + error = -EBUSY; + if (covered(nd.path.mnt, dentry)) + goto exit3; error = security_path_rmdir(&nd.path, dentry); if (error) goto exit3; @@@ -3658,14 -3671,12 +3672,12 @@@ int vfs_unlink(struct inode *dir, struc return -EPERM; mutex_lock(&dentry->d_inode->i_mutex); - if (d_mountpoint(dentry)) - error = -EBUSY; - else { - error = security_inode_unlink(dir, dentry); + error = security_inode_unlink(dir, dentry); + if (!error) { + error = dir->i_op->unlink(dir, dentry); if (!error) { - error = dir->i_op->unlink(dir, dentry); - if (!error) - dont_mount(dentry); + dont_mount(dentry); + detach_mounts(dentry); } } mutex_unlock(&dentry->d_inode->i_mutex); @@@ -3717,6 -3728,9 +3729,9 @@@ retry inode = dentry->d_inode; if (!inode) goto slashes; + error = -EBUSY; + if (covered(nd.path.mnt, dentry)) + goto exit2; ihold(inode); error = security_path_unlink(&nd.path, dentry); if (error) @@@ -3989,10 -4003,6 +4004,6 @@@ static int vfs_rename_dir(struct inode if (target) mutex_lock(&target->i_mutex); - error = -EBUSY; - if (d_mountpoint(old_dentry) || d_mountpoint(new_dentry)) - goto out; - error = -EMLINK; if (max_links && !target && new_dir != old_dir && new_dir->i_nlink >= max_links) @@@ -4007,6 -4017,7 +4018,7 @@@ if (target) { target->i_flags |= S_DEAD; dont_mount(new_dentry); + detach_mounts(new_dentry); } out: if (target) @@@ -4032,16 -4043,14 +4044,14 @@@ static int vfs_rename_other(struct inod if (target) mutex_lock(&target->i_mutex); - error = -EBUSY; - if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry)) - goto out; - error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); if (error) goto out; - if (target) + if (target) { dont_mount(new_dentry); + detach_mounts(new_dentry); + } if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE)) d_move(old_dentry, new_dentry); out: @@@ -4165,6 -4174,11 +4175,11 @@@ retry error = -ENOTEMPTY; if (new_dentry == trap) goto exit5; + error = -EBUSY; + if (covered(oldnd.path.mnt, old_dentry)) + goto exit5; + if (covered(newnd.path.mnt, new_dentry)) + goto exit5; error = security_path_rename(&oldnd.path, old_dentry, &newnd.path, new_dentry); diff --combined fs/namespace.c index 3ee6e59ead55,78f7c5c9e673..ca81c46ea649 --- a/fs/namespace.c +++ b/fs/namespace.c @@@ -39,7 -39,7 +39,7 @@@ static int mnt_group_start = 1 static struct list_head *mount_hashtable __read_mostly; static struct list_head *mountpoint_hashtable __read_mostly; static struct kmem_cache *mnt_cache __read_mostly; -static struct rw_semaphore namespace_sem; +static DECLARE_RWSEM(namespace_sem); /* /sys/fs */ struct kobject *fs_kobj; @@@ -197,6 -197,7 +197,7 @@@ static struct mount *alloc_vfsmnt(cons INIT_LIST_HEAD(&mnt->mnt_share); INIT_LIST_HEAD(&mnt->mnt_slave_list); INIT_LIST_HEAD(&mnt->mnt_slave); + INIT_LIST_HEAD(&mnt->mnt_mp_list); #ifdef CONFIG_FSNOTIFY INIT_HLIST_HEAD(&mnt->mnt_fsnotify_marks); #endif @@@ -636,6 -637,7 +637,7 @@@ static struct mountpoint *new_mountpoin mp->m_dentry = dentry; mp->m_count = 1; list_add(&mp->m_hash, chain); + INIT_LIST_HEAD(&mp->m_list); return mp; } @@@ -643,6 -645,7 +645,7 @@@ static void put_mountpoint(struct mount { if (!--mp->m_count) { struct dentry *dentry = mp->m_dentry; + BUG_ON(!list_empty(&mp->m_list)); spin_lock(&dentry->d_lock); dentry->d_flags &= ~DCACHE_MOUNTED; spin_unlock(&dentry->d_lock); @@@ -689,6 -692,7 +692,7 @@@ static void detach_mnt(struct mount *mn mnt->mnt_mountpoint = mnt->mnt.mnt_root; list_del_init(&mnt->mnt_child); list_del_init(&mnt->mnt_hash); + list_del_init(&mnt->mnt_mp_list); put_mountpoint(mnt->mnt_mp); mnt->mnt_mp = NULL; } @@@ -705,6 -709,7 +709,7 @@@ void mnt_set_mountpoint(struct mount *m child_mnt->mnt_mountpoint = dget(mp->m_dentry); child_mnt->mnt_parent = mnt; child_mnt->mnt_mp = mp; + list_add_tail(&child_mnt->mnt_mp_list, &mp->m_list); } /* @@@ -1191,6 -1196,7 +1196,7 @@@ void umount_tree(struct mount *mnt, in list_del_init(&p->mnt_child); if (mnt_has_parent(p)) { p->mnt_parent->mnt_ghosts++; + list_del_init(&p->mnt_mp_list); put_mountpoint(p->mnt_mp); p->mnt_mp = NULL; } @@@ -1289,6 -1295,30 +1295,30 @@@ static int do_umount(struct mount *mnt return retval; } + void detach_mounts(struct dentry *dentry) + { + struct mountpoint *mp; + struct mount *mnt; + + namespace_lock(); + if (!d_mountpoint(dentry)) + goto out_unlock; + + mp = new_mountpoint(dentry); + if (IS_ERR(mp)) + goto out_unlock; + + br_write_lock(&vfsmount_lock); + while (!list_empty(&mp->m_list)) { + mnt = list_first_entry(&mp->m_list, struct mount, mnt_mp_list); + umount_tree(mnt, 1); + } + br_write_unlock(&vfsmount_lock); + put_mountpoint(mp); + out_unlock: + namespace_unlock(); + } + /* * Is the caller allowed to modify his namespace? */ @@@ -1849,10 -1879,14 +1879,10 @@@ static int do_remount(struct path *path br_write_lock(&vfsmount_lock); mnt_flags |= mnt->mnt.mnt_flags & MNT_PROPAGATION_MASK; mnt->mnt.mnt_flags = mnt_flags; - br_write_unlock(&vfsmount_lock); - } - up_write(&sb->s_umount); - if (!err) { - br_write_lock(&vfsmount_lock); touch_mnt_namespace(mnt->mnt_ns); br_write_unlock(&vfsmount_lock); } + up_write(&sb->s_umount); return err; } @@@ -2440,7 -2474,9 +2470,7 @@@ static struct mnt_namespace *dup_mnt_ns return ERR_CAST(new); } new_ns->root = new; - br_write_lock(&vfsmount_lock); list_add_tail(&new_ns->list, &new->mnt_list); - br_write_unlock(&vfsmount_lock); /* * Second pass: switch the tsk->fs->* elements and mark new vfsmounts @@@ -2761,6 -2797,8 +2791,6 @@@ void __init mnt_init(void unsigned u; int err; - init_rwsem(&namespace_sem); - mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct mount), 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); @@@ -2794,7 -2832,11 +2824,7 @@@ void put_mnt_ns(struct mnt_namespace *n { if (!atomic_dec_and_test(&ns->count)) return; - namespace_lock(); - br_write_lock(&vfsmount_lock); - umount_tree(ns->root, 0); - br_write_unlock(&vfsmount_lock); - namespace_unlock(); + drop_collected_mounts(&ns->root->mnt); free_mnt_ns(ns); } @@@ -2863,7 -2905,7 +2893,7 @@@ bool fs_fully_visible(struct file_syste if (unlikely(!ns)) return false; - namespace_lock(); + down_read(&namespace_sem); list_for_each_entry(mnt, &ns->list, mnt_list) { struct mount *child; if (mnt->mnt.mnt_sb->s_type != type) @@@ -2884,7 -2926,7 +2914,7 @@@ next: ; } found: - namespace_unlock(); + up_read(&namespace_sem); return visible; } diff --combined fs/nfs/dir.c index 76548d81f926,e8e35acd8850..c20fffac621e --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@@ -98,7 -98,9 +98,7 @@@ nfs_opendir(struct inode *inode, struc struct nfs_open_dir_context *ctx; struct rpc_cred *cred; - dfprintk(FILE, "NFS: open dir(%s/%s)\n", - filp->f_path.dentry->d_parent->d_name.name, - filp->f_path.dentry->d_name.name); + dfprintk(FILE, "NFS: open dir(%pD2)\n", filp); nfs_inc_stats(inode, NFSIOS_VFSOPEN); @@@ -295,10 -297,11 +295,10 @@@ int nfs_readdir_search_for_cookie(struc if (ctx->duped > 0 && ctx->dup_cookie == *desc->dir_cookie) { if (printk_ratelimit()) { - pr_notice("NFS: directory %s/%s contains a readdir loop." + pr_notice("NFS: directory %pD2 contains a readdir loop." "Please contact your server vendor. " "The file: %s has duplicate cookie %llu\n", - desc->file->f_dentry->d_parent->d_name.name, - desc->file->f_dentry->d_name.name, + desc->file, array->array[i].string.name, *desc->dir_cookie); } @@@ -819,8 -822,9 +819,8 @@@ static int nfs_readdir(struct file *fil struct nfs_open_dir_context *dir_ctx = file->private_data; int res = 0; - dfprintk(FILE, "NFS: readdir(%s/%s) starting at cookie %llu\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - (long long)ctx->pos); + dfprintk(FILE, "NFS: readdir(%pD2) starting at cookie %llu\n", + file, (long long)ctx->pos); nfs_inc_stats(inode, NFSIOS_VFSGETDENTS); /* @@@ -876,17 -880,22 +876,17 @@@ out nfs_unblock_sillyrename(dentry); if (res > 0) res = 0; - dfprintk(FILE, "NFS: readdir(%s/%s) returns %d\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - res); + dfprintk(FILE, "NFS: readdir(%pD2) returns %d\n", file, res); return res; } static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence) { - struct dentry *dentry = filp->f_path.dentry; - struct inode *inode = dentry->d_inode; + struct inode *inode = file_inode(filp); struct nfs_open_dir_context *dir_ctx = filp->private_data; - dfprintk(FILE, "NFS: llseek dir(%s/%s, %lld, %d)\n", - dentry->d_parent->d_name.name, - dentry->d_name.name, - offset, whence); + dfprintk(FILE, "NFS: llseek dir(%pD2, %lld, %d)\n", + filp, offset, whence); mutex_lock(&inode->i_mutex); switch (whence) { @@@ -916,12 -925,15 +916,12 @@@ out static int nfs_fsync_dir(struct file *filp, loff_t start, loff_t end, int datasync) { - struct dentry *dentry = filp->f_path.dentry; - struct inode *inode = dentry->d_inode; + struct inode *inode = file_inode(filp); - dfprintk(FILE, "NFS: fsync dir(%s/%s) datasync %d\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - datasync); + dfprintk(FILE, "NFS: fsync dir(%pD2) datasync %d\n", filp, datasync); mutex_lock(&inode->i_mutex); - nfs_inc_stats(dentry->d_inode, NFSIOS_VFSFSYNC); + nfs_inc_stats(inode, NFSIOS_VFSFSYNC); mutex_unlock(&inode->i_mutex); return 0; } @@@ -1061,8 -1073,9 +1061,8 @@@ static int nfs_lookup_revalidate(struc } if (is_bad_inode(inode)) { - dfprintk(LOOKUPCACHE, "%s: %s/%s has dud inode\n", - __func__, dentry->d_parent->d_name.name, - dentry->d_name.name); + dfprintk(LOOKUPCACHE, "%s: %pd2 has dud inode\n", + __func__, dentry); goto out_bad; } @@@ -1112,8 -1125,9 +1112,8 @@@ out_set_verifier nfs_advise_use_readdirplus(dir); out_valid_noent: dput(parent); - dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is valid\n", - __func__, dentry->d_parent->d_name.name, - dentry->d_name.name); + dfprintk(LOOKUPCACHE, "NFS: %s(%pd2) is valid\n", + __func__, dentry); return 1; out_zap_parent: nfs_zap_caches(dir); @@@ -1128,21 -1142,20 +1128,18 @@@ if (dentry->d_flags & DCACHE_DISCONNECTED) goto out_valid; } - /* If we have submounts, don't unhash ! */ - if (check_submounts_and_drop(dentry) != 0) - goto out_valid; - + shrink_submounts_and_drop(dentry); dput(parent); - dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is invalid\n", - __func__, dentry->d_parent->d_name.name, - dentry->d_name.name); + dfprintk(LOOKUPCACHE, "NFS: %s(%pd2) is invalid\n", + __func__, dentry); return 0; out_error: nfs_free_fattr(fattr); nfs_free_fhandle(fhandle); nfs4_label_free(label); dput(parent); - dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) lookup returned error %d\n", - __func__, dentry->d_parent->d_name.name, - dentry->d_name.name, error); + dfprintk(LOOKUPCACHE, "NFS: %s(%pd2) lookup returned error %d\n", + __func__, dentry, error); return error; } @@@ -1166,14 -1179,16 +1163,14 @@@ static int nfs_weak_revalidate(struct d * eventually need to do something more here. */ if (!inode) { - dfprintk(LOOKUPCACHE, "%s: %s/%s has negative inode\n", - __func__, dentry->d_parent->d_name.name, - dentry->d_name.name); + dfprintk(LOOKUPCACHE, "%s: %pd2 has negative inode\n", + __func__, dentry); return 1; } if (is_bad_inode(inode)) { - dfprintk(LOOKUPCACHE, "%s: %s/%s has dud inode\n", - __func__, dentry->d_parent->d_name.name, - dentry->d_name.name); + dfprintk(LOOKUPCACHE, "%s: %pd2 has dud inode\n", + __func__, dentry); return 0; } @@@ -1188,8 -1203,9 +1185,8 @@@ */ static int nfs_dentry_delete(const struct dentry *dentry) { - dfprintk(VFS, "NFS: dentry_delete(%s/%s, %x)\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - dentry->d_flags); + dfprintk(VFS, "NFS: dentry_delete(%pd2, %x)\n", + dentry, dentry->d_flags); /* Unhash any dentry with a stale inode */ if (dentry->d_inode != NULL && NFS_STALE(dentry->d_inode)) @@@ -1267,7 -1283,8 +1264,7 @@@ struct dentry *nfs_lookup(struct inode struct nfs4_label *label = NULL; int error; - dfprintk(VFS, "NFS: lookup(%s/%s)\n", - dentry->d_parent->d_name.name, dentry->d_name.name); + dfprintk(VFS, "NFS: lookup(%pd2)\n", dentry); nfs_inc_stats(dir, NFSIOS_VFSLOOKUP); res = ERR_PTR(-ENAMETOOLONG); @@@ -1361,7 -1378,7 +1358,7 @@@ static struct nfs_open_context *create_ static int do_open(struct inode *inode, struct file *filp) { - nfs_fscache_set_inode_cookie(inode, filp); + nfs_fscache_open_file(inode, filp); return 0; } @@@ -1398,8 -1415,8 +1395,8 @@@ int nfs_atomic_open(struct inode *dir, /* Expect a negative dentry */ BUG_ON(dentry->d_inode); - dfprintk(VFS, "NFS: atomic_open(%s/%ld), %s\n", - dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); + dfprintk(VFS, "NFS: atomic_open(%s/%ld), %pd\n", + dir->i_sb->s_id, dir->i_ino, dentry); err = nfs_check_flags(open_flags); if (err) @@@ -1438,7 -1455,7 +1435,7 @@@ trace_nfs_atomic_open_enter(dir, ctx, open_flags); nfs_block_sillyrename(dentry->d_parent); - inode = NFS_PROTO(dir)->open_context(dir, ctx, open_flags, &attr); + inode = NFS_PROTO(dir)->open_context(dir, ctx, open_flags, &attr, opened); nfs_unblock_sillyrename(dentry->d_parent); if (IS_ERR(inode)) { err = PTR_ERR(inode); @@@ -1588,8 -1605,8 +1585,8 @@@ int nfs_create(struct inode *dir, struc int open_flags = excl ? O_CREAT | O_EXCL : O_CREAT; int error; - dfprintk(VFS, "NFS: create(%s/%ld), %s\n", - dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); + dfprintk(VFS, "NFS: create(%s/%ld), %pd\n", + dir->i_sb->s_id, dir->i_ino, dentry); attr.ia_mode = mode; attr.ia_valid = ATTR_MODE; @@@ -1615,8 -1632,8 +1612,8 @@@ nfs_mknod(struct inode *dir, struct den struct iattr attr; int status; - dfprintk(VFS, "NFS: mknod(%s/%ld), %s\n", - dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); + dfprintk(VFS, "NFS: mknod(%s/%ld), %pd\n", + dir->i_sb->s_id, dir->i_ino, dentry); if (!new_valid_dev(rdev)) return -EINVAL; @@@ -1644,8 -1661,8 +1641,8 @@@ int nfs_mkdir(struct inode *dir, struc struct iattr attr; int error; - dfprintk(VFS, "NFS: mkdir(%s/%ld), %s\n", - dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); + dfprintk(VFS, "NFS: mkdir(%s/%ld), %pd\n", + dir->i_sb->s_id, dir->i_ino, dentry); attr.ia_valid = ATTR_MODE; attr.ia_mode = mode | S_IFDIR; @@@ -1672,8 -1689,8 +1669,8 @@@ int nfs_rmdir(struct inode *dir, struc { int error; - dfprintk(VFS, "NFS: rmdir(%s/%ld), %s\n", - dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); + dfprintk(VFS, "NFS: rmdir(%s/%ld), %pd\n", + dir->i_sb->s_id, dir->i_ino, dentry); trace_nfs_rmdir_enter(dir, dentry); if (dentry->d_inode) { @@@ -1708,7 -1725,8 +1705,7 @@@ static int nfs_safe_remove(struct dentr struct inode *inode = dentry->d_inode; int error = -EBUSY; - dfprintk(VFS, "NFS: safe_remove(%s/%s)\n", - dentry->d_parent->d_name.name, dentry->d_name.name); + dfprintk(VFS, "NFS: safe_remove(%pd2)\n", dentry); /* If the dentry was sillyrenamed, we simply call d_delete() */ if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { @@@ -1741,8 -1759,8 +1738,8 @@@ int nfs_unlink(struct inode *dir, struc int error; int need_rehash = 0; - dfprintk(VFS, "NFS: unlink(%s/%ld, %s)\n", dir->i_sb->s_id, - dir->i_ino, dentry->d_name.name); + dfprintk(VFS, "NFS: unlink(%s/%ld, %pd)\n", dir->i_sb->s_id, + dir->i_ino, dentry); trace_nfs_unlink_enter(dir, dentry); spin_lock(&dentry->d_lock); @@@ -1792,8 -1810,8 +1789,8 @@@ int nfs_symlink(struct inode *dir, stru unsigned int pathlen = strlen(symname); int error; - dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s)\n", dir->i_sb->s_id, - dir->i_ino, dentry->d_name.name, symname); + dfprintk(VFS, "NFS: symlink(%s/%ld, %pd, %s)\n", dir->i_sb->s_id, + dir->i_ino, dentry, symname); if (pathlen > PAGE_SIZE) return -ENAMETOOLONG; @@@ -1815,9 -1833,9 +1812,9 @@@ error = NFS_PROTO(dir)->symlink(dir, dentry, page, pathlen, &attr); trace_nfs_symlink_exit(dir, dentry, error); if (error != 0) { - dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s) error %d\n", + dfprintk(VFS, "NFS: symlink(%s/%ld, %pd, %s) error %d\n", dir->i_sb->s_id, dir->i_ino, - dentry->d_name.name, symname, error); + dentry, symname, error); d_drop(dentry); __free_page(page); return error; @@@ -1844,8 -1862,9 +1841,8 @@@ nfs_link(struct dentry *old_dentry, str struct inode *inode = old_dentry->d_inode; int error; - dfprintk(VFS, "NFS: link(%s/%s -> %s/%s)\n", - old_dentry->d_parent->d_name.name, old_dentry->d_name.name, - dentry->d_parent->d_name.name, dentry->d_name.name); + dfprintk(VFS, "NFS: link(%pd2 -> %pd2)\n", + old_dentry, dentry); trace_nfs_link_enter(inode, dir, dentry); NFS_PROTO(inode)->return_delegation(inode); @@@ -1893,8 -1912,9 +1890,8 @@@ int nfs_rename(struct inode *old_dir, s struct dentry *dentry = NULL, *rehash = NULL; int error = -EBUSY; - dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n", - old_dentry->d_parent->d_name.name, old_dentry->d_name.name, - new_dentry->d_parent->d_name.name, new_dentry->d_name.name, + dfprintk(VFS, "NFS: rename(%pd2 -> %pd2, ct=%d)\n", + old_dentry, new_dentry, d_count(new_dentry)); trace_nfs_rename_enter(old_dir, old_dentry, new_dir, new_dentry); diff --combined fs/sysfs/dir.c index eab59de47556,477c66d4e2a8..11837b73abbf --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@@ -28,19 -28,19 +28,19 @@@ DEFINE_MUTEX(sysfs_mutex); DEFINE_SPINLOCK(sysfs_assoc_lock); -#define to_sysfs_dirent(X) rb_entry((X), struct sysfs_dirent, s_rb); +#define to_sysfs_dirent(X) rb_entry((X), struct sysfs_dirent, s_rb) static DEFINE_SPINLOCK(sysfs_ino_lock); static DEFINE_IDA(sysfs_ino_ida); /** * sysfs_name_hash - * @ns: Namespace tag to hash * @name: Null terminated string to hash + * @ns: Namespace tag to hash * * Returns 31 bit hash of ns + name (so it fits in an off_t ) */ -static unsigned int sysfs_name_hash(const void *ns, const char *name) +static unsigned int sysfs_name_hash(const char *name, const void *ns) { unsigned long hash = init_name_hash(); unsigned int len = strlen(name); @@@ -56,8 -56,8 +56,8 @@@ return hash; } -static int sysfs_name_compare(unsigned int hash, const void *ns, - const char *name, const struct sysfs_dirent *sd) +static int sysfs_name_compare(unsigned int hash, const char *name, + const void *ns, const struct sysfs_dirent *sd) { if (hash != sd->s_hash) return hash - sd->s_hash; @@@ -69,7 -69,7 +69,7 @@@ static int sysfs_sd_compare(const struct sysfs_dirent *left, const struct sysfs_dirent *right) { - return sysfs_name_compare(left->s_hash, left->s_ns, left->s_name, + return sysfs_name_compare(left->s_hash, left->s_name, left->s_ns, right); } @@@ -111,11 -111,6 +111,11 @@@ static int sysfs_link_sibling(struct sy /* add new node and rebalance the tree */ rb_link_node(&sd->s_rb, parent, node); rb_insert_color(&sd->s_rb, &sd->s_parent->s_dir.children); + + /* if @sd has ns tag, mark the parent to enable ns filtering */ + if (sd->s_ns) + sd->s_parent->s_flags |= SYSFS_FLAG_HAS_NS; + return 0; } @@@ -135,15 -130,26 +135,15 @@@ static void sysfs_unlink_sibling(struc sd->s_parent->s_dir.subdirs--; rb_erase(&sd->s_rb, &sd->s_parent->s_dir.children); -} - -#ifdef CONFIG_DEBUG_LOCK_ALLOC - -/* Test for attributes that want to ignore lockdep for read-locking */ -static bool ignore_lockdep(struct sysfs_dirent *sd) -{ - return sysfs_type(sd) == SYSFS_KOBJ_ATTR && - sd->s_attr.attr->ignore_lockdep; -} - -#else -static inline bool ignore_lockdep(struct sysfs_dirent *sd) -{ - return true; + /* + * Either all or none of the children have tags. Clearing HAS_NS + * when there's no child left is enough to keep the flag synced. + */ + if (RB_EMPTY_ROOT(&sd->s_parent->s_dir.children)) + sd->s_parent->s_flags &= ~SYSFS_FLAG_HAS_NS; } -#endif - /** * sysfs_get_active - get an active reference to sysfs_dirent * @sd: sysfs_dirent to get an active reference to @@@ -162,7 -168,7 +162,7 @@@ struct sysfs_dirent *sysfs_get_active(s if (!atomic_inc_unless_negative(&sd->s_active)) return NULL; - if (likely(!ignore_lockdep(sd))) + if (likely(!sysfs_ignore_lockdep(sd))) rwsem_acquire_read(&sd->dep_map, 0, 1, _RET_IP_); return sd; } @@@ -181,7 -187,7 +181,7 @@@ void sysfs_put_active(struct sysfs_dire if (unlikely(!sd)) return; - if (likely(!ignore_lockdep(sd))) + if (likely(!sysfs_ignore_lockdep(sd))) rwsem_release(&sd->dep_map, 1, _RET_IP_); v = atomic_dec_return(&sd->s_active); if (likely(v != SD_DEACTIVATED_BIAS)) @@@ -291,6 -297,7 +291,6 @@@ static int sysfs_dentry_delete(const st static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) { struct sysfs_dirent *sd; - int type; if (flags & LOOKUP_RCU) return -ECHILD; @@@ -311,11 -318,15 +311,10 @@@ goto out_bad; /* The sysfs dirent has been moved to a different namespace */ - type = KOBJ_NS_TYPE_NONE; - if (sd->s_parent) { - type = sysfs_ns_type(sd->s_parent); - if (type != KOBJ_NS_TYPE_NONE && - sysfs_info(dentry->d_sb)->ns[type] != sd->s_ns) - goto out_bad; - } + if (sd->s_ns && sd->s_ns != sysfs_info(dentry->d_sb)->ns) + goto out_bad; mutex_unlock(&sysfs_mutex); - out_valid: return 1; out_bad: /* Remove the dentry from the dcache hashes. @@@ -329,13 -340,7 +328,7 @@@ * to the dcache hashes. */ mutex_unlock(&sysfs_mutex); - - /* If we have submounts we must allow the vfs caches - * to lie about the state of the filesystem to prevent - * leaks and other nasty things. - */ - if (check_submounts_and_drop(dentry) != 0) - goto out_valid; + shrink_submounts_and_drop(dentry); return 0; } @@@ -388,19 -393,22 +381,19 @@@ struct sysfs_dirent *sysfs_new_dirent(c /** * sysfs_addrm_start - prepare for sysfs_dirent add/remove * @acxt: pointer to sysfs_addrm_cxt to be used - * @parent_sd: parent sysfs_dirent * - * This function is called when the caller is about to add or - * remove sysfs_dirent under @parent_sd. This function acquires - * sysfs_mutex. @acxt is used to keep and pass context to - * other addrm functions. + * This function is called when the caller is about to add or remove + * sysfs_dirent. This function acquires sysfs_mutex. @acxt is used + * to keep and pass context to other addrm functions. * * LOCKING: * Kernel thread context (may sleep). sysfs_mutex is locked on * return. */ -void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt, - struct sysfs_dirent *parent_sd) +void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt) + __acquires(sysfs_mutex) { memset(acxt, 0, sizeof(*acxt)); - acxt->parent_sd = parent_sd; mutex_lock(&sysfs_mutex); } @@@ -409,11 -417,10 +402,11 @@@ * __sysfs_add_one - add sysfs_dirent to parent without warning * @acxt: addrm context to use * @sd: sysfs_dirent to be added + * @parent_sd: the parent sysfs_dirent to add @sd to * - * Get @acxt->parent_sd and set sd->s_parent to it and increment - * nlink of parent inode if @sd is a directory and link into the - * children list of the parent. + * Get @parent_sd and set @sd->s_parent to it and increment nlink of + * the parent inode if @sd is a directory and link into the children + * list of the parent. * * This function should be called between calls to * sysfs_addrm_start() and sysfs_addrm_finish() and should be @@@ -426,21 -433,27 +419,21 @@@ * 0 on success, -EEXIST if entry with the given name already * exists. */ -int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) +int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, + struct sysfs_dirent *parent_sd) { struct sysfs_inode_attrs *ps_iattr; int ret; - if (!!sysfs_ns_type(acxt->parent_sd) != !!sd->s_ns) { - WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", - sysfs_ns_type(acxt->parent_sd) ? "required" : "invalid", - acxt->parent_sd->s_name, sd->s_name); - return -EINVAL; - } - - sd->s_hash = sysfs_name_hash(sd->s_ns, sd->s_name); - sd->s_parent = sysfs_get(acxt->parent_sd); + sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns); + sd->s_parent = sysfs_get(parent_sd); ret = sysfs_link_sibling(sd); if (ret) return ret; /* Update timestamps on the parent */ - ps_iattr = acxt->parent_sd->s_iattr; + ps_iattr = parent_sd->s_iattr; if (ps_iattr) { struct iattr *ps_iattrs = &ps_iattr->ia_iattr; ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME; @@@ -474,11 -487,10 +467,11 @@@ static char *sysfs_pathname(struct sysf * sysfs_add_one - add sysfs_dirent to parent * @acxt: addrm context to use * @sd: sysfs_dirent to be added + * @parent_sd: the parent sysfs_dirent to add @sd to * - * Get @acxt->parent_sd and set sd->s_parent to it and increment - * nlink of parent inode if @sd is a directory and link into the - * children list of the parent. + * Get @parent_sd and set @sd->s_parent to it and increment nlink of + * the parent inode if @sd is a directory and link into the children + * list of the parent. * * This function should be called between calls to * sysfs_addrm_start() and sysfs_addrm_finish() and should be @@@ -491,18 -503,17 +484,18 @@@ * 0 on success, -EEXIST if entry with the given name already * exists. */ -int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) +int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, + struct sysfs_dirent *parent_sd) { int ret; - ret = __sysfs_add_one(acxt, sd); + ret = __sysfs_add_one(acxt, sd, parent_sd); if (ret == -EEXIST) { char *path = kzalloc(PATH_MAX, GFP_KERNEL); WARN(1, KERN_WARNING "sysfs: cannot create duplicate filename '%s'\n", (path == NULL) ? sd->s_name - : (sysfs_pathname(acxt->parent_sd, path), + : (sysfs_pathname(parent_sd, path), strlcat(path, "/", PATH_MAX), strlcat(path, sd->s_name, PATH_MAX), path)); @@@ -527,22 -538,16 +520,22 @@@ * LOCKING: * Determined by sysfs_addrm_start(). */ -void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) +static void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, + struct sysfs_dirent *sd) { struct sysfs_inode_attrs *ps_iattr; - BUG_ON(sd->s_flags & SYSFS_FLAG_REMOVED); + /* + * Removal can be called multiple times on the same node. Only the + * first invocation is effective and puts the base ref. + */ + if (sd->s_flags & SYSFS_FLAG_REMOVED) + return; sysfs_unlink_sibling(sd); /* Update timestamps on the parent */ - ps_iattr = acxt->parent_sd->s_iattr; + ps_iattr = sd->s_parent->s_iattr; if (ps_iattr) { struct iattr *ps_iattrs = &ps_iattr->ia_iattr; ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME; @@@ -565,7 -570,6 +558,7 @@@ * sysfs_mutex is released. */ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt) + __releases(sysfs_mutex) { /* release resources acquired by sysfs_addrm_start() */ mutex_unlock(&sysfs_mutex); @@@ -577,7 -581,7 +570,7 @@@ acxt->removed = sd->u.removed_list; sysfs_deactivate(sd); - unmap_bin_file(sd); + sysfs_unmap_bin_file(sd); sysfs_put(sd); } } @@@ -586,7 -590,6 +579,7 @@@ * sysfs_find_dirent - find sysfs_dirent with the given name * @parent_sd: sysfs_dirent to search under * @name: name to look for + * @ns: the namespace tag to use * * Look for sysfs_dirent with name @name under @parent_sd. * @@@ -597,19 -600,26 +590,19 @@@ * Pointer to sysfs_dirent if found, NULL if not. */ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, - const void *ns, - const unsigned char *name) + const unsigned char *name, + const void *ns) { struct rb_node *node = parent_sd->s_dir.children.rb_node; unsigned int hash; - if (!!sysfs_ns_type(parent_sd) != !!ns) { - WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", - sysfs_ns_type(parent_sd) ? "required" : "invalid", - parent_sd->s_name, name); - return NULL; - } - - hash = sysfs_name_hash(ns, name); + hash = sysfs_name_hash(name, ns); while (node) { struct sysfs_dirent *sd; int result; sd = to_sysfs_dirent(node); - result = sysfs_name_compare(hash, ns, name, sd); + result = sysfs_name_compare(hash, name, ns, sd); if (result < 0) node = node->rb_left; else if (result > 0) @@@ -621,10 -631,9 +614,10 @@@ } /** - * sysfs_get_dirent - find and get sysfs_dirent with the given name + * sysfs_get_dirent_ns - find and get sysfs_dirent with the given name * @parent_sd: sysfs_dirent to search under * @name: name to look for + * @ns: the namespace tag to use * * Look for sysfs_dirent with name @name under @parent_sd and get * it if found. @@@ -635,24 -644,24 +628,24 @@@ * RETURNS: * Pointer to sysfs_dirent if found, NULL if not. */ -struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd, - const void *ns, - const unsigned char *name) +struct sysfs_dirent *sysfs_get_dirent_ns(struct sysfs_dirent *parent_sd, + const unsigned char *name, + const void *ns) { struct sysfs_dirent *sd; mutex_lock(&sysfs_mutex); - sd = sysfs_find_dirent(parent_sd, ns, name); + sd = sysfs_find_dirent(parent_sd, name, ns); sysfs_get(sd); mutex_unlock(&sysfs_mutex); return sd; } -EXPORT_SYMBOL_GPL(sysfs_get_dirent); +EXPORT_SYMBOL_GPL(sysfs_get_dirent_ns); static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, - enum kobj_ns_type type, const void *ns, const char *name, - struct sysfs_dirent **p_sd) + const char *name, const void *ns, + struct sysfs_dirent **p_sd) { umode_t mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; struct sysfs_addrm_cxt acxt; @@@ -664,12 -673,13 +657,12 @@@ if (!sd) return -ENOMEM; - sd->s_flags |= (type << SYSFS_NS_TYPE_SHIFT); sd->s_ns = ns; sd->s_dir.kobj = kobj; /* link in */ - sysfs_addrm_start(&acxt, parent_sd); - rc = sysfs_add_one(&acxt, sd); + sysfs_addrm_start(&acxt); + rc = sysfs_add_one(&acxt, sd, parent_sd); sysfs_addrm_finish(&acxt); if (rc == 0) @@@ -683,17 -693,44 +676,17 @@@ int sysfs_create_subdir(struct kobject *kobj, const char *name, struct sysfs_dirent **p_sd) { - return create_dir(kobj, kobj->sd, - KOBJ_NS_TYPE_NONE, NULL, name, p_sd); + return create_dir(kobj, kobj->sd, name, NULL, p_sd); } /** - * sysfs_read_ns_type: return associated ns_type - * @kobj: the kobject being queried - * - * Each kobject can be tagged with exactly one namespace type - * (i.e. network or user). Return the ns_type associated with - * this object if any + * sysfs_create_dir_ns - create a directory for an object with a namespace tag + * @kobj: object we're creating directory for + * @ns: the namespace tag to use */ -static enum kobj_ns_type sysfs_read_ns_type(struct kobject *kobj) +int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) { - const struct kobj_ns_type_operations *ops; - enum kobj_ns_type type; - - ops = kobj_child_ns_ops(kobj); - if (!ops) - return KOBJ_NS_TYPE_NONE; - - type = ops->type; - BUG_ON(type <= KOBJ_NS_TYPE_NONE); - BUG_ON(type >= KOBJ_NS_TYPES); - BUG_ON(!kobj_ns_type_registered(type)); - - return type; -} - -/** - * sysfs_create_dir - create a directory for an object. - * @kobj: object we're creating directory for. - */ -int sysfs_create_dir(struct kobject *kobj) -{ - enum kobj_ns_type type; struct sysfs_dirent *parent_sd, *sd; - const void *ns = NULL; int error = 0; BUG_ON(!kobj); @@@ -706,7 -743,11 +699,7 @@@ if (!parent_sd) return -ENOENT; - if (sysfs_ns_type(parent_sd)) - ns = kobj->ktype->namespace(kobj); - type = sysfs_read_ns_type(kobj); - - error = create_dir(kobj, parent_sd, type, ns, kobject_name(kobj), &sd); + error = create_dir(kobj, parent_sd, kobject_name(kobj), ns, &sd); if (!error) kobj->sd = sd; return error; @@@ -720,14 -761,15 +713,14 @@@ static struct dentry *sysfs_lookup(stru struct sysfs_dirent *parent_sd = parent->d_fsdata; struct sysfs_dirent *sd; struct inode *inode; - enum kobj_ns_type type; - const void *ns; + const void *ns = NULL; mutex_lock(&sysfs_mutex); - type = sysfs_ns_type(parent_sd); - ns = sysfs_info(dir->i_sb)->ns[type]; + if (parent_sd->s_flags & SYSFS_FLAG_HAS_NS) + ns = sysfs_info(dir->i_sb)->ns; - sd = sysfs_find_dirent(parent_sd, ns, dentry->d_name.name); + sd = sysfs_find_dirent(parent_sd, dentry->d_name.name, ns); /* no such entry */ if (!sd) { @@@ -758,92 -800,41 +751,92 @@@ const struct inode_operations sysfs_dir .setxattr = sysfs_setxattr, }; -static void remove_dir(struct sysfs_dirent *sd) +static struct sysfs_dirent *sysfs_leftmost_descendant(struct sysfs_dirent *pos) { - struct sysfs_addrm_cxt acxt; + struct sysfs_dirent *last; - sysfs_addrm_start(&acxt, sd->s_parent); - sysfs_remove_one(&acxt, sd); - sysfs_addrm_finish(&acxt); + while (true) { + struct rb_node *rbn; + + last = pos; + + if (sysfs_type(pos) != SYSFS_DIR) + break; + + rbn = rb_first(&pos->s_dir.children); + if (!rbn) + break; + + pos = to_sysfs_dirent(rbn); + } + + return last; } -void sysfs_remove_subdir(struct sysfs_dirent *sd) +/** + * sysfs_next_descendant_post - find the next descendant for post-order walk + * @pos: the current position (%NULL to initiate traversal) + * @root: sysfs_dirent whose descendants to walk + * + * Find the next descendant to visit for post-order traversal of @root's + * descendants. @root is included in the iteration and the last node to be + * visited. + */ +static struct sysfs_dirent *sysfs_next_descendant_post(struct sysfs_dirent *pos, + struct sysfs_dirent *root) { - remove_dir(sd); -} + struct rb_node *rbn; + + lockdep_assert_held(&sysfs_mutex); + /* if first iteration, visit leftmost descendant which may be root */ + if (!pos) + return sysfs_leftmost_descendant(root); + + /* if we visited @root, we're done */ + if (pos == root) + return NULL; -static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd) + /* if there's an unvisited sibling, visit its leftmost descendant */ + rbn = rb_next(&pos->s_rb); + if (rbn) + return sysfs_leftmost_descendant(to_sysfs_dirent(rbn)); + + /* no sibling left, visit parent */ + return pos->s_parent; +} + +void __sysfs_remove(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) { - struct sysfs_addrm_cxt acxt; - struct rb_node *pos; + struct sysfs_dirent *pos, *next; - if (!dir_sd) + if (!sd) return; - pr_debug("sysfs %s: removing dir\n", dir_sd->s_name); - sysfs_addrm_start(&acxt, dir_sd); - pos = rb_first(&dir_sd->s_dir.children); - while (pos) { - struct sysfs_dirent *sd = to_sysfs_dirent(pos); - pos = rb_next(pos); - if (sysfs_type(sd) != SYSFS_DIR) - sysfs_remove_one(&acxt, sd); - } - sysfs_addrm_finish(&acxt); + pr_debug("sysfs %s: removing\n", sd->s_name); - remove_dir(dir_sd); + next = NULL; + do { + pos = next; + next = sysfs_next_descendant_post(pos, sd); + if (pos) + sysfs_remove_one(acxt, pos); + } while (next); +} + +/** + * sysfs_remove - remove a sysfs_dirent recursively + * @sd: the sysfs_dirent to remove + * + * Remove @sd along with all its subdirectories and files. + */ +void sysfs_remove(struct sysfs_dirent *sd) +{ + struct sysfs_addrm_cxt acxt; + + sysfs_addrm_start(&acxt); + __sysfs_remove(&acxt, sd); + sysfs_addrm_finish(&acxt); } /** @@@ -854,6 -845,7 +847,6 @@@ * the directory before we remove the directory, and we've inlined * what used to be sysfs_rmdir() below, instead of calling separately. */ - void sysfs_remove_dir(struct kobject *kobj) { struct sysfs_dirent *sd = kobj->sd; @@@ -862,14 -854,12 +855,14 @@@ kobj->sd = NULL; spin_unlock(&sysfs_assoc_lock); - __sysfs_remove_dir(sd); + if (sd) { + WARN_ON_ONCE(sysfs_type(sd) != SYSFS_DIR); + sysfs_remove(sd); + } } -int sysfs_rename(struct sysfs_dirent *sd, - struct sysfs_dirent *new_parent_sd, const void *new_ns, - const char *new_name) +int sysfs_rename(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent_sd, + const char *new_name, const void *new_ns) { int error; @@@ -881,7 -871,7 +874,7 @@@ goto out; /* nothing to rename */ error = -EEXIST; - if (sysfs_find_dirent(new_parent_sd, new_ns, new_name)) + if (sysfs_find_dirent(new_parent_sd, new_name, new_ns)) goto out; /* rename sysfs_dirent */ @@@ -902,7 -892,7 +895,7 @@@ sysfs_get(new_parent_sd); sysfs_put(sd->s_parent); sd->s_ns = new_ns; - sd->s_hash = sysfs_name_hash(sd->s_ns, sd->s_name); + sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns); sd->s_parent = new_parent_sd; sysfs_link_sibling(sd); @@@ -912,25 -902,30 +905,25 @@@ return error; } -int sysfs_rename_dir(struct kobject *kobj, const char *new_name) +int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, + const void *new_ns) { struct sysfs_dirent *parent_sd = kobj->sd->s_parent; - const void *new_ns = NULL; - - if (sysfs_ns_type(parent_sd)) - new_ns = kobj->ktype->namespace(kobj); - return sysfs_rename(kobj->sd, parent_sd, new_ns, new_name); + return sysfs_rename(kobj->sd, parent_sd, new_name, new_ns); } -int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent_kobj) +int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj, + const void *new_ns) { struct sysfs_dirent *sd = kobj->sd; struct sysfs_dirent *new_parent_sd; - const void *new_ns = NULL; BUG_ON(!sd->s_parent); - if (sysfs_ns_type(sd->s_parent)) - new_ns = kobj->ktype->namespace(kobj); new_parent_sd = new_parent_kobj && new_parent_kobj->sd ? new_parent_kobj->sd : &sysfs_root; - return sysfs_rename(sd, new_parent_sd, new_ns, sd->s_name); + return sysfs_rename(sd, new_parent_sd, sd->s_name, new_ns); } /* Relationship between s_mode and the DT_xxx types */ @@@ -1000,15 -995,15 +993,15 @@@ static int sysfs_readdir(struct file *f struct dentry *dentry = file->f_path.dentry; struct sysfs_dirent *parent_sd = dentry->d_fsdata; struct sysfs_dirent *pos = file->private_data; - enum kobj_ns_type type; - const void *ns; - - type = sysfs_ns_type(parent_sd); - ns = sysfs_info(dentry->d_sb)->ns[type]; + const void *ns = NULL; if (!dir_emit_dots(file, ctx)) return 0; mutex_lock(&sysfs_mutex); + + if (parent_sd->s_flags & SYSFS_FLAG_HAS_NS) + ns = sysfs_info(dentry->d_sb)->ns; + for (pos = sysfs_dir_pos(ns, parent_sd, ctx->pos, pos); pos; pos = sysfs_dir_next_pos(ns, parent_sd, ctx->pos, pos)) { diff --combined include/linux/dcache.h index 716c3760ee39,17948b49f3d5..ae57848f92d9 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@@ -224,7 -224,6 +224,7 @@@ static inline int dname_external(const extern void d_instantiate(struct dentry *, struct inode *); extern struct dentry * d_instantiate_unique(struct dentry *, struct inode *); extern struct dentry * d_materialise_unique(struct dentry *, struct inode *); +extern int d_instantiate_no_diralias(struct dentry *, struct inode *); extern void __d_drop(struct dentry *dentry); extern void d_drop(struct dentry *dentry); extern void d_delete(struct dentry *); @@@ -255,7 -254,8 +255,8 @@@ extern void d_prune_aliases(struct inod /* test whether we have any submounts in a subdir tree */ extern int have_submounts(struct dentry *); - extern int check_submounts_and_drop(struct dentry *); + extern void detach_submounts(struct dentry *dentry); + extern void shrink_submounts_and_drop(struct dentry *); /* * This adds the entry to the hash queues.