From: Alex Lyakas Date: Wed, 18 May 2016 04:01:52 +0000 (+1000) Subject: xfs: optimise xfs_iext_destroy X-Git-Url: http://drtracing.org/?a=commitdiff_plain;h=32b43ab6fb983e5a117048443e628c235cd2c5bd;p=deliverable%2Flinux.git xfs: optimise xfs_iext_destroy When unmounting XFS, we call: xfs_inode_free => xfs_idestroy_fork => xfs_iext_destroy This goes over the whole indirection array and calls xfs_iext_irec_remove for each one of the erps (from the last one to the first one). As a result, we keep shrinking (reallocating actually) the indirection array until we shrink out all of its elements. When we have files with huge numbers of extents, umount takes 30-80 sec, depending on the amount of files that XFS loaded and the amount of indirection entries of each file. The unmount stack looks like: [] xfs_iext_realloc_indirect+0x40/0x60 [xfs] [] xfs_iext_irec_remove+0xee/0xf0 [xfs] [] xfs_iext_destroy+0x3d/0xb0 [xfs] [] xfs_idestroy_fork+0xb6/0xf0 [xfs] [] xfs_inode_free+0xb2/0xc0 [xfs] [] xfs_reclaim_inode+0x250/0x340 [xfs] [] xfs_reclaim_inodes_ag+0x233/0x370 [xfs] [] xfs_reclaim_inodes+0x1d/0x20 [xfs] [] xfs_unmountfs+0x7b/0x1a0 [xfs] [] xfs_fs_put_super+0x2d/0x70 [xfs] [] generic_shutdown_super+0x76/0x100 [] kill_block_super+0x27/0x70 [] deactivate_locked_super+0x49/0x60 [] deactivate_super+0x4e/0x70 [] cleanup_mnt+0x43/0x90 [] __cleanup_mnt+0x12/0x20 [] task_work_run+0xa7/0xe0 [] do_notify_resume+0x97/0xb0 [] int_signal+0x12/0x17 Further, this reallocation prevents us from freeing the extent list from a RCU callback as allocation can block. Hence if the extent list is in indirect format, optimise the freeing of the extent list to only use kmem_free calls by freeing entire extent buffer pages at a time, rather than extent by extent. [dchinner: simplified freeing loop based on Christoph's suggestion] Signed-off-by: Alex Lyakas Reviewed-by: Dave Chinner Signed-off-by: Dave Chinner --- diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c index 11faf7df14c8..8fe6617a8653 100644 --- a/fs/xfs/libxfs/xfs_inode_fork.c +++ b/fs/xfs/libxfs/xfs_inode_fork.c @@ -1496,6 +1496,24 @@ xfs_iext_indirect_to_direct( } } +/* + * Remove all records from the indirection array. + */ +STATIC void +xfs_iext_irec_remove_all( + struct xfs_ifork *ifp) +{ + int nlists; + int i; + + ASSERT(ifp->if_flags & XFS_IFEXTIREC); + nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ; + for (i = 0; i < nlists; i++) + kmem_free(ifp->if_u1.if_ext_irec[i].er_extbuf); + kmem_free(ifp->if_u1.if_ext_irec); + ifp->if_flags &= ~XFS_IFEXTIREC; +} + /* * Free incore file extents. */ @@ -1504,14 +1522,7 @@ xfs_iext_destroy( xfs_ifork_t *ifp) /* inode fork pointer */ { if (ifp->if_flags & XFS_IFEXTIREC) { - int erp_idx; - int nlists; - - nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ; - for (erp_idx = nlists - 1; erp_idx >= 0 ; erp_idx--) { - xfs_iext_irec_remove(ifp, erp_idx); - } - ifp->if_flags &= ~XFS_IFEXTIREC; + xfs_iext_irec_remove_all(ifp); } else if (ifp->if_real_bytes) { kmem_free(ifp->if_u1.if_extents); } else if (ifp->if_bytes) {