Conservative SmartOS global-zone memory pressure guard with dry-run-first policy and SMF integration.
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 

181 行
6.3 KiB

  1. #include "guard.h"
  2. #include <string.h>
  3. #include <limits.h>
  4. void history_add(history_t *h, double at, uint64_t value)
  5. {
  6. h->p[h->next].at = at; h->p[h->next].value = value;
  7. h->next = (h->next + 1) % HIST;
  8. if (h->count < HIST) h->count++;
  9. }
  10. delta_t history_delta(const history_t *h, double now, unsigned seconds)
  11. {
  12. delta_t d = {0, 0, 0};
  13. unsigned i, latest;
  14. double best = 1.25;
  15. if (h->count < 2) return d;
  16. latest = (h->next + HIST - 1) % HIST;
  17. if (now - h->p[latest].at > 1.25) return d;
  18. for (i = 0; i < h->count; i++) {
  19. double age = h->p[latest].at - h->p[i].at;
  20. double error = age - seconds;
  21. if (error < 0) error = -error;
  22. if (age > 0 && error < best && h->p[i].value <= INT64_MAX &&
  23. h->p[latest].value <= INT64_MAX) {
  24. best = error; d.valid = 1; d.age = age;
  25. d.value = (int64_t)h->p[latest].value - (int64_t)h->p[i].value;
  26. }
  27. }
  28. return d;
  29. }
  30. uint64_t rank_score(uint64_t rss, delta_t growth)
  31. {
  32. uint64_t add = growth.valid && growth.value > 0 ? (uint64_t)growth.value : 0;
  33. if (add > (UINT64_MAX - rss) / 2) return UINT64_MAX;
  34. return rss + 2 * add;
  35. }
  36. int swap_physical_bytes_from_blocks(uint64_t total_blocks,
  37. uint64_t free_blocks, uint64_t *bytes)
  38. {
  39. uint64_t used;
  40. if (!bytes || free_blocks > total_blocks) return -1;
  41. used = total_blocks - free_blocks;
  42. if (used > UINT64_MAX / 512) return -1;
  43. *bytes = used * 512;
  44. return 0;
  45. }
  46. const char *state_name(enum state s)
  47. {
  48. static const char *names[] = {"NORMAL", "PRE-PRESSURE", "PRESSURE",
  49. "CRITICAL", "EMERGENCY", "RECOVERY"};
  50. return names[s];
  51. }
  52. void engine_init(engine_t *e)
  53. {
  54. memset(e, 0, sizeof (*e));
  55. engine_gap(e);
  56. }
  57. void engine_gap(engine_t *e)
  58. {
  59. e->low_since = e->critical_since = e->recovery_since = -1;
  60. e->have_last = 0; e->danger = 0; e->floor = 0;
  61. }
  62. void engine_step(engine_t *e, const config_t *c, const sample_t *s,
  63. delta_t free_d5, delta_t physical_swap_d5, delta_t pageout_d5)
  64. {
  65. int stress;
  66. enum state desired = NORMAL;
  67. if (e->have_last && (s->at - e->last_at > 2.5 || s->at <= e->last_at))
  68. engine_gap(e);
  69. /* Physical swap growth is corroboration only, never an independent trigger. */
  70. stress = s->scan > 0 || (free_d5.valid && free_d5.value < 0) ||
  71. (e->have_last && s->free < e->last_free) ||
  72. (physical_swap_d5.valid && physical_swap_d5.value > 0 &&
  73. pageout_d5.valid && pageout_d5.value > 0 && s->free < s->lots);
  74. e->floor = s->free < s->min / 2;
  75. if (s->free < s->lots) {
  76. if (e->low_since < 0) e->low_since = s->at;
  77. } else e->low_since = -1;
  78. if (s->free < s->des && stress) {
  79. if (e->critical_since < 0) e->critical_since = s->at;
  80. } else e->critical_since = -1;
  81. if (e->low_since >= 0 && s->at - e->low_since >= c->pre_s)
  82. desired = PRE_PRESSURE;
  83. if (e->low_since >= 0 && s->at - e->low_since >= c->pressure_s && stress)
  84. desired = PRESSURE;
  85. if (e->critical_since >= 0 && s->at - e->critical_since >= c->critical_s)
  86. desired = CRITICAL;
  87. if (s->free < s->min) desired = EMERGENCY;
  88. e->danger = desired == CRITICAL || desired == EMERGENCY;
  89. /* State hysteresis never authorizes an action on its own. */
  90. if (desired >= PRE_PRESSURE && desired <= EMERGENCY) {
  91. e->recovery_since = -1;
  92. if (e->state == RECOVERY || desired > e->state) e->state = desired;
  93. }
  94. if (s->free >= s->lots && e->state != NORMAL) {
  95. e->state = RECOVERY;
  96. /* 10% exit margin, and no active scanner, for a full recovery. */
  97. if (s->free - s->lots >= s->lots / 10 && s->scan == 0) {
  98. if (e->recovery_since < 0) e->recovery_since = s->at;
  99. if (s->at - e->recovery_since >= c->recovery_s) e->state = NORMAL;
  100. } else e->recovery_since = -1;
  101. } else if (s->free < s->lots) {
  102. e->recovery_since = -1;
  103. if (e->state == RECOVERY) e->state = PRE_PRESSURE;
  104. }
  105. e->last_at = s->at; e->last_free = s->free; e->have_last = 1;
  106. }
  107. int same_process(const process_t *a, const process_t *b)
  108. {
  109. return a->pid == b->pid && a->start_sec == b->start_sec &&
  110. a->start_nsec == b->start_nsec && a->zid == b->zid &&
  111. a->zone_generation == b->zone_generation && !strcmp(a->uuid, b->uuid);
  112. }
  113. int was_victim(const controller_t *c, const process_t *p)
  114. {
  115. unsigned i;
  116. for (i = 0; i < c->nvictims; i++)
  117. if (same_process(&c->victims[i], p)) return 1;
  118. return 0;
  119. }
  120. plan_t action_plan(controller_t *a, const config_t *c, const engine_t *e,
  121. const sample_t *s, const process_t *candidate)
  122. {
  123. plan_t p;
  124. unsigned i, n = 0;
  125. memset(&p, 0, sizeof (p)); p.reason = "no_pressure";
  126. for (i = 0; i < a->nrate; i++)
  127. if (s->at - a->rate[i] < 60) a->rate[n++] = a->rate[i];
  128. a->nrate = n;
  129. if (e->state == NORMAL) a->nvictims = 0;
  130. if (s->free >= s->lots) a->has_pending = 0;
  131. if (!e->danger) return p;
  132. if (a->has_pending) {
  133. p.reason = "term_grace";
  134. if (e->floor || (s->free < s->min && s->at - a->term_at >= 1) ||
  135. s->at - a->term_at >= c->term_s) {
  136. p.target = a->pending; p.action = KILL;
  137. p.reason = e->floor ? "floor" : "memory_not_recovered";
  138. }
  139. return p;
  140. }
  141. if (a->nvictims >= c->max_victims || a->nrate >= c->max_per_minute) {
  142. p.reason = "victim_budget_exhausted"; return p;
  143. }
  144. /* Emergency bypasses the normal cooldown, not protection or budgets. */
  145. if (s->free >= s->min && s->at < a->next_at) {
  146. p.reason = "cooldown"; return p;
  147. }
  148. if (!candidate || protected_process(c, candidate) || was_victim(a, candidate)) {
  149. p.reason = "no_authorized_candidate"; return p;
  150. }
  151. p.target = *candidate;
  152. p.action = e->floor ? KILL : TERM;
  153. p.reason = e->floor ? "floor" : s->free < s->min ? "minfree" : "critical";
  154. return p;
  155. }
  156. void action_commit(controller_t *a, const config_t *c, const sample_t *s,
  157. const plan_t *p)
  158. {
  159. if (p->action == NONE) return;
  160. if (!was_victim(a, &p->target)) {
  161. if (a->nvictims < MAX_VICTIMS) a->victims[a->nvictims++] = p->target;
  162. if (a->nrate < 64) a->rate[a->nrate++] = s->at;
  163. }
  164. a->next_at = s->at + c->cooldown_s;
  165. if (p->action == TERM) {
  166. a->pending = p->target; a->term_at = s->at; a->has_pending = 1;
  167. } else a->has_pending = 0;
  168. }