xref: /dpdk/dts/framework/testbed_model/cpu.py (revision 3e967643bc51c87928453e4b718a9e09d731cb21)
1840b1e01SJuraj Linkeš# SPDX-License-Identifier: BSD-3-Clause
2840b1e01SJuraj Linkeš# Copyright(c) 2023 PANTHEON.tech s.r.o.
3840b1e01SJuraj Linkeš
46ef07151SJuraj Linkeš"""CPU core representation and filtering.
56ef07151SJuraj Linkeš
66ef07151SJuraj LinkešThis module provides a unified representation of logical CPU cores along
76ef07151SJuraj Linkešwith filtering capabilities.
86ef07151SJuraj Linkeš
96ef07151SJuraj LinkešWhen symmetric multiprocessing (SMP or multithreading) is enabled on a server,
106ef07151SJuraj Linkešthe physical CPU cores are split into logical CPU cores with different IDs.
116ef07151SJuraj Linkeš
126ef07151SJuraj Linkeš:class:`LogicalCoreCountFilter` filters by the number of logical cores. It's possible to specify
136ef07151SJuraj Linkešthe socket from which to filter the number of logical cores. It's also possible to not use all
146ef07151SJuraj Linkešlogical CPU cores from each physical core (e.g. only the first logical core of each physical core).
156ef07151SJuraj Linkeš
166ef07151SJuraj Linkeš:class:`LogicalCoreListFilter` filters by logical core IDs. This mostly checks that
176ef07151SJuraj Linkešthe logical cores are actually present on the server.
186ef07151SJuraj Linkeš"""
196ef07151SJuraj Linkeš
20840b1e01SJuraj Linkešimport dataclasses
21840b1e01SJuraj Linkešfrom abc import ABC, abstractmethod
22840b1e01SJuraj Linkešfrom collections.abc import Iterable, ValuesView
23840b1e01SJuraj Linkešfrom dataclasses import dataclass
24840b1e01SJuraj Linkeš
25840b1e01SJuraj Linkešfrom framework.utils import expand_range
26840b1e01SJuraj Linkeš
27840b1e01SJuraj Linkeš
28840b1e01SJuraj Linkeš@dataclass(slots=True, frozen=True)
29*3e967643SJuraj Linkešclass LogicalCore:
306ef07151SJuraj Linkeš    """Representation of a logical CPU core.
316ef07151SJuraj Linkeš
326ef07151SJuraj Linkeš    A physical core is represented in OS by multiple logical cores (lcores)
336ef07151SJuraj Linkeš    if CPU multithreading is enabled. When multithreading is disabled, their IDs are the same.
346ef07151SJuraj Linkeš
356ef07151SJuraj Linkeš    Attributes:
366ef07151SJuraj Linkeš        lcore: The logical core ID of a CPU core. It's the same as `core` with
376ef07151SJuraj Linkeš            disabled multithreading.
386ef07151SJuraj Linkeš        core: The physical core ID of a CPU core.
396ef07151SJuraj Linkeš        socket: The physical socket ID where the CPU resides.
406ef07151SJuraj Linkeš        node: The NUMA node ID where the CPU resides.
41840b1e01SJuraj Linkeš    """
42840b1e01SJuraj Linkeš
43840b1e01SJuraj Linkeš    lcore: int
44840b1e01SJuraj Linkeš    core: int
45840b1e01SJuraj Linkeš    socket: int
46840b1e01SJuraj Linkeš    node: int
47840b1e01SJuraj Linkeš
48840b1e01SJuraj Linkeš    def __int__(self) -> int:
496ef07151SJuraj Linkeš        """The CPU is best represented by the logical core, as that's what we configure in EAL."""
50840b1e01SJuraj Linkeš        return self.lcore
51840b1e01SJuraj Linkeš
52840b1e01SJuraj Linkeš
53*3e967643SJuraj Linkešclass LogicalCoreList:
546ef07151SJuraj Linkeš    r"""A unified way to store :class:`LogicalCore`\s.
55840b1e01SJuraj Linkeš
566ef07151SJuraj Linkeš    Create a unified format used across the framework and allow the user to use
576ef07151SJuraj Linkeš    either a :class:`str` representation (using ``str(instance)`` or directly in f-strings)
586ef07151SJuraj Linkeš    or a :class:`list` representation (by accessing the `lcore_list` property,
596ef07151SJuraj Linkeš    which stores logical core IDs).
60840b1e01SJuraj Linkeš    """
61840b1e01SJuraj Linkeš
62840b1e01SJuraj Linkeš    _lcore_list: list[int]
63840b1e01SJuraj Linkeš    _lcore_str: str
64840b1e01SJuraj Linkeš
65840b1e01SJuraj Linkeš    def __init__(self, lcore_list: list[int] | list[str] | list[LogicalCore] | str):
666ef07151SJuraj Linkeš        """Process `lcore_list`, then sort.
676ef07151SJuraj Linkeš
686ef07151SJuraj Linkeš        There are four supported logical core list formats::
696ef07151SJuraj Linkeš
706ef07151SJuraj Linkeš            lcore_list=[LogicalCore1, LogicalCore2]  # a list of LogicalCores
716ef07151SJuraj Linkeš            lcore_list=[0,1,2,3]        # a list of int indices
726ef07151SJuraj Linkeš            lcore_list=['0','1','2-3']  # a list of str indices; ranges are supported
736ef07151SJuraj Linkeš            lcore_list='0,1,2-3'        # a comma delimited str of indices; ranges are supported
746ef07151SJuraj Linkeš
756ef07151SJuraj Linkeš        Args:
766ef07151SJuraj Linkeš            lcore_list: Various ways to represent multiple logical cores.
776ef07151SJuraj Linkeš                Empty `lcore_list` is allowed.
786ef07151SJuraj Linkeš        """
79840b1e01SJuraj Linkeš        self._lcore_list = []
80840b1e01SJuraj Linkeš        if isinstance(lcore_list, str):
81840b1e01SJuraj Linkeš            lcore_list = lcore_list.split(",")
82840b1e01SJuraj Linkeš        for lcore in lcore_list:
83840b1e01SJuraj Linkeš            if isinstance(lcore, str):
84840b1e01SJuraj Linkeš                self._lcore_list.extend(expand_range(lcore))
85840b1e01SJuraj Linkeš            else:
86840b1e01SJuraj Linkeš                self._lcore_list.append(int(lcore))
87840b1e01SJuraj Linkeš
88840b1e01SJuraj Linkeš        # the input lcores may not be sorted
89840b1e01SJuraj Linkeš        self._lcore_list.sort()
90840b1e01SJuraj Linkeš        self._lcore_str = f'{",".join(self._get_consecutive_lcores_range(self._lcore_list))}'
91840b1e01SJuraj Linkeš
92840b1e01SJuraj Linkeš    @property
93840b1e01SJuraj Linkeš    def lcore_list(self) -> list[int]:
946ef07151SJuraj Linkeš        """The logical core IDs."""
95840b1e01SJuraj Linkeš        return self._lcore_list
96840b1e01SJuraj Linkeš
97840b1e01SJuraj Linkeš    def _get_consecutive_lcores_range(self, lcore_ids_list: list[int]) -> list[str]:
98840b1e01SJuraj Linkeš        formatted_core_list = []
99840b1e01SJuraj Linkeš        segment = lcore_ids_list[:1]
100840b1e01SJuraj Linkeš        for lcore_id in lcore_ids_list[1:]:
101840b1e01SJuraj Linkeš            if lcore_id - segment[-1] == 1:
102840b1e01SJuraj Linkeš                segment.append(lcore_id)
103840b1e01SJuraj Linkeš            else:
104840b1e01SJuraj Linkeš                formatted_core_list.append(
105840b1e01SJuraj Linkeš                    f"{segment[0]}-{segment[-1]}" if len(segment) > 1 else f"{segment[0]}"
106840b1e01SJuraj Linkeš                )
107840b1e01SJuraj Linkeš                current_core_index = lcore_ids_list.index(lcore_id)
108840b1e01SJuraj Linkeš                formatted_core_list.extend(
109840b1e01SJuraj Linkeš                    self._get_consecutive_lcores_range(lcore_ids_list[current_core_index:])
110840b1e01SJuraj Linkeš                )
111840b1e01SJuraj Linkeš                segment.clear()
112840b1e01SJuraj Linkeš                break
113840b1e01SJuraj Linkeš        if len(segment) > 0:
114840b1e01SJuraj Linkeš            formatted_core_list.append(
115840b1e01SJuraj Linkeš                f"{segment[0]}-{segment[-1]}" if len(segment) > 1 else f"{segment[0]}"
116840b1e01SJuraj Linkeš            )
117840b1e01SJuraj Linkeš        return formatted_core_list
118840b1e01SJuraj Linkeš
119840b1e01SJuraj Linkeš    def __str__(self) -> str:
1206ef07151SJuraj Linkeš        """The consecutive ranges of logical core IDs."""
121840b1e01SJuraj Linkeš        return self._lcore_str
122840b1e01SJuraj Linkeš
123840b1e01SJuraj Linkeš
124840b1e01SJuraj Linkeš@dataclasses.dataclass(slots=True, frozen=True)
125840b1e01SJuraj Linkešclass LogicalCoreCount(object):
1266ef07151SJuraj Linkeš    """Define the number of logical cores per physical cores per sockets."""
127840b1e01SJuraj Linkeš
1286ef07151SJuraj Linkeš    #: Use this many logical cores per each physical core.
129840b1e01SJuraj Linkeš    lcores_per_core: int = 1
1306ef07151SJuraj Linkeš    #: Use this many physical cores per each socket.
131840b1e01SJuraj Linkeš    cores_per_socket: int = 2
1326ef07151SJuraj Linkeš    #: Use this many sockets.
133840b1e01SJuraj Linkeš    socket_count: int = 1
1346ef07151SJuraj Linkeš    #: Use exactly these sockets. This takes precedence over `socket_count`,
1356ef07151SJuraj Linkeš    #: so when `sockets` is not :data:`None`, `socket_count` is ignored.
136840b1e01SJuraj Linkeš    sockets: list[int] | None = None
137840b1e01SJuraj Linkeš
138840b1e01SJuraj Linkeš
139840b1e01SJuraj Linkešclass LogicalCoreFilter(ABC):
1406ef07151SJuraj Linkeš    """Common filtering class.
1416ef07151SJuraj Linkeš
1426ef07151SJuraj Linkeš    Each filter needs to be implemented in a subclass. This base class sorts the list of cores
1436ef07151SJuraj Linkeš    and defines the filtering method, which must be implemented by subclasses.
144840b1e01SJuraj Linkeš    """
145840b1e01SJuraj Linkeš
146840b1e01SJuraj Linkeš    _filter_specifier: LogicalCoreCount | LogicalCoreList
147840b1e01SJuraj Linkeš    _lcores_to_filter: list[LogicalCore]
148840b1e01SJuraj Linkeš
149840b1e01SJuraj Linkeš    def __init__(
150840b1e01SJuraj Linkeš        self,
151840b1e01SJuraj Linkeš        lcore_list: list[LogicalCore],
152840b1e01SJuraj Linkeš        filter_specifier: LogicalCoreCount | LogicalCoreList,
153840b1e01SJuraj Linkeš        ascending: bool = True,
154840b1e01SJuraj Linkeš    ):
1556ef07151SJuraj Linkeš        """Filter according to the input filter specifier.
1566ef07151SJuraj Linkeš
1576ef07151SJuraj Linkeš        The input `lcore_list` is copied and sorted by physical core before filtering.
1586ef07151SJuraj Linkeš        The list is copied so that the original is left intact.
1596ef07151SJuraj Linkeš
1606ef07151SJuraj Linkeš        Args:
1616ef07151SJuraj Linkeš            lcore_list: The logical CPU cores to filter.
1626ef07151SJuraj Linkeš            filter_specifier: Filter cores from `lcore_list` according to this filter.
1636ef07151SJuraj Linkeš            ascending: Sort cores in ascending order (lowest to highest IDs). If data:`False`,
1646ef07151SJuraj Linkeš                sort in descending order.
1656ef07151SJuraj Linkeš        """
166840b1e01SJuraj Linkeš        self._filter_specifier = filter_specifier
167840b1e01SJuraj Linkeš
168840b1e01SJuraj Linkeš        # sorting by core is needed in case hyperthreading is enabled
169840b1e01SJuraj Linkeš        self._lcores_to_filter = sorted(lcore_list, key=lambda x: x.core, reverse=not ascending)
170840b1e01SJuraj Linkeš        self.filter()
171840b1e01SJuraj Linkeš
172840b1e01SJuraj Linkeš    @abstractmethod
173840b1e01SJuraj Linkeš    def filter(self) -> list[LogicalCore]:
1746ef07151SJuraj Linkeš        r"""Filter the cores.
1756ef07151SJuraj Linkeš
1766ef07151SJuraj Linkeš        Use `self._filter_specifier` to filter `self._lcores_to_filter` and return
1776ef07151SJuraj Linkeš        the filtered :class:`LogicalCore`\s.
1786ef07151SJuraj Linkeš        `self._lcores_to_filter` is a sorted copy of the original list, so it may be modified.
1796ef07151SJuraj Linkeš
1806ef07151SJuraj Linkeš        Returns:
1816ef07151SJuraj Linkeš            The filtered cores.
182840b1e01SJuraj Linkeš        """
183840b1e01SJuraj Linkeš
184840b1e01SJuraj Linkeš
185840b1e01SJuraj Linkešclass LogicalCoreCountFilter(LogicalCoreFilter):
1866ef07151SJuraj Linkeš    """Filter cores by specified counts.
1876ef07151SJuraj Linkeš
188840b1e01SJuraj Linkeš    Filter the input list of LogicalCores according to specified rules:
1896ef07151SJuraj Linkeš
1906ef07151SJuraj Linkeš        * The input `filter_specifier` is :class:`LogicalCoreCount`,
1916ef07151SJuraj Linkeš        * Use cores from the specified number of sockets or from the specified socket ids,
1926ef07151SJuraj Linkeš        * If `sockets` is specified, it takes precedence over `socket_count`,
1936ef07151SJuraj Linkeš        * From each of those sockets, use only `cores_per_socket` of cores,
1946ef07151SJuraj Linkeš        * And for each core, use `lcores_per_core` of logical cores. Hypertheading
195840b1e01SJuraj Linkeš          must be enabled for this to take effect.
196840b1e01SJuraj Linkeš    """
197840b1e01SJuraj Linkeš
198840b1e01SJuraj Linkeš    _filter_specifier: LogicalCoreCount
199840b1e01SJuraj Linkeš
200840b1e01SJuraj Linkeš    def filter(self) -> list[LogicalCore]:
2016ef07151SJuraj Linkeš        """Filter the cores according to :class:`LogicalCoreCount`.
2026ef07151SJuraj Linkeš
2036ef07151SJuraj Linkeš        Start by filtering the allowed sockets. The cores matching the allowed sockets are returned.
2046ef07151SJuraj Linkeš        The cores of each socket are stored in separate lists.
2056ef07151SJuraj Linkeš
2066ef07151SJuraj Linkeš        Then filter the allowed physical cores from those lists of cores per socket. When filtering
2076ef07151SJuraj Linkeš        physical cores, store the desired number of logical cores per physical core which then
2086ef07151SJuraj Linkeš        together constitute the final filtered list.
2096ef07151SJuraj Linkeš
2106ef07151SJuraj Linkeš        Returns:
2116ef07151SJuraj Linkeš            The filtered cores.
2126ef07151SJuraj Linkeš        """
213840b1e01SJuraj Linkeš        sockets_to_filter = self._filter_sockets(self._lcores_to_filter)
214840b1e01SJuraj Linkeš        filtered_lcores = []
215840b1e01SJuraj Linkeš        for socket_to_filter in sockets_to_filter:
216840b1e01SJuraj Linkeš            filtered_lcores.extend(self._filter_cores_from_socket(socket_to_filter))
217840b1e01SJuraj Linkeš        return filtered_lcores
218840b1e01SJuraj Linkeš
219840b1e01SJuraj Linkeš    def _filter_sockets(
220840b1e01SJuraj Linkeš        self, lcores_to_filter: Iterable[LogicalCore]
221840b1e01SJuraj Linkeš    ) -> ValuesView[list[LogicalCore]]:
2226ef07151SJuraj Linkeš        """Filter a list of cores per each allowed socket.
2236ef07151SJuraj Linkeš
2246ef07151SJuraj Linkeš        The sockets may be specified in two ways, either a number or a specific list of sockets.
2256ef07151SJuraj Linkeš        In case of a specific list, we just need to return the cores from those sockets.
2266ef07151SJuraj Linkeš        If filtering a number of cores, we need to go through all cores and note which sockets
2276ef07151SJuraj Linkeš        appear and only filter from the first n that appear.
2286ef07151SJuraj Linkeš
2296ef07151SJuraj Linkeš        Args:
2306ef07151SJuraj Linkeš            lcores_to_filter: The cores to filter. These must be sorted by the physical core.
2316ef07151SJuraj Linkeš
2326ef07151SJuraj Linkeš        Returns:
2336ef07151SJuraj Linkeš            A list of lists of logical CPU cores. Each list contains cores from one socket.
234840b1e01SJuraj Linkeš        """
235840b1e01SJuraj Linkeš        allowed_sockets: set[int] = set()
236840b1e01SJuraj Linkeš        socket_count = self._filter_specifier.socket_count
237840b1e01SJuraj Linkeš        if self._filter_specifier.sockets:
2386ef07151SJuraj Linkeš            # when sockets in filter is specified, the sockets are already set
239840b1e01SJuraj Linkeš            socket_count = len(self._filter_specifier.sockets)
240840b1e01SJuraj Linkeš            allowed_sockets = set(self._filter_specifier.sockets)
241840b1e01SJuraj Linkeš
2426ef07151SJuraj Linkeš        # filter socket_count sockets from all sockets by checking the socket of each CPU
243840b1e01SJuraj Linkeš        filtered_lcores: dict[int, list[LogicalCore]] = {}
244840b1e01SJuraj Linkeš        for lcore in lcores_to_filter:
245840b1e01SJuraj Linkeš            if not self._filter_specifier.sockets:
2466ef07151SJuraj Linkeš                # this is when sockets is not set, so we do the actual filtering
2476ef07151SJuraj Linkeš                # when it is set, allowed_sockets is already defined and can't be changed
248840b1e01SJuraj Linkeš                if len(allowed_sockets) < socket_count:
2496ef07151SJuraj Linkeš                    # allowed_sockets is a set, so adding an existing socket won't re-add it
250840b1e01SJuraj Linkeš                    allowed_sockets.add(lcore.socket)
251840b1e01SJuraj Linkeš            if lcore.socket in allowed_sockets:
2526ef07151SJuraj Linkeš                # separate lcores into sockets; this makes it easier in further processing
253840b1e01SJuraj Linkeš                if lcore.socket in filtered_lcores:
254840b1e01SJuraj Linkeš                    filtered_lcores[lcore.socket].append(lcore)
255840b1e01SJuraj Linkeš                else:
256840b1e01SJuraj Linkeš                    filtered_lcores[lcore.socket] = [lcore]
257840b1e01SJuraj Linkeš
258840b1e01SJuraj Linkeš        if len(allowed_sockets) < socket_count:
259840b1e01SJuraj Linkeš            raise ValueError(
260840b1e01SJuraj Linkeš                f"The actual number of sockets from which to use cores "
261840b1e01SJuraj Linkeš                f"({len(allowed_sockets)}) is lower than required ({socket_count})."
262840b1e01SJuraj Linkeš            )
263840b1e01SJuraj Linkeš
264840b1e01SJuraj Linkeš        return filtered_lcores.values()
265840b1e01SJuraj Linkeš
266840b1e01SJuraj Linkeš    def _filter_cores_from_socket(
267840b1e01SJuraj Linkeš        self, lcores_to_filter: Iterable[LogicalCore]
268840b1e01SJuraj Linkeš    ) -> list[LogicalCore]:
2696ef07151SJuraj Linkeš        """Filter a list of cores from the given socket.
270840b1e01SJuraj Linkeš
2716ef07151SJuraj Linkeš        Go through the cores and note how many logical cores per physical core have been filtered.
2726ef07151SJuraj Linkeš
2736ef07151SJuraj Linkeš        Returns:
2746ef07151SJuraj Linkeš            The filtered logical CPU cores.
2756ef07151SJuraj Linkeš        """
276840b1e01SJuraj Linkeš        # no need to use ordered dict, from Python3.7 the dict
277840b1e01SJuraj Linkeš        # insertion order is preserved (LIFO).
278840b1e01SJuraj Linkeš        lcore_count_per_core_map: dict[int, int] = {}
279840b1e01SJuraj Linkeš        filtered_lcores = []
280840b1e01SJuraj Linkeš        for lcore in lcores_to_filter:
281840b1e01SJuraj Linkeš            if lcore.core in lcore_count_per_core_map:
282840b1e01SJuraj Linkeš                current_core_lcore_count = lcore_count_per_core_map[lcore.core]
283840b1e01SJuraj Linkeš                if self._filter_specifier.lcores_per_core > current_core_lcore_count:
284840b1e01SJuraj Linkeš                    # only add lcores of the given core
285840b1e01SJuraj Linkeš                    lcore_count_per_core_map[lcore.core] += 1
286840b1e01SJuraj Linkeš                    filtered_lcores.append(lcore)
287840b1e01SJuraj Linkeš                else:
288840b1e01SJuraj Linkeš                    # we have enough lcores per this core
289840b1e01SJuraj Linkeš                    continue
290840b1e01SJuraj Linkeš            elif self._filter_specifier.cores_per_socket > len(lcore_count_per_core_map):
291840b1e01SJuraj Linkeš                # only add cores if we need more
292840b1e01SJuraj Linkeš                lcore_count_per_core_map[lcore.core] = 1
293840b1e01SJuraj Linkeš                filtered_lcores.append(lcore)
294840b1e01SJuraj Linkeš            else:
295840b1e01SJuraj Linkeš                # we have enough cores
296840b1e01SJuraj Linkeš                break
297840b1e01SJuraj Linkeš
298840b1e01SJuraj Linkeš        cores_per_socket = len(lcore_count_per_core_map)
299840b1e01SJuraj Linkeš        if cores_per_socket < self._filter_specifier.cores_per_socket:
300840b1e01SJuraj Linkeš            raise ValueError(
301840b1e01SJuraj Linkeš                f"The actual number of cores per socket ({cores_per_socket}) "
302840b1e01SJuraj Linkeš                f"is lower than required ({self._filter_specifier.cores_per_socket})."
303840b1e01SJuraj Linkeš            )
304840b1e01SJuraj Linkeš
305840b1e01SJuraj Linkeš        lcores_per_core = lcore_count_per_core_map[filtered_lcores[-1].core]
306840b1e01SJuraj Linkeš        if lcores_per_core < self._filter_specifier.lcores_per_core:
307840b1e01SJuraj Linkeš            raise ValueError(
308840b1e01SJuraj Linkeš                f"The actual number of logical cores per core ({lcores_per_core}) "
309840b1e01SJuraj Linkeš                f"is lower than required ({self._filter_specifier.lcores_per_core})."
310840b1e01SJuraj Linkeš            )
311840b1e01SJuraj Linkeš
312840b1e01SJuraj Linkeš        return filtered_lcores
313840b1e01SJuraj Linkeš
314840b1e01SJuraj Linkeš
315840b1e01SJuraj Linkešclass LogicalCoreListFilter(LogicalCoreFilter):
3166ef07151SJuraj Linkeš    """Filter the logical CPU cores by logical CPU core IDs.
3176ef07151SJuraj Linkeš
3186ef07151SJuraj Linkeš    This is a simple filter that looks at logical CPU IDs and only filter those that match.
3196ef07151SJuraj Linkeš
3206ef07151SJuraj Linkeš    The input filter is :class:`LogicalCoreList`. An empty LogicalCoreList won't filter anything.
321840b1e01SJuraj Linkeš    """
322840b1e01SJuraj Linkeš
323840b1e01SJuraj Linkeš    _filter_specifier: LogicalCoreList
324840b1e01SJuraj Linkeš
325840b1e01SJuraj Linkeš    def filter(self) -> list[LogicalCore]:
3266ef07151SJuraj Linkeš        """Filter based on logical CPU core ID.
3276ef07151SJuraj Linkeš
3286ef07151SJuraj Linkeš        Return:
3296ef07151SJuraj Linkeš            The filtered logical CPU cores.
3306ef07151SJuraj Linkeš        """
331840b1e01SJuraj Linkeš        if not len(self._filter_specifier.lcore_list):
332840b1e01SJuraj Linkeš            return self._lcores_to_filter
333840b1e01SJuraj Linkeš
334840b1e01SJuraj Linkeš        filtered_lcores = []
335840b1e01SJuraj Linkeš        for core in self._lcores_to_filter:
336840b1e01SJuraj Linkeš            if core.lcore in self._filter_specifier.lcore_list:
337840b1e01SJuraj Linkeš                filtered_lcores.append(core)
338840b1e01SJuraj Linkeš
339840b1e01SJuraj Linkeš        if len(filtered_lcores) != len(self._filter_specifier.lcore_list):
340840b1e01SJuraj Linkeš            raise ValueError(
341840b1e01SJuraj Linkeš                f"Not all logical cores from {self._filter_specifier.lcore_list} "
342840b1e01SJuraj Linkeš                f"were found among {self._lcores_to_filter}"
343840b1e01SJuraj Linkeš            )
344840b1e01SJuraj Linkeš
345840b1e01SJuraj Linkeš        return filtered_lcores
346840b1e01SJuraj Linkeš
347840b1e01SJuraj Linkeš
348840b1e01SJuraj Linkešdef lcore_filter(
349840b1e01SJuraj Linkeš    core_list: list[LogicalCore],
350840b1e01SJuraj Linkeš    filter_specifier: LogicalCoreCount | LogicalCoreList,
351840b1e01SJuraj Linkeš    ascending: bool,
352840b1e01SJuraj Linkeš) -> LogicalCoreFilter:
3536ef07151SJuraj Linkeš    """Factory for providing the filter that corresponds to `filter_specifier`.
3546ef07151SJuraj Linkeš
3556ef07151SJuraj Linkeš    Args:
3566ef07151SJuraj Linkeš        core_list: The logical CPU cores to filter.
3576ef07151SJuraj Linkeš        filter_specifier: The filter to use.
3586ef07151SJuraj Linkeš        ascending: Sort cores in ascending order (lowest to highest IDs). If :data:`False`,
3596ef07151SJuraj Linkeš            sort in descending order.
3606ef07151SJuraj Linkeš
3616ef07151SJuraj Linkeš    Returns:
3626ef07151SJuraj Linkeš        The filter that corresponds to `filter_specifier`.
3636ef07151SJuraj Linkeš    """
364840b1e01SJuraj Linkeš    if isinstance(filter_specifier, LogicalCoreList):
365840b1e01SJuraj Linkeš        return LogicalCoreListFilter(core_list, filter_specifier, ascending)
366840b1e01SJuraj Linkeš    elif isinstance(filter_specifier, LogicalCoreCount):
367840b1e01SJuraj Linkeš        return LogicalCoreCountFilter(core_list, filter_specifier, ascending)
368840b1e01SJuraj Linkeš    else:
369840b1e01SJuraj Linkeš        raise ValueError(f"Unsupported filter r{filter_specifier}")
370