Commit 7dd01862 authored by Dave Chinner's avatar Dave Chinner Committed by Long Li
Browse files

xfs,iomap: move delalloc punching to iomap

mainline inclusion
from mainline-v6.1-rc4
commit 9c7babf9
category: bugfix
bugzilla: 189079, https://gitee.com/openeuler/kernel/issues/I76JSK
CVE: NA

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=9c7babf94a0d686b552e53aded8d4703d1b8b92b



--------------------------------

Because that's what Christoph wants for this error handling path
only XFS uses.

It requires a new iomap export for handling errors over delalloc
ranges. This is basically the XFS code as is stands, but even though
Christoph wants this as iomap funcitonality, we still have
to call it from the filesystem specific ->iomap_end callback, and
call into the iomap code with yet another filesystem specific
callback to punch the delalloc extent within the defined ranges.

Signed-off-by: default avatarDave Chinner <dchinner@redhat.com>
Reviewed-by: default avatarDarrick J. Wong <djwong@kernel.org>
Signed-off-by: default avatarLong Li <leo.lilong@huawei.com>

Conflicts:
	fs/xfs/xfs_iomap.c
	fs/iomap/buffered-io.c
	include/linux/iomap.h
parent 6ec7377d
Loading
Loading
Loading
Loading
+57 −26
Original line number Diff line number Diff line
@@ -1114,58 +1114,62 @@ xfs_buffered_write_iomap_begin(
static int
xfs_buffered_write_delalloc_punch(
	struct inode		*inode,
	loff_t			start_byte,
	loff_t			end_byte)
	loff_t			offset,
	loff_t			length)
{
	struct xfs_mount	*mp = XFS_M(inode->i_sb);
	xfs_fileoff_t		start_fsb = XFS_B_TO_FSBT(mp, start_byte);
	xfs_fileoff_t		end_fsb = XFS_B_TO_FSB(mp, end_byte);
	xfs_fileoff_t		start_fsb = XFS_B_TO_FSBT(mp, offset);
	xfs_fileoff_t		end_fsb = XFS_B_TO_FSB(mp, offset + length);

	return xfs_bmap_punch_delalloc_range(XFS_I(inode), start_fsb,
				end_fsb - start_fsb);
}

/*
 * When a short write occurs, the filesystem may need to remove reserved space
 * that was allocated in ->iomap_begin from it's ->iomap_end method. For
 * filesystems that use delayed allocation, we need to punch out delalloc
 * extents from the range that are not dirty in the page cache. As the write can
 * race with page faults, there can be dirty pages over the delalloc extent
 * outside the range of a short write but still within the delalloc extent
 * allocated for this iomap.
 *
 * This function uses [start_byte, end_byte) intervals (i.e. open ended) to
 * simplify range iterations, but converts them back to {offset,len} tuples for
 * the punch callback.
 */
static int
xfs_buffered_write_iomap_end(
xfs_iomap_file_buffered_write_punch_delalloc(
		struct inode		*inode,
	loff_t			offset,
		struct iomap		*iomap,
		loff_t			pos,
		loff_t			length,
		ssize_t			written,
	unsigned		flags,
	struct iomap		*iomap)
		int (*punch)(struct inode *inode, loff_t pos, loff_t length))
{
	struct xfs_mount	*mp = XFS_M(inode->i_sb);
	struct xfs_inode	*ip = XFS_I(inode);
	loff_t			start_byte;
	loff_t			end_byte;
	int			blocksize = i_blocksize(inode);
	int			error = 0;

	if (iomap->type != IOMAP_DELALLOC)
		return 0;

	/*
	 * Behave as if the write failed if drop writes is enabled. Set the NEW
	 * flag to force delalloc cleanup.
	 */
	if (XFS_TEST_ERROR(false, mp, XFS_ERRTAG_DROP_WRITES)) {
		iomap->flags |= IOMAP_F_NEW;
		written = 0;
	}

	/* If we didn't reserve the blocks, we're not allowed to punch them. */
	if (!(iomap->flags & IOMAP_F_NEW))
		return 0;

	/*
	 * start_fsb refers to the first unused block after a short write. If
	 * start_byte refers to the first unused block after a short write. If
	 * nothing was written, round offset down to point at the first block in
	 * the range.
	 */
	if (unlikely(!written))
		start_byte = round_down(offset, mp->m_sb.sb_blocksize);
		start_byte = round_down(pos, blocksize);
	else
		start_byte = round_up(offset + written, mp->m_sb.sb_blocksize);
	end_byte = round_up(offset + length, mp->m_sb.sb_blocksize);
		start_byte = round_up(pos + written, blocksize);
	end_byte = round_up(pos + length, blocksize);

	/* Nothing to do if we've written the entire delalloc extent */
	if (start_byte >= end_byte)
@@ -1179,8 +1183,35 @@ xfs_buffered_write_iomap_end(
	 */
	xfs_ilock(ip, XFS_MMAPLOCK_EXCL);
	truncate_pagecache_range(inode, start_byte, end_byte - 1);
	error = xfs_buffered_write_delalloc_punch(inode, start_byte, end_byte);
	error = punch(inode, start_byte, end_byte - start_byte);
	xfs_iunlock(ip, XFS_MMAPLOCK_EXCL);

	return error;
}

static int
xfs_buffered_write_iomap_end(
	struct inode		*inode,
	loff_t			offset,
	loff_t			length,
	ssize_t			written,
	unsigned		flags,
	struct iomap		*iomap)
{
	struct xfs_mount	*mp = XFS_M(inode->i_sb);
	int			error;

	/*
	 * Behave as if the write failed if drop writes is enabled. Set the NEW
	 * flag to force delalloc cleanup.
	 */
	if (XFS_TEST_ERROR(false, mp, XFS_ERRTAG_DROP_WRITES)) {
		iomap->flags |= IOMAP_F_NEW;
		written = 0;
	}

	error = xfs_iomap_file_buffered_write_punch_delalloc(inode, iomap, offset,
			length, written, &xfs_buffered_write_delalloc_punch);
	if (error && !xfs_is_shutdown(mp)) {
		xfs_alert(mp, "%s: unable to clean up ino 0x%llx",
			__func__, XFS_I(inode)->i_ino);