Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bec58544 authored by Suren Baghdasaryan's avatar Suren Baghdasaryan Committed by Gerrit Code Review
Browse files

Merge "lmkd: Add support for psi monitors"

parents 59a01499 77122e57
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ cc_binary {
        "libcutils",
        "liblog",
        "libprocessgroup",
        "libpsi",
    ],
    static_libs: [
        "libstatslogc",
+139 −14
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@
#include <log/log.h>
#include <log/log_event_list.h>
#include <log/log_time.h>
#include <psi/psi.h>
#include <system/thread_defs.h>

#ifdef LMKD_LOG_STATS
@@ -93,6 +94,7 @@
#define TARGET_UPDATE_MIN_INTERVAL_MS 1000

#define NS_PER_MS (NS_PER_SEC / MS_PER_SEC)
#define US_PER_MS (US_PER_SEC / MS_PER_SEC)

/* Defined as ProcessList.SYSTEM_ADJ in ProcessList.java */
#define SYSTEM_ADJ (-900)
@@ -100,6 +102,18 @@
#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
#define STRINGIFY_INTERNAL(x) #x

/*
 * PSI monitor tracking window size.
 * PSI monitor generates events at most once per window,
 * therefore we poll memory state for the duration of
 * PSI_WINDOW_SIZE_MS after the event happens.
 */
#define PSI_WINDOW_SIZE_MS 1000
/* Polling period after initial PSI signal */
#define PSI_POLL_PERIOD_MS 200
/* Poll for the duration of one window after initial PSI signal */
#define PSI_POLL_COUNT (PSI_WINDOW_SIZE_MS / PSI_POLL_PERIOD_MS)

#define min(a, b) (((a) < (b)) ? (a) : (b))

#define FAIL_REPORT_RLIMIT_MS 1000
@@ -127,6 +141,11 @@ struct {
    int64_t max_nr_free_pages;
} low_pressure_mem = { -1, -1 };

struct psi_threshold {
    enum psi_stall_type stall_type;
    int threshold_ms;
};

static int level_oomadj[VMPRESS_LEVEL_COUNT];
static int mpevfd[VMPRESS_LEVEL_COUNT] = { -1, -1, -1 };
static bool debug_process_killing;
@@ -139,6 +158,12 @@ static unsigned long kill_timeout_ms;
static bool use_minfree_levels;
static bool per_app_memcg;
static int swap_free_low_percentage;
static bool use_psi_monitors = false;
static struct psi_threshold psi_thresholds[VMPRESS_LEVEL_COUNT] = {
    { PSI_SOME, 70 },    /* 70ms out of 1sec for partial stall */
    { PSI_SOME, 100 },   /* 100ms out of 1sec for partial stall */
    { PSI_FULL, 70 },    /* 70ms out of 1sec for complete stall */
};

static android_log_context ctx;

@@ -1524,6 +1549,11 @@ static void mp_event_common(int data, uint32_t events __unused) {
        .fd = -1,
    };

    if (debug_process_killing) {
        ALOGI("%s memory pressure event is triggered", level_name[level]);
    }

    if (!use_psi_monitors) {
        /*
         * Check all event counters from low to critical
         * and upgrade to the highest priority one. By reading
@@ -1537,6 +1567,7 @@ static void mp_event_common(int data, uint32_t events __unused) {
                level = lvl;
            }
        }
    }

    if (clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm) != 0) {
        ALOGE("Failed to get current time");
@@ -1722,6 +1753,54 @@ do_kill:
    }
}

static bool init_mp_psi(enum vmpressure_level level) {
    int fd = init_psi_monitor(psi_thresholds[level].stall_type,
        psi_thresholds[level].threshold_ms * US_PER_MS,
        PSI_WINDOW_SIZE_MS * US_PER_MS);

    if (fd < 0) {
        return false;
    }

    vmpressure_hinfo[level].handler = mp_event_common;
    vmpressure_hinfo[level].data = level;
    if (register_psi_monitor(epollfd, fd, &vmpressure_hinfo[level]) < 0) {
        destroy_psi_monitor(fd);
        return false;
    }
    maxevents++;
    mpevfd[level] = fd;

    return true;
}

static void destroy_mp_psi(enum vmpressure_level level) {
    int fd = mpevfd[level];

    if (unregister_psi_monitor(epollfd, fd) < 0) {
        ALOGE("Failed to unregister psi monitor for %s memory pressure; errno=%d",
            level_name[level], errno);
    }
    destroy_psi_monitor(fd);
    mpevfd[level] = -1;
}

static bool init_psi_monitors() {
    if (!init_mp_psi(VMPRESS_LEVEL_LOW)) {
        return false;
    }
    if (!init_mp_psi(VMPRESS_LEVEL_MEDIUM)) {
        destroy_mp_psi(VMPRESS_LEVEL_LOW);
        return false;
    }
    if (!init_mp_psi(VMPRESS_LEVEL_CRITICAL)) {
        destroy_mp_psi(VMPRESS_LEVEL_MEDIUM);
        destroy_mp_psi(VMPRESS_LEVEL_LOW);
        return false;
    }
    return true;
}

static bool init_mp_common(enum vmpressure_level level) {
    int mpfd;
    int evfd;
@@ -1837,12 +1916,22 @@ static int init(void) {
    if (use_inkernel_interface) {
        ALOGI("Using in-kernel low memory killer interface");
    } else {
        if (!init_mp_common(VMPRESS_LEVEL_LOW) ||
        /* Try to use psi monitor first if kernel has it */
        use_psi_monitors = property_get_bool("ro.lmk.use_psi", true) &&
            init_psi_monitors();
        /* Fall back to vmpressure */
        if (!use_psi_monitors &&
            (!init_mp_common(VMPRESS_LEVEL_LOW) ||
            !init_mp_common(VMPRESS_LEVEL_MEDIUM) ||
            !init_mp_common(VMPRESS_LEVEL_CRITICAL)) {
            !init_mp_common(VMPRESS_LEVEL_CRITICAL))) {
            ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
            return -1;
        }
        if (use_psi_monitors) {
            ALOGI("Using psi monitors for memory pressure detection");
        } else {
            ALOGI("Using vmpressure for memory pressure detection");
        }
    }

    for (i = 0; i <= ADJTOSLOT(OOM_SCORE_ADJ_MAX); i++) {
@@ -1857,14 +1946,39 @@ static int init(void) {

static void mainloop(void) {
    struct event_handler_info* handler_info;
    struct event_handler_info* poll_handler = NULL;
    struct timespec last_report_tm, curr_tm;
    struct epoll_event *evt;
    long delay = -1;
    int polling = 0;

    while (1) {
        struct epoll_event events[maxevents];
        int nevents;
        int i;

        if (polling) {
            /* Calculate next timeout */
            clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm);
            delay = get_time_diff_ms(&last_report_tm, &curr_tm);
            delay = (delay < PSI_POLL_PERIOD_MS) ?
                PSI_POLL_PERIOD_MS - delay : PSI_POLL_PERIOD_MS;

            /* Wait for events until the next polling timeout */
            nevents = epoll_wait(epollfd, events, maxevents, delay);

            clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm);
            if (get_time_diff_ms(&last_report_tm, &curr_tm) >= PSI_POLL_PERIOD_MS) {
                if (polling) {
                    polling--;
                    poll_handler->handler(poll_handler->data, 0);
                    last_report_tm = curr_tm;
                }
            }
        } else {
            /* Wait for events with no timeout */
            nevents = epoll_wait(epollfd, events, maxevents, -1);
        }

        if (nevents == -1) {
            if (errno == EINTR)
@@ -1899,6 +2013,17 @@ static void mainloop(void) {
            if (evt->data.ptr) {
                handler_info = (struct event_handler_info*)evt->data.ptr;
                handler_info->handler(handler_info->data, evt->events);

                if (use_psi_monitors && handler_info->handler == mp_event_common) {
                    /*
                     * Poll for the duration of PSI_WINDOW_SIZE_MS after the
                     * initial PSI event because psi events are rate-limited
                     * at one per sec.
                     */
                    polling = PSI_POLL_COUNT;
                    poll_handler = handler_info;
                    clock_gettime(CLOCK_MONOTONIC_COARSE, &last_report_tm);
                }
            }
        }
    }