diff options
author | Josh Boyer <jwboyer@fedoraproject.org> | 2014-12-15 08:14:26 -0500 |
---|---|---|
committer | Josh Boyer <jwboyer@fedoraproject.org> | 2014-12-16 14:24:35 -0500 |
commit | 0f1e808baf2f4de1270c17b98e2831c03db8507a (patch) | |
tree | 9b3c26589d54e5d2f754004aec05f3525e11ab92 | |
parent | 2b3a01ceecad93ff43e194e4e8605d3316ed4a96 (diff) | |
download | kernel-0f1e808baf2f4de1270c17b98e2831c03db8507a.tar.gz kernel-0f1e808baf2f4de1270c17b98e2831c03db8507a.tar.xz kernel-0f1e808baf2f4de1270c17b98e2831c03db8507a.zip |
CVE-2014-8559 deadlock due to incorrect usage of rename_lock (rhbz 1159313 1173814)
-rw-r--r-- | deal-with-deadlock-in-d_walk.patch | 86 | ||||
-rw-r--r-- | kernel.spec | 9 | ||||
-rw-r--r-- | move-d_rcu-from-overlapping-d_child-to-overlapping-d.patch | 736 |
3 files changed, 831 insertions, 0 deletions
diff --git a/deal-with-deadlock-in-d_walk.patch b/deal-with-deadlock-in-d_walk.patch new file mode 100644 index 00000000..5f9087b4 --- /dev/null +++ b/deal-with-deadlock-in-d_walk.patch @@ -0,0 +1,86 @@ +From: Al Viro <viro@zeniv.linux.org.uk> +Date: Sun, 26 Oct 2014 19:31:10 -0400 +Subject: [PATCH] deal with deadlock in d_walk() + +... by not hitting rename_retry for reasons other than rename having +happened. In other words, do _not_ restart when finding that +between unlocking the child and locking the parent the former got +into __dentry_kill(). Skip the killed siblings instead... + +Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> +--- + fs/dcache.c | 31 ++++++++++++++++--------------- + 1 file changed, 16 insertions(+), 15 deletions(-) + +diff --git a/fs/dcache.c b/fs/dcache.c +index 8d7c2b34cb3f..e6c207134a14 100644 +--- a/fs/dcache.c ++++ b/fs/dcache.c +@@ -465,7 +465,7 @@ static void __dentry_kill(struct dentry *dentry) + } + /* if it was on the hash then remove it */ + __d_drop(dentry); +- list_del(&dentry->d_child); ++ __list_del_entry(&dentry->d_child); + /* + * Inform d_walk() that we are no longer attached to the + * dentry tree +@@ -1113,33 +1113,31 @@ resume: + /* + * All done at this level ... ascend and resume the search. + */ ++ rcu_read_lock(); ++ascend: + if (this_parent != parent) { + struct dentry *child = this_parent; + this_parent = child->d_parent; + +- rcu_read_lock(); + spin_unlock(&child->d_lock); + spin_lock(&this_parent->d_lock); + +- /* +- * might go back up the wrong parent if we have had a rename +- * or deletion +- */ +- if (this_parent != child->d_parent || +- (child->d_flags & DCACHE_DENTRY_KILLED) || +- need_seqretry(&rename_lock, seq)) { +- spin_unlock(&this_parent->d_lock); +- rcu_read_unlock(); ++ /* might go back up the wrong parent if we have had a rename. */ ++ if (need_seqretry(&rename_lock, seq)) + goto rename_retry; ++ next = child->d_child.next; ++ while (unlikely(child->d_flags & DCACHE_DENTRY_KILLED)) { ++ if (next == &this_parent->d_subdirs) ++ goto ascend; ++ child = list_entry(next, struct dentry, d_child); ++ next = next->next; + } + rcu_read_unlock(); +- next = child->d_child.next; + goto resume; + } +- if (need_seqretry(&rename_lock, seq)) { +- spin_unlock(&this_parent->d_lock); ++ if (need_seqretry(&rename_lock, seq)) + goto rename_retry; +- } ++ rcu_read_unlock(); + if (finish) + finish(data); + +@@ -1149,6 +1147,9 @@ out_unlock: + return; + + rename_retry: ++ spin_unlock(&this_parent->d_lock); ++ rcu_read_unlock(); ++ BUG_ON(seq & 1); + if (!retry) + return; + seq = 1; +-- +2.1.0 + diff --git a/kernel.spec b/kernel.spec index 905c96e8..83618c38 100644 --- a/kernel.spec +++ b/kernel.spec @@ -644,6 +644,10 @@ Patch26100: x86-tls-Validate-TLS-entries-to-protect-espfix.patch #rhbz 1173806 Patch26101: powerpc-powernv-force-all-CPUs-to-be-bootable.patch +#CVE-2014-8559 rhbz 1159313 1173814 +Patch26098: move-d_rcu-from-overlapping-d_child-to-overlapping-d.patch +Patch26099: deal-with-deadlock-in-d_walk.patch + # git clone ssh://git.fedorahosted.org/git/kernel-arm64.git, git diff master...devel Patch30000: kernel-arm64.patch @@ -1397,6 +1401,10 @@ ApplyPatch x86-tls-Validate-TLS-entries-to-protect-espfix.patch #rhbz 1173806 ApplyPatch powerpc-powernv-force-all-CPUs-to-be-bootable.patch +#CVE-2014-8559 rhbz 1159313 1173814 +ApplyPatch move-d_rcu-from-overlapping-d_child-to-overlapping-d.patch +ApplyPatch deal-with-deadlock-in-d_walk.patch + %if 0%{?aarch64patches} ApplyPatch kernel-arm64.patch %ifnarch aarch64 # this is stupid, but i want to notice before secondary koji does. @@ -2272,6 +2280,7 @@ fi # || || %changelog * Tue Dec 16 2014 Josh Boyer <jwboyer@fedoraproject.org> +- CVE-2014-8559 deadlock due to incorrect usage of rename_lock (rhbz 1159313 1173814) - Add patch from Josh Stone to restore var-tracking via Kconfig (rhbz 1126580) * Mon Dec 15 2014 Josh Boyer <jwboyer@fedoraproject.org> diff --git a/move-d_rcu-from-overlapping-d_child-to-overlapping-d.patch b/move-d_rcu-from-overlapping-d_child-to-overlapping-d.patch new file mode 100644 index 00000000..62f7a686 --- /dev/null +++ b/move-d_rcu-from-overlapping-d_child-to-overlapping-d.patch @@ -0,0 +1,736 @@ +From: Al Viro <viro@zeniv.linux.org.uk> +Date: Sun, 26 Oct 2014 19:19:16 -0400 +Subject: [PATCH] move d_rcu from overlapping d_child to overlapping d_alias + +Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> +--- + arch/powerpc/platforms/cell/spufs/inode.c | 2 +- + drivers/staging/lustre/lustre/llite/dcache.c | 2 +- + drivers/staging/lustre/lustre/llite/llite_lib.c | 2 +- + drivers/staging/lustre/lustre/llite/namei.c | 8 ++-- + fs/affs/amigaffs.c | 2 +- + fs/autofs4/expire.c | 12 +++--- + fs/autofs4/root.c | 2 +- + fs/ceph/dir.c | 8 ++-- + fs/ceph/inode.c | 2 +- + fs/cifs/inode.c | 2 +- + fs/coda/cache.c | 2 +- + fs/dcache.c | 52 ++++++++++++------------- + fs/debugfs/inode.c | 2 +- + fs/exportfs/expfs.c | 2 +- + fs/libfs.c | 12 +++--- + fs/ncpfs/dir.c | 2 +- + fs/ncpfs/ncplib_kernel.h | 4 +- + fs/nfs/getroot.c | 2 +- + fs/notify/fsnotify.c | 4 +- + fs/ocfs2/dcache.c | 2 +- + include/linux/dcache.h | 8 ++-- + kernel/trace/trace.c | 4 +- + kernel/trace/trace_events.c | 2 +- + security/selinux/selinuxfs.c | 6 +-- + 24 files changed, 73 insertions(+), 73 deletions(-) + +diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c +index 87ba7cf99cd7..65d633f20d37 100644 +--- a/arch/powerpc/platforms/cell/spufs/inode.c ++++ b/arch/powerpc/platforms/cell/spufs/inode.c +@@ -164,7 +164,7 @@ static void spufs_prune_dir(struct dentry *dir) + struct dentry *dentry, *tmp; + + mutex_lock(&dir->d_inode->i_mutex); +- list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child) { ++ list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_child) { + spin_lock(&dentry->d_lock); + if (!(d_unhashed(dentry)) && dentry->d_inode) { + dget_dlock(dentry); +diff --git a/drivers/staging/lustre/lustre/llite/dcache.c b/drivers/staging/lustre/lustre/llite/dcache.c +index 49ae207ad425..e2add5fde0fe 100644 +--- a/drivers/staging/lustre/lustre/llite/dcache.c ++++ b/drivers/staging/lustre/lustre/llite/dcache.c +@@ -258,7 +258,7 @@ void ll_invalidate_aliases(struct inode *inode) + inode->i_ino, inode->i_generation, inode); + + ll_lock_dcache(inode); +- ll_d_hlist_for_each_entry(dentry, p, &inode->i_dentry, d_alias) { ++ ll_d_hlist_for_each_entry(dentry, p, &inode->i_dentry, d_u.d_alias) { + CDEBUG(D_DENTRY, "dentry in drop %.*s (%p) parent %p " + "inode %p flags %d\n", dentry->d_name.len, + dentry->d_name.name, dentry, dentry->d_parent, +diff --git a/drivers/staging/lustre/lustre/llite/llite_lib.c b/drivers/staging/lustre/lustre/llite/llite_lib.c +index 0c59e26c0805..36e62524a37b 100644 +--- a/drivers/staging/lustre/lustre/llite/llite_lib.c ++++ b/drivers/staging/lustre/lustre/llite/llite_lib.c +@@ -704,7 +704,7 @@ void lustre_dump_dentry(struct dentry *dentry, int recur) + return; + + list_for_each(tmp, &dentry->d_subdirs) { +- struct dentry *d = list_entry(tmp, struct dentry, d_u.d_child); ++ struct dentry *d = list_entry(tmp, struct dentry, d_child); + lustre_dump_dentry(d, recur - 1); + } + } +diff --git a/drivers/staging/lustre/lustre/llite/namei.c b/drivers/staging/lustre/lustre/llite/namei.c +index 0dc7173bbd41..9de0d51e33a2 100644 +--- a/drivers/staging/lustre/lustre/llite/namei.c ++++ b/drivers/staging/lustre/lustre/llite/namei.c +@@ -167,14 +167,14 @@ static void ll_invalidate_negative_children(struct inode *dir) + struct ll_d_hlist_node *p; + + ll_lock_dcache(dir); +- ll_d_hlist_for_each_entry(dentry, p, &dir->i_dentry, d_alias) { ++ ll_d_hlist_for_each_entry(dentry, p, &dir->i_dentry, d_u.d_alias) { + spin_lock(&dentry->d_lock); + if (!list_empty(&dentry->d_subdirs)) { + struct dentry *child; + + list_for_each_entry_safe(child, tmp_subdir, + &dentry->d_subdirs, +- d_u.d_child) { ++ d_child) { + if (child->d_inode == NULL) + d_lustre_invalidate(child, 1); + } +@@ -362,7 +362,7 @@ static struct dentry *ll_find_alias(struct inode *inode, struct dentry *dentry) + discon_alias = invalid_alias = NULL; + + ll_lock_dcache(inode); +- ll_d_hlist_for_each_entry(alias, p, &inode->i_dentry, d_alias) { ++ ll_d_hlist_for_each_entry(alias, p, &inode->i_dentry, d_u.d_alias) { + LASSERT(alias != dentry); + + spin_lock(&alias->d_lock); +@@ -943,7 +943,7 @@ static void ll_get_child_fid(struct inode * dir, struct qstr *name, + { + struct dentry *parent, *child; + +- parent = ll_d_hlist_entry(dir->i_dentry, struct dentry, d_alias); ++ parent = ll_d_hlist_entry(dir->i_dentry, struct dentry, d_u.d_alias); + child = d_lookup(parent, name); + if (child) { + if (child->d_inode) +diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c +index 406b29836b19..a674c114fd8e 100644 +--- a/fs/affs/amigaffs.c ++++ b/fs/affs/amigaffs.c +@@ -127,7 +127,7 @@ affs_fix_dcache(struct inode *inode, u32 entry_ino) + { + struct dentry *dentry; + spin_lock(&inode->i_lock); +- hlist_for_each_entry(dentry, &inode->i_dentry, d_alias) { ++ hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) { + if (entry_ino == (u32)(long)dentry->d_fsdata) { + dentry->d_fsdata = (void *)inode->i_ino; + break; +diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c +index a7be57e39be7..11c6cddff1b9 100644 +--- a/fs/autofs4/expire.c ++++ b/fs/autofs4/expire.c +@@ -91,7 +91,7 @@ static struct dentry *get_next_positive_subdir(struct dentry *prev, + spin_lock(&root->d_lock); + + if (prev) +- next = prev->d_u.d_child.next; ++ next = prev->d_child.next; + else { + prev = dget_dlock(root); + next = prev->d_subdirs.next; +@@ -105,13 +105,13 @@ cont: + return NULL; + } + +- q = list_entry(next, struct dentry, d_u.d_child); ++ q = list_entry(next, struct dentry, d_child); + + spin_lock_nested(&q->d_lock, DENTRY_D_LOCK_NESTED); + /* Already gone or negative dentry (under construction) - try next */ + if (!d_count(q) || !simple_positive(q)) { + spin_unlock(&q->d_lock); +- next = q->d_u.d_child.next; ++ next = q->d_child.next; + goto cont; + } + dget_dlock(q); +@@ -161,13 +161,13 @@ again: + goto relock; + } + spin_unlock(&p->d_lock); +- next = p->d_u.d_child.next; ++ next = p->d_child.next; + p = parent; + if (next != &parent->d_subdirs) + break; + } + } +- ret = list_entry(next, struct dentry, d_u.d_child); ++ ret = list_entry(next, struct dentry, d_child); + + spin_lock_nested(&ret->d_lock, DENTRY_D_LOCK_NESTED); + /* Negative dentry - try next */ +@@ -460,7 +460,7 @@ found: + spin_lock(&sbi->lookup_lock); + spin_lock(&expired->d_parent->d_lock); + spin_lock_nested(&expired->d_lock, DENTRY_D_LOCK_NESTED); +- list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); ++ list_move(&expired->d_parent->d_subdirs, &expired->d_child); + spin_unlock(&expired->d_lock); + spin_unlock(&expired->d_parent->d_lock); + spin_unlock(&sbi->lookup_lock); +diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c +index cdb25ebccc4c..38a9e0fa5177 100644 +--- a/fs/autofs4/root.c ++++ b/fs/autofs4/root.c +@@ -659,7 +659,7 @@ static void autofs_clear_leaf_automount_flags(struct dentry *dentry) + /* only consider parents below dentrys in the root */ + if (IS_ROOT(parent->d_parent)) + return; +- d_child = &dentry->d_u.d_child; ++ d_child = &dentry->d_child; + /* Set parent managed if it's becoming empty */ + if (d_child->next == &parent->d_subdirs && + d_child->prev == &parent->d_subdirs) +diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c +index c29d6ae68874..51ea03313df9 100644 +--- a/fs/ceph/dir.c ++++ b/fs/ceph/dir.c +@@ -111,7 +111,7 @@ static int fpos_cmp(loff_t l, loff_t r) + /* + * When possible, we try to satisfy a readdir by peeking at the + * dcache. We make this work by carefully ordering dentries on +- * d_u.d_child when we initially get results back from the MDS, and ++ * d_child when we initially get results back from the MDS, and + * falling back to a "normal" sync readdir if any dentries in the dir + * are dropped. + * +@@ -147,11 +147,11 @@ static int __dcache_readdir(struct file *file, struct dir_context *ctx, + p = parent->d_subdirs.prev; + dout(" initial p %p/%p\n", p->prev, p->next); + } else { +- p = last->d_u.d_child.prev; ++ p = last->d_child.prev; + } + + more: +- dentry = list_entry(p, struct dentry, d_u.d_child); ++ dentry = list_entry(p, struct dentry, d_child); + di = ceph_dentry(dentry); + while (1) { + dout(" p %p/%p %s d_subdirs %p/%p\n", p->prev, p->next, +@@ -174,7 +174,7 @@ more: + !dentry->d_inode ? " null" : ""); + spin_unlock(&dentry->d_lock); + p = p->prev; +- dentry = list_entry(p, struct dentry, d_u.d_child); ++ dentry = list_entry(p, struct dentry, d_child); + di = ceph_dentry(dentry); + } + +diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c +index 04c89c266cec..c3e103ff18bd 100644 +--- a/fs/ceph/inode.c ++++ b/fs/ceph/inode.c +@@ -1399,7 +1399,7 @@ retry_lookup: + /* reorder parent's d_subdirs */ + spin_lock(&parent->d_lock); + spin_lock_nested(&dn->d_lock, DENTRY_D_LOCK_NESTED); +- list_move(&dn->d_u.d_child, &parent->d_subdirs); ++ list_move(&dn->d_child, &parent->d_subdirs); + spin_unlock(&dn->d_lock); + spin_unlock(&parent->d_lock); + } +diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c +index 7899a40465b3..6d1dd0942937 100644 +--- a/fs/cifs/inode.c ++++ b/fs/cifs/inode.c +@@ -887,7 +887,7 @@ inode_has_hashed_dentries(struct inode *inode) + struct dentry *dentry; + + spin_lock(&inode->i_lock); +- hlist_for_each_entry(dentry, &inode->i_dentry, d_alias) { ++ hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) { + if (!d_unhashed(dentry) || IS_ROOT(dentry)) { + spin_unlock(&inode->i_lock); + return true; +diff --git a/fs/coda/cache.c b/fs/coda/cache.c +index 278f8fdeb9ef..46ee6f238985 100644 +--- a/fs/coda/cache.c ++++ b/fs/coda/cache.c +@@ -92,7 +92,7 @@ static void coda_flag_children(struct dentry *parent, int flag) + struct dentry *de; + + spin_lock(&parent->d_lock); +- list_for_each_entry(de, &parent->d_subdirs, d_u.d_child) { ++ list_for_each_entry(de, &parent->d_subdirs, d_child) { + /* don't know what to do with negative dentries */ + if (de->d_inode ) + coda_flag_inode(de->d_inode, flag); +diff --git a/fs/dcache.c b/fs/dcache.c +index 34b40be8af11..8d7c2b34cb3f 100644 +--- a/fs/dcache.c ++++ b/fs/dcache.c +@@ -44,7 +44,7 @@ + /* + * Usage: + * dcache->d_inode->i_lock protects: +- * - i_dentry, d_alias, d_inode of aliases ++ * - i_dentry, d_u.d_alias, d_inode of aliases + * dcache_hash_bucket lock protects: + * - the dcache hash table + * s_anon bl list spinlock protects: +@@ -59,7 +59,7 @@ + * - d_unhashed() + * - d_parent and d_subdirs + * - childrens' d_child and d_parent +- * - d_alias, d_inode ++ * - d_u.d_alias, d_inode + * + * Ordering: + * dentry->d_inode->i_lock +@@ -239,7 +239,6 @@ static void __d_free(struct rcu_head *head) + { + struct dentry *dentry = container_of(head, struct dentry, d_u.d_rcu); + +- WARN_ON(!hlist_unhashed(&dentry->d_alias)); + if (dname_external(dentry)) + kfree(dentry->d_name.name); + kmem_cache_free(dentry_cache, dentry); +@@ -247,6 +246,7 @@ static void __d_free(struct rcu_head *head) + + static void dentry_free(struct dentry *dentry) + { ++ WARN_ON(!hlist_unhashed(&dentry->d_u.d_alias)); + /* if dentry was never visible to RCU, immediate free is OK */ + if (!(dentry->d_flags & DCACHE_RCUACCESS)) + __d_free(&dentry->d_u.d_rcu); +@@ -280,7 +280,7 @@ static void dentry_iput(struct dentry * dentry) + struct inode *inode = dentry->d_inode; + if (inode) { + dentry->d_inode = NULL; +- hlist_del_init(&dentry->d_alias); ++ hlist_del_init(&dentry->d_u.d_alias); + spin_unlock(&dentry->d_lock); + spin_unlock(&inode->i_lock); + if (!inode->i_nlink) +@@ -305,7 +305,7 @@ static void dentry_unlink_inode(struct dentry * dentry) + struct inode *inode = dentry->d_inode; + __d_clear_type(dentry); + dentry->d_inode = NULL; +- hlist_del_init(&dentry->d_alias); ++ hlist_del_init(&dentry->d_u.d_alias); + dentry_rcuwalk_barrier(dentry); + spin_unlock(&dentry->d_lock); + spin_unlock(&inode->i_lock); +@@ -465,7 +465,7 @@ static void __dentry_kill(struct dentry *dentry) + } + /* if it was on the hash then remove it */ + __d_drop(dentry); +- list_del(&dentry->d_u.d_child); ++ list_del(&dentry->d_child); + /* + * Inform d_walk() that we are no longer attached to the + * dentry tree +@@ -746,7 +746,7 @@ static struct dentry *__d_find_alias(struct inode *inode) + + again: + discon_alias = NULL; +- hlist_for_each_entry(alias, &inode->i_dentry, d_alias) { ++ hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) { + spin_lock(&alias->d_lock); + if (S_ISDIR(inode->i_mode) || !d_unhashed(alias)) { + if (IS_ROOT(alias) && +@@ -796,7 +796,7 @@ void d_prune_aliases(struct inode *inode) + struct dentry *dentry; + restart: + spin_lock(&inode->i_lock); +- hlist_for_each_entry(dentry, &inode->i_dentry, d_alias) { ++ hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) { + spin_lock(&dentry->d_lock); + if (!dentry->d_lockref.count) { + /* +@@ -1081,7 +1081,7 @@ repeat: + resume: + while (next != &this_parent->d_subdirs) { + struct list_head *tmp = next; +- struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child); ++ struct dentry *dentry = list_entry(tmp, struct dentry, d_child); + next = tmp->next; + + spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); +@@ -1133,7 +1133,7 @@ resume: + goto rename_retry; + } + rcu_read_unlock(); +- next = child->d_u.d_child.next; ++ next = child->d_child.next; + goto resume; + } + if (need_seqretry(&rename_lock, seq)) { +@@ -1468,8 +1468,8 @@ struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name) + INIT_HLIST_BL_NODE(&dentry->d_hash); + INIT_LIST_HEAD(&dentry->d_lru); + INIT_LIST_HEAD(&dentry->d_subdirs); +- INIT_HLIST_NODE(&dentry->d_alias); +- INIT_LIST_HEAD(&dentry->d_u.d_child); ++ INIT_HLIST_NODE(&dentry->d_u.d_alias); ++ INIT_LIST_HEAD(&dentry->d_child); + d_set_d_op(dentry, dentry->d_sb->s_d_op); + + this_cpu_inc(nr_dentry); +@@ -1499,7 +1499,7 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name) + */ + __dget_dlock(parent); + dentry->d_parent = parent; +- list_add(&dentry->d_u.d_child, &parent->d_subdirs); ++ list_add(&dentry->d_child, &parent->d_subdirs); + spin_unlock(&parent->d_lock); + + return dentry; +@@ -1592,7 +1592,7 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode) + spin_lock(&dentry->d_lock); + __d_set_type(dentry, add_flags); + if (inode) +- hlist_add_head(&dentry->d_alias, &inode->i_dentry); ++ hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry); + dentry->d_inode = inode; + dentry_rcuwalk_barrier(dentry); + spin_unlock(&dentry->d_lock); +@@ -1616,7 +1616,7 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode) + + void d_instantiate(struct dentry *entry, struct inode * inode) + { +- BUG_ON(!hlist_unhashed(&entry->d_alias)); ++ BUG_ON(!hlist_unhashed(&entry->d_u.d_alias)); + if (inode) + spin_lock(&inode->i_lock); + __d_instantiate(entry, inode); +@@ -1655,7 +1655,7 @@ static struct dentry *__d_instantiate_unique(struct dentry *entry, + return NULL; + } + +- hlist_for_each_entry(alias, &inode->i_dentry, d_alias) { ++ hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) { + /* + * Don't need alias->d_lock here, because aliases with + * d_parent == entry->d_parent are not subject to name or +@@ -1681,7 +1681,7 @@ struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode) + { + struct dentry *result; + +- BUG_ON(!hlist_unhashed(&entry->d_alias)); ++ BUG_ON(!hlist_unhashed(&entry->d_u.d_alias)); + + if (inode) + spin_lock(&inode->i_lock); +@@ -1712,7 +1712,7 @@ EXPORT_SYMBOL(d_instantiate_unique); + */ + int d_instantiate_no_diralias(struct dentry *entry, struct inode *inode) + { +- BUG_ON(!hlist_unhashed(&entry->d_alias)); ++ BUG_ON(!hlist_unhashed(&entry->d_u.d_alias)); + + spin_lock(&inode->i_lock); + if (S_ISDIR(inode->i_mode) && !hlist_empty(&inode->i_dentry)) { +@@ -1751,7 +1751,7 @@ static struct dentry * __d_find_any_alias(struct inode *inode) + + if (hlist_empty(&inode->i_dentry)) + return NULL; +- alias = hlist_entry(inode->i_dentry.first, struct dentry, d_alias); ++ alias = hlist_entry(inode->i_dentry.first, struct dentry, d_u.d_alias); + __dget(alias); + return alias; + } +@@ -1813,7 +1813,7 @@ static struct dentry *__d_obtain_alias(struct inode *inode, int disconnected) + spin_lock(&tmp->d_lock); + tmp->d_inode = inode; + tmp->d_flags |= add_flags; +- hlist_add_head(&tmp->d_alias, &inode->i_dentry); ++ hlist_add_head(&tmp->d_u.d_alias, &inode->i_dentry); + hlist_bl_lock(&tmp->d_sb->s_anon); + hlist_bl_add_head(&tmp->d_hash, &tmp->d_sb->s_anon); + hlist_bl_unlock(&tmp->d_sb->s_anon); +@@ -2248,7 +2248,7 @@ int d_validate(struct dentry *dentry, struct dentry *dparent) + struct dentry *child; + + spin_lock(&dparent->d_lock); +- list_for_each_entry(child, &dparent->d_subdirs, d_u.d_child) { ++ list_for_each_entry(child, &dparent->d_subdirs, d_child) { + if (dentry == child) { + spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); + __dget_dlock(dentry); +@@ -2525,13 +2525,13 @@ static void __d_move(struct dentry *dentry, struct dentry *target, + /* splicing a tree */ + dentry->d_parent = target->d_parent; + target->d_parent = target; +- list_del_init(&target->d_u.d_child); +- list_move(&dentry->d_u.d_child, &dentry->d_parent->d_subdirs); ++ list_del_init(&target->d_child); ++ list_move(&dentry->d_child, &dentry->d_parent->d_subdirs); + } else { + /* swapping two dentries */ + swap(dentry->d_parent, target->d_parent); +- list_move(&target->d_u.d_child, &target->d_parent->d_subdirs); +- list_move(&dentry->d_u.d_child, &dentry->d_parent->d_subdirs); ++ list_move(&target->d_child, &target->d_parent->d_subdirs); ++ list_move(&dentry->d_child, &dentry->d_parent->d_subdirs); + if (exchange) + fsnotify_d_move(target); + fsnotify_d_move(dentry); +@@ -3322,7 +3322,7 @@ void d_tmpfile(struct dentry *dentry, struct inode *inode) + { + inode_dec_link_count(inode); + BUG_ON(dentry->d_name.name != dentry->d_iname || +- !hlist_unhashed(&dentry->d_alias) || ++ !hlist_unhashed(&dentry->d_u.d_alias) || + !d_unlinked(dentry)); + spin_lock(&dentry->d_parent->d_lock); + spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); +diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c +index 1e3b99d3db0d..05f2960ed7c3 100644 +--- a/fs/debugfs/inode.c ++++ b/fs/debugfs/inode.c +@@ -553,7 +553,7 @@ void debugfs_remove_recursive(struct dentry *dentry) + * use the d_u.d_child as the rcu head and corrupt this list. + */ + spin_lock(&parent->d_lock); +- list_for_each_entry(child, &parent->d_subdirs, d_u.d_child) { ++ list_for_each_entry(child, &parent->d_subdirs, d_child) { + if (!debugfs_positive(child)) + continue; + +diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c +index b01fbfb51f43..a3aa6baad1a1 100644 +--- a/fs/exportfs/expfs.c ++++ b/fs/exportfs/expfs.c +@@ -50,7 +50,7 @@ find_acceptable_alias(struct dentry *result, + + inode = result->d_inode; + spin_lock(&inode->i_lock); +- hlist_for_each_entry(dentry, &inode->i_dentry, d_alias) { ++ hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) { + dget(dentry); + spin_unlock(&inode->i_lock); + if (toput) +diff --git a/fs/libfs.c b/fs/libfs.c +index 88e3e00e2eca..e801b983b46b 100644 +--- a/fs/libfs.c ++++ b/fs/libfs.c +@@ -114,18 +114,18 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int whence) + + spin_lock(&dentry->d_lock); + /* d_lock not required for cursor */ +- list_del(&cursor->d_u.d_child); ++ list_del(&cursor->d_child); + p = dentry->d_subdirs.next; + while (n && p != &dentry->d_subdirs) { + struct dentry *next; +- next = list_entry(p, struct dentry, d_u.d_child); ++ next = list_entry(p, struct dentry, d_child); + spin_lock_nested(&next->d_lock, DENTRY_D_LOCK_NESTED); + if (simple_positive(next)) + n--; + spin_unlock(&next->d_lock); + p = p->next; + } +- list_add_tail(&cursor->d_u.d_child, p); ++ list_add_tail(&cursor->d_child, p); + spin_unlock(&dentry->d_lock); + } + } +@@ -150,7 +150,7 @@ int dcache_readdir(struct file *file, struct dir_context *ctx) + { + struct dentry *dentry = file->f_path.dentry; + struct dentry *cursor = file->private_data; +- struct list_head *p, *q = &cursor->d_u.d_child; ++ struct list_head *p, *q = &cursor->d_child; + + if (!dir_emit_dots(file, ctx)) + return 0; +@@ -159,7 +159,7 @@ int dcache_readdir(struct file *file, struct dir_context *ctx) + list_move(q, &dentry->d_subdirs); + + for (p = q->next; p != &dentry->d_subdirs; p = p->next) { +- struct dentry *next = list_entry(p, struct dentry, d_u.d_child); ++ struct dentry *next = list_entry(p, struct dentry, d_child); + spin_lock_nested(&next->d_lock, DENTRY_D_LOCK_NESTED); + if (!simple_positive(next)) { + spin_unlock(&next->d_lock); +@@ -287,7 +287,7 @@ int simple_empty(struct dentry *dentry) + int ret = 0; + + spin_lock(&dentry->d_lock); +- list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) { ++ list_for_each_entry(child, &dentry->d_subdirs, d_child) { + spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED); + if (simple_positive(child)) { + spin_unlock(&child->d_lock); +diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c +index 08b8ea8c353e..3a8ed0fb07be 100644 +--- a/fs/ncpfs/dir.c ++++ b/fs/ncpfs/dir.c +@@ -406,7 +406,7 @@ ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos) + spin_lock(&parent->d_lock); + next = parent->d_subdirs.next; + while (next != &parent->d_subdirs) { +- dent = list_entry(next, struct dentry, d_u.d_child); ++ dent = list_entry(next, struct dentry, d_child); + if ((unsigned long)dent->d_fsdata == fpos) { + if (dent->d_inode) + dget(dent); +diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h +index 32c06587351a..6d5e7c56c79d 100644 +--- a/fs/ncpfs/ncplib_kernel.h ++++ b/fs/ncpfs/ncplib_kernel.h +@@ -194,7 +194,7 @@ ncp_renew_dentries(struct dentry *parent) + spin_lock(&parent->d_lock); + next = parent->d_subdirs.next; + while (next != &parent->d_subdirs) { +- dentry = list_entry(next, struct dentry, d_u.d_child); ++ dentry = list_entry(next, struct dentry, d_child); + + if (dentry->d_fsdata == NULL) + ncp_age_dentry(server, dentry); +@@ -216,7 +216,7 @@ ncp_invalidate_dircache_entries(struct dentry *parent) + spin_lock(&parent->d_lock); + next = parent->d_subdirs.next; + while (next != &parent->d_subdirs) { +- dentry = list_entry(next, struct dentry, d_u.d_child); ++ dentry = list_entry(next, struct dentry, d_child); + dentry->d_fsdata = NULL; + ncp_age_dentry(server, dentry); + next = next->next; +diff --git a/fs/nfs/getroot.c b/fs/nfs/getroot.c +index 880618a8b048..ebc6a0add5ae 100644 +--- a/fs/nfs/getroot.c ++++ b/fs/nfs/getroot.c +@@ -58,7 +58,7 @@ static int nfs_superblock_set_dummy_root(struct super_block *sb, struct inode *i + */ + spin_lock(&sb->s_root->d_inode->i_lock); + spin_lock(&sb->s_root->d_lock); +- hlist_del_init(&sb->s_root->d_alias); ++ hlist_del_init(&sb->s_root->d_u.d_alias); + spin_unlock(&sb->s_root->d_lock); + spin_unlock(&sb->s_root->d_inode->i_lock); + } +diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c +index 9d3e9c50066a..700129940c6e 100644 +--- a/fs/notify/fsnotify.c ++++ b/fs/notify/fsnotify.c +@@ -63,14 +63,14 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode) + spin_lock(&inode->i_lock); + /* run all of the dentries associated with this inode. Since this is a + * directory, there damn well better only be one item on this list */ +- hlist_for_each_entry(alias, &inode->i_dentry, d_alias) { ++ hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) { + struct dentry *child; + + /* run all of the children of the original inode and fix their + * d_flags to indicate parental interest (their parent is the + * original inode) */ + spin_lock(&alias->d_lock); +- list_for_each_entry(child, &alias->d_subdirs, d_u.d_child) { ++ list_for_each_entry(child, &alias->d_subdirs, d_child) { + if (!child->d_inode) + continue; + +diff --git a/fs/ocfs2/dcache.c b/fs/ocfs2/dcache.c +index e2e05a106beb..92edcfc23c1c 100644 +--- a/fs/ocfs2/dcache.c ++++ b/fs/ocfs2/dcache.c +@@ -172,7 +172,7 @@ struct dentry *ocfs2_find_local_alias(struct inode *inode, + struct dentry *dentry; + + spin_lock(&inode->i_lock); +- hlist_for_each_entry(dentry, &inode->i_dentry, d_alias) { ++ hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) { + spin_lock(&dentry->d_lock); + if (ocfs2_match_dentry(dentry, parent_blkno, skip_unhashed)) { + trace_ocfs2_find_local_alias(dentry->d_name.len, +diff --git a/include/linux/dcache.h b/include/linux/dcache.h +index 75a227cc7ce2..82b5d1c2b856 100644 +--- a/include/linux/dcache.h ++++ b/include/linux/dcache.h +@@ -125,15 +125,15 @@ struct dentry { + void *d_fsdata; /* fs-specific data */ + + struct list_head d_lru; /* LRU list */ ++ struct list_head d_child; /* child of parent list */ ++ struct list_head d_subdirs; /* our children */ + /* +- * d_child and d_rcu can share memory ++ * d_alias and d_rcu can share memory + */ + union { +- struct list_head d_child; /* child of parent list */ ++ struct hlist_node d_alias; /* inode alias list */ + struct rcu_head d_rcu; + } d_u; +- struct list_head d_subdirs; /* our children */ +- struct hlist_node d_alias; /* inode alias list */ + }; + + /* +diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c +index 15209335888d..09acba6e908a 100644 +--- a/kernel/trace/trace.c ++++ b/kernel/trace/trace.c +@@ -6411,7 +6411,7 @@ static int instance_mkdir (struct inode *inode, struct dentry *dentry, umode_t m + int ret; + + /* Paranoid: Make sure the parent is the "instances" directory */ +- parent = hlist_entry(inode->i_dentry.first, struct dentry, d_alias); ++ parent = hlist_entry(inode->i_dentry.first, struct dentry, d_u.d_alias); + if (WARN_ON_ONCE(parent != trace_instance_dir)) + return -ENOENT; + +@@ -6438,7 +6438,7 @@ static int instance_rmdir(struct inode *inode, struct dentry *dentry) + int ret; + + /* Paranoid: Make sure the parent is the "instances" directory */ +- parent = hlist_entry(inode->i_dentry.first, struct dentry, d_alias); ++ parent = hlist_entry(inode->i_dentry.first, struct dentry, d_u.d_alias); + if (WARN_ON_ONCE(parent != trace_instance_dir)) + return -ENOENT; + +diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c +index ef06ce7e9cf8..85f9d33b4ad8 100644 +--- a/kernel/trace/trace_events.c ++++ b/kernel/trace/trace_events.c +@@ -461,7 +461,7 @@ static void remove_event_file_dir(struct ftrace_event_file *file) + + if (dir) { + spin_lock(&dir->d_lock); /* probably unneeded */ +- list_for_each_entry(child, &dir->d_subdirs, d_u.d_child) { ++ list_for_each_entry(child, &dir->d_subdirs, d_child) { + if (child->d_inode) /* probably unneeded */ + child->d_inode->i_private = NULL; + } +diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c +index c71737f6d1cc..33db1ad4fd10 100644 +--- a/security/selinux/selinuxfs.c ++++ b/security/selinux/selinuxfs.c +@@ -1200,7 +1200,7 @@ static void sel_remove_entries(struct dentry *de) + spin_lock(&de->d_lock); + node = de->d_subdirs.next; + while (node != &de->d_subdirs) { +- struct dentry *d = list_entry(node, struct dentry, d_u.d_child); ++ struct dentry *d = list_entry(node, struct dentry, d_child); + + spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED); + list_del_init(node); +@@ -1674,12 +1674,12 @@ static void sel_remove_classes(void) + + list_for_each(class_node, &class_dir->d_subdirs) { + struct dentry *class_subdir = list_entry(class_node, +- struct dentry, d_u.d_child); ++ struct dentry, d_child); + struct list_head *class_subdir_node; + + list_for_each(class_subdir_node, &class_subdir->d_subdirs) { + struct dentry *d = list_entry(class_subdir_node, +- struct dentry, d_u.d_child); ++ struct dentry, d_child); + + if (d->d_inode) + if (d->d_inode->i_mode & S_IFDIR) +-- +2.1.0 + |