From nobody Sun Feb 8 00:50:25 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4CED92D836D for ; Fri, 19 Dec 2025 19:41:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766173299; cv=none; b=IvCRhKna/nj3ZJ0k+m/GHObniyByBEzKacw98Er075qDtoOLIXTXCace9BMJ9zuUCupCQkCTtSX6UWTAxNIYjlB/DkqLIPfVqd4WKHgyFKm6soD4TL9qjI2YMna3StuyALMjC0kzpjb/lOflJbPfrzFBaEHOKhoyaEJysD/sk08= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766173299; c=relaxed/simple; bh=2ol1BGCSYZu/asfgW/CZB9fgvPXuGBX2o4VPZUbHbbk=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=BlW86pmTPcSdZDLhMG/C/0v8SQDlI4bTT9LdwZjWWtnwD0fE8ZqrE5H5QOc/Me/apL7NPSWQwIwP0nE+8ra2TnhYs23Hy7BRujjYISNUB676jEvzPHFv+B92+FotEjrJpP2WgkrP4r/smfwiRnvQCBhztABW5WWiHkcWDY7lhL8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=JKGCiA2/; dkim=pass (2048-bit key) header.d=redhat.com header.i=@redhat.com header.b=NZmMOnDA; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="JKGCiA2/"; dkim=pass (2048-bit key) header.d=redhat.com header.i=@redhat.com header.b="NZmMOnDA" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1766173296; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=Cb7orZv9lwhqM7zrUkw+3L2cgTBqrRVJ0Lj3cRpDYyk=; b=JKGCiA2/CR/9avx4eJdwzU0i8nflofEsjeSY04qI9V4PxgEVSDsGe9LBo92Sto8t0cLSeb 5wv67ay8C2m0JxHf5Gd1fssbJrNEF0xal/DqdYtUIpF3r6h0yqTf3qpCDsXeXjwLWTn4tI F5MkP0QFFVONIZ8mWyJb0C37Apdc07s= Received: from mail-pf1-f198.google.com (mail-pf1-f198.google.com [209.85.210.198]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-418-4j2zp9sQMnabZ4wmT6dn7g-1; Fri, 19 Dec 2025 14:41:34 -0500 X-MC-Unique: 4j2zp9sQMnabZ4wmT6dn7g-1 X-Mimecast-MFC-AGG-ID: 4j2zp9sQMnabZ4wmT6dn7g_1766173293 Received: by mail-pf1-f198.google.com with SMTP id d2e1a72fcca58-7c1df71b076so4188211b3a.0 for ; Fri, 19 Dec 2025 11:41:34 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1766173293; x=1766778093; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=Cb7orZv9lwhqM7zrUkw+3L2cgTBqrRVJ0Lj3cRpDYyk=; b=NZmMOnDAEWM7kvYkQlwA8wAhkTURIDHPiEdo9rZpWAdG5q6FEelQDbrlGKlDtV7CuQ jgby/edMDq/Kgt40eZ/YiDmnDrSdYtSdK/WK1UJdSTffA3fWT24M2KaPdw/3F+E9FIN1 rDyoxAedr6hkz6BLPlz8gqxSQ0nr4oNOies0AgJ3T6vXD5eg0yyeG//z+1wvoK/dTXAH OvBSyJXcTlYDI/0+dkSBJaKdtQ8P1LTWZ/h7qOA6q5zB/IaK6yEaD4V7sA/E42uxlRLj y5gU4T6hHl2D7Lrm+Gnp/Y5aGT5HroWczMsMEsxai6CRPe641pxB+uOD6a9Iz+7ZrJai mGhw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1766173293; x=1766778093; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=Cb7orZv9lwhqM7zrUkw+3L2cgTBqrRVJ0Lj3cRpDYyk=; b=v65jdWhqVJTgQAQaC/aVwAR5oyrdcZ5zv4fFIJyGGGk/BF5hJv4GwICe7rBLkivu08 DgYI4+yGHjqnR7CZrH0heUo3vaI/alkPKHpaHr1k+px4uVV1hE/UVIBb4+uPSQDX7EoX qysvWeUcf/wLanPB11kFfW8aBfCSSERhBAd0t53grjhhYzKbfgNj/v4hNx5nR0IepRkH DC4eB04KPj3RknosaNKiCq3FLoSzyj9361foZfV2xasW8hEb9El/PWVLiRNpLRwOHewb /UrAyRda5uW5dNxgB9QdpbnBd/R17KKu4YsPGp00LJiBk62ss+4JA2hH/UZr6y6h3aUe kEWw== X-Gm-Message-State: AOJu0YzjFjlWOvR3TxDDRIolRAhchEU8gUkBJ1LBCN7k/ZAIivDG1KQT 5u4abMjAsTZjn3zYPBvjnOQakkwGnCTEJlI9tePMXVwzS6ij9J0mqv0KXxOgm7YVOm3Bj7Q3o34 JeHnIswvYsU+791wsSpt8ujwYf1lkV0OTfE0pDWG17gDMxEqUBzlyXPdcQi9WL9/8lqRjpyQM X-Gm-Gg: AY/fxX4GbDqgj2lARSDhBkcVhqiJgSwjsosWJC7zJqrXIvNIjvzidsvEomnc8F6jgq0 tNwSdn/QSmzkv0Xl2AMpkNKEiuPvchIdwslNn9POoCZLkbUgTJdSX3txq6Bc4yXnq678fmoZnT9 wfINnTg7DNKXa+Rv960jTNY8yIbxIXF3xWDihIDa3r0KWK36L2dZh4nrgmwFzemxlYHplNmPQwY NOPvMTdp8S6cEZLGsQ45Y4p9rqSP2eY4wUBwVoGwDtI8wAbxh2lY9+cn5Zqim9Fl254yJXWZq2J fWju25f2BROrE39G5Ln1VuWgHJPBKky39enemjBEUk3uhPXr84o+kZf2QmZqPS7U7G0xVBu5Yni nMC2KwO/jpxjaOEQkIfZTT079e2BnH7KkgPjh+w== X-Received: by 2002:aa7:9a85:0:b0:7ed:2cd6:58f7 with SMTP id d2e1a72fcca58-7ff6607e361mr3417365b3a.36.1766173292757; Fri, 19 Dec 2025 11:41:32 -0800 (PST) X-Google-Smtp-Source: AGHT+IGFElQh23ZJsjNbRmDSeMUi7YcdVabCd1JUdkJS6sWBCx8TZLmGcmDbQ1OqR6oPFlR2ut46xQ== X-Received: by 2002:aa7:9a85:0:b0:7ed:2cd6:58f7 with SMTP id d2e1a72fcca58-7ff6607e361mr3417349b3a.36.1766173292315; Fri, 19 Dec 2025 11:41:32 -0800 (PST) Received: from dkarn-thinkpadp16vgen1.punetw6.csb ([2402:e280:3e0d:a45:3861:8b7f:6ae1:6229]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-7ff7a93b41fsm3159790b3a.13.2025.12.19.11.41.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 19 Dec 2025 11:41:31 -0800 (PST) From: Deepakkumar Karn To: willy@infradead.org, Jan Kara Cc: linux-kernel@vger.kernel.org, Deepakkumar Karn Subject: [RFC PATCH] mm/filemap: make release_folio mandatory and add block_release_folio Date: Sat, 20 Dec 2025 01:07:52 +0530 Message-ID: <20251219193751.1453197-2-dkarn@redhat.com> X-Mailer: git-send-email 2.52.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Currently, filemap_release_folio() falls back to try_to_free_buffers() when a filesystem's address_space_operations doesn't provide a release_folio callback. This creates an inconsistent API where some buffer_head-based filesystems define release_folio while others rely on the fallback behavior. Make the release_folio callback mandatory by: 1. Adding block_release_folio() in fs/buffer.c as the default implementation for buffer_head-based filesystems. This simply calls try_to_free_buffers(). 2. Updating all buffer_head-based filesystems that use buffer_migrate_folio() to set .release_folio =3D block_release_folio in their address_space_operations. 3. Removing the NULL check and try_to_free_buffers() fallback in filemap_release_folio(). Now it always calls the release_folio callback when a mapping exists. With these changes: - Makes the pattern explicit across all buffer_head filesystems - Prevents future bugs from missing release_folio implementations Filesystems updated: adfs, affs, bfs, exfat, ext2, fat, hpfs, isofs, jfs, minix, nilfs2, ntfs3, omfs, udf, ufs, plus block/fops.c Filesystems with existing custom implementations (ext4, gfs2, hfs, hfsplus, jfs metapage, ocfs2) are unchanged. Suggested-by: Matthew Wilcox Suggested-by: Jan Kara Signed-off-by: Deepakkumar Karn --- block/fops.c | 1 + fs/adfs/inode.c | 1 + fs/affs/file.c | 3 ++- fs/bfs/file.c | 1 + fs/buffer.c | 17 +++++++++++++++++ fs/exfat/inode.c | 1 + fs/ext2/inode.c | 1 + fs/fat/inode.c | 1 + fs/hpfs/file.c | 1 + fs/isofs/inode.c | 3 ++- fs/jfs/inode.c | 1 + fs/minix/inode.c | 3 ++- fs/nilfs2/inode.c | 1 + fs/nilfs2/mdt.c | 1 + fs/ntfs3/inode.c | 1 + fs/omfs/file.c | 1 + fs/udf/inode.c | 1 + fs/ufs/inode.c | 3 ++- include/linux/buffer_head.h | 2 ++ mm/filemap.c | 4 ++-- 20 files changed, 42 insertions(+), 6 deletions(-) diff --git a/block/fops.c b/block/fops.c index 4d32785b31d9..8adccc5fcb34 100644 --- a/block/fops.c +++ b/block/fops.c @@ -532,6 +532,7 @@ const struct address_space_operations def_blk_aops =3D { .write_end =3D blkdev_write_end, .migrate_folio =3D buffer_migrate_folio_norefs, .is_dirty_writeback =3D buffer_check_dirty_writeback, + .release_folio =3D block_release_folio, }; #else /* CONFIG_BUFFER_HEAD */ static int blkdev_read_folio(struct file *file, struct folio *folio) diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c index 6830f8bc8d4e..ca7411c40ace 100644 --- a/fs/adfs/inode.c +++ b/fs/adfs/inode.c @@ -83,6 +83,7 @@ static const struct address_space_operations adfs_aops = =3D { .write_end =3D generic_write_end, .migrate_folio =3D buffer_migrate_folio, .bmap =3D _adfs_bmap, + .release_folio =3D block_release_folio, }; =20 /* diff --git a/fs/affs/file.c b/fs/affs/file.c index 765c3443663e..c03ac926c3a5 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -464,7 +464,8 @@ const struct address_space_operations affs_aops =3D { .write_end =3D affs_write_end, .direct_IO =3D affs_direct_IO, .migrate_folio =3D buffer_migrate_folio, - .bmap =3D _affs_bmap + .bmap =3D _affs_bmap, + .release_folio =3D block_release_folio, }; =20 static inline struct buffer_head * diff --git a/fs/bfs/file.c b/fs/bfs/file.c index d33d6bde992b..34ab8ad50302 100644 --- a/fs/bfs/file.c +++ b/fs/bfs/file.c @@ -198,6 +198,7 @@ const struct address_space_operations bfs_aops =3D { .write_end =3D generic_write_end, .migrate_folio =3D buffer_migrate_folio, .bmap =3D bfs_bmap, + .release_folio =3D block_release_folio, }; =20 const struct inode_operations bfs_file_inops; diff --git a/fs/buffer.c b/fs/buffer.c index 838c0c571022..eb856901ac3a 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -2914,6 +2914,23 @@ drop_buffers(struct folio *folio, struct buffer_head= **buffers_to_free) return false; } =20 +/** + * block_release_folio - Release buffers attached to a BH-based filesystem= s. + * @folio: The folio. + * @gfp: The gfp parameter. + * + * This is the default release_folio implementation for buffer_head based + * filesystems. Filesystems with special requirements + * should implement their own release_folio callback. + * + * Return: true if buffers were released, false otherwise. + */ +bool block_release_folio(struct folio *folio, gfp_t gfp) +{ + return try_to_free_buffers(folio); +} +EXPORT_SYMBOL(block_release_folio); + /** * try_to_free_buffers - Release buffers attached to this folio. * @folio: The folio. diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c index f9501c3a3666..2586fee189d6 100644 --- a/fs/exfat/inode.c +++ b/fs/exfat/inode.c @@ -567,6 +567,7 @@ static const struct address_space_operations exfat_aops= =3D { .direct_IO =3D exfat_direct_IO, .bmap =3D exfat_aop_bmap, .migrate_folio =3D buffer_migrate_folio, + .release_folio =3D block_release_folio, }; =20 static inline unsigned long exfat_hash(loff_t i_pos) diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index dbfe9098a124..632463a203e4 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -980,6 +980,7 @@ const struct address_space_operations ext2_aops =3D { .migrate_folio =3D buffer_migrate_folio, .is_partially_uptodate =3D block_is_partially_uptodate, .error_remove_folio =3D generic_error_remove_folio, + .release_folio =3D block_release_folio, }; =20 static const struct address_space_operations ext2_dax_aops =3D { diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 0b6009cd1844..b6f13927e75b 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -348,6 +348,7 @@ static const struct address_space_operations fat_aops = =3D { .direct_IO =3D fat_direct_IO, .bmap =3D _fat_bmap, .migrate_folio =3D buffer_migrate_folio, + .release_folio =3D block_release_folio, }; =20 /* diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c index 29e876705369..b242439bf52f 100644 --- a/fs/hpfs/file.c +++ b/fs/hpfs/file.c @@ -252,6 +252,7 @@ const struct address_space_operations hpfs_aops =3D { .write_end =3D hpfs_write_end, .bmap =3D _hpfs_bmap, .migrate_folio =3D buffer_migrate_folio, + .release_folio =3D block_release_folio, }; =20 const struct file_operations hpfs_file_ops =3D diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index b7cbe126faf3..d79865bc86b3 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -1162,7 +1162,8 @@ static sector_t _isofs_bmap(struct address_space *map= ping, sector_t block) static const struct address_space_operations isofs_aops =3D { .read_folio =3D isofs_read_folio, .readahead =3D isofs_readahead, - .bmap =3D _isofs_bmap + .bmap =3D _isofs_bmap, + .release_folio =3D block_release_folio, }; =20 static int isofs_read_level3_size(struct inode *inode) diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c index 4709762713ef..f4c9c84fd75a 100644 --- a/fs/jfs/inode.c +++ b/fs/jfs/inode.c @@ -364,6 +364,7 @@ const struct address_space_operations jfs_aops =3D { .bmap =3D jfs_bmap, .direct_IO =3D jfs_direct_IO, .migrate_folio =3D buffer_migrate_folio, + .release_folio =3D block_release_folio, }; =20 /* diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 51ea9bdc813f..1d351d01a0c7 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -486,7 +486,8 @@ static const struct address_space_operations minix_aops= =3D { .write_end =3D generic_write_end, .migrate_folio =3D buffer_migrate_folio, .bmap =3D minix_bmap, - .direct_IO =3D noop_direct_IO + .direct_IO =3D noop_direct_IO, + .release_folio =3D block_release_folio, }; =20 static const struct inode_operations minix_symlink_inode_operations =3D { diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index 51bde45d5865..b8263da21038 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -280,6 +280,7 @@ const struct address_space_operations nilfs_aops =3D { .direct_IO =3D nilfs_direct_IO, .migrate_folio =3D buffer_migrate_folio_norefs, .is_partially_uptodate =3D block_is_partially_uptodate, + .release_folio =3D block_release_folio, }; =20 const struct address_space_operations nilfs_buffer_cache_aops =3D { diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c index 946b0d3534a5..bc1c620e2dce 100644 --- a/fs/nilfs2/mdt.c +++ b/fs/nilfs2/mdt.c @@ -443,6 +443,7 @@ static const struct address_space_operations def_mdt_ao= ps =3D { .invalidate_folio =3D block_invalidate_folio, .writepages =3D nilfs_mdt_writeback, .migrate_folio =3D buffer_migrate_folio_norefs, + .release_folio =3D block_release_folio, }; =20 static const struct inode_operations def_mdt_iops; diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index 0a9ac5efeb67..b671803147b5 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -2102,6 +2102,7 @@ const struct address_space_operations ntfs_aops =3D { .dirty_folio =3D block_dirty_folio, .migrate_folio =3D buffer_migrate_folio, .invalidate_folio =3D block_invalidate_folio, + .release_folio =3D block_release_folio, }; =20 const struct address_space_operations ntfs_aops_cmpr =3D { diff --git a/fs/omfs/file.c b/fs/omfs/file.c index 49a1de5a827f..25f88a2f36de 100644 --- a/fs/omfs/file.c +++ b/fs/omfs/file.c @@ -376,5 +376,6 @@ const struct address_space_operations omfs_aops =3D { .write_end =3D generic_write_end, .bmap =3D omfs_bmap, .migrate_folio =3D buffer_migrate_folio, + .release_folio =3D block_release_folio, }; =20 diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 7fae8002344a..5ba17373f9b1 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -335,6 +335,7 @@ const struct address_space_operations udf_aops =3D { .direct_IO =3D udf_direct_IO, .bmap =3D udf_bmap, .migrate_folio =3D buffer_migrate_folio, + .release_folio =3D block_release_folio, }; =20 /* diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index e2b0a35de2a7..4eed8f530737 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -514,7 +514,8 @@ const struct address_space_operations ufs_aops =3D { .write_begin =3D ufs_write_begin, .write_end =3D ufs_write_end, .migrate_folio =3D buffer_migrate_folio, - .bmap =3D ufs_bmap + .bmap =3D ufs_bmap, + .release_folio =3D block_release_folio, }; =20 static void ufs_set_inode_ops(struct inode *inode) diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index b16b88bfbc3e..16c793dad6f6 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -515,6 +515,7 @@ bool block_dirty_folio(struct address_space *mapping, s= truct folio *folio); =20 void buffer_init(void); bool try_to_free_buffers(struct folio *folio); +bool block_release_folio(struct folio *folio, gfp_t gfp); int inode_has_buffers(struct inode *inode); void invalidate_inode_buffers(struct inode *inode); int remove_inode_buffers(struct inode *inode); @@ -528,6 +529,7 @@ extern int buffer_heads_over_limit; =20 static inline void buffer_init(void) {} static inline bool try_to_free_buffers(struct folio *folio) { return true;= } +static inline bool block_release_folio(struct folio *folio, gfp_t gfp) { r= eturn true; } static inline int inode_has_buffers(struct inode *inode) { return 0; } static inline void invalidate_inode_buffers(struct inode *inode) {} static inline int remove_inode_buffers(struct inode *inode) { return 1; } diff --git a/mm/filemap.c b/mm/filemap.c index ebd75684cb0a..5af38d81498d 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -4490,9 +4490,9 @@ bool filemap_release_folio(struct folio *folio, gfp_t= gfp) if (folio_test_writeback(folio)) return false; =20 - if (mapping && mapping->a_ops->release_folio) + if (mapping) return mapping->a_ops->release_folio(folio, gfp); - return try_to_free_buffers(folio); + return true; } EXPORT_SYMBOL(filemap_release_folio); =20 --=20 2.52.0