mirror of
https://github.com/torvalds/linux
synced 2024-11-05 18:23:50 +00:00
xfs: map an inode's offset to an exact physical block
Teach the bmap routine to know how to map a range of file blocks to a specific range of physical blocks, instead of simply allocating fresh blocks. This enables reflink to map a file to blocks that are already in use. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
parent
77d61fe45e
commit
f65306ea52
3 changed files with 131 additions and 1 deletions
|
@ -3876,6 +3876,63 @@ xfs_bmap_btalloc(
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* For a remap operation, just "allocate" an extent at the address that the
|
||||
* caller passed in, and ensure that the AGFL is the right size. The caller
|
||||
* will then map the "allocated" extent into the file somewhere.
|
||||
*/
|
||||
STATIC int
|
||||
xfs_bmap_remap_alloc(
|
||||
struct xfs_bmalloca *ap)
|
||||
{
|
||||
struct xfs_trans *tp = ap->tp;
|
||||
struct xfs_mount *mp = tp->t_mountp;
|
||||
xfs_agblock_t bno;
|
||||
struct xfs_alloc_arg args;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* validate that the block number is legal - the enables us to detect
|
||||
* and handle a silent filesystem corruption rather than crashing.
|
||||
*/
|
||||
memset(&args, 0, sizeof(struct xfs_alloc_arg));
|
||||
args.tp = ap->tp;
|
||||
args.mp = ap->tp->t_mountp;
|
||||
bno = *ap->firstblock;
|
||||
args.agno = XFS_FSB_TO_AGNO(mp, bno);
|
||||
args.agbno = XFS_FSB_TO_AGBNO(mp, bno);
|
||||
if (args.agno >= mp->m_sb.sb_agcount ||
|
||||
args.agbno >= mp->m_sb.sb_agblocks)
|
||||
return -EFSCORRUPTED;
|
||||
|
||||
/* "Allocate" the extent from the range we passed in. */
|
||||
trace_xfs_bmap_remap_alloc(ap->ip, *ap->firstblock, ap->length);
|
||||
ap->blkno = bno;
|
||||
ap->ip->i_d.di_nblocks += ap->length;
|
||||
xfs_trans_log_inode(ap->tp, ap->ip, XFS_ILOG_CORE);
|
||||
|
||||
/* Fix the freelist, like a real allocator does. */
|
||||
args.datatype = ap->datatype;
|
||||
args.pag = xfs_perag_get(args.mp, args.agno);
|
||||
ASSERT(args.pag);
|
||||
|
||||
/*
|
||||
* The freelist fixing code will decline the allocation if
|
||||
* the size and shape of the free space doesn't allow for
|
||||
* allocating the extent and updating all the metadata that
|
||||
* happens during an allocation. We're remapping, not
|
||||
* allocating, so skip that check by pretending to be freeing.
|
||||
*/
|
||||
error = xfs_alloc_fix_freelist(&args, XFS_ALLOC_FLAG_FREEING);
|
||||
if (error)
|
||||
goto error0;
|
||||
error0:
|
||||
xfs_perag_put(args.pag);
|
||||
if (error)
|
||||
trace_xfs_bmap_remap_alloc_error(ap->ip, error, _RET_IP_);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* xfs_bmap_alloc is called by xfs_bmapi to allocate an extent for a file.
|
||||
* It figures out where to ask the underlying allocator to put the new extent.
|
||||
|
@ -3884,6 +3941,8 @@ STATIC int
|
|||
xfs_bmap_alloc(
|
||||
struct xfs_bmalloca *ap) /* bmap alloc argument struct */
|
||||
{
|
||||
if (ap->flags & XFS_BMAPI_REMAP)
|
||||
return xfs_bmap_remap_alloc(ap);
|
||||
if (XFS_IS_REALTIME_INODE(ap->ip) &&
|
||||
xfs_alloc_is_userdata(ap->datatype))
|
||||
return xfs_bmap_rtalloc(ap);
|
||||
|
@ -4442,6 +4501,9 @@ xfs_bmapi_write(
|
|||
ASSERT(len > 0);
|
||||
ASSERT(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_LOCAL);
|
||||
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
|
||||
ASSERT(!(flags & XFS_BMAPI_REMAP) || whichfork == XFS_DATA_FORK);
|
||||
ASSERT(!(flags & XFS_BMAPI_PREALLOC) || !(flags & XFS_BMAPI_REMAP));
|
||||
ASSERT(!(flags & XFS_BMAPI_CONVERT) || !(flags & XFS_BMAPI_REMAP));
|
||||
|
||||
/* zeroing is for currently only for data extents, not metadata */
|
||||
ASSERT((flags & (XFS_BMAPI_METADATA | XFS_BMAPI_ZERO)) !=
|
||||
|
@ -4502,6 +4564,12 @@ xfs_bmapi_write(
|
|||
inhole = eof || bma.got.br_startoff > bno;
|
||||
wasdelay = !inhole && isnullstartblock(bma.got.br_startblock);
|
||||
|
||||
/*
|
||||
* Make sure we only reflink into a hole.
|
||||
*/
|
||||
if (flags & XFS_BMAPI_REMAP)
|
||||
ASSERT(inhole);
|
||||
|
||||
/*
|
||||
* First, deal with the hole before the allocated space
|
||||
* that we found, if any.
|
||||
|
|
|
@ -97,6 +97,13 @@ struct xfs_extent_free_item
|
|||
*/
|
||||
#define XFS_BMAPI_ZERO 0x080
|
||||
|
||||
/*
|
||||
* Map the inode offset to the block given in ap->firstblock. Primarily
|
||||
* used for reflink. The range must be in a hole, and this flag cannot be
|
||||
* turned on with PREALLOC or CONVERT, and cannot be used on the attr fork.
|
||||
*/
|
||||
#define XFS_BMAPI_REMAP 0x100
|
||||
|
||||
#define XFS_BMAPI_FLAGS \
|
||||
{ XFS_BMAPI_ENTIRE, "ENTIRE" }, \
|
||||
{ XFS_BMAPI_METADATA, "METADATA" }, \
|
||||
|
@ -105,7 +112,8 @@ struct xfs_extent_free_item
|
|||
{ XFS_BMAPI_IGSTATE, "IGSTATE" }, \
|
||||
{ XFS_BMAPI_CONTIG, "CONTIG" }, \
|
||||
{ XFS_BMAPI_CONVERT, "CONVERT" }, \
|
||||
{ XFS_BMAPI_ZERO, "ZERO" }
|
||||
{ XFS_BMAPI_ZERO, "ZERO" }, \
|
||||
{ XFS_BMAPI_REMAP, "REMAP" }
|
||||
|
||||
|
||||
static inline int xfs_bmapi_aflag(int w)
|
||||
|
|
|
@ -2968,6 +2968,60 @@ TRACE_EVENT(xfs_refcount_finish_one_leftover,
|
|||
__entry->new_len)
|
||||
);
|
||||
|
||||
/* simple inode-based error/%ip tracepoint class */
|
||||
DECLARE_EVENT_CLASS(xfs_inode_error_class,
|
||||
TP_PROTO(struct xfs_inode *ip, int error, unsigned long caller_ip),
|
||||
TP_ARGS(ip, error, caller_ip),
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(xfs_ino_t, ino)
|
||||
__field(int, error)
|
||||
__field(unsigned long, caller_ip)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->dev = VFS_I(ip)->i_sb->s_dev;
|
||||
__entry->ino = ip->i_ino;
|
||||
__entry->error = error;
|
||||
__entry->caller_ip = caller_ip;
|
||||
),
|
||||
TP_printk("dev %d:%d ino %llx error %d caller %ps",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
__entry->ino,
|
||||
__entry->error,
|
||||
(char *)__entry->caller_ip)
|
||||
);
|
||||
|
||||
#define DEFINE_INODE_ERROR_EVENT(name) \
|
||||
DEFINE_EVENT(xfs_inode_error_class, name, \
|
||||
TP_PROTO(struct xfs_inode *ip, int error, \
|
||||
unsigned long caller_ip), \
|
||||
TP_ARGS(ip, error, caller_ip))
|
||||
|
||||
/* reflink allocator */
|
||||
TRACE_EVENT(xfs_bmap_remap_alloc,
|
||||
TP_PROTO(struct xfs_inode *ip, xfs_fsblock_t fsbno,
|
||||
xfs_extlen_t len),
|
||||
TP_ARGS(ip, fsbno, len),
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(xfs_ino_t, ino)
|
||||
__field(xfs_fsblock_t, fsbno)
|
||||
__field(xfs_extlen_t, len)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->dev = VFS_I(ip)->i_sb->s_dev;
|
||||
__entry->ino = ip->i_ino;
|
||||
__entry->fsbno = fsbno;
|
||||
__entry->len = len;
|
||||
),
|
||||
TP_printk("dev %d:%d ino 0x%llx fsbno 0x%llx len %x",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
__entry->ino,
|
||||
__entry->fsbno,
|
||||
__entry->len)
|
||||
);
|
||||
DEFINE_INODE_ERROR_EVENT(xfs_bmap_remap_alloc_error);
|
||||
|
||||
#endif /* _TRACE_XFS_H */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
|
|
Loading…
Reference in a new issue