Merge lp:~thomas-voss/unity-mir/oom_adjust into lp:unity-mir
- oom_adjust
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Gerry Boland |
Approved revision: | 123 |
Merged at revision: | 127 |
Proposed branch: | lp:~thomas-voss/unity-mir/oom_adjust |
Merge into: | lp:unity-mir |
Diff against target: |
250 lines (+212/-0) 1 file modified
src/modules/Unity/Application/taskcontroller.cpp (+212/-0) |
To merge this branch: | bzr merge lp:~thomas-voss/unity-mir/oom_adjust |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gerry Boland (community) | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Mir development team | Pending | ||
Review via email: mp+191123@code.launchpad.net |
Commit message
Slightly refactor the OOM adjustments to be a pure implementation detail of the TaskManager.
Description of the change
Slightly refactor the OOM adjustments to be a pure implementation detail of the TaskManager.
PS Jenkins bot (ps-jenkins) wrote : | # |
Gerry Boland (gerboland) wrote : | # |
+struct OomAdj
+struct OomScoreAdj
Please don't use the "Adj" abbreviation, it's vague. I'd recommend OomAdjuster.
+ static int disableOomKille
+ {
+ return -17;
+ }
Why not just a const static int disableOomKille
+ static int {min,max}Value()
I'd prefer a more descriptive name like "{least,
+ static QString filePattern()
+ {
+ return QString(
+ }
Why this in separate public method? It's only used in applyForPid, and is internal detail.
+ throw std::runtime_
This exception is not caught anywhere, as we tend to avoid using exceptions in our Qt code.
+struct OomScoreAdj
+ static OomScoreAdj leastLikelyToBe
+ {
+ // We could be way more clever here if we knew the distribution
+ // of oom_score_adj values of all app processes. However, we just
+ // make sure that the process is not ignored by the oom killer for now.
+ return OomScoreAdj(
+ }
I'd rather you move this comment beside minValue(), and have minValue=-800 for now.
focusCallback=....
+ DLOG("startCall
Copy/paste typo in the DLOG
TaskController:
+ DLOG("TaskContr
Again, c/p typo
This is a lot of code to do something relatively simple. While I like the clear structuring and documentation, creating an object which just writes a set of values to a file, and a separate object for slightly different values (but with same meaning) to a different file, is overkill IMO.
A singleton class is enough to do this, which would internally handle both proc files and their respective values, and so would remove this complexity from TaskManager's code:
+ if (!updateOomScor
+ {
+ if (!updateOomAdj(pid, OomAdj:
+ DLOG("startCall
+ }
reducing it to just:
if (!OomAdjuster:
DLOG(
Thomas Voß (thomas-voss) wrote : | # |
> +struct OomAdj
> +struct OomScoreAdj
> Please don't use the "Adj" abbreviation, it's vague. I'd recommend
> OomAdjuster.
>
> + static int disableOomKille
> + {
> + return -17;
> + }
> Why not just a const static int disableOomKille
> other *Value() methods.
>
As static constants are less likely to be hit by static initialization fiascos.
> + static int {min,max}Value()
> I'd prefer a more descriptive name like "{least,
>
Hmmm, I intentionally named them min and max, and introduced two more semantic functions to distinguish between what the kernel oom considers min and max and how it is interpreted, respectively.
> + static QString filePattern()
> + {
> + return QString(
> + }
> Why this in separate public method? It's only used in applyForPid, and is
> internal detail.
yup, fair point.
>
> + throw std::runtime_
> This exception is not caught anywhere, as we tend to avoid using exceptions in
> our Qt code.
>
> +struct OomScoreAdj
> + static OomScoreAdj leastLikelyToBe
> + {
> + // We could be way more clever here if we knew the distribution
> + // of oom_score_adj values of all app processes. However, we just
> + // make sure that the process is not ignored by the oom killer for now.
> + return OomScoreAdj(
> + }
> I'd rather you move this comment beside minValue(), and have minValue=-800 for
> now.
>
As explained before, minValue returns the value as defined in linux/oom.h and there is a significant difference between the min value and our interpretation of least likely to be killed.
> focusCallback=....
> + DLOG("startCall
> Copy/paste typo in the DLOG
>
> TaskController:
> + DLOG("TaskContr
> process");
> Again, c/p typo
>
>
>
> This is a lot of code to do something relatively simple. While I like the
> clear structuring and documentation, creating an object which just writes a
> set of values to a file, and a separate object for slightly different values
> (but with same meaning) to a different file, is overkill IMO.
>
> A singleton class is enough to do this, which would internally handle both
> proc files and their respective values, and so would remove this complexity
> from TaskManager's code:
>
I think we should stop using an anti-pattern like a Singleton _now_. It leads to tight coupling and is increasingly difficult to test. That being said, I do agree that we can further condense updateOom{
> + if (!updateOomScor
> + {
> + if (!updateOomAdj(pid, OomAdj:
> + DLOG("startCall
> + }
>
> reducing it to just:
>
> if (!OomAdjuster:
> DLOG("startCall
- 122. By Thomas Voß
-
Address reviewer comments.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:122
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Gerry Boland (gerboland) wrote : | # |
> > Please don't use the "Adj" abbreviation, it's vague. I'd recommend OomAdjuster.
Please address.
> As static constants are less likely to be hit by static initialization fiascos.
I don't understand this, these are integers, not static classes that have some interdependency.
> > + throw std::runtime_
> > This exception is not caught anywhere, as we tend to avoid using exceptions
> > in our Qt code.
I badly phrased this, I wanted you to remove the exception entirely as Qt is not fully exception safe: https:/
- 123. By Thomas Voß
-
* Rename Adj -> Adjuster as per review request.
* Do not throw on construction from invalid value.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:123
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Gerry Boland (gerboland) : | # |
Preview Diff
1 | === modified file 'src/modules/Unity/Application/taskcontroller.cpp' |
2 | --- src/modules/Unity/Application/taskcontroller.cpp 2013-09-26 15:36:27 +0000 |
3 | +++ src/modules/Unity/Application/taskcontroller.cpp 2013-10-15 16:19:00 +0000 |
4 | @@ -32,6 +32,210 @@ |
5 | #include <signal.h> |
6 | #include <unistd.h> |
7 | |
8 | +// linux specific |
9 | +#include <linux/oom.h> |
10 | + |
11 | +namespace |
12 | +{ |
13 | +/** |
14 | + * From man proc: |
15 | + * |
16 | + * This file can be used to adjust the score used to select which |
17 | + * process should be killed in an out-of-memory (OOM) situation. The |
18 | + * kernel uses this value for a bit-shift opera‐ tion of the process's |
19 | + * oom_score value: valid values are in the range -16 to +15, plus the |
20 | + * special value -17, which disables OOM-killing altogether for this |
21 | + * process. A posi‐ tive score increases the likelihood of this process |
22 | + * being killed by the OOM-killer; a negative score decreases the |
23 | + * likelihood. |
24 | + * |
25 | + * The default value for this file is 0; a new process inherits its |
26 | + * parent's oom_adj setting. A process must be privileged |
27 | + * (CAP_SYS_RESOURCE) to update this file. |
28 | + |
29 | + * Since Linux 2.6.36, use of this file is deprecated in favor of |
30 | + * /proc/[pid]/oom_score_adj. |
31 | + */ |
32 | +struct OomAdjuster |
33 | +{ |
34 | + static int disableOomKillerValue() |
35 | + { |
36 | + return OOM_DISABLE; |
37 | + } |
38 | + |
39 | + static int minValue() |
40 | + { |
41 | + return OOM_ADJUST_MIN; |
42 | + } |
43 | + |
44 | + static int maxValue() |
45 | + { |
46 | + return OOM_ADJUST_MAX; |
47 | + } |
48 | + |
49 | + static OomAdjuster leastLikelyToBeKilled() |
50 | + { |
51 | + return OomAdjuster(minValue()); |
52 | + } |
53 | + |
54 | + static OomAdjuster mostLikelyToBeKilled() |
55 | + { |
56 | + return OomAdjuster(maxValue()); |
57 | + } |
58 | + |
59 | + OomAdjuster(int value) : value(value) |
60 | + { |
61 | + } |
62 | + |
63 | + bool isValid() const |
64 | + { |
65 | + return !(value < disableOomKillerValue() || value > maxValue()); |
66 | + } |
67 | + |
68 | + bool applyForPid(pid_t pid) const |
69 | + { |
70 | + auto fn = QString("/proc/%1/oom_adj").arg(pid); |
71 | + QFile file(fn); |
72 | + |
73 | + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) |
74 | + return false; |
75 | + |
76 | + QTextStream out(&file); |
77 | + out << value; |
78 | + |
79 | + return true; |
80 | + } |
81 | + |
82 | + int value; |
83 | +}; |
84 | + |
85 | +/** |
86 | + * From man proc: |
87 | + * |
88 | + * This file can be used to adjust the badness heuristic used to |
89 | + * select which process gets killed in out-of-memory conditions. |
90 | + * |
91 | + * The badness heuristic assigns a value to each candidate |
92 | + * task ranging from 0 (never kill) to 1000 (always kill) |
93 | + * to determine which process is targeted. The units are |
94 | + * roughly a proportion along that range of allowed memory |
95 | + * the process may allocate from, based on an estimation of |
96 | + * its current memory and swap use. For example, if a task |
97 | + * is using all allowed memory, its badness score will be |
98 | + * 1000. If it is using half of its allowed memory, its |
99 | + * score will be 500. |
100 | + * |
101 | + * There is an additional factor included in the badness score: root |
102 | + * processes are given 3% extra memory over other tasks. |
103 | + * |
104 | + * The amount of "allowed" memory depends on the context in |
105 | + * which the OOM-killer was called. If it is due to the |
106 | + * memory assigned to the allocating task's cpuset being |
107 | + * exhausted, the allowed memory represents the set of mems |
108 | + * assigned to that cpuset (see cpuset(7)). If it is due |
109 | + * to a mempolicy's node(s) being exhausted, the allowed |
110 | + * memory represents the set of mempolicy nodes. If it is |
111 | + * due to a memory limit (or swap limit) being reached, the |
112 | + * allowed memory is that configured limit. Finally, if it |
113 | + * is due to the entire system being out of memory, the |
114 | + * allowed memory represents all allocatable resources. |
115 | + * |
116 | + * The value of oom_score_adj is added to the badness score before it |
117 | + * is used to determine which task to kill. Acceptable values range |
118 | + * from -1000 (OOM_SCORE_ADJ_MIN) to +1000 (OOM_SCORE_ADJ_MAX). This |
119 | + * allows user space to control the preference for OOM-killing, |
120 | + * ranging from always preferring a certain task or completely |
121 | + * disabling it from OOM- killing. The lowest possible value, -1000, |
122 | + * is equivalent to disabling OOM-killing entirely for that task, |
123 | + * since it will always report a badness score of 0. |
124 | + * |
125 | + * Consequently, it is very simple for user space to define the amount |
126 | + * of memory to consider for each task. Setting a oom_score_adj value |
127 | + * of +500, for example, is roughly equiv‐ alent to allowing the |
128 | + * remainder of tasks sharing the same system, cpuset, mempolicy, or |
129 | + * memory controller resources to use at least 50% more memory. A |
130 | + * value of -500, on the other hand, would be roughly equivalent to |
131 | + * discounting 50% of the task's allowed memory from being considered |
132 | + * as scoring against the task. |
133 | + * |
134 | + * For backward compatibility with previous kernels, |
135 | + * /proc/[pid]/oom_adj can still be used to tune the badness score. |
136 | + * Its value is scaled linearly with oom_score_adj. |
137 | + * |
138 | + * Writing to /proc/[pid]/oom_score_adj or |
139 | + * /proc/[pid]/oom_adj will change the other with its |
140 | + * scaled value. |
141 | + */ |
142 | +struct OomScoreAdjuster |
143 | +{ |
144 | + static int disableOomKillerValue() |
145 | + { |
146 | + return OOM_SCORE_ADJ_MIN; |
147 | + } |
148 | + |
149 | + static int minValue() |
150 | + { |
151 | + return OOM_SCORE_ADJ_MIN; |
152 | + } |
153 | + |
154 | + static int maxValue() |
155 | + { |
156 | + return OOM_SCORE_ADJ_MAX; |
157 | + } |
158 | + |
159 | + static OomScoreAdjuster leastLikelyToBeKilled() |
160 | + { |
161 | + // We could be way more clever here if we knew the distribution |
162 | + // of oom_score_adj values of all app processes. However, we just |
163 | + // make sure that the process is not ignored by the oom killer for now. |
164 | + return OomScoreAdjuster(minValue()+200); |
165 | + } |
166 | + |
167 | + static OomScoreAdjuster mostLikelyToBeKilled() |
168 | + { |
169 | + return OomScoreAdjuster(maxValue()); |
170 | + } |
171 | + |
172 | + OomScoreAdjuster(int value) : value(value) |
173 | + { |
174 | + } |
175 | + |
176 | + bool isValid() const |
177 | + { |
178 | + return !(value < disableOomKillerValue() || value > maxValue()); |
179 | + } |
180 | + |
181 | + bool applyForPid(pid_t pid) const |
182 | + { |
183 | + auto fn = QString("/proc/%1/oom_score_adj").arg(pid); |
184 | + QFile file(fn); |
185 | + |
186 | + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) |
187 | + return false; |
188 | + |
189 | + QTextStream out(&file); |
190 | + out << value; |
191 | + |
192 | + return true; |
193 | + } |
194 | + |
195 | + int value; |
196 | +}; |
197 | + |
198 | +void ensureProcessIsUnlikelyToBeKilled(pid_t pid) |
199 | +{ |
200 | + if (!OomScoreAdjuster::leastLikelyToBeKilled().applyForPid(pid)) |
201 | + if (!OomAdjuster::leastLikelyToBeKilled().applyForPid(pid)) |
202 | + DLOG("ensureProcessIsUnlikelyToBeKilled failed"); |
203 | +} |
204 | + |
205 | +void ensureProcessIsLikelyToBeKilled(pid_t pid) |
206 | +{ |
207 | + if (!OomScoreAdjuster::mostLikelyToBeKilled().applyForPid(pid)) |
208 | + if (!OomAdjuster::mostLikelyToBeKilled().applyForPid(pid)) |
209 | + DLOG("ensureProcessIsLikelyToBeKilled failed"); |
210 | +} |
211 | +} |
212 | |
213 | TaskController* TaskController::m_theTaskController = nullptr; |
214 | |
215 | @@ -48,6 +252,8 @@ |
216 | { |
217 | startCallback = [](const gchar * appId, gpointer userData) { |
218 | Q_UNUSED(userData) |
219 | + pid_t pid = upstart_app_launch_get_primary_pid(appId); |
220 | + ensureProcessIsUnlikelyToBeKilled(pid); |
221 | Q_EMIT TaskController::singleton()->processStartReport(QString(appId), false); |
222 | }; |
223 | |
224 | @@ -58,6 +264,8 @@ |
225 | |
226 | focusCallback = [](const gchar * appId, gpointer userData) { |
227 | Q_UNUSED(userData) |
228 | + pid_t pid = upstart_app_launch_get_primary_pid(appId); |
229 | + ensureProcessIsUnlikelyToBeKilled(pid); |
230 | Q_EMIT TaskController::singleton()->requestFocus(QString(appId)); |
231 | }; |
232 | |
233 | @@ -137,6 +345,8 @@ |
234 | DLOG("TaskController::suspend (this=%p, application=%p)", this, qPrintable(appId)); |
235 | pid_t pid = upstart_app_launch_get_primary_pid(appId.toLatin1().constData()); |
236 | |
237 | + ensureProcessIsLikelyToBeKilled(pid); |
238 | + |
239 | if (pid) { |
240 | kill(pid, SIGSTOP); |
241 | return true; |
242 | @@ -150,6 +360,8 @@ |
243 | DLOG("TaskController::resume (this=%p, application=%p)", this, qPrintable(appId)); |
244 | pid_t pid = upstart_app_launch_get_primary_pid(appId.toLatin1().constData()); |
245 | |
246 | + ensureProcessIsUnlikelyToBeKilled(pid); |
247 | + |
248 | if (pid) { |
249 | kill(pid, SIGCONT); |
250 | return true; |
PASSED: Continuous integration, rev:121 jenkins. qa.ubuntu. com/job/ unity-mir- ci/125/ jenkins. qa.ubuntu. com/job/ unity-mir- saucy-amd64- ci/67 jenkins. qa.ubuntu. com/job/ unity-mir- saucy-armhf- ci/125 jenkins. qa.ubuntu. com/job/ unity-mir- saucy-armhf- ci/125/ artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ unity-mir- saucy-i386- ci/125
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: 10.97.0. 26:8080/ job/unity- mir-ci/ 125/rebuild
http://