From b2ec957bf82517d73350db6d4abcea11ebcd59ad Mon Sep 17 00:00:00 2001 From: Patrick Bellasi Date: Tue, 29 Nov 2016 10:02:57 +0000 Subject: [PATCH 1/3] shutils/cgroups: fix run_into support In the previous patch: cgroups: Mount cgroups controllers in devlib working dir we changed the default mount point for devlib managed CGroups but forgot to update the support for execution of a workload within a specified CGroup. The run_into support is provide by a shutil script, which still has hardcoded the old path (i.e. /sys/fs/cgroup/devlib_*). This patch fixes this by: - resetting the default path to the Linux standard /sys/fs/cgroup - use-sing the existing CGMOUNT env variable to specify which CGroups mount point to use The cgroups::run_into is also updated to use the self.cgroup_root via CGMOUNT when the shutils' script is called. Moreover, an additional cgroups::run_into_cmd method is added which just returns a properly formatted run_into shutils' call. Signed-off-by: Patrick Bellasi --- devlib/bin/scripts/shutils.in | 2 +- devlib/module/cgroups.py | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/devlib/bin/scripts/shutils.in b/devlib/bin/scripts/shutils.in index 640d65d..fa4ad93 100755 --- a/devlib/bin/scripts/shutils.in +++ b/devlib/bin/scripts/shutils.in @@ -102,7 +102,7 @@ cgroups_get_attributes() { cgroups_run_into() { # Control groups mount point - CGMOUNT=${CGMOUNT:-/sys/fs/cgroup/devlib_*} + CGMOUNT=${CGMOUNT:-/sys/fs/cgroup} # The control group we want to run into CGP=${1} shift 1 diff --git a/devlib/module/cgroups.py b/devlib/module/cgroups.py index dd9543e..001867b 100644 --- a/devlib/module/cgroups.py +++ b/devlib/module/cgroups.py @@ -314,6 +314,10 @@ class CgroupsModule(Module): self.logger = logging.getLogger('CGroups') + # Set Devlib's CGroups mount point + self.cgroup_root = target.path.join( + target.working_directory, 'cgroups') + # Load list of available controllers controllers = [] subsys = self.list_subsystems() @@ -329,9 +333,7 @@ class CgroupsModule(Module): if not controller.probe(self.target): continue try: - cgroup_root = target.path.join(target.working_directory, - 'cgroups') - controller.mount(self.target, cgroup_root) + controller.mount(self.target, self.cgroup_root) except TargetError: message = 'cgroups {} controller is not supported by the target' raise TargetError(message.format(controller.kind)) @@ -359,12 +361,18 @@ class CgroupsModule(Module): return None return self.controllers[kind] + def run_into_cmd(self, cgroup, cmdline): + return 'CGMOUNT={} {} cgroups_run_into {} {}'\ + .format(self.cgroup_root, self.target.shutils, + cgroup, cmdline) + def run_into(self, cgroup, cmdline): """ Run the specified command into the specified CGroup """ return self.target._execute_util( - 'cgroups_run_into {} {}'.format(cgroup, cmdline), + 'CGMOUNT={} cgroups_run_into {} {}'\ + .format(self.cgroup_root, cgroup, cmdline), as_root=True) From 103f792736dc461793a020647b196db824e58e0e Mon Sep 17 00:00:00 2001 From: Patrick Bellasi Date: Tue, 29 Nov 2016 10:13:01 +0000 Subject: [PATCH 2/3] cgroups: use subsystems namedtuple in the controllers mounting loop This is just a simple re-factoring patch in preparation of the following one. Since we have a list of CGroups subsystems, which includes the hierarchy ID for each entry, let's use directly these namedtuples in the mouting loop. The following patch will make use of the hierarchy ID to properly mount each controller. Signed-off-by: Patrick Bellasi --- devlib/module/cgroups.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/devlib/module/cgroups.py b/devlib/module/cgroups.py index 001867b..111e7b5 100644 --- a/devlib/module/cgroups.py +++ b/devlib/module/cgroups.py @@ -318,17 +318,17 @@ class CgroupsModule(Module): self.cgroup_root = target.path.join( target.working_directory, 'cgroups') - # Load list of available controllers - controllers = [] + # Get the list of the available controllers subsys = self.list_subsystems() - for (n, h, c, e) in subsys: - controllers.append(n) - self.logger.debug('Available controllers: %s', controllers) + if len(subsys) == 0: + self.logger.warning('No CGroups controller available') + return # Initialize controllers + self.logger.info('Available controllers:') self.controllers = {} - for idx in controllers: - controller = Controller(idx) + for ss in subsys: + controller = Controller(ss.name) self.logger.debug('Init %s controller...', controller.kind) if not controller.probe(self.target): continue @@ -338,7 +338,7 @@ class CgroupsModule(Module): message = 'cgroups {} controller is not supported by the target' raise TargetError(message.format(controller.kind)) self.logger.debug('Controller %s enabled', controller.kind) - self.controllers[idx] = controller + self.controllers[ss.name] = controller def list_subsystems(self): subsystems = [] From b569a561a4f05f1adc50e0a78bbbbeb05e092b35 Mon Sep 17 00:00:00 2001 From: Patrick Bellasi Date: Tue, 29 Nov 2016 10:39:28 +0000 Subject: [PATCH 3/3] cgroups: add support for multi-controller hierarchies Some systems mount multiple CGroup controllers in the same hierarchy, this happens in desktop systems using systemd but it's also the default for systems using CGroups v2. Unfortunately the current CGroup modules can mount only single-controller hierarchies, thus failing when a controller is already mounted with others in a single hierarchy. This patch fixes this issue by: - keeping track of which controllers are mounted in each hierarchy - muting hierarchies instead of controllers - creating Controller objects which reference the hierarchy they belong to Thus, the new constructor of the Controller object requires to specify the hierarchy ID as well as the list of controller which have to be mounted in that hierarchy. The hierarchy ID is used to create a single mount point for all the controllers in that hierarchy. This single mount points are now named: /devlib_cgh where "cgh" stands for "CGroup Hierarchy" and is the specified numerical identified of that hierarchy. This patch removes also the Controller::__new__() method, which seems not to have sense anymore, as well as the Controller::probe() method, which is not more used. Indeed, all and only the available hierarchies are pre-mounted at module initialization time. Signed-off-by: Patrick Bellasi --- devlib/module/cgroups.py | 57 ++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/devlib/module/cgroups.py b/devlib/module/cgroups.py index 111e7b5..466423e 100644 --- a/devlib/module/cgroups.py +++ b/devlib/module/cgroups.py @@ -24,30 +24,33 @@ from devlib.utils.types import boolean class Controller(object): - def __new__(cls, arg): - if isinstance(arg, cls): - return arg - else: - return object.__new__(cls, arg) + def __init__(self, kind, hid, clist): + """ + Initialize a controller given the hierarchy it belongs to. - def __init__(self, kind): - self.mount_name = 'devlib_'+kind + :param kind: the name of the controller + :type kind: str + + :param hid: the Hierarchy ID this controller is mounted on + :type hid: int + + :param clist: the list of controller mounted in the same hierarchy + :type clist: list(str) + """ + self.mount_name = 'devlib_cgh{}'.format(hid) self.kind = kind + self.hid = hid + self.clist = clist self.target = None self._noprefix = False - self.logger = logging.getLogger('cgroups.'+self.kind) + self.logger = logging.getLogger('CGroup.'+self.kind) + self.logger.debug('Initialized [%s, %d, %s]', + self.kind, self.hid, self.clist) + self.mount_point = None self._cgroups = {} - def probe(self, target): - try: - exists = target.execute('{} grep {} /proc/cgroups'\ - .format(target.busybox, self.kind)) - except TargetError: - return False - return True - def mount(self, target, mount_root): mounted = target.list_file_systems() @@ -64,7 +67,7 @@ class Controller(object): target.execute('mkdir -p {} 2>/dev/null'\ .format(self.mount_point), as_root=True) target.execute('mount -t cgroup -o {} {} {}'\ - .format(self.kind, + .format(','.join(self.clist), self.mount_name, self.mount_point), as_root=True) @@ -324,20 +327,28 @@ class CgroupsModule(Module): self.logger.warning('No CGroups controller available') return + # Map hierarchy IDs into a list of controllers + hierarchy = {} + for ss in subsys: + try: + hierarchy[ss.hierarchy].append(ss.name) + except KeyError: + hierarchy[ss.hierarchy] = [ss.name] + self.logger.debug('Available hierarchies: %s', hierarchy) + # Initialize controllers self.logger.info('Available controllers:') self.controllers = {} for ss in subsys: - controller = Controller(ss.name) - self.logger.debug('Init %s controller...', controller.kind) - if not controller.probe(self.target): - continue + hid = ss.hierarchy + controller = Controller(ss.name, hid, hierarchy[hid]) try: controller.mount(self.target, self.cgroup_root) except TargetError: - message = 'cgroups {} controller is not supported by the target' + message = 'Failed to mount "{}" controller' raise TargetError(message.format(controller.kind)) - self.logger.debug('Controller %s enabled', controller.kind) + self.logger.info(' %-12s : %s', controller.kind, + controller.mount_point) self.controllers[ss.name] = controller def list_subsystems(self):