182a81a73SNamjae Jeon // SPDX-License-Identifier: GPL-2.0-or-later 282a81a73SNamjae Jeon /* 382a81a73SNamjae Jeon * iomap callack functions 482a81a73SNamjae Jeon * 582a81a73SNamjae Jeon * Copyright (C) 2026 Namjae Jeon <linkinjeon@kernel.org> 682a81a73SNamjae Jeon */ 782a81a73SNamjae Jeon 882a81a73SNamjae Jeon #include <linux/iomap.h> 982a81a73SNamjae Jeon #include <linux/pagemap.h> 1082a81a73SNamjae Jeon 1182a81a73SNamjae Jeon #include "exfat_raw.h" 1282a81a73SNamjae Jeon #include "exfat_fs.h" 1382a81a73SNamjae Jeon #include "iomap.h" 1482a81a73SNamjae Jeon 15867b9c96SNamjae Jeon /* 16867b9c96SNamjae Jeon * exfat_file_write_dio_end_io - Direct I/O write completion handler 17867b9c96SNamjae Jeon * 18867b9c96SNamjae Jeon * Updates i_size if the write extended the file. Called from the dio layer 19867b9c96SNamjae Jeon * after I/O completion. 20867b9c96SNamjae Jeon */ 21867b9c96SNamjae Jeon static int exfat_file_write_dio_end_io(struct kiocb *iocb, ssize_t size, 22867b9c96SNamjae Jeon int error, unsigned int flags) 23867b9c96SNamjae Jeon { 24867b9c96SNamjae Jeon struct inode *inode = file_inode(iocb->ki_filp); 25867b9c96SNamjae Jeon 26867b9c96SNamjae Jeon if (error) 27867b9c96SNamjae Jeon return error; 28867b9c96SNamjae Jeon 29867b9c96SNamjae Jeon if (size && i_size_read(inode) < iocb->ki_pos + size) { 30867b9c96SNamjae Jeon i_size_write(inode, iocb->ki_pos + size); 31867b9c96SNamjae Jeon mark_inode_dirty(inode); 32867b9c96SNamjae Jeon } 33867b9c96SNamjae Jeon 34867b9c96SNamjae Jeon return 0; 35867b9c96SNamjae Jeon } 36867b9c96SNamjae Jeon 37867b9c96SNamjae Jeon const struct iomap_dio_ops exfat_write_dio_ops = { 38867b9c96SNamjae Jeon .end_io = exfat_file_write_dio_end_io, 39867b9c96SNamjae Jeon }; 40867b9c96SNamjae Jeon 4182a81a73SNamjae Jeon static int __exfat_iomap_begin(struct inode *inode, loff_t offset, loff_t length, 4282a81a73SNamjae Jeon unsigned int flags, struct iomap *iomap, bool may_alloc) 4382a81a73SNamjae Jeon { 4482a81a73SNamjae Jeon struct super_block *sb = inode->i_sb; 4582a81a73SNamjae Jeon struct exfat_sb_info *sbi = EXFAT_SB(sb); 4682a81a73SNamjae Jeon struct exfat_inode_info *ei = EXFAT_I(inode); 4782a81a73SNamjae Jeon unsigned int cluster, num_clusters; 4882a81a73SNamjae Jeon loff_t cluster_offset, cluster_length; 4982a81a73SNamjae Jeon int err; 5082a81a73SNamjae Jeon bool balloc = false; 5182a81a73SNamjae Jeon 5282a81a73SNamjae Jeon if (!may_alloc) { 5382a81a73SNamjae Jeon /* Completely beyond EOF. Treat as hole */ 5482a81a73SNamjae Jeon if (i_size_read(inode) <= offset) { 5582a81a73SNamjae Jeon iomap->type = IOMAP_HOLE; 5682a81a73SNamjae Jeon iomap->addr = IOMAP_NULL_ADDR; 5782a81a73SNamjae Jeon iomap->offset = offset; 5882a81a73SNamjae Jeon iomap->length = length; 5982a81a73SNamjae Jeon return 0; 6082a81a73SNamjae Jeon } 6182a81a73SNamjae Jeon 6282a81a73SNamjae Jeon /* Clamp length if the requested range goes beyond i_size */ 6382a81a73SNamjae Jeon if (offset + length > i_size_read(inode)) 6482a81a73SNamjae Jeon length = round_up(i_size_read(inode), 6582a81a73SNamjae Jeon i_blocksize(inode)) - offset; 6682a81a73SNamjae Jeon } 6782a81a73SNamjae Jeon 6882a81a73SNamjae Jeon num_clusters = exfat_bytes_to_cluster_round_up(sbi, 6982a81a73SNamjae Jeon offset + length) - exfat_bytes_to_cluster(sbi, offset); 7082a81a73SNamjae Jeon 7182a81a73SNamjae Jeon mutex_lock(&sbi->s_lock); 7282a81a73SNamjae Jeon iomap->bdev = inode->i_sb->s_bdev; 7382a81a73SNamjae Jeon iomap->offset = offset; 7482a81a73SNamjae Jeon 7582a81a73SNamjae Jeon err = exfat_map_cluster(inode, exfat_bytes_to_cluster(sbi, offset), 7682a81a73SNamjae Jeon &cluster, &num_clusters, may_alloc, &balloc); 7782a81a73SNamjae Jeon if (err) 7882a81a73SNamjae Jeon goto out; 7982a81a73SNamjae Jeon 8082a81a73SNamjae Jeon cluster_offset = exfat_cluster_offset(sbi, offset); 8182a81a73SNamjae Jeon cluster_length = exfat_cluster_to_bytes(sbi, num_clusters); 8282a81a73SNamjae Jeon 8382a81a73SNamjae Jeon iomap->length = min_t(loff_t, length, cluster_length - cluster_offset); 8482a81a73SNamjae Jeon iomap->addr = exfat_cluster_to_phys_bytes(sbi, cluster) + cluster_offset; 8582a81a73SNamjae Jeon iomap->type = IOMAP_MAPPED; 8682a81a73SNamjae Jeon iomap->flags = IOMAP_F_MERGED; 8782a81a73SNamjae Jeon 8882a81a73SNamjae Jeon if (may_alloc || flags & IOMAP_ZERO) { 8982a81a73SNamjae Jeon if (balloc) 9082a81a73SNamjae Jeon iomap->flags |= IOMAP_F_NEW; 9182a81a73SNamjae Jeon else if (iomap->offset + iomap->length >= ei->valid_size) { 9282a81a73SNamjae Jeon /* 9382a81a73SNamjae Jeon * This is a write that starts at or extends beyond 9482a81a73SNamjae Jeon * the current valid_size. The region between the old 9582a81a73SNamjae Jeon * valid_size and the end of this write needs to be 9682a81a73SNamjae Jeon * zeroed in the page cache to prevent stale data 9782a81a73SNamjae Jeon * exposure (see IOMAP_F_ZERO_TAIL handling in 9882a81a73SNamjae Jeon * __iomap_write_begin()). 9982a81a73SNamjae Jeon */ 10082a81a73SNamjae Jeon iomap->flags |= IOMAP_F_ZERO_TAIL; 10182a81a73SNamjae Jeon } 10282a81a73SNamjae Jeon } else { 103b4b7fe2cSNamjae Jeon /* 104b4b7fe2cSNamjae Jeon * valid_size is tracked in byte granularity and 105b4b7fe2cSNamjae Jeon * marks the exact boundary between valid data and 106b4b7fe2cSNamjae Jeon * holes (or unwritten space). 107b4b7fe2cSNamjae Jeon * 108b4b7fe2cSNamjae Jeon * When IOMAP_REPORT is set (used by lseek(SEEK_HOLE) 109b4b7fe2cSNamjae Jeon * and SEEK_DATA), we return IOMAP_HOLE. This allows 110b4b7fe2cSNamjae Jeon * iomap_seek_hole_iter() to directly return the 111b4b7fe2cSNamjae Jeon * precise byte position. 112b4b7fe2cSNamjae Jeon * 113b4b7fe2cSNamjae Jeon * For normal I/O paths (without IOMAP_REPORT) we 114b4b7fe2cSNamjae Jeon * return IOMAP_UNWRITTEN so the write path can 115b4b7fe2cSNamjae Jeon * distinguish it from a real hole. 116b4b7fe2cSNamjae Jeon */ 11782a81a73SNamjae Jeon if (offset >= ei->valid_size) { 118b4b7fe2cSNamjae Jeon iomap->type = flags & IOMAP_REPORT ? 119b4b7fe2cSNamjae Jeon IOMAP_HOLE : IOMAP_UNWRITTEN; 12082a81a73SNamjae Jeon } else if (offset + iomap->length > ei->valid_size) { 121b4b7fe2cSNamjae Jeon if (flags & IOMAP_REPORT) { 122b4b7fe2cSNamjae Jeon /* 123b4b7fe2cSNamjae Jeon * For SEEK_HOLE/SEEK_DATA, clip the length 124b4b7fe2cSNamjae Jeon * to the exact byte boundary (valid_size). 125b4b7fe2cSNamjae Jeon * This ensures the caller gets the precise 126b4b7fe2cSNamjae Jeon * hole position in byte units. 127b4b7fe2cSNamjae Jeon */ 128b4b7fe2cSNamjae Jeon iomap->length = ei->valid_size - iomap->offset; 129b4b7fe2cSNamjae Jeon } else 13082a81a73SNamjae Jeon iomap->length = round_up(ei->valid_size, 13182a81a73SNamjae Jeon i_blocksize(inode)) - 13282a81a73SNamjae Jeon iomap->offset; 13382a81a73SNamjae Jeon } 13482a81a73SNamjae Jeon } 13582a81a73SNamjae Jeon 13682a81a73SNamjae Jeon iomap->flags |= IOMAP_F_MERGED; 13782a81a73SNamjae Jeon out: 13882a81a73SNamjae Jeon mutex_unlock(&sbi->s_lock); 13982a81a73SNamjae Jeon return err; 14082a81a73SNamjae Jeon } 14182a81a73SNamjae Jeon 14282a81a73SNamjae Jeon static int exfat_iomap_begin(struct inode *inode, loff_t offset, loff_t length, 14382a81a73SNamjae Jeon unsigned int flags, struct iomap *iomap, struct iomap *srcmap) 14482a81a73SNamjae Jeon { 14582a81a73SNamjae Jeon return __exfat_iomap_begin(inode, offset, length, flags, iomap, false); 14682a81a73SNamjae Jeon } 14782a81a73SNamjae Jeon 14882a81a73SNamjae Jeon static int exfat_write_iomap_begin(struct inode *inode, loff_t offset, loff_t length, 14982a81a73SNamjae Jeon unsigned int flags, struct iomap *iomap, struct iomap *srcmap) 15082a81a73SNamjae Jeon { 15182a81a73SNamjae Jeon return __exfat_iomap_begin(inode, offset, length, flags, iomap, true); 15282a81a73SNamjae Jeon } 15382a81a73SNamjae Jeon 15482a81a73SNamjae Jeon const struct iomap_ops exfat_iomap_ops = { 15582a81a73SNamjae Jeon .iomap_begin = exfat_iomap_begin, 15682a81a73SNamjae Jeon }; 15782a81a73SNamjae Jeon 15882a81a73SNamjae Jeon /* 15982a81a73SNamjae Jeon * exfat_write_iomap_end - Update the state after write 16082a81a73SNamjae Jeon * 16182a81a73SNamjae Jeon * Extends ->valid_size to cover the newly written range. 16282a81a73SNamjae Jeon * Marks the inode dirty if metadata was changed. 16382a81a73SNamjae Jeon */ 16482a81a73SNamjae Jeon static int exfat_write_iomap_end(struct inode *inode, loff_t pos, loff_t length, 16582a81a73SNamjae Jeon ssize_t written, unsigned int flags, struct iomap *iomap) 16682a81a73SNamjae Jeon { 16782a81a73SNamjae Jeon struct exfat_inode_info *ei = EXFAT_I(inode); 16882a81a73SNamjae Jeon bool dirtied = false; 16982a81a73SNamjae Jeon loff_t end; 17082a81a73SNamjae Jeon 17182a81a73SNamjae Jeon if (!written) 17282a81a73SNamjae Jeon return 0; 17382a81a73SNamjae Jeon 17482a81a73SNamjae Jeon end = pos + written; 17582a81a73SNamjae Jeon 17682a81a73SNamjae Jeon if (ei->valid_size < end) { 17782a81a73SNamjae Jeon ei->valid_size = end; 17882a81a73SNamjae Jeon if (ei->zeroed_size < end) 17982a81a73SNamjae Jeon ei->zeroed_size = end; 18082a81a73SNamjae Jeon dirtied = true; 18182a81a73SNamjae Jeon } 18282a81a73SNamjae Jeon 18382a81a73SNamjae Jeon if (dirtied || iomap->flags & IOMAP_F_SIZE_CHANGED) 18482a81a73SNamjae Jeon mark_inode_dirty(inode); 18582a81a73SNamjae Jeon 18682a81a73SNamjae Jeon return written; 18782a81a73SNamjae Jeon } 18882a81a73SNamjae Jeon 18982a81a73SNamjae Jeon const struct iomap_ops exfat_write_iomap_ops = { 19082a81a73SNamjae Jeon .iomap_begin = exfat_write_iomap_begin, 19182a81a73SNamjae Jeon .iomap_end = exfat_write_iomap_end, 19282a81a73SNamjae Jeon }; 19382a81a73SNamjae Jeon 19482a81a73SNamjae Jeon /* 19582a81a73SNamjae Jeon * exfat_writeback_range - Map folio during writeback 19682a81a73SNamjae Jeon * 19782a81a73SNamjae Jeon * Called for each folio during writeback. If the folio falls outside the 19882a81a73SNamjae Jeon * current iomap, remaps by calling read_iomap_begin. 19982a81a73SNamjae Jeon */ 20082a81a73SNamjae Jeon static ssize_t exfat_writeback_range(struct iomap_writepage_ctx *wpc, 20182a81a73SNamjae Jeon struct folio *folio, u64 offset, unsigned int len, u64 end_pos) 20282a81a73SNamjae Jeon { 20382a81a73SNamjae Jeon if (offset < wpc->iomap.offset || 20482a81a73SNamjae Jeon offset >= wpc->iomap.offset + wpc->iomap.length) { 20582a81a73SNamjae Jeon int error; 20682a81a73SNamjae Jeon 20782a81a73SNamjae Jeon error = __exfat_iomap_begin(wpc->inode, offset, len, 20882a81a73SNamjae Jeon 0, &wpc->iomap, false); 20982a81a73SNamjae Jeon if (error) 21082a81a73SNamjae Jeon return error; 21182a81a73SNamjae Jeon } 21282a81a73SNamjae Jeon 21382a81a73SNamjae Jeon return iomap_add_to_ioend(wpc, folio, offset, end_pos, len); 21482a81a73SNamjae Jeon } 21582a81a73SNamjae Jeon 21682a81a73SNamjae Jeon const struct iomap_writeback_ops exfat_writeback_ops = { 21782a81a73SNamjae Jeon .writeback_range = exfat_writeback_range, 21882a81a73SNamjae Jeon .writeback_submit = iomap_ioend_writeback_submit, 21982a81a73SNamjae Jeon }; 22082a81a73SNamjae Jeon 22182a81a73SNamjae Jeon /** 22282a81a73SNamjae Jeon * exfat_iomap_read_end_io - iomap read bio completion handler for exFAT 22382a81a73SNamjae Jeon * @bio: bio that has completed reading 22482a81a73SNamjae Jeon * 22582a81a73SNamjae Jeon * exfat_iomap_begin() rounds up MAPPED extents to the block boundary of 22682a81a73SNamjae Jeon * valid_size. This ensures that any subsequent blocks are treated as 22782a81a73SNamjae Jeon * IOMAP_UNWRITTEN, but it also causes the "straddle block" containing 22882a81a73SNamjae Jeon * valid_size to be read from disk. The disk data beyond valid_size in 22982a81a73SNamjae Jeon * this block is stale and must be zeroed to prevent data leakage. 23082a81a73SNamjae Jeon */ 23182a81a73SNamjae Jeon static void exfat_iomap_read_end_io(struct bio *bio) 23282a81a73SNamjae Jeon { 23382a81a73SNamjae Jeon int error = blk_status_to_errno(bio->bi_status); 23482a81a73SNamjae Jeon struct folio_iter iter; 23582a81a73SNamjae Jeon 23682a81a73SNamjae Jeon bio_for_each_folio_all(iter, bio) { 23782a81a73SNamjae Jeon struct folio *folio = iter.folio; 23882a81a73SNamjae Jeon struct exfat_inode_info *ei = EXFAT_I(folio->mapping->host); 23982a81a73SNamjae Jeon s64 valid_size; 24082a81a73SNamjae Jeon loff_t pos = folio_pos(folio); 24182a81a73SNamjae Jeon 24282a81a73SNamjae Jeon valid_size = ei->valid_size; 24382a81a73SNamjae Jeon if (pos + iter.offset < valid_size && 24482a81a73SNamjae Jeon pos + iter.offset + iter.length > valid_size) 24582a81a73SNamjae Jeon folio_zero_segment(folio, offset_in_folio(folio, valid_size), 24682a81a73SNamjae Jeon iter.offset + iter.length); 24782a81a73SNamjae Jeon 24882a81a73SNamjae Jeon iomap_finish_folio_read(folio, iter.offset, iter.length, error); 24982a81a73SNamjae Jeon } 25082a81a73SNamjae Jeon bio_put(bio); 25182a81a73SNamjae Jeon } 25282a81a73SNamjae Jeon 25382a81a73SNamjae Jeon static void exfat_iomap_bio_submit_read(const struct iomap_iter *iter, 25482a81a73SNamjae Jeon struct iomap_read_folio_ctx *ctx) 25582a81a73SNamjae Jeon { 256*044472d5SChristoph Hellwig iomap_bio_submit_read_endio(iter, ctx, exfat_iomap_read_end_io); 25782a81a73SNamjae Jeon } 25882a81a73SNamjae Jeon 25982a81a73SNamjae Jeon const struct iomap_read_ops exfat_iomap_bio_read_ops = { 26082a81a73SNamjae Jeon .read_folio_range = iomap_bio_read_folio_range, 26182a81a73SNamjae Jeon .submit_read = exfat_iomap_bio_submit_read, 26282a81a73SNamjae Jeon }; 26303a43677SJan Polensky 26403a43677SJan Polensky int exfat_iomap_swap_activate(struct swap_info_struct *sis, 26503a43677SJan Polensky struct file *file, sector_t *span) 26603a43677SJan Polensky { 26703a43677SJan Polensky return iomap_swapfile_activate(sis, file, span, &exfat_iomap_ops); 26803a43677SJan Polensky } 269