[PATCH] exfat: preserve allocated extents for swap activation

Harshit Mogalapalli posted 1 patch 8 hours ago
fs/exfat/iomap.c | 16 ++++++----------
1 file changed, 6 insertions(+), 10 deletions(-)
[PATCH] exfat: preserve allocated extents for swap activation
Posted by Harshit Mogalapalli 8 hours ago
exFAT reports allocated ranges beyond valid_size as IOMAP_HOLE whenever
IOMAP_REPORT is set. iomap_swapfile_activate() also uses IOMAP_REPORT
while collecting a swapfile's physical extents, so it treats the
preallocated tail as unallocated and rejects the file with -EINVAL.

Allocated space beyond valid_size is not a hole. Keep it as
IOMAP_UNWRITTEN so iomap consumers that need physical extent identity,
such as swap activation, can still use it. The iomap seek helpers
already handle unwritten extents appropriately for SEEK_HOLE and
SEEK_DATA.

Fixes: b4b7fe2c7cbf ("exfat: add support for SEEK_HOLE and SEEK_DATA in llseek")
Assisted-by: Codex:GPT-5.6
Signed-off-by: Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com>
---
LTP swapon/swapoff tests failed on exFAT, with this patch the tests
pass.
---
 fs/exfat/iomap.c | 16 ++++++----------
 1 file changed, 6 insertions(+), 10 deletions(-)

diff --git a/fs/exfat/iomap.c b/fs/exfat/iomap.c
index 190fc6471f84..24d93288a432 100644
--- a/fs/exfat/iomap.c
+++ b/fs/exfat/iomap.c
@@ -105,18 +105,14 @@ static int __exfat_iomap_begin(struct inode *inode, loff_t offset, loff_t length
 		 * marks the exact boundary between valid data and
 		 * holes (or unwritten space).
 		 *
-		 * When IOMAP_REPORT is set (used by lseek(SEEK_HOLE)
-		 * and SEEK_DATA), we return IOMAP_HOLE. This allows
-		 * iomap_seek_hole_iter() to directly return the
-		 * precise byte position.
-		 *
-		 * For normal I/O paths (without IOMAP_REPORT) we
-		 * return IOMAP_UNWRITTEN so the write path can
-		 * distinguish it from a real hole.
+		 * Allocated space beyond valid_size is not a hole. Report it
+		 * as IOMAP_UNWRITTEN so iomap consumers that need physical
+		 * extent identity, such as swap activation, can still use it.
+		 * The iomap seek helpers already handle unwritten extents
+		 * appropriately for SEEK_HOLE and SEEK_DATA.
 		 */
 		if (offset >= ei->valid_size) {
-			iomap->type = flags & IOMAP_REPORT ?
-				IOMAP_HOLE : IOMAP_UNWRITTEN;
+			iomap->type = IOMAP_UNWRITTEN;
 		} else if (offset + iomap->length > ei->valid_size) {
 			if (flags & IOMAP_REPORT) {
 				/*
-- 
2.50.1
Re: [PATCH] exfat: preserve allocated extents for swap activation
Posted by Namjae Jeon 3 hours ago
On Fri, Jul 24, 2026 at 4:19 AM Harshit Mogalapalli
<harshit.m.mogalapalli@oracle.com> wrote:
>
> exFAT reports allocated ranges beyond valid_size as IOMAP_HOLE whenever
> IOMAP_REPORT is set. iomap_swapfile_activate() also uses IOMAP_REPORT
> while collecting a swapfile's physical extents, so it treats the
> preallocated tail as unallocated and rejects the file with -EINVAL.
>
> Allocated space beyond valid_size is not a hole. Keep it as
> IOMAP_UNWRITTEN so iomap consumers that need physical extent identity,
> such as swap activation, can still use it. The iomap seek helpers
> already handle unwritten extents appropriately for SEEK_HOLE and
> SEEK_DATA.
>
> Fixes: b4b7fe2c7cbf ("exfat: add support for SEEK_HOLE and SEEK_DATA in llseek")
> Assisted-by: Codex:GPT-5.6
> Signed-off-by: Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com>
> ---
> LTP swapon/swapoff tests failed on exFAT, with this patch the tests
> pass.
> ---
>  fs/exfat/iomap.c | 16 ++++++----------
>  1 file changed, 6 insertions(+), 10 deletions(-)
>
> diff --git a/fs/exfat/iomap.c b/fs/exfat/iomap.c
> index 190fc6471f84..24d93288a432 100644
> --- a/fs/exfat/iomap.c
> +++ b/fs/exfat/iomap.c
> @@ -105,18 +105,14 @@ static int __exfat_iomap_begin(struct inode *inode, loff_t offset, loff_t length
>                  * marks the exact boundary between valid data and
>                  * holes (or unwritten space).
>                  *
> -                * When IOMAP_REPORT is set (used by lseek(SEEK_HOLE)
> -                * and SEEK_DATA), we return IOMAP_HOLE. This allows
> -                * iomap_seek_hole_iter() to directly return the
> -                * precise byte position.
> -                *
> -                * For normal I/O paths (without IOMAP_REPORT) we
> -                * return IOMAP_UNWRITTEN so the write path can
> -                * distinguish it from a real hole.
> +                * Allocated space beyond valid_size is not a hole. Report it
> +                * as IOMAP_UNWRITTEN so iomap consumers that need physical
> +                * extent identity, such as swap activation, can still use it.
> +                * The iomap seek helpers already handle unwritten extents
> +                * appropriately for SEEK_HOLE and SEEK_DATA.
>                  */
>                 if (offset >= ei->valid_size) {
> -                       iomap->type = flags & IOMAP_REPORT ?
> -                               IOMAP_HOLE : IOMAP_UNWRITTEN;
> +                       iomap->type = IOMAP_UNWRITTEN;
Returning IOMAP_UNWRITTEN with IOMAP_REPORT can make SEEK_HOLE rely on
page-cache/block-granularity handling, which can lose the
byte-accurate valid_size boundary. Before activating the swapfile,
extending ->valid_size to i_size through exfat_extend_valid_size()
safely zeroes the preallocated range and makes it valid for swap use.

Thanks.
Re: [PATCH] exfat: preserve allocated extents for swap activation
Posted by Darrick J. Wong 6 hours ago
On Thu, Jul 23, 2026 at 12:19:36PM -0700, Harshit Mogalapalli wrote:
> exFAT reports allocated ranges beyond valid_size as IOMAP_HOLE whenever
> IOMAP_REPORT is set. iomap_swapfile_activate() also uses IOMAP_REPORT
> while collecting a swapfile's physical extents, so it treats the
> preallocated tail as unallocated and rejects the file with -EINVAL.
> 
> Allocated space beyond valid_size is not a hole. Keep it as
> IOMAP_UNWRITTEN so iomap consumers that need physical extent identity,
> such as swap activation, can still use it. The iomap seek helpers
> already handle unwritten extents appropriately for SEEK_HOLE and
> SEEK_DATA.
> 
> Fixes: b4b7fe2c7cbf ("exfat: add support for SEEK_HOLE and SEEK_DATA in llseek")
> Assisted-by: Codex:GPT-5.6
> Signed-off-by: Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com>
> ---
> LTP swapon/swapoff tests failed on exFAT, with this patch the tests
> pass.
> ---
>  fs/exfat/iomap.c | 16 ++++++----------
>  1 file changed, 6 insertions(+), 10 deletions(-)
> 
> diff --git a/fs/exfat/iomap.c b/fs/exfat/iomap.c
> index 190fc6471f84..24d93288a432 100644
> --- a/fs/exfat/iomap.c
> +++ b/fs/exfat/iomap.c
> @@ -105,18 +105,14 @@ static int __exfat_iomap_begin(struct inode *inode, loff_t offset, loff_t length
>  		 * marks the exact boundary between valid data and
>  		 * holes (or unwritten space).
>  		 *
> -		 * When IOMAP_REPORT is set (used by lseek(SEEK_HOLE)
> -		 * and SEEK_DATA), we return IOMAP_HOLE. This allows
> -		 * iomap_seek_hole_iter() to directly return the
> -		 * precise byte position.
> -		 *
> -		 * For normal I/O paths (without IOMAP_REPORT) we
> -		 * return IOMAP_UNWRITTEN so the write path can
> -		 * distinguish it from a real hole.
> +		 * Allocated space beyond valid_size is not a hole. Report it
> +		 * as IOMAP_UNWRITTEN so iomap consumers that need physical
> +		 * extent identity, such as swap activation, can still use it.
> +		 * The iomap seek helpers already handle unwritten extents
> +		 * appropriately for SEEK_HOLE and SEEK_DATA.
>  		 */
>  		if (offset >= ei->valid_size) {
> -			iomap->type = flags & IOMAP_REPORT ?
> -				IOMAP_HOLE : IOMAP_UNWRITTEN;
> +			iomap->type = IOMAP_UNWRITTEN;

Assuming that the space between valid_size and i_size are allocated to
clusters and simply not yet written to, I think it's perfectly valid to
use IOMAP_UNWRITTEN here.  That's definitely not a hole.

If that assumption is correct then
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>

--D


>  		} else if (offset + iomap->length > ei->valid_size) {
>  			if (flags & IOMAP_REPORT) {
>  				/*
> -- 
> 2.50.1
>