blob: 959ce223255ad1a9432c93d5d697340986eeb80e [file] [log] [blame]
Dale Curtis8adf7892011-09-08 16:13:36 -07001import os, logging
2import time
3from tempfile import NamedTemporaryFile
4
5from autotest_lib.client.bin import test, utils
6from autotest_lib.client.common_lib import error
7from cgroup_common import Cgroup as CG
8from cgroup_common import CgroupModules
9
10class cgroup(test.test):
11 """
12 Tests the cgroup functionalities. It works by creating a process (which is
13 also a python application) that will try to use CPU and memory. We will
14 then verify whether the cgroups rules are obeyed.
15 """
16 version = 1
17 _client = ""
18 modules = CgroupModules()
19
20 def run_once(self):
21 """
22 Try to access different resources which are restricted by cgroup.
23 """
24 logging.info('Starting cgroup testing')
25
26 err = ""
27 # Run available tests
28 for i in ['memory', 'cpuset']:
29 logging.info("---< 'test_%s' START >---", i)
30 try:
31 if not self.modules.get_pwd(i):
32 raise error.TestFail("module not available/mounted")
33 t_function = getattr(self, "test_%s" % i)
34 t_function()
35 logging.info("---< 'test_%s' PASSED >---", i)
36 except AttributeError:
37 err += "%s, " % i
38 logging.error("test_%s: Test doesn't exist", i)
39 logging.info("---< 'test_%s' FAILED >---", i)
40 except Exception, inst:
41 err += "%s, " % i
42 logging.error("test_%s: %s", i, inst)
43 logging.info("---< 'test_%s' FAILED >---", i)
44
45 if err:
46 logging.error('Some subtests failed (%s)' % err[:-2])
47 raise error.TestFail('Some subtests failed (%s)' % err[:-2])
48
49
50 def setup(self):
51 """
52 Setup
53 """
54 logging.debug('Setting up cgroups modules')
55
56 self._client = os.path.join(self.bindir, "cgroup_client.py")
57
58 _modules = ['cpuset', 'ns', 'cpu', 'cpuacct', 'memory', 'devices',
59 'freezer', 'net_cls', 'blkio']
60 if (self.modules.init(_modules) <= 0):
61 raise error.TestFail('Can\'t mount any cgroup modules')
62
63
64 def cleanup(self):
65 """
66 Unmount all cgroups and remove directories
67 """
68 logging.info('Cleanup')
69 self.modules.cleanup()
70
71
72 #############################
73 # TESTS
74 #############################
75 def test_memory(self):
76 """
77 Memory test
78 """
79 def cleanup(supress=False):
80 # cleanup
81 logging.debug("test_memory: Cleanup")
82 err = ""
83 if item.rm_cgroup(pwd):
84 err += "\nCan't remove cgroup directory"
85
86 utils.system("swapon -a")
87
88 if err:
89 if supress:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -070090 logging.warning("Some parts of cleanup failed%s" % err)
Dale Curtis8adf7892011-09-08 16:13:36 -070091 else:
92 raise error.TestFail("Some parts of cleanup failed%s" % err)
93
94 # Preparation
95 item = CG('memory', self._client)
96 if item.initialize(self.modules):
97 raise error.TestFail("cgroup init failed")
98
99 if item.smoke_test():
100 raise error.TestFail("smoke_test failed")
101
102 pwd = item.mk_cgroup()
103 if pwd == None:
104 raise error.TestFail("Can't create cgroup")
105
106 logging.debug("test_memory: Memory filling test")
107
108 f = open('/proc/meminfo','r')
109 mem = f.readline()
110 while not mem.startswith("MemFree"):
111 mem = f.readline()
112 # Use only 1G or max of the free memory
113 mem = min(int(mem.split()[1])/1024, 1024)
114 mem = max(mem, 100) # at least 100M
115 memsw_limit_bytes = item.get_property("memory.memsw.limit_in_bytes",
116 supress=True)
117 if memsw_limit_bytes is not None:
118 memsw = True
119 # Clear swap
120 utils.system("swapoff -a")
121 utils.system("swapon -a")
122 f.seek(0)
123 swap = f.readline()
124 while not swap.startswith("SwapTotal"):
125 swap = f.readline()
126 swap = int(swap.split()[1])/1024
127 if swap < mem / 2:
128 logging.error("Not enough swap memory to test 'memsw'")
129 memsw = False
130 else:
131 # Doesn't support swap + memory limitation, disable swap
132 logging.info("System does not support 'memsw'")
133 utils.system("swapoff -a")
134 memsw = False
135 outf = NamedTemporaryFile('w+', prefix="cgroup_client-",
136 dir="/tmp")
137 logging.debug("test_memory: Initializition passed")
138
139 ################################################
140 # Fill the memory without cgroup limitation
141 # Should pass
142 ################################################
143 logging.debug("test_memory: Memfill WO cgroup")
144 ps = item.test("memfill %d %s" % (mem, outf.name))
145 ps.stdin.write('\n')
146 i = 0
147 while ps.poll() == None:
148 if i > 60:
149 break
150 i += 1
151 time.sleep(1)
152 if i > 60:
153 ps.terminate()
154 raise error.TestFail("Memory filling failed (WO cgroup)")
155 outf.seek(0)
156 outf.flush()
157 out = outf.readlines()
158 if (len(out) < 2) or (ps.poll() != 0):
159 raise error.TestFail("Process failed (WO cgroup); output:\n%s"
160 "\nReturn: %d" % (out, ps.poll()))
161 if not out[-1].startswith("PASS"):
162 raise error.TestFail("Unsuccessful memory filling "
163 "(WO cgroup)")
164 logging.debug("test_memory: Memfill WO cgroup passed")
165
166 ################################################
167 # Fill the memory with 1/2 memory limit
168 # memsw: should swap out part of the process and pass
169 # WO memsw: should fail (SIGKILL)
170 ################################################
171 logging.debug("test_memory: Memfill mem only limit")
172 ps = item.test("memfill %d %s" % (mem, outf.name))
173 if item.set_cgroup(ps.pid, pwd):
174 raise error.TestFail("Could not set cgroup")
175 if item.set_prop("memory.limit_in_bytes", ("%dM" % (mem/2)), pwd):
176 raise error.TestFail("Could not set mem limit (mem)")
177 ps.stdin.write('\n')
178 i = 0
179 while ps.poll() == None:
180 if i > 120:
181 break
182 i += 1
183 time.sleep(1)
184 if i > 120:
185 ps.terminate()
186 raise error.TestFail("Memory filling failed (mem)")
187 outf.seek(0)
188 outf.flush()
189 out = outf.readlines()
190 if (len(out) < 2):
191 raise error.TestFail("Process failed (mem); output:\n%s"
192 "\nReturn: %d" % (out, ps.poll()))
193 if memsw:
194 if not out[-1].startswith("PASS"):
195 logging.error("test_memory: cgroup_client.py returned %d; "
196 "output:\n%s", ps.poll(), out)
197 raise error.TestFail("Unsuccessful memory filling (mem)")
198 else:
199 if out[-1].startswith("PASS"):
200 raise error.TestFail("Unexpected memory filling (mem)")
201 else:
202 filled = int(out[-2].split()[1][:-1])
203 if mem/2 > 1.5 * filled:
204 logging.error("test_memory: Limit = %dM, Filled = %dM (+ "
205 "python overhead upto 1/3 (mem))", mem/2,
206 filled)
207 else:
208 logging.debug("test_memory: Limit = %dM, Filled = %dM (+ "
209 "python overhead upto 1/3 (mem))", mem/2,
210 filled)
211 logging.debug("test_memory: Memfill mem only cgroup passed")
212
213 ################################################
214 # Fill the memory with 1/2 memory+swap limit
215 # Should fail
216 # (memory.limit_in_bytes have to be set prior to this test)
217 ################################################
218 if memsw:
219 logging.debug("test_memory: Memfill mem + swap limit")
220 ps = item.test("memfill %d %s" % (mem, outf.name))
221 if item.set_cgroup(ps.pid, pwd):
222 raise error.TestFail("Could not set cgroup (memsw)")
223 if item.set_prop("memory.memsw.limit_in_bytes", "%dM"%(mem/2), pwd):
224 raise error.TestFail("Could not set mem limit (memsw)")
225 ps.stdin.write('\n')
226 i = 0
227 while ps.poll() == None:
228 if i > 120:
229 break
230 i += 1
231 time.sleep(1)
232 if i > 120:
233 ps.terminate()
234 raise error.TestFail("Memory filling failed (mem)")
235 outf.seek(0)
236 outf.flush()
237 out = outf.readlines()
238 if (len(out) < 2):
239 raise error.TestFail("Process failed (memsw); output:\n%s"
240 "\nReturn: %d" % (out, ps.poll()))
241 if out[-1].startswith("PASS"):
242 raise error.TestFail("Unexpected memory filling (memsw)",
243 mem)
244 else:
245 filled = int(out[-2].split()[1][:-1])
246 if mem / 2 > 1.5 * filled:
247 logging.error("test_memory: Limit = %dM, Filled = %dM (+ "
248 "python overhead upto 1/3 (memsw))", mem/2,
249 filled)
250 else:
251 logging.debug("test_memory: Limit = %dM, Filled = %dM (+ "
252 "python overhead upto 1/3 (memsw))", mem/2,
253 filled)
254 logging.debug("test_memory: Memfill mem + swap cgroup passed")
255
256 ################################################
257 # CLEANUP
258 ################################################
259 cleanup()
260
261
262
263 def test_cpuset(self):
264 """
265 Cpuset test
266 1) Initiate CPU load on CPU0, than spread into CPU* - CPU0
267 """
268 class per_cpu_load:
269 """
270 Handles the per_cpu_load stats
271 self.values [cpus, cpu0, cpu1, ...]
272 """
273 def __init__(self):
274 """
275 Init
276 """
277 self.values = []
278 self.f = open('/proc/stat', 'r')
279 line = self.f.readline()
280 while line:
281 if line.startswith('cpu'):
282 self.values.append(int(line.split()[1]))
283 else:
284 break
285 line = self.f.readline()
286
287 def reload(self):
288 """
289 Reload current values
290 """
291 self.values = self.get()
292
293 def get(self):
294 """
295 Get the current values
296 @return vals: array of current values [cpus, cpu0, cpu1..]
297 """
298 self.f.seek(0)
299 self.f.flush()
300 vals = []
301 for i in range(len(self.values)):
302 vals.append(int(self.f.readline().split()[1]))
303 return vals
304
305 def tick(self):
306 """
307 Reload values and returns the load between the last tick/reload
308 @return vals: array of load between ticks/reloads
309 values [cpus, cpu0, cpu1..]
310 """
311 vals = self.get()
312 ret = []
313 for i in range(len(self.values)):
314 ret.append(vals[i] - self.values[i])
315 self.values = vals
316 return ret
317
318 def cleanup(supress=False):
319 # cleanup
320 logging.debug("test_cpuset: Cleanup")
321 err = ""
322 try:
323 for task in tasks:
324 for i in range(10):
325 task.terminate()
326 if task.poll() != None:
327 break
328 time.sleep(1)
329 if i >= 9:
330 logging.error("test_cpuset: Subprocess didn't finish")
331 except Exception, inst:
332 err += "\nCan't terminate tasks: %s" % inst
333 if item.rm_cgroup(pwd):
334 err += "\nCan't remove cgroup direcotry"
335 if err:
336 if supress:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700337 logging.warning("Some parts of cleanup failed%s" % err)
Dale Curtis8adf7892011-09-08 16:13:36 -0700338 else:
339 raise error.TestFail("Some parts of cleanup failed%s" % err)
340
341 # Preparation
342 item = CG('cpuset', self._client)
343 if item.initialize(self.modules):
344 raise error.TestFail("cgroup init failed")
345
346 # FIXME: new cpuset cgroup doesn't have any mems and cpus assigned
347 # thus smoke_test won't work
348 #if item.smoke_test():
349 # raise error.TestFail("smoke_test failed")
350
351 try:
352 # Available cpus: cpuset.cpus = "0-$CPUS\n"
353 no_cpus = int(item.get_prop("cpuset.cpus").split('-')[1]) + 1
354 except:
355 raise error.TestFail("Failed to get no_cpus or no_cpus = 1")
356
357 pwd = item.mk_cgroup()
358 if pwd == None:
359 raise error.TestFail("Can't create cgroup")
360 # FIXME: new cpuset cgroup doesn't have any mems and cpus assigned
361 try:
362 tmp = item.get_prop("cpuset.cpus")
363 item.set_property("cpuset.cpus", tmp, pwd)
364 tmp = item.get_prop("cpuset.mems")
365 item.set_property("cpuset.mems", tmp, pwd)
366 except:
367 cleanup(True)
368 raise error.TestFail("Failed to set cpus and mems of"
369 "a new cgroup")
370
371 ################################################
372 # Cpu allocation test
373 # Use cpu0 and verify, than all cpu* - cpu0 and verify
374 ################################################
375 logging.debug("test_cpuset: Cpu allocation test")
376
377 tasks = []
378 # Run no_cpus + 1 jobs
379 for i in range(no_cpus + 1):
380 tasks.append(item.test("cpu"))
381 if item.set_cgroup(tasks[i].pid, pwd):
382 cleanup(True)
383 raise error.TestFail("Failed to set cgroup")
384 tasks[i].stdin.write('\n')
385 stats = per_cpu_load()
386 # Use only the first CPU
387 item.set_property("cpuset.cpus", 0, pwd)
388 stats.reload()
389 time.sleep(10)
390 # [0] = all cpus
391 s1 = stats.tick()[1:]
392 s2 = s1[1:]
393 s1 = s1[0]
394 for _s in s2:
395 if s1 < _s:
396 cleanup(True)
397 raise error.TestFail("Unused processor had higher utilization\n"
398 "used cpu: %s, remaining cpus: %s"
399 % (s1, s2))
400
401 if no_cpus == 2:
402 item.set_property("cpuset.cpus", "1", pwd)
403 else:
404 item.set_property("cpuset.cpus", "1-%d"%(no_cpus-1), pwd)
405 stats.reload()
406 time.sleep(10)
407 s1 = stats.tick()[1:]
408 s2 = s1[0]
409 s1 = s1[1:]
410 for _s in s1:
411 if s2 > _s:
412 cleanup(True)
413 raise error.TestFail("Unused processor had higher utilization\n"
414 "used cpus: %s, remaining cpu: %s"
415 % (s1, s2))
416 logging.debug("test_cpuset: Cpu allocation test passed")
417
418 ################################################
419 # CLEANUP
420 ################################################
421 cleanup()