|
- #include "guard.h"
- #include <string.h>
- #include <limits.h>
-
- void history_add(history_t *h, double at, uint64_t value)
- {
- h->p[h->next].at = at; h->p[h->next].value = value;
- h->next = (h->next + 1) % HIST;
- if (h->count < HIST) h->count++;
- }
-
- delta_t history_delta(const history_t *h, double now, unsigned seconds)
- {
- delta_t d = {0, 0, 0};
- unsigned i, latest;
- double best = 1.25;
- if (h->count < 2) return d;
- latest = (h->next + HIST - 1) % HIST;
- if (now - h->p[latest].at > 1.25) return d;
- for (i = 0; i < h->count; i++) {
- double age = h->p[latest].at - h->p[i].at;
- double error = age - seconds;
- if (error < 0) error = -error;
- if (age > 0 && error < best && h->p[i].value <= INT64_MAX &&
- h->p[latest].value <= INT64_MAX) {
- best = error; d.valid = 1; d.age = age;
- d.value = (int64_t)h->p[latest].value - (int64_t)h->p[i].value;
- }
- }
- return d;
- }
-
- uint64_t rank_score(uint64_t rss, delta_t growth)
- {
- uint64_t add = growth.valid && growth.value > 0 ? (uint64_t)growth.value : 0;
- if (add > (UINT64_MAX - rss) / 2) return UINT64_MAX;
- return rss + 2 * add;
- }
-
- int swap_physical_bytes_from_blocks(uint64_t total_blocks,
- uint64_t free_blocks, uint64_t *bytes)
- {
- uint64_t used;
- if (!bytes || free_blocks > total_blocks) return -1;
- used = total_blocks - free_blocks;
- if (used > UINT64_MAX / 512) return -1;
- *bytes = used * 512;
- return 0;
- }
-
- const char *state_name(enum state s)
- {
- static const char *names[] = {"NORMAL", "PRE-PRESSURE", "PRESSURE",
- "CRITICAL", "EMERGENCY", "RECOVERY"};
- return names[s];
- }
-
- void engine_init(engine_t *e)
- {
- memset(e, 0, sizeof (*e));
- engine_gap(e);
- }
-
- void engine_gap(engine_t *e)
- {
- e->low_since = e->critical_since = e->recovery_since = -1;
- e->have_last = 0; e->danger = 0; e->floor = 0;
- }
-
- void engine_step(engine_t *e, const config_t *c, const sample_t *s,
- delta_t free_d5, delta_t physical_swap_d5, delta_t pageout_d5)
- {
- int stress;
- enum state desired = NORMAL;
- if (e->have_last && (s->at - e->last_at > 2.5 || s->at <= e->last_at))
- engine_gap(e);
- /* Physical swap growth is corroboration only, never an independent trigger. */
- stress = s->scan > 0 || (free_d5.valid && free_d5.value < 0) ||
- (e->have_last && s->free < e->last_free) ||
- (physical_swap_d5.valid && physical_swap_d5.value > 0 &&
- pageout_d5.valid && pageout_d5.value > 0 && s->free < s->lots);
- e->floor = s->free < s->min / 2;
- if (s->free < s->lots) {
- if (e->low_since < 0) e->low_since = s->at;
- } else e->low_since = -1;
- if (s->free < s->des && stress) {
- if (e->critical_since < 0) e->critical_since = s->at;
- } else e->critical_since = -1;
- if (e->low_since >= 0 && s->at - e->low_since >= c->pre_s)
- desired = PRE_PRESSURE;
- if (e->low_since >= 0 && s->at - e->low_since >= c->pressure_s && stress)
- desired = PRESSURE;
- if (e->critical_since >= 0 && s->at - e->critical_since >= c->critical_s)
- desired = CRITICAL;
- if (s->free < s->min) desired = EMERGENCY;
- e->danger = desired == CRITICAL || desired == EMERGENCY;
- /* State hysteresis never authorizes an action on its own. */
- if (desired >= PRE_PRESSURE && desired <= EMERGENCY) {
- e->recovery_since = -1;
- if (e->state == RECOVERY || desired > e->state) e->state = desired;
- }
- if (s->free >= s->lots && e->state != NORMAL) {
- e->state = RECOVERY;
- /* 10% exit margin, and no active scanner, for a full recovery. */
- if (s->free - s->lots >= s->lots / 10 && s->scan == 0) {
- if (e->recovery_since < 0) e->recovery_since = s->at;
- if (s->at - e->recovery_since >= c->recovery_s) e->state = NORMAL;
- } else e->recovery_since = -1;
- } else if (s->free < s->lots) {
- e->recovery_since = -1;
- if (e->state == RECOVERY) e->state = PRE_PRESSURE;
- }
- e->last_at = s->at; e->last_free = s->free; e->have_last = 1;
- }
-
- int same_process(const process_t *a, const process_t *b)
- {
- return a->pid == b->pid && a->start_sec == b->start_sec &&
- a->start_nsec == b->start_nsec && a->zid == b->zid &&
- a->zone_generation == b->zone_generation && !strcmp(a->uuid, b->uuid);
- }
-
- int was_victim(const controller_t *c, const process_t *p)
- {
- unsigned i;
- for (i = 0; i < c->nvictims; i++)
- if (same_process(&c->victims[i], p)) return 1;
- return 0;
- }
-
- plan_t action_plan(controller_t *a, const config_t *c, const engine_t *e,
- const sample_t *s, const process_t *candidate)
- {
- plan_t p;
- unsigned i, n = 0;
- memset(&p, 0, sizeof (p)); p.reason = "no_pressure";
- for (i = 0; i < a->nrate; i++)
- if (s->at - a->rate[i] < 60) a->rate[n++] = a->rate[i];
- a->nrate = n;
- if (e->state == NORMAL) a->nvictims = 0;
- if (s->free >= s->lots) a->has_pending = 0;
- if (!e->danger) return p;
- if (a->has_pending) {
- p.reason = "term_grace";
- if (e->floor || (s->free < s->min && s->at - a->term_at >= 1) ||
- s->at - a->term_at >= c->term_s) {
- p.target = a->pending; p.action = KILL;
- p.reason = e->floor ? "floor" : "memory_not_recovered";
- }
- return p;
- }
- if (a->nvictims >= c->max_victims || a->nrate >= c->max_per_minute) {
- p.reason = "victim_budget_exhausted"; return p;
- }
- /* Emergency bypasses the normal cooldown, not protection or budgets. */
- if (s->free >= s->min && s->at < a->next_at) {
- p.reason = "cooldown"; return p;
- }
- if (!candidate || protected_process(c, candidate) || was_victim(a, candidate)) {
- p.reason = "no_authorized_candidate"; return p;
- }
- p.target = *candidate;
- p.action = e->floor ? KILL : TERM;
- p.reason = e->floor ? "floor" : s->free < s->min ? "minfree" : "critical";
- return p;
- }
-
- void action_commit(controller_t *a, const config_t *c, const sample_t *s,
- const plan_t *p)
- {
- if (p->action == NONE) return;
- if (!was_victim(a, &p->target)) {
- if (a->nvictims < MAX_VICTIMS) a->victims[a->nvictims++] = p->target;
- if (a->nrate < 64) a->rate[a->nrate++] = s->at;
- }
- a->next_at = s->at + c->cooldown_s;
- if (p->action == TERM) {
- a->pending = p->target; a->term_at = s->at; a->has_pending = 1;
- } else a->has_pending = 0;
- }
|