/* Advance the incore state tracking after exchanging a mapping. */ staticinlinevoid
xmi_advance( struct xfs_exchmaps_intent *xmi, conststruct xfs_bmbt_irec *irec)
{
xmi->xmi_startoff1 += irec->br_blockcount;
xmi->xmi_startoff2 += irec->br_blockcount;
xmi->xmi_blockcount -= irec->br_blockcount;
}
/* Do we still have more mappings to exchange? */ staticinlinebool
xmi_has_more_exchange_work(conststruct xfs_exchmaps_intent *xmi)
{ return xmi->xmi_blockcount > 0;
}
/* Do we have post-operation cleanups to perform? */ staticinlinebool
xmi_has_postop_work(conststruct xfs_exchmaps_intent *xmi)
{ return xmi->xmi_flags & (XFS_EXCHMAPS_CLEAR_INO1_REFLINK |
XFS_EXCHMAPS_CLEAR_INO2_REFLINK |
__XFS_EXCHMAPS_INO2_SHORTFORM);
}
/* Check all mappings to make sure we can actually exchange them. */ int
xfs_exchmaps_check_forks( struct xfs_mount *mp, conststruct xfs_exchmaps_req *req)
{ struct xfs_ifork *ifp1, *ifp2; int whichfork = xfs_exchmaps_reqfork(req);
/* No fork? */
ifp1 = xfs_ifork_ptr(req->ip1, whichfork);
ifp2 = xfs_ifork_ptr(req->ip2, whichfork); if (!ifp1 || !ifp2) return -EINVAL;
/* We don't know how to exchange local format forks. */ if (ifp1->if_format == XFS_DINODE_FMT_LOCAL ||
ifp2->if_format == XFS_DINODE_FMT_LOCAL) return -EINVAL;
return0;
}
#ifdef CONFIG_XFS_QUOTA /* Log the actual updates to the quota accounting. */ staticinlinevoid
xfs_exchmaps_update_quota( struct xfs_trans *tp, struct xfs_exchmaps_intent *xmi, struct xfs_bmbt_irec *irec1, struct xfs_bmbt_irec *irec2)
{
int64_t ip1_delta = 0, ip2_delta = 0; unsignedint qflag;
/* Convert inode2's remote symlink target back to shortform, if possible. */ STATICint
xfs_exchmaps_link_to_sf( struct xfs_trans *tp, struct xfs_exchmaps_intent *xmi)
{ struct xfs_inode *ip = xmi->xmi_ip2; struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK); char *buf; int error;
if (ifp->if_format == XFS_DINODE_FMT_LOCAL ||
ip->i_disk_size > xfs_inode_data_fork_size(ip)) return0;
/* Read the current symlink target into a buffer. */
buf = kmalloc(ip->i_disk_size + 1,
GFP_KERNEL | __GFP_NOLOCKDEP | __GFP_NOFAIL); if (!buf) {
ASSERT(0); return -ENOMEM;
}
error = xfs_symlink_remote_read(ip, buf); if (error) goto free;
/* Remove the blocks. */
error = xfs_symlink_remote_truncate(tp, ip); if (error) goto free;
/* Convert fork to local format and log our changes. */
xfs_idestroy_fork(ifp);
ifp->if_bytes = 0;
ifp->if_format = XFS_DINODE_FMT_LOCAL;
xfs_init_local_fork(ip, XFS_DATA_FORK, buf, ip->i_disk_size);
xfs_trans_log_inode(tp, ip, XFS_ILOG_DDATA | XFS_ILOG_CORE);
free:
kfree(buf); return error;
}
/* Clear the reflink flag after an exchange. */ staticinlinevoid
xfs_exchmaps_clear_reflink( struct xfs_trans *tp, struct xfs_inode *ip)
{
trace_xfs_reflink_unset_inode_flag(ip);
/* Finish whatever work might come after an exchange operation. */ staticint
xfs_exchmaps_do_postop_work( struct xfs_trans *tp, struct xfs_exchmaps_intent *xmi)
{ if (xmi->xmi_flags & __XFS_EXCHMAPS_INO2_SHORTFORM) { int error = 0;
if (XFS_TEST_ERROR(false, tp->t_mountp, XFS_ERRTAG_EXCHMAPS_FINISH_ONE)) return -EIO;
/* If we still have work to do, ask for a new transaction. */ if (xmi_has_more_exchange_work(xmi) || xmi_has_postop_work(xmi)) {
trace_xfs_exchmaps_defer(tp->t_mountp, xmi); return -EAGAIN;
}
/* Compute the space we should reserve for the rmap btree expansions. */ staticinline uint64_t
xfs_exchmaps_rmapbt_blocks( struct xfs_mount *mp, conststruct xfs_exchmaps_req *req)
{ if (!xfs_has_rmapbt(mp)) return0; if (XFS_IS_REALTIME_INODE(req->ip1)) return howmany_64(req->nr_exchanges,
XFS_MAX_CONTIG_RTRMAPS_PER_BLOCK(mp)) *
XFS_RTRMAPADD_SPACE_RES(mp);
/* Make sure the change in file block count doesn't overflow. */ if (check_add_overflow(req->ip1_bcount, bmbt_blocks, &req->ip1_bcount)) return -EFBIG; if (check_add_overflow(req->ip2_bcount, bmbt_blocks, &req->ip2_bcount)) return -EFBIG;
/* *Addtogetherthenumberofblocksweneedtohandlebtreegrowth, *thenaddittothenumberofblocksweneedtoreservetothis *transaction.
*/ if (check_add_overflow(resblks, bmbt_blocks, &resblks)) return -ENOSPC; if (check_add_overflow(resblks, bmbt_blocks, &resblks)) return -ENOSPC; if (check_add_overflow(resblks, rmapbt_blocks, &resblks)) return -ENOSPC; if (check_add_overflow(resblks, rmapbt_blocks, &resblks)) return -ENOSPC;
/* Can't actually reserve more than UINT_MAX blocks. */ if (req->resblks > UINT_MAX) return -ENOSPC;
if (chole)
state |= CHOLE; if (!lhole && !chole && xmi_can_merge(left, curr))
state |= CLEFT_CONTIG; if (!rhole && !chole && xmi_can_merge(curr, right))
state |= CRIGHT_CONTIG; if ((state & CBOTH_CONTIG) == CBOTH_CONTIG &&
!xmi_can_merge_all(left, curr, right))
state &= ~CRIGHT_CONTIG;
if (nhole)
state |= NHOLE; if (!lhole && !nhole && xmi_can_merge(left, new))
state |= NLEFT_CONTIG; if (!rhole && !nhole && xmi_can_merge(new, right))
state |= NRIGHT_CONTIG; if ((state & NBOTH_CONTIG) == NBOTH_CONTIG &&
!xmi_can_merge_all(left, new, right))
state &= ~NRIGHT_CONTIG;
switch (state & (CLEFT_CONTIG | CRIGHT_CONTIG | CHOLE)) { case CLEFT_CONTIG | CRIGHT_CONTIG: /* *left/curr/rightarethesamemapping,sodeletingcurr *causes2newmappingstobecreated.
*/
ret += 2; break; case0: /* *currisnotcontiguouswithanymapping,soweremovecurr *completely
*/
ret--; break; case CHOLE: /* hole, do nothing */ break; case CLEFT_CONTIG: case CRIGHT_CONTIG: /* trim either left or right, no change */ break;
}
switch (state & (NLEFT_CONTIG | NRIGHT_CONTIG | NHOLE)) { case NLEFT_CONTIG | NRIGHT_CONTIG: /* *left/curr/rightwillbecomethesamemapping,soadding *currcausesthedeletionofright.
*/
ret--; break; case0: /* new is not contiguous with any mapping */
ret++; break; case NHOLE: /* hole, do nothing. */ break; case NLEFT_CONTIG: case NRIGHT_CONTIG: /* new is absorbed into left or right, no change */ break;
}
/* Record the state of each inode's reflink flag before the op. */ if (xfs_is_reflink_inode(req->ip1))
rs |= 1; if (xfs_is_reflink_inode(req->ip2))
rs |= 2;
/* *Figureoutifwe'reclearingthereflinkflags(whicheffectively *exchangesthem)aftertheoperation.
*/ if (xmi_can_exchange_reflink_flags(req, rs)) { if (rs & 1)
xmi->xmi_flags |= XFS_EXCHMAPS_CLEAR_INO1_REFLINK; if (rs & 2)
xmi->xmi_flags |= XFS_EXCHMAPS_CLEAR_INO2_REFLINK;
}
if (S_ISDIR(VFS_I(xmi->xmi_ip2)->i_mode) ||
S_ISLNK(VFS_I(xmi->xmi_ip2)->i_mode))
xmi->xmi_flags |= __XFS_EXCHMAPS_INO2_SHORTFORM;
/* Now pretend we exchanged the mappings. */ if (xmi_can_merge(&adj.left2, &irec1))
adj.left2.br_blockcount += irec1.br_blockcount; else
memcpy(&adj.left2, &irec1, sizeof(irec1));
if (xmi_can_merge(&adj.left1, &irec2))
adj.left1.br_blockcount += irec2.br_blockcount; else
memcpy(&adj.left1, &irec2, sizeof(irec2));
xmi_advance(xmi, &irec1);
}
/* Account for the blocks that are being exchanged. */ if (XFS_IS_REALTIME_INODE(req->ip1) &&
xfs_exchmaps_reqfork(req) == XFS_DATA_FORK) {
req->ip1_rtbcount = ip1_blocks;
req->ip2_rtbcount = ip2_blocks;
} else {
req->ip1_bcount = ip1_blocks;
req->ip2_bcount = ip2_blocks;
}
/* Set the reflink flag before an operation. */ staticinlinevoid
xfs_exchmaps_set_reflink( struct xfs_trans *tp, struct xfs_inode *ip)
{
trace_xfs_reflink_set_inode_flag(ip);
if (xfs_is_reflink_inode(xmi->xmi_ip1))
rs |= 1; if (xfs_is_reflink_inode(xmi->xmi_ip2))
rs |= 2;
if ((rs & 1) && !xfs_is_reflink_inode(xmi->xmi_ip2))
xfs_exchmaps_set_reflink(tp, xmi->xmi_ip2);
if ((rs & 2) && !xfs_is_reflink_inode(xmi->xmi_ip1))
xfs_exchmaps_set_reflink(tp, xmi->xmi_ip1);
}
/* Set the large extent count flag before an operation if needed. */ staticinlinevoid
xfs_exchmaps_ensure_large_extent_counts( struct xfs_trans *tp, struct xfs_inode *ip)
{ if (xfs_inode_has_large_extent_counts(ip)) return;
/* Widen the extent counter fields of both inodes if necessary. */ void
xfs_exchmaps_upgrade_extent_counts( struct xfs_trans *tp, conststruct xfs_exchmaps_intent *xmi)
{ if (!xfs_has_large_extent_counts(tp->t_mountp)) return;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.