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 logging
 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
from enum import Enum
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from past.builtins import basestring
 | 
					from past.builtins import basestring
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -147,43 +146,44 @@ class SchedProcFSNode(object):
 | 
				
			|||||||
            self._dyn_attrs[key] = self._build_node(key, nodes[key])
 | 
					            self._dyn_attrs[key] = self._build_node(key, nodes[key])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DocInt(int):
 | 
					class _SchedDomainFlag:
 | 
				
			||||||
 | 
					 | 
				
			||||||
    # 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):
 | 
					 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    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
 | 
					    _INSTANCES = {}
 | 
				
			||||||
    def check_version(cls, target, logger):
 | 
					    """
 | 
				
			||||||
 | 
					    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
 | 
					        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)
 | 
					                "but target is running v{}".format(ref_parts, parts)
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return self.name
 | 
					        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):
 | 
					class SchedDomain(SchedProcFSNode):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Represents a sched domain as seen through procfs
 | 
					    Represents a sched domain as seen through procfs
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    def __init__(self, nodes):
 | 
					    def __init__(self, nodes):
 | 
				
			||||||
        super(SchedDomain, self).__init__(nodes)
 | 
					        super().__init__(nodes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        obj_flags = set()
 | 
					        flags = self.flags
 | 
				
			||||||
        for flag in list(SchedDomainFlag):
 | 
					        # Recent kernels now have a space-separated list of flags instead of a
 | 
				
			||||||
            if self.flags & flag.value == flag.value:
 | 
					        # packed bitfield
 | 
				
			||||||
                obj_flags.add(flag)
 | 
					        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):
 | 
					class SchedProcFSData(SchedProcFSNode):
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user