xfs: allocate direct I/O COW blocks in iomap_begin
Instead of preallocating all the required COW blocks in the high-level write code do it inside the iomap code, like we do for all other I/O. Signed-off-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
This commit is contained in:
parent
a14234c72b
commit
3c68d44a2b
5 changed files with 68 additions and 123 deletions
|
@ -561,14 +561,6 @@ xfs_file_dio_aio_write(
|
||||||
}
|
}
|
||||||
|
|
||||||
trace_xfs_file_direct_write(ip, count, iocb->ki_pos);
|
trace_xfs_file_direct_write(ip, count, iocb->ki_pos);
|
||||||
|
|
||||||
/* If this is a block-aligned directio CoW, remap immediately. */
|
|
||||||
if (xfs_is_reflink_inode(ip) && !unaligned_io) {
|
|
||||||
ret = xfs_reflink_allocate_cow_range(ip, iocb->ki_pos, count);
|
|
||||||
if (ret)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = iomap_dio_rw(iocb, from, &xfs_iomap_ops, xfs_dio_write_end_io);
|
ret = iomap_dio_rw(iocb, from, &xfs_iomap_ops, xfs_dio_write_end_io);
|
||||||
out:
|
out:
|
||||||
xfs_iunlock(ip, iolock);
|
xfs_iunlock(ip, iolock);
|
||||||
|
|
|
@ -995,37 +995,31 @@ xfs_file_iomap_begin(
|
||||||
offset_fsb = XFS_B_TO_FSBT(mp, offset);
|
offset_fsb = XFS_B_TO_FSBT(mp, offset);
|
||||||
end_fsb = XFS_B_TO_FSB(mp, offset + length);
|
end_fsb = XFS_B_TO_FSB(mp, offset + length);
|
||||||
|
|
||||||
if (xfs_is_reflink_inode(ip) &&
|
|
||||||
(flags & IOMAP_WRITE) && (flags & IOMAP_DIRECT)) {
|
|
||||||
shared = xfs_reflink_find_cow_mapping(ip, offset, &imap);
|
|
||||||
if (shared) {
|
|
||||||
xfs_iunlock(ip, lockmode);
|
|
||||||
goto alloc_done;
|
|
||||||
}
|
|
||||||
ASSERT(!isnullstartblock(imap.br_startblock));
|
|
||||||
}
|
|
||||||
|
|
||||||
error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb, &imap,
|
error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb, &imap,
|
||||||
&nimaps, 0);
|
&nimaps, 0);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
|
|
||||||
if ((flags & IOMAP_REPORT) ||
|
if (flags & IOMAP_REPORT) {
|
||||||
(xfs_is_reflink_inode(ip) &&
|
|
||||||
(flags & IOMAP_WRITE) && (flags & IOMAP_DIRECT))) {
|
|
||||||
/* Trim the mapping to the nearest shared extent boundary. */
|
/* Trim the mapping to the nearest shared extent boundary. */
|
||||||
error = xfs_reflink_trim_around_shared(ip, &imap, &shared,
|
error = xfs_reflink_trim_around_shared(ip, &imap, &shared,
|
||||||
&trimmed);
|
&trimmed);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
|
|
||||||
ASSERT((flags & IOMAP_REPORT) || !shared);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((flags & (IOMAP_WRITE | IOMAP_ZERO)) && xfs_is_reflink_inode(ip)) {
|
if ((flags & (IOMAP_WRITE | IOMAP_ZERO)) && xfs_is_reflink_inode(ip)) {
|
||||||
|
if (flags & IOMAP_DIRECT) {
|
||||||
|
/* may drop and re-acquire the ilock */
|
||||||
|
error = xfs_reflink_allocate_cow(ip, &imap, &shared,
|
||||||
|
&lockmode);
|
||||||
|
if (error)
|
||||||
|
goto out_unlock;
|
||||||
|
} else {
|
||||||
error = xfs_reflink_reserve_cow(ip, &imap, &shared);
|
error = xfs_reflink_reserve_cow(ip, &imap, &shared);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
end_fsb = imap.br_startoff + imap.br_blockcount;
|
end_fsb = imap.br_startoff + imap.br_blockcount;
|
||||||
length = XFS_FSB_TO_B(mp, end_fsb) - offset;
|
length = XFS_FSB_TO_B(mp, end_fsb) - offset;
|
||||||
|
@ -1054,7 +1048,6 @@ xfs_file_iomap_begin(
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
alloc_done:
|
|
||||||
iomap->flags = IOMAP_F_NEW;
|
iomap->flags = IOMAP_F_NEW;
|
||||||
trace_xfs_iomap_alloc(ip, offset, length, 0, &imap);
|
trace_xfs_iomap_alloc(ip, offset, length, 0, &imap);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -382,74 +382,75 @@ xfs_reflink_convert_cow(
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate all CoW reservations covering a range of blocks in a file. */
|
/* Allocate all CoW reservations covering a range of blocks in a file. */
|
||||||
static int
|
int
|
||||||
__xfs_reflink_allocate_cow(
|
xfs_reflink_allocate_cow(
|
||||||
struct xfs_inode *ip,
|
struct xfs_inode *ip,
|
||||||
xfs_fileoff_t *offset_fsb,
|
struct xfs_bmbt_irec *imap,
|
||||||
xfs_fileoff_t end_fsb)
|
bool *shared,
|
||||||
|
uint *lockmode)
|
||||||
{
|
{
|
||||||
struct xfs_mount *mp = ip->i_mount;
|
struct xfs_mount *mp = ip->i_mount;
|
||||||
struct xfs_bmbt_irec imap, got;
|
xfs_fileoff_t offset_fsb = imap->br_startoff;
|
||||||
|
xfs_filblks_t count_fsb = imap->br_blockcount;
|
||||||
|
struct xfs_bmbt_irec got;
|
||||||
struct xfs_defer_ops dfops;
|
struct xfs_defer_ops dfops;
|
||||||
struct xfs_trans *tp;
|
struct xfs_trans *tp = NULL;
|
||||||
xfs_fsblock_t first_block;
|
xfs_fsblock_t first_block;
|
||||||
int nimaps, error, lockmode;
|
int nimaps, error = 0;
|
||||||
bool shared, trimmed;
|
bool trimmed;
|
||||||
xfs_filblks_t resaligned;
|
xfs_filblks_t resaligned;
|
||||||
xfs_extlen_t resblks;
|
xfs_extlen_t resblks = 0;
|
||||||
xfs_extnum_t idx;
|
xfs_extnum_t idx;
|
||||||
|
|
||||||
resaligned = xfs_aligned_fsb_count(*offset_fsb, end_fsb - *offset_fsb,
|
retry:
|
||||||
xfs_get_cowextsz_hint(ip));
|
ASSERT(xfs_is_reflink_inode(ip));
|
||||||
resblks = XFS_DIOSTRAT_SPACE_RES(mp, resaligned);
|
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL | XFS_ILOCK_SHARED));
|
||||||
|
|
||||||
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp);
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
lockmode = XFS_ILOCK_EXCL;
|
|
||||||
xfs_ilock(ip, lockmode);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Even if the extent is not shared we might have a preallocation for
|
* Even if the extent is not shared we might have a preallocation for
|
||||||
* it in the COW fork. If so use it.
|
* it in the COW fork. If so use it.
|
||||||
*/
|
*/
|
||||||
if (xfs_iext_lookup_extent(ip, ip->i_cowfp, *offset_fsb, &idx, &got) &&
|
if (xfs_iext_lookup_extent(ip, ip->i_cowfp, offset_fsb, &idx, &got) &&
|
||||||
got.br_startoff <= *offset_fsb) {
|
got.br_startoff <= offset_fsb) {
|
||||||
|
*shared = true;
|
||||||
|
|
||||||
/* If we have a real allocation in the COW fork we're done. */
|
/* If we have a real allocation in the COW fork we're done. */
|
||||||
if (!isnullstartblock(got.br_startblock)) {
|
if (!isnullstartblock(got.br_startblock)) {
|
||||||
xfs_trim_extent(&got, *offset_fsb,
|
xfs_trim_extent(&got, offset_fsb, count_fsb);
|
||||||
end_fsb - *offset_fsb);
|
*imap = got;
|
||||||
*offset_fsb = got.br_startoff + got.br_blockcount;
|
goto convert;
|
||||||
goto out_trans_cancel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xfs_trim_extent(imap, got.br_startoff, got.br_blockcount);
|
||||||
} else {
|
} else {
|
||||||
nimaps = 1;
|
error = xfs_reflink_trim_around_shared(ip, imap, shared, &trimmed);
|
||||||
error = xfs_bmapi_read(ip, *offset_fsb, end_fsb - *offset_fsb,
|
if (error || !*shared)
|
||||||
&imap, &nimaps, 0);
|
goto out;
|
||||||
if (error)
|
|
||||||
goto out_trans_cancel;
|
|
||||||
ASSERT(nimaps == 1);
|
|
||||||
|
|
||||||
/* Trim the mapping to the nearest shared extent boundary. */
|
|
||||||
error = xfs_reflink_trim_around_shared(ip, &imap, &shared,
|
|
||||||
&trimmed);
|
|
||||||
if (error)
|
|
||||||
goto out_trans_cancel;
|
|
||||||
|
|
||||||
if (!shared) {
|
|
||||||
*offset_fsb = imap.br_startoff + imap.br_blockcount;
|
|
||||||
goto out_trans_cancel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*offset_fsb = imap.br_startoff;
|
if (!tp) {
|
||||||
end_fsb = imap.br_startoff + imap.br_blockcount;
|
resaligned = xfs_aligned_fsb_count(imap->br_startoff,
|
||||||
|
imap->br_blockcount, xfs_get_cowextsz_hint(ip));
|
||||||
|
resblks = XFS_DIOSTRAT_SPACE_RES(mp, resaligned);
|
||||||
|
|
||||||
|
xfs_iunlock(ip, *lockmode);
|
||||||
|
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp);
|
||||||
|
*lockmode = XFS_ILOCK_EXCL;
|
||||||
|
xfs_ilock(ip, *lockmode);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
error = xfs_qm_dqattach_locked(ip, 0);
|
||||||
|
if (error)
|
||||||
|
goto out;
|
||||||
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = xfs_trans_reserve_quota_nblks(tp, ip, resblks, 0,
|
error = xfs_trans_reserve_quota_nblks(tp, ip, resblks, 0,
|
||||||
XFS_QMOPT_RES_REGBLKS);
|
XFS_QMOPT_RES_REGBLKS);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_trans_cancel;
|
goto out;
|
||||||
|
|
||||||
xfs_trans_ijoin(tp, ip, 0);
|
xfs_trans_ijoin(tp, ip, 0);
|
||||||
|
|
||||||
|
@ -457,9 +458,9 @@ __xfs_reflink_allocate_cow(
|
||||||
nimaps = 1;
|
nimaps = 1;
|
||||||
|
|
||||||
/* Allocate the entire reservation as unwritten blocks. */
|
/* Allocate the entire reservation as unwritten blocks. */
|
||||||
error = xfs_bmapi_write(tp, ip, *offset_fsb, end_fsb - *offset_fsb,
|
error = xfs_bmapi_write(tp, ip, imap->br_startoff, imap->br_blockcount,
|
||||||
XFS_BMAPI_COWFORK | XFS_BMAPI_PREALLOC, &first_block,
|
XFS_BMAPI_COWFORK | XFS_BMAPI_PREALLOC, &first_block,
|
||||||
resblks, &imap, &nimaps, &dfops);
|
resblks, imap, &nimaps, &dfops);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_bmap_cancel;
|
goto out_bmap_cancel;
|
||||||
|
|
||||||
|
@ -470,57 +471,18 @@ __xfs_reflink_allocate_cow(
|
||||||
|
|
||||||
error = xfs_trans_commit(tp);
|
error = xfs_trans_commit(tp);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_unlock;
|
|
||||||
|
|
||||||
*offset_fsb = imap.br_startoff + imap.br_blockcount;
|
|
||||||
|
|
||||||
out_unlock:
|
|
||||||
xfs_iunlock(ip, lockmode);
|
|
||||||
return error;
|
return error;
|
||||||
|
convert:
|
||||||
|
return xfs_reflink_convert_cow_extent(ip, imap, offset_fsb, count_fsb,
|
||||||
|
&dfops);
|
||||||
out_bmap_cancel:
|
out_bmap_cancel:
|
||||||
xfs_defer_cancel(&dfops);
|
xfs_defer_cancel(&dfops);
|
||||||
xfs_trans_unreserve_quota_nblks(tp, ip, (long)resblks, 0,
|
xfs_trans_unreserve_quota_nblks(tp, ip, (long)resblks, 0,
|
||||||
XFS_QMOPT_RES_REGBLKS);
|
XFS_QMOPT_RES_REGBLKS);
|
||||||
out_trans_cancel:
|
out:
|
||||||
|
if (tp)
|
||||||
xfs_trans_cancel(tp);
|
xfs_trans_cancel(tp);
|
||||||
goto out_unlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate all CoW reservations covering a part of a file. */
|
|
||||||
int
|
|
||||||
xfs_reflink_allocate_cow_range(
|
|
||||||
struct xfs_inode *ip,
|
|
||||||
xfs_off_t offset,
|
|
||||||
xfs_off_t count)
|
|
||||||
{
|
|
||||||
struct xfs_mount *mp = ip->i_mount;
|
|
||||||
xfs_fileoff_t offset_fsb = XFS_B_TO_FSBT(mp, offset);
|
|
||||||
xfs_fileoff_t end_fsb = XFS_B_TO_FSB(mp, offset + count);
|
|
||||||
int error;
|
|
||||||
|
|
||||||
ASSERT(xfs_is_reflink_inode(ip));
|
|
||||||
|
|
||||||
trace_xfs_reflink_allocate_cow_range(ip, offset, count);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Make sure that the dquots are there.
|
|
||||||
*/
|
|
||||||
error = xfs_qm_dqattach(ip, 0);
|
|
||||||
if (error)
|
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
while (offset_fsb < end_fsb) {
|
|
||||||
error = __xfs_reflink_allocate_cow(ip, &offset_fsb, end_fsb);
|
|
||||||
if (error) {
|
|
||||||
trace_xfs_reflink_allocate_cow_range_error(ip, error,
|
|
||||||
_RET_IP_);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert the CoW extents to regular. */
|
|
||||||
return xfs_reflink_convert_cow(ip, offset, count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -28,8 +28,8 @@ extern int xfs_reflink_trim_around_shared(struct xfs_inode *ip,
|
||||||
|
|
||||||
extern int xfs_reflink_reserve_cow(struct xfs_inode *ip,
|
extern int xfs_reflink_reserve_cow(struct xfs_inode *ip,
|
||||||
struct xfs_bmbt_irec *imap, bool *shared);
|
struct xfs_bmbt_irec *imap, bool *shared);
|
||||||
extern int xfs_reflink_allocate_cow_range(struct xfs_inode *ip,
|
extern int xfs_reflink_allocate_cow(struct xfs_inode *ip,
|
||||||
xfs_off_t offset, xfs_off_t count);
|
struct xfs_bmbt_irec *imap, bool *shared, uint *lockmode);
|
||||||
extern int xfs_reflink_convert_cow(struct xfs_inode *ip, xfs_off_t offset,
|
extern int xfs_reflink_convert_cow(struct xfs_inode *ip, xfs_off_t offset,
|
||||||
xfs_off_t count);
|
xfs_off_t count);
|
||||||
extern bool xfs_reflink_find_cow_mapping(struct xfs_inode *ip, xfs_off_t offset,
|
extern bool xfs_reflink_find_cow_mapping(struct xfs_inode *ip, xfs_off_t offset,
|
||||||
|
|
|
@ -3247,7 +3247,6 @@ DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_enospc);
|
||||||
DEFINE_INODE_IREC_EVENT(xfs_reflink_convert_cow);
|
DEFINE_INODE_IREC_EVENT(xfs_reflink_convert_cow);
|
||||||
|
|
||||||
DEFINE_RW_EVENT(xfs_reflink_reserve_cow);
|
DEFINE_RW_EVENT(xfs_reflink_reserve_cow);
|
||||||
DEFINE_RW_EVENT(xfs_reflink_allocate_cow_range);
|
|
||||||
|
|
||||||
DEFINE_SIMPLE_IO_EVENT(xfs_reflink_bounce_dio_write);
|
DEFINE_SIMPLE_IO_EVENT(xfs_reflink_bounce_dio_write);
|
||||||
DEFINE_IOMAP_EVENT(xfs_reflink_find_cow_mapping);
|
DEFINE_IOMAP_EVENT(xfs_reflink_find_cow_mapping);
|
||||||
|
@ -3257,7 +3256,6 @@ DEFINE_SIMPLE_IO_EVENT(xfs_reflink_cancel_cow_range);
|
||||||
DEFINE_SIMPLE_IO_EVENT(xfs_reflink_end_cow);
|
DEFINE_SIMPLE_IO_EVENT(xfs_reflink_end_cow);
|
||||||
DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_remap);
|
DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_remap);
|
||||||
|
|
||||||
DEFINE_INODE_ERROR_EVENT(xfs_reflink_allocate_cow_range_error);
|
|
||||||
DEFINE_INODE_ERROR_EVENT(xfs_reflink_cancel_cow_range_error);
|
DEFINE_INODE_ERROR_EVENT(xfs_reflink_cancel_cow_range_error);
|
||||||
DEFINE_INODE_ERROR_EVENT(xfs_reflink_end_cow_error);
|
DEFINE_INODE_ERROR_EVENT(xfs_reflink_end_cow_error);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue