aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_mount.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_mount.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_mount.c')
-rw-r--r--fs/xfs/xfs_mount.c63
1 files changed, 48 insertions, 15 deletions
diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c
index 9055b60730d0..c04dd83cb57c 100644
--- a/fs/xfs/xfs_mount.c
+++ b/fs/xfs/xfs_mount.c
@@ -209,13 +209,16 @@ STATIC void
209xfs_free_perag( 209xfs_free_perag(
210 xfs_mount_t *mp) 210 xfs_mount_t *mp)
211{ 211{
212 if (mp->m_perag) { 212 xfs_agnumber_t agno;
213 int agno; 213 struct xfs_perag *pag;
214 214
215 for (agno = 0; agno < mp->m_maxagi; agno++) 215 for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
216 if (mp->m_perag[agno].pagb_list) 216 spin_lock(&mp->m_perag_lock);
217 kmem_free(mp->m_perag[agno].pagb_list); 217 pag = radix_tree_delete(&mp->m_perag_tree, agno);
218 kmem_free(mp->m_perag); 218 spin_unlock(&mp->m_perag_lock);
219 ASSERT(pag);
220 kmem_free(pag->pagb_list);
221 kmem_free(pag);
219 } 222 }
220} 223}
221 224
@@ -389,10 +392,11 @@ xfs_initialize_perag_icache(
389 } 392 }
390} 393}
391 394
392xfs_agnumber_t 395int
393xfs_initialize_perag( 396xfs_initialize_perag(
394 xfs_mount_t *mp, 397 xfs_mount_t *mp,
395 xfs_agnumber_t agcount) 398 xfs_agnumber_t agcount,
399 xfs_agnumber_t *maxagi)
396{ 400{
397 xfs_agnumber_t index, max_metadata; 401 xfs_agnumber_t index, max_metadata;
398 xfs_perag_t *pag; 402 xfs_perag_t *pag;
@@ -405,6 +409,33 @@ xfs_initialize_perag(
405 agino = XFS_OFFBNO_TO_AGINO(mp, sbp->sb_agblocks - 1, 0); 409 agino = XFS_OFFBNO_TO_AGINO(mp, sbp->sb_agblocks - 1, 0);
406 ino = XFS_AGINO_TO_INO(mp, agcount - 1, agino); 410 ino = XFS_AGINO_TO_INO(mp, agcount - 1, agino);
407 411
412 /*
413 * Walk the current per-ag tree so we don't try to initialise AGs
414 * that already exist (growfs case). Allocate and insert all the
415 * AGs we don't find ready for initialisation.
416 */
417 for (index = 0; index < agcount; index++) {
418 pag = xfs_perag_get(mp, index);
419 if (pag) {
420 xfs_perag_put(pag);
421 continue;
422 }
423 pag = kmem_zalloc(sizeof(*pag), KM_MAYFAIL);
424 if (!pag)
425 return -ENOMEM;
426 if (radix_tree_preload(GFP_NOFS))
427 return -ENOMEM;
428 spin_lock(&mp->m_perag_lock);
429 if (radix_tree_insert(&mp->m_perag_tree, index, pag)) {
430 BUG();
431 spin_unlock(&mp->m_perag_lock);
432 kmem_free(pag);
433 return -EEXIST;
434 }
435 spin_unlock(&mp->m_perag_lock);
436 radix_tree_preload_end();
437 }
438
408 /* Clear the mount flag if no inode can overflow 32 bits 439 /* Clear the mount flag if no inode can overflow 32 bits
409 * on this filesystem, or if specifically requested.. 440 * on this filesystem, or if specifically requested..
410 */ 441 */
@@ -454,7 +485,9 @@ xfs_initialize_perag(
454 xfs_perag_put(pag); 485 xfs_perag_put(pag);
455 } 486 }
456 } 487 }
457 return index; 488 if (maxagi)
489 *maxagi = index;
490 return 0;
458} 491}
459 492
460void 493void
@@ -1155,13 +1188,13 @@ xfs_mountfs(
1155 /* 1188 /*
1156 * Allocate and initialize the per-ag data. 1189 * Allocate and initialize the per-ag data.
1157 */ 1190 */
1158 init_rwsem(&mp->m_peraglock); 1191 spin_lock_init(&mp->m_perag_lock);
1159 mp->m_perag = kmem_zalloc(sbp->sb_agcount * sizeof(xfs_perag_t), 1192 INIT_RADIX_TREE(&mp->m_perag_tree, GFP_NOFS);
1160 KM_MAYFAIL); 1193 error = xfs_initialize_perag(mp, sbp->sb_agcount, &mp->m_maxagi);
1161 if (!mp->m_perag) 1194 if (error) {
1195 cmn_err(CE_WARN, "XFS: Failed per-ag init: %d", error);
1162 goto out_remove_uuid; 1196 goto out_remove_uuid;
1163 1197 }
1164 mp->m_maxagi = xfs_initialize_perag(mp, sbp->sb_agcount);
1165 1198
1166 if (!sbp->sb_logblocks) { 1199 if (!sbp->sb_logblocks) {
1167 cmn_err(CE_WARN, "XFS: no log defined"); 1200 cmn_err(CE_WARN, "XFS: no log defined");