From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <dev-bounces@dpdk.org>
Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124])
	by inbox.dpdk.org (Postfix) with ESMTP id 336CF4366A;
	Mon,  4 Dec 2023 11:26:20 +0100 (CET)
Received: from mails.dpdk.org (localhost [127.0.0.1])
	by mails.dpdk.org (Postfix) with ESMTP id D4C0942D0C;
	Mon,  4 Dec 2023 11:24:51 +0100 (CET)
Received: from mail-wm1-f46.google.com (mail-wm1-f46.google.com
 [209.85.128.46]) by mails.dpdk.org (Postfix) with ESMTP id BB64441153
 for <dev@dpdk.org>; Mon,  4 Dec 2023 11:24:45 +0100 (CET)
Received: by mail-wm1-f46.google.com with SMTP id
 5b1f17b1804b1-40c09dfa03cso10502345e9.2
 for <dev@dpdk.org>; Mon, 04 Dec 2023 02:24:45 -0800 (PST)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=pantheon.tech; s=google; t=1701685485; x=1702290285; darn=dpdk.org;
 h=content-transfer-encoding:mime-version:references:in-reply-to
 :message-id:date:subject:cc:to:from:from:to:cc:subject:date
 :message-id:reply-to;
 bh=lDAzXB13bmA5V61UH4hz6KNJGJbhuXFju4FDnIEpq4c=;
 b=b+eJJJDh/KG3Y491l0CiSemORgtEaz5f030QhWuy2C+VkNKBYO1a9kMFr+tO1o73Hg
 41QJUN8dMcganKUuDQ2bvbLby2vtyS0gLTMk8xLc1zIJ+DKh28QRjmdPHQsSkVbA3Jd+
 IodZ7TmFucH9cj4b5+AdGen68z8Kcds615vIPCPuAOJXBet6uXDdrOH8wNZS7LFCtOQy
 YHZrWerDqgMCLR8KweLB4AYIgQlyqn9mnxH5mZnjaP8jGwdRoaufysojnw4xLKVCyLyM
 RMm6dPVSvoDuVyVnwif9xUlBRRBWTfYnWSUBeR66Gwirv4PnNzxrURxjLArgMO6/5FRy
 omGA==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=1e100.net; s=20230601; t=1701685485; x=1702290285;
 h=content-transfer-encoding:mime-version:references:in-reply-to
 :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc
 :subject:date:message-id:reply-to;
 bh=lDAzXB13bmA5V61UH4hz6KNJGJbhuXFju4FDnIEpq4c=;
 b=c9HPrO70RWjbsgPTp8XzPo1QzHPY5oTpXlACMjpmdKtPJoF2sJvvserFodJcepRqBD
 GEv5WLq3b36yQEn6FEr+bp8rRe3rghl7+ZmU6l34bSWzEjU95w5orU4qkWaWf+ylXJds
 ksHyASghUpi2cLLQVMg1QsRixbQ/VMI3pLZSJ+7cE7aBlBcCRt0EAd+wQGsmVUyVBE6D
 ESdr4N8Aucu4B8vPzsoq6ionKvMiS/WnZHizWaRiaU1n2ZpQtUOBjhBqvf6JVM0CiLJ+
 RM//otW8cvBC2Ta1iEvAIiwQEkioavjKcl7BQwrggFui6McIDf0ONGuorOL3XSoB1Dlo
 W6Sw==
X-Gm-Message-State: AOJu0YxVT2EUJQmc8AJqEAUjZ6zCQRn9uLNhqlwNn/L+A2NvKQwGePHA
 dHxJ24ywoaDTsev4US8+io2dqg==
X-Google-Smtp-Source: AGHT+IFiCFiPxIQ/TDVtxFj/70TJ9dabIiG3KI1vVPKghAcld1ARfVEO8DrR0w+SGAL2mKojT0b6Dg==
X-Received: by 2002:a1c:7402:0:b0:40b:5e59:c573 with SMTP id
 p2-20020a1c7402000000b0040b5e59c573mr2617610wmc.157.1701685485372; 
 Mon, 04 Dec 2023 02:24:45 -0800 (PST)
Received: from jlinkes-PT-Latitude-5530.pantheon.local ([81.89.53.154])
 by smtp.gmail.com with ESMTPSA id
 m28-20020a05600c3b1c00b0040b2b38a1fasm14255415wms.4.2023.12.04.02.24.44
 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
 Mon, 04 Dec 2023 02:24:45 -0800 (PST)
From: =?UTF-8?q?Juraj=20Linke=C5=A1?= <juraj.linkes@pantheon.tech>
To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, jspewock@iol.unh.edu,
 probb@iol.unh.edu, paul.szczepanek@arm.com, yoan.picchi@foss.arm.com,
 Luca.Vizzarro@arm.com
Cc: dev@dpdk.org, =?UTF-8?q?Juraj=20Linke=C5=A1?= <juraj.linkes@pantheon.tech>
Subject: [PATCH v9 14/21] dts: cpu docstring update
Date: Mon,  4 Dec 2023 11:24:22 +0100
Message-Id: <20231204102429.106709-15-juraj.linkes@pantheon.tech>
X-Mailer: git-send-email 2.34.1
In-Reply-To: <20231204102429.106709-1-juraj.linkes@pantheon.tech>
References: <20231123151344.162812-1-juraj.linkes@pantheon.tech>
 <20231204102429.106709-1-juraj.linkes@pantheon.tech>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-BeenThere: dev@dpdk.org
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: DPDK patches and discussions <dev.dpdk.org>
List-Unsubscribe: <https://mails.dpdk.org/options/dev>,
 <mailto:dev-request@dpdk.org?subject=unsubscribe>
List-Archive: <http://mails.dpdk.org/archives/dev/>
List-Post: <mailto:dev@dpdk.org>
List-Help: <mailto:dev-request@dpdk.org?subject=help>
List-Subscribe: <https://mails.dpdk.org/listinfo/dev>,
 <mailto:dev-request@dpdk.org?subject=subscribe>
Errors-To: dev-bounces@dpdk.org

Format according to the Google format and PEP257, with slight
deviations.

Signed-off-by: Juraj Linkeš <juraj.linkes@pantheon.tech>
---
 dts/framework/testbed_model/cpu.py | 196 +++++++++++++++++++++--------
 1 file changed, 144 insertions(+), 52 deletions(-)

diff --git a/dts/framework/testbed_model/cpu.py b/dts/framework/testbed_model/cpu.py
index 1b392689f5..9e33b2825d 100644
--- a/dts/framework/testbed_model/cpu.py
+++ b/dts/framework/testbed_model/cpu.py
@@ -1,6 +1,22 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2023 PANTHEON.tech s.r.o.
 
+"""CPU core representation and filtering.
+
+This module provides a unified representation of logical CPU cores along
+with filtering capabilities.
+
+When symmetric multiprocessing (SMP or multithreading) is enabled on a server,
+the physical CPU cores are split into logical CPU cores with different IDs.
+
+:class:`LogicalCoreCountFilter` filters by the number of logical cores. It's possible to specify
+the socket from which to filter the number of logical cores. It's also possible to not use all
+logical CPU cores from each physical core (e.g. only the first logical core of each physical core).
+
+:class:`LogicalCoreListFilter` filters by logical core IDs. This mostly checks that
+the logical cores are actually present on the server.
+"""
+
 import dataclasses
 from abc import ABC, abstractmethod
 from collections.abc import Iterable, ValuesView
@@ -11,9 +27,17 @@
 
 @dataclass(slots=True, frozen=True)
 class LogicalCore(object):
-    """
-    Representation of a CPU core. A physical core is represented in OS
-    by multiple logical cores (lcores) if CPU multithreading is enabled.
+    """Representation of a logical CPU core.
+
+    A physical core is represented in OS by multiple logical cores (lcores)
+    if CPU multithreading is enabled. When multithreading is disabled, their IDs are the same.
+
+    Attributes:
+        lcore: The logical core ID of a CPU core. It's the same as `core` with
+            disabled multithreading.
+        core: The physical core ID of a CPU core.
+        socket: The physical socket ID where the CPU resides.
+        node: The NUMA node ID where the CPU resides.
     """
 
     lcore: int
@@ -22,27 +46,36 @@ class LogicalCore(object):
     node: int
 
     def __int__(self) -> int:
+        """The CPU is best represented by the logical core, as that's what we configure in EAL."""
         return self.lcore
 
 
 class LogicalCoreList(object):
-    """
-    Convert these options into a list of logical core ids.
-    lcore_list=[LogicalCore1, LogicalCore2] - a list of LogicalCores
-    lcore_list=[0,1,2,3] - a list of int indices
-    lcore_list=['0','1','2-3'] - a list of str indices; ranges are supported
-    lcore_list='0,1,2-3' - a comma delimited str of indices; ranges are supported
-
-    The class creates a unified format used across the framework and allows
-    the user to use either a str representation (using str(instance) or directly
-    in f-strings) or a list representation (by accessing instance.lcore_list).
-    Empty lcore_list is allowed.
+    r"""A unified way to store :class:`LogicalCore`\s.
+
+    Create a unified format used across the framework and allow the user to use
+    either a :class:`str` representation (using ``str(instance)`` or directly in f-strings)
+    or a :class:`list` representation (by accessing the `lcore_list` property,
+    which stores logical core IDs).
     """
 
     _lcore_list: list[int]
     _lcore_str: str
 
     def __init__(self, lcore_list: list[int] | list[str] | list[LogicalCore] | str):
+        """Process `lcore_list`, then sort.
+
+        There are four supported logical core list formats::
+
+            lcore_list=[LogicalCore1, LogicalCore2]  # a list of LogicalCores
+            lcore_list=[0,1,2,3]        # a list of int indices
+            lcore_list=['0','1','2-3']  # a list of str indices; ranges are supported
+            lcore_list='0,1,2-3'        # a comma delimited str of indices; ranges are supported
+
+        Args:
+            lcore_list: Various ways to represent multiple logical cores.
+                Empty `lcore_list` is allowed.
+        """
         self._lcore_list = []
         if isinstance(lcore_list, str):
             lcore_list = lcore_list.split(",")
@@ -58,6 +91,7 @@ def __init__(self, lcore_list: list[int] | list[str] | list[LogicalCore] | str):
 
     @property
     def lcore_list(self) -> list[int]:
+        """The logical core IDs."""
         return self._lcore_list
 
     def _get_consecutive_lcores_range(self, lcore_ids_list: list[int]) -> list[str]:
@@ -83,28 +117,30 @@ def _get_consecutive_lcores_range(self, lcore_ids_list: list[int]) -> list[str]:
         return formatted_core_list
 
     def __str__(self) -> str:
+        """The consecutive ranges of logical core IDs."""
         return self._lcore_str
 
 
 @dataclasses.dataclass(slots=True, frozen=True)
 class LogicalCoreCount(object):
-    """
-    Define the number of logical cores to use.
-    If sockets is not None, socket_count is ignored.
-    """
+    """Define the number of logical cores per physical cores per sockets."""
 
+    #: Use this many logical cores per each physical core.
     lcores_per_core: int = 1
+    #: Use this many physical cores per each socket.
     cores_per_socket: int = 2
+    #: Use this many sockets.
     socket_count: int = 1
+    #: Use exactly these sockets. This takes precedence over `socket_count`,
+    #: so when `sockets` is not :data:`None`, `socket_count` is ignored.
     sockets: list[int] | None = None
 
 
 class LogicalCoreFilter(ABC):
-    """
-    Filter according to the input filter specifier. Each filter needs to be
-    implemented in a derived class.
-    This class only implements operations common to all filters, such as sorting
-    the list to be filtered beforehand.
+    """Common filtering class.
+
+    Each filter needs to be implemented in a subclass. This base class sorts the list of cores
+    and defines the filtering method, which must be implemented by subclasses.
     """
 
     _filter_specifier: LogicalCoreCount | LogicalCoreList
@@ -116,6 +152,17 @@ def __init__(
         filter_specifier: LogicalCoreCount | LogicalCoreList,
         ascending: bool = True,
     ):
+        """Filter according to the input filter specifier.
+
+        The input `lcore_list` is copied and sorted by physical core before filtering.
+        The list is copied so that the original is left intact.
+
+        Args:
+            lcore_list: The logical CPU cores to filter.
+            filter_specifier: Filter cores from `lcore_list` according to this filter.
+            ascending: Sort cores in ascending order (lowest to highest IDs). If data:`False`,
+                sort in descending order.
+        """
         self._filter_specifier = filter_specifier
 
         # sorting by core is needed in case hyperthreading is enabled
@@ -124,31 +171,45 @@ def __init__(
 
     @abstractmethod
     def filter(self) -> list[LogicalCore]:
-        """
-        Use self._filter_specifier to filter self._lcores_to_filter
-        and return the list of filtered LogicalCores.
-        self._lcores_to_filter is a sorted copy of the original list,
-        so it may be modified.
+        r"""Filter the cores.
+
+        Use `self._filter_specifier` to filter `self._lcores_to_filter` and return
+        the filtered :class:`LogicalCore`\s.
+        `self._lcores_to_filter` is a sorted copy of the original list, so it may be modified.
+
+        Returns:
+            The filtered cores.
         """
 
 
 class LogicalCoreCountFilter(LogicalCoreFilter):
-    """
+    """Filter cores by specified counts.
+
     Filter the input list of LogicalCores according to specified rules:
-    Use cores from the specified number of sockets or from the specified socket ids.
-    If sockets is specified, it takes precedence over socket_count.
-    From each of those sockets, use only cores_per_socket of cores.
-    And for each core, use lcores_per_core of logical cores. Hypertheading
-    must be enabled for this to take effect.
-    If ascending is True, use cores with the lowest numerical id first
-    and continue in ascending order. If False, start with the highest
-    id and continue in descending order. This ordering affects which
-    sockets to consider first as well.
+
+        * The input `filter_specifier` is :class:`LogicalCoreCount`,
+        * Use cores from the specified number of sockets or from the specified socket ids,
+        * If `sockets` is specified, it takes precedence over `socket_count`,
+        * From each of those sockets, use only `cores_per_socket` of cores,
+        * And for each core, use `lcores_per_core` of logical cores. Hypertheading
+          must be enabled for this to take effect.
     """
 
     _filter_specifier: LogicalCoreCount
 
     def filter(self) -> list[LogicalCore]:
+        """Filter the cores according to :class:`LogicalCoreCount`.
+
+        Start by filtering the allowed sockets. The cores matching the allowed sockets are returned.
+        The cores of each socket are stored in separate lists.
+
+        Then filter the allowed physical cores from those lists of cores per socket. When filtering
+        physical cores, store the desired number of logical cores per physical core which then
+        together constitute the final filtered list.
+
+        Returns:
+            The filtered cores.
+        """
         sockets_to_filter = self._filter_sockets(self._lcores_to_filter)
         filtered_lcores = []
         for socket_to_filter in sockets_to_filter:
@@ -158,24 +219,37 @@ def filter(self) -> list[LogicalCore]:
     def _filter_sockets(
         self, lcores_to_filter: Iterable[LogicalCore]
     ) -> ValuesView[list[LogicalCore]]:
-        """
-        Remove all lcores that don't match the specified socket(s).
-        If self._filter_specifier.sockets is not None, keep lcores from those sockets,
-        otherwise keep lcores from the first
-        self._filter_specifier.socket_count sockets.
+        """Filter a list of cores per each allowed socket.
+
+        The sockets may be specified in two ways, either a number or a specific list of sockets.
+        In case of a specific list, we just need to return the cores from those sockets.
+        If filtering a number of cores, we need to go through all cores and note which sockets
+        appear and only filter from the first n that appear.
+
+        Args:
+            lcores_to_filter: The cores to filter. These must be sorted by the physical core.
+
+        Returns:
+            A list of lists of logical CPU cores. Each list contains cores from one socket.
         """
         allowed_sockets: set[int] = set()
         socket_count = self._filter_specifier.socket_count
         if self._filter_specifier.sockets:
+            # when sockets in filter is specified, the sockets are already set
             socket_count = len(self._filter_specifier.sockets)
             allowed_sockets = set(self._filter_specifier.sockets)
 
+        # filter socket_count sockets from all sockets by checking the socket of each CPU
         filtered_lcores: dict[int, list[LogicalCore]] = {}
         for lcore in lcores_to_filter:
             if not self._filter_specifier.sockets:
+                # this is when sockets is not set, so we do the actual filtering
+                # when it is set, allowed_sockets is already defined and can't be changed
                 if len(allowed_sockets) < socket_count:
+                    # allowed_sockets is a set, so adding an existing socket won't re-add it
                     allowed_sockets.add(lcore.socket)
             if lcore.socket in allowed_sockets:
+                # separate lcores into sockets; this makes it easier in further processing
                 if lcore.socket in filtered_lcores:
                     filtered_lcores[lcore.socket].append(lcore)
                 else:
@@ -192,12 +266,13 @@ def _filter_sockets(
     def _filter_cores_from_socket(
         self, lcores_to_filter: Iterable[LogicalCore]
     ) -> list[LogicalCore]:
-        """
-        Keep only the first self._filter_specifier.cores_per_socket cores.
-        In multithreaded environments, keep only
-        the first self._filter_specifier.lcores_per_core lcores of those cores.
-        """
+        """Filter a list of cores from the given socket.
+
+        Go through the cores and note how many logical cores per physical core have been filtered.
 
+        Returns:
+            The filtered logical CPU cores.
+        """
         # no need to use ordered dict, from Python3.7 the dict
         # insertion order is preserved (LIFO).
         lcore_count_per_core_map: dict[int, int] = {}
@@ -238,15 +313,21 @@ def _filter_cores_from_socket(
 
 
 class LogicalCoreListFilter(LogicalCoreFilter):
-    """
-    Filter the input list of Logical Cores according to the input list of
-    lcore indices.
-    An empty LogicalCoreList won't filter anything.
+    """Filter the logical CPU cores by logical CPU core IDs.
+
+    This is a simple filter that looks at logical CPU IDs and only filter those that match.
+
+    The input filter is :class:`LogicalCoreList`. An empty LogicalCoreList won't filter anything.
     """
 
     _filter_specifier: LogicalCoreList
 
     def filter(self) -> list[LogicalCore]:
+        """Filter based on logical CPU core ID.
+
+        Return:
+            The filtered logical CPU cores.
+        """
         if not len(self._filter_specifier.lcore_list):
             return self._lcores_to_filter
 
@@ -269,6 +350,17 @@ def lcore_filter(
     filter_specifier: LogicalCoreCount | LogicalCoreList,
     ascending: bool,
 ) -> LogicalCoreFilter:
+    """Factory for providing the filter that corresponds to `filter_specifier`.
+
+    Args:
+        core_list: The logical CPU cores to filter.
+        filter_specifier: The filter to use.
+        ascending: Sort cores in ascending order (lowest to highest IDs). If :data:`False`,
+            sort in descending order.
+
+    Returns:
+        The filter that corresponds to `filter_specifier`.
+    """
     if isinstance(filter_specifier, LogicalCoreList):
         return LogicalCoreListFilter(core_list, filter_specifier, ascending)
     elif isinstance(filter_specifier, LogicalCoreCount):
-- 
2.34.1