From 4787ab452f370c217ca1c3942442088bf1fa1804 Mon Sep 17 00:00:00 2001 From: Suren Baghdasaryan Date: Wed, 14 Aug 2019 09:48:35 -0700 Subject: [PATCH] lmkd: Use aggregate zone watermarks as low memory threshold Parsing /proc/zoneinfo is expensive and zone watermarks normally do no change often. Instead of checking free memory per each zone we aggregate zone watermarks and compare them with MemFree from meminfo as an approximation of memory being under a given watermark. zoneinfo parsing is rate limited to once per minute to detect a possible change of the memory margins from userspace. Bug: 132642304 Test: lmkd_unit_test, ACT memory pressure tests Change-Id: If4a8154c004e24324e6de44359de416766139df6 Signed-off-by: Suren Baghdasaryan --- lmkd/lmkd.c | 72 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c index 221fbc736..42e3db33e 100644 --- a/lmkd/lmkd.c +++ b/lmkd/lmkd.c @@ -1951,41 +1951,49 @@ enum zone_watermark { WMARK_NONE }; +struct zone_watermarks { + long high_wmark; + long low_wmark; + long min_wmark; +}; + /* * Returns lowest breached watermark or WMARK_NONE. */ -static enum zone_watermark get_lowest_watermark(struct zoneinfo *zi) +static enum zone_watermark get_lowest_watermark(union meminfo *mi, + struct zone_watermarks *watermarks) { - enum zone_watermark wmark = WMARK_NONE; + int64_t nr_free_pages = mi->field.nr_free_pages - mi->field.cma_free; + + if (nr_free_pages < watermarks->min_wmark) { + return WMARK_MIN; + } + if (nr_free_pages < watermarks->low_wmark) { + return WMARK_LOW; + } + if (nr_free_pages < watermarks->high_wmark) { + return WMARK_HIGH; + } + return WMARK_NONE; +} + +void calc_zone_watermarks(struct zoneinfo *zi, struct zone_watermarks *watermarks) { + memset(watermarks, 0, sizeof(struct zone_watermarks)); for (int node_idx = 0; node_idx < zi->node_count; node_idx++) { struct zoneinfo_node *node = &zi->nodes[node_idx]; - for (int zone_idx = 0; zone_idx < node->zone_count; zone_idx++) { struct zoneinfo_zone *zone = &node->zones[zone_idx]; - int zone_free_mem; if (!zone->fields.field.present) { continue; } - zone_free_mem = zone->fields.field.nr_free_pages - zone->fields.field.nr_free_cma; - if (zone_free_mem > zone->max_protection + zone->fields.field.high) { - continue; - } - if (zone_free_mem > zone->max_protection + zone->fields.field.low) { - if (wmark > WMARK_HIGH) wmark = WMARK_HIGH; - continue; - } - if (zone_free_mem > zone->max_protection + zone->fields.field.min) { - if (wmark > WMARK_LOW) wmark = WMARK_LOW; - continue; - } - wmark = WMARK_MIN; + watermarks->high_wmark += zone->max_protection + zone->fields.field.high; + watermarks->low_wmark += zone->max_protection + zone->fields.field.low; + watermarks->min_wmark += zone->max_protection + zone->fields.field.min; } } - - return wmark; } static void mp_event_psi(int data, uint32_t events, struct polling_params *poll_params) { @@ -2012,10 +2020,11 @@ static void mp_event_psi(int data, uint32_t events, struct polling_params *poll_ static bool killing; static int thrashing_limit; static bool in_reclaim; + static struct zone_watermarks watermarks; + static struct timespec wmark_update_tm; union meminfo mi; union vmstat vs; - struct zoneinfo zi; struct timespec curr_tm; int64_t thrashing = 0; bool swap_is_low = false; @@ -2088,12 +2097,25 @@ static void mp_event_psi(int data, uint32_t events, struct polling_params *poll_ } in_reclaim = true; + /* + * Refresh watermarks once per min in case user updated one of the margins. + * TODO: b/140521024 replace this periodic update with an API for AMS to notify LMKD + * that zone watermarks were changed by the system software. + */ + if (watermarks.high_wmark == 0 || get_time_diff_ms(&wmark_update_tm, &curr_tm) > 60000) { + struct zoneinfo zi; + + if (zoneinfo_parse(&zi) < 0) { + ALOGE("Failed to parse zoneinfo!"); + return; + } + + calc_zone_watermarks(&zi, &watermarks); + wmark_update_tm = curr_tm; + } + /* Find out which watermark is breached if any */ - if (zoneinfo_parse(&zi) < 0) { - ALOGE("Failed to parse zoneinfo!"); - return; - } - wmark = get_lowest_watermark(&zi); + wmark = get_lowest_watermark(&mi, &watermarks); /* * TODO: move this logic into a separate function