All of lore.kernel.org
 help / color / mirror / Atom feed
From: Al Viro <viro@ZenIV.linux.org.uk>
To: Jia-Ju Bai <baijiaju1990@163.com>
Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [BUG] fs/dcache: might_sleep is called under a spinlock
Date: Tue, 3 Oct 2017 04:19:20 +0100	[thread overview]
Message-ID: <20171003031920.GI21978@ZenIV.linux.org.uk> (raw)
In-Reply-To: <a64563c5-9f87-5150-fb2f-4e7220703c2d@163.com>

On Tue, Oct 03, 2017 at 10:38:25AM +0800, Jia-Ju Bai wrote:
> According to fs/dcache.c, might_sleep is called under a spinlock,
> and the function call path is:
> d_prune_aliases (acquire the spinlock)
>   dput
>     might_sleep
> 
> This bug is found by my static analysis tool and my code review.
> A possible fix is to remove might_sleep in dput.

... or to fix your static analysis tool.  First of all, that call
of dput() really *can* block and if we had inode->i_lock or dentry->d_lock
still held at that point we'd have a real bug.  However, __dentry_kill()
there is called with dentry->d_inode == inode and inode->i_lock held,
so dentry->d_inode is stable until inode->i_lock is dropped.  Said
__dentry_kill() contains
        if (dentry->d_inode)
                dentry_unlink_inode(dentry);
with inode->i_lock held until that point.  dentry_unlink_inode() starts
with
        struct inode *inode = dentry->d_inode;
        bool hashed = !d_unhashed(dentry);

        if (hashed)
                raw_write_seqcount_begin(&dentry->d_seq);
        __d_clear_type_and_inode(dentry);
        hlist_del_init(&dentry->d_u.d_alias);
        if (hashed)
                raw_write_seqcount_end(&dentry->d_seq);
        spin_unlock(&dentry->d_lock);
        spin_unlock(&inode->i_lock);
so
	1) inode in there is guaranteed to be equal to the argument of
d_prune_aliases() and
	2) both dentry->d_lock and inode->i_lock are dropped before
dentry_unlink_inode() returns.  inode->i_lock is not regained in the
rest of __dentry_kill(); dentry->d_lock is regained and dropped before
__dentry_kill() returns.  IOW, we are fine - dput() in d_prune_aliases()
is called without any spinlocks held.

That, BTW, is the reason for
                                goto restart;
in there, instead of just continuing the loop - if we get to that point,
the list of aliases might have changed.

Removing might_sleep() in dput() would've been wrong - it really might
sleep when called from that point.  Here's how: we used to have two
links to the same file - foo/bar and baz/barf.  baz/barf used to be
opened, then rm -rf baz happened and later we'd called d_prune_aliases()
on the inode of foo/bar.  And as the loop had been executed on one CPU,
on another the opened file got closed, dropping the last reference to
dentry that used to be baz/barf.  Note that its parent (the thing that
used to be dentry of baz) is unhashed and the only contributor to its
refcount is our dentry, so dput(parent) *does* drop the last remaining
reference, triggering the final iput() on inode of baz, along with
freeing on-disk inode, doing disk IO, etc.

Again, it's not that we can't block in that dput() - it's that __dentry_kill()
drops all spinlocks.

  reply	other threads:[~2017-10-03  3:19 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-10-03  2:38 [BUG] fs/dcache: might_sleep is called under a spinlock Jia-Ju Bai
2017-10-03  3:19 ` Al Viro [this message]
2017-10-03  8:46   ` Jia-Ju Bai

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=20171003031920.GI21978@ZenIV.linux.org.uk \
    --to=viro@zeniv.linux.org.uk \
    --cc=baijiaju1990@163.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.