Commit bd169565 authored by Dave Chinner's avatar Dave Chinner Committed by Felix Blyakher

xfs: speed up free inode search

Don't search too far - abort if it is outside a certain radius and simply do
a linear search for the first free inode.  In AGs with a million inodes this
can speed up allocation speed by 3-4x.

[hch: ported to the new xfs_ialloc.c world order]
Signed-off-by: default avatarDave Chinner <dgc@sgi.com>
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarAlex Elder <aelder@sgi.com>
Signed-off-by: default avatarFelix Blyakher <felixb@sgi.com>
parent 21875505
...@@ -198,6 +198,15 @@ typedef struct xfs_perag ...@@ -198,6 +198,15 @@ typedef struct xfs_perag
xfs_agino_t pagi_count; /* number of allocated inodes */ xfs_agino_t pagi_count; /* number of allocated inodes */
int pagb_count; /* pagb slots in use */ int pagb_count; /* pagb slots in use */
xfs_perag_busy_t *pagb_list; /* unstable blocks */ xfs_perag_busy_t *pagb_list; /* unstable blocks */
/*
* Inode allocation search lookup optimisation.
* If the pagino matches, the search for new inodes
* doesn't need to search the near ones again straight away
*/
xfs_agino_t pagl_pagino;
xfs_agino_t pagl_leftrec;
xfs_agino_t pagl_rightrec;
#ifdef __KERNEL__ #ifdef __KERNEL__
spinlock_t pagb_lock; /* lock for pagb_list */ spinlock_t pagb_lock; /* lock for pagb_list */
......
...@@ -587,6 +587,30 @@ xfs_ialloc_next_rec( ...@@ -587,6 +587,30 @@ xfs_ialloc_next_rec(
return 0; return 0;
} }
STATIC int
xfs_ialloc_get_rec(
struct xfs_btree_cur *cur,
xfs_agino_t agino,
xfs_inobt_rec_incore_t *rec,
int *done,
int left)
{
int error;
int i;
error = xfs_inobt_lookup(cur, agino, XFS_LOOKUP_EQ, &i);
if (error)
return error;
*done = !i;
if (i) {
error = xfs_inobt_get_rec(cur, rec, &i);
if (error)
return error;
XFS_WANT_CORRUPTED_RETURN(i == 1);
}
return 0;
}
/* /*
* Visible inode allocation functions. * Visible inode allocation functions.
...@@ -766,6 +790,8 @@ xfs_dialloc( ...@@ -766,6 +790,8 @@ xfs_dialloc(
*/ */
agno = tagno; agno = tagno;
*IO_agbp = NULL; *IO_agbp = NULL;
restart_pagno:
cur = xfs_inobt_init_cursor(mp, tp, agbp, be32_to_cpu(agi->agi_seqno)); cur = xfs_inobt_init_cursor(mp, tp, agbp, be32_to_cpu(agi->agi_seqno));
/* /*
* If pagino is 0 (this is the root inode allocation) use newino. * If pagino is 0 (this is the root inode allocation) use newino.
...@@ -782,8 +808,10 @@ xfs_dialloc( ...@@ -782,8 +808,10 @@ xfs_dialloc(
* If in the same AG as the parent, try to get near the parent. * If in the same AG as the parent, try to get near the parent.
*/ */
if (pagno == agno) { if (pagno == agno) {
xfs_perag_t *pag = &mp->m_perag[agno];
int doneleft; /* done, to the left */ int doneleft; /* done, to the left */
int doneright; /* done, to the right */ int doneright; /* done, to the right */
int searchdistance = 10;
error = xfs_inobt_lookup(cur, pagino, XFS_LOOKUP_LE, &i); error = xfs_inobt_lookup(cur, pagino, XFS_LOOKUP_LE, &i);
if (error) if (error)
...@@ -813,15 +841,33 @@ xfs_dialloc( ...@@ -813,15 +841,33 @@ xfs_dialloc(
if (error) if (error)
goto error0; goto error0;
/* search left with tcur, back up 1 record */ /*
error = xfs_ialloc_next_rec(tcur, &trec, &doneleft, 1); * Skip to last blocks looked up if same parent inode.
if (error) */
goto error1; if (pagino != NULLAGINO &&
pag->pagl_pagino == pagino &&
pag->pagl_leftrec != NULLAGINO &&
pag->pagl_rightrec != NULLAGINO) {
error = xfs_ialloc_get_rec(tcur, pag->pagl_leftrec,
&trec, &doneleft, 1);
if (error)
goto error1;
/* search right with cur, go forward 1 record. */ error = xfs_ialloc_get_rec(cur, pag->pagl_rightrec,
error = xfs_ialloc_next_rec(cur, &rec, &doneright, 0); &rec, &doneright, 0);
if (error) if (error)
goto error1; goto error1;
} else {
/* search left with tcur, back up 1 record */
error = xfs_ialloc_next_rec(tcur, &trec, &doneleft, 1);
if (error)
goto error1;
/* search right with cur, go forward 1 record. */
error = xfs_ialloc_next_rec(cur, &rec, &doneright, 0);
if (error)
goto error1;
}
/* /*
* Loop until we find an inode chunk with a free inode. * Loop until we find an inode chunk with a free inode.
...@@ -829,6 +875,17 @@ xfs_dialloc( ...@@ -829,6 +875,17 @@ xfs_dialloc(
while (!doneleft || !doneright) { while (!doneleft || !doneright) {
int useleft; /* using left inode chunk this time */ int useleft; /* using left inode chunk this time */
if (!--searchdistance) {
/*
* Not in range - save last search
* location and allocate a new inode
*/
pag->pagl_leftrec = trec.ir_startino;
pag->pagl_rightrec = rec.ir_startino;
pag->pagl_pagino = pagino;
goto newino;
}
/* figure out the closer block if both are valid. */ /* figure out the closer block if both are valid. */
if (!doneleft && !doneright) { if (!doneleft && !doneright) {
useleft = pagino - useleft = pagino -
...@@ -843,12 +900,20 @@ xfs_dialloc( ...@@ -843,12 +900,20 @@ xfs_dialloc(
rec = trec; rec = trec;
xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR); xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
cur = tcur; cur = tcur;
pag->pagl_leftrec = trec.ir_startino;
pag->pagl_rightrec = rec.ir_startino;
pag->pagl_pagino = pagino;
goto alloc_inode; goto alloc_inode;
} }
/* free inodes to the right? */ /* free inodes to the right? */
if (!useleft && rec.ir_freecount) { if (!useleft && rec.ir_freecount) {
xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR); xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
pag->pagl_leftrec = trec.ir_startino;
pag->pagl_rightrec = rec.ir_startino;
pag->pagl_pagino = pagino;
goto alloc_inode; goto alloc_inode;
} }
...@@ -863,14 +928,28 @@ xfs_dialloc( ...@@ -863,14 +928,28 @@ xfs_dialloc(
if (error) if (error)
goto error1; goto error1;
} }
ASSERT(!doneleft || !doneright);
/*
* We've reached the end of the btree. because
* we are only searching a small chunk of the
* btree each search, there is obviously free
* inodes closer to the parent inode than we
* are now. restart the search again.
*/
pag->pagl_pagino = NULLAGINO;
pag->pagl_leftrec = NULLAGINO;
pag->pagl_rightrec = NULLAGINO;
xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
goto restart_pagno;
} }
/* /*
* In a different AG from the parent. * In a different AG from the parent.
* See if the most recently allocated block has any free. * See if the most recently allocated block has any free.
*/ */
else if (be32_to_cpu(agi->agi_newino) != NULLAGINO) { newino:
if (be32_to_cpu(agi->agi_newino) != NULLAGINO) {
error = xfs_inobt_lookup(cur, be32_to_cpu(agi->agi_newino), error = xfs_inobt_lookup(cur, be32_to_cpu(agi->agi_newino),
XFS_LOOKUP_EQ, &i); XFS_LOOKUP_EQ, &i);
if (error) if (error)
...@@ -889,27 +968,27 @@ xfs_dialloc( ...@@ -889,27 +968,27 @@ xfs_dialloc(
goto alloc_inode; goto alloc_inode;
} }
} }
}
/* /*
* None left in the last group, search the whole AG * None left in the last group, search the whole AG
*/ */
error = xfs_inobt_lookup(cur, 0, XFS_LOOKUP_GE, &i); error = xfs_inobt_lookup(cur, 0, XFS_LOOKUP_GE, &i);
if (error)
goto error0;
XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
for (;;) {
error = xfs_inobt_get_rec(cur, &rec, &i);
if (error)
goto error0;
XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
if (rec.ir_freecount > 0)
break;
error = xfs_btree_increment(cur, 0, &i);
if (error) if (error)
goto error0; goto error0;
XFS_WANT_CORRUPTED_GOTO(i == 1, error0); XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
for (;;) {
error = xfs_inobt_get_rec(cur, &rec, &i);
if (error)
goto error0;
XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
if (rec.ir_freecount > 0)
break;
error = xfs_btree_increment(cur, 0, &i);
if (error)
goto error0;
XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
}
} }
alloc_inode: alloc_inode:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment