From nobody Thu Apr 2 23:53:34 2026 Received: from mail-oo1-f69.google.com (mail-oo1-f69.google.com [209.85.161.69]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5CC0B2DBF75 for ; Thu, 26 Mar 2026 01:50:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.161.69 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774489803; cv=none; b=moAltkdPHtrXxx6bKtMDZ25gfPDsDI8+aD3Zh0+KXNx3Vhf+2P1wVwWL/NYq/nkfOOVsIWTP0aoXwIuwhjv309DgvtteqBsMvZlA+Rq6jSDlp/2FwkqBoFqXlc1+nk5NVR8BDzVMx9JGAgcfR6wV+fKkDA+rWiYOY8vxqZoDD38= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774489803; c=relaxed/simple; bh=LnDnQXsx8ljwCeXYMQ/8Cwxht0xEosFqr+pubSurCxg=; h=MIME-Version:Date:In-Reply-To:Message-ID:Subject:From:To: Content-Type; b=Cf/YsJDN+4AnTE3nWoEyo/kLaWTEM1rOFcXtrgcpSFWW9qHmcm4iE5y8nAGPPwbIka/zo+IcVYJO5GBgvrGo5Flsu1lOcgzYE4hY4m0NKV15LaBXcgoYrWDlOPz/ZPOU4L//2JJb1ep5UE6EAQgh+2QqZaWb5acjguNiZ0qeUV8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=syzkaller.appspotmail.com; spf=pass smtp.mailfrom=M3KW2WVRGUFZ5GODRSRYTGD7.apphosting.bounces.google.com; arc=none smtp.client-ip=209.85.161.69 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=syzkaller.appspotmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=M3KW2WVRGUFZ5GODRSRYTGD7.apphosting.bounces.google.com Received: by mail-oo1-f69.google.com with SMTP id 006d021491bc7-67e0d0907a7so1406457eaf.1 for ; Wed, 25 Mar 2026 18:50:02 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774489801; x=1775094601; h=to:from:subject:message-id:in-reply-to:date:mime-version :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=4wy91PSYprXBHPLfCehT2IusR4tvgm0iPzoYXJd7A1Q=; b=JgUjQjpVkNzDEpdtDkx6t3blYo6A0uI/zZdPR4KEtFLU+G22uYsi9fb8pkxHJnrWBx 38Zgtwg5EA5AkmjnlRNCdHQa8rUifwpqp3jcvDKLN8dA3CONWhOGIuhzzeHC+mRQns6N pKRSEYIy0Mf9VAzOJ9BF7yQOaM5u7I79gh4wKrO+kdJFWQpjrnTg6ipWwZAFxVJLVSuv /26aFSMAXinArmPgJ8AbVx6cbEouWBPSpYNdC5/633U3LqAGSPltULTYwqoyn1GIutfP YGlT5HtQ+JeG49sL+wHvvLbSrO/yM4TVbqCnnEySpbgBb8hrGBLuCJ6l7KGilBkn94Jb k4vA== X-Gm-Message-State: AOJu0YybBSX0P2d2JxkhnobEpOgpYVrevbrPcf2uTWLBQlFaoDo2zhs8 t4T1x1Rs0an8sZOAhSkuFYNj3KVgHll9AW49Z8AhcZ6MNHC/eJ++BbclJC4NN8atpfHzEbepjQi r/zvMwUKWeQnq0i1928peSaezDBRzfxb1pf9+3suFKOyR+oeM+6BoDsTyB1o= Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Received: by 2002:a05:6820:2288:b0:67c:30a9:562b with SMTP id 006d021491bc7-67df5cde1b0mr5879672eaf.2.1774489801412; Wed, 25 Mar 2026 18:50:01 -0700 (PDT) Date: Wed, 25 Mar 2026 18:50:01 -0700 In-Reply-To: <69bba3fe.050a0220.227207.002f.GAE@google.com> X-Google-Appengine-App-Id: s~syzkaller X-Google-Appengine-App-Id-Alias: syzkaller Message-ID: <69c490c9.a70a0220.234938.0074.GAE@google.com> Subject: Forwarded: [PATCH] btrfs: fix hung task when cloning inline extent races with writeback From: syzbot To: linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" For archival purposes, forwarding an incoming command email to linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com. *** Subject: [PATCH] btrfs: fix hung task when cloning inline extent races with= writeback Author: kartikey406@gmail.com From: Deepanshu Kartikey #syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git= master When cloning an inline extent, clone_copy_inline_extent() calls copy_inline_to_page() which locks an extent range in the destination inode's io_tree, dirties a page with the inline data, and sets BTRFS_INODE_NO_DELALLOC_FLUSH on the inode. At this point i_size is still 0 since clone_finish_inode_update() has not been called yet. Then clone_copy_inline_extent() calls start_transaction() which may block waiting for the current transaction to commit. While blocked, the transaction commit calls btrfs_start_delalloc_flush() which calls try_to_writeback_inodes_sb(), queuing a kworker to flush the clone destination inode. The kworker calls btrfs_writepages() -> extent_writepage() and since i_size is still 0, the dirty page appears to be beyond EOF. This causes extent_writepage() to call folio_invalidate() -> btrfs_invalidate_folio() -> btrfs_lock_extent() which blocks forever because the clone operation holds that lock, creating a circular deadlock: clone -> waits for transaction commit to finish commit -> waits for kworker writeback to finish kworker -> waits for extent lock held by clone Additionally any periodic background writeback that races with the clone operation before i_size is updated will also block on the same extent lock causing a hung task warning. The flag BTRFS_INODE_NO_DELALLOC_FLUSH was introduced by commit 3d45f221ce62 to prevent this deadlock but was only checked inside start_delalloc_inodes(), which is only reached through the btrfs metadata reclaim path. The transaction commit path goes through try_to_writeback_inodes_sb() which is a VFS function that bypasses start_delalloc_inodes() entirely, so the flag was never checked there. Fix this by checking BTRFS_INODE_NO_DELALLOC_FLUSH at the top of btrfs_writepages() and returning early if set. This catches all writeback paths since every writeback on a btrfs inode eventually calls btrfs_writepages(). The inode will be safely written after the clone operation finishes and clears the flag, at which point all locks are released and i_size is properly updated. Also change the local variable type from 'struct inode *' to 'struct btrfs_inode *' to avoid the double BTRFS_I() conversion. Fixes: 3d45f221ce62 ("btrfs: fix deadlock when cloning inline extent and lo= w on free metadata space") CC: stable@vger.kernel.org Reported-by: syzbot+63056bf627663701bbbf@syzkaller.appspotmail.com Signed-off-by: Deepanshu Kartikey --- fs/btrfs/extent_io.c | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 5f97a3d2a8d7..f7df7c0c8955 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -2698,21 +2698,54 @@ void extent_write_locked_range(struct inode *inode,= const struct folio *locked_f =20 int btrfs_writepages(struct address_space *mapping, struct writeback_contr= ol *wbc) { - struct inode *inode =3D mapping->host; + struct btrfs_inode *inode =3D BTRFS_I(mapping->host); int ret =3D 0; struct btrfs_bio_ctrl bio_ctrl =3D { .wbc =3D wbc, .opf =3D REQ_OP_WRITE | wbc_to_write_flags(wbc), }; =20 + /* + * If this inode is being used for a clone/reflink operation that + * copied an inline extent into a page of the destination inode, skip + * writeback to avoid a deadlock or a long blocked task. + * + * The clone operation holds the extent range locked in the inode's + * io_tree for its entire duration. Any writeback attempt on this + * inode will block trying to lock that same extent range inside + * writepage_delalloc() or btrfs_invalidate_folio(), causing a + * hung task. + * + * When writeback is triggered from the transaction commit path via + * btrfs_start_delalloc_flush() -> try_to_writeback_inodes_sb(), + * this becomes a true circular deadlock: + * + * clone -> waits for transaction commit to finish + * commit -> waits for kworker writeback to finish + * kworker -> waits for extent lock held by clone + * + * The flag BTRFS_INODE_NO_DELALLOC_FLUSH was already checked in + * start_delalloc_inodes() but only for the btrfs metadata reclaim + * path. The transaction commit path goes through + * try_to_writeback_inodes_sb() which bypasses that check entirely + * and calls btrfs_writepages() directly. + * + * By checking the flag here we catch all writeback paths. The inode + * will be safely written after the clone operation finishes and + * clears BTRFS_INODE_NO_DELALLOC_FLUSH, at which point all locks + * are released and writeback can proceed normally. + */ + if (test_bit(BTRFS_INODE_NO_DELALLOC_FLUSH, &inode->runtime_flags)) + return 0; + /* * Allow only a single thread to do the reloc work in zoned mode to * protect the write pointer updates. */ - btrfs_zoned_data_reloc_lock(BTRFS_I(inode)); + btrfs_zoned_data_reloc_lock(inode); ret =3D extent_write_cache_pages(mapping, &bio_ctrl); submit_write_bio(&bio_ctrl, ret); - btrfs_zoned_data_reloc_unlock(BTRFS_I(inode)); + btrfs_zoned_data_reloc_unlock(inode); return ret; } =20 --=20 2.43.0