Whamcloud - gitweb
LU-9868 llite: handle DCACHE_PAR_LOOKUP in ll_dcompare 86/28486/24
authorNeilBrown <neilb@suse.com>
Mon, 1 Oct 2018 18:19:05 +0000 (14:19 -0400)
committerOleg Drokin <green@whamcloud.com>
Mon, 11 Feb 2019 03:21:43 +0000 (03:21 +0000)
ll_dcompare is used in two slightly different contexts.
It is called (from __d_lookup, __d_lookup_rcu, and d_exact_alias)
to compare a name against a dentry that is already in the dcache.
It is also called (from d_alloc_parallel) to compare a name against
a dentry that is not in the dcache yet, but is part of an active
"lookup" or "atomic_open" call.

In the first case we need to avoid matching against "invalid" dentries
as a match implies something about ldlm locks which is not accurate.
In the second case we need to allow matching against "invalid" dentries
as the dentry will always be invalid (set by ll_d_init()) but we still
want to guard against multiple concurrent lookups of the same name.
d_alloc_parallel() will repeat the call to ll_dcompare() after
the lookup has finished, and if the dentry is still invalid, the whole
d_alloc_parallel() process is repeated.  This assures us that it is safe
to report success whenever d_in_lookup().

With this patch, there will never be two threads concurrently in
ll_lookup_nd(), looking up the same name in the same directory.

Linux-commit: a22c3d41d187dc3cdaf41166ef0a20b8663fdfee

Change-Id: If489a6f2bbc5c0974570583e3d5083cf77a3b950
Signed-off-by: NeilBrown <neilb@suse.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: James Simmons <uja.ornl@yahoo.com>
Reviewed-on: https://review.whamcloud.com/28486
Tested-by: Jenkins
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Bobi Jam <bobijam@hotmail.com>
Reviewed-by: Patrick Farrell <pfarrell@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>

index 029a2f5..257058f 100644 (file)
@@ -2517,6 +2517,28 @@ EXTRA_KCFLAGS="$tmp_flags"
+# Kernel version 4.6 commit 85c7f81041d57cfe9dc97f4680d5586b54534a39
+# introduced parallel lookups in the VFS layer. The inline function
+# d_in_lookup was added to notify when the same item was being queried
+# at the same time.
+LB_CHECK_COMPILE([if 'd_in_lookup' is defined],
+d_in_lookup, [
+       #include <linux/dcache.h>
+       d_in_lookup(NULL);
+       AC_DEFINE(HAVE_D_IN_LOOKUP, 1, [d_in_lookup is defined])
 # Kernel version 4.7 commit c8b8e32d700fe943a935e435ae251364d016c497
@@ -3182,6 +3204,7 @@ AC_DEFUN([LC_PROG_LINUX], [
        # 4.7
+       LC_D_IN_LOOKUP
index 4baa6ef..03de741 100644 (file)
@@ -246,6 +246,13 @@ static inline void INIT_LIST_HEAD_RCU(struct list_head *list)
 #define DECLARE_LL_D_HLIST_NODE_PTR(name) /* nothing */
 #endif /* !DATA_FOR_LLITE_IS_LIST */
+static inline int d_in_lookup(struct dentry *dentry)
+       return false;
 #ifndef QUOTA_OK
 # define QUOTA_OK 0
index 6a026f0..5790afb 100644 (file)
@@ -77,7 +77,14 @@ static void ll_release(struct dentry *de)
  * This avoids a race where ll_lookup_it() instantiates a dentry, but we get
  * an AST before calling d_revalidate_it().  The dentry still exists (marked
  * INVALID) so d_lookup() matches it, but we have no lock on it (so
- * lock_match() fails) and we spin around real_lookup(). */
+ * lock_match() fails) and we spin around real_lookup().
+ *
+ * This race doesn't apply to lookups in d_alloc_parallel(), and for
+ * those we want to ensure that only one dentry with a given name is
+ * in ll_lookup_nd() at a time.  So allow invalid dentries to match
+ * while d_in_lookup().  We will be called again when the lookup
+ * completes, and can give a different answer then.
+ */
 static int ll_dcompare(const struct dentry *parent, const struct inode *pinode,
                       const struct dentry *dentry, const struct inode *inode,
@@ -117,6 +124,10 @@ static int ll_dcompare(struct dentry *parent, struct qstr *d_name,
        if (d_mountpoint((struct dentry *)dentry))
+       /* ensure exclusion against parallel lookup of the same name */
+       if (d_in_lookup((struct dentry *)dentry))
+               return 0;
        if (d_lustre_invalid(dentry))