Source code for wlauto.modules.cgroups

#    Copyright 2014-2015 ARM Limited
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# pylint: disable=attribute-defined-outside-init
import logging

import wlauto.core.signal as signal
from wlauto import Module, Parameter
from wlauto.utils.misc import list_to_ranges, isiterable


[docs]class CgroupController(object): kind = 'cpuset' def __new__(cls, arg): if isinstance(arg, cls): return arg else: return object.__new__(cls, arg) def __init__(self, mount_name): self.mount_point = None self.mount_name = mount_name self.logger = logging.getLogger(self.kind)
[docs] def mount(self, device, mount_root): self.device = device self.mount_point = device.path.join(mount_root, self.mount_name) mounted = self.device.list_file_systems() if self.mount_point in [e.mount_point for e in mounted]: self.logger.debug('controller is already mounted.') else: self.device.execute('mkdir -p {} 2>/dev/null'.format(self.mount_point), as_root=True) self.device.execute('mount -t cgroup -o {} {} {}'.format(self.kind, self.mount_name, self.mount_point), as_root=True)
[docs]class CpusetGroup(object): def __init__(self, controller, name, cpus, mems): self.controller = controller self.device = controller.device self.name = name if name == 'root': self.directory = controller.mount_point else: self.directory = self.device.path.join(controller.mount_point, name) self.device.execute('mkdir -p {}'.format(self.directory), as_root=True) self.cpus_file = self.device.path.join(self.directory, 'cpuset.cpus') self.mems_file = self.device.path.join(self.directory, 'cpuset.mems') self.tasks_file = self.device.path.join(self.directory, 'tasks') self.set(cpus, mems)
[docs] def set(self, cpus, mems): if isiterable(cpus): cpus = list_to_ranges(cpus) if isiterable(mems): mems = list_to_ranges(mems) self.device.set_sysfile_value(self.cpus_file, cpus) self.device.set_sysfile_value(self.mems_file, mems)
[docs] def get(self): cpus = self.device.get_sysfile_value(self.cpus_file) mems = self.device.get_sysfile_value(self.mems_file) return (cpus, mems)
[docs] def get_tasks(self): task_ids = self.device.get_sysfile_value(self.tasks_file).split() return map(int, task_ids)
[docs] def add_tasks(self, tasks): for tid in tasks: self.add_task(tid)
[docs] def add_task(self, tid): self.device.set_sysfile_value(self.tasks_file, tid, verify=False)
[docs]class CpusetController(CgroupController): def __init__(self, *args, **kwargs): super(CpusetController, self).__init__(*args, **kwargs) self.groups = {}
[docs] def mount(self, device, mount_root): super(CpusetController, self).mount(device, mount_root) self.create_group('root', self.device.online_cpus, 0)
[docs] def create_group(self, name, cpus, mems): if not hasattr(self, 'device'): raise RuntimeError('Attempting to create group for unmounted controller {}'.format(self.kind)) if name in self.groups: raise ValueError('Group {} already exists'.format(name)) self.groups[name] = CpusetGroup(self, name, cpus, mems)
[docs] def move_tasks(self, source, dest): try: source_group = self.groups[source] dest_group = self.groups[dest] command = 'for task in $(cat {}); do echo $task>{}; done' self.device.execute(command.format(source_group.tasks_file, dest_group.tasks_file), # this will always fail as some of the tasks # are kthreads that cannot be migrated, but we # don't care about those, so don't check exit # code. check_exit_code=False, as_root=True) except KeyError as e: raise ValueError('Unkown group: {}'.format(e))
[docs] def move_all_tasks_to(self, target_group): for group in self.groups: if group != target_group: self.move_tasks(group, target_group)
def __getattr__(self, name): try: return self.groups[name] except KeyError: raise AttributeError(name)
[docs]class Cgroups(Module): name = 'cgroups' description = """ Adds cgroups query and manupution APIs to a Device interface. Currently, only cpusets controller is supported. """ capabilities = ['cgroups'] controllers = [ CpusetController('wa_cpuset'), ] parameters = [ Parameter('cgroup_root', default='/sys/fs/cgroup', description='Location where cgroups are mounted on the device.'), ] def initialize(self, context): self.device = self.root_owner signal.connect(self._on_device_init, signal.RUN_INIT, priority=1)
[docs] def get_cgroup_controller(self, kind): for controller in self.controllers: if controller.kind == kind: return controller raise ValueError(kind)
def _on_device_init(self, context): # pylint: disable=unused-argument mounted = self.device.list_file_systems() if self.cgroup_root not in [e.mount_point for e in mounted]: self.device.execute('mount -t tmpfs {} {}'.format('cgroup_root', self.cgroup_root), as_root=True) else: self.logger.debug('cgroup_root already mounted at {}'.format(self.cgroup_root)) for controller in self.controllers: if controller.kind in [e.device for e in mounted]: self.logger.debug('controller {} is already mounted.'.format(controller.kind)) else: controller.mount(self.device, self.cgroup_root)