Linux filesystem development
 help / color / mirror / Atom feed
* [PATCH] exfat: implement swap activate
@ 2026-06-18  9:33 Andrea Cervesato
  2026-06-18 18:15 ` David Timber
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Andrea Cervesato @ 2026-06-18  9:33 UTC (permalink / raw)
  To: Namjae Jeon, Sungjong Seo, Yuezhang Mo, David Timber
  Cc: linux-fsdevel, linux-kernel, jack, chrubis, Andrea Cervesato

From: Andrea Cervesato <andrea.cervesato@suse.com>

exfat's fallocate allocates clusters without updating valid_size,
leaving them invisible to bmap(). Extend valid_size to i_size so
that generic_swapfile_activate() can map all blocks.

This bug has been found during a Linux Test Project regression test
using swapon/swapoff testing suite.

Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
Fixes: bf1797960c20 ("exfat: add fallocate FALLOC_FL_ALLOCATE_RANGE support")
Link: https://patchwork.ozlabs.org/project/ltp/patch/20260608155241.270875-1-japo@linux.ibm.com/
---
bf1797960c20 - ("exfat: add fallocate FALLOC_FL_ALLOCATE_RANGE support")
introduces fallocate() support in exfat, but omits the swap activate
implementation, causing EINVAL inside Linux Test Project swapon/swapoff
testing suites which are testing these syscalls with many filesystems,
including exfat.

This turned out to be a bug inside the kernel. The patch implements
swap activate in order to ensure that exfat valid_size is correctly
updated and the generic_swapfile_activate() doesn't fail with EINVAL
due to valid_size=0, which is passed to bmap().
---
 fs/exfat/exfat_fs.h |  1 +
 fs/exfat/file.c     |  2 +-
 fs/exfat/inode.c    | 23 +++++++++++++++++++++++
 3 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index aff4dcd4e75a55296d536c19306813a566d6f0bb..18382c661d4358f8b80e6f7b5032a356a5905614 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -489,6 +489,7 @@ int exfat_trim_fs(struct inode *inode, struct fstrim_range *range);
 
 /* file.c */
 extern const struct file_operations exfat_file_operations;
+int exfat_extend_valid_size(struct inode *inode, loff_t new_valid_size);
 int __exfat_truncate(struct inode *inode);
 void exfat_truncate(struct inode *inode);
 int exfat_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index 91e5511945d11b12658908dc6287fabd572d1d6a..12ed28c3ff896fb545bad58b47cccf808b48618d 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -642,7 +642,7 @@ int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
 	return blkdev_issue_flush(inode->i_sb->s_bdev);
 }
 
-static int exfat_extend_valid_size(struct inode *inode, loff_t new_valid_size)
+int exfat_extend_valid_size(struct inode *inode, loff_t new_valid_size)
 {
 	int err;
 	loff_t pos;
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index 1ea4c740fef9ef6932a75e122e1b0130c85533b2..5a395851062e4a14b456fa79546541227d8427e6 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -11,6 +11,7 @@
 #include <linux/time.h>
 #include <linux/writeback.h>
 #include <linux/uio.h>
+#include <linux/swap.h>
 #include <linux/random.h>
 #include <linux/iversion.h>
 
@@ -534,6 +535,27 @@ int exfat_block_truncate_page(struct inode *inode, loff_t from)
 	return block_truncate_page(inode->i_mapping, from, exfat_get_block);
 }
 
+static int exfat_swap_activate(struct swap_info_struct *sis,
+			       struct file *file, sector_t *span)
+{
+	struct inode *inode = file_inode(file);
+	struct exfat_inode_info *ei = EXFAT_I(inode);
+	int ret;
+
+	/*
+	 * exfat's fallocate allocates clusters without updating valid_size,
+	 * leaving them invisible to bmap(). Extend valid_size to i_size so
+	 * that generic_swapfile_activate() can map all blocks.
+	 */
+	if (ei->valid_size < i_size_read(inode)) {
+		ret = exfat_extend_valid_size(inode, i_size_read(inode));
+		if (ret)
+			return ret;
+	}
+
+	return generic_swapfile_activate(sis, file, span);
+}
+
 static const struct address_space_operations exfat_aops = {
 	.dirty_folio	= block_dirty_folio,
 	.invalidate_folio = block_invalidate_folio,
@@ -545,6 +567,7 @@ static const struct address_space_operations exfat_aops = {
 	.direct_IO	= exfat_direct_IO,
 	.bmap		= exfat_aop_bmap,
 	.migrate_folio	= buffer_migrate_folio,
+	.swap_activate	= exfat_swap_activate,
 };
 
 static inline unsigned long exfat_hash(loff_t i_pos)

---
base-commit: e771677c937da5808f7b6c1f0e4a97ec1a84f8a8
change-id: 20260618-exfat_swap_activate-86e4f1a475ef

Best regards,
-- 
Andrea Cervesato <andrea.cervesato@suse.com>


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-06-25 13:56 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-18  9:33 [PATCH] exfat: implement swap activate Andrea Cervesato
2026-06-18 18:15 ` David Timber
2026-06-18 19:59 ` David Timber
2026-06-25 13:56   ` Andrea Cervesato
2026-06-24  2:54 ` kernel test robot
2026-06-24  6:02 ` kernel test robot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox