fs/exfat/iomap.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-)
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
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.
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
>
© 2016 - 2026 Red Hat, Inc.