Negative dentries are still invalidated, triggering a lookup in the disk. Signed-off-by: Gabriel Krisman Bertazi <krisman@xxxxxxxxxxxxxxx> --- fs/dcache.c | 43 +++++++++++++++++++++++++++++++++--------- fs/namei.c | 5 +++-- include/linux/dcache.h | 8 ++++++-- 3 files changed, 43 insertions(+), 13 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 7c38f39958bc..4bd28d7caa53 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2049,6 +2049,16 @@ struct dentry *d_obtain_root(struct inode *inode) } EXPORT_SYMBOL(d_obtain_root); +static inline bool d_same_name_ci(const struct dentry *dentry, + const struct dentry *parent, + const struct qstr *name) +{ + return parent->d_op->d_compare_ci(dentry, + dentry->d_name.len, + dentry->d_name.name, + name) == 0; +} + /** * d_add_ci - lookup or allocate new dentry with case-exact name * @inode: the inode case-insensitive lookup has found @@ -2122,6 +2132,7 @@ static inline bool d_same_name(const struct dentry *dentry, * @parent: parent dentry * @name: qstr of name we wish to find * @seqp: returns d_seq value at the point where the dentry was found + * @ci_lookup: Whether d_lookup_rcu should do a CI lookup * Returns: dentry, or NULL * * __d_lookup_rcu is the dcache lookup function for rcu-walk name @@ -2148,7 +2159,7 @@ static inline bool d_same_name(const struct dentry *dentry, */ struct dentry *__d_lookup_rcu(const struct dentry *parent, const struct qstr *name, - unsigned *seqp) + unsigned *seqp, int ci_lookup) { u64 hashlen = name->hash_len; const unsigned char *str = name->name; @@ -2215,9 +2226,16 @@ struct dentry *__d_lookup_rcu(const struct dentry *parent, cpu_relax(); goto seqretry; } - if (parent->d_op->d_compare(dentry, - tlen, tname, name) != 0) - continue; + + if (ci_lookup) { + if (parent->d_op->d_compare_ci(dentry, tlen, + tname, name) != 0) + continue; + } else { + if (parent->d_op->d_compare(dentry, tlen, + tname, name) != 0) + continue; + } } else { if (dentry->d_name.hash_len != hashlen) continue; @@ -2248,7 +2266,7 @@ struct dentry *d_lookup(const struct dentry *parent, const struct qstr *name) do { seq = read_seqbegin(&rename_lock); - dentry = __d_lookup(parent, name); + dentry = __d_lookup(parent, name, 0); if (dentry) break; } while (read_seqretry(&rename_lock, seq)); @@ -2260,6 +2278,7 @@ EXPORT_SYMBOL(d_lookup); * __d_lookup - search for a dentry (racy) * @parent: parent dentry * @name: qstr of name we wish to find + * @ci_lookup: Whether d_lookup_rcu should do a CI lookup * Returns: dentry, or NULL * * __d_lookup is like d_lookup, however it may (rarely) return a @@ -2271,7 +2290,8 @@ EXPORT_SYMBOL(d_lookup); * * __d_lookup callers must be commented. */ -struct dentry *__d_lookup(const struct dentry *parent, const struct qstr *name) +struct dentry *__d_lookup(const struct dentry *parent, const struct qstr *name, + int ci_lookup) { unsigned int hash = name->hash; struct hlist_bl_head *b = d_hash(hash); @@ -2312,8 +2332,13 @@ struct dentry *__d_lookup(const struct dentry *parent, const struct qstr *name) if (d_unhashed(dentry)) goto next; - if (!d_same_name(dentry, parent, name)) - goto next; + if (ci_lookup) { + if (!d_same_name_ci(dentry, parent, name)) + goto next; + } else { + if (!d_same_name(dentry, parent, name)) + goto next; + } dentry->d_lockref.count++; found = dentry; @@ -2476,7 +2501,7 @@ struct dentry *d_alloc_parallel(struct dentry *parent, rcu_read_lock(); seq = smp_load_acquire(&parent->d_inode->i_dir_seq) & ~1; r_seq = read_seqbegin(&rename_lock); - dentry = __d_lookup_rcu(parent, name, &d_seq); + dentry = __d_lookup_rcu(parent, name, &d_seq, 0); if (unlikely(dentry)) { if (!lockref_get_not_dead(&dentry->d_lockref)) { rcu_read_unlock(); diff --git a/fs/namei.c b/fs/namei.c index 135a9a4e676b..1ebe5e775a9a 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1540,6 +1540,7 @@ static int lookup_fast(struct nameidata *nd, struct dentry *dentry, *parent = nd->path.dentry; int status = 1; int err; + int ci_lookup = (nd->flags & LOOKUP_CASEFOLD); /* * Rename seqlock is not required here because in the off chance @@ -1549,7 +1550,7 @@ static int lookup_fast(struct nameidata *nd, if (nd->flags & LOOKUP_RCU) { unsigned seq; bool negative; - dentry = __d_lookup_rcu(parent, &nd->last, &seq); + dentry = __d_lookup_rcu(parent, &nd->last, &seq, ci_lookup); if (unlikely(!dentry)) { if (unlazy_walk(nd)) return -ECHILD; @@ -1595,7 +1596,7 @@ static int lookup_fast(struct nameidata *nd, /* we'd been told to redo it in non-rcu mode */ status = d_revalidate(dentry, nd->flags); } else { - dentry = __d_lookup(parent, &nd->last); + dentry = __d_lookup(parent, &nd->last, ci_lookup); if (unlikely(!dentry)) return 0; status = d_revalidate(dentry, nd->flags); diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 82a99d366aec..a4ce5ea207ad 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -139,6 +139,8 @@ struct dentry_operations { int (*d_hash)(const struct dentry *, struct qstr *); int (*d_compare)(const struct dentry *, unsigned int, const char *, const struct qstr *); + int (*d_compare_ci)(const struct dentry *, + unsigned int, const char *, const struct qstr *); int (*d_delete)(const struct dentry *); int (*d_init)(struct dentry *); void (*d_release)(struct dentry *); @@ -282,9 +284,11 @@ extern struct dentry *d_ancestor(struct dentry *, struct dentry *); /* appendix may either be NULL or be used for transname suffixes */ extern struct dentry *d_lookup(const struct dentry *, const struct qstr *); extern struct dentry *d_hash_and_lookup(struct dentry *, struct qstr *); -extern struct dentry *__d_lookup(const struct dentry *, const struct qstr *); +extern struct dentry *__d_lookup(const struct dentry *, const struct qstr *, + int ci_lookup); extern struct dentry *__d_lookup_rcu(const struct dentry *parent, - const struct qstr *name, unsigned *seq); + const struct qstr *name, unsigned *seq, + int ci_lookup); static inline unsigned d_count(const struct dentry *dentry) { -- 2.17.0