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 <surenb@google.com>
This commit is contained in:
parent
af2be4c55d
commit
81c75b2a33
72
lmkd.c
72
lmkd.c
|
|
@ -1951,41 +1951,49 @@ enum zone_watermark {
|
||||||
WMARK_NONE
|
WMARK_NONE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct zone_watermarks {
|
||||||
|
long high_wmark;
|
||||||
|
long low_wmark;
|
||||||
|
long min_wmark;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns lowest breached watermark or WMARK_NONE.
|
* 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++) {
|
for (int node_idx = 0; node_idx < zi->node_count; node_idx++) {
|
||||||
struct zoneinfo_node *node = &zi->nodes[node_idx];
|
struct zoneinfo_node *node = &zi->nodes[node_idx];
|
||||||
|
|
||||||
for (int zone_idx = 0; zone_idx < node->zone_count; zone_idx++) {
|
for (int zone_idx = 0; zone_idx < node->zone_count; zone_idx++) {
|
||||||
struct zoneinfo_zone *zone = &node->zones[zone_idx];
|
struct zoneinfo_zone *zone = &node->zones[zone_idx];
|
||||||
int zone_free_mem;
|
|
||||||
|
|
||||||
if (!zone->fields.field.present) {
|
if (!zone->fields.field.present) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
zone_free_mem = zone->fields.field.nr_free_pages - zone->fields.field.nr_free_cma;
|
watermarks->high_wmark += zone->max_protection + zone->fields.field.high;
|
||||||
if (zone_free_mem > zone->max_protection + zone->fields.field.high) {
|
watermarks->low_wmark += zone->max_protection + zone->fields.field.low;
|
||||||
continue;
|
watermarks->min_wmark += zone->max_protection + zone->fields.field.min;
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return wmark;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mp_event_psi(int data, uint32_t events, struct polling_params *poll_params) {
|
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 bool killing;
|
||||||
static int thrashing_limit;
|
static int thrashing_limit;
|
||||||
static bool in_reclaim;
|
static bool in_reclaim;
|
||||||
|
static struct zone_watermarks watermarks;
|
||||||
|
static struct timespec wmark_update_tm;
|
||||||
|
|
||||||
union meminfo mi;
|
union meminfo mi;
|
||||||
union vmstat vs;
|
union vmstat vs;
|
||||||
struct zoneinfo zi;
|
|
||||||
struct timespec curr_tm;
|
struct timespec curr_tm;
|
||||||
int64_t thrashing = 0;
|
int64_t thrashing = 0;
|
||||||
bool swap_is_low = false;
|
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;
|
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 */
|
/* Find out which watermark is breached if any */
|
||||||
if (zoneinfo_parse(&zi) < 0) {
|
wmark = get_lowest_watermark(&mi, &watermarks);
|
||||||
ALOGE("Failed to parse zoneinfo!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
wmark = get_lowest_watermark(&zi);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO: move this logic into a separate function
|
* TODO: move this logic into a separate function
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue