diff options
author | Dave Chinner <dchinner@redhat.com> | 2010-04-28 19:55:50 -0400 |
---|---|---|
committer | Alex Elder <aelder@sgi.com> | 2010-04-29 17:22:13 -0400 |
commit | 9bf729c0af67897ea8498ce17c29b0683f7f2028 (patch) | |
tree | 9e6350fd9da5aef0b53ddeddd82f618ae16bf931 /fs/xfs/linux-2.6/xfs_sync.c | |
parent | 79dba2eaa771c3173957eccfd288e0e0d12e4d3f (diff) |
xfs: add a shrinker to background inode reclaim
On low memory boxes or those with highmem, kernel can OOM before the
background reclaims inodes via xfssyncd. Add a shrinker to run inode
reclaim so that it inode reclaim is expedited when memory is low.
This is more complex than it needs to be because the VM folk don't
want a context added to the shrinker infrastructure. Hence we need
to add a global list of XFS mount structures so the shrinker can
traverse them.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Diffstat (limited to 'fs/xfs/linux-2.6/xfs_sync.c')
-rw-r--r-- | fs/xfs/linux-2.6/xfs_sync.c | 112 |
1 files changed, 105 insertions, 7 deletions
diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c index fd9698215759..a427c638d909 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( | |||
95 | struct xfs_perag *pag, int flags), | 95 | struct xfs_perag *pag, int flags), |
96 | int flags, | 96 | int flags, |
97 | int tag, | 97 | int tag, |
98 | int exclusive) | 98 | int exclusive, |
99 | int *nr_to_scan) | ||
99 | { | 100 | { |
100 | uint32_t first_index; | 101 | uint32_t first_index; |
101 | int last_error = 0; | 102 | int last_error = 0; |
@@ -134,7 +135,7 @@ restart: | |||
134 | if (error == EFSCORRUPTED) | 135 | if (error == EFSCORRUPTED) |
135 | break; | 136 | break; |
136 | 137 | ||
137 | } while (1); | 138 | } while ((*nr_to_scan)--); |
138 | 139 | ||
139 | if (skipped) { | 140 | if (skipped) { |
140 | delay(1); | 141 | delay(1); |
@@ -150,12 +151,15 @@ xfs_inode_ag_iterator( | |||
150 | struct xfs_perag *pag, int flags), | 151 | struct xfs_perag *pag, int flags), |
151 | int flags, | 152 | int flags, |
152 | int tag, | 153 | int tag, |
153 | int exclusive) | 154 | int exclusive, |
155 | int *nr_to_scan) | ||
154 | { | 156 | { |
155 | int error = 0; | 157 | int error = 0; |
156 | int last_error = 0; | 158 | int last_error = 0; |
157 | xfs_agnumber_t ag; | 159 | xfs_agnumber_t ag; |
160 | int nr; | ||
158 | 161 | ||
162 | nr = nr_to_scan ? *nr_to_scan : INT_MAX; | ||
159 | for (ag = 0; ag < mp->m_sb.sb_agcount; ag++) { | 163 | for (ag = 0; ag < mp->m_sb.sb_agcount; ag++) { |
160 | struct xfs_perag *pag; | 164 | struct xfs_perag *pag; |
161 | 165 | ||
@@ -165,14 +169,18 @@ xfs_inode_ag_iterator( | |||
165 | continue; | 169 | continue; |
166 | } | 170 | } |
167 | error = xfs_inode_ag_walk(mp, pag, execute, flags, tag, | 171 | error = xfs_inode_ag_walk(mp, pag, execute, flags, tag, |
168 | exclusive); | 172 | exclusive, &nr); |
169 | xfs_perag_put(pag); | 173 | xfs_perag_put(pag); |
170 | if (error) { | 174 | if (error) { |
171 | last_error = error; | 175 | last_error = error; |
172 | if (error == EFSCORRUPTED) | 176 | if (error == EFSCORRUPTED) |
173 | break; | 177 | break; |
174 | } | 178 | } |
179 | if (nr <= 0) | ||
180 | break; | ||
175 | } | 181 | } |
182 | if (nr_to_scan) | ||
183 | *nr_to_scan = nr; | ||
176 | return XFS_ERROR(last_error); | 184 | return XFS_ERROR(last_error); |
177 | } | 185 | } |
178 | 186 | ||
@@ -291,7 +299,7 @@ xfs_sync_data( | |||
291 | ASSERT((flags & ~(SYNC_TRYLOCK|SYNC_WAIT)) == 0); | 299 | ASSERT((flags & ~(SYNC_TRYLOCK|SYNC_WAIT)) == 0); |
292 | 300 | ||
293 | error = xfs_inode_ag_iterator(mp, xfs_sync_inode_data, flags, | 301 | error = xfs_inode_ag_iterator(mp, xfs_sync_inode_data, flags, |
294 | XFS_ICI_NO_TAG, 0); | 302 | XFS_ICI_NO_TAG, 0, NULL); |
295 | if (error) | 303 | if (error) |
296 | return XFS_ERROR(error); | 304 | return XFS_ERROR(error); |
297 | 305 | ||
@@ -310,7 +318,7 @@ xfs_sync_attr( | |||
310 | ASSERT((flags & ~SYNC_WAIT) == 0); | 318 | ASSERT((flags & ~SYNC_WAIT) == 0); |
311 | 319 | ||
312 | return xfs_inode_ag_iterator(mp, xfs_sync_inode_attr, flags, | 320 | return xfs_inode_ag_iterator(mp, xfs_sync_inode_attr, flags, |
313 | XFS_ICI_NO_TAG, 0); | 321 | XFS_ICI_NO_TAG, 0, NULL); |
314 | } | 322 | } |
315 | 323 | ||
316 | STATIC int | 324 | STATIC int |
@@ -673,6 +681,7 @@ __xfs_inode_set_reclaim_tag( | |||
673 | radix_tree_tag_set(&pag->pag_ici_root, | 681 | radix_tree_tag_set(&pag->pag_ici_root, |
674 | XFS_INO_TO_AGINO(ip->i_mount, ip->i_ino), | 682 | XFS_INO_TO_AGINO(ip->i_mount, ip->i_ino), |
675 | XFS_ICI_RECLAIM_TAG); | 683 | XFS_ICI_RECLAIM_TAG); |
684 | pag->pag_ici_reclaimable++; | ||
676 | } | 685 | } |
677 | 686 | ||
678 | /* | 687 | /* |
@@ -705,6 +714,7 @@ __xfs_inode_clear_reclaim_tag( | |||
705 | { | 714 | { |
706 | radix_tree_tag_clear(&pag->pag_ici_root, | 715 | radix_tree_tag_clear(&pag->pag_ici_root, |
707 | XFS_INO_TO_AGINO(mp, ip->i_ino), XFS_ICI_RECLAIM_TAG); | 716 | XFS_INO_TO_AGINO(mp, ip->i_ino), XFS_ICI_RECLAIM_TAG); |
717 | pag->pag_ici_reclaimable--; | ||
708 | } | 718 | } |
709 | 719 | ||
710 | /* | 720 | /* |
@@ -854,5 +864,93 @@ xfs_reclaim_inodes( | |||
854 | int mode) | 864 | int mode) |
855 | { | 865 | { |
856 | return xfs_inode_ag_iterator(mp, xfs_reclaim_inode, mode, | 866 | return xfs_inode_ag_iterator(mp, xfs_reclaim_inode, mode, |
857 | XFS_ICI_RECLAIM_TAG, 1); | 867 | XFS_ICI_RECLAIM_TAG, 1, NULL); |
868 | } | ||
869 | |||
870 | /* | ||
871 | * Shrinker infrastructure. | ||
872 | * | ||
873 | * This is all far more complex than it needs to be. It adds a global list of | ||
874 | * mounts because the shrinkers can only call a global context. We need to make | ||
875 | * the shrinkers pass a context to avoid the need for global state. | ||
876 | */ | ||
877 | static LIST_HEAD(xfs_mount_list); | ||
878 | static struct rw_semaphore xfs_mount_list_lock; | ||
879 | |||
880 | static int | ||
881 | xfs_reclaim_inode_shrink( | ||
882 | int nr_to_scan, | ||
883 | gfp_t gfp_mask) | ||
884 | { | ||
885 | struct xfs_mount *mp; | ||
886 | struct xfs_perag *pag; | ||
887 | xfs_agnumber_t ag; | ||
888 | int reclaimable = 0; | ||
889 | |||
890 | if (nr_to_scan) { | ||
891 | if (!(gfp_mask & __GFP_FS)) | ||
892 | return -1; | ||
893 | |||
894 | down_read(&xfs_mount_list_lock); | ||
895 | list_for_each_entry(mp, &xfs_mount_list, m_mplist) { | ||
896 | xfs_inode_ag_iterator(mp, xfs_reclaim_inode, 0, | ||
897 | XFS_ICI_RECLAIM_TAG, 1, &nr_to_scan); | ||
898 | if (nr_to_scan <= 0) | ||
899 | break; | ||
900 | } | ||
901 | up_read(&xfs_mount_list_lock); | ||
902 | } | ||
903 | |||
904 | down_read(&xfs_mount_list_lock); | ||
905 | list_for_each_entry(mp, &xfs_mount_list, m_mplist) { | ||
906 | for (ag = 0; ag < mp->m_sb.sb_agcount; ag++) { | ||
907 | |||
908 | pag = xfs_perag_get(mp, ag); | ||
909 | if (!pag->pag_ici_init) { | ||
910 | xfs_perag_put(pag); | ||
911 | continue; | ||
912 | } | ||
913 | reclaimable += pag->pag_ici_reclaimable; | ||
914 | xfs_perag_put(pag); | ||
915 | } | ||
916 | } | ||
917 | up_read(&xfs_mount_list_lock); | ||
918 | return reclaimable; | ||
919 | } | ||
920 | |||
921 | static struct shrinker xfs_inode_shrinker = { | ||
922 | .shrink = xfs_reclaim_inode_shrink, | ||
923 | .seeks = DEFAULT_SEEKS, | ||
924 | }; | ||
925 | |||
926 | void __init | ||
927 | xfs_inode_shrinker_init(void) | ||
928 | { | ||
929 | init_rwsem(&xfs_mount_list_lock); | ||
930 | register_shrinker(&xfs_inode_shrinker); | ||
931 | } | ||
932 | |||
933 | void | ||
934 | xfs_inode_shrinker_destroy(void) | ||
935 | { | ||
936 | ASSERT(list_empty(&xfs_mount_list)); | ||
937 | unregister_shrinker(&xfs_inode_shrinker); | ||
938 | } | ||
939 | |||
940 | void | ||
941 | xfs_inode_shrinker_register( | ||
942 | struct xfs_mount *mp) | ||
943 | { | ||
944 | down_write(&xfs_mount_list_lock); | ||
945 | list_add_tail(&mp->m_mplist, &xfs_mount_list); | ||
946 | up_write(&xfs_mount_list_lock); | ||
947 | } | ||
948 | |||
949 | void | ||
950 | xfs_inode_shrinker_unregister( | ||
951 | struct xfs_mount *mp) | ||
952 | { | ||
953 | down_write(&xfs_mount_list_lock); | ||
954 | list_del(&mp->m_mplist); | ||
955 | up_write(&xfs_mount_list_lock); | ||
858 | } | 956 | } |