xfs_ialloc_ag_alloc() makes several attempts to allocate a full inode
chunk. If all else fails, reduce the allocation to inode cluster size
and attempt to allocate a sparse inode chunk.
If sparse chunk allocation succeeds, check whether an inobt record
already exists that can track the chunk. If so, inherit and update the
existing record. Otherwise, insert a new record for the sparse chunk.
Update xfs_inobt_insert_rec() to take the holemask as a parameter and
set the associated field on disk. Convert xfs_inobt_insert() to
xfs_inobt_update_insert() to handle record insertion or update in a
generic fashion. This facilitates the continued use of the same function
for the inobt and finobt.
Signed-off-by: Brian Foster <bfoster@xxxxxxxxxx>
---
fs/xfs/libxfs/xfs_ialloc.c | 105 +++++++++++++++++++++++++++++++++------------
1 file changed, 77 insertions(+), 28 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c
index be57b51..4226b1b 100644
--- a/fs/xfs/libxfs/xfs_ialloc.c
+++ b/fs/xfs/libxfs/xfs_ialloc.c
@@ -121,28 +121,28 @@ xfs_inobt_get_rec(
STATIC int
xfs_inobt_insert_rec(
struct xfs_btree_cur *cur,
+ __uint16_t holemask,
__int32_t freecount,
xfs_inofree_t free,
int *stat)
{
- cur->bc_rec.i.ir_holemask = 0;
+ cur->bc_rec.i.ir_holemask = holemask;
cur->bc_rec.i.ir_freecount = freecount;
cur->bc_rec.i.ir_free = free;
return xfs_btree_insert(cur, stat);
}
/*
- * Insert records describing a newly allocated inode chunk into the inobt.
+ * Update or insert records describing a newly allocated inode chunk into the
+ * specified inobt.
*/
STATIC int
-xfs_inobt_insert(
- struct xfs_mount *mp,
- struct xfs_trans *tp,
- struct xfs_buf *agbp,
- xfs_agino_t newino, /* start inode of record */
- xfs_agino_t count, /* inode count */
- xfs_inofree_t free, /* free mask */
- xfs_btnum_t btnum)
+xfs_inobt_update_insert(
+ struct xfs_mount *mp,
+ struct xfs_trans *tp,
+ struct xfs_buf *agbp,
+ struct xfs_inobt_rec_incore *rec, /* record to update/insert */
+ xfs_btnum_t btnum)
{
struct xfs_btree_cur *cur;
struct xfs_agi *agi = XFS_BUF_TO_AGI(agbp);
@@ -152,23 +152,25 @@ xfs_inobt_insert(
cur = xfs_inobt_init_cursor(mp, tp, agbp, agno, btnum);
- error = xfs_inobt_lookup(cur, newino, XFS_LOOKUP_EQ, &i);
- if (error) {
- xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
- return error;
- }
- ASSERT(i == 0);
-
- error = xfs_inobt_insert_rec(cur, count, free, &i);
- if (error) {
- xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
- return error;
+ error = xfs_inobt_lookup(cur, rec->ir_startino, XFS_LOOKUP_EQ, &i);
+ if (i == 1) {
+ error = xfs_inobt_update(cur, rec);
+ if (error)
+ goto error;
+ } else {
+ error = xfs_inobt_insert_rec(cur, rec->ir_holemask,
+ rec->ir_freecount, rec->ir_free, &i);
+ if (error)
+ goto error;
+ ASSERT(i == 1);
}
- ASSERT(i == 1);
xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
-
return 0;
+
+error:
+ xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
+ return error;
}
/*
@@ -423,6 +425,10 @@ xfs_ialloc_ag_alloc(
xfs_agino_t newlen; /* new number of inodes */
int isaligned = 0; /* inode allocation at stripe unit */
/* boundary */
+ uint16_t allocmask = (uint16_t) -1; /* init. to full chunk */
+ struct xfs_inobt_rec_incore rec;
+ int offset;
+
struct xfs_perag *pag;
memset(&args, 0, sizeof(args));
@@ -538,6 +544,28 @@ xfs_ialloc_ag_alloc(
return error;
}
+ /*
+ * Finally, try a sparse allocation if the filesystem supports it.
+ */
+ if (xfs_sb_version_hassparseinodes(&args.mp->m_sb) &&
+ args.fsbno == NULLFSBLOCK) {
+ args.type = XFS_ALLOCTYPE_NEAR_BNO;
+ args.agbno = be32_to_cpu(agi->agi_root);
+ args.fsbno = XFS_AGB_TO_FSB(args.mp, agno, args.agbno);
+ args.alignment = xfs_ialloc_cluster_alignment(args.mp);
+
+ /* allocate sparse regions in cluster granularity */
+ args.minlen = xfs_ialloc_cluster_alignment(args.mp);
+ args.maxlen = args.minlen;
+
+ error = xfs_alloc_vextent(&args);
+ if (error)
+ return error;
+
+ newlen = args.len << args.mp->m_sb.sb_inopblog;
+ allocmask = (1 << (newlen / XFS_INODES_PER_SPCHUNK)) - 1;
+ }
+
if (args.fsbno == NULLFSBLOCK) {
*alloc = 0;
return 0;
@@ -572,14 +600,34 @@ xfs_ialloc_ag_alloc(
/*
* Insert records describing the new inode chunk into the btrees.
*/
- error = xfs_inobt_insert(args.mp, tp, agbp, newino, newlen,
- XFS_INOBT_ALL_FREE, XFS_BTNUM_INO);
+ error = xfs_spchunk_has_record(args.mp, tp, agbp, newino, newlen,
+ XFS_BTNUM_INO, &rec);
+ if (error)
+ return error;
+ if (rec.ir_startino == NULLAGINO) {
+ /* no existing record, set all fields */
+ rec.ir_startino = newino;
+ rec.ir_holemask = ~allocmask;
+ rec.ir_freecount = newlen;
+ rec.ir_free = XFS_INOBT_ALL_FREE;
+ } else {
+ /* we already have a record, update it */
+ offset = newino - rec.ir_startino;
+ ASSERT(offset % XFS_INODES_PER_SPCHUNK == 0);
+
+ allocmask <<= offset / XFS_INODES_PER_SPCHUNK;
+
+ rec.ir_freecount += newlen;
+ rec.ir_holemask &= ~allocmask;
+ }
+
+ error = xfs_inobt_update_insert(args.mp, tp, agbp, &rec, XFS_BTNUM_INO);
if (error)
return error;
if (xfs_sb_version_hasfinobt(&args.mp->m_sb)) {
- error = xfs_inobt_insert(args.mp, tp, agbp, newino, newlen,
- XFS_INOBT_ALL_FREE, XFS_BTNUM_FINO);
+ error = xfs_inobt_update_insert(args.mp, tp, agbp, &rec,
+ XFS_BTNUM_FINO);
if (error)
return error;
}
@@ -1644,7 +1692,8 @@ xfs_difree_finobt(
*/
XFS_WANT_CORRUPTED_GOTO(ibtrec->ir_freecount == 1, error);
- error = xfs_inobt_insert_rec(cur, ibtrec->ir_freecount,
+ error = xfs_inobt_insert_rec(cur, ibtrec->ir_holemask,
+ ibtrec->ir_freecount,
ibtrec->ir_free, &i);
if (error)
goto error;
--
1.8.3.1
|