aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_ialloc.c
diff options
context:
space:
mode:
authorDave Chinner <david@fromorbit.com>2010-01-11 06:47:44 -0500
committerAlex Elder <aelder@sgi.com>2010-01-15 16:33:52 -0500
commit1c1c6ebcf5284aee4910f3b906ac90c20e510c82 (patch)
treebbcf74752bf7bc058a5c5bdd6bd03090c845b041 /fs/xfs/xfs_ialloc.c
parent44b56e0a1aed522a10051645e85d300e10926fd3 (diff)
xfs: Replace per-ag array with a radix tree
The use of an array for the per-ag structures requires reallocation of the array when growing the filesystem. This requires locking access to the array to avoid use after free situations, and the locking is difficult to get right. To avoid needing to reallocate an array, change the per-ag structures to an allocated object per ag and index them using a tree structure. The AGs are always densely indexed (hence the use of an array), but the number supported is 2^32 and lookups tend to be random and hence indexing needs to scale. A simple choice is a radix tree - it works well with this sort of index. This change also removes another large contiguous allocation from the mount/growfs path in XFS. The growing process now needs to change to only initialise the new AGs required for the extra space, and as such only needs to exclusively lock the tree for inserts. The rest of the code only needs to lock the tree while doing lookups, and hence this will remove all the deadlocks that currently occur on the m_perag_lock as it is now an innermost lock. The lock is also changed to a spinlock from a read/write lock as the hold time is now extremely short. To complete the picture, the per-ag structures will need to be reference counted to ensure that we don't free/modify them while they are still in use. This will be done in subsequent patch. Signed-off-by: Dave Chinner <david@fromorbit.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Alex Elder <aelder@sgi.com>
Diffstat (limited to 'fs/xfs/xfs_ialloc.c')
-rw-r--r--fs/xfs/xfs_ialloc.c25
1 files changed, 2 insertions, 23 deletions
diff --git a/fs/xfs/xfs_ialloc.c b/fs/xfs/xfs_ialloc.c
index 884ee1367f46..52c9d006c0e6 100644
--- a/fs/xfs/xfs_ialloc.c
+++ b/fs/xfs/xfs_ialloc.c
@@ -383,11 +383,9 @@ xfs_ialloc_ag_alloc(
383 newino = XFS_OFFBNO_TO_AGINO(args.mp, args.agbno, 0); 383 newino = XFS_OFFBNO_TO_AGINO(args.mp, args.agbno, 0);
384 be32_add_cpu(&agi->agi_count, newlen); 384 be32_add_cpu(&agi->agi_count, newlen);
385 be32_add_cpu(&agi->agi_freecount, newlen); 385 be32_add_cpu(&agi->agi_freecount, newlen);
386 down_read(&args.mp->m_peraglock);
387 pag = xfs_perag_get(args.mp, agno); 386 pag = xfs_perag_get(args.mp, agno);
388 pag->pagi_freecount += newlen; 387 pag->pagi_freecount += newlen;
389 xfs_perag_put(pag); 388 xfs_perag_put(pag);
390 up_read(&args.mp->m_peraglock);
391 agi->agi_newino = cpu_to_be32(newino); 389 agi->agi_newino = cpu_to_be32(newino);
392 390
393 /* 391 /*
@@ -489,7 +487,6 @@ xfs_ialloc_ag_select(
489 */ 487 */
490 agno = pagno; 488 agno = pagno;
491 flags = XFS_ALLOC_FLAG_TRYLOCK; 489 flags = XFS_ALLOC_FLAG_TRYLOCK;
492 down_read(&mp->m_peraglock);
493 for (;;) { 490 for (;;) {
494 pag = xfs_perag_get(mp, agno); 491 pag = xfs_perag_get(mp, agno);
495 if (!pag->pagi_init) { 492 if (!pag->pagi_init) {
@@ -531,7 +528,6 @@ xfs_ialloc_ag_select(
531 goto nextag; 528 goto nextag;
532 } 529 }
533 xfs_perag_put(pag); 530 xfs_perag_put(pag);
534 up_read(&mp->m_peraglock);
535 return agbp; 531 return agbp;
536 } 532 }
537 } 533 }
@@ -544,18 +540,14 @@ nextag:
544 * No point in iterating over the rest, if we're shutting 540 * No point in iterating over the rest, if we're shutting
545 * down. 541 * down.
546 */ 542 */
547 if (XFS_FORCED_SHUTDOWN(mp)) { 543 if (XFS_FORCED_SHUTDOWN(mp))
548 up_read(&mp->m_peraglock);
549 return NULL; 544 return NULL;
550 }
551 agno++; 545 agno++;
552 if (agno >= agcount) 546 if (agno >= agcount)
553 agno = 0; 547 agno = 0;
554 if (agno == pagno) { 548 if (agno == pagno) {
555 if (flags == 0) { 549 if (flags == 0)
556 up_read(&mp->m_peraglock);
557 return NULL; 550 return NULL;
558 }
559 flags = 0; 551 flags = 0;
560 } 552 }
561 } 553 }
@@ -777,16 +769,13 @@ nextag:
777 *inop = NULLFSINO; 769 *inop = NULLFSINO;
778 return noroom ? ENOSPC : 0; 770 return noroom ? ENOSPC : 0;
779 } 771 }
780 down_read(&mp->m_peraglock);
781 pag = xfs_perag_get(mp, tagno); 772 pag = xfs_perag_get(mp, tagno);
782 if (pag->pagi_inodeok == 0) { 773 if (pag->pagi_inodeok == 0) {
783 xfs_perag_put(pag); 774 xfs_perag_put(pag);
784 up_read(&mp->m_peraglock);
785 goto nextag; 775 goto nextag;
786 } 776 }
787 error = xfs_ialloc_read_agi(mp, tp, tagno, &agbp); 777 error = xfs_ialloc_read_agi(mp, tp, tagno, &agbp);
788 xfs_perag_put(pag); 778 xfs_perag_put(pag);
789 up_read(&mp->m_peraglock);
790 if (error) 779 if (error)
791 goto nextag; 780 goto nextag;
792 agi = XFS_BUF_TO_AGI(agbp); 781 agi = XFS_BUF_TO_AGI(agbp);
@@ -1015,9 +1004,7 @@ alloc_inode:
1015 goto error0; 1004 goto error0;
1016 be32_add_cpu(&agi->agi_freecount, -1); 1005 be32_add_cpu(&agi->agi_freecount, -1);
1017 xfs_ialloc_log_agi(tp, agbp, XFS_AGI_FREECOUNT); 1006 xfs_ialloc_log_agi(tp, agbp, XFS_AGI_FREECOUNT);
1018 down_read(&mp->m_peraglock);
1019 pag->pagi_freecount--; 1007 pag->pagi_freecount--;
1020 up_read(&mp->m_peraglock);
1021 1008
1022 error = xfs_check_agi_freecount(cur, agi); 1009 error = xfs_check_agi_freecount(cur, agi);
1023 if (error) 1010 if (error)
@@ -1100,9 +1087,7 @@ xfs_difree(
1100 /* 1087 /*
1101 * Get the allocation group header. 1088 * Get the allocation group header.
1102 */ 1089 */
1103 down_read(&mp->m_peraglock);
1104 error = xfs_ialloc_read_agi(mp, tp, agno, &agbp); 1090 error = xfs_ialloc_read_agi(mp, tp, agno, &agbp);
1105 up_read(&mp->m_peraglock);
1106 if (error) { 1091 if (error) {
1107 cmn_err(CE_WARN, 1092 cmn_err(CE_WARN,
1108 "xfs_difree: xfs_ialloc_read_agi() returned an error %d on %s. Returning error.", 1093 "xfs_difree: xfs_ialloc_read_agi() returned an error %d on %s. Returning error.",
@@ -1169,11 +1154,9 @@ xfs_difree(
1169 be32_add_cpu(&agi->agi_count, -ilen); 1154 be32_add_cpu(&agi->agi_count, -ilen);
1170 be32_add_cpu(&agi->agi_freecount, -(ilen - 1)); 1155 be32_add_cpu(&agi->agi_freecount, -(ilen - 1));
1171 xfs_ialloc_log_agi(tp, agbp, XFS_AGI_COUNT | XFS_AGI_FREECOUNT); 1156 xfs_ialloc_log_agi(tp, agbp, XFS_AGI_COUNT | XFS_AGI_FREECOUNT);
1172 down_read(&mp->m_peraglock);
1173 pag = xfs_perag_get(mp, agno); 1157 pag = xfs_perag_get(mp, agno);
1174 pag->pagi_freecount -= ilen - 1; 1158 pag->pagi_freecount -= ilen - 1;
1175 xfs_perag_put(pag); 1159 xfs_perag_put(pag);
1176 up_read(&mp->m_peraglock);
1177 xfs_trans_mod_sb(tp, XFS_TRANS_SB_ICOUNT, -ilen); 1160 xfs_trans_mod_sb(tp, XFS_TRANS_SB_ICOUNT, -ilen);
1178 xfs_trans_mod_sb(tp, XFS_TRANS_SB_IFREE, -(ilen - 1)); 1161 xfs_trans_mod_sb(tp, XFS_TRANS_SB_IFREE, -(ilen - 1));
1179 1162
@@ -1202,11 +1185,9 @@ xfs_difree(
1202 */ 1185 */
1203 be32_add_cpu(&agi->agi_freecount, 1); 1186 be32_add_cpu(&agi->agi_freecount, 1);
1204 xfs_ialloc_log_agi(tp, agbp, XFS_AGI_FREECOUNT); 1187 xfs_ialloc_log_agi(tp, agbp, XFS_AGI_FREECOUNT);
1205 down_read(&mp->m_peraglock);
1206 pag = xfs_perag_get(mp, agno); 1188 pag = xfs_perag_get(mp, agno);
1207 pag->pagi_freecount++; 1189 pag->pagi_freecount++;
1208 xfs_perag_put(pag); 1190 xfs_perag_put(pag);
1209 up_read(&mp->m_peraglock);
1210 xfs_trans_mod_sb(tp, XFS_TRANS_SB_IFREE, 1); 1191 xfs_trans_mod_sb(tp, XFS_TRANS_SB_IFREE, 1);
1211 } 1192 }
1212 1193
@@ -1328,9 +1309,7 @@ xfs_imap(
1328 xfs_buf_t *agbp; /* agi buffer */ 1309 xfs_buf_t *agbp; /* agi buffer */
1329 int i; /* temp state */ 1310 int i; /* temp state */
1330 1311
1331 down_read(&mp->m_peraglock);
1332 error = xfs_ialloc_read_agi(mp, tp, agno, &agbp); 1312 error = xfs_ialloc_read_agi(mp, tp, agno, &agbp);
1333 up_read(&mp->m_peraglock);
1334 if (error) { 1313 if (error) {
1335 xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: " 1314 xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: "
1336 "xfs_ialloc_read_agi() returned " 1315 "xfs_ialloc_read_agi() returned "