History

psi: introduce state_mask to represent stalled psi states

Patch series "psi: pressure stall monitors", v6. This is a respin of: https://lwn.net/ml/linux-kernel/20190308184311.144521-1-surenb%40google.com/ Android is adopting psi to detect and remedy memory pressure that results in stuttering and decreased responsiveness on mobile devices. Psi gives us the stall information, but because we're dealing with latencies in the millisecond range, periodically reading the pressure files to detect stalls in a timely fashion is not feasible. Psi also doesn't aggregate its averages at a high-enough frequency right now. This patch series extends the psi interface such that users can configure sensitive latency thresholds and use poll() and friends to be notified when these are breached. As high-frequency aggregation is costly, it implements an aggregation method that is optimized for fast, short-interval averaging, and makes the aggregation frequency adaptive, such that high-frequency updates only happen while monitored stall events are actively occurring. With these patches applied, Android can monitor for, and ward off, mounting memory shortages before they cause problems for the user. For example, using memory stall monitors in userspace low memory killer daemon (lmkd) we can detect mounting pressure and kill less important processes before device becomes visibly sluggish. In our memory stress testing psi memory monitors produce roughly 10x less false positives compared to vmpressure signals. Having ability to specify multiple triggers for the same psi metric allows other parts of Android framework to monitor memory state of the device and act accordingly. The new interface is straight-forward. The user opens one of the pressure files for writing and writes a trigger description into the file descriptor that defines the stall state - some or full, and the maximum stall time over a given window of time. E.g.: /* Signal when stall time exceeds 100ms of a 1s window */ char trigger[] = "full 100000 1000000" fd = open("/proc/pressure/memory") write(fd, trigger, sizeof(trigger)) while (poll() >= 0) { ... }; close(fd); When the monitored stall state is entered, psi adapts its aggregation frequency according to what the configured time window requires in order to emit event signals in a timely fashion. Once the stalling subsides, aggregation reverts back to normal. The trigger is associated with the open file descriptor. To stop monitoring, the user only needs to close the file descriptor and the trigger is discarded. Patches 1-6 prepare the psi code for polling support. Patch 7 implements the adaptive polling logic, the pressure growth detection optimized for short intervals, and hooks up write() and poll() on the pressure files. The patches were developed in collaboration with Johannes Weiner. This patch (of 7): The psi monitoring patches will need to determine the same states as record_times(). To avoid calculating them twice, maintain a state mask that can be consulted cheaply. Do this in a separate patch to keep the churn in the main feature patch at a minimum. This adds 4-byte state_mask member into psi_group_cpu struct which results in its first cacheline-aligned part becoming 52 bytes long. Add explicit values to enumeration element counters that affect psi_group_cpu struct size.

Suren Baghdasaryan committed 7 years ago

Commit 33b2d631 parent 136ac592 files · +25 −13Browse files

Unified · Split

2 files
  1. include/linux/psi_types.h +6 −3
  2. kernel/sched/psi.c +19 −10
include/linux/psi_types.h+6−3
@@ -11,7 +11,7 @@ enum psi_task_count { · 10 unmodified lines
11 NR_IOWAIT,11 NR_IOWAIT,
12 NR_MEMSTALL,12 NR_MEMSTALL,
13 NR_RUNNING,13 NR_RUNNING,
14 NR_PSI_TASK_COUNTS,14 NR_PSI_TASK_COUNTS = 3,
15};15};
16 16
17/* Task state bitmasks */17/* Task state bitmasks */
@@ -24,7 +24,7 @@ enum psi_res { · 6 unmodified lines
24 PSI_IO,24 PSI_IO,
25 PSI_MEM,25 PSI_MEM,
26 PSI_CPU,26 PSI_CPU,
27 NR_PSI_RESOURCES,27 NR_PSI_RESOURCES = 3,
28};28};
29 29
30/*30/*
@@ -41,7 +41,7 @@ enum psi_states { · 10 unmodified lines
41 PSI_CPU_SOME,41 PSI_CPU_SOME,
42 /* Only per-CPU, to weigh the CPU in the global average: */42 /* Only per-CPU, to weigh the CPU in the global average: */
43 PSI_NONIDLE,43 PSI_NONIDLE,
44 NR_PSI_STATES,44 NR_PSI_STATES = 6,
45};45};
46 46
47struct psi_group_cpu {47struct psi_group_cpu {
@@ -53,6 +53,9 @@ struct psi_group_cpu { · 5 unmodified lines
53 /* States of the tasks belonging to this group */53 /* States of the tasks belonging to this group */
54 unsigned int tasks[NR_PSI_TASK_COUNTS];54 unsigned int tasks[NR_PSI_TASK_COUNTS];
55 55
56 /* Aggregate pressure state derived from the tasks */
57 u32 state_mask;
58
56 /* Period time sampling buckets for each state of interest (ns) */59 /* Period time sampling buckets for each state of interest (ns) */
57 u32 times[NR_PSI_STATES];60 u32 times[NR_PSI_STATES];
58 61
kernel/sched/psi.c+19−10
@@ -213,17 +213,17 @@ static bool test_state(unsigned int *tasks, enum psi_states state) · 212 unmodified lines
213static void get_recent_times(struct psi_group *group, int cpu, u32 *times)213static void get_recent_times(struct psi_group *group, int cpu, u32 *times)
214{214{
215 struct psi_group_cpu *groupc = per_cpu_ptr(group->pcpu, cpu);215 struct psi_group_cpu *groupc = per_cpu_ptr(group->pcpu, cpu);
216 unsigned int tasks[NR_PSI_TASK_COUNTS];
217 u64 now, state_start;216 u64 now, state_start;
217 enum psi_states s;
218 unsigned int seq;218 unsigned int seq;
219 int s;219 u32 state_mask;
220 220
221 /* Snapshot a coherent view of the CPU state */221 /* Snapshot a coherent view of the CPU state */
222 do {222 do {
223 seq = read_seqcount_begin(&groupc->seq);223 seq = read_seqcount_begin(&groupc->seq);
224 now = cpu_clock(cpu);224 now = cpu_clock(cpu);
225 memcpy(times, groupc->times, sizeof(groupc->times));225 memcpy(times, groupc->times, sizeof(groupc->times));
226 memcpy(tasks, groupc->tasks, sizeof(groupc->tasks));226 state_mask = groupc->state_mask;
227 state_start = groupc->state_start;227 state_start = groupc->state_start;
228 } while (read_seqcount_retry(&groupc->seq, seq));228 } while (read_seqcount_retry(&groupc->seq, seq));
229 229
@@ -239,7 +239,7 @@ static void get_recent_times(struct psi_group *group, int cpu, u32 *times) · 9 unmodified lines
239 * (u32) and our reported pressure close to what's239 * (u32) and our reported pressure close to what's
240 * actually happening.240 * actually happening.
241 */241 */
242 if (test_state(tasks, s))242 if (state_mask & (1 << s))
243 times[s] += now - state_start;243 times[s] += now - state_start;
244 244
245 delta = times[s] - groupc->times_prev[s];245 delta = times[s] - groupc->times_prev[s];
@@ -407,15 +407,15 @@ static void record_times(struct psi_group_cpu *groupc, int cpu, · 161 unmodified lines
407 delta = now - groupc->state_start;407 delta = now - groupc->state_start;
408 groupc->state_start = now;408 groupc->state_start = now;
409 409
410 if (test_state(groupc->tasks, PSI_IO_SOME)) {410 if (groupc->state_mask & (1 << PSI_IO_SOME)) {
411 groupc->times[PSI_IO_SOME] += delta;411 groupc->times[PSI_IO_SOME] += delta;
412 if (test_state(groupc->tasks, PSI_IO_FULL))412 if (groupc->state_mask & (1 << PSI_IO_FULL))
413 groupc->times[PSI_IO_FULL] += delta;413 groupc->times[PSI_IO_FULL] += delta;
414 }414 }
415 415
416 if (test_state(groupc->tasks, PSI_MEM_SOME)) {416 if (groupc->state_mask & (1 << PSI_MEM_SOME)) {
417 groupc->times[PSI_MEM_SOME] += delta;417 groupc->times[PSI_MEM_SOME] += delta;
418 if (test_state(groupc->tasks, PSI_MEM_FULL))418 if (groupc->state_mask & (1 << PSI_MEM_FULL))
419 groupc->times[PSI_MEM_FULL] += delta;419 groupc->times[PSI_MEM_FULL] += delta;
420 else if (memstall_tick) {420 else if (memstall_tick) {
421 u32 sample;421 u32 sample;
@@ -436,10 +436,10 @@ static void record_times(struct psi_group_cpu *groupc, int cpu, · 14 unmodified lines
436 }436 }
437 }437 }
438 438
439 if (test_state(groupc->tasks, PSI_CPU_SOME))439 if (groupc->state_mask & (1 << PSI_CPU_SOME))
440 groupc->times[PSI_CPU_SOME] += delta;440 groupc->times[PSI_CPU_SOME] += delta;
441 441
442 if (test_state(groupc->tasks, PSI_NONIDLE))442 if (groupc->state_mask & (1 << PSI_NONIDLE))
443 groupc->times[PSI_NONIDLE] += delta;443 groupc->times[PSI_NONIDLE] += delta;
444}444}
445 445
@@ -448,6 +448,8 @@ static void psi_group_change(struct psi_group *group, int cpu, · 2 unmodified lines
448{448{
449 struct psi_group_cpu *groupc;449 struct psi_group_cpu *groupc;
450 unsigned int t, m;450 unsigned int t, m;
451 enum psi_states s;
452 u32 state_mask = 0;
451 453
452 groupc = per_cpu_ptr(group->pcpu, cpu);454 groupc = per_cpu_ptr(group->pcpu, cpu);
453 455
@@ -480,6 +482,13 @@ static void psi_group_change(struct psi_group *group, int cpu, · 26 unmodified lines
480 if (set & (1 << t))482 if (set & (1 << t))
481 groupc->tasks[t]++;483 groupc->tasks[t]++;
482 484
485 /* Calculate state mask representing active states */
486 for (s = 0; s < NR_PSI_STATES; s++) {
487 if (test_state(groupc->tasks, s))
488 state_mask |= (1 << s);
489 }
490 groupc->state_mask = state_mask;
491
483 write_seqcount_end(&groupc->seq);492 write_seqcount_end(&groupc->seq);
484}493}
485 494

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t