Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 1 | # Copyright 2014-2015 ARM Limited |
| 2 | # |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | # you may not use this file except in compliance with the License. |
| 5 | # You may obtain a copy of the License at |
| 6 | # |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | # |
| 9 | # Unless required by applicable law or agreed to in writing, software |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | # See the License for the specific language governing permissions and |
| 13 | # limitations under the License. |
| 14 | # |
| 15 | # pylint: disable=attribute-defined-outside-init |
| 16 | import logging |
| 17 | from collections import namedtuple |
| 18 | |
| 19 | from devlib.module import Module |
| 20 | from devlib.exception import TargetError |
| 21 | from devlib.utils.misc import list_to_ranges, isiterable |
| 22 | from devlib.utils.types import boolean |
| 23 | |
| 24 | |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 25 | class Controller(object): |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 26 | |
Patrick Bellasi | b569a56 | 2016-11-29 10:39:28 +0000 | [diff] [blame] | 27 | def __init__(self, kind, hid, clist): |
| 28 | """ |
| 29 | Initialize a controller given the hierarchy it belongs to. |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 30 | |
Patrick Bellasi | b569a56 | 2016-11-29 10:39:28 +0000 | [diff] [blame] | 31 | :param kind: the name of the controller |
| 32 | :type kind: str |
| 33 | |
| 34 | :param hid: the Hierarchy ID this controller is mounted on |
| 35 | :type hid: int |
| 36 | |
| 37 | :param clist: the list of controller mounted in the same hierarchy |
| 38 | :type clist: list(str) |
| 39 | """ |
| 40 | self.mount_name = 'devlib_cgh{}'.format(hid) |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 41 | self.kind = kind |
Patrick Bellasi | b569a56 | 2016-11-29 10:39:28 +0000 | [diff] [blame] | 42 | self.hid = hid |
| 43 | self.clist = clist |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 44 | self.target = None |
Patrick Bellasi | 15f9c03 | 2016-04-26 15:23:56 +0100 | [diff] [blame] | 45 | self._noprefix = False |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 46 | |
Patrick Bellasi | b569a56 | 2016-11-29 10:39:28 +0000 | [diff] [blame] | 47 | self.logger = logging.getLogger('CGroup.'+self.kind) |
| 48 | self.logger.debug('Initialized [%s, %d, %s]', |
| 49 | self.kind, self.hid, self.clist) |
| 50 | |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 51 | self.mount_point = None |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 52 | self._cgroups = {} |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 53 | |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 54 | def mount(self, target, mount_root): |
| 55 | |
| 56 | mounted = target.list_file_systems() |
| 57 | if self.mount_name in [e.device for e in mounted]: |
| 58 | # Identify mount point if controller is already in use |
| 59 | self.mount_point = [ |
| 60 | fs.mount_point |
| 61 | for fs in mounted |
| 62 | if fs.device == self.mount_name |
| 63 | ][0] |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 64 | else: |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 65 | # Mount the controller if not already in use |
| 66 | self.mount_point = target.path.join(mount_root, self.mount_name) |
| 67 | target.execute('mkdir -p {} 2>/dev/null'\ |
| 68 | .format(self.mount_point), as_root=True) |
| 69 | target.execute('mount -t cgroup -o {} {} {}'\ |
Patrick Bellasi | b569a56 | 2016-11-29 10:39:28 +0000 | [diff] [blame] | 70 | .format(','.join(self.clist), |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 71 | self.mount_name, |
| 72 | self.mount_point), |
| 73 | as_root=True) |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 74 | |
Patrick Bellasi | 15f9c03 | 2016-04-26 15:23:56 +0100 | [diff] [blame] | 75 | # Check if this controller uses "noprefix" option |
| 76 | output = target.execute('mount | grep "{} "'.format(self.mount_name)) |
| 77 | if 'noprefix' in output: |
| 78 | self._noprefix = True |
| 79 | # self.logger.debug('Controller %s using "noprefix" option', |
| 80 | # self.kind) |
| 81 | |
| 82 | self.logger.debug('Controller %s mounted under: %s (noprefix=%s)', |
| 83 | self.kind, self.mount_point, self._noprefix) |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 84 | |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 85 | # Mark this contoller as available |
| 86 | self.target = target |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 87 | |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 88 | # Create root control group |
| 89 | self.cgroup('/') |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 90 | |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 91 | def cgroup(self, name): |
| 92 | if not self.target: |
| 93 | raise RuntimeError('CGroup creation failed: {} controller not mounted'\ |
| 94 | .format(self.kind)) |
| 95 | if name not in self._cgroups: |
| 96 | self._cgroups[name] = CGroup(self, name) |
| 97 | return self._cgroups[name] |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 98 | |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 99 | def exists(self, name): |
| 100 | if not self.target: |
| 101 | raise RuntimeError('CGroup creation failed: {} controller not mounted'\ |
| 102 | .format(self.kind)) |
| 103 | if name not in self._cgroups: |
| 104 | self._cgroups[name] = CGroup(self, name, create=False) |
| 105 | return self._cgroups[name].existe() |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 106 | |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 107 | def list_all(self): |
| 108 | self.logger.debug('Listing groups for %s controller', self.kind) |
| 109 | output = self.target.execute('{} find {} -type d'\ |
Patrick Bellasi | 3acf5d5 | 2016-04-26 15:31:05 +0100 | [diff] [blame] | 110 | .format(self.target.busybox, self.mount_point), |
| 111 | as_root=True) |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 112 | cgroups = [] |
Patrick Bellasi | e2e5e68 | 2016-02-23 12:12:28 +0000 | [diff] [blame] | 113 | for cg in output.splitlines(): |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 114 | cg = cg.replace(self.mount_point + '/', '/') |
| 115 | cg = cg.replace(self.mount_point, '/') |
| 116 | cg = cg.strip() |
| 117 | if cg == '': |
| 118 | continue |
| 119 | self.logger.debug('Populate %s cgroup: %s', self.kind, cg) |
| 120 | cgroups.append(cg) |
| 121 | return cgroups |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 122 | |
Patrick Bellasi | 3cab786 | 2016-08-26 16:27:23 +0100 | [diff] [blame] | 123 | def move_tasks(self, source, dest, exclude=[]): |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 124 | try: |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 125 | srcg = self._cgroups[source] |
| 126 | dstg = self._cgroups[dest] |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 127 | except KeyError as e: |
| 128 | raise ValueError('Unkown group: {}'.format(e)) |
Patrick Bellasi | 3cab786 | 2016-08-26 16:27:23 +0100 | [diff] [blame] | 129 | output = self.target._execute_util( |
| 130 | 'cgroups_tasks_move {} {} \'{}\''.format( |
| 131 | srcg.directory, dstg.directory, exclude), |
| 132 | as_root=True) |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 133 | |
Patrick Bellasi | 21d18f8 | 2016-08-26 18:13:47 +0100 | [diff] [blame] | 134 | def move_all_tasks_to(self, dest, exclude=[]): |
| 135 | """ |
| 136 | Move all the tasks to the specified CGroup |
| 137 | |
| 138 | Tasks are moved from all their original CGroup the the specified on. |
| 139 | The tasks which name matches one of the string in exclude are moved |
| 140 | instead in the root CGroup for the controller. |
| 141 | The name of a tasks to exclude must be a substring of the task named as |
| 142 | reported by the "ps" command. Indeed, this list will be translated into |
| 143 | a: "ps | grep -e name1 -e name2..." in order to obtain the PID of these |
| 144 | tasks. |
| 145 | |
| 146 | :param exclude: list of commands to keep in the root CGroup |
| 147 | :type exlude: list(str) |
| 148 | """ |
| 149 | |
| 150 | if isinstance(exclude, str): |
| 151 | exclude = [exclude] |
| 152 | if not isinstance(exclude, list): |
| 153 | raise ValueError('wrong type for "exclude" parameter, ' |
| 154 | 'it must be a str or a list') |
| 155 | |
Patrick Bellasi | c8f118d | 2016-08-31 11:39:33 +0100 | [diff] [blame] | 156 | logging.debug('Moving all tasks into %s', dest) |
Patrick Bellasi | 21d18f8 | 2016-08-26 18:13:47 +0100 | [diff] [blame] | 157 | |
| 158 | # Build list of tasks to exclude |
| 159 | grep_filters = '' |
| 160 | for comm in exclude: |
Brendan Jackman | 3dd4ea6 | 2016-11-25 16:04:34 +0000 | [diff] [blame] | 161 | grep_filters += '-e {} '.format(comm) |
Patrick Bellasi | c8f118d | 2016-08-31 11:39:33 +0100 | [diff] [blame] | 162 | logging.debug(' using grep filter: %s', grep_filters) |
Patrick Bellasi | 21d18f8 | 2016-08-26 18:13:47 +0100 | [diff] [blame] | 163 | if grep_filters != '': |
Patrick Bellasi | c8f118d | 2016-08-31 11:39:33 +0100 | [diff] [blame] | 164 | logging.debug(' excluding tasks which name matches:') |
| 165 | logging.debug(' %s', ', '.join(exclude)) |
Patrick Bellasi | 21d18f8 | 2016-08-26 18:13:47 +0100 | [diff] [blame] | 166 | |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 167 | for cgroup in self._cgroups: |
| 168 | if cgroup != dest: |
Patrick Bellasi | 21d18f8 | 2016-08-26 18:13:47 +0100 | [diff] [blame] | 169 | self.move_tasks(cgroup, dest, grep_filters) |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 170 | |
Patrick Bellasi | 42efd0a | 2016-08-26 16:25:43 +0100 | [diff] [blame] | 171 | def tasks(self, cgroup): |
| 172 | try: |
| 173 | cg = self._cgroups[cgroup] |
| 174 | except KeyError as e: |
| 175 | raise ValueError('Unkown group: {}'.format(e)) |
| 176 | output = self.target._execute_util( |
| 177 | 'cgroups_tasks_in {}'.format(cg.directory), |
| 178 | as_root=True) |
| 179 | entries = output.splitlines() |
| 180 | tasks = {} |
| 181 | for task in entries: |
| 182 | tid = task.split(',')[0] |
| 183 | try: |
| 184 | tname = task.split(',')[1] |
| 185 | except: continue |
| 186 | try: |
| 187 | tcmdline = task.split(',')[2] |
| 188 | except: |
| 189 | tcmdline = '' |
| 190 | tasks[int(tid)] = (tname, tcmdline) |
| 191 | return tasks |
| 192 | |
Patrick Bellasi | d8ae3ab | 2016-08-26 16:28:45 +0100 | [diff] [blame] | 193 | def tasks_count(self, cgroup): |
| 194 | try: |
| 195 | cg = self._cgroups[cgroup] |
| 196 | except KeyError as e: |
| 197 | raise ValueError('Unkown group: {}'.format(e)) |
| 198 | output = self.target.execute( |
| 199 | '{} wc -l {}/tasks'.format( |
| 200 | self.target.busybox, cg.directory), |
| 201 | as_root=True) |
| 202 | return int(output.split()[0]) |
| 203 | |
| 204 | def tasks_per_group(self): |
| 205 | tasks = {} |
| 206 | for cg in self.list_all(): |
| 207 | tasks[cg] = self.tasks_count(cg) |
| 208 | return tasks |
| 209 | |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 210 | class CGroup(object): |
| 211 | |
| 212 | def __init__(self, controller, name, create=True): |
| 213 | self.logger = logging.getLogger('cgroups.' + controller.kind) |
| 214 | self.target = controller.target |
| 215 | self.controller = controller |
| 216 | self.name = name |
| 217 | |
| 218 | # Control cgroup path |
| 219 | self.directory = controller.mount_point |
| 220 | if name != '/': |
| 221 | self.directory = self.target.path.join(controller.mount_point, name[1:]) |
| 222 | |
| 223 | # Setup path for tasks file |
| 224 | self.tasks_file = self.target.path.join(self.directory, 'tasks') |
| 225 | self.procs_file = self.target.path.join(self.directory, 'cgroup.procs') |
| 226 | |
| 227 | if not create: |
| 228 | return |
| 229 | |
Patrick Bellasi | 7112cfe | 2016-02-25 16:54:16 +0000 | [diff] [blame] | 230 | self.logger.debug('Creating cgroup %s', self.directory) |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 231 | self.target.execute('[ -d {0} ] || mkdir -p {0}'\ |
| 232 | .format(self.directory), as_root=True) |
| 233 | |
| 234 | def exists(self): |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 235 | try: |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 236 | self.target.execute('[ -d {0} ]'\ |
Patrick Bellasi | 3acf5d5 | 2016-04-26 15:31:05 +0100 | [diff] [blame] | 237 | .format(self.directory), as_root=True) |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 238 | return True |
| 239 | except TargetError: |
| 240 | return False |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 241 | |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 242 | def get(self): |
| 243 | conf = {} |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 244 | |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 245 | logging.debug('Reading %s attributes from:', |
| 246 | self.controller.kind) |
| 247 | logging.debug(' %s', |
| 248 | self.directory) |
Patrick Bellasi | a65ff13 | 2016-02-23 12:11:06 +0000 | [diff] [blame] | 249 | output = self.target._execute_util( |
| 250 | 'cgroups_get_attributes {} {}'.format( |
Patrick Bellasi | 3acf5d5 | 2016-04-26 15:31:05 +0100 | [diff] [blame] | 251 | self.directory, self.controller.kind), |
| 252 | as_root=True) |
Patrick Bellasi | a65ff13 | 2016-02-23 12:11:06 +0000 | [diff] [blame] | 253 | for res in output.splitlines(): |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 254 | attr = res.split(':')[0] |
| 255 | value = res.split(':')[1] |
| 256 | conf[attr] = value |
| 257 | |
| 258 | return conf |
| 259 | |
| 260 | def set(self, **attrs): |
| 261 | for idx in attrs: |
| 262 | if isiterable(attrs[idx]): |
| 263 | attrs[idx] = list_to_ranges(attrs[idx]) |
| 264 | # Build attribute path |
Patrick Bellasi | 658005a | 2016-04-26 15:26:44 +0100 | [diff] [blame] | 265 | if self.controller._noprefix: |
| 266 | attr_name = '{}'.format(idx) |
| 267 | else: |
| 268 | attr_name = '{}.{}'.format(self.controller.kind, idx) |
| 269 | path = self.target.path.join(self.directory, attr_name) |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 270 | |
| 271 | self.logger.debug('Set attribute [%s] to: %s"', |
| 272 | path, attrs[idx]) |
| 273 | |
| 274 | # Set the attribute value |
Patrick Bellasi | 658005a | 2016-04-26 15:26:44 +0100 | [diff] [blame] | 275 | try: |
| 276 | self.target.write_value(path, attrs[idx]) |
| 277 | except TargetError: |
| 278 | # Check if the error is due to a non-existing attribute |
| 279 | attrs = self.get() |
| 280 | if idx not in attrs: |
| 281 | raise ValueError('Controller [{}] does not provide attribute [{}]'\ |
| 282 | .format(self.controller.kind, attr_name)) |
| 283 | raise |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 284 | |
| 285 | def get_tasks(self): |
| 286 | task_ids = self.target.read_value(self.tasks_file).split() |
| 287 | logging.debug('Tasks: %s', task_ids) |
| 288 | return map(int, task_ids) |
| 289 | |
| 290 | def add_task(self, tid): |
| 291 | self.target.write_value(self.tasks_file, tid, verify=False) |
| 292 | |
| 293 | def add_tasks(self, tasks): |
| 294 | for tid in tasks: |
| 295 | self.add_task(tid) |
| 296 | |
| 297 | def add_proc(self, pid): |
| 298 | self.target.write_value(self.procs_file, pid, verify=False) |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 299 | |
| 300 | CgroupSubsystemEntry = namedtuple('CgroupSubsystemEntry', 'name hierarchy num_cgroups enabled') |
| 301 | |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 302 | class CgroupsModule(Module): |
| 303 | |
| 304 | name = 'cgroups' |
Patrick Bellasi | 616f229 | 2016-05-13 18:17:22 +0100 | [diff] [blame] | 305 | stage = 'setup' |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 306 | |
| 307 | @staticmethod |
| 308 | def probe(target): |
Patrick Bellasi | 4b58c57 | 2016-04-26 15:33:23 +0100 | [diff] [blame] | 309 | if not target.is_rooted: |
| 310 | return False |
| 311 | if target.file_exists('/proc/cgroups'): |
| 312 | return True |
| 313 | return target.config.has('cgroups') |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 314 | |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 315 | def __init__(self, target): |
| 316 | super(CgroupsModule, self).__init__(target) |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 317 | |
| 318 | self.logger = logging.getLogger('CGroups') |
| 319 | |
Patrick Bellasi | b2ec957 | 2016-11-29 10:02:57 +0000 | [diff] [blame] | 320 | # Set Devlib's CGroups mount point |
| 321 | self.cgroup_root = target.path.join( |
| 322 | target.working_directory, 'cgroups') |
| 323 | |
Patrick Bellasi | 103f792 | 2016-11-29 10:13:01 +0000 | [diff] [blame] | 324 | # Get the list of the available controllers |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 325 | subsys = self.list_subsystems() |
Patrick Bellasi | 103f792 | 2016-11-29 10:13:01 +0000 | [diff] [blame] | 326 | if len(subsys) == 0: |
| 327 | self.logger.warning('No CGroups controller available') |
| 328 | return |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 329 | |
Patrick Bellasi | b569a56 | 2016-11-29 10:39:28 +0000 | [diff] [blame] | 330 | # Map hierarchy IDs into a list of controllers |
| 331 | hierarchy = {} |
| 332 | for ss in subsys: |
| 333 | try: |
| 334 | hierarchy[ss.hierarchy].append(ss.name) |
| 335 | except KeyError: |
| 336 | hierarchy[ss.hierarchy] = [ss.name] |
| 337 | self.logger.debug('Available hierarchies: %s', hierarchy) |
| 338 | |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 339 | # Initialize controllers |
Patrick Bellasi | 103f792 | 2016-11-29 10:13:01 +0000 | [diff] [blame] | 340 | self.logger.info('Available controllers:') |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 341 | self.controllers = {} |
Patrick Bellasi | 103f792 | 2016-11-29 10:13:01 +0000 | [diff] [blame] | 342 | for ss in subsys: |
Patrick Bellasi | b569a56 | 2016-11-29 10:39:28 +0000 | [diff] [blame] | 343 | hid = ss.hierarchy |
| 344 | controller = Controller(ss.name, hid, hierarchy[hid]) |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 345 | try: |
Patrick Bellasi | b2ec957 | 2016-11-29 10:02:57 +0000 | [diff] [blame] | 346 | controller.mount(self.target, self.cgroup_root) |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 347 | except TargetError: |
Patrick Bellasi | b569a56 | 2016-11-29 10:39:28 +0000 | [diff] [blame] | 348 | message = 'Failed to mount "{}" controller' |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 349 | raise TargetError(message.format(controller.kind)) |
Patrick Bellasi | b569a56 | 2016-11-29 10:39:28 +0000 | [diff] [blame] | 350 | self.logger.info(' %-12s : %s', controller.kind, |
| 351 | controller.mount_point) |
Patrick Bellasi | 103f792 | 2016-11-29 10:13:01 +0000 | [diff] [blame] | 352 | self.controllers[ss.name] = controller |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 353 | |
| 354 | def list_subsystems(self): |
| 355 | subsystems = [] |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 356 | for line in self.target.execute('{} cat /proc/cgroups'\ |
Patrick Bellasi | e2e5e68 | 2016-02-23 12:12:28 +0000 | [diff] [blame] | 357 | .format(self.target.busybox)).splitlines()[1:]: |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 358 | line = line.strip() |
| 359 | if not line or line.startswith('#'): |
| 360 | continue |
| 361 | name, hierarchy, num_cgroups, enabled = line.split() |
| 362 | subsystems.append(CgroupSubsystemEntry(name, |
| 363 | int(hierarchy), |
| 364 | int(num_cgroups), |
| 365 | boolean(enabled))) |
| 366 | return subsystems |
| 367 | |
| 368 | |
Patrick Bellasi | 9c9a748 | 2015-11-03 11:49:31 +0000 | [diff] [blame] | 369 | def controller(self, kind): |
| 370 | if kind not in self.controllers: |
| 371 | self.logger.warning('Controller %s not available', kind) |
| 372 | return None |
| 373 | return self.controllers[kind] |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 374 | |
Patrick Bellasi | b2ec957 | 2016-11-29 10:02:57 +0000 | [diff] [blame] | 375 | def run_into_cmd(self, cgroup, cmdline): |
Brendan Jackman | dbd1299 | 2017-01-12 16:34:34 +0000 | [diff] [blame] | 376 | """ |
| 377 | Get the command to run a command into a given cgroup |
| 378 | |
| 379 | :param cmdline: Commdand to be run into cgroup |
| 380 | :param cgroup: Name of cgroup to run command into |
| 381 | :returns: A command to run `cmdline` into `cgroup` |
| 382 | """ |
Patrick Bellasi | b2ec957 | 2016-11-29 10:02:57 +0000 | [diff] [blame] | 383 | return 'CGMOUNT={} {} cgroups_run_into {} {}'\ |
| 384 | .format(self.cgroup_root, self.target.shutils, |
| 385 | cgroup, cmdline) |
| 386 | |
Patrick Bellasi | 2873939 | 2016-04-22 11:43:49 +0100 | [diff] [blame] | 387 | def run_into(self, cgroup, cmdline): |
| 388 | """ |
| 389 | Run the specified command into the specified CGroup |
Brendan Jackman | dbd1299 | 2017-01-12 16:34:34 +0000 | [diff] [blame] | 390 | |
| 391 | :param cmdline: Command to be run into cgroup |
| 392 | :param cgroup: Name of cgroup to run command into |
| 393 | :returns: Output of command. |
Patrick Bellasi | 2873939 | 2016-04-22 11:43:49 +0100 | [diff] [blame] | 394 | """ |
Brendan Jackman | d0e28f0 | 2017-01-12 16:38:12 +0000 | [diff] [blame^] | 395 | cmd = self.run_into_cmd(cgroup, cmdline) |
| 396 | return self.target.execute(cmd) |
Patrick Bellasi | 2873939 | 2016-04-22 11:43:49 +0100 | [diff] [blame] | 397 | |
| 398 | def cgroups_tasks_move(self, srcg, dstg, exclude=''): |
| 399 | """ |
| 400 | Move all the tasks from the srcg CGroup to the dstg one. |
| 401 | A regexps of tasks names can be used to defined tasks which should not |
| 402 | be moved. |
| 403 | """ |
| 404 | return self.target._execute_util( |
| 405 | 'cgroups_tasks_move {} {} {}'.format(srcg, dstg, exclude), |
| 406 | as_root=True) |
| 407 | |
Patrick Bellasi | 75a086d | 2016-08-26 16:29:43 +0100 | [diff] [blame] | 408 | def isolate(self, cpus, exclude=[]): |
| 409 | """ |
| 410 | Remove all userspace tasks from specified CPUs. |
| 411 | |
| 412 | A list of CPUs can be specified where we do not want userspace tasks |
| 413 | running. This functions creates a sandbox cpuset CGroup where all |
| 414 | user-space tasks and not-pinned kernel-space tasks are moved into. |
| 415 | This should allows to isolate the specified CPUs which will not get |
| 416 | tasks running unless explicitely moved into the isolated group. |
| 417 | |
| 418 | :param cpus: the list of CPUs to isolate |
| 419 | :type cpus: list(int) |
| 420 | |
| 421 | :return: the (sandbox, isolated) tuple, where: |
| 422 | sandbox is the CGroup of sandboxed CPUs |
| 423 | isolated is the CGroup of isolated CPUs |
| 424 | """ |
| 425 | all_cpus = set(range(self.target.number_of_cpus)) |
| 426 | sbox_cpus = list(all_cpus - set(cpus)) |
| 427 | isol_cpus = list(all_cpus - set(sbox_cpus)) |
| 428 | |
| 429 | # Create Sandbox and Isolated cpuset CGroups |
| 430 | cpuset = self.controller('cpuset') |
| 431 | sbox_cg = cpuset.cgroup('/DEVLIB_SBOX') |
| 432 | isol_cg = cpuset.cgroup('/DEVLIB_ISOL') |
| 433 | |
| 434 | # Set CPUs for Sandbox and Isolated CGroups |
| 435 | sbox_cg.set(cpus=sbox_cpus, mems=0) |
| 436 | isol_cg.set(cpus=isol_cpus, mems=0) |
| 437 | |
| 438 | # Move all currently running tasks to the Sandbox CGroup |
| 439 | cpuset.move_all_tasks_to('/DEVLIB_SBOX', exclude) |
| 440 | |
| 441 | return sbox_cg, isol_cg |
| 442 | |
Patrick Bellasi | 23ad61f | 2016-08-26 18:14:37 +0100 | [diff] [blame] | 443 | def freeze(self, exclude=[], thaw=False): |
| 444 | """ |
Patrick Bellasi | 730bb60 | 2016-08-31 11:40:10 +0100 | [diff] [blame] | 445 | Freeze all user-space tasks but the specified ones |
Patrick Bellasi | 23ad61f | 2016-08-26 18:14:37 +0100 | [diff] [blame] | 446 | |
| 447 | A freezer cgroup is used to stop all the tasks in the target system but |
| 448 | the ones which name match one of the path specified by the exclude |
| 449 | paramater. The name of a tasks to exclude must be a substring of the |
| 450 | task named as reported by the "ps" command. Indeed, this list will be |
| 451 | translated into a: "ps | grep -e name1 -e name2..." in order to obtain |
| 452 | the PID of these tasks. |
| 453 | |
| 454 | :param exclude: list of commands paths to exclude from freezer |
Patrick Bellasi | 730bb60 | 2016-08-31 11:40:10 +0100 | [diff] [blame] | 455 | :type exclude: list(str) |
| 456 | |
| 457 | :param thaw: if true thaw tasks instead |
| 458 | :type thaw: bool |
Patrick Bellasi | 23ad61f | 2016-08-26 18:14:37 +0100 | [diff] [blame] | 459 | """ |
| 460 | |
| 461 | # Create Freezer CGroup |
| 462 | freezer = self.controller('freezer') |
Brendan Jackman | c89f712 | 2016-11-25 11:56:42 +0000 | [diff] [blame] | 463 | if freezer is None: |
| 464 | raise RuntimeError('freezer cgroup controller not present') |
Patrick Bellasi | 23ad61f | 2016-08-26 18:14:37 +0100 | [diff] [blame] | 465 | freezer_cg = freezer.cgroup('/DEVLIB_FREEZER') |
| 466 | thawed_cg = freezer.cgroup('/') |
| 467 | |
| 468 | if thaw: |
| 469 | # Restart froozen tasks |
| 470 | freezer_cg.set(state='THAWED') |
| 471 | # Remove all tasks from freezer |
| 472 | freezer.move_all_tasks_to('/') |
| 473 | return |
| 474 | |
| 475 | # Move all tasks into the freezer group |
| 476 | freezer.move_all_tasks_to('/DEVLIB_FREEZER', exclude) |
| 477 | |
Patrick Bellasi | c8f118d | 2016-08-31 11:39:33 +0100 | [diff] [blame] | 478 | # Get list of not frozen tasks, which is reported as output |
Patrick Bellasi | 23ad61f | 2016-08-26 18:14:37 +0100 | [diff] [blame] | 479 | tasks = freezer.tasks('/') |
Patrick Bellasi | 23ad61f | 2016-08-26 18:14:37 +0100 | [diff] [blame] | 480 | |
| 481 | # Freeze all tasks |
| 482 | freezer_cg.set(state='FROZEN') |
| 483 | |
Patrick Bellasi | c8f118d | 2016-08-31 11:39:33 +0100 | [diff] [blame] | 484 | return tasks |
| 485 | |