mirror of
				https://github.com/ARM-software/devlib.git
				synced 2025-11-04 07:51:21 +00:00 
			
		
		
		
	modules/sched: Fix sched domain flags parsing
Recent kernels can have a space-separated list of textual flags rather than a bitfield packed in an int.
This commit is contained in:
		
				
					committed by
					
						
						Marc Bonnici
					
				
			
			
				
	
			
			
			
						parent
						
							552040f390
						
					
				
				
					commit
					d2b80ccaf9
				
			@@ -15,7 +15,6 @@
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
import re
 | 
			
		||||
from enum import Enum
 | 
			
		||||
 | 
			
		||||
from past.builtins import basestring
 | 
			
		||||
 | 
			
		||||
@@ -147,43 +146,44 @@ class SchedProcFSNode(object):
 | 
			
		||||
            self._dyn_attrs[key] = self._build_node(key, nodes[key])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DocInt(int):
 | 
			
		||||
 | 
			
		||||
    # See https://stackoverflow.com/a/50473952/5096023
 | 
			
		||||
    def __new__(cls, value, doc):
 | 
			
		||||
        new = super(DocInt, cls).__new__(cls, value)
 | 
			
		||||
        new.__doc__ = doc
 | 
			
		||||
        return new
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SchedDomainFlag(DocInt, Enum):
 | 
			
		||||
class _SchedDomainFlag:
 | 
			
		||||
    """
 | 
			
		||||
    Represents a sched domain flag
 | 
			
		||||
    Backward-compatible emulation of the former :class:`enum.Enum` that will
 | 
			
		||||
    work on recent kernels with dynamic sched domain flags name and no value
 | 
			
		||||
    exposed.
 | 
			
		||||
    """
 | 
			
		||||
    # pylint: disable=bad-whitespace
 | 
			
		||||
    # Domain flags obtained from include/linux/sched/topology.h on v4.17
 | 
			
		||||
    # https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux/+/v4.17/include/linux/sched/topology.h#20
 | 
			
		||||
    SD_LOAD_BALANCE =        0x0001, "Do load balancing on this domain"
 | 
			
		||||
    SD_BALANCE_NEWIDLE =     0x0002, "Balance when about to become idle"
 | 
			
		||||
    SD_BALANCE_EXEC =        0x0004, "Balance on exec"
 | 
			
		||||
    SD_BALANCE_FORK =        0x0008, "Balance on fork, clone"
 | 
			
		||||
    SD_BALANCE_WAKE =        0x0010, "Balance on wakeup"
 | 
			
		||||
    SD_WAKE_AFFINE =         0x0020, "Wake task to waking CPU"
 | 
			
		||||
    SD_ASYM_CPUCAPACITY =    0x0040, "Groups have different max cpu capacities"
 | 
			
		||||
    SD_SHARE_CPUCAPACITY =   0x0080, "Domain members share cpu capacity"
 | 
			
		||||
    SD_SHARE_POWERDOMAIN =   0x0100, "Domain members share power domain"
 | 
			
		||||
    SD_SHARE_PKG_RESOURCES = 0x0200, "Domain members share cpu pkg resources"
 | 
			
		||||
    SD_SERIALIZE =           0x0400, "Only a single load balancing instance"
 | 
			
		||||
    SD_ASYM_PACKING =        0x0800, "Place busy groups earlier in the domain"
 | 
			
		||||
    SD_PREFER_SIBLING =      0x1000, "Prefer to place tasks in a sibling domain"
 | 
			
		||||
    SD_OVERLAP =             0x2000, "Sched_domains of this level overlap"
 | 
			
		||||
    SD_NUMA =                0x4000, "Cross-node balancing"
 | 
			
		||||
    # Only defined in Android
 | 
			
		||||
    # https://android.googlesource.com/kernel/common/+/android-4.14/include/linux/sched/topology.h#29
 | 
			
		||||
    SD_SHARE_CAP_STATES =    0x8000, "(Android only) Domain members share capacity state"
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def check_version(cls, target, logger):
 | 
			
		||||
    _INSTANCES = {}
 | 
			
		||||
    """
 | 
			
		||||
    Dictionary storing the instances so that they can be compared with ``is``
 | 
			
		||||
    operator.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __new__(cls, name, value, doc=None):
 | 
			
		||||
        self = super().__new__(cls)
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self._value = value
 | 
			
		||||
        self.__doc__ = doc
 | 
			
		||||
        return cls._INSTANCES.setdefault(self, self)
 | 
			
		||||
 | 
			
		||||
    def __eq__(self, other):
 | 
			
		||||
        # We *have to* check for "value" as well, otherwise it will be
 | 
			
		||||
        # impossible to keep in the same set 2 instances with differing values.
 | 
			
		||||
        return self.name == other.name and self._value == other._value
 | 
			
		||||
 | 
			
		||||
    def __hash__(self):
 | 
			
		||||
        return hash((self.name, self._value))
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def value(self):
 | 
			
		||||
        value = self._value
 | 
			
		||||
        if value is None:
 | 
			
		||||
            raise AttributeError('The kernel does not expose the sched domain flag values')
 | 
			
		||||
        else:
 | 
			
		||||
            return value
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def check_version(target, logger):
 | 
			
		||||
        """
 | 
			
		||||
        Check the target and see if its kernel version matches our view of the world
 | 
			
		||||
        """
 | 
			
		||||
@@ -197,24 +197,111 @@ class SchedDomainFlag(DocInt, Enum):
 | 
			
		||||
                "but target is running v{}".format(ref_parts, parts)
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.name
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return '<SchedDomainFlag: {}>'.format(self.name)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class _SchedDomainFlagMeta(type):
 | 
			
		||||
    """
 | 
			
		||||
    Metaclass of :class:`SchedDomainFlag`.
 | 
			
		||||
 | 
			
		||||
    Provides some level of emulation of :class:`enum.Enum` behavior for
 | 
			
		||||
    backward compatibility.
 | 
			
		||||
    """
 | 
			
		||||
    @property
 | 
			
		||||
    def _flags(self):
 | 
			
		||||
        return [
 | 
			
		||||
            attr
 | 
			
		||||
            for name, attr in self.__dict__.items()
 | 
			
		||||
            if name.startswith('SD_')
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    def __getitem__(self, i):
 | 
			
		||||
        return self._flags[i]
 | 
			
		||||
 | 
			
		||||
    def __len__(self):
 | 
			
		||||
        return len(self._flags)
 | 
			
		||||
 | 
			
		||||
    # These would be provided by collections.abc.Sequence, but using it on a
 | 
			
		||||
    # metaclass seems to have issues around __init_subclass__
 | 
			
		||||
    def __iter__(self):
 | 
			
		||||
        return iter(self._flags)
 | 
			
		||||
 | 
			
		||||
    def __reversed__(self):
 | 
			
		||||
        return reversed(self._flags)
 | 
			
		||||
 | 
			
		||||
    def __contains__(self, x):
 | 
			
		||||
        return x in self._flags
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def __members__(self):
 | 
			
		||||
        return {flag.name: flag for flag in self._flags}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SchedDomainFlag(_SchedDomainFlag, metaclass=_SchedDomainFlagMeta):
 | 
			
		||||
    """
 | 
			
		||||
    Represents a sched domain flag.
 | 
			
		||||
 | 
			
		||||
    .. note:: ``SD_*`` class attributes are deprecated, new code should never
 | 
			
		||||
        test a given flag against one of these attributes with ``is`` (.e.g ``x
 | 
			
		||||
        is SchedDomainFlag.SD_LOAD_BALANCE``. This is because the
 | 
			
		||||
        ``SD_LOAD_BALANCE`` flag exists in two flavors that are not equal: one
 | 
			
		||||
        with a value (the class attribute) and one without (dynamically created
 | 
			
		||||
        when parsing flags for new kernels). Old code ran on old kernels should
 | 
			
		||||
        work fine though.
 | 
			
		||||
    """
 | 
			
		||||
    # pylint: disable=bad-whitespace
 | 
			
		||||
    # Domain flags obtained from include/linux/sched/topology.h on v4.17
 | 
			
		||||
    # https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux/+/v4.17/include/linux/sched/topology.h#20
 | 
			
		||||
    SD_LOAD_BALANCE =        _SchedDomainFlag("SD_LOAD_BALANCE", 0x0001, "Do load balancing on this domain")
 | 
			
		||||
    SD_BALANCE_NEWIDLE =     _SchedDomainFlag("SD_BALANCE_NEWIDLE", 0x0002, "Balance when about to become idle")
 | 
			
		||||
    SD_BALANCE_EXEC =        _SchedDomainFlag("SD_BALANCE_EXEC", 0x0004, "Balance on exec")
 | 
			
		||||
    SD_BALANCE_FORK =        _SchedDomainFlag("SD_BALANCE_FORK", 0x0008, "Balance on fork, clone")
 | 
			
		||||
    SD_BALANCE_WAKE =        _SchedDomainFlag("SD_BALANCE_WAKE", 0x0010, "Balance on wakeup")
 | 
			
		||||
    SD_WAKE_AFFINE =         _SchedDomainFlag("SD_WAKE_AFFINE", 0x0020, "Wake task to waking CPU")
 | 
			
		||||
    SD_ASYM_CPUCAPACITY =    _SchedDomainFlag("SD_ASYM_CPUCAPACITY", 0x0040, "Groups have different max cpu capacities")
 | 
			
		||||
    SD_SHARE_CPUCAPACITY =   _SchedDomainFlag("SD_SHARE_CPUCAPACITY", 0x0080, "Domain members share cpu capacity")
 | 
			
		||||
    SD_SHARE_POWERDOMAIN =   _SchedDomainFlag("SD_SHARE_POWERDOMAIN", 0x0100, "Domain members share power domain")
 | 
			
		||||
    SD_SHARE_PKG_RESOURCES = _SchedDomainFlag("SD_SHARE_PKG_RESOURCES", 0x0200, "Domain members share cpu pkg resources")
 | 
			
		||||
    SD_SERIALIZE =           _SchedDomainFlag("SD_SERIALIZE", 0x0400, "Only a single load balancing instance")
 | 
			
		||||
    SD_ASYM_PACKING =        _SchedDomainFlag("SD_ASYM_PACKING", 0x0800, "Place busy groups earlier in the domain")
 | 
			
		||||
    SD_PREFER_SIBLING =      _SchedDomainFlag("SD_PREFER_SIBLING", 0x1000, "Prefer to place tasks in a sibling domain")
 | 
			
		||||
    SD_OVERLAP =             _SchedDomainFlag("SD_OVERLAP", 0x2000, "Sched_domains of this level overlap")
 | 
			
		||||
    SD_NUMA =                _SchedDomainFlag("SD_NUMA", 0x4000, "Cross-node balancing")
 | 
			
		||||
    # Only defined in Android
 | 
			
		||||
    # https://android.googlesource.com/kernel/common/+/android-4.14/include/linux/sched/topology.h#29
 | 
			
		||||
    SD_SHARE_CAP_STATES =    _SchedDomainFlag("SD_SHARE_CAP_STATES", 0x8000, "(Android only) Domain members share capacity state")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SchedDomain(SchedProcFSNode):
 | 
			
		||||
    """
 | 
			
		||||
    Represents a sched domain as seen through procfs
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, nodes):
 | 
			
		||||
        super(SchedDomain, self).__init__(nodes)
 | 
			
		||||
        super().__init__(nodes)
 | 
			
		||||
 | 
			
		||||
        obj_flags = set()
 | 
			
		||||
        for flag in list(SchedDomainFlag):
 | 
			
		||||
            if self.flags & flag.value == flag.value:
 | 
			
		||||
                obj_flags.add(flag)
 | 
			
		||||
        flags = self.flags
 | 
			
		||||
        # Recent kernels now have a space-separated list of flags instead of a
 | 
			
		||||
        # packed bitfield
 | 
			
		||||
        if isinstance(flags, str):
 | 
			
		||||
            flags = {
 | 
			
		||||
                _SchedDomainFlag(name=name, value=None)
 | 
			
		||||
                for name in flags.split()
 | 
			
		||||
            }
 | 
			
		||||
        else:
 | 
			
		||||
            def has_flag(flags, flag):
 | 
			
		||||
                return flags & flag.value == flag.value
 | 
			
		||||
 | 
			
		||||
        self.flags = obj_flags
 | 
			
		||||
            flags = {
 | 
			
		||||
                flag
 | 
			
		||||
                for flag in SchedDomainFlag
 | 
			
		||||
                if has_flag(flags, flag)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        self.flags = flags
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SchedProcFSData(SchedProcFSNode):
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user