diff options
-rw-r--r-- | include/linux/shrinker.h | 38 | ||||
-rw-r--r-- | mm/vmscan.c | 60 |
2 files changed, 69 insertions, 29 deletions
diff --git a/include/linux/shrinker.h b/include/linux/shrinker.h index ac6b8ee07825..884e76222e1b 100644 --- a/include/linux/shrinker.h +++ b/include/linux/shrinker.h | |||
@@ -4,6 +4,12 @@ | |||
4 | /* | 4 | /* |
5 | * This struct is used to pass information from page reclaim to the shrinkers. | 5 | * This struct is used to pass information from page reclaim to the shrinkers. |
6 | * We consolidate the values for easier extention later. | 6 | * We consolidate the values for easier extention later. |
7 | * | ||
8 | * The 'gfpmask' refers to the allocation we are currently trying to | ||
9 | * fulfil. | ||
10 | * | ||
11 | * Note that 'shrink' will be passed nr_to_scan == 0 when the VM is | ||
12 | * querying the cache size, so a fastpath for that case is appropriate. | ||
7 | */ | 13 | */ |
8 | struct shrink_control { | 14 | struct shrink_control { |
9 | gfp_t gfp_mask; | 15 | gfp_t gfp_mask; |
@@ -12,23 +18,37 @@ struct shrink_control { | |||
12 | unsigned long nr_to_scan; | 18 | unsigned long nr_to_scan; |
13 | }; | 19 | }; |
14 | 20 | ||
21 | #define SHRINK_STOP (~0UL) | ||
15 | /* | 22 | /* |
16 | * A callback you can register to apply pressure to ageable caches. | 23 | * A callback you can register to apply pressure to ageable caches. |
17 | * | 24 | * |
18 | * 'sc' is passed shrink_control which includes a count 'nr_to_scan' | 25 | * @shrink() should look through the least-recently-used 'nr_to_scan' entries |
19 | * and a 'gfpmask'. It should look through the least-recently-used | 26 | * and attempt to free them up. It should return the number of objects which |
20 | * 'nr_to_scan' entries and attempt to free them up. It should return | 27 | * remain in the cache. If it returns -1, it means it cannot do any scanning at |
21 | * the number of objects which remain in the cache. If it returns -1, it means | 28 | * this time (eg. there is a risk of deadlock). |
22 | * it cannot do any scanning at this time (eg. there is a risk of deadlock). | ||
23 | * | 29 | * |
24 | * The 'gfpmask' refers to the allocation we are currently trying to | 30 | * @count_objects should return the number of freeable items in the cache. If |
25 | * fulfil. | 31 | * there are no objects to free or the number of freeable items cannot be |
32 | * determined, it should return 0. No deadlock checks should be done during the | ||
33 | * count callback - the shrinker relies on aggregating scan counts that couldn't | ||
34 | * be executed due to potential deadlocks to be run at a later call when the | ||
35 | * deadlock condition is no longer pending. | ||
26 | * | 36 | * |
27 | * Note that 'shrink' will be passed nr_to_scan == 0 when the VM is | 37 | * @scan_objects will only be called if @count_objects returned a non-zero |
28 | * querying the cache size, so a fastpath for that case is appropriate. | 38 | * value for the number of freeable objects. The callout should scan the cache |
39 | * and attempt to free items from the cache. It should then return the number | ||
40 | * of objects freed during the scan, or SHRINK_STOP if progress cannot be made | ||
41 | * due to potential deadlocks. If SHRINK_STOP is returned, then no further | ||
42 | * attempts to call the @scan_objects will be made from the current reclaim | ||
43 | * context. | ||
29 | */ | 44 | */ |
30 | struct shrinker { | 45 | struct shrinker { |
31 | int (*shrink)(struct shrinker *, struct shrink_control *sc); | 46 | int (*shrink)(struct shrinker *, struct shrink_control *sc); |
47 | unsigned long (*count_objects)(struct shrinker *, | ||
48 | struct shrink_control *sc); | ||
49 | unsigned long (*scan_objects)(struct shrinker *, | ||
50 | struct shrink_control *sc); | ||
51 | |||
32 | int seeks; /* seeks to recreate an obj */ | 52 | int seeks; /* seeks to recreate an obj */ |
33 | long batch; /* reclaim batch size, 0 = default */ | 53 | long batch; /* reclaim batch size, 0 = default */ |
34 | 54 | ||
diff --git a/mm/vmscan.c b/mm/vmscan.c index 2cff0d491c6d..4d4e859b4b9c 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c | |||
@@ -205,19 +205,24 @@ static inline int do_shrinker_shrink(struct shrinker *shrinker, | |||
205 | * | 205 | * |
206 | * Returns the number of slab objects which we shrunk. | 206 | * Returns the number of slab objects which we shrunk. |
207 | */ | 207 | */ |
208 | unsigned long shrink_slab(struct shrink_control *shrink, | 208 | unsigned long shrink_slab(struct shrink_control *shrinkctl, |
209 | unsigned long nr_pages_scanned, | 209 | unsigned long nr_pages_scanned, |
210 | unsigned long lru_pages) | 210 | unsigned long lru_pages) |
211 | { | 211 | { |
212 | struct shrinker *shrinker; | 212 | struct shrinker *shrinker; |
213 | unsigned long ret = 0; | 213 | unsigned long freed = 0; |
214 | 214 | ||
215 | if (nr_pages_scanned == 0) | 215 | if (nr_pages_scanned == 0) |
216 | nr_pages_scanned = SWAP_CLUSTER_MAX; | 216 | nr_pages_scanned = SWAP_CLUSTER_MAX; |
217 | 217 | ||
218 | if (!down_read_trylock(&shrinker_rwsem)) { | 218 | if (!down_read_trylock(&shrinker_rwsem)) { |
219 | /* Assume we'll be able to shrink next time */ | 219 | /* |
220 | ret = 1; | 220 | * If we would return 0, our callers would understand that we |
221 | * have nothing else to shrink and give up trying. By returning | ||
222 | * 1 we keep it going and assume we'll be able to shrink next | ||
223 | * time. | ||
224 | */ | ||
225 | freed = 1; | ||
221 | goto out; | 226 | goto out; |
222 | } | 227 | } |
223 | 228 | ||
@@ -225,14 +230,16 @@ unsigned long shrink_slab(struct shrink_control *shrink, | |||
225 | unsigned long long delta; | 230 | unsigned long long delta; |
226 | long total_scan; | 231 | long total_scan; |
227 | long max_pass; | 232 | long max_pass; |
228 | int shrink_ret = 0; | ||
229 | long nr; | 233 | long nr; |
230 | long new_nr; | 234 | long new_nr; |
231 | long batch_size = shrinker->batch ? shrinker->batch | 235 | long batch_size = shrinker->batch ? shrinker->batch |
232 | : SHRINK_BATCH; | 236 | : SHRINK_BATCH; |
233 | 237 | ||
234 | max_pass = do_shrinker_shrink(shrinker, shrink, 0); | 238 | if (shrinker->count_objects) |
235 | if (max_pass <= 0) | 239 | max_pass = shrinker->count_objects(shrinker, shrinkctl); |
240 | else | ||
241 | max_pass = do_shrinker_shrink(shrinker, shrinkctl, 0); | ||
242 | if (max_pass == 0) | ||
236 | continue; | 243 | continue; |
237 | 244 | ||
238 | /* | 245 | /* |
@@ -248,8 +255,8 @@ unsigned long shrink_slab(struct shrink_control *shrink, | |||
248 | do_div(delta, lru_pages + 1); | 255 | do_div(delta, lru_pages + 1); |
249 | total_scan += delta; | 256 | total_scan += delta; |
250 | if (total_scan < 0) { | 257 | if (total_scan < 0) { |
251 | printk(KERN_ERR "shrink_slab: %pF negative objects to " | 258 | printk(KERN_ERR |
252 | "delete nr=%ld\n", | 259 | "shrink_slab: %pF negative objects to delete nr=%ld\n", |
253 | shrinker->shrink, total_scan); | 260 | shrinker->shrink, total_scan); |
254 | total_scan = max_pass; | 261 | total_scan = max_pass; |
255 | } | 262 | } |
@@ -277,20 +284,33 @@ unsigned long shrink_slab(struct shrink_control *shrink, | |||
277 | if (total_scan > max_pass * 2) | 284 | if (total_scan > max_pass * 2) |
278 | total_scan = max_pass * 2; | 285 | total_scan = max_pass * 2; |
279 | 286 | ||
280 | trace_mm_shrink_slab_start(shrinker, shrink, nr, | 287 | trace_mm_shrink_slab_start(shrinker, shrinkctl, nr, |
281 | nr_pages_scanned, lru_pages, | 288 | nr_pages_scanned, lru_pages, |
282 | max_pass, delta, total_scan); | 289 | max_pass, delta, total_scan); |
283 | 290 | ||
284 | while (total_scan >= batch_size) { | 291 | while (total_scan >= batch_size) { |
285 | int nr_before; | ||
286 | 292 | ||
287 | nr_before = do_shrinker_shrink(shrinker, shrink, 0); | 293 | if (shrinker->scan_objects) { |
288 | shrink_ret = do_shrinker_shrink(shrinker, shrink, | 294 | unsigned long ret; |
289 | batch_size); | 295 | shrinkctl->nr_to_scan = batch_size; |
290 | if (shrink_ret == -1) | 296 | ret = shrinker->scan_objects(shrinker, shrinkctl); |
291 | break; | 297 | |
292 | if (shrink_ret < nr_before) | 298 | if (ret == SHRINK_STOP) |
293 | ret += nr_before - shrink_ret; | 299 | break; |
300 | freed += ret; | ||
301 | } else { | ||
302 | int nr_before; | ||
303 | long ret; | ||
304 | |||
305 | nr_before = do_shrinker_shrink(shrinker, shrinkctl, 0); | ||
306 | ret = do_shrinker_shrink(shrinker, shrinkctl, | ||
307 | batch_size); | ||
308 | if (ret == -1) | ||
309 | break; | ||
310 | if (ret < nr_before) | ||
311 | freed += nr_before - ret; | ||
312 | } | ||
313 | |||
294 | count_vm_events(SLABS_SCANNED, batch_size); | 314 | count_vm_events(SLABS_SCANNED, batch_size); |
295 | total_scan -= batch_size; | 315 | total_scan -= batch_size; |
296 | 316 | ||
@@ -308,12 +328,12 @@ unsigned long shrink_slab(struct shrink_control *shrink, | |||
308 | else | 328 | else |
309 | new_nr = atomic_long_read(&shrinker->nr_in_batch); | 329 | new_nr = atomic_long_read(&shrinker->nr_in_batch); |
310 | 330 | ||
311 | trace_mm_shrink_slab_end(shrinker, shrink_ret, nr, new_nr); | 331 | trace_mm_shrink_slab_end(shrinker, freed, nr, new_nr); |
312 | } | 332 | } |
313 | up_read(&shrinker_rwsem); | 333 | up_read(&shrinker_rwsem); |
314 | out: | 334 | out: |
315 | cond_resched(); | 335 | cond_resched(); |
316 | return ret; | 336 | return freed; |
317 | } | 337 | } |
318 | 338 | ||
319 | static inline int is_page_cache_freeable(struct page *page) | 339 | static inline int is_page_cache_freeable(struct page *page) |