aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-09-14 20:37:36 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-09-14 20:37:36 -0400
commit83373f702829dd9f6dcc56d275978d986fafee48 (patch)
tree6e816715ce2e9ea73da8b8462953262f47460897 /fs
parent9226b5b440f2b4fbb3b797f3cb74a9a627220660 (diff)
parent4023bfc9f351a7994fb6a7d515476c320f94a574 (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull vfs fixes from Al Viro: "double iput() on failure exit in lustre, racy removal of spliced dentries from ->s_anon in __d_materialise_dentry() plus a bunch of assorted RCU pathwalk fixes" The RCU pathwalk fixes end up fixing a couple of cases where we incorrectly dropped out of RCU walking, due to incorrect initialization and testing of the sequence locks in some corner cases. Since dropping out of RCU walk mode forces the slow locked accesses, those corner cases slowed down quite dramatically. * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: be careful with nd->inode in path_init() and follow_dotdot_rcu() don't bugger nd->seq on set_root_rcu() from follow_dotdot_rcu() fix bogus read_seqretry() checks introduced in b37199e move the call of __d_drop(anon) into __d_materialise_unique(dentry, anon) [fix] lustre: d_make_root() does iput() on dentry allocation failure
Diffstat (limited to 'fs')
-rw-r--r--fs/dcache.c8
-rw-r--r--fs/namei.c52
2 files changed, 38 insertions, 22 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index 4023e77b800e..7a5b51440afa 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -2655,6 +2655,12 @@ static void __d_materialise_dentry(struct dentry *dentry, struct dentry *anon)
2655 dentry->d_parent = dentry; 2655 dentry->d_parent = dentry;
2656 list_del_init(&dentry->d_u.d_child); 2656 list_del_init(&dentry->d_u.d_child);
2657 anon->d_parent = dparent; 2657 anon->d_parent = dparent;
2658 if (likely(!d_unhashed(anon))) {
2659 hlist_bl_lock(&anon->d_sb->s_anon);
2660 __hlist_bl_del(&anon->d_hash);
2661 anon->d_hash.pprev = NULL;
2662 hlist_bl_unlock(&anon->d_sb->s_anon);
2663 }
2658 list_move(&anon->d_u.d_child, &dparent->d_subdirs); 2664 list_move(&anon->d_u.d_child, &dparent->d_subdirs);
2659 2665
2660 write_seqcount_end(&dentry->d_seq); 2666 write_seqcount_end(&dentry->d_seq);
@@ -2713,7 +2719,6 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
2713 write_seqlock(&rename_lock); 2719 write_seqlock(&rename_lock);
2714 __d_materialise_dentry(dentry, new); 2720 __d_materialise_dentry(dentry, new);
2715 write_sequnlock(&rename_lock); 2721 write_sequnlock(&rename_lock);
2716 __d_drop(new);
2717 _d_rehash(new); 2722 _d_rehash(new);
2718 spin_unlock(&new->d_lock); 2723 spin_unlock(&new->d_lock);
2719 spin_unlock(&inode->i_lock); 2724 spin_unlock(&inode->i_lock);
@@ -2777,7 +2782,6 @@ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode)
2777 * could splice into our tree? */ 2782 * could splice into our tree? */
2778 __d_materialise_dentry(dentry, alias); 2783 __d_materialise_dentry(dentry, alias);
2779 write_sequnlock(&rename_lock); 2784 write_sequnlock(&rename_lock);
2780 __d_drop(alias);
2781 goto found; 2785 goto found;
2782 } else { 2786 } else {
2783 /* Nope, but we must(!) avoid directory 2787 /* Nope, but we must(!) avoid directory
diff --git a/fs/namei.c b/fs/namei.c
index 2be5120b81b3..215e44254c53 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -644,24 +644,22 @@ static int complete_walk(struct nameidata *nd)
644 644
645static __always_inline void set_root(struct nameidata *nd) 645static __always_inline void set_root(struct nameidata *nd)
646{ 646{
647 if (!nd->root.mnt) 647 get_fs_root(current->fs, &nd->root);
648 get_fs_root(current->fs, &nd->root);
649} 648}
650 649
651static int link_path_walk(const char *, struct nameidata *); 650static int link_path_walk(const char *, struct nameidata *);
652 651
653static __always_inline void set_root_rcu(struct nameidata *nd) 652static __always_inline unsigned set_root_rcu(struct nameidata *nd)
654{ 653{
655 if (!nd->root.mnt) { 654 struct fs_struct *fs = current->fs;
656 struct fs_struct *fs = current->fs; 655 unsigned seq, res;
657 unsigned seq;
658 656
659 do { 657 do {
660 seq = read_seqcount_begin(&fs->seq); 658 seq = read_seqcount_begin(&fs->seq);
661 nd->root = fs->root; 659 nd->root = fs->root;
662 nd->seq = __read_seqcount_begin(&nd->root.dentry->d_seq); 660 res = __read_seqcount_begin(&nd->root.dentry->d_seq);
663 } while (read_seqcount_retry(&fs->seq, seq)); 661 } while (read_seqcount_retry(&fs->seq, seq));
664 } 662 return res;
665} 663}
666 664
667static void path_put_conditional(struct path *path, struct nameidata *nd) 665static void path_put_conditional(struct path *path, struct nameidata *nd)
@@ -861,7 +859,8 @@ follow_link(struct path *link, struct nameidata *nd, void **p)
861 return PTR_ERR(s); 859 return PTR_ERR(s);
862 } 860 }
863 if (*s == '/') { 861 if (*s == '/') {
864 set_root(nd); 862 if (!nd->root.mnt)
863 set_root(nd);
865 path_put(&nd->path); 864 path_put(&nd->path);
866 nd->path = nd->root; 865 nd->path = nd->root;
867 path_get(&nd->root); 866 path_get(&nd->root);
@@ -1138,13 +1137,15 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
1138 */ 1137 */
1139 *inode = path->dentry->d_inode; 1138 *inode = path->dentry->d_inode;
1140 } 1139 }
1141 return read_seqretry(&mount_lock, nd->m_seq) && 1140 return !read_seqretry(&mount_lock, nd->m_seq) &&
1142 !(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT); 1141 !(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT);
1143} 1142}
1144 1143
1145static int follow_dotdot_rcu(struct nameidata *nd) 1144static int follow_dotdot_rcu(struct nameidata *nd)
1146{ 1145{
1147 set_root_rcu(nd); 1146 struct inode *inode = nd->inode;
1147 if (!nd->root.mnt)
1148 set_root_rcu(nd);
1148 1149
1149 while (1) { 1150 while (1) {
1150 if (nd->path.dentry == nd->root.dentry && 1151 if (nd->path.dentry == nd->root.dentry &&
@@ -1156,6 +1157,7 @@ static int follow_dotdot_rcu(struct nameidata *nd)
1156 struct dentry *parent = old->d_parent; 1157 struct dentry *parent = old->d_parent;
1157 unsigned seq; 1158 unsigned seq;
1158 1159
1160 inode = parent->d_inode;
1159 seq = read_seqcount_begin(&parent->d_seq); 1161 seq = read_seqcount_begin(&parent->d_seq);
1160 if (read_seqcount_retry(&old->d_seq, nd->seq)) 1162 if (read_seqcount_retry(&old->d_seq, nd->seq))
1161 goto failed; 1163 goto failed;
@@ -1165,6 +1167,7 @@ static int follow_dotdot_rcu(struct nameidata *nd)
1165 } 1167 }
1166 if (!follow_up_rcu(&nd->path)) 1168 if (!follow_up_rcu(&nd->path))
1167 break; 1169 break;
1170 inode = nd->path.dentry->d_inode;
1168 nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq); 1171 nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
1169 } 1172 }
1170 while (d_mountpoint(nd->path.dentry)) { 1173 while (d_mountpoint(nd->path.dentry)) {
@@ -1174,11 +1177,12 @@ static int follow_dotdot_rcu(struct nameidata *nd)
1174 break; 1177 break;
1175 nd->path.mnt = &mounted->mnt; 1178 nd->path.mnt = &mounted->mnt;
1176 nd->path.dentry = mounted->mnt.mnt_root; 1179 nd->path.dentry = mounted->mnt.mnt_root;
1180 inode = nd->path.dentry->d_inode;
1177 nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq); 1181 nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
1178 if (!read_seqretry(&mount_lock, nd->m_seq)) 1182 if (read_seqretry(&mount_lock, nd->m_seq))
1179 goto failed; 1183 goto failed;
1180 } 1184 }
1181 nd->inode = nd->path.dentry->d_inode; 1185 nd->inode = inode;
1182 return 0; 1186 return 0;
1183 1187
1184failed: 1188failed:
@@ -1257,7 +1261,8 @@ static void follow_mount(struct path *path)
1257 1261
1258static void follow_dotdot(struct nameidata *nd) 1262static void follow_dotdot(struct nameidata *nd)
1259{ 1263{
1260 set_root(nd); 1264 if (!nd->root.mnt)
1265 set_root(nd);
1261 1266
1262 while(1) { 1267 while(1) {
1263 struct dentry *old = nd->path.dentry; 1268 struct dentry *old = nd->path.dentry;
@@ -1853,7 +1858,7 @@ static int path_init(int dfd, const char *name, unsigned int flags,
1853 if (*name=='/') { 1858 if (*name=='/') {
1854 if (flags & LOOKUP_RCU) { 1859 if (flags & LOOKUP_RCU) {
1855 rcu_read_lock(); 1860 rcu_read_lock();
1856 set_root_rcu(nd); 1861 nd->seq = set_root_rcu(nd);
1857 } else { 1862 } else {
1858 set_root(nd); 1863 set_root(nd);
1859 path_get(&nd->root); 1864 path_get(&nd->root);
@@ -1904,7 +1909,14 @@ static int path_init(int dfd, const char *name, unsigned int flags,
1904 } 1909 }
1905 1910
1906 nd->inode = nd->path.dentry->d_inode; 1911 nd->inode = nd->path.dentry->d_inode;
1907 return 0; 1912 if (!(flags & LOOKUP_RCU))
1913 return 0;
1914 if (likely(!read_seqcount_retry(&nd->path.dentry->d_seq, nd->seq)))
1915 return 0;
1916 if (!(nd->flags & LOOKUP_ROOT))
1917 nd->root.mnt = NULL;
1918 rcu_read_unlock();
1919 return -ECHILD;
1908} 1920}
1909 1921
1910static inline int lookup_last(struct nameidata *nd, struct path *path) 1922static inline int lookup_last(struct nameidata *nd, struct path *path)