linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "J. R. Okajima" <hooanon05g@gmail.com>
To: Al Viro <viro@ZenIV.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
Subject: Re: Q. hlist_bl_add_head_rcu() in d_alloc_parallel()
Date: Mon, 20 Jun 2016 13:34:14 +0900	[thread overview]
Message-ID: <28627.1466397254@jrobl> (raw)
In-Reply-To: <20160619165557.GH14480@ZenIV.linux.org.uk>


Al Viro:
> How would processB get past d_wait_lookup()?  It would have to have

By the first d_unhashed() test in the loop, processB doesn't reach
d_wait_lookup().

Here is a more detailed scenario I am considering.
- two processes try opening the same file
- the both enter lookup_open() and the hlist_bl_lock protected loop in
  d_alloc_parallel()

processA (the winner of hlist_bl_lock)
d_alloc_parallel()
+ new = d_alloc()
+ rcu_read_lock()
+ dentry = __d_lookup_rcu()
  NULL
+ hlist_bl_lock()		--- (BL0)
+ test parent's i_dir_seq	--- (DS1)
+ rcu_read_unlock()
+ hlist_bl_for_each_entry(d_u.d_in_lookup_hash)
  not found
+ new->d_flags |= DCACHE_PAR_LOOKUP
+ new->d_wait = wq
+ hlist_bl_add_head_rcu(new->d_u.d_in_lookup_hash)
+ hlist_bl_unlock()		--- (BL1)
+ return new
--> a new dentry is added into in-lookup hash.

And then
processA
->atomic_open or ->lookup
+ __d_add()
  + spin_lock(d_lock)
  + update parent's i_dir_seq	--- (DS2)
  + __d_lookup_done()
    + hlist_bl_lock()		--- (BL2)
    + dentry->d_flags &= ~DCACHE_PAR_LOOKUP
    + __hlist_bl_del(d_u.d_in_lookup_hash)
    + hlist_bl_unlock()		--- (BL3)
  + _d_rehash()
    + hlist_bl_lock()		--- (BL4)
    + hlist_bl_add_head_rcu(d_hash)
    + hlist_bl_unlock()
  + spin_unlock(d_lock)
--> the new dentry is moved from in-lookup to primary hash.


Between (BL1) and (BL2) in processA flow, processB may acquire
hlist_bl_lock() which was blocked at (BL0), and it may happen even
before processA's (DS2).
In this case, processB will return an unexpectedly duplicated dentry
because DS1 test will be passed and the loop body will be skipped by
d_unhashed() test before d_wait_lookup().
It is after (BL4) when d_unhashed() turns FALSE.

processB
d_alloc_parallel()
+ hlist_bl_lock()
+ test parent's i_dir_seq
  passed if processA doesn't reach (DS2) yet.
+ rcu_read_unlock()
+ hlist_bl_for_each_entry(d_u.d_in_lookup_hash)
  dentry found but skips because
	if (d_unhashed(dentry))
		continue;
  before d_wait_lookup().
+ new->d_flags |= DCACHE_PAR_LOOKUP
+ new->d_wait = wq
+ hlist_bl_add_head_rcu(new->d_u.d_in_lookup_hash)
+ hlist_bl_unlock()
+ return new
--> another same named dentry is added into in-lookup hash.


On the other hand, in case of processB acquires hlist_bl_lock() between
(BL3) and (BL4) in processA's flow, processB will detect the parent's
i_dir_seq is modified and 'goto retry'. It is good.

Finally the race condition I am afraid is
- processB aqcuires BL0 between processA's BL1 and BL2.
  and
- processB tests DS1 before processA's DS2.


J. R. Okajima

  reply	other threads:[~2016-06-20  4:34 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-06-17 20:50 Q. hlist_bl_add_head_rcu() in d_alloc_parallel() J. R. Okajima
2016-06-17 22:16 ` Al Viro
2016-06-17 22:56   ` Al Viro
2016-06-19  5:24   ` J. R. Okajima
2016-06-19 16:55     ` Al Viro
2016-06-20  4:34       ` J. R. Okajima [this message]
2016-06-20  5:35         ` Al Viro
2016-06-20 14:51           ` Al Viro
2016-06-20 17:14             ` [git pull] vfs fixes Al Viro
2016-06-23  1:19           ` Q. hlist_bl_add_head_rcu() in d_alloc_parallel() J. R. Okajima
2016-06-23  2:58             ` Al Viro
2016-06-24  5:57               ` Linus Torvalds
2016-06-25 22:54                 ` Al Viro
2016-06-26  1:25                   ` Linus Torvalds
2016-06-29  8:17                     ` Al Viro
2016-06-29  9:22                       ` Hekuang

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=28627.1466397254@jrobl \
    --to=hooanon05g@gmail.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=viro@ZenIV.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).