diff options
author | Michal Hocko <mhocko@suse.com> | 2017-02-22 18:45:58 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-02-22 19:41:30 -0500 |
commit | fd538803731e50367b7c59ce4ad3454426a3d671 (patch) | |
tree | de58ea9fa0765edb9ab42e5145bf1d65245f7f54 | |
parent | f0958906cd2bf3730cd7938b8af80a1c23e8ac06 (diff) |
mm, vmscan: cleanup lru size claculations
lruvec_lru_size returns the full size of the LRU list while we sometimes
need a value reduced only to eligible zones (e.g. for lowmem requests).
inactive_list_is_low is one such user. Later patches will add more of
them. Add a new parameter to lruvec_lru_size and allow it filter out
zones which are not eligible for the given context.
Link: http://lkml.kernel.org/r/20170117103702.28542-2-mhocko@kernel.org
Signed-off-by: Michal Hocko <mhocko@suse.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: Hillf Danton <hillf.zj@alibaba-inc.com>
Acked-by: Minchan Kim <minchan@kernel.org>
Acked-by: Mel Gorman <mgorman@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | include/linux/mmzone.h | 2 | ||||
-rw-r--r-- | mm/vmscan.c | 89 | ||||
-rw-r--r-- | mm/workingset.c | 2 |
3 files changed, 46 insertions, 47 deletions
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index f4aac87adcc3..82fc632fd11d 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h | |||
@@ -779,7 +779,7 @@ static inline struct pglist_data *lruvec_pgdat(struct lruvec *lruvec) | |||
779 | #endif | 779 | #endif |
780 | } | 780 | } |
781 | 781 | ||
782 | extern unsigned long lruvec_lru_size(struct lruvec *lruvec, enum lru_list lru); | 782 | extern unsigned long lruvec_lru_size(struct lruvec *lruvec, enum lru_list lru, int zone_idx); |
783 | 783 | ||
784 | #ifdef CONFIG_HAVE_MEMORY_PRESENT | 784 | #ifdef CONFIG_HAVE_MEMORY_PRESENT |
785 | void memory_present(int nid, unsigned long start, unsigned long end); | 785 | void memory_present(int nid, unsigned long start, unsigned long end); |
diff --git a/mm/vmscan.c b/mm/vmscan.c index 277e105646a5..e7c75a3c6b53 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c | |||
@@ -234,22 +234,39 @@ bool pgdat_reclaimable(struct pglist_data *pgdat) | |||
234 | pgdat_reclaimable_pages(pgdat) * 6; | 234 | pgdat_reclaimable_pages(pgdat) * 6; |
235 | } | 235 | } |
236 | 236 | ||
237 | unsigned long lruvec_lru_size(struct lruvec *lruvec, enum lru_list lru) | 237 | /** |
238 | * lruvec_lru_size - Returns the number of pages on the given LRU list. | ||
239 | * @lruvec: lru vector | ||
240 | * @lru: lru to use | ||
241 | * @zone_idx: zones to consider (use MAX_NR_ZONES for the whole LRU list) | ||
242 | */ | ||
243 | unsigned long lruvec_lru_size(struct lruvec *lruvec, enum lru_list lru, int zone_idx) | ||
238 | { | 244 | { |
245 | unsigned long lru_size; | ||
246 | int zid; | ||
247 | |||
239 | if (!mem_cgroup_disabled()) | 248 | if (!mem_cgroup_disabled()) |
240 | return mem_cgroup_get_lru_size(lruvec, lru); | 249 | lru_size = mem_cgroup_get_lru_size(lruvec, lru); |
250 | else | ||
251 | lru_size = node_page_state(lruvec_pgdat(lruvec), NR_LRU_BASE + lru); | ||
241 | 252 | ||
242 | return node_page_state(lruvec_pgdat(lruvec), NR_LRU_BASE + lru); | 253 | for (zid = zone_idx + 1; zid < MAX_NR_ZONES; zid++) { |
243 | } | 254 | struct zone *zone = &lruvec_pgdat(lruvec)->node_zones[zid]; |
255 | unsigned long size; | ||
244 | 256 | ||
245 | unsigned long lruvec_zone_lru_size(struct lruvec *lruvec, enum lru_list lru, | 257 | if (!managed_zone(zone)) |
246 | int zone_idx) | 258 | continue; |
247 | { | 259 | |
248 | if (!mem_cgroup_disabled()) | 260 | if (!mem_cgroup_disabled()) |
249 | return mem_cgroup_get_zone_lru_size(lruvec, lru, zone_idx); | 261 | size = mem_cgroup_get_zone_lru_size(lruvec, lru, zid); |
262 | else | ||
263 | size = zone_page_state(&lruvec_pgdat(lruvec)->node_zones[zid], | ||
264 | NR_ZONE_LRU_BASE + lru); | ||
265 | lru_size -= min(size, lru_size); | ||
266 | } | ||
267 | |||
268 | return lru_size; | ||
250 | 269 | ||
251 | return zone_page_state(&lruvec_pgdat(lruvec)->node_zones[zone_idx], | ||
252 | NR_ZONE_LRU_BASE + lru); | ||
253 | } | 270 | } |
254 | 271 | ||
255 | /* | 272 | /* |
@@ -2049,11 +2066,10 @@ static bool inactive_list_is_low(struct lruvec *lruvec, bool file, | |||
2049 | struct scan_control *sc, bool trace) | 2066 | struct scan_control *sc, bool trace) |
2050 | { | 2067 | { |
2051 | unsigned long inactive_ratio; | 2068 | unsigned long inactive_ratio; |
2052 | unsigned long total_inactive, inactive; | 2069 | unsigned long inactive, active; |
2053 | unsigned long total_active, active; | 2070 | enum lru_list inactive_lru = file * LRU_FILE; |
2071 | enum lru_list active_lru = file * LRU_FILE + LRU_ACTIVE; | ||
2054 | unsigned long gb; | 2072 | unsigned long gb; |
2055 | struct pglist_data *pgdat = lruvec_pgdat(lruvec); | ||
2056 | int zid; | ||
2057 | 2073 | ||
2058 | /* | 2074 | /* |
2059 | * If we don't have swap space, anonymous page deactivation | 2075 | * If we don't have swap space, anonymous page deactivation |
@@ -2062,27 +2078,8 @@ static bool inactive_list_is_low(struct lruvec *lruvec, bool file, | |||
2062 | if (!file && !total_swap_pages) | 2078 | if (!file && !total_swap_pages) |
2063 | return false; | 2079 | return false; |
2064 | 2080 | ||
2065 | total_inactive = inactive = lruvec_lru_size(lruvec, file * LRU_FILE); | 2081 | inactive = lruvec_lru_size(lruvec, inactive_lru, sc->reclaim_idx); |
2066 | total_active = active = lruvec_lru_size(lruvec, file * LRU_FILE + LRU_ACTIVE); | 2082 | active = lruvec_lru_size(lruvec, active_lru, sc->reclaim_idx); |
2067 | |||
2068 | /* | ||
2069 | * For zone-constrained allocations, it is necessary to check if | ||
2070 | * deactivations are required for lowmem to be reclaimed. This | ||
2071 | * calculates the inactive/active pages available in eligible zones. | ||
2072 | */ | ||
2073 | for (zid = sc->reclaim_idx + 1; zid < MAX_NR_ZONES; zid++) { | ||
2074 | struct zone *zone = &pgdat->node_zones[zid]; | ||
2075 | unsigned long inactive_zone, active_zone; | ||
2076 | |||
2077 | if (!managed_zone(zone)) | ||
2078 | continue; | ||
2079 | |||
2080 | inactive_zone = lruvec_zone_lru_size(lruvec, file * LRU_FILE, zid); | ||
2081 | active_zone = lruvec_zone_lru_size(lruvec, (file * LRU_FILE) + LRU_ACTIVE, zid); | ||
2082 | |||
2083 | inactive -= min(inactive, inactive_zone); | ||
2084 | active -= min(active, active_zone); | ||
2085 | } | ||
2086 | 2083 | ||
2087 | gb = (inactive + active) >> (30 - PAGE_SHIFT); | 2084 | gb = (inactive + active) >> (30 - PAGE_SHIFT); |
2088 | if (gb) | 2085 | if (gb) |
@@ -2091,10 +2088,12 @@ static bool inactive_list_is_low(struct lruvec *lruvec, bool file, | |||
2091 | inactive_ratio = 1; | 2088 | inactive_ratio = 1; |
2092 | 2089 | ||
2093 | if (trace) | 2090 | if (trace) |
2094 | trace_mm_vmscan_inactive_list_is_low(pgdat->node_id, | 2091 | trace_mm_vmscan_inactive_list_is_low(lruvec_pgdat(lruvec)->node_id, |
2095 | sc->reclaim_idx, | 2092 | sc->reclaim_idx, |
2096 | total_inactive, inactive, | 2093 | lruvec_lru_size(lruvec, inactive_lru, MAX_NR_ZONES), inactive, |
2097 | total_active, active, inactive_ratio, file); | 2094 | lruvec_lru_size(lruvec, active_lru, MAX_NR_ZONES), active, |
2095 | inactive_ratio, file); | ||
2096 | |||
2098 | return inactive * inactive_ratio < active; | 2097 | return inactive * inactive_ratio < active; |
2099 | } | 2098 | } |
2100 | 2099 | ||
@@ -2234,7 +2233,7 @@ static void get_scan_count(struct lruvec *lruvec, struct mem_cgroup *memcg, | |||
2234 | * system is under heavy pressure. | 2233 | * system is under heavy pressure. |
2235 | */ | 2234 | */ |
2236 | if (!inactive_list_is_low(lruvec, true, sc, false) && | 2235 | if (!inactive_list_is_low(lruvec, true, sc, false) && |
2237 | lruvec_lru_size(lruvec, LRU_INACTIVE_FILE) >> sc->priority) { | 2236 | lruvec_lru_size(lruvec, LRU_INACTIVE_FILE, MAX_NR_ZONES) >> sc->priority) { |
2238 | scan_balance = SCAN_FILE; | 2237 | scan_balance = SCAN_FILE; |
2239 | goto out; | 2238 | goto out; |
2240 | } | 2239 | } |
@@ -2260,10 +2259,10 @@ static void get_scan_count(struct lruvec *lruvec, struct mem_cgroup *memcg, | |||
2260 | * anon in [0], file in [1] | 2259 | * anon in [0], file in [1] |
2261 | */ | 2260 | */ |
2262 | 2261 | ||
2263 | anon = lruvec_lru_size(lruvec, LRU_ACTIVE_ANON) + | 2262 | anon = lruvec_lru_size(lruvec, LRU_ACTIVE_ANON, MAX_NR_ZONES) + |
2264 | lruvec_lru_size(lruvec, LRU_INACTIVE_ANON); | 2263 | lruvec_lru_size(lruvec, LRU_INACTIVE_ANON, MAX_NR_ZONES); |
2265 | file = lruvec_lru_size(lruvec, LRU_ACTIVE_FILE) + | 2264 | file = lruvec_lru_size(lruvec, LRU_ACTIVE_FILE, MAX_NR_ZONES) + |
2266 | lruvec_lru_size(lruvec, LRU_INACTIVE_FILE); | 2265 | lruvec_lru_size(lruvec, LRU_INACTIVE_FILE, MAX_NR_ZONES); |
2267 | 2266 | ||
2268 | spin_lock_irq(&pgdat->lru_lock); | 2267 | spin_lock_irq(&pgdat->lru_lock); |
2269 | if (unlikely(reclaim_stat->recent_scanned[0] > anon / 4)) { | 2268 | if (unlikely(reclaim_stat->recent_scanned[0] > anon / 4)) { |
@@ -2301,7 +2300,7 @@ out: | |||
2301 | unsigned long size; | 2300 | unsigned long size; |
2302 | unsigned long scan; | 2301 | unsigned long scan; |
2303 | 2302 | ||
2304 | size = lruvec_lru_size(lruvec, lru); | 2303 | size = lruvec_lru_size(lruvec, lru, MAX_NR_ZONES); |
2305 | scan = size >> sc->priority; | 2304 | scan = size >> sc->priority; |
2306 | 2305 | ||
2307 | if (!scan && pass && force_scan) | 2306 | if (!scan && pass && force_scan) |
diff --git a/mm/workingset.c b/mm/workingset.c index abb58ffa3c64..a67f5796b995 100644 --- a/mm/workingset.c +++ b/mm/workingset.c | |||
@@ -267,7 +267,7 @@ bool workingset_refault(void *shadow) | |||
267 | } | 267 | } |
268 | lruvec = mem_cgroup_lruvec(pgdat, memcg); | 268 | lruvec = mem_cgroup_lruvec(pgdat, memcg); |
269 | refault = atomic_long_read(&lruvec->inactive_age); | 269 | refault = atomic_long_read(&lruvec->inactive_age); |
270 | active_file = lruvec_lru_size(lruvec, LRU_ACTIVE_FILE); | 270 | active_file = lruvec_lru_size(lruvec, LRU_ACTIVE_FILE, MAX_NR_ZONES); |
271 | rcu_read_unlock(); | 271 | rcu_read_unlock(); |
272 | 272 | ||
273 | /* | 273 | /* |