Linux filesystem development
 help / color / mirror / Atom feed
From: Dave Chinner <david@fromorbit.com>
To: Bob Peterson <rpeterso@redhat.com>
Cc: linux-fsdevel <linux-fsdevel@vger.kernel.org>, tj <tj@kernel.org>,
	Andreas Gruenbacher <agruenba@redhat.com>
Subject: Re: locking (or LOCKDEP) problem with mark_buffer_dirty()
Date: Sat, 16 Jan 2021 09:18:25 +1100	[thread overview]
Message-ID: <20210115221825.GA78965@dread.disaster.area> (raw)
In-Reply-To: <1403463545.44592876.1610637956402.JavaMail.zimbra@redhat.com>

On Thu, Jan 14, 2021 at 10:25:56AM -0500, Bob Peterson wrote:
> Hi Tejun and linux-fsdevel,
> 
> I have a question about function mark_buffer_dirty and LOCKDEP.
> 
> Background: Func mark_buffer_dirty() has a calling sequence that looks kind
> of like this (simplified):
> 
> mark_buffer_dirty()
>    __set_page_dirty()
>       account_page_dirtied()
>          inode_to_wb() which contains:
> #ifdef CONFIG_LOCKDEP
> 	WARN_ON_ONCE(debug_locks &&
> 		     (!lockdep_is_held(&inode->i_lock) &&
> 		      !lockdep_is_held(&inode->i_mapping->i_pages.xa_lock) &&
> 		      !lockdep_is_held(&inode->i_wb->list_lock)));
> #endif

inode_to_wb() gets called:

- under the xa_lock from page accounting (such as the above path), 
- under the inode->i_lock from wbc_attach_and_unlock_inode() and
  other places,
- and wb->list_lock is what protects the writeback list that the
  inode is on, so it held whenever the inode is added/removed from a
  writeback list.

Essentially, one of those locks has to be held to keep inode_to_wb()
stable and valid.  The function inode_switch_wbs_work_fn() explains
this in a roundabout way:

.....
         * Grabbing old_wb->list_lock, inode->i_lock and the i_pages lock
         * gives us exclusion against all wb related operations on @inode
         * including IO list manipulations and stat updates.
         */

So, essentially, inode_to_wb() is checking at least one of these
locks is held when it is called.

In the above case, __set_page_dirty() takes the xa_lock before
calling account_page_dirtied(), so the lockdep warning should not
ever fire in this path. It also means that you can't hold the
xa_lock when calling mark_buffer_dirty()....

>    ...
>    __mark_inode_dirty()
>       spin_lock(&inode->i_lock);
>       ...
>       spin_unlock(&inode->i_lock);
>    ...      

__mark_inode_dirty() also takes the wb->list_lock (via
locked_inode_to_wb_and_lock_list() because the inode->i_lock is held
so the lockdep check won't fire). Hence you can't hold the
wb->list_lock when calling mark_buffer_dirty() either.

> The LOCKDEP checks were added with Tejun Heo's 2015 patch, aaa2cacf8184e2a92accb8e443b1608d65f9a13f.
> 
> Since mark_buffer_dirty()'s call to __mark_inode_dirty() locks the inode->i_lock,
> functions must not call mark_buffer_dirty() with inode->i_lock locked: or deadlock.

You can't hold any of those three locks inode_to_wb() checks when calling
mark_buffer_dirty(), nor should you. And, AFAICT,
mark_buffer_dirty() is doing all the right locking, so maybe there's
something else going on here that isn't actually a bug in
mark_buffer_dirty().

> If they're not doing anything with the xarrays or the i_wb list (i.e. holding the
> other two locks), they get these LOCKDEP warnings.
>
> So either:
> (a) the LOCKDEP warnings are not valid in all cases -or-
> (b) mark_buffer_dirty() should be grabbing inode->i_lock at some point like __mark_inode_dirty() does.

> My question is: which is it, a or b? TIA.

c) something else?

Perhaps you've got some other inode->i_lock locking bug, and this is
the first place that happens to notice it? Or perhap lockdep itself
has been broken again?

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

      reply	other threads:[~2021-01-15 22:19 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <330231792.44586135.1610635888053.JavaMail.zimbra@redhat.com>
2021-01-14 15:25 ` locking (or LOCKDEP) problem with mark_buffer_dirty() Bob Peterson
2021-01-15 22:18   ` Dave Chinner [this message]

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=20210115221825.GA78965@dread.disaster.area \
    --to=david@fromorbit.com \
    --cc=agruenba@redhat.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=rpeterso@redhat.com \
    --cc=tj@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox