// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( c ) 2014 Red Hat , Inc .
* All Rights Reserved .
*/
#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_bit.h"
#include "xfs_mount.h"
#include "xfs_sb.h"
#include "xfs_defer.h"
#include "xfs_btree.h"
#include "xfs_trans.h"
#include "xfs_alloc.h"
#include "xfs_rmap.h"
#include "xfs_rmap_btree.h"
#include "xfs_trace.h"
#include "xfs_errortag.h"
#include "xfs_error.h"
#include "xfs_inode.h"
#include "xfs_ag.h"
#include "xfs_health.h"
#include "xfs_rmap_item.h"
#include "xfs_rtgroup.h"
#include "xfs_rtrmap_btree.h"
struct kmem_cache *xfs_rmap_intent_cache;
/*
* Lookup the first record less than or equal to [ bno , len , owner , offset ]
* in the btree given by cur .
*/
int
xfs_rmap_lookup_le(
struct xfs_btree_cur *cur,
xfs_agblock_t bno,
uint64_t owner,
uint64_t offset,
unsigned int flags,
struct xfs_rmap_irec *irec,
int *stat)
{
int get_stat = 0 ;
int error;
cur->bc_rec.r.rm_startblock = bno;
cur->bc_rec.r.rm_blockcount = 0 ;
cur->bc_rec.r.rm_owner = owner;
cur->bc_rec.r.rm_offset = offset;
cur->bc_rec.r.rm_flags = flags;
error = xfs_btree_lookup(cur, XFS_LOOKUP_LE, stat);
if (error || !(*stat) || !irec)
return error;
error = xfs_rmap_get_rec(cur, irec, &get_stat);
if (error)
return error;
if (!get_stat) {
xfs_btree_mark_sick(cur);
return -EFSCORRUPTED;
}
return 0 ;
}
/*
* Lookup the record exactly matching [ bno , len , owner , offset ]
* in the btree given by cur .
*/
int
xfs_rmap_lookup_eq(
struct xfs_btree_cur *cur,
xfs_agblock_t bno,
xfs_extlen_t len,
uint64_t owner,
uint64_t offset,
unsigned int flags,
int *stat)
{
cur->bc_rec.r.rm_startblock = bno;
cur->bc_rec.r.rm_blockcount = len;
cur->bc_rec.r.rm_owner = owner;
cur->bc_rec.r.rm_offset = offset;
cur->bc_rec.r.rm_flags = flags;
return xfs_btree_lookup(cur, XFS_LOOKUP_EQ, stat);
}
/*
* Update the record referred to by cur to the value given
* by [ bno , len , owner , offset ] .
* This either works ( return 0 ) or gets an EFSCORRUPTED error .
*/
STATIC int
xfs_rmap_update(
struct xfs_btree_cur *cur,
struct xfs_rmap_irec *irec)
{
union xfs_btree_rec rec;
int error;
trace_xfs_rmap_update(cur, irec->rm_startblock, irec->rm_blockcount,
irec->rm_owner, irec->rm_offset, irec->rm_flags);
rec.rmap.rm_startblock = cpu_to_be32(irec->rm_startblock);
rec.rmap.rm_blockcount = cpu_to_be32(irec->rm_blockcount);
rec.rmap.rm_owner = cpu_to_be64(irec->rm_owner);
rec.rmap.rm_offset = cpu_to_be64(
xfs_rmap_irec_offset_pack(irec));
error = xfs_btree_update(cur, &rec);
if (error)
trace_xfs_rmap_update_error(cur, error, _RET_IP_);
return error;
}
int
xfs_rmap_insert(
struct xfs_btree_cur *rcur,
xfs_agblock_t agbno,
xfs_extlen_t len,
uint64_t owner,
uint64_t offset,
unsigned int flags)
{
int i;
int error;
trace_xfs_rmap_insert(rcur, agbno, len, owner, offset, flags);
error = xfs_rmap_lookup_eq(rcur, agbno, len, owner, offset, flags, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(rcur->bc_mp, i != 0 )) {
xfs_btree_mark_sick(rcur);
error = -EFSCORRUPTED;
goto done;
}
rcur->bc_rec.r.rm_startblock = agbno;
rcur->bc_rec.r.rm_blockcount = len;
rcur->bc_rec.r.rm_owner = owner;
rcur->bc_rec.r.rm_offset = offset;
rcur->bc_rec.r.rm_flags = flags;
error = xfs_btree_insert(rcur, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(rcur->bc_mp, i != 1 )) {
xfs_btree_mark_sick(rcur);
error = -EFSCORRUPTED;
goto done;
}
done:
if (error)
trace_xfs_rmap_insert_error(rcur, error, _RET_IP_);
return error;
}
STATIC int
xfs_rmap_delete(
struct xfs_btree_cur *rcur,
xfs_agblock_t agbno,
xfs_extlen_t len,
uint64_t owner,
uint64_t offset,
unsigned int flags)
{
int i;
int error;
trace_xfs_rmap_delete(rcur, agbno, len, owner, offset, flags);
error = xfs_rmap_lookup_eq(rcur, agbno, len, owner, offset, flags, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(rcur->bc_mp, i != 1 )) {
xfs_btree_mark_sick(rcur);
error = -EFSCORRUPTED;
goto done;
}
error = xfs_btree_delete(rcur, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(rcur->bc_mp, i != 1 )) {
xfs_btree_mark_sick(rcur);
error = -EFSCORRUPTED;
goto done;
}
done:
if (error)
trace_xfs_rmap_delete_error(rcur, error, _RET_IP_);
return error;
}
/* Convert an internal btree record to an rmap record. */
xfs_failaddr_t
xfs_rmap_btrec_to_irec(
const union xfs_btree_rec *rec,
struct xfs_rmap_irec *irec)
{
irec->rm_startblock = be32_to_cpu(rec->rmap.rm_startblock);
irec->rm_blockcount = be32_to_cpu(rec->rmap.rm_blockcount);
irec->rm_owner = be64_to_cpu(rec->rmap.rm_owner);
return xfs_rmap_irec_offset_unpack(be64_to_cpu(rec->rmap.rm_offset),
irec);
}
/* Simple checks for rmap records. */
xfs_failaddr_t
xfs_rmap_check_irec(
struct xfs_perag *pag,
const struct xfs_rmap_irec *irec)
{
struct xfs_mount *mp = pag_mount(pag);
bool is_inode;
bool is_unwritten;
bool is_bmbt;
bool is_attr;
if (irec->rm_blockcount == 0 )
return __this_address;
if (irec->rm_startblock <= XFS_AGFL_BLOCK(mp)) {
if (irec->rm_owner != XFS_RMAP_OWN_FS)
return __this_address;
if (irec->rm_blockcount != XFS_AGFL_BLOCK(mp) + 1 )
return __this_address;
} else {
/* check for valid extent range, including overflow */
if (!xfs_verify_agbext(pag, irec->rm_startblock,
irec->rm_blockcount))
return __this_address;
}
if (!(xfs_verify_ino(mp, irec->rm_owner) ||
(irec->rm_owner <= XFS_RMAP_OWN_FS &&
irec->rm_owner >= XFS_RMAP_OWN_MIN)))
return __this_address;
/* Check flags. */
is_inode = !XFS_RMAP_NON_INODE_OWNER(irec->rm_owner);
is_bmbt = irec->rm_flags & XFS_RMAP_BMBT_BLOCK;
is_attr = irec->rm_flags & XFS_RMAP_ATTR_FORK;
is_unwritten = irec->rm_flags & XFS_RMAP_UNWRITTEN;
if (is_bmbt && irec->rm_offset != 0 )
return __this_address;
if (!is_inode && irec->rm_offset != 0 )
return __this_address;
if (is_unwritten && (is_bmbt || !is_inode || is_attr))
return __this_address;
if (!is_inode && (is_bmbt || is_unwritten || is_attr))
return __this_address;
/* Check for a valid fork offset, if applicable. */
if (is_inode && !is_bmbt &&
!xfs_verify_fileext(mp, irec->rm_offset, irec->rm_blockcount))
return __this_address;
return NULL;
}
static xfs_failaddr_t
xfs_rtrmap_check_meta_irec(
struct xfs_rtgroup *rtg,
const struct xfs_rmap_irec *irec)
{
struct xfs_mount *mp = rtg_mount(rtg);
if (irec->rm_offset != 0 )
return __this_address;
if (irec->rm_flags & XFS_RMAP_UNWRITTEN)
return __this_address;
switch (irec->rm_owner) {
case XFS_RMAP_OWN_FS:
if (irec->rm_startblock != 0 )
return __this_address;
if (irec->rm_blockcount != mp->m_sb.sb_rextsize)
return __this_address;
return NULL;
case XFS_RMAP_OWN_COW:
if (!xfs_has_rtreflink(mp))
return __this_address;
if (!xfs_verify_rgbext(rtg, irec->rm_startblock,
irec->rm_blockcount))
return __this_address;
return NULL;
default :
return __this_address;
}
return NULL;
}
static xfs_failaddr_t
xfs_rtrmap_check_inode_irec(
struct xfs_rtgroup *rtg,
const struct xfs_rmap_irec *irec)
{
struct xfs_mount *mp = rtg_mount(rtg);
if (!xfs_verify_ino(mp, irec->rm_owner))
return __this_address;
if (!xfs_verify_rgbext(rtg, irec->rm_startblock, irec->rm_blockcount))
return __this_address;
if (!xfs_verify_fileext(mp, irec->rm_offset, irec->rm_blockcount))
return __this_address;
return NULL;
}
xfs_failaddr_t
xfs_rtrmap_check_irec(
struct xfs_rtgroup *rtg,
const struct xfs_rmap_irec *irec)
{
if (irec->rm_blockcount == 0 )
return __this_address;
if (irec->rm_flags & (XFS_RMAP_BMBT_BLOCK | XFS_RMAP_ATTR_FORK))
return __this_address;
if (XFS_RMAP_NON_INODE_OWNER(irec->rm_owner))
return xfs_rtrmap_check_meta_irec(rtg, irec);
return xfs_rtrmap_check_inode_irec(rtg, irec);
}
static inline xfs_failaddr_t
xfs_rmap_check_btrec(
struct xfs_btree_cur *cur,
const struct xfs_rmap_irec *irec)
{
if (xfs_btree_is_rtrmap(cur->bc_ops) ||
xfs_btree_is_mem_rtrmap(cur->bc_ops))
return xfs_rtrmap_check_irec(to_rtg(cur->bc_group), irec);
return xfs_rmap_check_irec(to_perag(cur->bc_group), irec);
}
static inline int
xfs_rmap_complain_bad_rec(
struct xfs_btree_cur *cur,
xfs_failaddr_t fa,
const struct xfs_rmap_irec *irec)
{
struct xfs_mount *mp = cur->bc_mp;
if (xfs_btree_is_mem_rmap(cur->bc_ops))
xfs_warn(mp,
"In-Memory Reverse Mapping BTree record corruption detected at %pS!" , fa);
else if (xfs_btree_is_rtrmap(cur->bc_ops))
xfs_warn(mp,
"RT Reverse Mapping BTree record corruption in rtgroup %u detected at %pS!" ,
cur->bc_group->xg_gno, fa);
else
xfs_warn(mp,
"Reverse Mapping BTree record corruption in AG %d detected at %pS!" ,
cur->bc_group->xg_gno, fa);
xfs_warn(mp,
"Owner 0x%llx, flags 0x%x, start block 0x%x block count 0x%x" ,
irec->rm_owner, irec->rm_flags, irec->rm_startblock,
irec->rm_blockcount);
xfs_btree_mark_sick(cur);
return -EFSCORRUPTED;
}
/*
* Get the data from the pointed - to record .
*/
int
xfs_rmap_get_rec(
struct xfs_btree_cur *cur,
struct xfs_rmap_irec *irec,
int *stat)
{
union xfs_btree_rec *rec;
xfs_failaddr_t fa;
int error;
error = xfs_btree_get_rec(cur, &rec, stat);
if (error || !*stat)
return error;
fa = xfs_rmap_btrec_to_irec(rec, irec);
if (!fa)
fa = xfs_rmap_check_btrec(cur, irec);
if (fa)
return xfs_rmap_complain_bad_rec(cur, fa, irec);
return 0 ;
}
struct xfs_find_left_neighbor_info {
struct xfs_rmap_irec high;
struct xfs_rmap_irec *irec;
};
/* For each rmap given, figure out if it matches the key we want. */
STATIC int
xfs_rmap_find_left_neighbor_helper(
struct xfs_btree_cur *cur,
const struct xfs_rmap_irec *rec,
void *priv)
{
struct xfs_find_left_neighbor_info *info = priv;
trace_xfs_rmap_find_left_neighbor_candidate(cur, rec->rm_startblock,
rec->rm_blockcount, rec->rm_owner, rec->rm_offset,
rec->rm_flags);
if (rec->rm_owner != info->high.rm_owner)
return 0 ;
if (!XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) &&
!(rec->rm_flags & XFS_RMAP_BMBT_BLOCK) &&
rec->rm_offset + rec->rm_blockcount - 1 != info->high.rm_offset)
return 0 ;
*info->irec = *rec;
return -ECANCELED;
}
/*
* Find the record to the left of the given extent , being careful only to
* return a match with the same owner and adjacent physical and logical
* block ranges .
*/
STATIC int
xfs_rmap_find_left_neighbor(
struct xfs_btree_cur *cur,
xfs_agblock_t bno,
uint64_t owner,
uint64_t offset,
unsigned int flags,
struct xfs_rmap_irec *irec,
int *stat)
{
struct xfs_find_left_neighbor_info info;
int found = 0 ;
int error;
*stat = 0 ;
if (bno == 0 )
return 0 ;
info.high.rm_startblock = bno - 1 ;
info.high.rm_owner = owner;
if (!XFS_RMAP_NON_INODE_OWNER(owner) &&
!(flags & XFS_RMAP_BMBT_BLOCK)) {
if (offset == 0 )
return 0 ;
info.high.rm_offset = offset - 1 ;
} else
info.high.rm_offset = 0 ;
info.high.rm_flags = flags;
info.high.rm_blockcount = 0 ;
info.irec = irec;
trace_xfs_rmap_find_left_neighbor_query(cur, bno, 0 , owner, offset,
flags);
/*
* Historically , we always used the range query to walk every reverse
* mapping that could possibly overlap the key that the caller asked
* for , and filter out the ones that don ' t . That is very slow when
* there are a lot of records .
*
* However , there are two scenarios where the classic btree search can
* produce correct results - - if the index contains a record that is an
* exact match for the lookup key ; and if there are no other records
* between the record we want and the key we supplied .
*
* As an optimization , try a non - overlapped lookup first . This makes
* extent conversion and remap operations run a bit faster if the
* physical extents aren ' t being shared . If we don ' t find what we
* want , we fall back to the overlapped query .
*/
error = xfs_rmap_lookup_le(cur, bno, owner, offset, flags, irec,
&found);
if (error)
return error;
if (found)
error = xfs_rmap_find_left_neighbor_helper(cur, irec, &info);
if (!error)
error = xfs_rmap_query_range(cur, &info.high, &info.high,
xfs_rmap_find_left_neighbor_helper, &info);
if (error != -ECANCELED)
return error;
*stat = 1 ;
trace_xfs_rmap_find_left_neighbor_result(cur, irec->rm_startblock,
irec->rm_blockcount, irec->rm_owner, irec->rm_offset,
irec->rm_flags);
return 0 ;
}
/* For each rmap given, figure out if it matches the key we want. */
STATIC int
xfs_rmap_lookup_le_range_helper(
struct xfs_btree_cur *cur,
const struct xfs_rmap_irec *rec,
void *priv)
{
struct xfs_find_left_neighbor_info *info = priv;
trace_xfs_rmap_lookup_le_range_candidate(cur, rec->rm_startblock,
rec->rm_blockcount, rec->rm_owner, rec->rm_offset,
rec->rm_flags);
if (rec->rm_owner != info->high.rm_owner)
return 0 ;
if (!XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) &&
!(rec->rm_flags & XFS_RMAP_BMBT_BLOCK) &&
(rec->rm_offset > info->high.rm_offset ||
rec->rm_offset + rec->rm_blockcount <= info->high.rm_offset))
return 0 ;
*info->irec = *rec;
return -ECANCELED;
}
/*
* Find the record to the left of the given extent , being careful only to
* return a match with the same owner and overlapping physical and logical
* block ranges . This is the overlapping - interval version of
* xfs_rmap_lookup_le .
*/
int
xfs_rmap_lookup_le_range(
struct xfs_btree_cur *cur,
xfs_agblock_t bno,
uint64_t owner,
uint64_t offset,
unsigned int flags,
struct xfs_rmap_irec *irec,
int *stat)
{
struct xfs_find_left_neighbor_info info;
int found = 0 ;
int error;
info.high.rm_startblock = bno;
info.high.rm_owner = owner;
if (!XFS_RMAP_NON_INODE_OWNER(owner) && !(flags & XFS_RMAP_BMBT_BLOCK))
info.high.rm_offset = offset;
else
info.high.rm_offset = 0 ;
info.high.rm_flags = flags;
info.high.rm_blockcount = 0 ;
*stat = 0 ;
info.irec = irec;
trace_xfs_rmap_lookup_le_range(cur, bno, 0 , owner, offset, flags);
/*
* Historically , we always used the range query to walk every reverse
* mapping that could possibly overlap the key that the caller asked
* for , and filter out the ones that don ' t . That is very slow when
* there are a lot of records .
*
* However , there are two scenarios where the classic btree search can
* produce correct results - - if the index contains a record that is an
* exact match for the lookup key ; and if there are no other records
* between the record we want and the key we supplied .
*
* As an optimization , try a non - overlapped lookup first . This makes
* scrub run much faster on most filesystems because bmbt records are
* usually an exact match for rmap records . If we don ' t find what we
* want , we fall back to the overlapped query .
*/
error = xfs_rmap_lookup_le(cur, bno, owner, offset, flags, irec,
&found);
if (error)
return error;
if (found)
error = xfs_rmap_lookup_le_range_helper(cur, irec, &info);
if (!error)
error = xfs_rmap_query_range(cur, &info.high, &info.high,
xfs_rmap_lookup_le_range_helper, &info);
if (error != -ECANCELED)
return error;
*stat = 1 ;
trace_xfs_rmap_lookup_le_range_result(cur, irec->rm_startblock,
irec->rm_blockcount, irec->rm_owner, irec->rm_offset,
irec->rm_flags);
return 0 ;
}
/*
* Perform all the relevant owner checks for a removal op . If we ' re doing an
* unknown - owner removal then we have no owner information to check .
*/
static int
xfs_rmap_free_check_owner(
struct xfs_btree_cur *cur,
uint64_t ltoff,
struct xfs_rmap_irec *rec,
xfs_extlen_t len,
uint64_t owner,
uint64_t offset,
unsigned int flags)
{
struct xfs_mount *mp = cur->bc_mp;
int error = 0 ;
if (owner == XFS_RMAP_OWN_UNKNOWN)
return 0 ;
/* Make sure the unwritten flag matches. */
if (XFS_IS_CORRUPT(mp,
(flags & XFS_RMAP_UNWRITTEN) !=
(rec->rm_flags & XFS_RMAP_UNWRITTEN))) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out;
}
/* Make sure the owner matches what we expect to find in the tree. */
if (XFS_IS_CORRUPT(mp, owner != rec->rm_owner)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out;
}
/* Check the offset, if necessary. */
if (XFS_RMAP_NON_INODE_OWNER(owner))
goto out;
if (flags & XFS_RMAP_BMBT_BLOCK) {
if (XFS_IS_CORRUPT(mp,
!(rec->rm_flags & XFS_RMAP_BMBT_BLOCK))) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out;
}
} else {
if (XFS_IS_CORRUPT(mp, rec->rm_offset > offset)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out;
}
if (XFS_IS_CORRUPT(mp,
offset + len > ltoff + rec->rm_blockcount)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out;
}
}
out:
return error;
}
/*
* Find the extent in the rmap btree and remove it .
*
* The record we find should always be an exact match for the extent that we ' re
* looking for , since we insert them into the btree without modification .
*
* Special Case # 1 : when growing the filesystem , we " free " an extent when
* growing the last AG . This extent is new space and so it is not tracked as
* used space in the btree . The growfs code will pass in an owner of
* XFS_RMAP_OWN_NULL to indicate that it expected that there is no owner of this
* extent . We verify that - the extent lookup result in a record that does not
* overlap .
*
* Special Case # 2 : EFIs do not record the owner of the extent , so when
* recovering EFIs from the log we pass in XFS_RMAP_OWN_UNKNOWN to tell the rmap
* btree to ignore the owner ( i . e . wildcard match ) so we don ' t trigger
* corruption checks during log recovery .
*/
STATIC int
xfs_rmap_unmap(
struct xfs_btree_cur *cur,
xfs_agblock_t bno,
xfs_extlen_t len,
bool unwritten,
const struct xfs_owner_info *oinfo)
{
struct xfs_mount *mp = cur->bc_mp;
struct xfs_rmap_irec ltrec;
uint64_t ltoff;
int error = 0 ;
int i;
uint64_t owner;
uint64_t offset;
unsigned int flags;
bool ignore_off;
xfs_owner_info_unpack(oinfo, &owner, &offset, &flags);
ignore_off = XFS_RMAP_NON_INODE_OWNER(owner) ||
(flags & XFS_RMAP_BMBT_BLOCK);
if (unwritten)
flags |= XFS_RMAP_UNWRITTEN;
trace_xfs_rmap_unmap(cur, bno, len, unwritten, oinfo);
/*
* We should always have a left record because there ' s a static record
* for the AG headers at rm_startblock = = 0 created by mkfs / growfs that
* will not ever be removed from the tree .
*/
error = xfs_rmap_lookup_le(cur, bno, owner, offset, flags, <rec, &i);
if (error)
goto out_error;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out_error;
}
trace_xfs_rmap_lookup_le_range_result(cur, ltrec.rm_startblock,
ltrec.rm_blockcount, ltrec.rm_owner, ltrec.rm_offset,
ltrec.rm_flags);
ltoff = ltrec.rm_offset;
/*
* For growfs , the incoming extent must be beyond the left record we
* just found as it is new space and won ' t be used by anyone . This is
* just a corruption check as we don ' t actually do anything with this
* extent . Note that we need to use > = instead of > because it might
* be the case that the " left " extent goes all the way to EOFS .
*/
if (owner == XFS_RMAP_OWN_NULL) {
if (XFS_IS_CORRUPT(mp,
bno <
ltrec.rm_startblock + ltrec.rm_blockcount)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out_error;
}
goto out_done;
}
/*
* If we ' re doing an unknown - owner removal for EFI recovery , we expect
* to find the full range in the rmapbt or nothing at all . If we
* don ' t find any rmaps overlapping either end of the range , we ' re
* done . Hopefully this means that the EFI creator already queued
* ( and finished ) a RUI to remove the rmap .
*/
if (owner == XFS_RMAP_OWN_UNKNOWN &&
ltrec.rm_startblock + ltrec.rm_blockcount <= bno) {
struct xfs_rmap_irec rtrec;
error = xfs_btree_increment(cur, 0 , &i);
if (error)
goto out_error;
if (i == 0 )
goto out_done;
error = xfs_rmap_get_rec(cur, &rtrec, &i);
if (error)
goto out_error;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out_error;
}
if (rtrec.rm_startblock >= bno + len)
goto out_done;
}
/* Make sure the extent we found covers the entire freeing range. */
if (XFS_IS_CORRUPT(mp,
ltrec.rm_startblock > bno ||
ltrec.rm_startblock + ltrec.rm_blockcount <
bno + len)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out_error;
}
/* Check owner information. */
error = xfs_rmap_free_check_owner(cur, ltoff, <rec, len, owner,
offset, flags);
if (error)
goto out_error;
if (ltrec.rm_startblock == bno && ltrec.rm_blockcount == len) {
/* exact match, simply remove the record from rmap tree */
trace_xfs_rmap_delete(cur, ltrec.rm_startblock,
ltrec.rm_blockcount, ltrec.rm_owner,
ltrec.rm_offset, ltrec.rm_flags);
error = xfs_btree_delete(cur, &i);
if (error)
goto out_error;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out_error;
}
} else if (ltrec.rm_startblock == bno) {
/*
* overlap left hand side of extent : move the start , trim the
* length and update the current record .
*
* ltbno ltlen
* Orig : | oooooooooooooooooooo |
* Freeing : | fffffffff |
* Result : | rrrrrrrrrr |
* bno len
*/
ltrec.rm_startblock += len;
ltrec.rm_blockcount -= len;
if (!ignore_off)
ltrec.rm_offset += len;
error = xfs_rmap_update(cur, <rec);
if (error)
goto out_error;
} else if (ltrec.rm_startblock + ltrec.rm_blockcount == bno + len) {
/*
* overlap right hand side of extent : trim the length and update
* the current record .
*
* ltbno ltlen
* Orig : | oooooooooooooooooooo |
* Freeing : | fffffffff |
* Result : | rrrrrrrrrr |
* bno len
*/
ltrec.rm_blockcount -= len;
error = xfs_rmap_update(cur, <rec);
if (error)
goto out_error;
} else {
/*
* overlap middle of extent : trim the length of the existing
* record to the length of the new left - extent size , increment
* the insertion position so we can insert a new record
* containing the remaining right - extent space .
*
* ltbno ltlen
* Orig : | oooooooooooooooooooo |
* Freeing : | fffffffff |
* Result : | rrrrr | | rrrr |
* bno len
*/
xfs_extlen_t orig_len = ltrec.rm_blockcount;
ltrec.rm_blockcount = bno - ltrec.rm_startblock;
error = xfs_rmap_update(cur, <rec);
if (error)
goto out_error;
error = xfs_btree_increment(cur, 0 , &i);
if (error)
goto out_error;
cur->bc_rec.r.rm_startblock = bno + len;
cur->bc_rec.r.rm_blockcount = orig_len - len -
ltrec.rm_blockcount;
cur->bc_rec.r.rm_owner = ltrec.rm_owner;
if (ignore_off)
cur->bc_rec.r.rm_offset = 0 ;
else
cur->bc_rec.r.rm_offset = offset + len;
cur->bc_rec.r.rm_flags = flags;
trace_xfs_rmap_insert(cur, cur->bc_rec.r.rm_startblock,
cur->bc_rec.r.rm_blockcount,
cur->bc_rec.r.rm_owner,
cur->bc_rec.r.rm_offset,
cur->bc_rec.r.rm_flags);
error = xfs_btree_insert(cur, &i);
if (error)
goto out_error;
}
out_done:
trace_xfs_rmap_unmap_done(cur, bno, len, unwritten, oinfo);
out_error:
if (error)
trace_xfs_rmap_unmap_error(cur, error, _RET_IP_);
return error;
}
#ifdef CONFIG_XFS_LIVE_HOOKS
/*
* Use a static key here to reduce the overhead of rmapbt live updates . If
* the compiler supports jump labels , the static branch will be replaced by a
* nop sled when there are no hook users . Online fsck is currently the only
* caller , so this is a reasonable tradeoff .
*
* Note : Patching the kernel code requires taking the cpu hotplug lock . Other
* parts of the kernel allocate memory with that lock held , which means that
* XFS callers cannot hold any locks that might be used by memory reclaim or
* writeback when calling the static_branch_ { inc , dec } functions .
*/
DEFINE_STATIC_XFS_HOOK_SWITCH(xfs_rmap_hooks_switch);
void
xfs_rmap_hook_disable(void )
{
xfs_hooks_switch_off(&xfs_rmap_hooks_switch);
}
void
xfs_rmap_hook_enable(void )
{
xfs_hooks_switch_on(&xfs_rmap_hooks_switch);
}
/* Call downstream hooks for a reverse mapping update. */
static inline void
xfs_rmap_update_hook(
struct xfs_trans *tp,
struct xfs_group *xg,
enum xfs_rmap_intent_type op,
xfs_agblock_t startblock,
xfs_extlen_t blockcount,
bool unwritten,
const struct xfs_owner_info *oinfo)
{
if (xfs_hooks_switched_on(&xfs_rmap_hooks_switch)) {
struct xfs_rmap_update_params p = {
.startblock = startblock,
.blockcount = blockcount,
.unwritten = unwritten,
.oinfo = *oinfo, /* struct copy */
};
if (xg)
xfs_hooks_call(&xg->xg_rmap_update_hooks, op, &p);
}
}
/* Call the specified function during a reverse mapping update. */
int
xfs_rmap_hook_add(
struct xfs_group *xg,
struct xfs_rmap_hook *hook)
{
return xfs_hooks_add(&xg->xg_rmap_update_hooks, &hook->rmap_hook);
}
/* Stop calling the specified function during a reverse mapping update. */
void
xfs_rmap_hook_del(
struct xfs_group *xg,
struct xfs_rmap_hook *hook)
{
xfs_hooks_del(&xg->xg_rmap_update_hooks, &hook->rmap_hook);
}
/* Configure rmap update hook functions. */
void
xfs_rmap_hook_setup(
struct xfs_rmap_hook *hook,
notifier_fn_t mod_fn)
{
xfs_hook_setup(&hook->rmap_hook, mod_fn);
}
#else
# define xfs_rmap_update_hook(t, p, o, s, b, u, oi) do { } while (0 )
#endif /* CONFIG_XFS_LIVE_HOOKS */
/*
* Remove a reference to an extent in the rmap btree .
*/
int
xfs_rmap_free(
struct xfs_trans *tp,
struct xfs_buf *agbp,
struct xfs_perag *pag,
xfs_agblock_t bno,
xfs_extlen_t len,
const struct xfs_owner_info *oinfo)
{
struct xfs_mount *mp = tp->t_mountp;
struct xfs_btree_cur *cur;
int error;
if (!xfs_has_rmapbt(mp))
return 0 ;
cur = xfs_rmapbt_init_cursor(mp, tp, agbp, pag);
xfs_rmap_update_hook(tp, pag_group(pag), XFS_RMAP_UNMAP, bno, len,
false , oinfo);
error = xfs_rmap_unmap(cur, bno, len, false , oinfo);
xfs_btree_del_cursor(cur, error);
return error;
}
/*
* A mergeable rmap must have the same owner and the same values for
* the unwritten , attr_fork , and bmbt flags . The startblock and
* offset are checked separately .
*/
static bool
xfs_rmap_is_mergeable(
struct xfs_rmap_irec *irec,
uint64_t owner,
unsigned int flags)
{
if (irec->rm_owner == XFS_RMAP_OWN_NULL)
return false ;
if (irec->rm_owner != owner)
return false ;
if ((flags & XFS_RMAP_UNWRITTEN) ^
(irec->rm_flags & XFS_RMAP_UNWRITTEN))
return false ;
if ((flags & XFS_RMAP_ATTR_FORK) ^
(irec->rm_flags & XFS_RMAP_ATTR_FORK))
return false ;
if ((flags & XFS_RMAP_BMBT_BLOCK) ^
(irec->rm_flags & XFS_RMAP_BMBT_BLOCK))
return false ;
return true ;
}
/*
* When we allocate a new block , the first thing we do is add a reference to
* the extent in the rmap btree . This takes the form of a [ agbno , length ,
* owner , offset ] record . Flags are encoded in the high bits of the offset
* field .
*/
STATIC int
xfs_rmap_map(
struct xfs_btree_cur *cur,
xfs_agblock_t bno,
xfs_extlen_t len,
bool unwritten,
const struct xfs_owner_info *oinfo)
{
struct xfs_mount *mp = cur->bc_mp;
struct xfs_rmap_irec ltrec;
struct xfs_rmap_irec gtrec;
int have_gt;
int have_lt;
int error = 0 ;
int i;
uint64_t owner;
uint64_t offset;
unsigned int flags = 0 ;
bool ignore_off;
xfs_owner_info_unpack(oinfo, &owner, &offset, &flags);
ASSERT(owner != 0 );
ignore_off = XFS_RMAP_NON_INODE_OWNER(owner) ||
(flags & XFS_RMAP_BMBT_BLOCK);
if (unwritten)
flags |= XFS_RMAP_UNWRITTEN;
trace_xfs_rmap_map(cur, bno, len, unwritten, oinfo);
ASSERT(!xfs_rmap_should_skip_owner_update(oinfo));
/*
* For the initial lookup , look for an exact match or the left - adjacent
* record for our insertion point . This will also give us the record for
* start block contiguity tests .
*/
error = xfs_rmap_lookup_le(cur, bno, owner, offset, flags, <rec,
&have_lt);
if (error)
goto out_error;
if (have_lt) {
trace_xfs_rmap_lookup_le_range_result(cur, ltrec.rm_startblock,
ltrec.rm_blockcount, ltrec.rm_owner,
ltrec.rm_offset, ltrec.rm_flags);
if (!xfs_rmap_is_mergeable(<rec, owner, flags))
have_lt = 0 ;
}
if (XFS_IS_CORRUPT(mp,
have_lt != 0 &&
ltrec.rm_startblock + ltrec.rm_blockcount > bno)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out_error;
}
/*
* Increment the cursor to see if we have a right - adjacent record to our
* insertion point . This will give us the record for end block
* contiguity tests .
*/
error = xfs_btree_increment(cur, 0 , &have_gt);
if (error)
goto out_error;
if (have_gt) {
error = xfs_rmap_get_rec(cur, >rec, &have_gt);
if (error)
goto out_error;
if (XFS_IS_CORRUPT(mp, have_gt != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out_error;
}
if (XFS_IS_CORRUPT(mp, bno + len > gtrec.rm_startblock)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out_error;
}
trace_xfs_rmap_find_right_neighbor_result(cur,
gtrec.rm_startblock, gtrec.rm_blockcount,
gtrec.rm_owner, gtrec.rm_offset,
gtrec.rm_flags);
if (!xfs_rmap_is_mergeable(>rec, owner, flags))
have_gt = 0 ;
}
/*
* Note : cursor currently points one record to the right of ltrec , even
* if there is no record in the tree to the right .
*/
if (have_lt &&
ltrec.rm_startblock + ltrec.rm_blockcount == bno &&
(ignore_off || ltrec.rm_offset + ltrec.rm_blockcount == offset)) {
/*
* left edge contiguous , merge into left record .
*
* ltbno ltlen
* orig : | ooooooooo |
* adding : | aaaaaaaaa |
* result : | rrrrrrrrrrrrrrrrrrr |
* bno len
*/
ltrec.rm_blockcount += len;
if (have_gt &&
bno + len == gtrec.rm_startblock &&
(ignore_off || offset + len == gtrec.rm_offset) &&
(unsigned long )ltrec.rm_blockcount + len +
gtrec.rm_blockcount <= XFS_RMAP_LEN_MAX) {
/*
* right edge also contiguous , delete right record
* and merge into left record .
*
* ltbno ltlen gtbno gtlen
* orig : | ooooooooo | | ooooooooo |
* adding : | aaaaaaaaa |
* result : | rrrrrrrrrrrrrrrrrrrrrrrrrrrrr |
*/
ltrec.rm_blockcount += gtrec.rm_blockcount;
trace_xfs_rmap_delete(cur, gtrec.rm_startblock,
gtrec.rm_blockcount, gtrec.rm_owner,
gtrec.rm_offset, gtrec.rm_flags);
error = xfs_btree_delete(cur, &i);
if (error)
goto out_error;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out_error;
}
}
/* point the cursor back to the left record and update */
error = xfs_btree_decrement(cur, 0 , &have_gt);
if (error)
goto out_error;
error = xfs_rmap_update(cur, <rec);
if (error)
goto out_error;
} else if (have_gt &&
bno + len == gtrec.rm_startblock &&
(ignore_off || offset + len == gtrec.rm_offset)) {
/*
* right edge contiguous , merge into right record .
*
* gtbno gtlen
* Orig : | ooooooooo |
* adding : | aaaaaaaaa |
* Result : | rrrrrrrrrrrrrrrrrrr |
* bno len
*/
gtrec.rm_startblock = bno;
gtrec.rm_blockcount += len;
if (!ignore_off)
gtrec.rm_offset = offset;
error = xfs_rmap_update(cur, >rec);
if (error)
goto out_error;
} else {
/*
* no contiguous edge with identical owner , insert
* new record at current cursor position .
*/
cur->bc_rec.r.rm_startblock = bno;
cur->bc_rec.r.rm_blockcount = len;
cur->bc_rec.r.rm_owner = owner;
cur->bc_rec.r.rm_offset = offset;
cur->bc_rec.r.rm_flags = flags;
trace_xfs_rmap_insert(cur, bno, len, owner, offset, flags);
error = xfs_btree_insert(cur, &i);
if (error)
goto out_error;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out_error;
}
}
trace_xfs_rmap_map_done(cur, bno, len, unwritten, oinfo);
out_error:
if (error)
trace_xfs_rmap_map_error(cur, error, _RET_IP_);
return error;
}
/*
* Add a reference to an extent in the rmap btree .
*/
int
xfs_rmap_alloc(
struct xfs_trans *tp,
struct xfs_buf *agbp,
struct xfs_perag *pag,
xfs_agblock_t bno,
xfs_extlen_t len,
const struct xfs_owner_info *oinfo)
{
struct xfs_mount *mp = tp->t_mountp;
struct xfs_btree_cur *cur;
int error;
if (!xfs_has_rmapbt(mp))
return 0 ;
cur = xfs_rmapbt_init_cursor(mp, tp, agbp, pag);
xfs_rmap_update_hook(tp, pag_group(pag), XFS_RMAP_MAP, bno, len, false ,
oinfo);
error = xfs_rmap_map(cur, bno, len, false , oinfo);
xfs_btree_del_cursor(cur, error);
return error;
}
#define RMAP_LEFT_CONTIG (1 << 0 )
#define RMAP_RIGHT_CONTIG (1 << 1 )
#define RMAP_LEFT_FILLING (1 << 2 )
#define RMAP_RIGHT_FILLING (1 << 3 )
#define RMAP_LEFT_VALID (1 << 6 )
#define RMAP_RIGHT_VALID (1 << 7 )
#define LEFT r[0 ]
#define RIGHT r[1 ]
#define PREV r[2 ]
#define NEW r[3 ]
/*
* Convert an unwritten extent to a real extent or vice versa .
* Does not handle overlapping extents .
*/
STATIC int
xfs_rmap_convert(
struct xfs_btree_cur *cur,
xfs_agblock_t bno,
xfs_extlen_t len,
bool unwritten,
const struct xfs_owner_info *oinfo)
{
struct xfs_mount *mp = cur->bc_mp;
struct xfs_rmap_irec r[4 ]; /* neighbor extent entries */
/* left is 0, right is 1, */
/* prev is 2, new is 3 */
uint64_t owner;
uint64_t offset;
uint64_t new_endoff;
unsigned int oldext;
unsigned int newext;
unsigned int flags = 0 ;
int i;
int state = 0 ;
int error;
xfs_owner_info_unpack(oinfo, &owner, &offset, &flags);
ASSERT(!(XFS_RMAP_NON_INODE_OWNER(owner) ||
(flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK))));
oldext = unwritten ? XFS_RMAP_UNWRITTEN : 0 ;
new_endoff = offset + len;
trace_xfs_rmap_convert(cur, bno, len, unwritten, oinfo);
/*
* For the initial lookup , look for an exact match or the left - adjacent
* record for our insertion point . This will also give us the record for
* start block contiguity tests .
*/
error = xfs_rmap_lookup_le(cur, bno, owner, offset, oldext, &PREV, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
trace_xfs_rmap_lookup_le_range_result(cur, PREV.rm_startblock,
PREV.rm_blockcount, PREV.rm_owner, PREV.rm_offset,
PREV.rm_flags);
ASSERT(PREV.rm_offset <= offset);
ASSERT(PREV.rm_offset + PREV.rm_blockcount >= new_endoff);
ASSERT((PREV.rm_flags & XFS_RMAP_UNWRITTEN) == oldext);
newext = ~oldext & XFS_RMAP_UNWRITTEN;
/*
* Set flags determining what part of the previous oldext allocation
* extent is being replaced by a newext allocation .
*/
if (PREV.rm_offset == offset)
state |= RMAP_LEFT_FILLING;
if (PREV.rm_offset + PREV.rm_blockcount == new_endoff)
state |= RMAP_RIGHT_FILLING;
/*
* Decrement the cursor to see if we have a left - adjacent record to our
* insertion point . This will give us the record for end block
* contiguity tests .
*/
error = xfs_btree_decrement(cur, 0 , &i);
if (error)
goto done;
if (i) {
state |= RMAP_LEFT_VALID;
error = xfs_rmap_get_rec(cur, &LEFT, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
if (XFS_IS_CORRUPT(mp,
LEFT.rm_startblock + LEFT.rm_blockcount >
bno)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
trace_xfs_rmap_find_left_neighbor_result(cur,
LEFT.rm_startblock, LEFT.rm_blockcount,
LEFT.rm_owner, LEFT.rm_offset, LEFT.rm_flags);
if (LEFT.rm_startblock + LEFT.rm_blockcount == bno &&
LEFT.rm_offset + LEFT.rm_blockcount == offset &&
xfs_rmap_is_mergeable(&LEFT, owner, newext))
state |= RMAP_LEFT_CONTIG;
}
/*
* Increment the cursor to see if we have a right - adjacent record to our
* insertion point . This will give us the record for end block
* contiguity tests .
*/
error = xfs_btree_increment(cur, 0 , &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
error = xfs_btree_increment(cur, 0 , &i);
if (error)
goto done;
if (i) {
state |= RMAP_RIGHT_VALID;
error = xfs_rmap_get_rec(cur, &RIGHT, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
if (XFS_IS_CORRUPT(mp, bno + len > RIGHT.rm_startblock)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
trace_xfs_rmap_find_right_neighbor_result(cur,
RIGHT.rm_startblock, RIGHT.rm_blockcount,
RIGHT.rm_owner, RIGHT.rm_offset,
RIGHT.rm_flags);
if (bno + len == RIGHT.rm_startblock &&
offset + len == RIGHT.rm_offset &&
xfs_rmap_is_mergeable(&RIGHT, owner, newext))
state |= RMAP_RIGHT_CONTIG;
}
/* check that left + prev + right is not too long */
if ((state & (RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG |
RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG)) ==
(RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG |
RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG) &&
(unsigned long )LEFT.rm_blockcount + len +
RIGHT.rm_blockcount > XFS_RMAP_LEN_MAX)
state &= ~RMAP_RIGHT_CONTIG;
trace_xfs_rmap_convert_state(cur, state, _RET_IP_);
/* reset the cursor back to PREV */
error = xfs_rmap_lookup_le(cur, bno, owner, offset, oldext, NULL, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
/*
* Switch out based on the FILLING and CONTIG state bits .
*/
switch (state & (RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG |
RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG)) {
case RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG |
RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG:
/*
* Setting all of a previous oldext extent to newext .
* The left and right neighbors are both contiguous with new .
*/
error = xfs_btree_increment(cur, 0 , &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
trace_xfs_rmap_delete(cur, RIGHT.rm_startblock,
RIGHT.rm_blockcount, RIGHT.rm_owner,
RIGHT.rm_offset, RIGHT.rm_flags);
error = xfs_btree_delete(cur, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
error = xfs_btree_decrement(cur, 0 , &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
trace_xfs_rmap_delete(cur, PREV.rm_startblock,
PREV.rm_blockcount, PREV.rm_owner,
PREV.rm_offset, PREV.rm_flags);
error = xfs_btree_delete(cur, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
error = xfs_btree_decrement(cur, 0 , &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
NEW = LEFT;
NEW .rm_blockcount += PREV.rm_blockcount + RIGHT.rm_blockcount;
error = xfs_rmap_update(cur, &NEW );
if (error)
goto done;
break ;
case RMAP_LEFT_FILLING | RMAP_RIGHT_FILLING | RMAP_LEFT_CONTIG:
/*
* Setting all of a previous oldext extent to newext .
* The left neighbor is contiguous , the right is not .
*/
trace_xfs_rmap_delete(cur, PREV.rm_startblock,
PREV.rm_blockcount, PREV.rm_owner,
PREV.rm_offset, PREV.rm_flags);
error = xfs_btree_delete(cur, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
error = xfs_btree_decrement(cur, 0 , &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
NEW = LEFT;
NEW .rm_blockcount += PREV.rm_blockcount;
error = xfs_rmap_update(cur, &NEW );
if (error)
goto done;
break ;
case RMAP_LEFT_FILLING | RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG:
/*
* Setting all of a previous oldext extent to newext .
* The right neighbor is contiguous , the left is not .
*/
error = xfs_btree_increment(cur, 0 , &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
trace_xfs_rmap_delete(cur, RIGHT.rm_startblock,
RIGHT.rm_blockcount, RIGHT.rm_owner,
RIGHT.rm_offset, RIGHT.rm_flags);
error = xfs_btree_delete(cur, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
error = xfs_btree_decrement(cur, 0 , &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
NEW = PREV;
NEW .rm_blockcount = len + RIGHT.rm_blockcount;
NEW .rm_flags = newext;
error = xfs_rmap_update(cur, &NEW );
if (error)
goto done;
break ;
case RMAP_LEFT_FILLING | RMAP_RIGHT_FILLING:
/*
* Setting all of a previous oldext extent to newext .
* Neither the left nor right neighbors are contiguous with
* the new one .
*/
NEW = PREV;
NEW .rm_flags = newext;
error = xfs_rmap_update(cur, &NEW );
if (error)
goto done;
break ;
case RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG:
/*
* Setting the first part of a previous oldext extent to newext .
* The left neighbor is contiguous .
*/
NEW = PREV;
NEW .rm_offset += len;
NEW .rm_startblock += len;
NEW .rm_blockcount -= len;
error = xfs_rmap_update(cur, &NEW );
if (error)
goto done;
error = xfs_btree_decrement(cur, 0 , &i);
if (error)
goto done;
NEW = LEFT;
NEW .rm_blockcount += len;
error = xfs_rmap_update(cur, &NEW );
if (error)
goto done;
break ;
case RMAP_LEFT_FILLING:
/*
* Setting the first part of a previous oldext extent to newext .
* The left neighbor is not contiguous .
*/
NEW = PREV;
NEW .rm_startblock += len;
NEW .rm_offset += len;
NEW .rm_blockcount -= len;
error = xfs_rmap_update(cur, &NEW );
if (error)
goto done;
NEW .rm_startblock = bno;
NEW .rm_owner = owner;
NEW .rm_offset = offset;
NEW .rm_blockcount = len;
NEW .rm_flags = newext;
cur->bc_rec.r = NEW ;
trace_xfs_rmap_insert(cur, bno, len, owner, offset, newext);
error = xfs_btree_insert(cur, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
break ;
case RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG:
/*
* Setting the last part of a previous oldext extent to newext .
* The right neighbor is contiguous with the new allocation .
*/
NEW = PREV;
NEW .rm_blockcount -= len;
error = xfs_rmap_update(cur, &NEW );
if (error)
goto done;
error = xfs_btree_increment(cur, 0 , &i);
if (error)
goto done;
NEW = RIGHT;
NEW .rm_offset = offset;
NEW .rm_startblock = bno;
NEW .rm_blockcount += len;
error = xfs_rmap_update(cur, &NEW );
if (error)
goto done;
break ;
case RMAP_RIGHT_FILLING:
/*
* Setting the last part of a previous oldext extent to newext .
* The right neighbor is not contiguous .
*/
NEW = PREV;
NEW .rm_blockcount -= len;
error = xfs_rmap_update(cur, &NEW );
if (error)
goto done;
error = xfs_rmap_lookup_eq(cur, bno, len, owner, offset,
oldext, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 0 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
NEW .rm_startblock = bno;
NEW .rm_owner = owner;
NEW .rm_offset = offset;
NEW .rm_blockcount = len;
NEW .rm_flags = newext;
cur->bc_rec.r = NEW ;
trace_xfs_rmap_insert(cur, bno, len, owner, offset, newext);
error = xfs_btree_insert(cur, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
break ;
case 0 :
/*
* Setting the middle part of a previous oldext extent to
* newext . Contiguity is impossible here .
* One extent becomes three extents .
*/
/* new right extent - oldext */
NEW .rm_startblock = bno + len;
NEW .rm_owner = owner;
NEW .rm_offset = new_endoff;
NEW .rm_blockcount = PREV.rm_offset + PREV.rm_blockcount -
new_endoff;
NEW .rm_flags = PREV.rm_flags;
error = xfs_rmap_update(cur, &NEW );
if (error)
goto done;
/* new left extent - oldext */
NEW = PREV;
NEW .rm_blockcount = offset - PREV.rm_offset;
cur->bc_rec.r = NEW ;
trace_xfs_rmap_insert(cur, NEW .rm_startblock,
NEW .rm_blockcount, NEW .rm_owner, NEW .rm_offset,
NEW .rm_flags);
error = xfs_btree_insert(cur, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
/*
* Reset the cursor to the position of the new extent
* we are about to insert as we can ' t trust it after
* the previous insert .
*/
error = xfs_rmap_lookup_eq(cur, bno, len, owner, offset,
oldext, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 0 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
/* new middle extent - newext */
cur->bc_rec.r.rm_flags &= ~XFS_RMAP_UNWRITTEN;
cur->bc_rec.r.rm_flags |= newext;
trace_xfs_rmap_insert(cur, bno, len, owner, offset, newext);
error = xfs_btree_insert(cur, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
break ;
case RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG | RMAP_RIGHT_CONTIG:
case RMAP_RIGHT_FILLING | RMAP_LEFT_CONTIG | RMAP_RIGHT_CONTIG:
case RMAP_LEFT_FILLING | RMAP_RIGHT_CONTIG:
case RMAP_RIGHT_FILLING | RMAP_LEFT_CONTIG:
case RMAP_LEFT_CONTIG | RMAP_RIGHT_CONTIG:
case RMAP_LEFT_CONTIG:
case RMAP_RIGHT_CONTIG:
/*
* These cases are all impossible .
*/
ASSERT(0 );
}
trace_xfs_rmap_convert_done(cur, bno, len, unwritten, oinfo);
done:
if (error)
trace_xfs_rmap_convert_error(cur, error, _RET_IP_);
return error;
}
/*
* Convert an unwritten extent to a real extent or vice versa . If there is no
* possibility of overlapping extents , delegate to the simpler convert
* function .
*/
STATIC int
xfs_rmap_convert_shared(
struct xfs_btree_cur *cur,
xfs_agblock_t bno,
xfs_extlen_t len,
bool unwritten,
const struct xfs_owner_info *oinfo)
{
struct xfs_mount *mp = cur->bc_mp;
struct xfs_rmap_irec r[4 ]; /* neighbor extent entries */
/* left is 0, right is 1, */
/* prev is 2, new is 3 */
uint64_t owner;
uint64_t offset;
uint64_t new_endoff;
unsigned int oldext;
unsigned int newext;
unsigned int flags = 0 ;
int i;
int state = 0 ;
int error;
xfs_owner_info_unpack(oinfo, &owner, &offset, &flags);
ASSERT(!(XFS_RMAP_NON_INODE_OWNER(owner) ||
(flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK))));
oldext = unwritten ? XFS_RMAP_UNWRITTEN : 0 ;
new_endoff = offset + len;
trace_xfs_rmap_convert(cur, bno, len, unwritten, oinfo);
/*
* For the initial lookup , look for and exact match or the left - adjacent
* record for our insertion point . This will also give us the record for
* start block contiguity tests .
*/
error = xfs_rmap_lookup_le_range(cur, bno, owner, offset, oldext,
&PREV, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
ASSERT(PREV.rm_offset <= offset);
ASSERT(PREV.rm_offset + PREV.rm_blockcount >= new_endoff);
ASSERT((PREV.rm_flags & XFS_RMAP_UNWRITTEN) == oldext);
newext = ~oldext & XFS_RMAP_UNWRITTEN;
/*
* Set flags determining what part of the previous oldext allocation
* extent is being replaced by a newext allocation .
*/
if (PREV.rm_offset == offset)
state |= RMAP_LEFT_FILLING;
if (PREV.rm_offset + PREV.rm_blockcount == new_endoff)
state |= RMAP_RIGHT_FILLING;
/* Is there a left record that abuts our range? */
error = xfs_rmap_find_left_neighbor(cur, bno, owner, offset, newext,
&LEFT, &i);
if (error)
goto done;
if (i) {
state |= RMAP_LEFT_VALID;
if (XFS_IS_CORRUPT(mp,
LEFT.rm_startblock + LEFT.rm_blockcount >
bno)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
if (xfs_rmap_is_mergeable(&LEFT, owner, newext))
state |= RMAP_LEFT_CONTIG;
}
/* Is there a right record that abuts our range? */
error = xfs_rmap_lookup_eq(cur, bno + len, len, owner, offset + len,
newext, &i);
if (error)
goto done;
if (i) {
state |= RMAP_RIGHT_VALID;
error = xfs_rmap_get_rec(cur, &RIGHT, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
if (XFS_IS_CORRUPT(mp, bno + len > RIGHT.rm_startblock)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
trace_xfs_rmap_find_right_neighbor_result(cur,
RIGHT.rm_startblock, RIGHT.rm_blockcount,
RIGHT.rm_owner, RIGHT.rm_offset,
RIGHT.rm_flags);
if (xfs_rmap_is_mergeable(&RIGHT, owner, newext))
state |= RMAP_RIGHT_CONTIG;
}
/* check that left + prev + right is not too long */
if ((state & (RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG |
RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG)) ==
(RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG |
RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG) &&
(unsigned long )LEFT.rm_blockcount + len +
RIGHT.rm_blockcount > XFS_RMAP_LEN_MAX)
state &= ~RMAP_RIGHT_CONTIG;
trace_xfs_rmap_convert_state(cur, state, _RET_IP_);
/*
* Switch out based on the FILLING and CONTIG state bits .
*/
switch (state & (RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG |
RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG)) {
case RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG |
RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG:
/*
* Setting all of a previous oldext extent to newext .
* The left and right neighbors are both contiguous with new .
*/
error = xfs_rmap_delete(cur, RIGHT.rm_startblock,
RIGHT.rm_blockcount, RIGHT.rm_owner,
RIGHT.rm_offset, RIGHT.rm_flags);
if (error)
goto done;
error = xfs_rmap_delete(cur, PREV.rm_startblock,
PREV.rm_blockcount, PREV.rm_owner,
PREV.rm_offset, PREV.rm_flags);
if (error)
goto done;
NEW = LEFT;
error = xfs_rmap_lookup_eq(cur, NEW .rm_startblock,
NEW .rm_blockcount, NEW .rm_owner,
NEW .rm_offset, NEW .rm_flags, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
NEW .rm_blockcount += PREV.rm_blockcount + RIGHT.rm_blockcount;
error = xfs_rmap_update(cur, &NEW );
if (error)
goto done;
break ;
case RMAP_LEFT_FILLING | RMAP_RIGHT_FILLING | RMAP_LEFT_CONTIG:
/*
* Setting all of a previous oldext extent to newext .
* The left neighbor is contiguous , the right is not .
*/
error = xfs_rmap_delete(cur, PREV.rm_startblock,
PREV.rm_blockcount, PREV.rm_owner,
PREV.rm_offset, PREV.rm_flags);
if (error)
goto done;
NEW = LEFT;
error = xfs_rmap_lookup_eq(cur, NEW .rm_startblock,
NEW .rm_blockcount, NEW .rm_owner,
NEW .rm_offset, NEW .rm_flags, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
NEW .rm_blockcount += PREV.rm_blockcount;
error = xfs_rmap_update(cur, &NEW );
if (error)
goto done;
break ;
case RMAP_LEFT_FILLING | RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG:
/*
* Setting all of a previous oldext extent to newext .
* The right neighbor is contiguous , the left is not .
*/
error = xfs_rmap_delete(cur, RIGHT.rm_startblock,
RIGHT.rm_blockcount, RIGHT.rm_owner,
RIGHT.rm_offset, RIGHT.rm_flags);
if (error)
goto done;
NEW = PREV;
error = xfs_rmap_lookup_eq(cur, NEW .rm_startblock,
NEW .rm_blockcount, NEW .rm_owner,
NEW .rm_offset, NEW .rm_flags, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
NEW .rm_blockcount += RIGHT.rm_blockcount;
NEW .rm_flags = RIGHT.rm_flags;
error = xfs_rmap_update(cur, &NEW );
if (error)
goto done;
break ;
case RMAP_LEFT_FILLING | RMAP_RIGHT_FILLING:
/*
* Setting all of a previous oldext extent to newext .
* Neither the left nor right neighbors are contiguous with
* the new one .
*/
NEW = PREV;
error = xfs_rmap_lookup_eq(cur, NEW .rm_startblock,
NEW .rm_blockcount, NEW .rm_owner,
NEW .rm_offset, NEW .rm_flags, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
NEW .rm_flags = newext;
error = xfs_rmap_update(cur, &NEW );
if (error)
goto done;
break ;
case RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG:
/*
* Setting the first part of a previous oldext extent to newext .
* The left neighbor is contiguous .
*/
NEW = PREV;
error = xfs_rmap_delete(cur, NEW .rm_startblock,
NEW .rm_blockcount, NEW .rm_owner,
NEW .rm_offset, NEW .rm_flags);
if (error)
goto done;
NEW .rm_offset += len;
NEW .rm_startblock += len;
NEW .rm_blockcount -= len;
error = xfs_rmap_insert(cur, NEW .rm_startblock,
NEW .rm_blockcount, NEW .rm_owner,
NEW .rm_offset, NEW .rm_flags);
if (error)
goto done;
NEW = LEFT;
error = xfs_rmap_lookup_eq(cur, NEW .rm_startblock,
NEW .rm_blockcount, NEW .rm_owner,
NEW .rm_offset, NEW .rm_flags, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
NEW .rm_blockcount += len;
error = xfs_rmap_update(cur, &NEW );
if (error)
goto done;
break ;
case RMAP_LEFT_FILLING:
/*
* Setting the first part of a previous oldext extent to newext .
* The left neighbor is not contiguous .
*/
NEW = PREV;
error = xfs_rmap_delete(cur, NEW .rm_startblock,
NEW .rm_blockcount, NEW .rm_owner,
NEW .rm_offset, NEW .rm_flags);
if (error)
goto done;
NEW .rm_offset += len;
NEW .rm_startblock += len;
NEW .rm_blockcount -= len;
error = xfs_rmap_insert(cur, NEW .rm_startblock,
NEW .rm_blockcount, NEW .rm_owner,
NEW .rm_offset, NEW .rm_flags);
if (error)
goto done;
error = xfs_rmap_insert(cur, bno, len, owner, offset, newext);
if (error)
goto done;
break ;
case RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG:
/*
* Setting the last part of a previous oldext extent to newext .
* The right neighbor is contiguous with the new allocation .
*/
NEW = PREV;
error = xfs_rmap_lookup_eq(cur, NEW .rm_startblock,
NEW .rm_blockcount, NEW .rm_owner,
NEW .rm_offset, NEW .rm_flags, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
NEW .rm_blockcount = offset - NEW .rm_offset;
error = xfs_rmap_update(cur, &NEW );
if (error)
goto done;
NEW = RIGHT;
error = xfs_rmap_delete(cur, NEW .rm_startblock,
NEW .rm_blockcount, NEW .rm_owner,
NEW .rm_offset, NEW .rm_flags);
if (error)
goto done;
NEW .rm_offset = offset;
NEW .rm_startblock = bno;
NEW .rm_blockcount += len;
error = xfs_rmap_insert(cur, NEW .rm_startblock,
NEW .rm_blockcount, NEW .rm_owner,
NEW .rm_offset, NEW .rm_flags);
if (error)
goto done;
break ;
case RMAP_RIGHT_FILLING:
/*
* Setting the last part of a previous oldext extent to newext .
* The right neighbor is not contiguous .
*/
NEW = PREV;
error = xfs_rmap_lookup_eq(cur, NEW .rm_startblock,
NEW .rm_blockcount, NEW .rm_owner,
NEW .rm_offset, NEW .rm_flags, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
NEW .rm_blockcount -= len;
error = xfs_rmap_update(cur, &NEW );
if (error)
goto done;
error = xfs_rmap_insert(cur, bno, len, owner, offset, newext);
if (error)
goto done;
break ;
case 0 :
/*
* Setting the middle part of a previous oldext extent to
* newext . Contiguity is impossible here .
* One extent becomes three extents .
*/
/* new right extent - oldext */
NEW .rm_startblock = bno + len;
NEW .rm_owner = owner;
NEW .rm_offset = new_endoff;
NEW .rm_blockcount = PREV.rm_offset + PREV.rm_blockcount -
new_endoff;
NEW .rm_flags = PREV.rm_flags;
error = xfs_rmap_insert(cur, NEW .rm_startblock,
NEW .rm_blockcount, NEW .rm_owner, NEW .rm_offset,
NEW .rm_flags);
if (error)
goto done;
/* new left extent - oldext */
NEW = PREV;
error = xfs_rmap_lookup_eq(cur, NEW .rm_startblock,
NEW .rm_blockcount, NEW .rm_owner,
NEW .rm_offset, NEW .rm_flags, &i);
if (error)
goto done;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto done;
}
NEW .rm_blockcount = offset - NEW .rm_offset;
error = xfs_rmap_update(cur, &NEW );
if (error)
goto done;
/* new middle extent - newext */
NEW .rm_startblock = bno;
NEW .rm_blockcount = len;
NEW .rm_owner = owner;
NEW .rm_offset = offset;
NEW .rm_flags = newext;
error = xfs_rmap_insert(cur, NEW .rm_startblock,
NEW .rm_blockcount, NEW .rm_owner, NEW .rm_offset,
NEW .rm_flags);
if (error)
goto done;
break ;
case RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG | RMAP_RIGHT_CONTIG:
case RMAP_RIGHT_FILLING | RMAP_LEFT_CONTIG | RMAP_RIGHT_CONTIG:
case RMAP_LEFT_FILLING | RMAP_RIGHT_CONTIG:
case RMAP_RIGHT_FILLING | RMAP_LEFT_CONTIG:
case RMAP_LEFT_CONTIG | RMAP_RIGHT_CONTIG:
case RMAP_LEFT_CONTIG:
case RMAP_RIGHT_CONTIG:
/*
* These cases are all impossible .
*/
ASSERT(0 );
}
trace_xfs_rmap_convert_done(cur, bno, len, unwritten, oinfo);
done:
if (error)
trace_xfs_rmap_convert_error(cur, error, _RET_IP_);
return error;
}
#undef NEW
#undef LEFT
#undef RIGHT
#undef PREV
/*
* Find an extent in the rmap btree and unmap it . For rmap extent types that
* can overlap ( data fork rmaps on reflink filesystems ) we must be careful
* that the prev / next records in the btree might belong to another owner .
* Therefore we must use delete + insert to alter any of the key fields .
*
* For every other situation there can only be one owner for a given extent ,
* so we can call the regular _ free function .
*/
STATIC int
xfs_rmap_unmap_shared(
struct xfs_btree_cur *cur,
xfs_agblock_t bno,
xfs_extlen_t len,
bool unwritten,
const struct xfs_owner_info *oinfo)
{
struct xfs_mount *mp = cur->bc_mp;
struct xfs_rmap_irec ltrec;
uint64_t ltoff;
int error = 0 ;
int i;
uint64_t owner;
uint64_t offset;
unsigned int flags;
xfs_owner_info_unpack(oinfo, &owner, &offset, &flags);
if (unwritten)
flags |= XFS_RMAP_UNWRITTEN;
trace_xfs_rmap_unmap(cur, bno, len, unwritten, oinfo);
/*
* We should always have a left record because there ' s a static record
* for the AG headers at rm_startblock = = 0 created by mkfs / growfs that
* will not ever be removed from the tree .
*/
error = xfs_rmap_lookup_le_range(cur, bno, owner, offset, flags,
<rec, &i);
if (error)
goto out_error;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out_error;
}
ltoff = ltrec.rm_offset;
/* Make sure the extent we found covers the entire freeing range. */
if (XFS_IS_CORRUPT(mp,
ltrec.rm_startblock > bno ||
ltrec.rm_startblock + ltrec.rm_blockcount <
bno + len)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out_error;
}
/* Make sure the owner matches what we expect to find in the tree. */
if (XFS_IS_CORRUPT(mp, owner != ltrec.rm_owner)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out_error;
}
/* Make sure the unwritten flag matches. */
if (XFS_IS_CORRUPT(mp,
(flags & XFS_RMAP_UNWRITTEN) !=
(ltrec.rm_flags & XFS_RMAP_UNWRITTEN))) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out_error;
}
/* Check the offset. */
if (XFS_IS_CORRUPT(mp, ltrec.rm_offset > offset)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out_error;
}
if (XFS_IS_CORRUPT(mp, offset > ltoff + ltrec.rm_blockcount)) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out_error;
}
if (ltrec.rm_startblock == bno && ltrec.rm_blockcount == len) {
/* Exact match, simply remove the record from rmap tree. */
error = xfs_rmap_delete(cur, ltrec.rm_startblock,
ltrec.rm_blockcount, ltrec.rm_owner,
ltrec.rm_offset, ltrec.rm_flags);
if (error)
goto out_error;
} else if (ltrec.rm_startblock == bno) {
/*
* Overlap left hand side of extent : move the start , trim the
* length and update the current record .
*
* ltbno ltlen
* Orig : | oooooooooooooooooooo |
* Freeing : | fffffffff |
* Result : | rrrrrrrrrr |
* bno len
*/
/* Delete prev rmap. */
error = xfs_rmap_delete(cur, ltrec.rm_startblock,
ltrec.rm_blockcount, ltrec.rm_owner,
ltrec.rm_offset, ltrec.rm_flags);
if (error)
goto out_error;
/* Add an rmap at the new offset. */
ltrec.rm_startblock += len;
ltrec.rm_blockcount -= len;
ltrec.rm_offset += len;
error = xfs_rmap_insert(cur, ltrec.rm_startblock,
ltrec.rm_blockcount, ltrec.rm_owner,
ltrec.rm_offset, ltrec.rm_flags);
if (error)
goto out_error;
} else if (ltrec.rm_startblock + ltrec.rm_blockcount == bno + len) {
/*
* Overlap right hand side of extent : trim the length and
* update the current record .
*
* ltbno ltlen
* Orig : | oooooooooooooooooooo |
* Freeing : | fffffffff |
* Result : | rrrrrrrrrr |
* bno len
*/
error = xfs_rmap_lookup_eq(cur, ltrec.rm_startblock,
ltrec.rm_blockcount, ltrec.rm_owner,
ltrec.rm_offset, ltrec.rm_flags, &i);
if (error)
goto out_error;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out_error;
}
ltrec.rm_blockcount -= len;
error = xfs_rmap_update(cur, <rec);
if (error)
goto out_error;
} else {
/*
* Overlap middle of extent : trim the length of the existing
* record to the length of the new left - extent size , increment
* the insertion position so we can insert a new record
* containing the remaining right - extent space .
*
* ltbno ltlen
* Orig : | oooooooooooooooooooo |
* Freeing : | fffffffff |
* Result : | rrrrr | | rrrr |
* bno len
*/
xfs_extlen_t orig_len = ltrec.rm_blockcount;
/* Shrink the left side of the rmap */
error = xfs_rmap_lookup_eq(cur, ltrec.rm_startblock,
ltrec.rm_blockcount, ltrec.rm_owner,
ltrec.rm_offset, ltrec.rm_flags, &i);
if (error)
goto out_error;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out_error;
}
ltrec.rm_blockcount = bno - ltrec.rm_startblock;
error = xfs_rmap_update(cur, <rec);
if (error)
goto out_error;
/* Add an rmap at the new offset */
error = xfs_rmap_insert(cur, bno + len,
orig_len - len - ltrec.rm_blockcount,
ltrec.rm_owner, offset + len,
ltrec.rm_flags);
if (error)
goto out_error;
}
trace_xfs_rmap_unmap_done(cur, bno, len, unwritten, oinfo);
out_error:
if (error)
trace_xfs_rmap_unmap_error(cur, error, _RET_IP_);
return error;
}
/*
* Find an extent in the rmap btree and map it . For rmap extent types that
* can overlap ( data fork rmaps on reflink filesystems ) we must be careful
* that the prev / next records in the btree might belong to another owner .
* Therefore we must use delete + insert to alter any of the key fields .
*
* For every other situation there can only be one owner for a given extent ,
* so we can call the regular _ alloc function .
*/
STATIC int
xfs_rmap_map_shared(
struct xfs_btree_cur *cur,
xfs_agblock_t bno,
xfs_extlen_t len,
bool unwritten,
const struct xfs_owner_info *oinfo)
{
struct xfs_mount *mp = cur->bc_mp;
struct xfs_rmap_irec ltrec;
struct xfs_rmap_irec gtrec;
int have_gt;
int have_lt;
int error = 0 ;
int i;
uint64_t owner;
uint64_t offset;
unsigned int flags = 0 ;
xfs_owner_info_unpack(oinfo, &owner, &offset, &flags);
if (unwritten)
flags |= XFS_RMAP_UNWRITTEN;
trace_xfs_rmap_map(cur, bno, len, unwritten, oinfo);
/* Is there a left record that abuts our range? */
error = xfs_rmap_find_left_neighbor(cur, bno, owner, offset, flags,
<rec, &have_lt);
if (error)
goto out_error;
if (have_lt &&
!xfs_rmap_is_mergeable(<rec, owner, flags))
have_lt = 0 ;
/* Is there a right record that abuts our range? */
error = xfs_rmap_lookup_eq(cur, bno + len, len, owner, offset + len,
flags, &have_gt);
if (error)
goto out_error;
if (have_gt) {
error = xfs_rmap_get_rec(cur, >rec, &have_gt);
if (error)
goto out_error;
if (XFS_IS_CORRUPT(mp, have_gt != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out_error;
}
trace_xfs_rmap_find_right_neighbor_result(cur,
gtrec.rm_startblock, gtrec.rm_blockcount,
gtrec.rm_owner, gtrec.rm_offset,
gtrec.rm_flags);
if (!xfs_rmap_is_mergeable(>rec, owner, flags))
have_gt = 0 ;
}
if (have_lt &&
ltrec.rm_startblock + ltrec.rm_blockcount == bno &&
ltrec.rm_offset + ltrec.rm_blockcount == offset) {
/*
* Left edge contiguous , merge into left record .
*
* ltbno ltlen
* orig : | ooooooooo |
* adding : | aaaaaaaaa |
* result : | rrrrrrrrrrrrrrrrrrr |
* bno len
*/
ltrec.rm_blockcount += len;
if (have_gt &&
bno + len == gtrec.rm_startblock &&
offset + len == gtrec.rm_offset) {
/*
* Right edge also contiguous , delete right record
* and merge into left record .
*
* ltbno ltlen gtbno gtlen
* orig : | ooooooooo | | ooooooooo |
* adding : | aaaaaaaaa |
* result : | rrrrrrrrrrrrrrrrrrrrrrrrrrrrr |
*/
ltrec.rm_blockcount += gtrec.rm_blockcount;
error = xfs_rmap_delete(cur, gtrec.rm_startblock,
gtrec.rm_blockcount, gtrec.rm_owner,
gtrec.rm_offset, gtrec.rm_flags);
if (error)
goto out_error;
}
/* Point the cursor back to the left record and update. */
error = xfs_rmap_lookup_eq(cur, ltrec.rm_startblock,
ltrec.rm_blockcount, ltrec.rm_owner,
ltrec.rm_offset, ltrec.rm_flags, &i);
if (error)
goto out_error;
if (XFS_IS_CORRUPT(mp, i != 1 )) {
xfs_btree_mark_sick(cur);
error = -EFSCORRUPTED;
goto out_error;
}
error = xfs_rmap_update(cur, <rec);
if (error)
goto out_error;
} else if (have_gt &&
bno + len == gtrec.rm_startblock &&
offset + len == gtrec.rm_offset) {
/*
* Right edge contiguous , merge into right record .
*
* gtbno gtlen
* Orig : | ooooooooo |
* adding : | aaaaaaaaa |
* Result : | rrrrrrrrrrrrrrrrrrr |
* bno len
*/
/* Delete the old record. */
error = xfs_rmap_delete(cur, gtrec.rm_startblock,
gtrec.rm_blockcount, gtrec.rm_owner,
gtrec.rm_offset, gtrec.rm_flags);
if (error)
goto out_error;
/* Move the start and re-add it. */
gtrec.rm_startblock = bno;
gtrec.rm_blockcount += len;
gtrec.rm_offset = offset;
error = xfs_rmap_insert(cur, gtrec.rm_startblock,
gtrec.rm_blockcount, gtrec.rm_owner,
gtrec.rm_offset, gtrec.rm_flags);
if (error)
goto out_error;
} else {
/*
* No contiguous edge with identical owner , insert
* new record at current cursor position .
*/
error = xfs_rmap_insert(cur, bno, len, owner, offset, flags);
if (error)
goto out_error;
}
trace_xfs_rmap_map_done(cur, bno, len, unwritten, oinfo);
out_error:
if (error)
trace_xfs_rmap_map_error(cur, error, _RET_IP_);
return error;
}
/* Insert a raw rmap into the rmapbt. */
int
xfs_rmap_map_raw(
struct xfs_btree_cur *cur,
struct xfs_rmap_irec *rmap)
{
struct xfs_owner_info oinfo;
xfs_owner_info_pack(&oinfo, rmap->rm_owner, rmap->rm_offset,
rmap->rm_flags);
if ((rmap->rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK |
XFS_RMAP_UNWRITTEN)) ||
XFS_RMAP_NON_INODE_OWNER(rmap->rm_owner))
return xfs_rmap_map(cur, rmap->rm_startblock,
rmap->rm_blockcount,
rmap->rm_flags & XFS_RMAP_UNWRITTEN,
&oinfo);
return xfs_rmap_map_shared(cur, rmap->rm_startblock,
rmap->rm_blockcount,
rmap->rm_flags & XFS_RMAP_UNWRITTEN,
&oinfo);
}
struct xfs_rmap_query_range_info {
xfs_rmap_query_range_fn fn;
void *priv;
};
/* Format btree record and pass to our callback. */
STATIC int
xfs_rmap_query_range_helper(
struct xfs_btree_cur *cur,
const union xfs_btree_rec *rec,
void *priv)
{
struct xfs_rmap_query_range_info *query = priv;
struct xfs_rmap_irec irec;
xfs_failaddr_t fa;
fa = xfs_rmap_btrec_to_irec(rec, &irec);
if (!fa)
fa = xfs_rmap_check_btrec(cur, &irec);
if (fa)
return xfs_rmap_complain_bad_rec(cur, fa, &irec);
return query->fn(cur, &irec, query->priv);
}
/* Find all rmaps between two keys. */
int
xfs_rmap_query_range(
struct xfs_btree_cur *cur,
const struct xfs_rmap_irec *low_rec,
const struct xfs_rmap_irec *high_rec,
xfs_rmap_query_range_fn fn,
void *priv)
{
union xfs_btree_irec low_brec = { .r = *low_rec };
union xfs_btree_irec high_brec = { .r = *high_rec };
struct xfs_rmap_query_range_info query = { .priv = priv, .fn = fn };
return xfs_btree_query_range(cur, &low_brec, &high_brec,
xfs_rmap_query_range_helper, &query);
}
/* Find all rmaps. */
int
xfs_rmap_query_all(
struct xfs_btree_cur *cur,
xfs_rmap_query_range_fn fn,
void *priv)
{
struct xfs_rmap_query_range_info query;
query.priv = priv;
query.fn = fn;
return xfs_btree_query_all(cur, xfs_rmap_query_range_helper, &query);
}
/* Commit an rmap operation into the ondisk tree. */
int
__xfs_rmap_finish_intent(
struct xfs_btree_cur *rcur,
enum xfs_rmap_intent_type op,
xfs_agblock_t bno,
xfs_extlen_t len,
const struct xfs_owner_info *oinfo,
bool unwritten)
{
switch (op) {
case XFS_RMAP_ALLOC:
case XFS_RMAP_MAP:
return xfs_rmap_map(rcur, bno, len, unwritten, oinfo);
case XFS_RMAP_MAP_SHARED:
return xfs_rmap_map_shared(rcur, bno, len, unwritten, oinfo);
case XFS_RMAP_FREE:
case XFS_RMAP_UNMAP:
return xfs_rmap_unmap(rcur, bno, len, unwritten, oinfo);
case XFS_RMAP_UNMAP_SHARED:
return xfs_rmap_unmap_shared(rcur, bno, len, unwritten, oinfo);
case XFS_RMAP_CONVERT:
return xfs_rmap_convert(rcur, bno, len, !unwritten, oinfo);
case XFS_RMAP_CONVERT_SHARED:
return xfs_rmap_convert_shared(rcur, bno, len, !unwritten,
oinfo);
default :
ASSERT(0 );
return -EFSCORRUPTED;
}
}
static int
xfs_rmap_finish_init_cursor(
struct xfs_trans *tp,
struct xfs_rmap_intent *ri,
struct xfs_btree_cur **pcur)
{
struct xfs_perag *pag = to_perag(ri->ri_group);
struct xfs_buf *agbp = NULL;
int error;
/*
* Refresh the freelist before we start changing the rmapbt , because a
* shape change could cause us to allocate blocks .
*/
error = xfs_free_extent_fix_freelist(tp, pag, &agbp);
if (error) {
xfs_ag_mark_sick(pag, XFS_SICK_AG_AGFL);
return error;
}
if (XFS_IS_CORRUPT(tp->t_mountp, !agbp)) {
xfs_ag_mark_sick(pag, XFS_SICK_AG_AGFL);
return -EFSCORRUPTED;
}
*pcur = xfs_rmapbt_init_cursor(tp->t_mountp, tp, agbp, pag);
return 0 ;
}
static int
xfs_rtrmap_finish_init_cursor(
struct xfs_trans *tp,
struct xfs_rmap_intent *ri,
struct xfs_btree_cur **pcur)
{
struct xfs_rtgroup *rtg = to_rtg(ri->ri_group);
xfs_rtgroup_lock(rtg, XFS_RTGLOCK_RMAP);
xfs_rtgroup_trans_join(tp, rtg, XFS_RTGLOCK_RMAP);
*pcur = xfs_rtrmapbt_init_cursor(tp, rtg);
return 0 ;
}
/*
* Process one of the deferred rmap operations . We pass back the
* btree cursor to maintain our lock on the rmapbt between calls .
* This saves time and eliminates a buffer deadlock between the
* superblock and the AGF because we ' ll always grab them in the same
* order .
*/
int
xfs_rmap_finish_one(
struct xfs_trans *tp,
struct xfs_rmap_intent *ri,
struct xfs_btree_cur **pcur)
{
struct xfs_owner_info oinfo;
struct xfs_mount *mp = tp->t_mountp;
xfs_agblock_t bno;
bool unwritten;
int error = 0 ;
trace_xfs_rmap_deferred(mp, ri);
if (XFS_TEST_ERROR(false , mp, XFS_ERRTAG_RMAP_FINISH_ONE))
return -EIO;
/*
* If we haven ' t gotten a cursor or the cursor AG doesn ' t match
* the startblock , get one now .
*/
if (*pcur != NULL && (*pcur)->bc_group != ri->ri_group) {
xfs_btree_del_cursor(*pcur, 0 );
*pcur = NULL;
}
if (*pcur == NULL) {
if (ri->ri_group->xg_type == XG_TYPE_RTG)
error = xfs_rtrmap_finish_init_cursor(tp, ri, pcur);
else
error = xfs_rmap_finish_init_cursor(tp, ri, pcur);
if (error)
return error;
}
xfs_rmap_ino_owner(&oinfo, ri->ri_owner, ri->ri_whichfork,
ri->ri_bmap.br_startoff);
unwritten = ri->ri_bmap.br_state == XFS_EXT_UNWRITTEN;
bno = xfs_fsb_to_gbno(mp, ri->ri_bmap.br_startblock,
ri->ri_group->xg_type);
error = __xfs_rmap_finish_intent(*pcur, ri->ri_type, bno,
ri->ri_bmap.br_blockcount, &oinfo, unwritten);
if (error)
return error;
xfs_rmap_update_hook(tp, ri->ri_group, ri->ri_type, bno,
ri->ri_bmap.br_blockcount, unwritten, &oinfo);
return 0 ;
}
/*
* Don ' t defer an rmap if we aren ' t an rmap filesystem .
*/
static bool
xfs_rmap_update_is_needed(
struct xfs_mount *mp,
int whichfork)
{
return xfs_has_rmapbt(mp) && whichfork != XFS_COW_FORK;
}
/*
* Record a rmap intent ; the list is kept sorted first by AG and then by
* increasing age .
*/
static void
__xfs_rmap_add(
struct xfs_trans *tp,
enum xfs_rmap_intent_type type,
uint64_t owner,
bool isrt,
int whichfork,
struct xfs_bmbt_irec *bmap)
{
struct xfs_rmap_intent *ri;
ri = kmem_cache_alloc(xfs_rmap_intent_cache, GFP_KERNEL | __GFP_NOFAIL);
INIT_LIST_HEAD(&ri->ri_list);
ri->ri_type = type;
ri->ri_owner = owner;
ri->ri_whichfork = whichfork;
ri->ri_bmap = *bmap;
ri->ri_realtime = isrt;
xfs_rmap_defer_add(tp, ri);
}
/* Map an extent into a file. */
void
xfs_rmap_map_extent(
struct xfs_trans *tp,
struct xfs_inode *ip,
int whichfork,
struct xfs_bmbt_irec *PREV)
{
enum xfs_rmap_intent_type type = XFS_RMAP_MAP;
bool isrt = xfs_ifork_is_realtime(ip, whichfork);
if (!xfs_rmap_update_is_needed(tp->t_mountp, whichfork))
return ;
if (whichfork != XFS_ATTR_FORK && xfs_is_reflink_inode(ip))
type = XFS_RMAP_MAP_SHARED;
__xfs_rmap_add(tp, type, ip->i_ino, isrt, whichfork, PREV);
}
/* Unmap an extent out of a file. */
void
xfs_rmap_unmap_extent(
struct xfs_trans *tp,
struct xfs_inode *ip,
int whichfork,
struct xfs_bmbt_irec *PREV)
{
enum xfs_rmap_intent_type type = XFS_RMAP_UNMAP;
bool isrt = xfs_ifork_is_realtime(ip, whichfork);
if (!xfs_rmap_update_is_needed(tp->t_mountp, whichfork))
return ;
if (whichfork != XFS_ATTR_FORK && xfs_is_reflink_inode(ip))
type = XFS_RMAP_UNMAP_SHARED;
__xfs_rmap_add(tp, type, ip->i_ino, isrt, whichfork, PREV);
}
/*
* Convert a data fork extent from unwritten to real or vice versa .
*
* Note that tp can be NULL here as no transaction is used for COW fork
* unwritten conversion .
*/
void
xfs_rmap_convert_extent(
struct xfs_mount *mp,
struct xfs_trans *tp,
struct xfs_inode *ip,
int whichfork,
struct xfs_bmbt_irec *PREV)
{
enum xfs_rmap_intent_type type = XFS_RMAP_CONVERT;
bool isrt = xfs_ifork_is_realtime(ip, whichfork);
if (!xfs_rmap_update_is_needed(mp, whichfork))
return ;
if (whichfork != XFS_ATTR_FORK && xfs_is_reflink_inode(ip))
type = XFS_RMAP_CONVERT_SHARED;
__xfs_rmap_add(tp, type, ip->i_ino, isrt, whichfork, PREV);
}
/* Schedule the creation of an rmap for non-file data. */
void
xfs_rmap_alloc_extent(
struct xfs_trans *tp,
bool isrt,
xfs_fsblock_t fsbno,
xfs_extlen_t len,
uint64_t owner)
{
struct xfs_bmbt_irec bmap;
if (!xfs_rmap_update_is_needed(tp->t_mountp, XFS_DATA_FORK))
return ;
bmap.br_startblock = fsbno;
bmap.br_blockcount = len;
bmap.br_startoff = 0 ;
bmap.br_state = XFS_EXT_NORM;
__xfs_rmap_add(tp, XFS_RMAP_ALLOC, owner, isrt, XFS_DATA_FORK, &bmap);
}
/* Schedule the deletion of an rmap for non-file data. */
void
xfs_rmap_free_extent(
struct xfs_trans *tp,
bool isrt,
xfs_fsblock_t fsbno,
xfs_extlen_t len,
uint64_t owner)
{
struct xfs_bmbt_irec bmap;
if (!xfs_rmap_update_is_needed(tp->t_mountp, XFS_DATA_FORK))
return ;
bmap.br_startblock = fsbno;
bmap.br_blockcount = len;
bmap.br_startoff = 0 ;
bmap.br_state = XFS_EXT_NORM;
__xfs_rmap_add(tp, XFS_RMAP_FREE, owner, isrt, XFS_DATA_FORK, &bmap);
}
/* Compare rmap records. Returns -1 if a < b, 1 if a > b, and 0 if equal. */
int
xfs_rmap_compare(
const struct xfs_rmap_irec *a,
const struct xfs_rmap_irec *b)
{
__u64 oa;
__u64 ob;
oa = xfs_rmap_irec_offset_pack(a);
ob = xfs_rmap_irec_offset_pack(b);
if (a->rm_startblock < b->rm_startblock)
return -1 ;
else if (a->rm_startblock > b->rm_startblock)
return 1 ;
else if (a->rm_owner < b->rm_owner)
return -1 ;
else if (a->rm_owner > b->rm_owner)
return 1 ;
else if (oa < ob)
return -1 ;
else if (oa > ob)
return 1 ;
else
return 0 ;
}
/*
* Scan the physical storage part of the keyspace of the reverse mapping index
* and tell us if the area has no records , is fully mapped by records , or is
* partially filled .
*/
int
xfs_rmap_has_records(
struct xfs_btree_cur *cur,
xfs_agblock_t bno,
xfs_extlen_t len,
enum xbtree_recpacking *outcome)
{
union xfs_btree_key mask = {
.rmap.rm_startblock = cpu_to_be32(-1 U),
};
union xfs_btree_irec low;
union xfs_btree_irec high;
memset(&low, 0 , sizeof (low));
low.r.rm_startblock = bno;
memset(&high, 0 xFF, sizeof (high));
high.r.rm_startblock = bno + len - 1 ;
return xfs_btree_has_records(cur, &low, &high, &mask, outcome);
}
struct xfs_rmap_ownercount {
/* Owner that we're looking for. */
struct xfs_rmap_irec good;
/* rmap search keys */
struct xfs_rmap_irec low;
struct xfs_rmap_irec high;
struct xfs_rmap_matches *results;
/* Stop early if we find a nonmatch? */
bool stop_on_nonmatch;
};
/* Does this rmap represent space that can have multiple owners? */
static inline bool
xfs_rmap_shareable(
struct xfs_mount *mp,
const struct xfs_rmap_irec *rmap)
{
if (!xfs_has_reflink(mp))
return false ;
if (XFS_RMAP_NON_INODE_OWNER(rmap->rm_owner))
return false ;
if (rmap->rm_flags & (XFS_RMAP_ATTR_FORK |
XFS_RMAP_BMBT_BLOCK))
return false ;
return true ;
}
static inline void
xfs_rmap_ownercount_init(
struct xfs_rmap_ownercount *roc,
xfs_agblock_t bno,
xfs_extlen_t len,
const struct xfs_owner_info *oinfo,
struct xfs_rmap_matches *results)
{
memset(roc, 0 , sizeof (*roc));
roc->results = results;
roc->low.rm_startblock = bno;
memset(&roc->high, 0 xFF, sizeof (roc->high));
roc->high.rm_startblock = bno + len - 1 ;
memset(results, 0 , sizeof (*results));
roc->good.rm_startblock = bno;
roc->good.rm_blockcount = len;
roc->good.rm_owner = oinfo->oi_owner;
roc->good.rm_offset = oinfo->oi_offset;
if (oinfo->oi_flags & XFS_OWNER_INFO_ATTR_FORK)
roc->good.rm_flags |= XFS_RMAP_ATTR_FORK;
if (oinfo->oi_flags & XFS_OWNER_INFO_BMBT_BLOCK)
roc->good.rm_flags |= XFS_RMAP_BMBT_BLOCK;
}
/* Figure out if this is a match for the owner. */
STATIC int
xfs_rmap_count_owners_helper(
struct xfs_btree_cur *cur,
const struct xfs_rmap_irec *rec,
void *priv)
{
struct xfs_rmap_ownercount *roc = priv;
struct xfs_rmap_irec check = *rec;
unsigned int keyflags;
bool filedata;
int64_t delta;
filedata = !XFS_RMAP_NON_INODE_OWNER(check.rm_owner) &&
!(check.rm_flags & XFS_RMAP_BMBT_BLOCK);
/* Trim the part of check that comes before the comparison range. */
delta = (int64_t)roc->good.rm_startblock - check.rm_startblock;
if (delta > 0 ) {
check.rm_startblock += delta;
check.rm_blockcount -= delta;
if (filedata)
check.rm_offset += delta;
}
/* Trim the part of check that comes after the comparison range. */
delta = (check.rm_startblock + check.rm_blockcount) -
(roc->good.rm_startblock + roc->good.rm_blockcount);
if (delta > 0 )
check.rm_blockcount -= delta;
/* Don't care about unwritten status for establishing ownership. */
keyflags = check.rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK);
if (check.rm_startblock == roc->good.rm_startblock &&
check.rm_blockcount == roc->good.rm_blockcount &&
check.rm_owner == roc->good.rm_owner &&
check.rm_offset == roc->good.rm_offset &&
keyflags == roc->good.rm_flags) {
roc->results->matches++;
} else {
roc->results->non_owner_matches++;
if (xfs_rmap_shareable(cur->bc_mp, &roc->good) ^
xfs_rmap_shareable(cur->bc_mp, &check))
roc->results->bad_non_owner_matches++;
}
if (roc->results->non_owner_matches && roc->stop_on_nonmatch)
return -ECANCELED;
return 0 ;
}
/* Count the number of owners and non-owners of this range of blocks. */
int
xfs_rmap_count_owners(
struct xfs_btree_cur *cur,
xfs_agblock_t bno,
xfs_extlen_t len,
const struct xfs_owner_info *oinfo,
struct xfs_rmap_matches *results)
{
struct xfs_rmap_ownercount roc;
int error;
xfs_rmap_ownercount_init(&roc, bno, len, oinfo, results);
error = xfs_rmap_query_range(cur, &roc.low, &roc.high,
xfs_rmap_count_owners_helper, &roc);
if (error)
return error;
/*
* There can ' t be any non - owner rmaps that conflict with the given
* owner if we didn ' t find any rmaps matching the owner .
*/
if (!results->matches)
results->bad_non_owner_matches = 0 ;
return 0 ;
}
/*
* Given an extent and some owner info , can we find records overlapping
* the extent whose owner info does not match the given owner ?
*/
int
xfs_rmap_has_other_keys(
struct xfs_btree_cur *cur,
xfs_agblock_t bno,
xfs_extlen_t len,
const struct xfs_owner_info *oinfo,
bool *has_other)
{
struct xfs_rmap_matches res;
struct xfs_rmap_ownercount roc;
int error;
xfs_rmap_ownercount_init(&roc, bno, len, oinfo, &res);
roc.stop_on_nonmatch = true ;
error = xfs_rmap_query_range(cur, &roc.low, &roc.high,
xfs_rmap_count_owners_helper, &roc);
if (error == -ECANCELED) {
*has_other = true ;
return 0 ;
}
if (error)
return error;
*has_other = false ;
return 0 ;
}
const struct xfs_owner_info XFS_RMAP_OINFO_SKIP_UPDATE = {
.oi_owner = XFS_RMAP_OWN_NULL,
};
const struct xfs_owner_info XFS_RMAP_OINFO_ANY_OWNER = {
.oi_owner = XFS_RMAP_OWN_UNKNOWN,
};
const struct xfs_owner_info XFS_RMAP_OINFO_FS = {
.oi_owner = XFS_RMAP_OWN_FS,
};
const struct xfs_owner_info XFS_RMAP_OINFO_LOG = {
.oi_owner = XFS_RMAP_OWN_LOG,
};
const struct xfs_owner_info XFS_RMAP_OINFO_AG = {
.oi_owner = XFS_RMAP_OWN_AG,
};
const struct xfs_owner_info XFS_RMAP_OINFO_INOBT = {
.oi_owner = XFS_RMAP_OWN_INOBT,
};
const struct xfs_owner_info XFS_RMAP_OINFO_INODES = {
.oi_owner = XFS_RMAP_OWN_INODES,
};
const struct xfs_owner_info XFS_RMAP_OINFO_REFC = {
.oi_owner = XFS_RMAP_OWN_REFC,
};
const struct xfs_owner_info XFS_RMAP_OINFO_COW = {
.oi_owner = XFS_RMAP_OWN_COW,
};
int __init
xfs_rmap_intent_init_cache(void )
{
xfs_rmap_intent_cache = kmem_cache_create("xfs_rmap_intent" ,
sizeof (struct xfs_rmap_intent),
0 , 0 , NULL);
return xfs_rmap_intent_cache != NULL ? 0 : -ENOMEM;
}
void
xfs_rmap_intent_destroy_cache(void )
{
kmem_cache_destroy(xfs_rmap_intent_cache);
xfs_rmap_intent_cache = NULL;
}
Messung V0.5 in Prozent C=96 H=81 G=88
¤ 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.0.112Bemerkung:
(vorverarbeitet am 2026-09-29)
¤
*Bot Zugriff