Linux EXT4 FS development
 help / color / mirror / Atom feed
From: Jan Kara <jack@suse.cz>
To: Zhang Yi <yi.zhang@huaweicloud.com>
Cc: Jan Kara <jack@suse.cz>,
	linux-ext4@vger.kernel.org,
	Alexander Coffin <alex.coffin@maticrobots.com>,
	stable@vger.kernel.org, Ted Tso <tytso@mit.edu>,
	chengzhihao1@huawei.com
Subject: Re: [PATCH v2 3/4] jbd2: Avoid infinite transaction commit loop
Date: Wed, 26 Jun 2024 16:55:16 +0200	[thread overview]
Message-ID: <20240626145516.xopg62cxcztte3ek@quack3> (raw)
In-Reply-To: <e5a3187c-b333-ec5b-0d8b-a02934a37b06@huaweicloud.com>

On Wed 26-06-24 21:24:13, Zhang Yi wrote:
> On 2024/6/26 19:22, Jan Kara wrote:
> > On Wed 26-06-24 15:38:42, Zhang Yi wrote:
> >> On 2024/6/25 1:01, Jan Kara wrote:
> >>> Commit 9f356e5a4f12 ("jbd2: Account descriptor blocks into
> >>> t_outstanding_credits") started to account descriptor blocks into
> >>> transactions outstanding credits. However it didn't appropriately
> >>> decrease the maximum amount of credits available to userspace. Thus if
> >>> the filesystem requests a transaction smaller than
> >>> j_max_transaction_buffers but large enough that when descriptor blocks
> >>> are added the size exceeds j_max_transaction_buffers, we confuse
> >>> add_transaction_credits() into thinking previous handles have grown the
> >>> transaction too much and enter infinite journal commit loop in
> >>> start_this_handle() -> add_transaction_credits() trying to create
> >>> transaction with enough credits available.
> >>
> >> I understand that the incorrect max transaction limit in
> >> start_this_handle() could lead to infinite loop in
> >> start_this_handle()-> add_transaction_credits() with large enough
> >> userspace credits (from j_max_transaction_buffers - overheads to
> >> j_max_transaction_buffers), but I don't get how could it lead to ran
> >> out of space in the journal commit traction? IIUC, below codes in
> >> add_transaction_credits() could make sure that we have enough space
> >> when committing traction:
> >>
> >> static int add_transaction_credits()
> >> {
> >> ...
> >> 	if (jbd2_log_space_left(journal) < journal->j_max_transaction_buffers) {
> >> 		...
> >> 		return 1;
> >> 		...
> >> 	}
> >> ...
> >> }
> >>
> >> I can't open and download the image Alexander gave, so I can't get to
> >> the bottom of this issue, please let me know what happened with
> >> jbd2_journal_next_log_block().
> > 
> > Sure. So what was exactly happening is a loop like this:
> > 
> > start_this_handle()
> >   blocks = 252 (handle->h_total_credits)
> >   - starts a new transaction
> >     - t_outstanding_credits set to 6 to account for the commit block and
> >       descriptor blocks
> >   add_transaction_credits(journal, 252, 0)
> >      needed = atomic_add_return(252, &t->t_outstanding_credits);
> >      if (needed > journal->j_max_transaction_buffers) {
> > 	/* Yes, this is exceeded due to descriptor blocks being in
> > 	 * t_outstanding_credits */
> >         ...
> >         wait_transaction_locked(journal);
> > 	  - this commits an empty transaction - contains only the commit
> > 	    block
> >         return 1
> >   goto repeat
> > 
> > So we commit single block transactions in a loop until we exhaust all the
> > journal space. The condition in add_transaction_credits() whether there's
> > enough space in the journal is never reached so we don't ever push the
> > journal tail to make space in the journal.
> >     
> 
> mm-hm, ha, yeah, this will lead to submit an empty transaction in each loop,
> but I still have one question, although the journal tail can't be updated
> in add_transaction_credits(), I think the journal tail should be updated in
> jbd2_journal_commit_transaction()->jbd2_update_log_tail() since we don't
> add empty transactions to journal->j_checkpoint_transactions list, the loop
> in jbd2_journal_commit_transaction() should like this:
> 
> ...
> jbd2_journal_commit_transaction()
>   update_tail = jbd2_journal_get_log_tail()
>     //journal->j_checkpoint_transactions should be NULL, tid is the
>     //committing transaction's tid, which should be large than the
>     //tail tid since the second loop, so this should be true after
>     //the second loop
>   if (freed < journal->j_max_transaction_buffers)
>     update_tail = 0;
>     //update_tail should be true after j_max_transaction_buffers loop
> 
> jbd2_update_log_tail()  //j_free should be increased in each
>                         //j_max_transaction_buffers loop
>   if (tid_gt(tid, journal->j_tail_sequence)) //it's true
>   __jbd2_update_log_tail()
>     journal->j_free += freed; //update log tail and increase j_free
>                               //j_max_transaction_buffers blocks
> ...
> 
> As I understand it, the journal space can't be exhausted, I don't know how
> it happened, am I missing something?

Well, at the beginning of the journal there are a few non-empty
transactions whose buffers didn't get checkpointed before we entered the
commit loop. So we cannot just push the journal tail without doing a
checkpoint and we never call into the checkpointing code in the commit
loop...

								Honza
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

  reply	other threads:[~2024-06-26 14:55 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-06-24 17:01 [PATCH 0/4] jbd2: Avoid infinite transaction commit loop Jan Kara
2024-06-24 17:01 ` [PATCH v2 1/4] jbd2: Make jbd2_journal_get_max_txn_bufs() internal Jan Kara
2024-06-27  6:47   ` Zhang Yi
2024-07-11  2:35   ` Theodore Ts'o
2024-06-24 17:01 ` [PATCH v2 2/4] jbd2: Precompute number of transaction descriptor blocks Jan Kara
2024-06-25  9:31   ` Kemeng Shi
2024-06-25 11:07     ` Jan Kara
2024-06-25 12:02       ` Kemeng Shi
2024-06-27  7:06   ` Zhang Yi
2024-06-24 17:01 ` [PATCH v2 3/4] jbd2: Avoid infinite transaction commit loop Jan Kara
2024-06-26  7:38   ` Zhang Yi
2024-06-26 11:22     ` Jan Kara
2024-06-26 13:24       ` Zhang Yi
2024-06-26 14:55         ` Jan Kara [this message]
2024-06-27  6:43           ` Zhang Yi
2024-06-24 17:01 ` [PATCH v2 4/4] jbd2: Drop pointless shrinker batch initialization Jan Kara
2024-06-27  7:07   ` Zhang Yi

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=20240626145516.xopg62cxcztte3ek@quack3 \
    --to=jack@suse.cz \
    --cc=alex.coffin@maticrobots.com \
    --cc=chengzhihao1@huawei.com \
    --cc=linux-ext4@vger.kernel.org \
    --cc=stable@vger.kernel.org \
    --cc=tytso@mit.edu \
    --cc=yi.zhang@huaweicloud.com \
    /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