xfs_ialloc_ag_alloc() makes several attempts to allocate a full inode
chunk. If all else fails, reduce the allocation to the minimum sparse
granularity 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. Create the xfs_inobt_update_insert()
helper to handle the sparse chunk allocation case - insert or update an
existing record depending on whether it already exists.
Signed-off-by: Brian Foster <bfoster@xxxxxxxxxx>
---
fs/xfs/libxfs/xfs_ialloc.c | 149 +++++++++++++++++++++++++++++++++++++++++----
1 file changed, 136 insertions(+), 13 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c
index 6879213..d22dd8a 100644
--- a/fs/xfs/libxfs/xfs_ialloc.c
+++ b/fs/xfs/libxfs/xfs_ialloc.c
@@ -123,12 +123,16 @@ xfs_inobt_get_rec(
STATIC int
xfs_inobt_insert_rec(
struct xfs_btree_cur *cur,
+ __uint16_t holemask,
+ __uint8_t count,
__int32_t freecount,
xfs_inofree_t free,
int *stat)
{
- cur->bc_rec.i.ir_holemask = 0;
- cur->bc_rec.i.ir_count = 0; /* zero for backwards compatibility */
+ ASSERT(count == 0 || xfs_sb_version_hassparseinodes(&cur->bc_mp->m_sb));
+
+ cur->bc_rec.i.ir_holemask = holemask;
+ cur->bc_rec.i.ir_count = count;
cur->bc_rec.i.ir_freecount = freecount;
cur->bc_rec.i.ir_free = free;
return xfs_btree_insert(cur, stat);
@@ -152,6 +156,19 @@ xfs_inobt_insert(
xfs_agino_t thisino;
int i;
int error;
+ uint8_t count;
+
+ /*
+ * Only set ir_count in the inobt record if the sparse inodes feature is
+ * enabled. If disabled, we must maintain backwards compatibility with
+ * the older inobt record format where the current count and holemask
+ * fields map to the higher order bytes of freecount and thus must be
+ * zeroed.
+ */
+ if (xfs_sb_version_hassparseinodes(&mp->m_sb))
+ count = XFS_INODES_PER_CHUNK;
+ else
+ count = 0;
cur = xfs_inobt_init_cursor(mp, tp, agbp, agno, btnum);
@@ -165,7 +182,7 @@ xfs_inobt_insert(
}
ASSERT(i == 0);
- error = xfs_inobt_insert_rec(cur, XFS_INODES_PER_CHUNK,
+ error = xfs_inobt_insert_rec(cur, 0, count,
XFS_INODES_PER_CHUNK,
XFS_INOBT_ALL_FREE, &i);
if (error) {
xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
@@ -175,8 +192,45 @@ xfs_inobt_insert(
}
xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+ return 0;
+}
+
+STATIC int
+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);
+ xfs_agnumber_t agno = be32_to_cpu(agi->agi_seqno);
+ int i;
+ int error;
+
+ cur = xfs_inobt_init_cursor(mp, tp, agbp, agno, btnum);
+
+ 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_count, rec->ir_freecount, rec->ir_free,
+ &i);
+ if (error)
+ goto error;
+ ASSERT(i == 1);
+ }
+ xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
return 0;
+
+error:
+ xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
+ return error;
}
/*
@@ -437,6 +491,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));
@@ -552,6 +610,27 @@ 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 = args.mp->m_sb.sb_inoalignmt;
+
+ args.minlen = args.mp->m_ialloc_min_blks;
+ 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_HOLEMASK_BIT)) - 1;
+ }
+
if (args.fsbno == NULLFSBLOCK) {
*alloc = 0;
return 0;
@@ -583,20 +662,62 @@ xfs_ialloc_ag_alloc(
xfs_perag_put(pag);
agi->agi_newino = cpu_to_be32(newino);
- /*
- * Insert records describing the new inode chunk into the btrees.
- */
- error = xfs_inobt_insert(args.mp, tp, agbp, newino, newlen,
- XFS_BTNUM_INO);
- if (error)
- return error;
+ if (xfs_inobt_issparse(~allocmask)) {
+ /*
+ * We've allocated a sparse chunk...
+ */
+ error = xfs_inobt_rec_exists(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_count = newlen;
+ rec.ir_freecount = newlen;
+ rec.ir_free = XFS_INOBT_ALL_FREE;
+ } else {
+ /* we already have a record, update it */
+ offset = newino - rec.ir_startino;
+ allocmask <<= offset / XFS_INODES_PER_HOLEMASK_BIT;
+
+ ASSERT(offset % XFS_INODES_PER_HOLEMASK_BIT == 0);
+ ASSERT(rec.ir_count + newlen <= XFS_INODES_PER_CHUNK);
+ ASSERT(rec.ir_freecount + newlen <=
+ XFS_INODES_PER_CHUNK);
+
+ rec.ir_count += newlen;
+ rec.ir_freecount += newlen;
+ rec.ir_holemask &= ~allocmask;
+ }
- if (xfs_sb_version_hasfinobt(&args.mp->m_sb)) {
+ 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_update_insert(args.mp, tp, agbp, &rec,
+ XFS_BTNUM_FINO);
+ if (error)
+ return error;
+ }
+ } else {
+ /* full chunk - insert new records to both btrees */
error = xfs_inobt_insert(args.mp, tp, agbp, newino, newlen,
- XFS_BTNUM_FINO);
+ 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_BTNUM_FINO);
+ if (error)
+ return error;
+ }
}
+
/*
* Log allocation group header fields
*/
@@ -1657,7 +1778,9 @@ 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_count,
+ ibtrec->ir_freecount,
ibtrec->ir_free, &i);
if (error)
goto error;
--
1.8.3.1
|