Commit 95a4ccbb authored by Al Viro's avatar Al Viro
Browse files

dissolve external_name.u into separate members



... and document the constraints on the layout.  Kept separate from
the previous commit to keep the noise separate from actual changes.
The reason for explicit __aligned() on ->name[] rather than relying
upon the alignment of the previous field is that the previous iteration
of that commit tried to save 4 bytes on 64bit by eliminating a hole
in there, which broke the assumptions in dentry_string_cmp().
Better spell it out and avoid the temptation for the future...

Reviewed-by: default avatarJeff Layton <jlayton@kernel.org>
Reviewed-by: default avatarJan Kara <jack@suse.cz>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 1c9be84a
Loading
Loading
Loading
Loading
+17 −13
Original line number Diff line number Diff line
@@ -295,12 +295,16 @@ static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *c
	return dentry_string_cmp(cs, ct, tcount);
}

/*
 * long names are allocated separately from dentry and never modified.
 * Refcounted, freeing is RCU-delayed.  See take_dentry_name_snapshot()
 * for the reason why ->count and ->head can't be combined into a union.
 * dentry_string_cmp() relies upon ->name[] being word-aligned.
 */
struct external_name {
	struct {
		atomic_t count;		// ->count and ->head can't be combined
		struct rcu_head head;	// see take_dentry_name_snapshot()
	} u;
	unsigned char name[];
	atomic_t count;
	struct rcu_head head;
	unsigned char name[] __aligned(sizeof(unsigned long));
};

static inline struct external_name *external_name(struct dentry *dentry)
@@ -344,7 +348,7 @@ void take_dentry_name_snapshot(struct name_snapshot *name, struct dentry *dentry
		struct external_name *p;
		p = container_of(s, struct external_name, name[0]);
		// get a valid reference
		if (unlikely(!atomic_inc_not_zero(&p->u.count)))
		if (unlikely(!atomic_inc_not_zero(&p->count)))
			goto retry;
		name->name.name = s;
	}
@@ -361,8 +365,8 @@ void release_dentry_name_snapshot(struct name_snapshot *name)
	if (unlikely(name->name.name != name->inline_name.string)) {
		struct external_name *p;
		p = container_of(name->name.name, struct external_name, name[0]);
		if (unlikely(atomic_dec_and_test(&p->u.count)))
			kfree_rcu(p, u.head);
		if (unlikely(atomic_dec_and_test(&p->count)))
			kfree_rcu(p, head);
	}
}
EXPORT_SYMBOL(release_dentry_name_snapshot);
@@ -400,7 +404,7 @@ static void dentry_free(struct dentry *dentry)
	WARN_ON(!hlist_unhashed(&dentry->d_u.d_alias));
	if (unlikely(dname_external(dentry))) {
		struct external_name *p = external_name(dentry);
		if (likely(atomic_dec_and_test(&p->u.count))) {
		if (likely(atomic_dec_and_test(&p->count))) {
			call_rcu(&dentry->d_u.d_rcu, __d_free_external);
			return;
		}
@@ -1681,7 +1685,7 @@ static struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
			kmem_cache_free(dentry_cache, dentry); 
			return NULL;
		}
		atomic_set(&p->u.count, 1);
		atomic_set(&p->count, 1);
		dname = p->name;
	} else  {
		dname = dentry->d_shortname.string;
@@ -2774,15 +2778,15 @@ static void copy_name(struct dentry *dentry, struct dentry *target)
	if (unlikely(dname_external(dentry)))
		old_name = external_name(dentry);
	if (unlikely(dname_external(target))) {
		atomic_inc(&external_name(target)->u.count);
		atomic_inc(&external_name(target)->count);
		dentry->d_name = target->d_name;
	} else {
		dentry->d_shortname = target->d_shortname;
		dentry->d_name.name = dentry->d_shortname.string;
		dentry->d_name.hash_len = target->d_name.hash_len;
	}
	if (old_name && likely(atomic_dec_and_test(&old_name->u.count)))
		kfree_rcu(old_name, u.head);
	if (old_name && likely(atomic_dec_and_test(&old_name->count)))
		kfree_rcu(old_name, head);
}

/*