linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Q: spin_unlock(dentry) after lock_parent(dentry)
@ 2014-06-03 14:36 J. R. Okajima
  2014-06-03 23:17 ` Al Viro
  0 siblings, 1 reply; 4+ messages in thread
From: J. R. Okajima @ 2014-06-03 14:36 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel


Hello Al Viro,

I have a question about spin_unlock(dentry) after lock_parent(dentry).
In lock_parent(dentry), spin_unlock(dentry) is called. And
spin_lock(dentry) is called again when (parent != dentry) is
true. Otherwise, dentry left spin_unlock-ed and lock_parent() returns
NULL.

Even in the case of lock_parent() returns NULL, shrink_dentry_list()
calls spin_unlock(dentry). Is it balanced and correct?


J. R. Okajima

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Q: spin_unlock(dentry) after lock_parent(dentry)
  2014-06-03 14:36 Q: spin_unlock(dentry) after lock_parent(dentry) J. R. Okajima
@ 2014-06-03 23:17 ` Al Viro
  2014-06-04 17:03   ` J. R. Okajima
  0 siblings, 1 reply; 4+ messages in thread
From: Al Viro @ 2014-06-03 23:17 UTC (permalink / raw)
  To: J. R. Okajima; +Cc: linux-fsdevel

On Tue, Jun 03, 2014 at 11:36:55PM +0900, J. R. Okajima wrote:
> 
> Hello Al Viro,
> 
> I have a question about spin_unlock(dentry) after lock_parent(dentry).
> In lock_parent(dentry), spin_unlock(dentry) is called. And
> spin_lock(dentry) is called again when (parent != dentry) is
> true. Otherwise, dentry left spin_unlock-ed and lock_parent() returns
> NULL.

Nope.  You are misreading it.  Note that in *all* cases the parent is
locked.  And NULL is returned exactly in the case when that parent is
equal to dentry itself.  IOW, in all cases, lock_parent(d) returns with d
locked and after p = lock_parent(d) either
	1) d->d_parent == d and p == NULL, or
	2) d->d_parent == p, p != d and p is locked as well.
That holds whether we had succeeded with trylock or had to drop and regain
the lock on d.

> Even in the case of lock_parent() returns NULL, shrink_dentry_list()
> calls spin_unlock(dentry). Is it balanced and correct?

Yes.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Q: spin_unlock(dentry) after lock_parent(dentry)
  2014-06-03 23:17 ` Al Viro
@ 2014-06-04 17:03   ` J. R. Okajima
  2014-06-07 13:51     ` J. R. Okajima
  0 siblings, 1 reply; 4+ messages in thread
From: J. R. Okajima @ 2014-06-04 17:03 UTC (permalink / raw)
  To: Al Viro; +Cc: linux-fsdevel


Al Viro:
> Nope.  You are misreading it.  Note that in *all* cases the parent is
> locked.  And NULL is returned exactly in the case when that parent is
	:::

Thanks.
Please allow me another question.

In the case of concurrent dentry_kill() and shrink_dentry_list(),
dentry_kill() kills the dentry and calls list_del(&dentry->d_u.d_child).
lock_parent() (called by shrink_dentry_list()) doesn't check d_child
nor DCACHE_DENTRY_KILLED. So shrink_dentry_list() may get a parent of a
dead dentry and tries traversing via d_parent.
Is it correct to track d_parent after list_del(&dentry->d_u.d_child)?
Should lock_parent() consider DCACHE_DENTRY_KILLED too?

--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -533,7 +533,8 @@ failed:
 static inline struct dentry *lock_parent(struct dentry *dentry)
 {
 	struct dentry *parent = dentry->d_parent;
-	if (IS_ROOT(dentry))
+	if (IS_ROOT(dentry)
+	    || (dentry->d_flags & DCACHE_DENTRY_KILLED))
 		return NULL;
 	if (likely(spin_trylock(&parent->d_lock)))
 		return parent;


J. R. Okajima

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Q: spin_unlock(dentry) after lock_parent(dentry)
  2014-06-04 17:03   ` J. R. Okajima
@ 2014-06-07 13:51     ` J. R. Okajima
  0 siblings, 0 replies; 4+ messages in thread
From: J. R. Okajima @ 2014-06-07 13:51 UTC (permalink / raw)
  To: Al Viro, linux-fsdevel


Al Viro,

I think I could find a clearer problematic scenario.
Can it happen in real world?

- /.../dirA/dirB/fileC exists.
- shrink_dentry_list() gets a list containing fileC and dirB, but not
  dirA
  + Round 1: fileC
    - shrink_dentry_list() gets d_parent which is dirB
    - dirB is DCACHE_SHRINK_LIST set, so __dentry_kill() is called but
      not dentry_free()
    - shrink_dentry_list() gets higher d_parent which is dirA
    - dirA is not DCACHE_SHRINK_LIST set, so both of __dentry_kill() and
      dentry_free() are is called
  + Round 2: dirB
    - shrink_dentry_list() tries getting d_parent which is dirA
    - dirA is freed by RCU. if RCU already activated __d_free(), BANG!


> In the case of concurrent dentry_kill() and shrink_dentry_list(),
> dentry_kill() kills the dentry and calls list_del(&dentry->d_u.d_child).
> lock_parent() (called by shrink_dentry_list()) doesn't check d_child
> nor DCACHE_DENTRY_KILLED. So shrink_dentry_list() may get a parent of a
> dead dentry and tries traversing via d_parent.
> Is it correct to track d_parent after list_del(&dentry->d_u.d_child)?
> Should lock_parent() consider DCACHE_DENTRY_KILLED too?
>
> --- a/fs/dcache.c
> +++ b/fs/dcache.c
> @@ -533,7 +533,8 @@ failed:
>  static inline struct dentry *lock_parent(struct dentry *dentry)
>  {
>  	struct dentry *parent = dentry->d_parent;
> -	if (IS_ROOT(dentry))
> +	if (IS_ROOT(dentry)
> +	    || (dentry->d_flags & DCACHE_DENTRY_KILLED))
>  		return NULL;
>  	if (likely(spin_trylock(&parent->d_lock)))
>  		return parent;


J. R. Okajima

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2014-06-07 13:51 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-06-03 14:36 Q: spin_unlock(dentry) after lock_parent(dentry) J. R. Okajima
2014-06-03 23:17 ` Al Viro
2014-06-04 17:03   ` J. R. Okajima
2014-06-07 13:51     ` J. R. Okajima

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).