All of lore.kernel.org
 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 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.