blob: 498ad50d1f452807eba88e042f55341ea4498c3d [file] [log] [blame]
/*
* Copyright (c) 2000-2006 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_types.h"
#include "xfs_bit.h"
#include "xfs_log.h"
#include "xfs_inum.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
#include "xfs_dir2.h"
#include "xfs_da_btree.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include "xfs_inode.h"
#include "xfs_btree.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_ialloc.h"
#include "xfs_itable.h"
#include "xfs_dir2_data.h"
#include "xfs_dir2_leaf.h"
#include "xfs_dir2_block.h"
#include "xfs_inode_item.h"
#include "xfs_extfree_item.h"
#include "xfs_alloc.h"
#include "xfs_bmap.h"
#include "xfs_rtalloc.h"
#include "xfs_error.h"
#include "xfs_attr_leaf.h"
#include "xfs_rw.h"
#include "xfs_quota.h"
#include "xfs_trans_space.h"
#include "xfs_buf_item.h"
#ifdef DEBUG
STATIC void
xfs_bmap_check_leaf_extents(xfs_btree_cur_t *cur, xfs_inode_t *ip, int whichfork);
#endif
kmem_zone_t *xfs_bmap_free_item_zone;
/*
* Prototypes for internal bmap routines.
*/
/*
* Called from xfs_bmap_add_attrfork to handle extents format files.
*/
STATIC int /* error */
xfs_bmap_add_attrfork_extents(
xfs_trans_t *tp, /* transaction pointer */
xfs_inode_t *ip, /* incore inode pointer */
xfs_fsblock_t *firstblock, /* first block allocated */
xfs_bmap_free_t *flist, /* blocks to free at commit */
int *flags); /* inode logging flags */
/*
* Called from xfs_bmap_add_attrfork to handle local format files.
*/
STATIC int /* error */
xfs_bmap_add_attrfork_local(
xfs_trans_t *tp, /* transaction pointer */
xfs_inode_t *ip, /* incore inode pointer */
xfs_fsblock_t *firstblock, /* first block allocated */
xfs_bmap_free_t *flist, /* blocks to free at commit */
int *flags); /* inode logging flags */
/*
* Called by xfs_bmapi to update file extent records and the btree
* after allocating space (or doing a delayed allocation).
*/
STATIC int /* error */
xfs_bmap_add_extent(
xfs_inode_t *ip, /* incore inode pointer */
xfs_extnum_t idx, /* extent number to update/insert */
xfs_btree_cur_t **curp, /* if *curp is null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
xfs_fsblock_t *first, /* pointer to firstblock variable */
xfs_bmap_free_t *flist, /* list of extents to be freed */
int *logflagsp, /* inode logging flags */
xfs_extdelta_t *delta, /* Change made to incore extents */
int whichfork, /* data or attr fork */
int rsvd); /* OK to allocate reserved blocks */
/*
* Called by xfs_bmap_add_extent to handle cases converting a delayed
* allocation to a real allocation.
*/
STATIC int /* error */
xfs_bmap_add_extent_delay_real(
xfs_inode_t *ip, /* incore inode pointer */
xfs_extnum_t idx, /* extent number to update/insert */
xfs_btree_cur_t **curp, /* if *curp is null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
xfs_filblks_t *dnew, /* new delayed-alloc indirect blocks */
xfs_fsblock_t *first, /* pointer to firstblock variable */
xfs_bmap_free_t *flist, /* list of extents to be freed */
int *logflagsp, /* inode logging flags */
xfs_extdelta_t *delta, /* Change made to incore extents */
int rsvd); /* OK to allocate reserved blocks */
/*
* Called by xfs_bmap_add_extent to handle cases converting a hole
* to a delayed allocation.
*/
STATIC int /* error */
xfs_bmap_add_extent_hole_delay(
xfs_inode_t *ip, /* incore inode pointer */
xfs_extnum_t idx, /* extent number to update/insert */
xfs_btree_cur_t *cur, /* if null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
int *logflagsp,/* inode logging flags */
xfs_extdelta_t *delta, /* Change made to incore extents */
int rsvd); /* OK to allocate reserved blocks */
/*
* Called by xfs_bmap_add_extent to handle cases converting a hole
* to a real allocation.
*/
STATIC int /* error */
xfs_bmap_add_extent_hole_real(
xfs_inode_t *ip, /* incore inode pointer */
xfs_extnum_t idx, /* extent number to update/insert */
xfs_btree_cur_t *cur, /* if null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
int *logflagsp, /* inode logging flags */
xfs_extdelta_t *delta, /* Change made to incore extents */
int whichfork); /* data or attr fork */
/*
* Called by xfs_bmap_add_extent to handle cases converting an unwritten
* allocation to a real allocation or vice versa.
*/
STATIC int /* error */
xfs_bmap_add_extent_unwritten_real(
xfs_inode_t *ip, /* incore inode pointer */
xfs_extnum_t idx, /* extent number to update/insert */
xfs_btree_cur_t **curp, /* if *curp is null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
int *logflagsp, /* inode logging flags */
xfs_extdelta_t *delta); /* Change made to incore extents */
/*
* xfs_bmap_alloc is called by xfs_bmapi to allocate an extent for a file.
* It figures out where to ask the underlying allocator to put the new extent.
*/
STATIC int /* error */
xfs_bmap_alloc(
xfs_bmalloca_t *ap); /* bmap alloc argument struct */
/*
* Transform a btree format file with only one leaf node, where the
* extents list will fit in the inode, into an extents format file.
* Since the file extents are already in-core, all we have to do is
* give up the space for the btree root and pitch the leaf block.
*/
STATIC int /* error */
xfs_bmap_btree_to_extents(
xfs_trans_t *tp, /* transaction pointer */
xfs_inode_t *ip, /* incore inode pointer */
xfs_btree_cur_t *cur, /* btree cursor */
int *logflagsp, /* inode logging flags */
int whichfork); /* data or attr fork */
#ifdef DEBUG
/*
* Check that the extents list for the inode ip is in the right order.
*/
STATIC void
xfs_bmap_check_extents(
xfs_inode_t *ip, /* incore inode pointer */
int whichfork); /* data or attr fork */
#endif
/*
* Called by xfs_bmapi to update file extent records and the btree
* after removing space (or undoing a delayed allocation).
*/
STATIC int /* error */
xfs_bmap_del_extent(
xfs_inode_t *ip, /* incore inode pointer */
xfs_trans_t *tp, /* current trans pointer */
xfs_extnum_t idx, /* extent number to update/insert */
xfs_bmap_free_t *flist, /* list of extents to be freed */
xfs_btree_cur_t *cur, /* if null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
int *logflagsp,/* inode logging flags */
xfs_extdelta_t *delta, /* Change made to incore extents */
int whichfork, /* data or attr fork */
int rsvd); /* OK to allocate reserved blocks */
/*
* Remove the entry "free" from the free item list. Prev points to the
* previous entry, unless "free" is the head of the list.
*/
STATIC void
xfs_bmap_del_free(
xfs_bmap_free_t *flist, /* free item list header */
xfs_bmap_free_item_t *prev, /* previous item on list, if any */
xfs_bmap_free_item_t *free); /* list item to be freed */
/*
* Convert an extents-format file into a btree-format file.
* The new file will have a root block (in the inode) and a single child block.
*/
STATIC int /* error */
xfs_bmap_extents_to_btree(
xfs_trans_t *tp, /* transaction pointer */
xfs_inode_t *ip, /* incore inode pointer */
xfs_fsblock_t *firstblock, /* first-block-allocated */
xfs_bmap_free_t *flist, /* blocks freed in xaction */
xfs_btree_cur_t **curp, /* cursor returned to caller */
int wasdel, /* converting a delayed alloc */
int *logflagsp, /* inode logging flags */
int whichfork); /* data or attr fork */
/*
* Convert a local file to an extents file.
* This code is sort of bogus, since the file data needs to get
* logged so it won't be lost. The bmap-level manipulations are ok, though.
*/
STATIC int /* error */
xfs_bmap_local_to_extents(
xfs_trans_t *tp, /* transaction pointer */
xfs_inode_t *ip, /* incore inode pointer */
xfs_fsblock_t *firstblock, /* first block allocated in xaction */
xfs_extlen_t total, /* total blocks needed by transaction */
int *logflagsp, /* inode logging flags */
int whichfork); /* data or attr fork */
/*
* Search the extents list for the inode, for the extent containing bno.
* If bno lies in a hole, point to the next entry. If bno lies past eof,
* *eofp will be set, and *prevp will contain the last entry (null if none).
* Else, *lastxp will be set to the index of the found
* entry; *gotp will contain the entry.
*/
STATIC xfs_bmbt_rec_t * /* pointer to found extent entry */
xfs_bmap_search_extents(
xfs_inode_t *ip, /* incore inode pointer */
xfs_fileoff_t bno, /* block number searched for */
int whichfork, /* data or attr fork */
int *eofp, /* out: end of file found */
xfs_extnum_t *lastxp, /* out: last extent index */
xfs_bmbt_irec_t *gotp, /* out: extent entry found */
xfs_bmbt_irec_t *prevp); /* out: previous extent entry found */
/*
* Check the last inode extent to determine whether this allocation will result
* in blocks being allocated at the end of the file. When we allocate new data
* blocks at the end of the file which do not start at the previous data block,
* we will try to align the new blocks at stripe unit boundaries.
*/
STATIC int /* error */
xfs_bmap_isaeof(
xfs_inode_t *ip, /* incore inode pointer */
xfs_fileoff_t off, /* file offset in fsblocks */
int whichfork, /* data or attribute fork */
char *aeof); /* return value */
#ifdef XFS_BMAP_TRACE
/*
* Add a bmap trace buffer entry. Base routine for the others.
*/
STATIC void
xfs_bmap_trace_addentry(
int opcode, /* operation */
char *fname, /* function name */
char *desc, /* operation description */
xfs_inode_t *ip, /* incore inode pointer */
xfs_extnum_t idx, /* index of entry(ies) */
xfs_extnum_t cnt, /* count of entries, 1 or 2 */
xfs_bmbt_rec_t *r1, /* first record */
xfs_bmbt_rec_t *r2, /* second record or null */
int whichfork); /* data or attr fork */
/*
* Add bmap trace entry prior to a call to xfs_iext_remove.
*/
STATIC void
xfs_bmap_trace_delete(
char *fname, /* function name */
char *desc, /* operation description */
xfs_inode_t *ip, /* incore inode pointer */
xfs_extnum_t idx, /* index of entry(entries) deleted */
xfs_extnum_t cnt, /* count of entries deleted, 1 or 2 */
int whichfork); /* data or attr fork */
/*
* Add bmap trace entry prior to a call to xfs_iext_insert, or
* reading in the extents list from the disk (in the btree).
*/
STATIC void
xfs_bmap_trace_insert(
char *fname, /* function name */
char *desc, /* operation description */
xfs_inode_t *ip, /* incore inode pointer */
xfs_extnum_t idx, /* index of entry(entries) inserted */
xfs_extnum_t cnt, /* count of entries inserted, 1 or 2 */
xfs_bmbt_irec_t *r1, /* inserted record 1 */
xfs_bmbt_irec_t *r2, /* inserted record 2 or null */
int whichfork); /* data or attr fork */
/*
* Add bmap trace entry after updating an extent record in place.
*/
STATIC void
xfs_bmap_trace_post_update(
char *fname, /* function name */
char *desc, /* operation description */
xfs_inode_t *ip, /* incore inode pointer */
xfs_extnum_t idx, /* index of entry updated */
int whichfork); /* data or attr fork */
/*
* Add bmap trace entry prior to updating an extent record in place.
*/
STATIC void
xfs_bmap_trace_pre_update(
char *fname, /* function name */
char *desc, /* operation description */
xfs_inode_t *ip, /* incore inode pointer */
xfs_extnum_t idx, /* index of entry to be updated */
int whichfork); /* data or attr fork */
#else
#define xfs_bmap_trace_delete(f,d,ip,i,c,w)
#define xfs_bmap_trace_insert(f,d,ip,i,c,r1,r2,w)
#define xfs_bmap_trace_post_update(f,d,ip,i,w)
#define xfs_bmap_trace_pre_update(f,d,ip,i,w)
#endif /* XFS_BMAP_TRACE */
/*
* Compute the worst-case number of indirect blocks that will be used
* for ip's delayed extent of length "len".
*/
STATIC xfs_filblks_t
xfs_bmap_worst_indlen(
xfs_inode_t *ip, /* incore inode pointer */
xfs_filblks_t len); /* delayed extent length */
#ifdef DEBUG
/*
* Perform various validation checks on the values being returned
* from xfs_bmapi().
*/
STATIC void
xfs_bmap_validate_ret(
xfs_fileoff_t bno,
xfs_filblks_t len,
int flags,
xfs_bmbt_irec_t *mval,
int nmap,
int ret_nmap);
#else
#define xfs_bmap_validate_ret(bno,len,flags,mval,onmap,nmap)
#endif /* DEBUG */
#if defined(XFS_RW_TRACE)
STATIC void
xfs_bunmap_trace(
xfs_inode_t *ip,
xfs_fileoff_t bno,
xfs_filblks_t len,
int flags,
inst_t *ra);
#else
#define xfs_bunmap_trace(ip, bno, len, flags, ra)
#endif /* XFS_RW_TRACE */
STATIC int
xfs_bmap_count_tree(
xfs_mount_t *mp,
xfs_trans_t *tp,
xfs_ifork_t *ifp,
xfs_fsblock_t blockno,
int levelin,
int *count);
STATIC int
xfs_bmap_count_leaves(
xfs_ifork_t *ifp,
xfs_extnum_t idx,
int numrecs,
int *count);
STATIC int
xfs_bmap_disk_count_leaves(
xfs_ifork_t *ifp,
xfs_mount_t *mp,
xfs_extnum_t idx,
xfs_bmbt_block_t *block,
int numrecs,
int *count);
/*
* Bmap internal routines.
*/
/*
* Called from xfs_bmap_add_attrfork to handle btree format files.
*/
STATIC int /* error */
xfs_bmap_add_attrfork_btree(
xfs_trans_t *tp, /* transaction pointer */
xfs_inode_t *ip, /* incore inode pointer */
xfs_fsblock_t *firstblock, /* first block allocated */
xfs_bmap_free_t *flist, /* blocks to free at commit */
int *flags) /* inode logging flags */
{
xfs_btree_cur_t *cur; /* btree cursor */
int error; /* error return value */
xfs_mount_t *mp; /* file system mount struct */
int stat; /* newroot status */
mp = ip->i_mount;
if (ip->i_df.if_broot_bytes <= XFS_IFORK_DSIZE(ip))
*flags |= XFS_ILOG_DBROOT;
else {
cur = xfs_btree_init_cursor(mp, tp, NULL, 0, XFS_BTNUM_BMAP, ip,
XFS_DATA_FORK);
cur->bc_private.b.flist = flist;
cur->bc_private.b.firstblock = *firstblock;
if ((error = xfs_bmbt_lookup_ge(cur, 0, 0, 0, &stat)))
goto error0;
ASSERT(stat == 1); /* must be at least one entry */
if ((error = xfs_bmbt_newroot(cur, flags, &stat)))
goto error0;
if (stat == 0) {
xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
return XFS_ERROR(ENOSPC);
}
*firstblock = cur->bc_private.b.firstblock;
cur->bc_private.b.allocated = 0;
xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
}
return 0;
error0:
xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
return error;
}
/*
* Called from xfs_bmap_add_attrfork to handle extents format files.
*/
STATIC int /* error */
xfs_bmap_add_attrfork_extents(
xfs_trans_t *tp, /* transaction pointer */
xfs_inode_t *ip, /* incore inode pointer */
xfs_fsblock_t *firstblock, /* first block allocated */
xfs_bmap_free_t *flist, /* blocks to free at commit */
int *flags) /* inode logging flags */
{
xfs_btree_cur_t *cur; /* bmap btree cursor */
int error; /* error return value */
if (ip->i_d.di_nextents * sizeof(xfs_bmbt_rec_t) <= XFS_IFORK_DSIZE(ip))
return 0;
cur = NULL;
error = xfs_bmap_extents_to_btree(tp, ip, firstblock, flist, &cur, 0,
flags, XFS_DATA_FORK);
if (cur) {
cur->bc_private.b.allocated = 0;
xfs_btree_del_cursor(cur,
error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
}
return error;
}
/*
* Called from xfs_bmap_add_attrfork to handle local format files.
*/
STATIC int /* error */
xfs_bmap_add_attrfork_local(
xfs_trans_t *tp, /* transaction pointer */
xfs_inode_t *ip, /* incore inode pointer */
xfs_fsblock_t *firstblock, /* first block allocated */
xfs_bmap_free_t *flist, /* blocks to free at commit */
int *flags) /* inode logging flags */
{
xfs_da_args_t dargs; /* args for dir/attr code */
int error; /* error return value */
xfs_mount_t *mp; /* mount structure pointer */
if (ip->i_df.if_bytes <= XFS_IFORK_DSIZE(ip))
return 0;
if ((ip->i_d.di_mode & S_IFMT) == S_IFDIR) {
mp = ip->i_mount;
memset(&dargs, 0, sizeof(dargs));
dargs.dp = ip;
dargs.firstblock = firstblock;
dargs.flist = flist;
dargs.total = mp->m_dirblkfsbs;
dargs.whichfork = XFS_DATA_FORK;
dargs.trans = tp;
error = xfs_dir2_sf_to_block(&dargs);
} else
error = xfs_bmap_local_to_extents(tp, ip, firstblock, 1, flags,
XFS_DATA_FORK);
return error;
}
/*
* Called by xfs_bmapi to update file extent records and the btree
* after allocating space (or doing a delayed allocation).
*/
STATIC int /* error */
xfs_bmap_add_extent(
xfs_inode_t *ip, /* incore inode pointer */
xfs_extnum_t idx, /* extent number to update/insert */
xfs_btree_cur_t **curp, /* if *curp is null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
xfs_fsblock_t *first, /* pointer to firstblock variable */
xfs_bmap_free_t *flist, /* list of extents to be freed */
int *logflagsp, /* inode logging flags */
xfs_extdelta_t *delta, /* Change made to incore extents */
int whichfork, /* data or attr fork */
int rsvd) /* OK to use reserved data blocks */
{
xfs_btree_cur_t *cur; /* btree cursor or null */
xfs_filblks_t da_new; /* new count del alloc blocks used */
xfs_filblks_t da_old; /* old count del alloc blocks used */
int error; /* error return value */
#ifdef XFS_BMAP_TRACE
static char fname[] = "xfs_bmap_add_extent";
#endif
xfs_ifork_t *ifp; /* inode fork ptr */
int logflags; /* returned value */
xfs_extnum_t nextents; /* number of extents in file now */
XFS_STATS_INC(xs_add_exlist);
cur = *curp;
ifp = XFS_IFORK_PTR(ip, whichfork);
nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
ASSERT(idx <= nextents);
da_old = da_new = 0;
error = 0;
/*
* This is the first extent added to a new/empty file.
* Special case this one, so other routines get to assume there are
* already extents in the list.
*/
if (nextents == 0) {
xfs_bmap_trace_insert(fname, "insert empty", ip, 0, 1, new,
NULL, whichfork);
xfs_iext_insert(ifp, 0, 1, new);
ASSERT(cur == NULL);
ifp->if_lastex = 0;
if (!ISNULLSTARTBLOCK(new->br_startblock)) {
XFS_IFORK_NEXT_SET(ip, whichfork, 1);
logflags = XFS_ILOG_CORE | XFS_ILOG_FEXT(whichfork);
} else
logflags = 0;
/* DELTA: single new extent */
if (delta) {
if (delta->xed_startoff > new->br_startoff)
delta->xed_startoff = new->br_startoff;
if (delta->xed_blockcount <
new->br_startoff + new->br_blockcount)
delta->xed_blockcount = new->br_startoff +
new->br_blockcount;
}
}
/*
* Any kind of new delayed allocation goes here.
*/
else if (ISNULLSTARTBLOCK(new->br_startblock)) {
if (cur)
ASSERT((cur->bc_private.b.flags &
XFS_BTCUR_BPRV_WASDEL) == 0);
if ((error = xfs_bmap_add_extent_hole_delay(ip, idx, cur, new,
&logflags, delta, rsvd)))
goto done;
}
/*
* Real allocation off the end of the file.
*/
else if (idx == nextents) {
if (cur)
ASSERT((cur->bc_private.b.flags &
XFS_BTCUR_BPRV_WASDEL) == 0);
if ((error = xfs_bmap_add_extent_hole_real(ip, idx, cur, new,
&logflags, delta, whichfork)))
goto done;
} else {
xfs_bmbt_irec_t prev; /* old extent at offset idx */
/*
* Get the record referred to by idx.
*/
xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx), &prev);
/*
* If it's a real allocation record, and the new allocation ends
* after the start of the referred to record, then we're filling
* in a delayed or unwritten allocation with a real one, or
* converting real back to unwritten.
*/
if (!ISNULLSTARTBLOCK(new->br_startblock) &&
new->br_startoff + new->br_blockcount > prev.br_startoff) {
if (prev.br_state != XFS_EXT_UNWRITTEN &&
ISNULLSTARTBLOCK(prev.br_startblock)) {
da_old = STARTBLOCKVAL(prev.br_startblock);
if (cur)
ASSERT(cur->bc_private.b.flags &
XFS_BTCUR_BPRV_WASDEL);
if ((error = xfs_bmap_add_extent_delay_real(ip,
idx, &cur, new, &da_new, first, flist,
&logflags, delta, rsvd)))
goto done;
} else if (new->br_state == XFS_EXT_NORM) {
ASSERT(new->br_state == XFS_EXT_NORM);
if ((error = xfs_bmap_add_extent_unwritten_real(
ip, idx, &cur, new, &logflags, delta)))
goto done;
} else {
ASSERT(new->br_state == XFS_EXT_UNWRITTEN);
if ((error = xfs_bmap_add_extent_unwritten_real(
ip, idx, &cur, new, &logflags, delta)))
goto done;
}
ASSERT(*curp == cur || *curp == NULL);
}
/*
* Otherwise we're filling in a hole with an allocation.
*/
else {
if (cur)
ASSERT((cur->bc_private.b.flags &
XFS_BTCUR_BPRV_WASDEL) == 0);
if ((error = xfs_bmap_add_extent_hole_real(ip, idx, cur,
new, &logflags, delta, whichfork)))
goto done;
}
}
ASSERT(*curp == cur || *curp == NULL);
/*
* Convert to a btree if necessary.
*/
if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS &&
XFS_IFORK_NEXTENTS(ip, whichfork) > ifp->if_ext_max) {
int tmp_logflags; /* partial log flag return val */
ASSERT(cur == NULL);
error = xfs_bmap_extents_to_btree(ip->i_transp, ip, first,
flist, &cur, da_old > 0, &tmp_logflags, whichfork);
logflags |= tmp_logflags;
if (error)
goto done;
}
/*
* Adjust for changes in reserved delayed indirect blocks.
* Nothing to do for disk quotas here.
*/
if (da_old || da_new) {
xfs_filblks_t nblks;
nblks = da_new;
if (cur)
nblks += cur->bc_private.b.allocated;
ASSERT(nblks <= da_old);
if (nblks < da_old)
xfs_mod_incore_sb(ip->i_mount, XFS_SBS_FDBLOCKS,
(int)(da_old - nblks), rsvd);
}
/*
* Clear out the allocated field, done with it now in any case.
*/
if (cur) {
cur->bc_private.b.allocated = 0;
*curp = cur;
}
done:
#ifdef DEBUG
if (!error)
xfs_bmap_check_leaf_extents(*curp, ip, whichfork);
#endif
*logflagsp = logflags;
return error;
}
/*
* Called by xfs_bmap_add_extent to handle cases converting a delayed
* allocation to a real allocation.
*/
STATIC int /* error */
xfs_bmap_add_extent_delay_real(
xfs_inode_t *ip, /* incore inode pointer */
xfs_extnum_t idx, /* extent number to update/insert */
xfs_btree_cur_t **curp, /* if *curp is null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
xfs_filblks_t *dnew, /* new delayed-alloc indirect blocks */
xfs_fsblock_t *first, /* pointer to firstblock variable */
xfs_bmap_free_t *flist, /* list of extents to be freed */
int *logflagsp, /* inode logging flags */
xfs_extdelta_t *delta, /* Change made to incore extents */
int rsvd) /* OK to use reserved data block allocation */
{
xfs_btree_cur_t *cur; /* btree cursor */
int diff; /* temp value */
xfs_bmbt_rec_t *ep; /* extent entry for idx */
int error; /* error return value */
#ifdef XFS_BMAP_TRACE
static char fname[] = "xfs_bmap_add_extent_delay_real";
#endif
int i; /* temp state */
xfs_ifork_t *ifp; /* inode fork pointer */
xfs_fileoff_t new_endoff; /* end offset of new entry */
xfs_bmbt_irec_t r[3]; /* neighbor extent entries */
/* left is 0, right is 1, prev is 2 */
int rval=0; /* return value (logging flags) */
int state = 0;/* state bits, accessed thru macros */
xfs_filblks_t temp=0; /* value for dnew calculations */
xfs_filblks_t temp2=0;/* value for dnew calculations */
int tmp_rval; /* partial logging flags */
enum { /* bit number definitions for state */
LEFT_CONTIG, RIGHT_CONTIG,
LEFT_FILLING, RIGHT_FILLING,
LEFT_DELAY, RIGHT_DELAY,
LEFT_VALID, RIGHT_VALID
};
#define LEFT r[0]
#define RIGHT r[1]
#define PREV r[2]
#define MASK(b) (1 << (b))
#define MASK2(a,b) (MASK(a) | MASK(b))
#define MASK3(a,b,c) (MASK2(a,b) | MASK(c))
#define MASK4(a,b,c,d) (MASK3(a,b,c) | MASK(d))
#define STATE_SET(b,v) ((v) ? (state |= MASK(b)) : (state &= ~MASK(b)))
#define STATE_TEST(b) (state & MASK(b))
#define STATE_SET_TEST(b,v) ((v) ? ((state |= MASK(b)), 1) : \
((state &= ~MASK(b)), 0))
#define SWITCH_STATE \
(state & MASK4(LEFT_FILLING, RIGHT_FILLING, LEFT_CONTIG, RIGHT_CONTIG))
/*
* Set up a bunch of variables to make the tests simpler.
*/
cur = *curp;
ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
ep = xfs_iext_get_ext(ifp, idx);
xfs_bmbt_get_all(ep, &PREV);
new_endoff = new->br_startoff + new->br_blockcount;
ASSERT(PREV.br_startoff <= new->br_startoff);
ASSERT(PREV.br_startoff + PREV.br_blockcount >= new_endoff);
/*
* Set flags determining what part of the previous delayed allocation
* extent is being replaced by a real allocation.
*/
STATE_SET(LEFT_FILLING, PREV.br_startoff == new->br_startoff);
STATE_SET(RIGHT_FILLING,
PREV.br_startoff + PREV.br_blockcount == new_endoff);
/*
* Check and set flags if this segment has a left neighbor.
* Don't set contiguous if the combined extent would be too large.
*/
if (STATE_SET_TEST(LEFT_VALID, idx > 0)) {
xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx - 1), &LEFT);
STATE_SET(LEFT_DELAY, ISNULLSTARTBLOCK(LEFT.br_startblock));
}
STATE_SET(LEFT_CONTIG,
STATE_TEST(LEFT_VALID) && !STATE_TEST(LEFT_DELAY) &&
LEFT.br_startoff + LEFT.br_blockcount == new->br_startoff &&
LEFT.br_startblock + LEFT.br_blockcount == new->br_startblock &&
LEFT.br_state == new->br_state &&
LEFT.br_blockcount + new->br_blockcount <= MAXEXTLEN);
/*
* Check and set flags if this segment has a right neighbor.
* Don't set contiguous if the combined extent would be too large.
* Also check for all-three-contiguous being too large.
*/
if (STATE_SET_TEST(RIGHT_VALID,
idx <
ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t) - 1)) {
xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx + 1), &RIGHT);
STATE_SET(RIGHT_DELAY, ISNULLSTARTBLOCK(RIGHT.br_startblock));
}
STATE_SET(RIGHT_CONTIG,
STATE_TEST(RIGHT_VALID) && !STATE_TEST(RIGHT_DELAY) &&
new_endoff == RIGHT.br_startoff &&
new->br_startblock + new->br_blockcount ==
RIGHT.br_startblock &&
new->br_state == RIGHT.br_state &&
new->br_blockcount + RIGHT.br_blockcount <= MAXEXTLEN &&
((state & MASK3(LEFT_CONTIG, LEFT_FILLING, RIGHT_FILLING)) !=
MASK3(LEFT_CONTIG, LEFT_FILLING, RIGHT_FILLING) ||
LEFT.br_blockcount + new->br_blockcount + RIGHT.br_blockcount
<= MAXEXTLEN));
error = 0;
/*
* Switch out based on the FILLING and CONTIG state bits.
*/
switch (SWITCH_STATE) {
case MASK4(LEFT_FILLING, RIGHT_FILLING, LEFT_CONTIG, RIGHT_CONTIG):
/*
* Filling in all of a previously delayed allocation extent.
* The left and right neighbors are both contiguous with new.
*/
xfs_bmap_trace_pre_update(fname, "LF|RF|LC|RC", ip, idx - 1,
XFS_DATA_FORK);
xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1),
LEFT.br_blockcount + PREV.br_blockcount +
RIGHT.br_blockcount);
xfs_bmap_trace_post_update(fname, "LF|RF|LC|RC", ip, idx - 1,
XFS_DATA_FORK);
xfs_bmap_trace_delete(fname, "LF|RF|LC|RC", ip, idx, 2,
XFS_DATA_FORK);
xfs_iext_remove(ifp, idx, 2);
ip->i_df.if_lastex = idx - 1;
ip->i_d.di_nextents--;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
else {
rval = XFS_ILOG_CORE;
if ((error = xfs_bmbt_lookup_eq(cur, RIGHT.br_startoff,
RIGHT.br_startblock,
RIGHT.br_blockcount, &i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_delete(cur, &i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_decrement(cur, 0, &i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_update(cur, LEFT.br_startoff,
LEFT.br_startblock,
LEFT.br_blockcount +
PREV.br_blockcount +
RIGHT.br_blockcount, LEFT.br_state)))
goto done;
}
*dnew = 0;
/* DELTA: Three in-core extents are replaced by one. */
temp = LEFT.br_startoff;
temp2 = LEFT.br_blockcount +
PREV.br_blockcount +
RIGHT.br_blockcount;
break;
case MASK3(LEFT_FILLING, RIGHT_FILLING, LEFT_CONTIG):
/*
* Filling in all of a previously delayed allocation extent.
* The left neighbor is contiguous, the right is not.
*/
xfs_bmap_trace_pre_update(fname, "LF|RF|LC", ip, idx - 1,
XFS_DATA_FORK);
xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1),
LEFT.br_blockcount + PREV.br_blockcount);
xfs_bmap_trace_post_update(fname, "LF|RF|LC", ip, idx - 1,
XFS_DATA_FORK);
ip->i_df.if_lastex = idx - 1;
xfs_bmap_trace_delete(fname, "LF|RF|LC", ip, idx, 1,
XFS_DATA_FORK);
xfs_iext_remove(ifp, idx, 1);
if (cur == NULL)
rval = XFS_ILOG_DEXT;
else {
rval = 0;
if ((error = xfs_bmbt_lookup_eq(cur, LEFT.br_startoff,
LEFT.br_startblock, LEFT.br_blockcount,
&i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_update(cur, LEFT.br_startoff,
LEFT.br_startblock,
LEFT.br_blockcount +
PREV.br_blockcount, LEFT.br_state)))
goto done;
}
*dnew = 0;
/* DELTA: Two in-core extents are replaced by one. */
temp = LEFT.br_startoff;
temp2 = LEFT.br_blockcount +
PREV.br_blockcount;
break;
case MASK3(LEFT_FILLING, RIGHT_FILLING, RIGHT_CONTIG):
/*
* Filling in all of a previously delayed allocation extent.
* The right neighbor is contiguous, the left is not.
*/
xfs_bmap_trace_pre_update(fname, "LF|RF|RC", ip, idx,
XFS_DATA_FORK);
xfs_bmbt_set_startblock(ep, new->br_startblock);
xfs_bmbt_set_blockcount(ep,
PREV.br_blockcount + RIGHT.br_blockcount);
xfs_bmap_trace_post_update(fname, "LF|RF|RC", ip, idx,
XFS_DATA_FORK);
ip->i_df.if_lastex = idx;
xfs_bmap_trace_delete(fname, "LF|RF|RC", ip, idx + 1, 1,
XFS_DATA_FORK);
xfs_iext_remove(ifp, idx + 1, 1);
if (cur == NULL)
rval = XFS_ILOG_DEXT;
else {
rval = 0;
if ((error = xfs_bmbt_lookup_eq(cur, RIGHT.br_startoff,
RIGHT.br_startblock,
RIGHT.br_blockcount, &i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_update(cur, PREV.br_startoff,
new->br_startblock,
PREV.br_blockcount +
RIGHT.br_blockcount, PREV.br_state)))
goto done;
}
*dnew = 0;
/* DELTA: Two in-core extents are replaced by one. */
temp = PREV.br_startoff;
temp2 = PREV.br_blockcount +
RIGHT.br_blockcount;
break;
case MASK2(LEFT_FILLING, RIGHT_FILLING):
/*
* Filling in all of a previously delayed allocation extent.
* Neither the left nor right neighbors are contiguous with
* the new one.
*/
xfs_bmap_trace_pre_update(fname, "LF|RF", ip, idx,
XFS_DATA_FORK);
xfs_bmbt_set_startblock(ep, new->br_startblock);
xfs_bmap_trace_post_update(fname, "LF|RF", ip, idx,
XFS_DATA_FORK);
ip->i_df.if_lastex = idx;
ip->i_d.di_nextents++;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
else {
rval = XFS_ILOG_CORE;
if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff,
new->br_startblock, new->br_blockcount,
&i)))
goto done;
ASSERT(i == 0);
cur->bc_rec.b.br_state = XFS_EXT_NORM;
if ((error = xfs_bmbt_insert(cur, &i)))
goto done;
ASSERT(i == 1);
}
*dnew = 0;
/* DELTA: The in-core extent described by new changed type. */
temp = new->br_startoff;
temp2 = new->br_blockcount;
break;
case MASK2(LEFT_FILLING, LEFT_CONTIG):
/*
* Filling in the first part of a previous delayed allocation.
* The left neighbor is contiguous.
*/
xfs_bmap_trace_pre_update(fname, "LF|LC", ip, idx - 1,
XFS_DATA_FORK);
xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1),
LEFT.br_blockcount + new->br_blockcount);
xfs_bmbt_set_startoff(ep,
PREV.br_startoff + new->br_blockcount);
xfs_bmap_trace_post_update(fname, "LF|LC", ip, idx - 1,
XFS_DATA_FORK);
temp = PREV.br_blockcount - new->br_blockcount;
xfs_bmap_trace_pre_update(fname, "LF|LC", ip, idx,
XFS_DATA_FORK);
xfs_bmbt_set_blockcount(ep, temp);
ip->i_df.if_lastex = idx - 1;
if (cur == NULL)
rval = XFS_ILOG_DEXT;
else {
rval = 0;
if ((error = xfs_bmbt_lookup_eq(cur, LEFT.br_startoff,
LEFT.br_startblock, LEFT.br_blockcount,
&i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_update(cur, LEFT.br_startoff,
LEFT.br_startblock,
LEFT.br_blockcount +
new->br_blockcount,
LEFT.br_state)))
goto done;
}
temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
STARTBLOCKVAL(PREV.br_startblock));
xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp));
xfs_bmap_trace_post_update(fname, "LF|LC", ip, idx,
XFS_DATA_FORK);
*dnew = temp;
/* DELTA: The boundary between two in-core extents moved. */
temp = LEFT.br_startoff;
temp2 = LEFT.br_blockcount +
PREV.br_blockcount;
break;
case MASK(LEFT_FILLING):
/*
* Filling in the first part of a previous delayed allocation.
* The left neighbor is not contiguous.
*/
xfs_bmap_trace_pre_update(fname, "LF", ip, idx, XFS_DATA_FORK);
xfs_bmbt_set_startoff(ep, new_endoff);
temp = PREV.br_blockcount - new->br_blockcount;
xfs_bmbt_set_blockcount(ep, temp);
xfs_bmap_trace_insert(fname, "LF", ip, idx, 1, new, NULL,
XFS_DATA_FORK);
xfs_iext_insert(ifp, idx, 1, new);
ip->i_df.if_lastex = idx;
ip->i_d.di_nextents++;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
else {
rval = XFS_ILOG_CORE;
if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff,
new->br_startblock, new->br_blockcount,
&i)))
goto done;
ASSERT(i == 0);
cur->bc_rec.b.br_state = XFS_EXT_NORM;
if ((error = xfs_bmbt_insert(cur, &i)))
goto done;
ASSERT(i == 1);
}
if (ip->i_d.di_format == XFS_DINODE_FMT_EXTENTS &&
ip->i_d.di_nextents > ip->i_df.if_ext_max) {
error = xfs_bmap_extents_to_btree(ip->i_transp, ip,
first, flist, &cur, 1, &tmp_rval,
XFS_DATA_FORK);
rval |= tmp_rval;
if (error)
goto done;
}
temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
STARTBLOCKVAL(PREV.br_startblock) -
(cur ? cur->bc_private.b.allocated : 0));
ep = xfs_iext_get_ext(ifp, idx + 1);
xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp));
xfs_bmap_trace_post_update(fname, "LF", ip, idx + 1,
XFS_DATA_FORK);
*dnew = temp;
/* DELTA: One in-core extent is split in two. */
temp = PREV.br_startoff;
temp2 = PREV.br_blockcount;
break;
case MASK2(RIGHT_FILLING, RIGHT_CONTIG):
/*
* Filling in the last part of a previous delayed allocation.
* The right neighbor is contiguous with the new allocation.
*/
temp = PREV.br_blockcount - new->br_blockcount;
xfs_bmap_trace_pre_update(fname, "RF|RC", ip, idx,
XFS_DATA_FORK);
xfs_bmap_trace_pre_update(fname, "RF|RC", ip, idx + 1,
XFS_DATA_FORK);
xfs_bmbt_set_blockcount(ep, temp);
xfs_bmbt_set_allf(xfs_iext_get_ext(ifp, idx + 1),
new->br_startoff, new->br_startblock,
new->br_blockcount + RIGHT.br_blockcount,
RIGHT.br_state);
xfs_bmap_trace_post_update(fname, "RF|RC", ip, idx + 1,
XFS_DATA_FORK);
ip->i_df.if_lastex = idx + 1;
if (cur == NULL)
rval = XFS_ILOG_DEXT;
else {
rval = 0;
if ((error = xfs_bmbt_lookup_eq(cur, RIGHT.br_startoff,
RIGHT.br_startblock,
RIGHT.br_blockcount, &i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_update(cur, new->br_startoff,
new->br_startblock,
new->br_blockcount +
RIGHT.br_blockcount,
RIGHT.br_state)))
goto done;
}
temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
STARTBLOCKVAL(PREV.br_startblock));
xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp));
xfs_bmap_trace_post_update(fname, "RF|RC", ip, idx,
XFS_DATA_FORK);
*dnew = temp;
/* DELTA: The boundary between two in-core extents moved. */
temp = PREV.br_startoff;
temp2 = PREV.br_blockcount +
RIGHT.br_blockcount;
break;
case MASK(RIGHT_FILLING):
/*
* Filling in the last part of a previous delayed allocation.
* The right neighbor is not contiguous.
*/
temp = PREV.br_blockcount - new->br_blockcount;
xfs_bmap_trace_pre_update(fname, "RF", ip, idx, XFS_DATA_FORK);
xfs_bmbt_set_blockcount(ep, temp);
xfs_bmap_trace_insert(fname, "RF", ip, idx + 1, 1,
new, NULL, XFS_DATA_FORK);
xfs_iext_insert(ifp, idx + 1, 1, new);
ip->i_df.if_lastex = idx + 1;
ip->i_d.di_nextents++;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
else {
rval = XFS_ILOG_CORE;
if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff,
new->br_startblock, new->br_blockcount,
&i)))
goto done;
ASSERT(i == 0);
cur->bc_rec.b.br_state = XFS_EXT_NORM;
if ((error = xfs_bmbt_insert(cur, &i)))
goto done;
ASSERT(i == 1);
}
if (ip->i_d.di_format == XFS_DINODE_FMT_EXTENTS &&
ip->i_d.di_nextents > ip->i_df.if_ext_max) {
error = xfs_bmap_extents_to_btree(ip->i_transp, ip,
first, flist, &cur, 1, &tmp_rval,
XFS_DATA_FORK);
rval |= tmp_rval;
if (error)
goto done;
}
temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
STARTBLOCKVAL(PREV.br_startblock) -
(cur ? cur->bc_private.b.allocated : 0));
ep = xfs_iext_get_ext(ifp, idx);
xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp));
xfs_bmap_trace_post_update(fname, "RF", ip, idx, XFS_DATA_FORK);
*dnew = temp;
/* DELTA: One in-core extent is split in two. */
temp = PREV.br_startoff;
temp2 = PREV.br_blockcount;
break;
case 0:
/*
* Filling in the middle part of a previous delayed allocation.
* Contiguity is impossible here.
* This case is avoided almost all the time.
*/
temp = new->br_startoff - PREV.br_startoff;
xfs_bmap_trace_pre_update(fname, "0", ip, idx, XFS_DATA_FORK);
xfs_bmbt_set_blockcount(ep, temp);
r[0] = *new;
r[1].br_state = PREV.br_state;
r[1].br_startblock = 0;
r[1].br_startoff = new_endoff;
temp2 = PREV.br_startoff + PREV.br_blockcount - new_endoff;
r[1].br_blockcount = temp2;
xfs_bmap_trace_insert(fname, "0", ip, idx + 1, 2, &r[0], &r[1],
XFS_DATA_FORK);
xfs_iext_insert(ifp, idx + 1, 2, &r[0]);
ip->i_df.if_lastex = idx + 1;
ip->i_d.di_nextents++;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
else {
rval = XFS_ILOG_CORE;
if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff,
new->br_startblock, new->br_blockcount,
&i)))
goto done;
ASSERT(i == 0);
cur->bc_rec.b.br_state = XFS_EXT_NORM;
if ((error = xfs_bmbt_insert(cur, &i)))
goto done;
ASSERT(i == 1);
}
if (ip->i_d.di_format == XFS_DINODE_FMT_EXTENTS &&
ip->i_d.di_nextents > ip->i_df.if_ext_max) {
error = xfs_bmap_extents_to_btree(ip->i_transp, ip,
first, flist, &cur, 1, &tmp_rval,
XFS_DATA_FORK);
rval |= tmp_rval;
if (error)
goto done;
}
temp = xfs_bmap_worst_indlen(ip, temp);
temp2 = xfs_bmap_worst_indlen(ip, temp2);
diff = (int)(temp + temp2 - STARTBLOCKVAL(PREV.br_startblock) -
(cur ? cur->bc_private.b.allocated : 0));
if (diff > 0 &&
xfs_mod_incore_sb(ip->i_mount, XFS_SBS_FDBLOCKS, -diff, rsvd)) {
/*
* Ick gross gag me with a spoon.
*/
ASSERT(0); /* want to see if this ever happens! */
while (diff > 0) {
if (temp) {
temp--;
diff--;
if (!diff ||
!xfs_mod_incore_sb(ip->i_mount,
XFS_SBS_FDBLOCKS, -diff, rsvd))
break;
}
if (temp2) {
temp2--;
diff--;
if (!diff ||
!xfs_mod_incore_sb(ip->i_mount,
XFS_SBS_FDBLOCKS, -diff, rsvd))
break;
}
}
}
ep = xfs_iext_get_ext(ifp, idx);
xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp));
xfs_bmap_trace_post_update(fname, "0", ip, idx, XFS_DATA_FORK);
xfs_bmap_trace_pre_update(fname, "0", ip, idx + 2,
XFS_DATA_FORK);
xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, idx + 2),
NULLSTARTBLOCK((int)temp2));
xfs_bmap_trace_post_update(fname, "0", ip, idx + 2,
XFS_DATA_FORK);
*dnew = temp + temp2;
/* DELTA: One in-core extent is split in three. */
temp = PREV.br_startoff;
temp2 = PREV.br_blockcount;
break;
case MASK3(LEFT_FILLING, LEFT_CONTIG, RIGHT_CONTIG):
case MASK3(RIGHT_FILLING, LEFT_CONTIG, RIGHT_CONTIG):
case MASK2(LEFT_FILLING, RIGHT_CONTIG):
case MASK2(RIGHT_FILLING, LEFT_CONTIG):
case MASK2(LEFT_CONTIG, RIGHT_CONTIG):
case MASK(LEFT_CONTIG):
case MASK(RIGHT_CONTIG):
/*
* These cases are all impossible.
*/
ASSERT(0);
}
*curp = cur;
if (delta) {
temp2 += temp;
if (delta->xed_startoff > temp)
delta->xed_startoff = temp;
if (delta->xed_blockcount < temp2)
delta->xed_blockcount = temp2;
}
done:
*logflagsp = rval;
return error;
#undef LEFT
#undef RIGHT
#undef PREV
#undef MASK
#undef MASK2
#undef MASK3
#undef MASK4
#undef STATE_SET
#undef STATE_TEST
#undef STATE_SET_TEST
#undef SWITCH_STATE
}
/*
* Called by xfs_bmap_add_extent to handle cases converting an unwritten
* allocation to a real allocation or vice versa.
*/
STATIC int /* error */
xfs_bmap_add_extent_unwritten_real(
xfs_inode_t *ip, /* incore inode pointer */
xfs_extnum_t idx, /* extent number to update/insert */
xfs_btree_cur_t **curp, /* if *curp is null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
int *logflagsp, /* inode logging flags */
xfs_extdelta_t *delta) /* Change made to incore extents */
{
xfs_btree_cur_t *cur; /* btree cursor */
xfs_bmbt_rec_t *ep; /* extent entry for idx */
int error; /* error return value */
#ifdef XFS_BMAP_TRACE
static char fname[] = "xfs_bmap_add_extent_unwritten_real";
#endif
int i; /* temp state */
xfs_ifork_t *ifp; /* inode fork pointer */
xfs_fileoff_t new_endoff; /* end offset of new entry */
xfs_exntst_t newext; /* new extent state */
xfs_exntst_t oldext; /* old extent state */
xfs_bmbt_irec_t r[3]; /* neighbor extent entries */
/* left is 0, right is 1, prev is 2 */
int rval=0; /* return value (logging flags) */
int state = 0;/* state bits, accessed thru macros */
xfs_filblks_t temp=0;
xfs_filblks_t temp2=0;
enum { /* bit number definitions for state */
LEFT_CONTIG, RIGHT_CONTIG,
LEFT_FILLING, RIGHT_FILLING,
LEFT_DELAY, RIGHT_DELAY,
LEFT_VALID, RIGHT_VALID
};
#define LEFT r[0]
#define RIGHT r[1]
#define PREV r[2]
#define MASK(b) (1 << (b))
#define MASK2(a,b) (MASK(a) | MASK(b))
#define MASK3(a,b,c) (MASK2(a,b) | MASK(c))
#define MASK4(a,b,c,d) (MASK3(a,b,c) | MASK(d))
#define STATE_SET(b,v) ((v) ? (state |= MASK(b)) : (state &= ~MASK(b)))
#define STATE_TEST(b) (state & MASK(b))
#define STATE_SET_TEST(b,v) ((v) ? ((state |= MASK(b)), 1) : \
((state &= ~MASK(b)), 0))
#define SWITCH_STATE \
(state & MASK4(LEFT_FILLING, RIGHT_FILLING, LEFT_CONTIG, RIGHT_CONTIG))
/*
* Set up a bunch of variables to make the tests simpler.
*/
error = 0;
cur = *curp;
ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
ep = xfs_iext_get_ext(ifp, idx);
xfs_bmbt_get_all(ep, &PREV);
newext = new->br_state;
oldext = (newext == XFS_EXT_UNWRITTEN) ?
XFS_EXT_NORM : XFS_EXT_UNWRITTEN;
ASSERT(PREV.br_state == oldext);
new_endoff = new->br_startoff + new->br_blockcount;
ASSERT(PREV.br_startoff <= new->br_startoff);
ASSERT(PREV.br_startoff + PREV.br_blockcount >= new_endoff);
/*
* Set flags determining what part of the previous oldext allocation
* extent is being replaced by a newext allocation.
*/
STATE_SET(LEFT_FILLING, PREV.br_startoff == new->br_startoff);
STATE_SET(RIGHT_FILLING,
PREV.br_startoff + PREV.br_blockcount == new_endoff);
/*
* Check and set flags if this segment has a left neighbor.
* Don't set contiguous if the combined extent would be too large.
*/
if (STATE_SET_TEST(LEFT_VALID, idx > 0)) {
xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx - 1), &LEFT);
STATE_SET(LEFT_DELAY, ISNULLSTARTBLOCK(LEFT.br_startblock));
}
STATE_SET(LEFT_CONTIG,
STATE_TEST(LEFT_VALID) && !STATE_TEST(LEFT_DELAY) &&
LEFT.br_startoff + LEFT.br_blockcount == new->br_startoff &&
LEFT.br_startblock + LEFT.br_blockcount == new->br_startblock &&
LEFT.br_state == newext &&
LEFT.br_blockcount + new->br_blockcount <= MAXEXTLEN);
/*
* Check and set flags if this segment has a right neighbor.
* Don't set contiguous if the combined extent would be too large.
* Also check for all-three-contiguous being too large.
*/
if (STATE_SET_TEST(RIGHT_VALID,
idx <
ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t) - 1)) {
xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx + 1), &RIGHT);
STATE_SET(RIGHT_DELAY, ISNULLSTARTBLOCK(RIGHT.br_startblock));
}
STATE_SET(RIGHT_CONTIG,
STATE_TEST(RIGHT_VALID) && !STATE_TEST(RIGHT_DELAY) &&
new_endoff == RIGHT.br_startoff &&
new->br_startblock + new->br_blockcount ==
RIGHT.br_startblock &&
newext == RIGHT.br_state &&
new->br_blockcount + RIGHT.br_blockcount <= MAXEXTLEN &&
((state & MASK3(LEFT_CONTIG, LEFT_FILLING, RIGHT_FILLING)) !=
MASK3(LEFT_CONTIG, LEFT_FILLING, RIGHT_FILLING) ||
LEFT.br_blockcount + new->br_blockcount + RIGHT.br_blockcount
<= MAXEXTLEN));
/*
* Switch out based on the FILLING and CONTIG state bits.
*/
switch (SWITCH_STATE) {
case MASK4(LEFT_FILLING, RIGHT_FILLING, LEFT_CONTIG, RIGHT_CONTIG):
/*
* Setting all of a previous oldext extent to newext.
* The left and right neighbors are both contiguous with new.
*/
xfs_bmap_trace_pre_update(fname, "LF|RF|LC|RC", ip, idx - 1,
XFS_DATA_FORK);
xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1),
LEFT.br_blockcount + PREV.br_blockcount +
RIGHT.br_blockcount);
xfs_bmap_trace_post_update(fname, "LF|RF|LC|RC", ip, idx - 1,
XFS_DATA_FORK);
xfs_bmap_trace_delete(fname, "LF|RF|LC|RC", ip, idx, 2,
XFS_DATA_FORK);
xfs_iext_remove(ifp, idx, 2);
ip->i_df.if_lastex = idx - 1;
ip->i_d.di_nextents -= 2;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
else {
rval = XFS_ILOG_CORE;
if ((error = xfs_bmbt_lookup_eq(cur, RIGHT.br_startoff,
RIGHT.br_startblock,
RIGHT.br_blockcount, &i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_delete(cur, &i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_decrement(cur, 0, &i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_delete(cur, &i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_decrement(cur, 0, &i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_update(cur, LEFT.br_startoff,
LEFT.br_startblock,
LEFT.br_blockcount + PREV.br_blockcount +
RIGHT.br_blockcount, LEFT.br_state)))
goto done;
}
/* DELTA: Three in-core extents are replaced by one. */
temp = LEFT.br_startoff;
temp2 = LEFT.br_blockcount +
PREV.br_blockcount +
RIGHT.br_blockcount;
break;
case MASK3(LEFT_FILLING, RIGHT_FILLING, LEFT_CONTIG):
/*
* Setting all of a previous oldext extent to newext.
* The left neighbor is contiguous, the right is not.
*/
xfs_bmap_trace_pre_update(fname, "LF|RF|LC", ip, idx - 1,
XFS_DATA_FORK);
xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1),
LEFT.br_blockcount + PREV.br_blockcount);
xfs_bmap_trace_post_update(fname, "LF|RF|LC", ip, idx - 1,
XFS_DATA_FORK);
ip->i_df.if_lastex = idx - 1;
xfs_bmap_trace_delete(fname, "LF|RF|LC", ip, idx, 1,
XFS_DATA_FORK);
xfs_iext_remove(ifp, idx, 1);
ip->i_d.di_nextents--;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
else {
rval = XFS_ILOG_CORE;
if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff,
PREV.br_startblock, PREV.br_blockcount,
&i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_delete(cur, &i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_decrement(cur, 0, &i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_update(cur, LEFT.br_startoff,
LEFT.br_startblock,
LEFT.br_blockcount + PREV.br_blockcount,
LEFT.br_state)))
goto done;
}
/* DELTA: Two in-core extents are replaced by one. */
temp = LEFT.br_startoff;
temp2 = LEFT.br_blockcount +
PREV.br_blockcount;
break;
case MASK3(LEFT_FILLING, RIGHT_FILLING, RIGHT_CONTIG):
/*
* Setting all of a previous oldext extent to newext.
* The right neighbor is contiguous, the left is not.
*/
xfs_bmap_trace_pre_update(fname, "LF|RF|RC", ip, idx,
XFS_DATA_FORK);
xfs_bmbt_set_blockcount(ep,
PREV.br_blockcount + RIGHT.br_blockcount);
xfs_bmbt_set_state(ep, newext);
xfs_bmap_trace_post_update(fname, "LF|RF|RC", ip, idx,
XFS_DATA_FORK);
ip->i_df.if_lastex = idx;
xfs_bmap_trace_delete(fname, "LF|RF|RC", ip, idx + 1, 1,
XFS_DATA_FORK);
xfs_iext_remove(ifp, idx + 1, 1);
ip->i_d.di_nextents--;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
else {
rval = XFS_ILOG_CORE;
if ((error = xfs_bmbt_lookup_eq(cur, RIGHT.br_startoff,
RIGHT.br_startblock,
RIGHT.br_blockcount, &i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_delete(cur, &i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_decrement(cur, 0, &i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_update(cur, new->br_startoff,
new->br_startblock,
new->br_blockcount + RIGHT.br_blockcount,
newext)))
goto done;
}
/* DELTA: Two in-core extents are replaced by one. */
temp = PREV.br_startoff;
temp2 = PREV.br_blockcount +
RIGHT.br_blockcount;
break;
case MASK2(LEFT_FILLING, RIGHT_FILLING):
/*
* Setting all of a previous oldext extent to newext.
* Neither the left nor right neighbors are contiguous with
* the new one.
*/
xfs_bmap_trace_pre_update(fname, "LF|RF", ip, idx,
XFS_DATA_FORK);
xfs_bmbt_set_state(ep, newext);
xfs_bmap_trace_post_update(fname, "LF|RF", ip, idx,
XFS_DATA_FORK);
ip->i_df.if_lastex = idx;
if (cur == NULL)
rval = XFS_ILOG_DEXT;
else {
rval = 0;
if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff,
new->br_startblock, new->br_blockcount,
&i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_update(cur, new->br_startoff,
new->br_startblock, new->br_blockcount,
newext)))
goto done;
}
/* DELTA: The in-core extent described by new changed type. */
temp = new->br_startoff;
temp2 = new->br_blockcount;
break;
case MASK2(LEFT_FILLING, LEFT_CONTIG):
/*
* Setting the first part of a previous oldext extent to newext.
* The left neighbor is contiguous.
*/
xfs_bmap_trace_pre_update(fname, "LF|LC", ip, idx - 1,
XFS_DATA_FORK);
xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1),
LEFT.br_blockcount + new->br_blockcount);
xfs_bmbt_set_startoff(ep,
PREV.br_startoff + new->br_blockcount);
xfs_bmap_trace_post_update(fname, "LF|LC", ip, idx - 1,
XFS_DATA_FORK);
xfs_bmap_trace_pre_update(fname, "LF|LC", ip, idx,
XFS_DATA_FORK);
xfs_bmbt_set_startblock(ep,
new->br_startblock + new->br_blockcount);
xfs_bmbt_set_blockcount(ep,
PREV.br_blockcount - new->br_blockcount);
xfs_bmap_trace_post_update(fname, "LF|LC", ip, idx,
XFS_DATA_FORK);
ip->i_df.if_lastex = idx - 1;
if (cur == NULL)
rval = XFS_ILOG_DEXT;
else {
rval = 0;
if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff,
PREV.br_startblock, PREV.br_blockcount,
&i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_update(cur,
PREV.br_startoff + new->br_blockcount,
PREV.br_startblock + new->br_blockcount,
PREV.br_blockcount - new->br_blockcount,
oldext)))
goto done;
if ((error = xfs_bmbt_decrement(cur, 0, &i)))
goto done;
if (xfs_bmbt_update(cur, LEFT.br_startoff,
LEFT.br_startblock,
LEFT.br_blockcount + new->br_blockcount,
LEFT.br_state))
goto done;
}
/* DELTA: The boundary between two in-core extents moved. */
temp = LEFT.br_startoff;
temp2 = LEFT.br_blockcount +
PREV.br_blockcount;
break;
case MASK(LEFT_FILLING):
/*
* Setting the first part of a previous oldext extent to newext.
* The left neighbor is not contiguous.
*/
xfs_bmap_trace_pre_update(fname, "LF", ip, idx, XFS_DATA_FORK);
ASSERT(ep && xfs_bmbt_get_state(ep) == oldext);
xfs_bmbt_set_startoff(ep, new_endoff);
xfs_bmbt_set_blockcount(ep,
PREV.br_blockcount - new->br_blockcount);
xfs_bmbt_set_startblock(ep,
new->br_startblock + new->br_blockcount);
xfs_bmap_trace_post_update(fname, "LF", ip, idx, XFS_DATA_FORK);
xfs_bmap_trace_insert(fname, "LF", ip, idx, 1, new, NULL,
XFS_DATA_FORK);
xfs_iext_insert(ifp, idx, 1, new);
ip->i_df.if_lastex = idx;
ip->i_d.di_nextents++;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
else {
rval = XFS_ILOG_CORE;
if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff,
PREV.br_startblock, PREV.br_blockcount,
&i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_update(cur,
PREV.br_startoff + new->br_blockcount,
PREV.br_startblock + new->br_blockcount,
PREV.br_blockcount - new->br_blockcount,
oldext)))
goto done;
cur->bc_rec.b = *new;
if ((error = xfs_bmbt_insert(cur, &i)))
goto done;
ASSERT(i == 1);
}
/* DELTA: One in-core extent is split in two. */
temp = PREV.br_startoff;
temp2 = PREV.br_blockcount;
break;
case MASK2(RIGHT_FILLING, RIGHT_CONTIG):
/*
* Setting the last part of a previous oldext extent to newext.
* The right neighbor is contiguous with the new allocation.
*/
xfs_bmap_trace_pre_update(fname, "RF|RC", ip, idx,
XFS_DATA_FORK);
xfs_bmap_trace_pre_update(fname, "RF|RC", ip, idx + 1,
XFS_DATA_FORK);
xfs_bmbt_set_blockcount(ep,
PREV.br_blockcount - new->br_blockcount);
xfs_bmap_trace_post_update(fname, "RF|RC", ip, idx,
XFS_DATA_FORK);
xfs_bmbt_set_allf(xfs_iext_get_ext(ifp, idx + 1),
new->br_startoff, new->br_startblock,
new->br_blockcount + RIGHT.br_blockcount, newext);
xfs_bmap_trace_post_update(fname, "RF|RC", ip, idx + 1,
XFS_DATA_FORK);
ip->i_df.if_lastex = idx + 1;
if (cur == NULL)
rval = XFS_ILOG_DEXT;
else {
rval = 0;
if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff,
PREV.br_startblock,
PREV.br_blockcount, &i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_update(cur, PREV.br_startoff,
PREV.br_startblock,
PREV.br_blockcount - new->br_blockcount,
oldext)))
goto done;
if ((error = xfs_bmbt_increment(cur, 0, &i)))
goto done;
if ((error = xfs_bmbt_update(cur, new->br_startoff,
new->br_startblock,
new->br_blockcount + RIGHT.br_blockcount,
newext)))
goto done;
}
/* DELTA: The boundary between two in-core extents moved. */
temp = PREV.br_startoff;
temp2 = PREV.br_blockcount +
RIGHT.br_blockcount;
break;
case MASK(RIGHT_FILLING):
/*
* Setting the last part of a previous oldext extent to newext.
* The right neighbor is not contiguous.
*/
xfs_bmap_trace_pre_update(fname, "RF", ip, idx, XFS_DATA_FORK);
xfs_bmbt_set_blockcount(ep,
PREV.br_blockcount - new->br_blockcount);
xfs_bmap_trace_post_update(fname, "RF", ip, idx, XFS_DATA_FORK);
xfs_bmap_trace_insert(fname, "RF", ip, idx + 1, 1,
new, NULL, XFS_DATA_FORK);
xfs_iext_insert(ifp, idx + 1, 1, new);
ip->i_df.if_lastex = idx + 1;
ip->i_d.di_nextents++;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
else {
rval = XFS_ILOG_CORE;
if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff,
PREV.br_startblock, PREV.br_blockcount,
&i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_update(cur, PREV.br_startoff,
PREV.br_startblock,
PREV.br_blockcount - new->br_blockcount,
oldext)))
goto done;
if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff,
new->br_startblock, new->br_blockcount,
&i)))
goto done;
ASSERT(i == 0);
cur->bc_rec.b.br_state = XFS_EXT_NORM;
if ((error = xfs_bmbt_insert(cur, &i)))
goto done;
ASSERT(i == 1);
}
/* DELTA: One in-core extent is split in two. */
temp = PREV.br_startoff;
temp2 = PREV.br_blockcount;
break;
case 0:
/*
* Setting the middle part of a previous oldext extent to
* newext. Contiguity is impossible here.
* One extent becomes three extents.
*/
xfs_bmap_trace_pre_update(fname, "0", ip, idx, XFS_DATA_FORK);
xfs_bmbt_set_blockcount(ep,
new->br_startoff - PREV.br_startoff);
xfs_bmap_trace_post_update(fname, "0", ip, idx, XFS_DATA_FORK);
r[0] = *new;
r[1].br_startoff = new_endoff;
r[1].br_blockcount =
PREV.br_startoff + PREV.br_blockcount - new_endoff;
r[1].br_startblock = new->br_startblock + new->br_blockcount;
r[1].br_state = oldext;
xfs_bmap_trace_insert(fname, "0", ip, idx + 1, 2, &r[0], &r[1],
XFS_DATA_FORK);
xfs_iext_insert(ifp, idx + 1, 2, &r[0]);
ip->i_df.if_lastex = idx + 1;
ip->i_d.di_nextents += 2;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
else {
rval = XFS_ILOG_CORE;
if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff,
PREV.br_startblock, PREV.br_blockcount,
&i)))
goto done;
ASSERT(i == 1);
/* new right extent - oldext */
if ((error = xfs_bmbt_update(cur, r[1].br_startoff,
r[1].br_startblock, r[1].br_blockcount,
r[1].br_state)))
goto done;
/* new left extent - oldext */
PREV.br_blockcount =
new->br_startoff - PREV.br_startoff;
cur->bc_rec.b = PREV;
if ((error = xfs_bmbt_insert(cur, &i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_increment(cur, 0, &i)))
goto done;
ASSERT(i == 1);
/* new middle extent - newext */
cur->bc_rec.b = *new;
if ((error = xfs_bmbt_insert(cur, &i)))
goto done;
ASSERT(i == 1);
}
/* DELTA: One in-core extent is split in three. */
temp = PREV.br_startoff;
temp2 = PREV.br_blockcount;
break;
case MASK3(LEFT_FILLING, LEFT_CONTIG, RIGHT_CONTIG):
case MASK3(RIGHT_FILLING, LEFT_CONTIG, RIGHT_CONTIG):
case MASK2(LEFT_FILLING, RIGHT_CONTIG):
case MASK2(RIGHT_FILLING, LEFT_CONTIG):
case MASK2(LEFT_CONTIG, RIGHT_CONTIG):
case MASK(LEFT_CONTIG):
case MASK(RIGHT_CONTIG):
/*
* These cases are all impossible.
*/
ASSERT(0);
}
*curp = cur;
if (delta) {
temp2 += temp;
if (delta->xed_startoff > temp)
delta->xed_startoff = temp;
if (delta->xed_blockcount < temp2)
delta->xed_blockcount = temp2;
}
done:
*logflagsp = rval;
return error;
#undef LEFT
#undef RIGHT
#undef PREV
#undef MASK
#undef MASK2
#undef MASK3
#undef MASK4
#undef STATE_SET
#undef STATE_TEST
#undef STATE_SET_TEST
#undef SWITCH_STATE
}
/*
* Called by xfs_bmap_add_extent to handle cases converting a hole
* to a delayed allocation.
*/
/*ARGSUSED*/
STATIC int /* error */
xfs_bmap_add_extent_hole_delay(
xfs_inode_t *ip, /* incore inode pointer */
xfs_extnum_t idx, /* extent number to update/insert */
xfs_btree_cur_t *cur, /* if null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
int *logflagsp, /* inode logging flags */
xfs_extdelta_t *delta, /* Change made to incore extents */
int rsvd) /* OK to allocate reserved blocks */
{
xfs_bmbt_rec_t *ep; /* extent record for idx */
#ifdef XFS_BMAP_TRACE
static char fname[] = "xfs_bmap_add_extent_hole_delay";
#endif
xfs_ifork_t *ifp; /* inode fork pointer */
xfs_bmbt_irec_t left; /* left neighbor extent entry */
xfs_filblks_t newlen=0; /* new indirect size */
xfs_filblks_t oldlen=0; /* old indirect size */
xfs_bmbt_irec_t right; /* right neighbor extent entry */
int state; /* state bits, accessed thru macros */
xfs_filblks_t temp=0; /* temp for indirect calculations */
xfs_filblks_t temp2=0;
enum { /* bit number definitions for state */
LEFT_CONTIG, RIGHT_CONTIG,
LEFT_DELAY, RIGHT_DELAY,
LEFT_VALID, RIGHT_VALID
};
#define MASK(b) (1 << (b))
#define MASK2(a,b) (MASK(a) | MASK(b))
#define STATE_SET(b,v) ((v) ? (state |= MASK(b)) : (state &= ~MASK(b)))
#define STATE_TEST(b) (state & MASK(b))
#define STATE_SET_TEST(b,v) ((v) ? ((state |= MASK(b)), 1) : \
((state &= ~MASK(b)), 0))
#define SWITCH_STATE (state & MASK2(LEFT_CONTIG, RIGHT_CONTIG))
ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
ep = xfs_iext_get_ext(ifp, idx);
state = 0;
ASSERT(ISNULLSTARTBLOCK(new->br_startblock));
/*
* Check and set flags if this segment has a left neighbor
*/
if (STATE_SET_TEST(LEFT_VALID, idx > 0)) {
xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx - 1), &left);
STATE_SET(LEFT_DELAY, ISNULLSTARTBLOCK(left.br_startblock));
}
/*
* Check and set flags if the current (right) segment exists.
* If it doesn't exist, we're converting the hole at end-of-file.
*/
if (STATE_SET_TEST(RIGHT_VALID,
idx <
ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t))) {
xfs_bmbt_get_all(ep, &right);
STATE_SET(RIGHT_DELAY, ISNULLSTARTBLOCK(right.br_startblock));
}
/*
* Set contiguity flags on the left and right neighbors.
* Don't let extents get too large, even if the pieces are contiguous.
*/
STATE_SET(LEFT_CONTIG,
STATE_TEST(LEFT_VALID) && STATE_TEST(LEFT_DELAY) &&
left.br_startoff + left.br_blockcount == new->br_startoff &&
left.br_blockcount + new->br_blockcount <= MAXEXTLEN);
STATE_SET(RIGHT_CONTIG,
STATE_TEST(RIGHT_VALID) && STATE_TEST(RIGHT_DELAY) &&
new->br_startoff + new->br_blockcount == right.br_startoff &&
new->br_blockcount + right.br_blockcount <= MAXEXTLEN &&
(!STATE_TEST(LEFT_CONTIG) ||
(left.br_blockcount + new->br_blockcount +
right.br_blockcount <= MAXEXTLEN)));
/*
* Switch out based on the contiguity flags.
*/
switch (SWITCH_STATE) {
case MASK2(LEFT_CONTIG, RIGHT_CONTIG):
/*
* New allocation is contiguous with delayed allocations
* on the left and on the right.
* Merge all three into a single extent record.
*/
temp = left.br_blockcount + new->br_blockcount +
right.br_blockcount;
xfs_bmap_trace_pre_update(fname, "LC|RC", ip, idx - 1,
XFS_DATA_FORK);
xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), temp);
oldlen = STARTBLOCKVAL(left.br_startblock) +
STARTBLOCKVAL(new->br_startblock) +
STARTBLOCKVAL(right.br_startblock);
newlen = xfs_bmap_worst_indlen(ip, temp);
xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, idx - 1),
NULLSTARTBLOCK((int)newlen));
xfs_bmap_trace_post_update(fname, "LC|RC", ip, idx - 1,
XFS_DATA_FORK);
xfs_bmap_trace_delete(fname, "LC|RC", ip, idx, 1,
XFS_DATA_FORK);
xfs_iext_remove(ifp, idx, 1);
ip->i_df.if_lastex = idx - 1;
/* DELTA: Two in-core extents were replaced by one. */
temp2 = temp;
temp = left.br_startoff;
break;
case MASK(LEFT_CONTIG):
/*
* New allocation is contiguous with a delayed allocation
* on the left.
* Merge the new allocation with the left neighbor.
*/
temp = left.br_blockcount + new->br_blockcount;
xfs_bmap_trace_pre_update(fname, "LC", ip, idx - 1,
XFS_DATA_FORK);
xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), temp);
oldlen = STARTBLOCKVAL(left.br_startblock) +
STARTBLOCKVAL(new->br_startblock);
newlen = xfs_bmap_worst_indlen(ip, temp);
xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, idx - 1),
NULLSTARTBLOCK((int)newlen));
xfs_bmap_trace_post_update(fname, "LC", ip, idx - 1,
XFS_DATA_FORK);
ip->i_df.if_lastex = idx - 1;
/* DELTA: One in-core extent grew into a hole. */
temp2 = temp;
temp = left.br_startoff;
break;
case MASK(RIGHT_CONTIG):
/*
* New allocation is contiguous with a delayed allocation
* on the right.
* Merge the new allocation with the right neighbor.
*/
xfs_bmap_trace_pre_update(fname, "RC", ip, idx, XFS_DATA_FORK);
temp = new->br_blockcount + right.br_blockcount;
oldlen = STARTBLOCKVAL(new->br_startblock) +
STARTBLOCKVAL(right.br_startblock);
newlen = xfs_bmap_worst_indlen(ip, temp);
xfs_bmbt_set_allf(ep, new->br_startoff,
NULLSTARTBLOCK((int)newlen), temp, right.br_state);
xfs_bmap_trace_post_update(fname, "RC", ip, idx, XFS_DATA_FORK);
ip->i_df.if_lastex = idx;
/* DELTA: One in-core extent grew into a hole. */
temp2 = temp;
temp = new->br_startoff;
break;
case 0:
/*
* New allocation is not contiguous with another
* delayed allocation.
* Insert a new entry.
*/
oldlen = newlen = 0;
xfs_bmap_trace_insert(fname, "0", ip, idx, 1, new, NULL,
XFS_DATA_FORK);
xfs_iext_insert(ifp, idx, 1, new);
ip->i_df.if_lastex = idx;
/* DELTA: A new in-core extent was added in a hole. */
temp2 = new->br_blockcount;
temp = new->br_startoff;
break;
}
if (oldlen != newlen) {
ASSERT(oldlen > newlen);
xfs_mod_incore_sb(ip->i_mount, XFS_SBS_FDBLOCKS,
(int)(oldlen - newlen), rsvd);
/*
* Nothing to do for disk quota accounting here.
*/
}
if (delta) {
temp2 += temp;
if (delta->xed_startoff > temp)
delta->xed_startoff = temp;
if (delta->xed_blockcount < temp2)
delta->xed_blockcount = temp2;
}
*logflagsp = 0;
return 0;
#undef MASK
#undef MASK2
#undef STATE_SET
#undef STATE_TEST
#undef STATE_SET_TEST
#undef SWITCH_STATE
}
/*
* Called by xfs_bmap_add_extent to handle cases converting a hole
* to a real allocation.
*/
STATIC int /* error */
xfs_bmap_add_extent_hole_real(
xfs_inode_t *ip, /* incore inode pointer */
xfs_extnum_t idx, /* extent number to update/insert */
xfs_btree_cur_t *cur, /* if null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
int *logflagsp, /* inode logging flags */
xfs_extdelta_t *delta, /* Change made to incore extents */
int whichfork) /* data or attr fork */
{
xfs_bmbt_rec_t *ep; /* pointer to extent entry ins. point */
int error; /* error return value */
#ifdef XFS_BMAP_TRACE
static char fname[] = "xfs_bmap_add_extent_hole_real";
#endif
int i; /* temp state */
xfs_ifork_t *ifp; /* inode fork pointer */
xfs_bmbt_irec_t left; /* left neighbor extent entry */
xfs_bmbt_irec_t right; /* right neighbor extent entry */
int rval=0; /* return value (logging flags) */
int state; /* state bits, accessed thru macros */
xfs_filblks_t temp=0;
xfs_filblks_t temp2=0;
enum { /* bit number definitions for state */
LEFT_CONTIG, RIGHT_CONTIG,
LEFT_DELAY, RIGHT_DELAY,
LEFT_VALID, RIGHT_VALID
};
#define MASK(b) (1 << (b))
#define MASK2(a,b) (MASK(a) | MASK(b))
#define STATE_SET(b,v) ((v) ? (state |= MASK(b)) : (state &= ~MASK(b)))
#define STATE_TEST(b) (state & MASK(b))
#define STATE_SET_TEST(b,v) ((v) ? ((state |= MASK(b)), 1) : \
((state &= ~MASK(b)), 0))
#define SWITCH_STATE (state & MASK2(LEFT_CONTIG, RIGHT_CONTIG))
ifp = XFS_IFORK_PTR(ip, whichfork);
ASSERT(idx <= ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t));
ep = xfs_iext_get_ext(ifp, idx);
state = 0;
/*
* Check and set flags if this segment has a left neighbor.
*/
if (STATE_SET_TEST(LEFT_VALID, idx > 0)) {
xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx - 1), &left);
STATE_SET(LEFT_DELAY, ISNULLSTARTBLOCK(left.br_startblock));
}
/*
* Check and set flags if this segment has a current value.
* Not true if we're inserting into the "hole" at eof.
*/
if (STATE_SET_TEST(RIGHT_VALID,
idx <
ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t))) {
xfs_bmbt_get_all(ep, &right);
STATE_SET(RIGHT_DELAY, ISNULLSTARTBLOCK(right.br_startblock));
}
/*
* We're inserting a real allocation between "left" and "right".
* Set the contiguity flags. Don't let extents get too large.
*/
STATE_SET(LEFT_CONTIG,
STATE_TEST(LEFT_VALID) && !STATE_TEST(LEFT_DELAY) &&
left.br_startoff + left.br_blockcount == new->br_startoff &&
left.br_startblock + left.br_blockcount == new->br_startblock &&
left.br_state == new->br_state &&
left.br_blockcount + new->br_blockcount <= MAXEXTLEN);
STATE_SET(RIGHT_CONTIG,
STATE_TEST(RIGHT_VALID) && !STATE_TEST(RIGHT_DELAY) &&
new->br_startoff + new->br_blockcount == right.br_startoff &&
new->br_startblock + new->br_blockcount ==
right.br_startblock &&
new->br_state == right.br_state &&
new->br_blockcount + right.br_blockcount <= MAXEXTLEN &&
(!STATE_TEST(LEFT_CONTIG) ||
left.br_blockcount + new->br_blockcount +
right.br_blockcount <= MAXEXTLEN));
error = 0;
/*
* Select which case we're in here, and implement it.
*/
switch (SWITCH_STATE) {
case MASK2(LEFT_CONTIG, RIGHT_CONTIG):
/*
* New allocation is contiguous with real allocations on the
* left and on the right.
* Merge all three into a single extent record.
*/
xfs_bmap_trace_pre_update(fname, "LC|RC", ip, idx - 1,
whichfork);
xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1),
left.br_blockcount + new->br_blockcount +
right.br_blockcount);
xfs_bmap_trace_post_update(fname, "LC|RC", ip, idx - 1,
whichfork);
xfs_bmap_trace_delete(fname, "LC|RC", ip,
idx, 1, whichfork);
xfs_iext_remove(ifp, idx, 1);
ifp->if_lastex = idx - 1;
XFS_IFORK_NEXT_SET(ip, whichfork,
XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
if (cur == NULL) {
rval = XFS_ILOG_CORE | XFS_ILOG_FEXT(whichfork);
} else {
rval = XFS_ILOG_CORE;
if ((error = xfs_bmbt_lookup_eq(cur,
right.br_startoff,
right.br_startblock,
right.br_blockcount, &i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_delete(cur, &i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_decrement(cur, 0, &i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_update(cur, left.br_startoff,
left.br_startblock,
left.br_blockcount +
new->br_blockcount +
right.br_blockcount,
left.br_state)))
goto done;
}
/* DELTA: Two in-core extents were replaced by one. */
temp = left.br_startoff;
temp2 = left.br_blockcount +
new->br_blockcount +
right.br_blockcount;
break;
case MASK(LEFT_CONTIG):
/*
* New allocation is contiguous with a real allocation
* on the left.
* Merge the new allocation with the left neighbor.
*/
xfs_bmap_trace_pre_update(fname, "LC", ip, idx - 1, whichfork);
xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1),
left.br_blockcount + new->br_blockcount);
xfs_bmap_trace_post_update(fname, "LC", ip, idx - 1, whichfork);
ifp->if_lastex = idx - 1;
if (cur == NULL) {
rval = XFS_ILOG_FEXT(whichfork);
} else {
rval = 0;
if ((error = xfs_bmbt_lookup_eq(cur,
left.br_startoff,
left.br_startblock,
left.br_blockcount, &i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_update(cur, left.br_startoff,
left.br_startblock,
left.br_blockcount +
new->br_blockcount,
left.br_state)))
goto done;
}
/* DELTA: One in-core extent grew. */
temp = left.br_startoff;
temp2 = left.br_blockcount +
new->br_blockcount;
break;
case MASK(RIGHT_CONTIG):
/*
* New allocation is contiguous with a real allocation
* on the right.
* Merge the new allocation with the right neighbor.
*/
xfs_bmap_trace_pre_update(fname, "RC", ip, idx, whichfork);
xfs_bmbt_set_allf(ep, new->br_startoff, new->br_startblock,
new->br_blockcount + right.br_blockcount,
right.br_state);
xfs_bmap_trace_post_update(fname, "RC", ip, idx, whichfork);
ifp->if_lastex = idx;
if (cur == NULL) {
rval = XFS_ILOG_FEXT(whichfork);
} else {
rval = 0;
if ((error = xfs_bmbt_lookup_eq(cur,
right.br_startoff,
right.br_startblock,
right.br_blockcount, &i)))
goto done;
ASSERT(i == 1);
if ((error = xfs_bmbt_update(cur, new->br_startoff,
new->br_startblock,
new->br_blockcount +
right.br_blockcount,
right.br_state)))
goto done;
}
/* DELTA: One in-core extent grew. */
temp = new->br_startoff;
temp2 = new->br_blockcount +
right.br_blockcount;
break;
case 0:
/*
* New allocation is not contiguous with another
* real allocation.
* Insert a new entry.
*/
xfs_bmap_trace_insert(fname, "0", ip, idx, 1, new, NULL,
whichfork);
xfs_iext_insert(ifp, idx, 1, new);
ifp->if_lastex = idx;
XFS_IFORK_NEXT_SET(ip, whichfork,
XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
if (cur == NULL) {
rval = XFS_ILOG_CORE | XFS_ILOG_FEXT(whichfork);
} else {
rval = XFS_ILOG_CORE;
if ((error = xfs_bmbt_lookup_eq(cur,
new->br_startoff,
new->br_startblock,
new->br_blockcount, &i)))
goto done;
ASSERT(i == 0);
cur->bc_rec.b.br_state = new->br_state;
if ((error = xfs_bmbt_insert(cur, &i)))
goto done;
ASSERT(i == 1);
}
/* DELTA: A new extent was added in a hole. */
temp = new->br_startoff;
temp2 = new->br_blockcount;
break;
}
if (delta) {
temp2 += temp;
if (delta->xed_startoff > temp)
delta->xed_startoff = temp;
if (delta->xed_blockcount < temp2)
delta->xed_blockcount = temp2;
}
done:
*logflagsp = rval;
return error;
#undef MASK
#undef MASK2
#undef STATE_SET
#undef STATE_TEST
#undef STATE_SET_TEST
#undef SWITCH_STATE
}
/*
* Adjust the size of the new extent based on di_extsize and rt extsize.
*/
STATIC int
xfs_bmap_extsize_align(
xfs_mount_t *mp,
xfs_bmbt_irec_t *gotp, /* next extent pointer */
xfs_bmbt_irec_t *prevp, /* previous extent pointer */
xfs_extlen_t extsz, /* align to this extent size */
int rt, /* is this a realtime inode? */
int eof, /* is extent at end-of-file? */
int delay, /* creating delalloc extent? */
int convert, /* overwriting unwritten extent? */
xfs_fileoff_t *offp, /* in/out: aligned offset */
xfs_extlen_t *lenp) /* in/out: aligned length */
{
xfs_fileoff_t orig_off; /* original offset */
xfs_extlen_t orig_alen; /* original length */
xfs_fileoff_t orig_end; /* original off+len */
xfs_fileoff_t nexto; /* next file offset */
xfs_fileoff_t prevo; /* previous file offset */
xfs_fileoff_t align_off; /* temp for offset */
xfs_extlen_t align_alen; /* temp for length */
xfs_extlen_t temp; /* temp for calculations */
if (convert)
return 0;
orig_off = align_off = *offp;
orig_alen = align_alen = *lenp;
orig_end = orig_off + orig_alen;
/*
* If this request overlaps an existing extent, then don't
* attempt to perform any additional alignment.
*/
if (!delay && !eof &&
(orig_off >= gotp->br_startoff) &&
(orig_end <= gotp->br_startoff + gotp->br_blockcount)) {
return 0;
}
/*
* If the file offset is unaligned vs. the extent size
* we need to align it. This will be possible unless
* the file was previously written with a kernel that didn't
* perform this alignment, or if a truncate shot us in the
* foot.
*/
temp = do_mod(orig_off, extsz);
if (temp) {
align_alen += temp;
align_off -= temp;
}
/*
* Same adjustment for the end of the requested area.
*/
if ((temp = (align_alen % extsz))) {
align_alen += extsz - temp;
}
/*
* If the previous block overlaps with this proposed allocation
* then move the start forward without adjusting the length.
*/
if (prevp->br_startoff != NULLFILEOFF) {
if (prevp->br_startblock == HOLESTARTBLOCK)
prevo = prevp->br_startoff;
else
prevo = prevp->br_startoff + prevp->br_blockcount;
} else
prevo = 0;
if (align_off != orig_off && align_off < prevo)
align_off = prevo;
/*
* If the next block overlaps with this proposed allocation
* then move the start back without adjusting the length,
* but not before offset 0.
* This may of course make the start overlap previous block,
* and if we hit the offset 0 limit then the next block
* can still overlap too.
*/
if (!eof && gotp->br_startoff != NULLFILEOFF) {
if ((delay && gotp->br_startblock == HOLESTARTBLOCK) ||
(!delay && gotp->br_startblock == DELAYSTARTBLOCK))
nexto = gotp->br_startoff + gotp->br_blockcount;
else
nexto = gotp->br_startoff;
} else
nexto = NULLFILEOFF;
if (!eof &&
align_off + align_alen != orig_end &&
align_off + align_alen > nexto)
align_off = nexto > align_alen ? nexto - align_alen : 0;
/*
* If we're now overlapping the next or previous extent that
* means we can't fit an extsz piece in this hole. Just move
* the start forward to the first valid spot and set
* the length so we hit the end.
*/
if (align_off != orig_off && align_off < prevo)
align_off = prevo;
if (align_off + align_alen != orig_end &&
align_off + align_alen > nexto &&
nexto != NULLFILEOFF) {
ASSERT(nexto > prevo);
align_alen = nexto - align_off;
}
/*
* If realtime, and the result isn't a multiple of the realtime
* extent size we need to remove blocks until it is.
*/
if (rt && (temp = (align_alen % mp->m_sb.sb_rextsize))) {
/*
* We're not covering the original request, or
* we won't be able to once we fix the length.
*/
if (orig_off < align_off ||
orig_end > align_off + align_alen ||
align_alen - temp < orig_alen)
return XFS_ERROR(EINVAL);
/*
* Try to fix it by moving the start up.
*/
if (align_off + temp <= orig_off) {
align_alen -= temp;
align_off += temp;
}
/*
* Try to fix it by moving the end in.
*/
else if (align_off + align_alen - temp >= orig_end)
align_alen -= temp;
/*
* Set the start to the minimum then trim the length.
*/
else {
align_alen -= orig_off - align_off;
align_off = orig_off;
align_alen -= align_alen % mp->m_sb.sb_rextsize;
}
/*
* Result doesn't cover the request, fail it.
*/
if (orig_off < align_off || orig_end > align_off + align_alen)
return XFS_ERROR(EINVAL);
} else {
ASSERT(orig_off >= align_off);
ASSERT(orig_end <= align_off + align_alen);
}
#ifdef DEBUG
if (!eof && gotp->br_startoff != NULLFILEOFF)
ASSERT(align_off + align_alen <= gotp->br_startoff);
if (prevp->br_startoff != NULLFILEOFF)
ASSERT(align_off >= prevp->br_startoff + prevp->br_blockcount);
#endif
*lenp = align_alen;
*offp = align_off;
return 0;
}
#define XFS_ALLOC_GAP_UNITS 4
STATIC int
xfs_bmap_adjacent(
xfs_bmalloca_t *ap) /* bmap alloc argument struct */
{
xfs_fsblock_t adjust; /* adjustment to block numbers */
xfs_agnumber_t fb_agno; /* ag number of ap->firstblock */
xfs_mount_t *mp; /* mount point structure */
int nullfb; /* true if ap->firstblock isn't set */
int rt; /* true if inode is realtime */
#define ISVALID(x,y) \
(rt ? \
(x) < mp->m_sb.sb_rblocks : \
XFS_FSB_TO_AGNO(mp, x) == XFS_FSB_TO_AGNO(mp, y) && \
XFS_FSB_TO_AGNO(mp, x) < mp->m_sb.sb_agcount && \
XFS_FSB_TO_AGBNO(mp, x) < mp->m_sb.sb_agblocks)
mp = ap->ip->i_mount;
nullfb = ap->firstblock == NULLFSBLOCK;
rt = XFS_IS_REALTIME_INODE(ap->ip) && ap->userdata;
fb_agno = nullfb ? NULLAGNUMBER : XFS_FSB_TO_AGNO(mp, ap->firstblock);
/*
* If allocating at eof, and there's a previous real block,
* try to use it's last block as our starting point.
*/
if (ap->eof && ap->prevp->br_startoff != NULLFILEOFF &&
!ISNULLSTARTBLOCK(ap->prevp->br_startblock) &&
ISVALID(ap->prevp->br_startblock + ap->prevp->br_blockcount,
ap->prevp->br_startblock)) {
ap->rval = ap->prevp->br_startblock + ap->prevp->br_blockcount;
/*
* Adjust for the gap between prevp and us.
*/
adjust = ap->off -
(ap->prevp->br_startoff + ap->prevp->br_blockcount);
if (adjust &&
ISVALID(ap->rval + adjust, ap->prevp->br_startblock))
ap->rval += adjust;
}
/*
* If not at eof, then compare the two neighbor blocks.
* Figure out whether either one gives us a good starting point,
* and pick the better one.
*/
else if (!ap->eof) {
xfs_fsblock_t gotbno; /* right side block number */
xfs_fsblock_t gotdiff=0; /* right side difference */
xfs_fsblock_t prevbno; /* left side block number */
xfs_fsblock_t prevdiff=0; /* left side difference */
/*
* If there's a previous (left) block, select a requested
* start block based on it.
*/
if (ap->prevp->br_startoff != NULLFILEOFF &&
!ISNULLSTARTBLOCK(ap->prevp->br_startblock) &&
(prevbno = ap->prevp->br_startblock +
ap->prevp->br_blockcount) &&
ISVALID(prevbno, ap->prevp->br_startblock)) {
/*
* Calculate gap to end of previous block.
*/
adjust = prevdiff = ap->off -
(ap->prevp->br_startoff +
ap->prevp->br_blockcount);
/*
* Figure the startblock based on the previous block's
* end and the gap size.
* Heuristic!
* If the gap is large relative to the piece we're
* allocating, or using it gives us an invalid block
* number, then just use the end of the previous block.
*/
if (prevdiff <= XFS_ALLOC_GAP_UNITS * ap->alen &&
ISVALID(prevbno + prevdiff,
ap->prevp->br_startblock))
prevbno += adjust;
else
prevdiff += adjust;
/*
* If the firstblock forbids it, can't use it,
* must use default.
*/
if (!rt && !nullfb &&
XFS_FSB_TO_AGNO(mp, prevbno) != fb_agno)
prevbno = NULLFSBLOCK;
}
/*
* No previous block or can't follow it, just default.
*/
else
prevbno = NULLFSBLOCK;
/*
* If there's a following (right) block, select a requested
* start block based on it.
*/
if (!ISNULLSTARTBLOCK(ap->gotp->br_startblock)) {
/*
* Calculate gap to start of next block.
*/
adjust = gotdiff = ap->gotp->br_startoff - ap->off;
/*
* Figure the startblock based on the next block's
* start and the gap size.
*/
gotbno = ap->gotp->br_startblock;
/*
* Heuristic!
* If the gap is large relative to the piece we're
* allocating, or using it gives us an invalid block
* number, then just use the start of the next block
* offset by our length.
*/
if (gotdiff <= XFS_ALLOC_GAP_UNITS * ap->alen &&
ISVALID(gotbno - gotdiff, gotbno))
gotbno -= adjust;
else if (ISVALID(gotbno - ap->alen, gotbno)) {
gotbno -= ap->alen;
gotdiff += adjust - ap->alen;
} else
gotdiff += adjust;
/*
* If the firstblock forbids it, can't use it,
* must use default.
*/
if (!rt && !nullfb &&
XFS_FSB_TO_AGNO(mp, gotbno) != fb_agno)
gotbno = NULLFSBLOCK;
}
/*
* No next block, just default.
*/
else
gotbno = NULLFSBLOCK;
/*
* If both valid, pick the better one, else the only good
* one, else ap->rval is already set (to 0 or the inode block).
*/
if (prevbno != NULLFSBLOCK && gotbno != NULLFSBLOCK)
ap->rval = prevdiff <= gotdiff ? prevbno : gotbno;
else if (prevbno != NULLFSBLOCK)
ap->rval = prevbno;
else if (