All of lore.kernel.org
 help / color / mirror / Atom feed
From: Joseph Qi <joseph.qi@linux.alibaba.com>
To: Heming Zhao <heming.zhao@suse.com>
Cc: Andrew Morton <akpm@linux-foundation.org>,
	Mark Fasheh <mark@fasheh.com>, Joel Becker <jlbec@evilplan.org>,
	ocfs2-devel@lists.linux.dev, linux-kernel@vger.kernel.org
Subject: Re: [PATCH] ocfs2: defer suballocator block group reclaim to workqueue
Date: Fri, 28 Aug 2026 15:06:53 +0800	[thread overview]
Message-ID: <b0c3117c-5fb4-4cce-b29f-cfd72a3f93bd@linux.alibaba.com> (raw)
In-Reply-To: <apEeXAAo1wd5E5Or@p15>



On 8/28/26 1:47 PM, Heming Zhao wrote:
> On Thu, Aug 27, 2026 at 08:43:13PM +0800, Joseph Qi wrote:
>> When the last bit in a suballocator block group is freed,
>> _ocfs2_free_suballoc_bits() reclaims the group back to the global
>> bitmap.  The reclaim takes inode_lock() on the global bitmap inode
>> while running inside the freeing transaction, adding a lock
>> dependency of
>>
>>   j_trans_barrier -> global bitmap inode i_rwsem
>>
>> This forms a circular dependency with paths such as
>> ocfs2_shutdown_local_alloc(), which take the global bitmap inode
>> lock before starting a transaction:
>>
>>   Task1 (dealloc):
>>     ocfs2_run_deallocs
>>       ocfs2_free_cached_blocks
>>         ocfs2_start_trans
>>           down_read(j_trans_barrier)
>>         _ocfs2_free_suballoc_bits
>>           _ocfs2_reclaim_suballoc_to_main
>>             inode_lock(main_bm_inode)  <- wait on Task2
>>
>>   Task2 (dismount):
>>     ocfs2_shutdown_local_alloc
>>       inode_lock(main_bm_inode)
>>       ocfs2_start_trans
>>         down_read(j_trans_barrier)  <- wait on Task3
>>
>>   Task3 (ocfs2cmt):
>>     ocfs2_commit_cache
>>       down_write(j_trans_barrier)  <- wait on Task1's handle
>>       jbd2_journal_flush
>>
>> Task1 waits for Task2's inode_lock(), Task2 waits for the
>> j_trans_barrier down_write() held by ocfs2cmt, and ocfs2cmt waits
>> for Task1's running transaction to commit - a real deadlock,
>> observed with aio-stress direct IO writes racing dismount.
>>
>> Fix it by deferring the reclaim to the per-superblock ocfs2_wq
>> workqueue, so the freeing transaction no longer takes the global
>> bitmap inode lock.  The worker re-checks under the suballocator
>> locks that the block group is still fully freed (it may have been
>> allocated from again in the meantime), takes the global bitmap
>> inode locks before starting its own transaction, and performs the
>> same suballocator cleanup and space return.  The inode lock order
>> (suballocator inode -> global bitmap inode) is consistent with the
>> existing "inode lock before transaction" order, breaking the cycle.
>>
>> Reclaim work can still be queued late in dismount, e.g. when orphan
>> dir recovery frees inode bits, so both ocfs2_dismount_volume() and
>> the mount error path flush ocfs2_wq right before the journal is
>> shut down to make sure no reclaim work is left running.  The worker
>> also bails out if the journal is already gone.
>>
>> Tested with the ocfs2 testsuite (including aio-stress direct IO)
>> and umount/mount cycles on a CONFIG_PROVE_LOCKING kernel: the
>> circular locking dependency is gone and freed block groups are
>> still returned to the global bitmap.
>>
>> Fixes: 4a54331616b3 ("ocfs2: give ocfs2 the ability to reclaim suballocator free bg")
>> Assisted-by: Qoder:Qwen3.8-Max
>> Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com>
>> ---
>>  fs/ocfs2/ocfs2.h    |   5 ++
>>  fs/ocfs2/suballoc.c | 202 +++++++++++++++++++++++++++++++++++++-------
>>  fs/ocfs2/suballoc.h |   2 +-
>>  fs/ocfs2/super.c    |  12 +++
>>  4 files changed, 191 insertions(+), 30 deletions(-)
>>

......

>> diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
>> index c62e389d4dd6..c1aafbb9500a 100644
>> --- a/fs/ocfs2/super.c
>> +++ b/fs/ocfs2/super.c
>> @@ -1779,6 +1779,9 @@ static int ocfs2_mount_volume(struct super_block *sb)
>>  	if (osb->local_alloc_state == OCFS2_LA_ENABLED)
>>  		ocfs2_shutdown_local_alloc(osb);
>>  	ocfs2_release_system_inodes(osb);
>> +	/* Drain pending suballoc reclaim work before the journal goes away */
>> +	if (osb->ocfs2_wq)
>> +		flush_workqueue(osb->ocfs2_wq);
> 
> The flush_workqueue() calls ocfs2_get_system_file_inode() to grab main_bm_inode.
> However, after ocfs2_release_system_inodes(), all the cached system inodes are gone,
> And the _ocfs2_get_system_file_inode() uses osb->sys_root_inode, which is already
> freed by ocfs2_release_system_inodes(). So we should move the
> flush_workqueue() calls to before ocfs2_release_system_inodes().
> 

Thanks, sashiko also has ponited out this issue.
I've fixed in v2 and now it is under testing. Will send out later.

Thanks,
Joseph


      reply	other threads:[~2026-08-28  7:06 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-27 12:43 [PATCH] ocfs2: defer suballocator block group reclaim to workqueue Joseph Qi
2026-08-28  5:47 ` Heming Zhao
2026-08-28  7:06   ` Joseph Qi [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=b0c3117c-5fb4-4cce-b29f-cfd72a3f93bd@linux.alibaba.com \
    --to=joseph.qi@linux.alibaba.com \
    --cc=akpm@linux-foundation.org \
    --cc=heming.zhao@suse.com \
    --cc=jlbec@evilplan.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mark@fasheh.com \
    --cc=ocfs2-devel@lists.linux.dev \
    /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.