diff options
Diffstat (limited to 'fs/xfs/linux-2.6/xfs_sync.c')
| -rw-r--r-- | fs/xfs/linux-2.6/xfs_sync.c | 130 | 
1 files changed, 77 insertions, 53 deletions
diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c index ef7f0218bcc..a51a07c3a70 100644 --- a/fs/xfs/linux-2.6/xfs_sync.c +++ b/fs/xfs/linux-2.6/xfs_sync.c @@ -144,6 +144,41 @@ restart:  	return last_error;  } +/* + * Select the next per-ag structure to iterate during the walk. The reclaim + * walk is optimised only to walk AGs with reclaimable inodes in them. + */ +static struct xfs_perag * +xfs_inode_ag_iter_next_pag( +	struct xfs_mount	*mp, +	xfs_agnumber_t		*first, +	int			tag) +{ +	struct xfs_perag	*pag = NULL; + +	if (tag == XFS_ICI_RECLAIM_TAG) { +		int found; +		int ref; + +		spin_lock(&mp->m_perag_lock); +		found = radix_tree_gang_lookup_tag(&mp->m_perag_tree, +				(void **)&pag, *first, 1, tag); +		if (found <= 0) { +			spin_unlock(&mp->m_perag_lock); +			return NULL; +		} +		*first = pag->pag_agno + 1; +		/* open coded pag reference increment */ +		ref = atomic_inc_return(&pag->pag_ref); +		spin_unlock(&mp->m_perag_lock); +		trace_xfs_perag_get_reclaim(mp, pag->pag_agno, ref, _RET_IP_); +	} else { +		pag = xfs_perag_get(mp, *first); +		(*first)++; +	} +	return pag; +} +  int  xfs_inode_ag_iterator(  	struct xfs_mount	*mp, @@ -154,16 +189,15 @@ xfs_inode_ag_iterator(  	int			exclusive,  	int			*nr_to_scan)  { +	struct xfs_perag	*pag;  	int			error = 0;  	int			last_error = 0;  	xfs_agnumber_t		ag;  	int			nr;  	nr = nr_to_scan ? *nr_to_scan : INT_MAX; -	for (ag = 0; ag < mp->m_sb.sb_agcount; ag++) { -		struct xfs_perag	*pag; - -		pag = xfs_perag_get(mp, ag); +	ag = 0; +	while ((pag = xfs_inode_ag_iter_next_pag(mp, &ag, tag))) {  		error = xfs_inode_ag_walk(mp, pag, execute, flags, tag,  						exclusive, &nr);  		xfs_perag_put(pag); @@ -640,6 +674,17 @@ __xfs_inode_set_reclaim_tag(  	radix_tree_tag_set(&pag->pag_ici_root,  			   XFS_INO_TO_AGINO(ip->i_mount, ip->i_ino),  			   XFS_ICI_RECLAIM_TAG); + +	if (!pag->pag_ici_reclaimable) { +		/* propagate the reclaim tag up into the perag radix tree */ +		spin_lock(&ip->i_mount->m_perag_lock); +		radix_tree_tag_set(&ip->i_mount->m_perag_tree, +				XFS_INO_TO_AGNO(ip->i_mount, ip->i_ino), +				XFS_ICI_RECLAIM_TAG); +		spin_unlock(&ip->i_mount->m_perag_lock); +		trace_xfs_perag_set_reclaim(ip->i_mount, pag->pag_agno, +							-1, _RET_IP_); +	}  	pag->pag_ici_reclaimable++;  } @@ -674,6 +719,16 @@ __xfs_inode_clear_reclaim_tag(  	radix_tree_tag_clear(&pag->pag_ici_root,  			XFS_INO_TO_AGINO(mp, ip->i_ino), XFS_ICI_RECLAIM_TAG);  	pag->pag_ici_reclaimable--; +	if (!pag->pag_ici_reclaimable) { +		/* clear the reclaim tag from the perag radix tree */ +		spin_lock(&ip->i_mount->m_perag_lock); +		radix_tree_tag_clear(&ip->i_mount->m_perag_tree, +				XFS_INO_TO_AGNO(ip->i_mount, ip->i_ino), +				XFS_ICI_RECLAIM_TAG); +		spin_unlock(&ip->i_mount->m_perag_lock); +		trace_xfs_perag_clear_reclaim(ip->i_mount, pag->pag_agno, +							-1, _RET_IP_); +	}  }  /* @@ -828,83 +883,52 @@ xfs_reclaim_inodes(  /*   * Shrinker infrastructure. - * - * This is all far more complex than it needs to be. It adds a global list of - * mounts because the shrinkers can only call a global context. We need to make - * the shrinkers pass a context to avoid the need for global state.   */ -static LIST_HEAD(xfs_mount_list); -static struct rw_semaphore xfs_mount_list_lock; -  static int  xfs_reclaim_inode_shrink( +	struct shrinker	*shrink,  	int		nr_to_scan,  	gfp_t		gfp_mask)  {  	struct xfs_mount *mp;  	struct xfs_perag *pag;  	xfs_agnumber_t	ag; -	int		reclaimable = 0; +	int		reclaimable; +	mp = container_of(shrink, struct xfs_mount, m_inode_shrink);  	if (nr_to_scan) {  		if (!(gfp_mask & __GFP_FS))  			return -1; -		down_read(&xfs_mount_list_lock); -		list_for_each_entry(mp, &xfs_mount_list, m_mplist) { -			xfs_inode_ag_iterator(mp, xfs_reclaim_inode, 0, +		xfs_inode_ag_iterator(mp, xfs_reclaim_inode, 0,  					XFS_ICI_RECLAIM_TAG, 1, &nr_to_scan); -			if (nr_to_scan <= 0) -				break; -		} -		up_read(&xfs_mount_list_lock); -	} +		/* if we don't exhaust the scan, don't bother coming back */ +		if (nr_to_scan > 0) +			return -1; +       } -	down_read(&xfs_mount_list_lock); -	list_for_each_entry(mp, &xfs_mount_list, m_mplist) { -		for (ag = 0; ag < mp->m_sb.sb_agcount; ag++) { -			pag = xfs_perag_get(mp, ag); -			reclaimable += pag->pag_ici_reclaimable; -			xfs_perag_put(pag); -		} +	reclaimable = 0; +	ag = 0; +	while ((pag = xfs_inode_ag_iter_next_pag(mp, &ag, +					XFS_ICI_RECLAIM_TAG))) { +		reclaimable += pag->pag_ici_reclaimable; +		xfs_perag_put(pag);  	} -	up_read(&xfs_mount_list_lock);  	return reclaimable;  } -static struct shrinker xfs_inode_shrinker = { -	.shrink = xfs_reclaim_inode_shrink, -	.seeks = DEFAULT_SEEKS, -}; - -void __init -xfs_inode_shrinker_init(void) -{ -	init_rwsem(&xfs_mount_list_lock); -	register_shrinker(&xfs_inode_shrinker); -} - -void -xfs_inode_shrinker_destroy(void) -{ -	ASSERT(list_empty(&xfs_mount_list)); -	unregister_shrinker(&xfs_inode_shrinker); -} -  void  xfs_inode_shrinker_register(  	struct xfs_mount	*mp)  { -	down_write(&xfs_mount_list_lock); -	list_add_tail(&mp->m_mplist, &xfs_mount_list); -	up_write(&xfs_mount_list_lock); +	mp->m_inode_shrink.shrink = xfs_reclaim_inode_shrink; +	mp->m_inode_shrink.seeks = DEFAULT_SEEKS; +	register_shrinker(&mp->m_inode_shrink);  }  void  xfs_inode_shrinker_unregister(  	struct xfs_mount	*mp)  { -	down_write(&xfs_mount_list_lock); -	list_del(&mp->m_mplist); -	up_write(&xfs_mount_list_lock); +	unregister_shrinker(&mp->m_inode_shrink);  }  |