mirror of https://lore.kernel.org/lkml/
 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	[flat|nested] 6+ messages in thread

* Re: [PATCH] exfat: implement swap activate
  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
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: David Timber @ 2026-06-18 18:15 UTC (permalink / raw)
  To: Andrea Cervesato, Namjae Jeon, Sungjong Seo, Yuezhang Mo
  Cc: linux-fsdevel, linux-kernel, jack, chrubis, Andrea Cervesato

On 6/18/26 18:33, Andrea Cervesato wrote:
> 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));
This incurs uninterruptible write amplification. Say you fallocated()'d
or truncate()'d a 1TB file on a USB stick, it will probably take as long
as it takes to fill the VDL hole. Some people will find that
unacceptible. This is a problem unique to exfat, and not the limitation
of the kernel implementation but rather that of the exfat format itself.
There's nothing we can do about it except somehow we make the write
amplification cases interruptible/cancelable from userspace if need be.

From what I gathered, userland used to dd swap images before doing
mkswap and using them for filesystems without fallocate() support(like
FAT on a live usb stick). Even with the ones that did, there was no
guarantee that the swap would function properly, it was not worth it.

tbh, I genuinely wonder if there's a real use case for using swap images
on exfat. Has any user come forward with this bug with a valid use case?
I think we should ask ourselves if we're patching exfat to just make the
test work rather than fixing the real problems. You can't make
fs-agnostic test cases. Some fs need special treatment, unfortunately.
If we didn't allow that, (x)fstests wouldn't just be possible.

This has been my two cents. Now it's up to maintainers to decide. At the
end of the day, defered write amplification on swapon() could just
become another quirk of exfat.

Davo

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

* Re: [PATCH] exfat: implement swap activate
  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
  3 siblings, 1 reply; 6+ messages in thread
From: David Timber @ 2026-06-18 19:59 UTC (permalink / raw)
  To: Andrea Cervesato, Namjae Jeon, Sungjong Seo, Yuezhang Mo
  Cc: linux-fsdevel, linux-kernel, jack, chrubis, Andrea Cervesato

On 6/18/26 18:33, Andrea Cervesato wrote:
> 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)) {
Also, if we want to do this, please ensure that ei->valid_size is cached
in by locking the inode or etc before referencing it.

I'll see if I can make exfat_extend_valid_size() interruptible by
placing missing fatal_signal_pending(current) check in both stable and
iomap branches.

Davo

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

* Re: [PATCH] exfat: implement swap activate
  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-24  2:54 ` kernel test robot
  2026-06-24  6:02 ` kernel test robot
  3 siblings, 0 replies; 6+ messages in thread
From: kernel test robot @ 2026-06-24  2:54 UTC (permalink / raw)
  To: Andrea Cervesato, Namjae Jeon, Sungjong Seo, Yuezhang Mo, David Timber
  Cc: oe-kbuild-all, linux-fsdevel, linux-kernel, jack, chrubis,
	Andrea Cervesato

Hi Andrea,

kernel test robot noticed the following build errors:

[auto build test ERROR on e771677c937da5808f7b6c1f0e4a97ec1a84f8a8]

url:    https://github.com/intel-lab-lkp/linux/commits/Andrea-Cervesato/exfat-implement-swap-activate/20260622-172327
base:   e771677c937da5808f7b6c1f0e4a97ec1a84f8a8
patch link:    https://lore.kernel.org/r/20260618-exfat_swap_activate-v1-1-4c6a465df295%40suse.com
patch subject: [PATCH] exfat: implement swap activate
config: microblaze-allyesconfig (https://download.01.org/0day-ci/archive/20260624/202606241018.4N100hSQ-lkp@intel.com/config)
compiler: microblaze-linux-gcc (GCC) 16.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260624/202606241018.4N100hSQ-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202606241018.4N100hSQ-lkp@intel.com/

All errors (new ones prefixed by >>):

   fs/exfat/inode.c: In function 'exfat_swap_activate':
>> fs/exfat/inode.c:556:16: error: implicit declaration of function 'generic_swapfile_activate' [-Wimplicit-function-declaration]
     556 |         return generic_swapfile_activate(sis, file, span);
         |                ^~~~~~~~~~~~~~~~~~~~~~~~~


vim +/generic_swapfile_activate +556 fs/exfat/inode.c

   537	
   538	static int exfat_swap_activate(struct swap_info_struct *sis,
   539				       struct file *file, sector_t *span)
   540	{
   541		struct inode *inode = file_inode(file);
   542		struct exfat_inode_info *ei = EXFAT_I(inode);
   543		int ret;
   544	
   545		/*
   546		 * exfat's fallocate allocates clusters without updating valid_size,
   547		 * leaving them invisible to bmap(). Extend valid_size to i_size so
   548		 * that generic_swapfile_activate() can map all blocks.
   549		 */
   550		if (ei->valid_size < i_size_read(inode)) {
   551			ret = exfat_extend_valid_size(inode, i_size_read(inode));
   552			if (ret)
   553				return ret;
   554		}
   555	
 > 556		return generic_swapfile_activate(sis, file, span);
   557	}
   558	

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

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

* Re: [PATCH] exfat: implement swap activate
  2026-06-18  9:33 [PATCH] exfat: implement swap activate Andrea Cervesato
                   ` (2 preceding siblings ...)
  2026-06-24  2:54 ` kernel test robot
@ 2026-06-24  6:02 ` kernel test robot
  3 siblings, 0 replies; 6+ messages in thread
From: kernel test robot @ 2026-06-24  6:02 UTC (permalink / raw)
  To: Andrea Cervesato, Namjae Jeon, Sungjong Seo, Yuezhang Mo, David Timber
  Cc: llvm, oe-kbuild-all, linux-fsdevel, linux-kernel, jack, chrubis,
	Andrea Cervesato

Hi Andrea,

kernel test robot noticed the following build errors:

[auto build test ERROR on e771677c937da5808f7b6c1f0e4a97ec1a84f8a8]

url:    https://github.com/intel-lab-lkp/linux/commits/Andrea-Cervesato/exfat-implement-swap-activate/20260622-172327
base:   e771677c937da5808f7b6c1f0e4a97ec1a84f8a8
patch link:    https://lore.kernel.org/r/20260618-exfat_swap_activate-v1-1-4c6a465df295%40suse.com
patch subject: [PATCH] exfat: implement swap activate
config: hexagon-allmodconfig (https://download.01.org/0day-ci/archive/20260624/202606241341.hkQXa0PG-lkp@intel.com/config)
compiler: clang version 23.0.0git (https://github.com/llvm/llvm-project a9b492db3d50683e446cd1a5c9ffaf4e92cb77a7)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260624/202606241341.hkQXa0PG-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202606241341.hkQXa0PG-lkp@intel.com/

All errors (new ones prefixed by >>, old ones prefixed by <<):

>> ERROR: modpost: "generic_swapfile_activate" [fs/exfat/exfat.ko] undefined!

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

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

* Re: [PATCH] exfat: implement swap activate
  2026-06-18 19:59 ` David Timber
@ 2026-06-25 13:56   ` Andrea Cervesato
  0 siblings, 0 replies; 6+ messages in thread
From: Andrea Cervesato @ 2026-06-25 13:56 UTC (permalink / raw)
  To: David Timber
  Cc: Andrea Cervesato, Namjae Jeon, Sungjong Seo, Yuezhang Mo,
	linux-fsdevel, linux-kernel, jack, chrubis

Hi David,

thanks for explaining your point of view. I will wait for other
maintainers to decide if this feature can be integrated into the
kernel or not, before proceding with the v2.

Best regards,
--
Andrea Cervesato
SUSE QE Automation Engineer Linux
andrea.cervesato@suse.com

^ permalink raw reply	[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

Powered by JetHome