diff options
Diffstat (limited to 'fs/xfs/linux-2.6/xfs_sync.c')
| -rw-r--r-- | fs/xfs/linux-2.6/xfs_sync.c | 116 | 
1 files changed, 107 insertions, 9 deletions
diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c index 05cd85317f6..a427c638d90 100644 --- a/fs/xfs/linux-2.6/xfs_sync.c +++ b/fs/xfs/linux-2.6/xfs_sync.c @@ -95,7 +95,8 @@ xfs_inode_ag_walk(  					   struct xfs_perag *pag, int flags),  	int			flags,  	int			tag, -	int			exclusive) +	int			exclusive, +	int			*nr_to_scan)  {  	uint32_t		first_index;  	int			last_error = 0; @@ -134,7 +135,7 @@ restart:  		if (error == EFSCORRUPTED)  			break; -	} while (1); +	} while ((*nr_to_scan)--);  	if (skipped) {  		delay(1); @@ -150,12 +151,15 @@ xfs_inode_ag_iterator(  					   struct xfs_perag *pag, int flags),  	int			flags,  	int			tag, -	int			exclusive) +	int			exclusive, +	int			*nr_to_scan)  {  	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; @@ -165,14 +169,18 @@ xfs_inode_ag_iterator(  			continue;  		}  		error = xfs_inode_ag_walk(mp, pag, execute, flags, tag, -						exclusive); +						exclusive, &nr);  		xfs_perag_put(pag);  		if (error) {  			last_error = error;  			if (error == EFSCORRUPTED)  				break;  		} +		if (nr <= 0) +			break;  	} +	if (nr_to_scan) +		*nr_to_scan = nr;  	return XFS_ERROR(last_error);  } @@ -291,7 +299,7 @@ xfs_sync_data(  	ASSERT((flags & ~(SYNC_TRYLOCK|SYNC_WAIT)) == 0);  	error = xfs_inode_ag_iterator(mp, xfs_sync_inode_data, flags, -				      XFS_ICI_NO_TAG, 0); +				      XFS_ICI_NO_TAG, 0, NULL);  	if (error)  		return XFS_ERROR(error); @@ -310,7 +318,7 @@ xfs_sync_attr(  	ASSERT((flags & ~SYNC_WAIT) == 0);  	return xfs_inode_ag_iterator(mp, xfs_sync_inode_attr, flags, -				     XFS_ICI_NO_TAG, 0); +				     XFS_ICI_NO_TAG, 0, NULL);  }  STATIC int @@ -673,6 +681,7 @@ __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); +	pag->pag_ici_reclaimable++;  }  /* @@ -705,6 +714,7 @@ __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--;  }  /* @@ -820,10 +830,10 @@ xfs_reclaim_inode(  	 * call into reclaim to find it in a clean state instead of waiting for  	 * it now. We also don't return errors here - if the error is transient  	 * then the next reclaim pass will flush the inode, and if the error -	 * is permanent then the next sync reclaim will relcaim the inode and +	 * is permanent then the next sync reclaim will reclaim the inode and  	 * pass on the error.  	 */ -	if (error && !XFS_FORCED_SHUTDOWN(ip->i_mount)) { +	if (error && error != EAGAIN && !XFS_FORCED_SHUTDOWN(ip->i_mount)) {  		xfs_fs_cmn_err(CE_WARN, ip->i_mount,  			"inode 0x%llx background reclaim flush failed with %d",  			(long long)ip->i_ino, error); @@ -854,5 +864,93 @@ xfs_reclaim_inodes(  	int		mode)  {  	return xfs_inode_ag_iterator(mp, xfs_reclaim_inode, mode, -					XFS_ICI_RECLAIM_TAG, 1); +					XFS_ICI_RECLAIM_TAG, 1, NULL); +} + +/* + * 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( +	int		nr_to_scan, +	gfp_t		gfp_mask) +{ +	struct xfs_mount *mp; +	struct xfs_perag *pag; +	xfs_agnumber_t	ag; +	int		reclaimable = 0; + +	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_ICI_RECLAIM_TAG, 1, &nr_to_scan); +			if (nr_to_scan <= 0) +				break; +		} +		up_read(&xfs_mount_list_lock); +	} + +	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); +			if (!pag->pag_ici_init) { +				xfs_perag_put(pag); +				continue; +			} +			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); +} + +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);  }  |