Commit 966ceafc authored by Darrick J. Wong's avatar Darrick J. Wong

xfs: create deferred log items for file mapping exchanges

Now that we've created the skeleton of a log intent item to track and
restart file mapping exchange operations, add the upper level logic to
commit intent items and turn them into concrete work recorded in the
log.  This builds on the existing bmap update intent items that have
been around for a while now.
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 6c08f434
......@@ -34,6 +34,7 @@ xfs-y += $(addprefix libxfs/, \
xfs_dir2_node.o \
xfs_dir2_sf.o \
xfs_dquot_buf.o \
xfs_exchmaps.o \
xfs_ialloc.o \
xfs_ialloc_btree.o \
xfs_iext_tree.o \
......
......@@ -27,6 +27,7 @@
#include "xfs_da_btree.h"
#include "xfs_attr.h"
#include "xfs_trans_priv.h"
#include "xfs_exchmaps.h"
static struct kmem_cache *xfs_defer_pending_cache;
......@@ -1176,6 +1177,10 @@ xfs_defer_init_item_caches(void)
error = xfs_attr_intent_init_cache();
if (error)
goto err;
error = xfs_exchmaps_intent_init_cache();
if (error)
goto err;
return 0;
err:
xfs_defer_destroy_item_caches();
......@@ -1186,6 +1191,7 @@ xfs_defer_init_item_caches(void)
void
xfs_defer_destroy_item_caches(void)
{
xfs_exchmaps_intent_destroy_cache();
xfs_attr_intent_destroy_cache();
xfs_extfree_intent_destroy_cache();
xfs_bmap_intent_destroy_cache();
......
......@@ -72,7 +72,7 @@ extern const struct xfs_defer_op_type xfs_rmap_update_defer_type;
extern const struct xfs_defer_op_type xfs_extent_free_defer_type;
extern const struct xfs_defer_op_type xfs_agfl_free_defer_type;
extern const struct xfs_defer_op_type xfs_attr_defer_type;
extern const struct xfs_defer_op_type xfs_exchmaps_defer_type;
/*
* Deferred operation item relogging limits.
......
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) 2020-2024 Oracle. All Rights Reserved.
* Author: Darrick J. Wong <djwong@kernel.org>
*/
#ifndef __XFS_EXCHMAPS_H__
#define __XFS_EXCHMAPS_H__
/* In-core deferred operation info about a file mapping exchange request. */
struct xfs_exchmaps_intent {
/* List of other incore deferred work. */
struct list_head xmi_list;
/* Inodes participating in the operation. */
struct xfs_inode *xmi_ip1;
struct xfs_inode *xmi_ip2;
/* File offset range information. */
xfs_fileoff_t xmi_startoff1;
xfs_fileoff_t xmi_startoff2;
xfs_filblks_t xmi_blockcount;
/* Set these file sizes after the operation, unless negative. */
xfs_fsize_t xmi_isize1;
xfs_fsize_t xmi_isize2;
uint64_t xmi_flags; /* XFS_EXCHMAPS_* flags */
};
/* flags that can be passed to xfs_exchmaps_{estimate,mappings} */
#define XFS_EXCHMAPS_PARAMS (XFS_EXCHMAPS_ATTR_FORK | \
XFS_EXCHMAPS_SET_SIZES | \
XFS_EXCHMAPS_INO1_WRITTEN)
static inline int
xfs_exchmaps_whichfork(const struct xfs_exchmaps_intent *xmi)
{
if (xmi->xmi_flags & XFS_EXCHMAPS_ATTR_FORK)
return XFS_ATTR_FORK;
return XFS_DATA_FORK;
}
/* Parameters for a mapping exchange request. */
struct xfs_exchmaps_req {
/* Inodes participating in the operation. */
struct xfs_inode *ip1;
struct xfs_inode *ip2;
/* File offset range information. */
xfs_fileoff_t startoff1;
xfs_fileoff_t startoff2;
xfs_filblks_t blockcount;
/* XFS_EXCHMAPS_* operation flags */
uint64_t flags;
/*
* Fields below this line are filled out by xfs_exchmaps_estimate;
* callers should initialize this part of the struct to zero.
*/
/*
* Data device blocks to be moved out of ip1, and free space needed to
* handle the bmbt changes.
*/
xfs_filblks_t ip1_bcount;
/*
* Data device blocks to be moved out of ip2, and free space needed to
* handle the bmbt changes.
*/
xfs_filblks_t ip2_bcount;
/* rt blocks to be moved out of ip1. */
xfs_filblks_t ip1_rtbcount;
/* rt blocks to be moved out of ip2. */
xfs_filblks_t ip2_rtbcount;
/* Free space needed to handle the bmbt changes */
unsigned long long resblks;
/* Number of exchanges needed to complete the operation */
unsigned long long nr_exchanges;
};
static inline int
xfs_exchmaps_reqfork(const struct xfs_exchmaps_req *req)
{
if (req->flags & XFS_EXCHMAPS_ATTR_FORK)
return XFS_ATTR_FORK;
return XFS_DATA_FORK;
}
int xfs_exchmaps_estimate(struct xfs_exchmaps_req *req);
extern struct kmem_cache *xfs_exchmaps_intent_cache;
int __init xfs_exchmaps_intent_init_cache(void);
void xfs_exchmaps_intent_destroy_cache(void);
struct xfs_exchmaps_intent *xfs_exchmaps_init_intent(
const struct xfs_exchmaps_req *req);
void xfs_exchmaps_ensure_reflink(struct xfs_trans *tp,
const struct xfs_exchmaps_intent *xmi);
void xfs_exchmaps_upgrade_extent_counts(struct xfs_trans *tp,
const struct xfs_exchmaps_intent *xmi);
int xfs_exchmaps_finish_one(struct xfs_trans *tp,
struct xfs_exchmaps_intent *xmi);
int xfs_exchmaps_check_forks(struct xfs_mount *mp,
const struct xfs_exchmaps_req *req);
void xfs_exchange_mappings(struct xfs_trans *tp,
const struct xfs_exchmaps_req *req);
#endif /* __XFS_EXCHMAPS_H__ */
......@@ -904,7 +904,29 @@ struct xfs_xmi_log_format {
uint64_t xmi_isize2; /* intended file2 size */
};
#define XFS_EXCHMAPS_LOGGED_FLAGS (0)
/* Exchange mappings between extended attribute forks instead of data forks. */
#define XFS_EXCHMAPS_ATTR_FORK (1ULL << 0)
/* Set the file sizes when finished. */
#define XFS_EXCHMAPS_SET_SIZES (1ULL << 1)
/*
* Exchange the mappings of the two files only if the file allocation units
* mapped to file1's range have been written.
*/
#define XFS_EXCHMAPS_INO1_WRITTEN (1ULL << 2)
/* Clear the reflink flag from inode1 after the operation. */
#define XFS_EXCHMAPS_CLEAR_INO1_REFLINK (1ULL << 3)
/* Clear the reflink flag from inode2 after the operation. */
#define XFS_EXCHMAPS_CLEAR_INO2_REFLINK (1ULL << 4)
#define XFS_EXCHMAPS_LOGGED_FLAGS (XFS_EXCHMAPS_ATTR_FORK | \
XFS_EXCHMAPS_SET_SIZES | \
XFS_EXCHMAPS_INO1_WRITTEN | \
XFS_EXCHMAPS_CLEAR_INO1_REFLINK | \
XFS_EXCHMAPS_CLEAR_INO2_REFLINK)
/* This is the structure used to lay out an mapping exchange done log item. */
struct xfs_xmd_log_format {
......
......@@ -10,6 +10,10 @@
* Components of space reservations.
*/
/* Worst case number of bmaps that can be held in a block. */
#define XFS_MAX_CONTIG_BMAPS_PER_BLOCK(mp) \
(((mp)->m_bmap_dmxr[0]) - ((mp)->m_bmap_dmnr[0]))
/* Worst case number of rmaps that can be held in a block. */
#define XFS_MAX_CONTIG_RMAPS_PER_BLOCK(mp) \
(((mp)->m_rmap_mxr[0]) - ((mp)->m_rmap_mnr[0]))
......
This diff is collapsed.
......@@ -56,4 +56,9 @@ struct xfs_xmd_log_item {
extern struct kmem_cache *xfs_xmi_cache;
extern struct kmem_cache *xfs_xmd_cache;
struct xfs_exchmaps_intent;
void xfs_exchmaps_defer_add(struct xfs_trans *tp,
struct xfs_exchmaps_intent *xmi);
#endif /* __XFS_EXCHMAPS_ITEM_H__ */
......@@ -13,8 +13,57 @@
#include "xfs_inode.h"
#include "xfs_trans.h"
#include "xfs_exchrange.h"
#include "xfs_exchmaps.h"
#include <linux/fsnotify.h>
/* Lock (and optionally join) two inodes for a file range exchange. */
void
xfs_exchrange_ilock(
struct xfs_trans *tp,
struct xfs_inode *ip1,
struct xfs_inode *ip2)
{
if (ip1 != ip2)
xfs_lock_two_inodes(ip1, XFS_ILOCK_EXCL,
ip2, XFS_ILOCK_EXCL);
else
xfs_ilock(ip1, XFS_ILOCK_EXCL);
if (tp) {
xfs_trans_ijoin(tp, ip1, 0);
if (ip2 != ip1)
xfs_trans_ijoin(tp, ip2, 0);
}
}
/* Unlock two inodes after a file range exchange operation. */
void
xfs_exchrange_iunlock(
struct xfs_inode *ip1,
struct xfs_inode *ip2)
{
if (ip2 != ip1)
xfs_iunlock(ip2, XFS_ILOCK_EXCL);
xfs_iunlock(ip1, XFS_ILOCK_EXCL);
}
/*
* Estimate the resource requirements to exchange file contents between the two
* files. The caller is required to hold the IOLOCK and the MMAPLOCK and to
* have flushed both inodes' pagecache and active direct-ios.
*/
int
xfs_exchrange_estimate(
struct xfs_exchmaps_req *req)
{
int error;
xfs_exchrange_ilock(NULL, req->ip1, req->ip2);
error = xfs_exchmaps_estimate(req);
xfs_exchrange_iunlock(req->ip1, req->ip2);
return error;
}
/*
* Generic code for exchanging ranges of two files via XFS_IOC_EXCHANGE_RANGE.
* This part deals with struct file objects and byte ranges and does not deal
......
......@@ -27,4 +27,12 @@ struct xfs_exchrange {
long xfs_ioc_exchange_range(struct file *file,
struct xfs_exchange_range __user *argp);
struct xfs_exchmaps_req;
void xfs_exchrange_ilock(struct xfs_trans *tp, struct xfs_inode *ip1,
struct xfs_inode *ip2);
void xfs_exchrange_iunlock(struct xfs_inode *ip1, struct xfs_inode *ip2);
int xfs_exchrange_estimate(struct xfs_exchmaps_req *req);
#endif /* __XFS_EXCHRANGE_H__ */
......@@ -39,6 +39,7 @@
#include "xfs_buf_mem.h"
#include "xfs_btree_mem.h"
#include "xfs_bmap.h"
#include "xfs_exchmaps.h"
/*
* We include this last to have the helpers above available for the trace
......
......@@ -82,6 +82,8 @@ struct xfs_perag;
struct xfbtree;
struct xfs_btree_ops;
struct xfs_bmap_intent;
struct xfs_exchmaps_intent;
struct xfs_exchmaps_req;
#define XFS_ATTR_FILTER_FLAGS \
{ XFS_ATTR_ROOT, "ROOT" }, \
......@@ -4770,6 +4772,221 @@ DEFINE_XFBTREE_FREESP_EVENT(xfbtree_alloc_block);
DEFINE_XFBTREE_FREESP_EVENT(xfbtree_free_block);
#endif /* CONFIG_XFS_BTREE_IN_MEM */
/* exchmaps tracepoints */
#define XFS_EXCHMAPS_STRINGS \
{ XFS_EXCHMAPS_ATTR_FORK, "ATTRFORK" }, \
{ XFS_EXCHMAPS_SET_SIZES, "SETSIZES" }, \
{ XFS_EXCHMAPS_INO1_WRITTEN, "INO1_WRITTEN" }, \
{ XFS_EXCHMAPS_CLEAR_INO1_REFLINK, "CLEAR_INO1_REFLINK" }, \
{ XFS_EXCHMAPS_CLEAR_INO2_REFLINK, "CLEAR_INO2_REFLINK" }
DEFINE_INODE_IREC_EVENT(xfs_exchmaps_mapping1_skip);
DEFINE_INODE_IREC_EVENT(xfs_exchmaps_mapping1);
DEFINE_INODE_IREC_EVENT(xfs_exchmaps_mapping2);
DEFINE_ITRUNC_EVENT(xfs_exchmaps_update_inode_size);
TRACE_EVENT(xfs_exchmaps_overhead,
TP_PROTO(struct xfs_mount *mp, unsigned long long bmbt_blocks,
unsigned long long rmapbt_blocks),
TP_ARGS(mp, bmbt_blocks, rmapbt_blocks),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(unsigned long long, bmbt_blocks)
__field(unsigned long long, rmapbt_blocks)
),
TP_fast_assign(
__entry->dev = mp->m_super->s_dev;
__entry->bmbt_blocks = bmbt_blocks;
__entry->rmapbt_blocks = rmapbt_blocks;
),
TP_printk("dev %d:%d bmbt_blocks 0x%llx rmapbt_blocks 0x%llx",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->bmbt_blocks,
__entry->rmapbt_blocks)
);
DECLARE_EVENT_CLASS(xfs_exchmaps_estimate_class,
TP_PROTO(const struct xfs_exchmaps_req *req),
TP_ARGS(req),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino1)
__field(xfs_ino_t, ino2)
__field(xfs_fileoff_t, startoff1)
__field(xfs_fileoff_t, startoff2)
__field(xfs_filblks_t, blockcount)
__field(uint64_t, flags)
__field(xfs_filblks_t, ip1_bcount)
__field(xfs_filblks_t, ip2_bcount)
__field(xfs_filblks_t, ip1_rtbcount)
__field(xfs_filblks_t, ip2_rtbcount)
__field(unsigned long long, resblks)
__field(unsigned long long, nr_exchanges)
),
TP_fast_assign(
__entry->dev = req->ip1->i_mount->m_super->s_dev;
__entry->ino1 = req->ip1->i_ino;
__entry->ino2 = req->ip2->i_ino;
__entry->startoff1 = req->startoff1;
__entry->startoff2 = req->startoff2;
__entry->blockcount = req->blockcount;
__entry->flags = req->flags;
__entry->ip1_bcount = req->ip1_bcount;
__entry->ip2_bcount = req->ip2_bcount;
__entry->ip1_rtbcount = req->ip1_rtbcount;
__entry->ip2_rtbcount = req->ip2_rtbcount;
__entry->resblks = req->resblks;
__entry->nr_exchanges = req->nr_exchanges;
),
TP_printk("dev %d:%d ino1 0x%llx fileoff1 0x%llx ino2 0x%llx fileoff2 0x%llx fsbcount 0x%llx flags (%s) bcount1 0x%llx rtbcount1 0x%llx bcount2 0x%llx rtbcount2 0x%llx resblks 0x%llx nr_exchanges %llu",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino1, __entry->startoff1,
__entry->ino2, __entry->startoff2,
__entry->blockcount,
__print_flags_u64(__entry->flags, "|", XFS_EXCHMAPS_STRINGS),
__entry->ip1_bcount,
__entry->ip1_rtbcount,
__entry->ip2_bcount,
__entry->ip2_rtbcount,
__entry->resblks,
__entry->nr_exchanges)
);
#define DEFINE_EXCHMAPS_ESTIMATE_EVENT(name) \
DEFINE_EVENT(xfs_exchmaps_estimate_class, name, \
TP_PROTO(const struct xfs_exchmaps_req *req), \
TP_ARGS(req))
DEFINE_EXCHMAPS_ESTIMATE_EVENT(xfs_exchmaps_initial_estimate);
DEFINE_EXCHMAPS_ESTIMATE_EVENT(xfs_exchmaps_final_estimate);
DECLARE_EVENT_CLASS(xfs_exchmaps_intent_class,
TP_PROTO(struct xfs_mount *mp, const struct xfs_exchmaps_intent *xmi),
TP_ARGS(mp, xmi),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino1)
__field(xfs_ino_t, ino2)
__field(uint64_t, flags)
__field(xfs_fileoff_t, startoff1)
__field(xfs_fileoff_t, startoff2)
__field(xfs_filblks_t, blockcount)
__field(xfs_fsize_t, isize1)
__field(xfs_fsize_t, isize2)
__field(xfs_fsize_t, new_isize1)
__field(xfs_fsize_t, new_isize2)
),
TP_fast_assign(
__entry->dev = mp->m_super->s_dev;
__entry->ino1 = xmi->xmi_ip1->i_ino;
__entry->ino2 = xmi->xmi_ip2->i_ino;
__entry->flags = xmi->xmi_flags;
__entry->startoff1 = xmi->xmi_startoff1;
__entry->startoff2 = xmi->xmi_startoff2;
__entry->blockcount = xmi->xmi_blockcount;
__entry->isize1 = xmi->xmi_ip1->i_disk_size;
__entry->isize2 = xmi->xmi_ip2->i_disk_size;
__entry->new_isize1 = xmi->xmi_isize1;
__entry->new_isize2 = xmi->xmi_isize2;
),
TP_printk("dev %d:%d ino1 0x%llx fileoff1 0x%llx ino2 0x%llx fileoff2 0x%llx fsbcount 0x%llx flags (%s) isize1 0x%llx newisize1 0x%llx isize2 0x%llx newisize2 0x%llx",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino1, __entry->startoff1,
__entry->ino2, __entry->startoff2,
__entry->blockcount,
__print_flags_u64(__entry->flags, "|", XFS_EXCHMAPS_STRINGS),
__entry->isize1, __entry->new_isize1,
__entry->isize2, __entry->new_isize2)
);
#define DEFINE_EXCHMAPS_INTENT_EVENT(name) \
DEFINE_EVENT(xfs_exchmaps_intent_class, name, \
TP_PROTO(struct xfs_mount *mp, const struct xfs_exchmaps_intent *xmi), \
TP_ARGS(mp, xmi))
DEFINE_EXCHMAPS_INTENT_EVENT(xfs_exchmaps_defer);
DEFINE_EXCHMAPS_INTENT_EVENT(xfs_exchmaps_recover);
TRACE_EVENT(xfs_exchmaps_delta_nextents_step,
TP_PROTO(struct xfs_mount *mp,
const struct xfs_bmbt_irec *left,
const struct xfs_bmbt_irec *curr,
const struct xfs_bmbt_irec *new,
const struct xfs_bmbt_irec *right,
int delta, unsigned int state),
TP_ARGS(mp, left, curr, new, right, delta, state),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_fileoff_t, loff)
__field(xfs_fsblock_t, lstart)
__field(xfs_filblks_t, lcount)
__field(xfs_fileoff_t, coff)
__field(xfs_fsblock_t, cstart)
__field(xfs_filblks_t, ccount)
__field(xfs_fileoff_t, noff)
__field(xfs_fsblock_t, nstart)
__field(xfs_filblks_t, ncount)
__field(xfs_fileoff_t, roff)
__field(xfs_fsblock_t, rstart)
__field(xfs_filblks_t, rcount)
__field(int, delta)
__field(unsigned int, state)
),
TP_fast_assign(
__entry->dev = mp->m_super->s_dev;
__entry->loff = left->br_startoff;
__entry->lstart = left->br_startblock;
__entry->lcount = left->br_blockcount;
__entry->coff = curr->br_startoff;
__entry->cstart = curr->br_startblock;
__entry->ccount = curr->br_blockcount;
__entry->noff = new->br_startoff;
__entry->nstart = new->br_startblock;
__entry->ncount = new->br_blockcount;
__entry->roff = right->br_startoff;
__entry->rstart = right->br_startblock;
__entry->rcount = right->br_blockcount;
__entry->delta = delta;
__entry->state = state;
),
TP_printk("dev %d:%d left 0x%llx:0x%llx:0x%llx; curr 0x%llx:0x%llx:0x%llx <- new 0x%llx:0x%llx:0x%llx; right 0x%llx:0x%llx:0x%llx delta %d state 0x%x",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->loff, __entry->lstart, __entry->lcount,
__entry->coff, __entry->cstart, __entry->ccount,
__entry->noff, __entry->nstart, __entry->ncount,
__entry->roff, __entry->rstart, __entry->rcount,
__entry->delta, __entry->state)
);
TRACE_EVENT(xfs_exchmaps_delta_nextents,
TP_PROTO(const struct xfs_exchmaps_req *req, int64_t d_nexts1,
int64_t d_nexts2),
TP_ARGS(req, d_nexts1, d_nexts2),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino1)
__field(xfs_ino_t, ino2)
__field(xfs_extnum_t, nexts1)
__field(xfs_extnum_t, nexts2)
__field(int64_t, d_nexts1)
__field(int64_t, d_nexts2)
),
TP_fast_assign(
int whichfork = xfs_exchmaps_reqfork(req);
__entry->dev = req->ip1->i_mount->m_super->s_dev;
__entry->ino1 = req->ip1->i_ino;
__entry->ino2 = req->ip2->i_ino;
__entry->nexts1 = xfs_ifork_ptr(req->ip1, whichfork)->if_nextents;
__entry->nexts2 = xfs_ifork_ptr(req->ip2, whichfork)->if_nextents;
__entry->d_nexts1 = d_nexts1;
__entry->d_nexts2 = d_nexts2;
),
TP_printk("dev %d:%d ino1 0x%llx nexts %llu ino2 0x%llx nexts %llu delta1 %lld delta2 %lld",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino1, __entry->nexts1,
__entry->ino2, __entry->nexts2,
__entry->d_nexts1, __entry->d_nexts2)
);
#endif /* _TRACE_XFS_H */
#undef TRACE_INCLUDE_PATH
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment