linux-next: manual merge of the vfs-brauner tree with the exfat tree

Mark Brown posted 1 patch 5 days, 20 hours ago
linux-next: manual merge of the vfs-brauner tree with the exfat tree
Posted by Mark Brown 5 days, 20 hours ago
Hi all,

Today's linux-next merge of the vfs-brauner tree got a conflict in:

  fs/exfat/file.c

between commit:

  9b373eacd6e6c4 ("exfat: add iomap buffered I/O support")

from the exfat tree and commit:

  5f36c9ca333360 ("fs: Rename generic_file_fsync() to simple_fsync()")

from the vfs-brauner tree.

I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging.  You may also want to consider cooperating

diff --combined fs/exfat/file.c
index 9a30c32b3a0526,4e8d34a75b6638..00000000000000
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@@ -13,12 -13,9 +13,12 @@@
  #include <linux/msdos_fs.h>
  #include <linux/writeback.h>
  #include <linux/filelock.h>
 +#include <linux/falloc.h>
 +#include <linux/iomap.h>
  
  #include "exfat_raw.h"
  #include "exfat_fs.h"
 +#include "iomap.h"
  
  static int exfat_cont_expand(struct inode *inode, loff_t size)
  {
@@@ -57,7 -54,7 +57,7 @@@
  	clu.flags = ei->flags;
  
  	ret = exfat_alloc_cluster(inode, new_num_clusters - num_clusters,
 -			&clu, inode_needs_sync(inode));
 +			&clu, inode_needs_sync(inode), false);
  	if (ret)
  		return ret;
  
@@@ -93,45 -90,6 +93,45 @@@ free_clu
  	return -EIO;
  }
  
 +/*
 + * Preallocate space for a file. This implements exfat's fallocate file
 + * operation, which gets called from sys_fallocate system call. User space
 + * requests len bytes at offset. In contrary to fat, we only support
 + * FALLOC_FL_ALLOCATE_RANGE because by leaving the valid data length(VDL)
 + * field, it is unnecessary to zero out the newly allocated clusters.
 + */
 +static long exfat_fallocate(struct file *file, int mode,
 +			  loff_t offset, loff_t len)
 +{
 +	struct inode *inode = file->f_mapping->host;
 +	loff_t newsize = offset + len;
 +	int err = 0;
 +
 +	/* No support for other modes */
 +	if (mode != FALLOC_FL_ALLOCATE_RANGE)
 +		return -EOPNOTSUPP;
 +
 +	/* No support for dir */
 +	if (!S_ISREG(inode->i_mode))
 +		return -EOPNOTSUPP;
 +
 +	if (unlikely(exfat_forced_shutdown(inode->i_sb)))
 +		return -EIO;
 +
 +	inode_lock(inode);
 +
 +	if (newsize <= i_size_read(inode))
 +		goto error;
 +
 +	/* This is just an expanding truncate */
 +	err = exfat_cont_expand(inode, newsize);
 +
 +error:
 +	inode_unlock(inode);
 +
 +	return err;
 +}
 +
  static bool exfat_allow_set_time(struct mnt_idmap *idmap,
  				 struct exfat_sb_info *sbi, struct inode *inode)
  {
@@@ -337,18 -295,7 +337,18 @@@ int exfat_setattr(struct mnt_idmap *idm
  
  	if ((attr->ia_valid & ATTR_SIZE) &&
  	    attr->ia_size > i_size_read(inode)) {
 +		loff_t old_size = i_size_read(inode);
 +
  		error = exfat_cont_expand(inode, attr->ia_size);
 +		if (!error && attr->ia_size > old_size &&
 +		    old_size % PAGE_SIZE != 0) {
 +			loff_t len = min_t(loff_t,
 +					round_up(old_size, PAGE_SIZE) - old_size,
 +					attr->ia_size - old_size);
 +			error = iomap_zero_range(inode, old_size, len,
 +					NULL, &exfat_read_iomap_ops,
 +					&exfat_iomap_folio_ops, NULL);
 +		}
  		if (error || attr->ia_valid == ATTR_SIZE)
  			return error;
  		attr->ia_valid &= ~ATTR_SIZE;
@@@ -395,10 -342,7 +395,10 @@@
  	exfat_truncate_inode_atime(inode);
  
  	if (attr->ia_valid & ATTR_SIZE) {
 -		error = exfat_block_truncate_page(inode, attr->ia_size);
 +		inode_dio_wait(inode);
 +		error = iomap_truncate_page(inode, attr->ia_size, NULL,
 +				&exfat_read_iomap_ops,
 +				&exfat_iomap_folio_ops, NULL);
  		if (error)
  			goto out;
  
@@@ -633,14 -577,10 +633,14 @@@ int exfat_file_fsync(struct file *filp
  	if (unlikely(exfat_forced_shutdown(inode->i_sb)))
  		return -EIO;
  
 -	err = simple_fsync_noflush(filp, start, end, datasync);
 +	err = file_write_and_wait_range(filp, start, end);
  	if (err)
  		return err;
  
 +	if (!datasync)
 +		err = __exfat_write_inode(inode, 1);
 +	write_inode_now(inode, !datasync);
 +
  	err = sync_blockdev(inode->i_sb->s_bdev);
  	if (err)
  		return err;
@@@ -648,72 -588,44 +648,72 @@@
  	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 off, bool bsync)
  {
 -	int err;
 -	loff_t pos;
  	struct exfat_inode_info *ei = EXFAT_I(inode);
 -	struct address_space *mapping = inode->i_mapping;
 -	const struct address_space_operations *ops = mapping->a_ops;
 +	struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
 +	loff_t old_valid_size;
 +	int ret = 0;
  
 -	pos = ei->valid_size;
 -	while (pos < new_valid_size) {
 -		u32 len;
 -		struct folio *folio;
 -		unsigned long off;
 +	mutex_lock(&sbi->s_lock);
 +	old_valid_size = ei->valid_size;
 +	mutex_unlock(&sbi->s_lock);
  
 -		len = PAGE_SIZE - (pos & (PAGE_SIZE - 1));
 -		if (pos + len > new_valid_size)
 -			len = new_valid_size - pos;
 -
 -		err = ops->write_begin(NULL, mapping, pos, len, &folio, NULL);
 -		if (err)
 -			goto out;
 -
 -		off = offset_in_folio(folio, pos);
 -		folio_zero_new_buffers(folio, off, off + len);
 -
 -		err = ops->write_end(NULL, mapping, pos, len, len, folio, NULL);
 -		if (err < 0)
 -			goto out;
 -		pos += len;
 -
 -		balance_dirty_pages_ratelimited(mapping);
 -		cond_resched();
 +	if (old_valid_size < off) {
 +		ret = iomap_zero_range(inode, old_valid_size,
 +				off - old_valid_size, NULL,
 +				&exfat_write_iomap_ops, &exfat_iomap_folio_ops,
 +				NULL);
 +		if (!ret && bsync)
 +			ret = filemap_write_and_wait_range(inode->i_mapping,
 +							   old_valid_size,
 +							   off - 1);
  	}
  
 -	return 0;
 +	return ret;
 +}
  
 +static ssize_t exfat_dio_write_iter(struct kiocb *iocb, struct iov_iter *from)
 +{
 +	ssize_t ret;
 +
 +	ret = iomap_dio_rw(iocb, from, &exfat_write_iomap_ops,
 +			&exfat_write_dio_ops, 0, NULL, 0);
 +	if (ret == -ENOTBLK)
 +		ret = 0;
 +	else if (ret < 0)
 +		goto out;
 +
 +	if (iov_iter_count(from)) {
 +		loff_t offset, end;
 +		ssize_t written;
 +		int ret2;
 +
 +		offset = iocb->ki_pos;
 +		iocb->ki_flags &= ~IOCB_DIRECT;
 +		written = iomap_file_buffered_write(iocb, from,
 +				&exfat_write_iomap_ops, &exfat_iomap_folio_ops,
 +				NULL);
 +		if (written < 0) {
 +			ret = written;
 +			goto out;
 +		}
 +
 +		ret += written;
 +		end = iocb->ki_pos + written - 1;
 +		ret2 = filemap_write_and_wait_range(iocb->ki_filp->f_mapping,
 +				offset, end);
 +		if (ret2) {
 +			ret = -EIO;
 +			goto out;
 +		}
 +		if (!ret2)
 +			invalidate_mapping_pages(iocb->ki_filp->f_mapping,
 +					offset >> PAGE_SHIFT,
 +					end >> PAGE_SHIFT);
 +	}
  out:
 -	return err;
 +	return ret;
  }
  
  static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
@@@ -724,7 -636,6 +724,7 @@@
  	struct exfat_inode_info *ei = EXFAT_I(inode);
  	loff_t pos = iocb->ki_pos;
  	loff_t valid_size;
 +	int err;
  
  	if (unlikely(exfat_forced_shutdown(inode->i_sb)))
  		return -EIO;
@@@ -740,18 -651,28 +740,18 @@@
  	if (ret <= 0)
  		goto unlock;
  
 -	if (iocb->ki_flags & IOCB_DIRECT) {
 -		unsigned long align = pos | iov_iter_alignment(iter);
 -
 -		if (!IS_ALIGNED(align, i_blocksize(inode)) &&
 -		    !IS_ALIGNED(align, bdev_logical_block_size(inode->i_sb->s_bdev))) {
 -			ret = -EINVAL;
 -			goto unlock;
 -		}
 +	err = file_modified(iocb->ki_filp);
 +	if (err) {
 +		ret = err;
 +		goto unlock;
  	}
  
 -	if (pos > valid_size) {
 -		ret = exfat_extend_valid_size(inode, pos);
 -		if (ret < 0 && ret != -ENOSPC) {
 -			exfat_err(inode->i_sb,
 -				"write: fail to zero from %llu to %llu(%zd)",
 -				valid_size, pos, ret);
 -		}
 -		if (ret < 0)
 -			goto unlock;
 -	}
 -
 -	ret = __generic_file_write_iter(iocb, iter);
 +	if (iocb->ki_flags & IOCB_DIRECT)
 +		ret = exfat_dio_write_iter(iocb, iter);
 +	else
 +		ret = iomap_file_buffered_write(iocb, iter,
 +				&exfat_write_iomap_ops, &exfat_iomap_folio_ops,
 +				NULL);
  	if (ret < 0)
  		goto unlock;
  
@@@ -778,52 -699,37 +778,52 @@@ unlock
  static ssize_t exfat_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
  {
  	struct inode *inode = file_inode(iocb->ki_filp);
 +	ssize_t ret;
  
  	if (unlikely(exfat_forced_shutdown(inode->i_sb)))
  		return -EIO;
  
 -	return generic_file_read_iter(iocb, iter);
 +	inode_lock_shared(inode);
 +
 +	if (iocb->ki_flags & IOCB_DIRECT) {
 +		size_t count = iov_iter_count(iter);
 +
 +		if ((iocb->ki_pos | count) & (inode->i_sb->s_blocksize - 1)) {
 +			ret = -EINVAL;
 +			goto inode_unlock;
 +		}
 +
 +		file_accessed(iocb->ki_filp);
 +		ret = iomap_dio_rw(iocb, iter, &exfat_read_iomap_ops, NULL, 0,
 +				NULL, 0);
 +	} else {
 +		ret = generic_file_read_iter(iocb, iter);
 +	}
 +
 +inode_unlock:
 +	inode_unlock_shared(inode);
 +
 +	return ret;
  }
  
  static vm_fault_t exfat_page_mkwrite(struct vm_fault *vmf)
  {
 -	int err;
  	struct inode *inode = file_inode(vmf->vma->vm_file);
 -	struct exfat_inode_info *ei = EXFAT_I(inode);
 -	loff_t new_valid_size;
 +	vm_fault_t ret;
  
  	if (!inode_trylock(inode))
  		return VM_FAULT_RETRY;
  
 -	new_valid_size = ((loff_t)vmf->pgoff + 1) << PAGE_SHIFT;
 -	new_valid_size = min(new_valid_size, i_size_read(inode));
 -
 -	if (ei->valid_size < new_valid_size) {
 -		err = exfat_extend_valid_size(inode, new_valid_size);
 -		if (err < 0) {
 -			inode_unlock(inode);
 -			return vmf_fs_error(err);
 -		}
 -	}
 +	sb_start_pagefault(inode->i_sb);
 +	file_update_time(vmf->vma->vm_file);
  
 +	filemap_invalidate_lock_shared(inode->i_mapping);
 +	ret = iomap_page_mkwrite(vmf, &exfat_mkwrite_iomap_ops, NULL);
 +	filemap_invalidate_unlock_shared(inode->i_mapping);
 +	sb_end_pagefault(inode->i_sb);
  	inode_unlock(inode);
  
 -	return filemap_page_mkwrite(vmf);
 +	return ret;
  }
  
  static const struct vm_operations_struct exfat_file_vm_ops = {
@@@ -839,21 -745,6 +839,21 @@@ static int exfat_file_mmap_prepare(stru
  	if (unlikely(exfat_forced_shutdown(file_inode(desc->file)->i_sb)))
  		return -EIO;
  
 +	if (vma_desc_test_flags(desc, VMA_WRITE_BIT)) {
 +		struct inode *inode = file_inode(file);
 +		loff_t from, to;
 +		int err;
 +
 +		from = ((loff_t)desc->pgoff << PAGE_SHIFT);
 +		to = min_t(loff_t, i_size_read(inode),
 +				from + vma_desc_size(desc));
 +		if (EXFAT_I(inode)->valid_size < to) {
 +			err = exfat_extend_valid_size(inode, to, false);
 +			if (err)
 +				return err;
 +		}
 +	}
 +
  	file_accessed(file);
  	desc->vm_ops = &exfat_file_vm_ops;
  	return 0;
@@@ -868,50 -759,8 +868,50 @@@ static ssize_t exfat_splice_read(struc
  	return filemap_splice_read(in, ppos, pipe, len, flags);
  }
  
 +static int exfat_file_open(struct inode *inode, struct file *filp)
 +{
 +	int err;
 +
 +	if (unlikely(exfat_forced_shutdown(inode->i_sb)))
 +		return -EIO;
 +
 +	err = generic_file_open(inode, filp);
 +	if (err)
 +		return err;
 +
 +	filp->f_mode |= FMODE_CAN_ODIRECT;
 +
 +	return 0;
 +}
 +
 +static loff_t exfat_file_llseek(struct file *file, loff_t offset, int whence)
 +{
 +	struct inode *inode = file->f_mapping->host;
 +
 +	switch (whence) {
 +	case SEEK_HOLE:
 +		inode_lock_shared(inode);
 +		offset = iomap_seek_hole(inode, offset, &exfat_read_iomap_ops);
 +		inode_unlock_shared(inode);
 +		break;
 +	case SEEK_DATA:
 +		inode_lock_shared(inode);
 +		offset = iomap_seek_data(inode, offset, &exfat_read_iomap_ops);
 +		inode_unlock_shared(inode);
 +		break;
 +	default:
 +		return generic_file_llseek_size(file, offset, whence,
 +						inode->i_sb->s_maxbytes,
 +						i_size_read(inode));
 +	}
 +	if (offset < 0)
 +		return offset;
 +	return vfs_setpos(file, offset, inode->i_sb->s_maxbytes);
 +}
 +
  const struct file_operations exfat_file_operations = {
 -	.llseek		= generic_file_llseek,
 +	.open		= exfat_file_open,
 +	.llseek		= exfat_file_llseek,
  	.read_iter	= exfat_file_read_iter,
  	.write_iter	= exfat_file_write_iter,
  	.unlocked_ioctl = exfat_ioctl,
@@@ -922,7 -771,6 +922,7 @@@
  	.fsync		= exfat_file_fsync,
  	.splice_read	= exfat_splice_read,
  	.splice_write	= iter_file_splice_write,
 +	.fallocate	= exfat_fallocate,
  	.setlease	= generic_setlease,
  };
  
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
Re: linux-next: manual merge of the vfs-brauner tree with the exfat tree
Posted by Namjae Jeon 5 days, 13 hours ago
On Sat, Mar 28, 2026 at 2:35 AM Mark Brown <broonie@kernel.org> wrote:
>
> Hi all,
>
> Today's linux-next merge of the vfs-brauner tree got a conflict in:
>
>   fs/exfat/file.c
>
> between commit:
>
>   9b373eacd6e6c4 ("exfat: add iomap buffered I/O support")
>
> from the exfat tree and commit:
>
>   5f36c9ca333360 ("fs: Rename generic_file_fsync() to simple_fsync()")
>
> from the vfs-brauner tree.
>
> I fixed it up (see below) and can carry the fix as necessary. This
> is now fixed as far as linux-next is concerned, but any non trivial
> conflicts should be mentioned to your upstream maintainer when your tree
> is submitted for merging.  You may also want to consider cooperating

@@@ -633,14 -577,10 +633,14 @@@ int exfat_file_fsync(struct file *filp
        if (unlikely(exfat_forced_shutdown(inode->i_sb)))
                return -EIO;

 -      err = simple_fsync_noflush(filp, start, end, datasync);
 +      err = file_write_and_wait_range(filp, start, end);
        if (err)
                return err;

Looks good to me:)
To avoid merge conflicts, I will wait for Christian to send his PR to
Linus first. I’ll then include this change and submit exfat's PR
afterward.
Thanks!