diff options
| author | Al Viro <viro@zeniv.linux.org.uk> | 2016-06-25 23:33:49 -0400 | 
|---|---|---|
| committer | Al Viro <viro@zeniv.linux.org.uk> | 2016-06-30 23:30:44 -0400 | 
| commit | d4c91a8f7e5514a1e9cd37b453fda0dedfa8045d (patch) | |
| tree | b482299b94b6ad6561bb27215f71b798a7073188 | |
| parent | ae0a843c740b4e63684eefae96097cf62d9b7a14 (diff) | |
new helper: d_same_name()
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
| -rw-r--r-- | fs/dcache.c | 127 | 
1 files changed, 36 insertions, 91 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index dc37c0238b46..040c2586d483 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2066,42 +2066,19 @@ struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode,  }  EXPORT_SYMBOL(d_add_ci); -/* - * Do the slow-case of the dentry name compare. - * - * Unlike the dentry_cmp() function, we need to atomically - * load the name and length information, so that the - * filesystem can rely on them, and can use the 'name' and - * 'len' information without worrying about walking off the - * end of memory etc. - * - * Thus the read_seqcount_retry() and the "duplicate" info - * in arguments (the low-level filesystem should not look - * at the dentry inode or name contents directly, since - * rename can change them while we're in RCU mode). - */ -enum slow_d_compare { -	D_COMP_OK, -	D_COMP_NOMATCH, -	D_COMP_SEQRETRY, -}; -static noinline enum slow_d_compare slow_dentry_cmp( -		const struct dentry *parent, -		struct dentry *dentry, -		unsigned int seq, -		const struct qstr *name) +static inline bool d_same_name(const struct dentry *dentry, +				const struct dentry *parent, +				const struct qstr *name)  { -	int tlen = dentry->d_name.len; -	const char *tname = dentry->d_name.name; - -	if (read_seqcount_retry(&dentry->d_seq, seq)) { -		cpu_relax(); -		return D_COMP_SEQRETRY; +	if (likely(!(parent->d_flags & DCACHE_OP_COMPARE))) { +		if (dentry->d_name.len != name->len) +			return false; +		return dentry_cmp(dentry, name->name, name->len) == 0;  	} -	if (parent->d_op->d_compare(parent, dentry, tlen, tname, name)) -		return D_COMP_NOMATCH; -	return D_COMP_OK; +	return parent->d_op->d_compare(parent, dentry, +				       dentry->d_name.len, dentry->d_name.name, +				       name) == 0;  }  /** @@ -2180,6 +2157,9 @@ seqretry:  		 * dentry compare, we will do seqretries until it is stable,  		 * and if we end up with a successful lookup, we actually  		 * want to exit RCU lookup anyway. +		 * +		 * Note that raw_seqcount_begin still *does* smp_rmb(), so +		 * we are still guaranteed NUL-termination of ->d_name.name.  		 */  		seq = raw_seqcount_begin(&dentry->d_seq);  		if (dentry->d_parent != parent) @@ -2188,24 +2168,28 @@ seqretry:  			continue;  		if (unlikely(parent->d_flags & DCACHE_OP_COMPARE)) { +			int tlen; +			const char *tname;  			if (dentry->d_name.hash != hashlen_hash(hashlen))  				continue; -			*seqp = seq; -			switch (slow_dentry_cmp(parent, dentry, seq, name)) { -			case D_COMP_OK: -				return dentry; -			case D_COMP_NOMATCH: -				continue; -			default: +			tlen = dentry->d_name.len; +			tname = dentry->d_name.name; +			/* we want a consistent (name,len) pair */ +			if (read_seqcount_retry(&dentry->d_seq, seq)) { +				cpu_relax();  				goto seqretry;  			} +			if (parent->d_op->d_compare(parent, dentry, +						    tlen, tname, name) != 0) +				continue; +		} else { +			if (dentry->d_name.hash_len != hashlen) +				continue; +			if (dentry_cmp(dentry, str, hashlen_len(hashlen)) != 0) +				continue;  		} - -		if (dentry->d_name.hash_len != hashlen) -			continue;  		*seqp = seq; -		if (!dentry_cmp(dentry, str, hashlen_len(hashlen))) -			return dentry; +		return dentry;  	}  	return NULL;  } @@ -2253,9 +2237,7 @@ EXPORT_SYMBOL(d_lookup);   */  struct dentry *__d_lookup(const struct dentry *parent, const struct qstr *name)  { -	unsigned int len = name->len;  	unsigned int hash = name->hash; -	const unsigned char *str = name->name;  	struct hlist_bl_head *b = d_hash(parent, hash);  	struct hlist_bl_node *node;  	struct dentry *found = NULL; @@ -2294,21 +2276,8 @@ struct dentry *__d_lookup(const struct dentry *parent, const struct qstr *name)  		if (d_unhashed(dentry))  			goto next; -		/* -		 * It is safe to compare names since d_move() cannot -		 * change the qstr (protected by d_lock). -		 */ -		if (parent->d_flags & DCACHE_OP_COMPARE) { -			int tlen = dentry->d_name.len; -			const char *tname = dentry->d_name.name; -			if (parent->d_op->d_compare(parent, dentry, tlen, tname, name)) -				goto next; -		} else { -			if (dentry->d_name.len != len) -				goto next; -			if (dentry_cmp(dentry, str, len)) -				goto next; -		} +		if (!d_same_name(dentry, parent, name)) +			goto next;  		dentry->d_lockref.count++;  		found = dentry; @@ -2461,9 +2430,7 @@ struct dentry *d_alloc_parallel(struct dentry *parent,  				const struct qstr *name,  				wait_queue_head_t *wq)  { -	unsigned int len = name->len;  	unsigned int hash = name->hash; -	const unsigned char *str = name->name;  	struct hlist_bl_head *b = in_lookup_hash(parent, hash);  	struct hlist_bl_node *node;  	struct dentry *new = d_alloc(parent, name); @@ -2514,17 +2481,8 @@ retry:  			continue;  		if (dentry->d_parent != parent)  			continue; -		if (parent->d_flags & DCACHE_OP_COMPARE) { -			int tlen = dentry->d_name.len; -			const char *tname = dentry->d_name.name; -			if (parent->d_op->d_compare(parent, dentry, tlen, tname, name)) -				continue; -		} else { -			if (dentry->d_name.len != len) -				continue; -			if (dentry_cmp(dentry, str, len)) -				continue; -		} +		if (!d_same_name(dentry, parent, name)) +			continue;  		hlist_bl_unlock(b);  		/* now we can try to grab a reference */  		if (!lockref_get_not_dead(&dentry->d_lockref)) { @@ -2551,17 +2509,8 @@ retry:  			goto mismatch;  		if (unlikely(d_unhashed(dentry)))  			goto mismatch; -		if (parent->d_flags & DCACHE_OP_COMPARE) { -			int tlen = dentry->d_name.len; -			const char *tname = dentry->d_name.name; -			if (parent->d_op->d_compare(parent, dentry, tlen, tname, name)) -				goto mismatch; -		} else { -			if (unlikely(dentry->d_name.len != len)) -				goto mismatch; -			if (unlikely(dentry_cmp(dentry, str, len))) -				goto mismatch; -		} +		if (unlikely(!d_same_name(dentry, parent, name))) +			goto mismatch;  		/* OK, it *is* a hashed match; return it */  		spin_unlock(&dentry->d_lock);  		dput(new); @@ -2657,8 +2606,6 @@ EXPORT_SYMBOL(d_add);  struct dentry *d_exact_alias(struct dentry *entry, struct inode *inode)  {  	struct dentry *alias; -	int len = entry->d_name.len; -	const char *name = entry->d_name.name;  	unsigned int hash = entry->d_name.hash;  	spin_lock(&inode->i_lock); @@ -2672,9 +2619,7 @@ struct dentry *d_exact_alias(struct dentry *entry, struct inode *inode)  			continue;  		if (alias->d_parent != entry->d_parent)  			continue; -		if (alias->d_name.len != len) -			continue; -		if (dentry_cmp(alias, name, len)) +		if (!d_same_name(alias, entry->d_parent, &entry->d_name))  			continue;  		spin_lock(&alias->d_lock);  		if (!d_unhashed(alias)) {  | 
