| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- * Copyright (c) 2020-2024 Oracle. All Rights Reserved.
- * Author: Darrick J. Wong <djwong@kernel.org>
- */
- #include "xfs.h"
- #include "xfs_fs.h"
- #include "xfs_shared.h"
- #include "xfs_format.h"
- #include "xfs_log_format.h"
- #include "xfs_trans_resv.h"
- #include "xfs_mount.h"
- #include "xfs_defer.h"
- #include "xfs_inode.h"
- #include "xfs_trans.h"
- #include "xfs_bmap.h"
- #include "xfs_icache.h"
- #include "xfs_quota.h"
- #include "xfs_exchmaps.h"
- #include "xfs_trace.h"
- #include "xfs_bmap_btree.h"
- #include "xfs_trans_space.h"
- #include "xfs_error.h"
- #include "xfs_errortag.h"
- #include "xfs_health.h"
- #include "xfs_exchmaps_item.h"
- #include "xfs_da_format.h"
- #include "xfs_da_btree.h"
- #include "xfs_attr_leaf.h"
- #include "xfs_attr.h"
- #include "xfs_dir2_priv.h"
- #include "xfs_dir2.h"
- #include "xfs_symlink_remote.h"
- struct kmem_cache *xfs_exchmaps_intent_cache;
- /* bmbt mappings adjacent to a pair of records. */
- struct xfs_exchmaps_adjacent {
- struct xfs_bmbt_irec left1;
- struct xfs_bmbt_irec right1;
- struct xfs_bmbt_irec left2;
- struct xfs_bmbt_irec right2;
- };
- #define ADJACENT_INIT { \
- .left1 = { .br_startblock = HOLESTARTBLOCK }, \
- .right1 = { .br_startblock = HOLESTARTBLOCK }, \
- .left2 = { .br_startblock = HOLESTARTBLOCK }, \
- .right2 = { .br_startblock = HOLESTARTBLOCK }, \
- }
- /* Information to reset reflink flag / CoW fork state after an exchange. */
- /*
- * If the reflink flag is set on either inode, make sure it has an incore CoW
- * fork, since all reflink inodes must have them. If there's a CoW fork and it
- * has mappings in it, make sure the inodes are tagged appropriately so that
- * speculative preallocations can be GC'd if we run low of space.
- */
- static inline void
- xfs_exchmaps_ensure_cowfork(
- struct xfs_inode *ip)
- {
- struct xfs_ifork *cfork;
- if (xfs_is_reflink_inode(ip))
- xfs_ifork_init_cow(ip);
- cfork = xfs_ifork_ptr(ip, XFS_COW_FORK);
- if (!cfork)
- return;
- if (cfork->if_bytes > 0)
- xfs_inode_set_cowblocks_tag(ip);
- else
- xfs_inode_clear_cowblocks_tag(ip);
- }
- /*
- * Adjust the on-disk inode size upwards if needed so that we never add
- * mappings into the file past EOF. This is crucial so that log recovery won't
- * get confused by the sudden appearance of post-eof mappings.
- */
- STATIC void
- xfs_exchmaps_update_size(
- struct xfs_trans *tp,
- struct xfs_inode *ip,
- struct xfs_bmbt_irec *imap,
- xfs_fsize_t new_isize)
- {
- struct xfs_mount *mp = tp->t_mountp;
- xfs_fsize_t len;
- if (new_isize < 0)
- return;
- len = min(XFS_FSB_TO_B(mp, imap->br_startoff + imap->br_blockcount),
- new_isize);
- if (len <= ip->i_disk_size)
- return;
- trace_xfs_exchmaps_update_inode_size(ip, len);
- ip->i_disk_size = len;
- xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
- }
- /* Advance the incore state tracking after exchanging a mapping. */
- static inline void
- xmi_advance(
- struct xfs_exchmaps_intent *xmi,
- const struct 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? */
- static inline bool
- xmi_has_more_exchange_work(const struct xfs_exchmaps_intent *xmi)
- {
- return xmi->xmi_blockcount > 0;
- }
- /* Do we have post-operation cleanups to perform? */
- static inline bool
- xmi_has_postop_work(const struct 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,
- const struct 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;
- return 0;
- }
- #ifdef CONFIG_XFS_QUOTA
- /* Log the actual updates to the quota accounting. */
- static inline void
- 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;
- unsigned int qflag;
- qflag = XFS_IS_REALTIME_INODE(xmi->xmi_ip1) ? XFS_TRANS_DQ_RTBCOUNT :
- XFS_TRANS_DQ_BCOUNT;
- if (xfs_bmap_is_real_extent(irec1)) {
- ip1_delta -= irec1->br_blockcount;
- ip2_delta += irec1->br_blockcount;
- }
- if (xfs_bmap_is_real_extent(irec2)) {
- ip1_delta += irec2->br_blockcount;
- ip2_delta -= irec2->br_blockcount;
- }
- xfs_trans_mod_dquot_byino(tp, xmi->xmi_ip1, qflag, ip1_delta);
- xfs_trans_mod_dquot_byino(tp, xmi->xmi_ip2, qflag, ip2_delta);
- }
- #else
- # define xfs_exchmaps_update_quota(tp, xmi, irec1, irec2) ((void)0)
- #endif
- /* Decide if we want to skip this mapping from file1. */
- static inline bool
- xfs_exchmaps_can_skip_mapping(
- struct xfs_exchmaps_intent *xmi,
- struct xfs_bmbt_irec *irec)
- {
- struct xfs_mount *mp = xmi->xmi_ip1->i_mount;
- /* Do not skip this mapping if the caller did not tell us to. */
- if (!(xmi->xmi_flags & XFS_EXCHMAPS_INO1_WRITTEN))
- return false;
- /* Do not skip mapped, written mappings. */
- if (xfs_bmap_is_written_extent(irec))
- return false;
- /*
- * The mapping is unwritten or a hole. It cannot be a delalloc
- * reservation because we already excluded those. It cannot be an
- * unwritten extent with dirty page cache because we flushed the page
- * cache. For files where the allocation unit is 1FSB (files on the
- * data dev, rt files if the extent size is 1FSB), we can safely
- * skip this mapping.
- */
- if (!xfs_inode_has_bigrtalloc(xmi->xmi_ip1))
- return true;
- /*
- * For a realtime file with a multi-fsb allocation unit, the decision
- * is trickier because we can only swap full allocation units.
- * Unwritten mappings can appear in the middle of an rtx if the rtx is
- * partially written, but they can also appear for preallocations.
- *
- * If the mapping is a hole, skip it entirely. Holes should align with
- * rtx boundaries.
- */
- if (!xfs_bmap_is_real_extent(irec))
- return true;
- /*
- * All mappings below this point are unwritten.
- *
- * - If the beginning is not aligned to an rtx, trim the end of the
- * mapping so that it does not cross an rtx boundary, and swap it.
- *
- * - If both ends are aligned to an rtx, skip the entire mapping.
- */
- if (!isaligned_64(irec->br_startoff, mp->m_sb.sb_rextsize)) {
- xfs_fileoff_t new_end;
- new_end = roundup_64(irec->br_startoff, mp->m_sb.sb_rextsize);
- irec->br_blockcount = min(irec->br_blockcount,
- new_end - irec->br_startoff);
- return false;
- }
- if (isaligned_64(irec->br_blockcount, mp->m_sb.sb_rextsize))
- return true;
- /*
- * All mappings below this point are unwritten, start on an rtx
- * boundary, and do not end on an rtx boundary.
- *
- * - If the mapping is longer than one rtx, trim the end of the mapping
- * down to an rtx boundary and skip it.
- *
- * - The mapping is shorter than one rtx. Swap it.
- */
- if (irec->br_blockcount > mp->m_sb.sb_rextsize) {
- xfs_fileoff_t new_end;
- new_end = rounddown_64(irec->br_startoff + irec->br_blockcount,
- mp->m_sb.sb_rextsize);
- irec->br_blockcount = new_end - irec->br_startoff;
- return true;
- }
- return false;
- }
- /*
- * Walk forward through the file ranges in @xmi until we find two different
- * mappings to exchange. If there is work to do, return the mappings;
- * otherwise we've reached the end of the range and xmi_blockcount will be
- * zero.
- *
- * If the walk skips over a pair of mappings to the same storage, save them as
- * the left records in @adj (if provided) so that the simulation phase can
- * avoid an extra lookup.
- */
- static int
- xfs_exchmaps_find_mappings(
- struct xfs_exchmaps_intent *xmi,
- struct xfs_bmbt_irec *irec1,
- struct xfs_bmbt_irec *irec2,
- struct xfs_exchmaps_adjacent *adj)
- {
- int nimaps;
- int bmap_flags;
- int error;
- bmap_flags = xfs_bmapi_aflag(xfs_exchmaps_whichfork(xmi));
- for (; xmi_has_more_exchange_work(xmi); xmi_advance(xmi, irec1)) {
- /* Read mapping from the first file */
- nimaps = 1;
- error = xfs_bmapi_read(xmi->xmi_ip1, xmi->xmi_startoff1,
- xmi->xmi_blockcount, irec1, &nimaps,
- bmap_flags);
- if (error)
- return error;
- if (nimaps != 1 ||
- irec1->br_startblock == DELAYSTARTBLOCK ||
- irec1->br_startoff != xmi->xmi_startoff1) {
- /*
- * We should never get no mapping or a delalloc mapping
- * or something that doesn't match what we asked for,
- * since the caller flushed both inodes and we hold the
- * ILOCKs for both inodes.
- */
- ASSERT(0);
- return -EINVAL;
- }
- if (xfs_exchmaps_can_skip_mapping(xmi, irec1)) {
- trace_xfs_exchmaps_mapping1_skip(xmi->xmi_ip1, irec1);
- continue;
- }
- /* Read mapping from the second file */
- nimaps = 1;
- error = xfs_bmapi_read(xmi->xmi_ip2, xmi->xmi_startoff2,
- irec1->br_blockcount, irec2, &nimaps,
- bmap_flags);
- if (error)
- return error;
- if (nimaps != 1 ||
- irec2->br_startblock == DELAYSTARTBLOCK ||
- irec2->br_startoff != xmi->xmi_startoff2) {
- /*
- * We should never get no mapping or a delalloc mapping
- * or something that doesn't match what we asked for,
- * since the caller flushed both inodes and we hold the
- * ILOCKs for both inodes.
- */
- ASSERT(0);
- return -EINVAL;
- }
- /*
- * We can only exchange as many blocks as the smaller of the
- * two mapping maps.
- */
- irec1->br_blockcount = min(irec1->br_blockcount,
- irec2->br_blockcount);
- trace_xfs_exchmaps_mapping1(xmi->xmi_ip1, irec1);
- trace_xfs_exchmaps_mapping2(xmi->xmi_ip2, irec2);
- /* We found something to exchange, so return it. */
- if (irec1->br_startblock != irec2->br_startblock)
- return 0;
- /*
- * Two mappings pointing to the same physical block must not
- * have different states; that's filesystem corruption. Move
- * on to the next mapping if they're both holes or both point
- * to the same physical space extent.
- */
- if (irec1->br_state != irec2->br_state) {
- xfs_bmap_mark_sick(xmi->xmi_ip1,
- xfs_exchmaps_whichfork(xmi));
- xfs_bmap_mark_sick(xmi->xmi_ip2,
- xfs_exchmaps_whichfork(xmi));
- return -EFSCORRUPTED;
- }
- /*
- * Save the mappings if we're estimating work and skipping
- * these identical mappings.
- */
- if (adj) {
- memcpy(&adj->left1, irec1, sizeof(*irec1));
- memcpy(&adj->left2, irec2, sizeof(*irec2));
- }
- }
- return 0;
- }
- /* Exchange these two mappings. */
- static void
- xfs_exchmaps_one_step(
- struct xfs_trans *tp,
- struct xfs_exchmaps_intent *xmi,
- struct xfs_bmbt_irec *irec1,
- struct xfs_bmbt_irec *irec2)
- {
- int whichfork = xfs_exchmaps_whichfork(xmi);
- xfs_exchmaps_update_quota(tp, xmi, irec1, irec2);
- /* Remove both mappings. */
- xfs_bmap_unmap_extent(tp, xmi->xmi_ip1, whichfork, irec1);
- xfs_bmap_unmap_extent(tp, xmi->xmi_ip2, whichfork, irec2);
- /*
- * Re-add both mappings. We exchange the file offsets between the two
- * maps and add the opposite map, which has the effect of filling the
- * logical offsets we just unmapped, but with with the physical mapping
- * information exchanged.
- */
- swap(irec1->br_startoff, irec2->br_startoff);
- xfs_bmap_map_extent(tp, xmi->xmi_ip1, whichfork, irec2);
- xfs_bmap_map_extent(tp, xmi->xmi_ip2, whichfork, irec1);
- /* Make sure we're not adding mappings past EOF. */
- if (whichfork == XFS_DATA_FORK) {
- xfs_exchmaps_update_size(tp, xmi->xmi_ip1, irec2,
- xmi->xmi_isize1);
- xfs_exchmaps_update_size(tp, xmi->xmi_ip2, irec1,
- xmi->xmi_isize2);
- }
- /*
- * Advance our cursor and exit. The caller (either defer ops or log
- * recovery) will log the XMD item, and if *blockcount is nonzero, it
- * will log a new XMI item for the remainder and call us back.
- */
- xmi_advance(xmi, irec1);
- }
- /* Convert inode2's leaf attr fork back to shortform, if possible.. */
- STATIC int
- xfs_exchmaps_attr_to_sf(
- struct xfs_trans *tp,
- struct xfs_exchmaps_intent *xmi)
- {
- struct xfs_da_args args = {
- .dp = xmi->xmi_ip2,
- .geo = tp->t_mountp->m_attr_geo,
- .whichfork = XFS_ATTR_FORK,
- .trans = tp,
- .owner = xmi->xmi_ip2->i_ino,
- };
- struct xfs_buf *bp;
- int forkoff;
- int error;
- if (!xfs_attr_is_leaf(xmi->xmi_ip2))
- return 0;
- error = xfs_attr3_leaf_read(tp, xmi->xmi_ip2, xmi->xmi_ip2->i_ino, 0,
- &bp);
- if (error)
- return error;
- forkoff = xfs_attr_shortform_allfit(bp, xmi->xmi_ip2);
- if (forkoff == 0)
- return 0;
- return xfs_attr3_leaf_to_shortform(bp, &args, forkoff);
- }
- /* Convert inode2's block dir fork back to shortform, if possible.. */
- STATIC int
- xfs_exchmaps_dir_to_sf(
- struct xfs_trans *tp,
- struct xfs_exchmaps_intent *xmi)
- {
- struct xfs_da_args args = {
- .dp = xmi->xmi_ip2,
- .geo = tp->t_mountp->m_dir_geo,
- .whichfork = XFS_DATA_FORK,
- .trans = tp,
- .owner = xmi->xmi_ip2->i_ino,
- };
- struct xfs_dir2_sf_hdr sfh;
- struct xfs_buf *bp;
- int size;
- int error = 0;
- if (xfs_dir2_format(&args, &error) != XFS_DIR2_FMT_BLOCK)
- return error;
- error = xfs_dir3_block_read(tp, xmi->xmi_ip2, xmi->xmi_ip2->i_ino, &bp);
- if (error)
- return error;
- size = xfs_dir2_block_sfsize(xmi->xmi_ip2, bp->b_addr, &sfh);
- if (size > xfs_inode_data_fork_size(xmi->xmi_ip2))
- return 0;
- return xfs_dir2_block_to_sf(&args, bp, size, &sfh);
- }
- /* Convert inode2's remote symlink target back to shortform, if possible. */
- STATIC int
- 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))
- return 0;
- /* 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. */
- static inline void
- xfs_exchmaps_clear_reflink(
- struct xfs_trans *tp,
- struct xfs_inode *ip)
- {
- trace_xfs_reflink_unset_inode_flag(ip);
- ip->i_diflags2 &= ~XFS_DIFLAG2_REFLINK;
- xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
- }
- /* Finish whatever work might come after an exchange operation. */
- static int
- 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 (xmi->xmi_flags & XFS_EXCHMAPS_ATTR_FORK)
- error = xfs_exchmaps_attr_to_sf(tp, xmi);
- else if (S_ISDIR(VFS_I(xmi->xmi_ip2)->i_mode))
- error = xfs_exchmaps_dir_to_sf(tp, xmi);
- else if (S_ISLNK(VFS_I(xmi->xmi_ip2)->i_mode))
- error = xfs_exchmaps_link_to_sf(tp, xmi);
- xmi->xmi_flags &= ~__XFS_EXCHMAPS_INO2_SHORTFORM;
- if (error)
- return error;
- }
- if (xmi->xmi_flags & XFS_EXCHMAPS_CLEAR_INO1_REFLINK) {
- xfs_exchmaps_clear_reflink(tp, xmi->xmi_ip1);
- xmi->xmi_flags &= ~XFS_EXCHMAPS_CLEAR_INO1_REFLINK;
- }
- if (xmi->xmi_flags & XFS_EXCHMAPS_CLEAR_INO2_REFLINK) {
- xfs_exchmaps_clear_reflink(tp, xmi->xmi_ip2);
- xmi->xmi_flags &= ~XFS_EXCHMAPS_CLEAR_INO2_REFLINK;
- }
- return 0;
- }
- /* Finish one step in a mapping exchange operation, possibly relogging. */
- int
- xfs_exchmaps_finish_one(
- struct xfs_trans *tp,
- struct xfs_exchmaps_intent *xmi)
- {
- struct xfs_bmbt_irec irec1, irec2;
- int error;
- if (xmi_has_more_exchange_work(xmi)) {
- /*
- * If the operation state says that some range of the files
- * have not yet been exchanged, look for mappings in that range
- * to exchange. If we find some mappings, exchange them.
- */
- error = xfs_exchmaps_find_mappings(xmi, &irec1, &irec2, NULL);
- if (error)
- return error;
- if (xmi_has_more_exchange_work(xmi))
- xfs_exchmaps_one_step(tp, xmi, &irec1, &irec2);
- /*
- * If the caller asked us to exchange the file sizes after the
- * exchange and either we just exchanged the last mappings in
- * the range or we didn't find anything to exchange, update the
- * ondisk file sizes.
- */
- if ((xmi->xmi_flags & XFS_EXCHMAPS_SET_SIZES) &&
- !xmi_has_more_exchange_work(xmi)) {
- xmi->xmi_ip1->i_disk_size = xmi->xmi_isize1;
- xmi->xmi_ip2->i_disk_size = xmi->xmi_isize2;
- xfs_trans_log_inode(tp, xmi->xmi_ip1, XFS_ILOG_CORE);
- xfs_trans_log_inode(tp, xmi->xmi_ip2, XFS_ILOG_CORE);
- }
- } else if (xmi_has_postop_work(xmi)) {
- /*
- * Now that we're finished with the exchange operation,
- * complete the post-op cleanup work.
- */
- error = xfs_exchmaps_do_postop_work(tp, xmi);
- if (error)
- return error;
- }
- 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;
- }
- /*
- * If we reach here, we've finished all the exchange work and the post
- * operation work. The last thing we need to do before returning to
- * the caller is to make sure that COW forks are set up correctly.
- */
- if (!(xmi->xmi_flags & XFS_EXCHMAPS_ATTR_FORK)) {
- xfs_exchmaps_ensure_cowfork(xmi->xmi_ip1);
- xfs_exchmaps_ensure_cowfork(xmi->xmi_ip2);
- }
- return 0;
- }
- /*
- * Compute the amount of bmbt blocks we should reserve for each file. In the
- * worst case, each exchange will fill a hole with a new mapping, which could
- * result in a btree split every time we add a new leaf block.
- */
- static inline uint64_t
- xfs_exchmaps_bmbt_blocks(
- struct xfs_mount *mp,
- const struct xfs_exchmaps_req *req)
- {
- return howmany_64(req->nr_exchanges,
- XFS_MAX_CONTIG_BMAPS_PER_BLOCK(mp)) *
- XFS_EXTENTADD_SPACE_RES(mp, xfs_exchmaps_reqfork(req));
- }
- /* Compute the space we should reserve for the rmap btree expansions. */
- static inline uint64_t
- xfs_exchmaps_rmapbt_blocks(
- struct xfs_mount *mp,
- const struct xfs_exchmaps_req *req)
- {
- if (!xfs_has_rmapbt(mp))
- return 0;
- if (XFS_IS_REALTIME_INODE(req->ip1))
- return 0;
- return howmany_64(req->nr_exchanges,
- XFS_MAX_CONTIG_RMAPS_PER_BLOCK(mp)) *
- XFS_RMAPADD_SPACE_RES(mp);
- }
- /* Estimate the bmbt and rmapbt overhead required to exchange mappings. */
- int
- xfs_exchmaps_estimate_overhead(
- struct xfs_exchmaps_req *req)
- {
- struct xfs_mount *mp = req->ip1->i_mount;
- xfs_filblks_t bmbt_blocks;
- xfs_filblks_t rmapbt_blocks;
- xfs_filblks_t resblks = req->resblks;
- /*
- * Compute the number of bmbt and rmapbt blocks we might need to handle
- * the estimated number of exchanges.
- */
- bmbt_blocks = xfs_exchmaps_bmbt_blocks(mp, req);
- rmapbt_blocks = xfs_exchmaps_rmapbt_blocks(mp, req);
- trace_xfs_exchmaps_overhead(mp, bmbt_blocks, rmapbt_blocks);
- /* 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;
- /*
- * Add together the number of blocks we need to handle btree growth,
- * then add it to the number of blocks we need to reserve to this
- * 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;
- req->resblks = resblks;
- trace_xfs_exchmaps_final_estimate(req);
- return 0;
- }
- /* Decide if we can merge two real mappings. */
- static inline bool
- xmi_can_merge(
- const struct xfs_bmbt_irec *b1,
- const struct xfs_bmbt_irec *b2)
- {
- /* Don't merge holes. */
- if (b1->br_startblock == HOLESTARTBLOCK ||
- b2->br_startblock == HOLESTARTBLOCK)
- return false;
- /* We don't merge holes. */
- if (!xfs_bmap_is_real_extent(b1) || !xfs_bmap_is_real_extent(b2))
- return false;
- if (b1->br_startoff + b1->br_blockcount == b2->br_startoff &&
- b1->br_startblock + b1->br_blockcount == b2->br_startblock &&
- b1->br_state == b2->br_state &&
- b1->br_blockcount + b2->br_blockcount <= XFS_MAX_BMBT_EXTLEN)
- return true;
- return false;
- }
- /*
- * Decide if we can merge three mappings. Caller must ensure all three
- * mappings must not be holes or delalloc reservations.
- */
- static inline bool
- xmi_can_merge_all(
- const struct xfs_bmbt_irec *l,
- const struct xfs_bmbt_irec *m,
- const struct xfs_bmbt_irec *r)
- {
- xfs_filblks_t new_len;
- new_len = l->br_blockcount + m->br_blockcount + r->br_blockcount;
- return new_len <= XFS_MAX_BMBT_EXTLEN;
- }
- #define CLEFT_CONTIG 0x01
- #define CRIGHT_CONTIG 0x02
- #define CHOLE 0x04
- #define CBOTH_CONTIG (CLEFT_CONTIG | CRIGHT_CONTIG)
- #define NLEFT_CONTIG 0x10
- #define NRIGHT_CONTIG 0x20
- #define NHOLE 0x40
- #define NBOTH_CONTIG (NLEFT_CONTIG | NRIGHT_CONTIG)
- /* Estimate the effect of a single exchange on mapping count. */
- static inline int
- xmi_delta_nextents_step(
- 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)
- {
- bool lhole, rhole, chole, nhole;
- unsigned int state = 0;
- int ret = 0;
- lhole = left->br_startblock == HOLESTARTBLOCK;
- rhole = right->br_startblock == HOLESTARTBLOCK;
- chole = curr->br_startblock == HOLESTARTBLOCK;
- nhole = new->br_startblock == HOLESTARTBLOCK;
- 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/right are the same mapping, so deleting curr
- * causes 2 new mappings to be created.
- */
- ret += 2;
- break;
- case 0:
- /*
- * curr is not contiguous with any mapping, so we remove curr
- * 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/right will become the same mapping, so adding
- * curr causes the deletion of right.
- */
- ret--;
- break;
- case 0:
- /* 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;
- }
- trace_xfs_exchmaps_delta_nextents_step(mp, left, curr, new, right, ret,
- state);
- return ret;
- }
- /* Make sure we don't overflow the extent (mapping) counters. */
- static inline int
- xmi_ensure_delta_nextents(
- struct xfs_exchmaps_req *req,
- struct xfs_inode *ip,
- int64_t delta)
- {
- struct xfs_mount *mp = ip->i_mount;
- int whichfork = xfs_exchmaps_reqfork(req);
- struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
- uint64_t new_nextents;
- xfs_extnum_t max_nextents;
- if (delta < 0)
- return 0;
- /*
- * It's always an error if the delta causes integer overflow. delta
- * needs an explicit cast here to avoid warnings about implicit casts
- * coded into the overflow check.
- */
- if (check_add_overflow(ifp->if_nextents, (uint64_t)delta,
- &new_nextents))
- return -EFBIG;
- if (XFS_TEST_ERROR(false, mp, XFS_ERRTAG_REDUCE_MAX_IEXTENTS) &&
- new_nextents > 10)
- return -EFBIG;
- /*
- * We always promote both inodes to have large extent counts if the
- * superblock feature is enabled, so we only need to check against the
- * theoretical maximum.
- */
- max_nextents = xfs_iext_max_nextents(xfs_has_large_extent_counts(mp),
- whichfork);
- if (new_nextents > max_nextents)
- return -EFBIG;
- return 0;
- }
- /* Find the next mapping after irec. */
- static inline int
- xmi_next(
- struct xfs_inode *ip,
- int bmap_flags,
- const struct xfs_bmbt_irec *irec,
- struct xfs_bmbt_irec *nrec)
- {
- xfs_fileoff_t off;
- xfs_filblks_t blockcount;
- int nimaps = 1;
- int error;
- off = irec->br_startoff + irec->br_blockcount;
- blockcount = XFS_MAX_FILEOFF - off;
- error = xfs_bmapi_read(ip, off, blockcount, nrec, &nimaps, bmap_flags);
- if (error)
- return error;
- if (nrec->br_startblock == DELAYSTARTBLOCK ||
- nrec->br_startoff != off) {
- /*
- * If we don't get the mapping we want, return a zero-length
- * mapping, which our estimator function will pretend is a hole.
- * We shouldn't get delalloc reservations.
- */
- nrec->br_startblock = HOLESTARTBLOCK;
- }
- return 0;
- }
- int __init
- xfs_exchmaps_intent_init_cache(void)
- {
- xfs_exchmaps_intent_cache = kmem_cache_create("xfs_exchmaps_intent",
- sizeof(struct xfs_exchmaps_intent),
- 0, 0, NULL);
- return xfs_exchmaps_intent_cache != NULL ? 0 : -ENOMEM;
- }
- void
- xfs_exchmaps_intent_destroy_cache(void)
- {
- kmem_cache_destroy(xfs_exchmaps_intent_cache);
- xfs_exchmaps_intent_cache = NULL;
- }
- /*
- * Decide if we will exchange the reflink flags between the two files after the
- * exchange. The only time we want to do this is if we're exchanging all
- * mappings under EOF and the inode reflink flags have different states.
- */
- static inline bool
- xmi_can_exchange_reflink_flags(
- const struct xfs_exchmaps_req *req,
- unsigned int reflink_state)
- {
- struct xfs_mount *mp = req->ip1->i_mount;
- if (hweight32(reflink_state) != 1)
- return false;
- if (req->startoff1 != 0 || req->startoff2 != 0)
- return false;
- if (req->blockcount != XFS_B_TO_FSB(mp, req->ip1->i_disk_size))
- return false;
- if (req->blockcount != XFS_B_TO_FSB(mp, req->ip2->i_disk_size))
- return false;
- return true;
- }
- /* Allocate and initialize a new incore intent item from a request. */
- struct xfs_exchmaps_intent *
- xfs_exchmaps_init_intent(
- const struct xfs_exchmaps_req *req)
- {
- struct xfs_exchmaps_intent *xmi;
- unsigned int rs = 0;
- xmi = kmem_cache_zalloc(xfs_exchmaps_intent_cache,
- GFP_NOFS | __GFP_NOFAIL);
- INIT_LIST_HEAD(&xmi->xmi_list);
- xmi->xmi_ip1 = req->ip1;
- xmi->xmi_ip2 = req->ip2;
- xmi->xmi_startoff1 = req->startoff1;
- xmi->xmi_startoff2 = req->startoff2;
- xmi->xmi_blockcount = req->blockcount;
- xmi->xmi_isize1 = xmi->xmi_isize2 = -1;
- xmi->xmi_flags = req->flags & XFS_EXCHMAPS_PARAMS;
- if (xfs_exchmaps_whichfork(xmi) == XFS_ATTR_FORK) {
- xmi->xmi_flags |= __XFS_EXCHMAPS_INO2_SHORTFORM;
- return xmi;
- }
- if (req->flags & XFS_EXCHMAPS_SET_SIZES) {
- xmi->xmi_flags |= XFS_EXCHMAPS_SET_SIZES;
- xmi->xmi_isize1 = req->ip2->i_disk_size;
- xmi->xmi_isize2 = req->ip1->i_disk_size;
- }
- /* 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;
- /*
- * Figure out if we're clearing the reflink flags (which effectively
- * exchanges them) after the operation.
- */
- 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;
- return xmi;
- }
- /*
- * Estimate the number of exchange operations and the number of file blocks
- * in each file that will be affected by the exchange operation.
- */
- int
- xfs_exchmaps_estimate(
- struct xfs_exchmaps_req *req)
- {
- struct xfs_exchmaps_intent *xmi;
- struct xfs_bmbt_irec irec1, irec2;
- struct xfs_exchmaps_adjacent adj = ADJACENT_INIT;
- xfs_filblks_t ip1_blocks = 0, ip2_blocks = 0;
- int64_t d_nexts1, d_nexts2;
- int bmap_flags;
- int error;
- ASSERT(!(req->flags & ~XFS_EXCHMAPS_PARAMS));
- bmap_flags = xfs_bmapi_aflag(xfs_exchmaps_reqfork(req));
- xmi = xfs_exchmaps_init_intent(req);
- /*
- * To guard against the possibility of overflowing the extent counters,
- * we have to estimate an upper bound on the potential increase in that
- * counter. We can split the mapping at each end of the range, and for
- * each step of the exchange we can split the mapping that we're
- * working on if the mappings do not align.
- */
- d_nexts1 = d_nexts2 = 3;
- while (xmi_has_more_exchange_work(xmi)) {
- /*
- * Walk through the file ranges until we find something to
- * exchange. Because we're simulating the exchange, pass in
- * adj to capture skipped mappings for correct estimation of
- * bmbt record merges.
- */
- error = xfs_exchmaps_find_mappings(xmi, &irec1, &irec2, &adj);
- if (error)
- goto out_free;
- if (!xmi_has_more_exchange_work(xmi))
- break;
- /* Update accounting. */
- if (xfs_bmap_is_real_extent(&irec1))
- ip1_blocks += irec1.br_blockcount;
- if (xfs_bmap_is_real_extent(&irec2))
- ip2_blocks += irec2.br_blockcount;
- req->nr_exchanges++;
- /* Read the next mappings from both files. */
- error = xmi_next(req->ip1, bmap_flags, &irec1, &adj.right1);
- if (error)
- goto out_free;
- error = xmi_next(req->ip2, bmap_flags, &irec2, &adj.right2);
- if (error)
- goto out_free;
- /* Update extent count deltas. */
- d_nexts1 += xmi_delta_nextents_step(req->ip1->i_mount,
- &adj.left1, &irec1, &irec2, &adj.right1);
- d_nexts2 += xmi_delta_nextents_step(req->ip1->i_mount,
- &adj.left2, &irec2, &irec1, &adj.right2);
- /* 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;
- }
- /*
- * Make sure that both forks have enough slack left in their extent
- * counters that the exchange operation will not overflow.
- */
- trace_xfs_exchmaps_delta_nextents(req, d_nexts1, d_nexts2);
- if (req->ip1 == req->ip2) {
- error = xmi_ensure_delta_nextents(req, req->ip1,
- d_nexts1 + d_nexts2);
- } else {
- error = xmi_ensure_delta_nextents(req, req->ip1, d_nexts1);
- if (error)
- goto out_free;
- error = xmi_ensure_delta_nextents(req, req->ip2, d_nexts2);
- }
- if (error)
- goto out_free;
- trace_xfs_exchmaps_initial_estimate(req);
- error = xfs_exchmaps_estimate_overhead(req);
- out_free:
- kmem_cache_free(xfs_exchmaps_intent_cache, xmi);
- return error;
- }
- /* Set the reflink flag before an operation. */
- static inline void
- xfs_exchmaps_set_reflink(
- struct xfs_trans *tp,
- struct xfs_inode *ip)
- {
- trace_xfs_reflink_set_inode_flag(ip);
- ip->i_diflags2 |= XFS_DIFLAG2_REFLINK;
- xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
- }
- /*
- * If either file has shared blocks and we're exchanging data forks, we must
- * flag the other file as having shared blocks so that we get the shared-block
- * rmap functions if we need to fix up the rmaps.
- */
- void
- xfs_exchmaps_ensure_reflink(
- struct xfs_trans *tp,
- const struct xfs_exchmaps_intent *xmi)
- {
- unsigned int rs = 0;
- 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. */
- static inline void
- xfs_exchmaps_ensure_large_extent_counts(
- struct xfs_trans *tp,
- struct xfs_inode *ip)
- {
- if (xfs_inode_has_large_extent_counts(ip))
- return;
- ip->i_diflags2 |= XFS_DIFLAG2_NREXT64;
- xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
- }
- /* Widen the extent counter fields of both inodes if necessary. */
- void
- xfs_exchmaps_upgrade_extent_counts(
- struct xfs_trans *tp,
- const struct xfs_exchmaps_intent *xmi)
- {
- if (!xfs_has_large_extent_counts(tp->t_mountp))
- return;
- xfs_exchmaps_ensure_large_extent_counts(tp, xmi->xmi_ip1);
- xfs_exchmaps_ensure_large_extent_counts(tp, xmi->xmi_ip2);
- }
- /*
- * Schedule an exchange a range of mappings from one inode to another.
- *
- * The use of file mapping exchange log intent items ensures the operation can
- * be resumed even if the system goes down. The caller must commit the
- * transaction to start the work.
- *
- * The caller must ensure the inodes must be joined to the transaction and
- * ILOCKd; they will still be joined to the transaction at exit.
- */
- void
- xfs_exchange_mappings(
- struct xfs_trans *tp,
- const struct xfs_exchmaps_req *req)
- {
- struct xfs_exchmaps_intent *xmi;
- BUILD_BUG_ON(XFS_EXCHMAPS_INTERNAL_FLAGS & XFS_EXCHMAPS_LOGGED_FLAGS);
- xfs_assert_ilocked(req->ip1, XFS_ILOCK_EXCL);
- xfs_assert_ilocked(req->ip2, XFS_ILOCK_EXCL);
- ASSERT(!(req->flags & ~XFS_EXCHMAPS_LOGGED_FLAGS));
- if (req->flags & XFS_EXCHMAPS_SET_SIZES)
- ASSERT(!(req->flags & XFS_EXCHMAPS_ATTR_FORK));
- ASSERT(xfs_has_exchange_range(tp->t_mountp));
- if (req->blockcount == 0)
- return;
- xmi = xfs_exchmaps_init_intent(req);
- xfs_exchmaps_defer_add(tp, xmi);
- xfs_exchmaps_ensure_reflink(tp, xmi);
- xfs_exchmaps_upgrade_extent_counts(tp, xmi);
- }
|