get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/133915/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 133915,
    "url": "http://patches.dpdk.org/api/patches/133915/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20231106171601.160749-15-juraj.linkes@pantheon.tech/",
    "project": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<20231106171601.160749-15-juraj.linkes@pantheon.tech>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20231106171601.160749-15-juraj.linkes@pantheon.tech",
    "date": "2023-11-06T17:15:52",
    "name": "[v5,14/23] dts: cpu docstring update",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "3e8eaeefe3a879c772f71a39b533e26bb18376ec",
    "submitter": {
        "id": 1626,
        "url": "http://patches.dpdk.org/api/people/1626/?format=api",
        "name": "Juraj Linkeš",
        "email": "juraj.linkes@pantheon.tech"
    },
    "delegate": {
        "id": 2642,
        "url": "http://patches.dpdk.org/api/users/2642/?format=api",
        "username": "mcoquelin",
        "first_name": "Maxime",
        "last_name": "Coquelin",
        "email": "maxime.coquelin@redhat.com"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/20231106171601.160749-15-juraj.linkes@pantheon.tech/mbox/",
    "series": [
        {
            "id": 30173,
            "url": "http://patches.dpdk.org/api/series/30173/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=30173",
            "date": "2023-11-06T17:15:38",
            "name": "dts: add dts api docs",
            "version": 5,
            "mbox": "http://patches.dpdk.org/series/30173/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/133915/comments/",
    "check": "success",
    "checks": "http://patches.dpdk.org/api/patches/133915/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 795D8432BB;\n\tMon,  6 Nov 2023 18:18:07 +0100 (CET)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 7AB4442D2A;\n\tMon,  6 Nov 2023 18:16:27 +0100 (CET)",
            "from mail-ej1-f47.google.com (mail-ej1-f47.google.com\n [209.85.218.47]) by mails.dpdk.org (Postfix) with ESMTP id 9BDBD427E2\n for <dev@dpdk.org>; Mon,  6 Nov 2023 18:16:24 +0100 (CET)",
            "by mail-ej1-f47.google.com with SMTP id\n a640c23a62f3a-9c41e95efcbso690562466b.3\n for <dev@dpdk.org>; Mon, 06 Nov 2023 09:16:24 -0800 (PST)",
            "from jlinkes-PT-Latitude-5530.. (ip-46.34.243.197.o2inet.sk.\n [46.34.243.197]) by smtp.gmail.com with ESMTPSA id\n s10-20020a170906354a00b009b947aacb4bsm47016eja.191.2023.11.06.09.16.23\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Mon, 06 Nov 2023 09:16:24 -0800 (PST)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=pantheon.tech; s=google; t=1699290984; x=1699895784; darn=dpdk.org;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n :message-id:reply-to;\n bh=pLeNkk+BE6ZnW6umBr/glSV3roMC3ycMuTjWgCecNoQ=;\n b=X7F6FYAZ0kxipOqQUyi+Dk0ejlZZBWZyfcqJjYCXI8LjgEUfe/5uYLCZ94+Y1byJUr\n z822edt3lrm24wxEnwB6AbWozZ/8R7jQKaXhrQKnJ3dE79bJ9PD1muqYu79jSJuTHEhL\n CTVLGoIBUVJTgaMqkig8DIrfxBGWm7W270W1/55uCC5M08HSxHWG2U+u/mLA4xAc5JRW\n Ejgc6vAZ6uCC9mUCVaz2PGicE8jlOn/N8O1vgYZK4VsgySu9LXKuEGyZm2clrZU/q6ud\n 67a1jTTgFz5xaHBpR9HmLFYdSXhc8HFtkAn+k57a2htS34FsIho+nHTn0dXBD56lSR9e\n A3DA==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20230601; t=1699290984; x=1699895784;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc\n :subject:date:message-id:reply-to;\n bh=pLeNkk+BE6ZnW6umBr/glSV3roMC3ycMuTjWgCecNoQ=;\n b=BswA6DbYsN10WyEJ+7M633tKrwG2thCGrLC2tT2Hu5v3+e54eePe7Hv7zYq/sqQtSN\n bJX1C7Wcijw7Hg1pQytRUz6UM7ScfjQBF4hdm94ScHqw1e/u6IN9Tdvz00OeyzIYlNjx\n WCZmqlLgY/YeCpvg6UTkUiOCV5jQ1l3HHlaSALazFauoKh6R5ADsElHFs091prvXqLZq\n S2MqdL8il39cETuVaf8e11RCxEOWPuxZL7gunxE0aoJn3G8JOOjo3JmWNKM8+/vKzb3i\n +UBAn+OQZZvQykIFqWjGSiRzzoo2NpVvd/RcwojZs0MK5V5IhcuDRf08ydP6Mx6ZwTEh\n 8C7Q==",
        "X-Gm-Message-State": "AOJu0Yx3IKqvPR7TjqX3Y+e2Vxlo4OmHJXOaZhyIoIrrwwfI950qfs2p\n RyLEFCSrrmHq9xxdmQRYQiTjng==",
        "X-Google-Smtp-Source": "\n AGHT+IEM2lqXheFD1PB7Kz0+zszevi5/GC/w3fPF+vO/3qOil0P1lgGwWL8//Dhs9dS6Awys2SmpSg==",
        "X-Received": "by 2002:a17:907:2ce2:b0:9be:36c2:162 with SMTP id\n hz2-20020a1709072ce200b009be36c20162mr14305567ejc.31.1699290984189;\n Mon, 06 Nov 2023 09:16:24 -0800 (PST)",
        "From": "=?utf-8?q?Juraj_Linke=C5=A1?= <juraj.linkes@pantheon.tech>",
        "To": "thomas@monjalon.net, Honnappa.Nagarahalli@arm.com,\n bruce.richardson@intel.com, jspewock@iol.unh.edu, probb@iol.unh.edu,\n paul.szczepanek@arm.com, yoan.picchi@foss.arm.com",
        "Cc": "dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= <juraj.linkes@pantheon.tech>",
        "Subject": "[PATCH v5 14/23] dts: cpu docstring update",
        "Date": "Mon,  6 Nov 2023 18:15:52 +0100",
        "Message-Id": "<20231106171601.160749-15-juraj.linkes@pantheon.tech>",
        "X-Mailer": "git-send-email 2.34.1",
        "In-Reply-To": "<20231106171601.160749-1-juraj.linkes@pantheon.tech>",
        "References": "<20230831100407.59865-1-juraj.linkes@pantheon.tech>\n <20231106171601.160749-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>,\n <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>,\n <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org"
    },
    "content": "Format according to the Google format and PEP257, with slight\ndeviations.\n\nSigned-off-by: Juraj Linkeš <juraj.linkes@pantheon.tech>\n---\n dts/framework/testbed_model/cpu.py | 196 +++++++++++++++++++++--------\n 1 file changed, 144 insertions(+), 52 deletions(-)",
    "diff": "diff --git a/dts/framework/testbed_model/cpu.py b/dts/framework/testbed_model/cpu.py\nindex 8fe785dfe4..4edeb4a7c2 100644\n--- a/dts/framework/testbed_model/cpu.py\n+++ b/dts/framework/testbed_model/cpu.py\n@@ -1,6 +1,22 @@\n # SPDX-License-Identifier: BSD-3-Clause\n # Copyright(c) 2023 PANTHEON.tech s.r.o.\n \n+\"\"\"CPU core representation and filtering.\n+\n+This module provides a unified representation of logical CPU cores along\n+with filtering capabilities.\n+\n+When symmetric multiprocessing (SMP or multithreading) is enabled on a server,\n+the physical CPU cores are split into logical CPU cores with different IDs.\n+\n+:class:`LogicalCoreCountFilter` filters by the number of logical cores. It's possible to specify\n+the socket from which to filter the number of logical cores. It's also possible to not use all\n+logical CPU cores from each physical core (e.g. only the first logical core of each physical core).\n+\n+:class:`LogicalCoreListFilter` filters by logical core IDs. This mostly checks that\n+the logical cores are actually present on the server.\n+\"\"\"\n+\n import dataclasses\n from abc import ABC, abstractmethod\n from collections.abc import Iterable, ValuesView\n@@ -11,9 +27,17 @@\n \n @dataclass(slots=True, frozen=True)\n class LogicalCore(object):\n-    \"\"\"\n-    Representation of a CPU core. A physical core is represented in OS\n-    by multiple logical cores (lcores) if CPU multithreading is enabled.\n+    \"\"\"Representation of a logical CPU core.\n+\n+    A physical core is represented in OS by multiple logical cores (lcores)\n+    if CPU multithreading is enabled. When multithreading is disabled, their IDs are the same.\n+\n+    Attributes:\n+        lcore: The logical core ID of a CPU core. It's the same as `core` with\n+            disabled multithreading.\n+        core: The physical core ID of a CPU core.\n+        socket: The physical socket ID where the CPU resides.\n+        node: The NUMA node ID where the CPU resides.\n     \"\"\"\n \n     lcore: int\n@@ -22,27 +46,36 @@ class LogicalCore(object):\n     node: int\n \n     def __int__(self) -> int:\n+        \"\"\"The CPU is best represented by the logical core, as that's what we configure in EAL.\"\"\"\n         return self.lcore\n \n \n class LogicalCoreList(object):\n-    \"\"\"\n-    Convert these options into a list of logical core ids.\n-    lcore_list=[LogicalCore1, LogicalCore2] - a list of LogicalCores\n-    lcore_list=[0,1,2,3] - a list of int indices\n-    lcore_list=['0','1','2-3'] - a list of str indices; ranges are supported\n-    lcore_list='0,1,2-3' - a comma delimited str of indices; ranges are supported\n-\n-    The class creates a unified format used across the framework and allows\n-    the user to use either a str representation (using str(instance) or directly\n-    in f-strings) or a list representation (by accessing instance.lcore_list).\n-    Empty lcore_list is allowed.\n+    r\"\"\"A unified way to store :class:`LogicalCore`\\s.\n+\n+    Create a unified format used across the framework and allow the user to use\n+    either a :class:`str` representation (using ``str(instance)`` or directly in f-strings)\n+    or a :class:`list` representation (by accessing the `lcore_list` property,\n+    which stores logical core IDs).\n     \"\"\"\n \n     _lcore_list: list[int]\n     _lcore_str: str\n \n     def __init__(self, lcore_list: list[int] | list[str] | list[LogicalCore] | str):\n+        \"\"\"Process `lcore_list`, then sort.\n+\n+        There are four supported logical core list formats::\n+\n+            lcore_list=[LogicalCore1, LogicalCore2]  # a list of LogicalCores\n+            lcore_list=[0,1,2,3]        # a list of int indices\n+            lcore_list=['0','1','2-3']  # a list of str indices; ranges are supported\n+            lcore_list='0,1,2-3'        # a comma delimited str of indices; ranges are supported\n+\n+        Args:\n+            lcore_list: Various ways to represent multiple logical cores.\n+                Empty `lcore_list` is allowed.\n+        \"\"\"\n         self._lcore_list = []\n         if isinstance(lcore_list, str):\n             lcore_list = lcore_list.split(\",\")\n@@ -60,6 +93,7 @@ def __init__(self, lcore_list: list[int] | list[str] | list[LogicalCore] | str):\n \n     @property\n     def lcore_list(self) -> list[int]:\n+        \"\"\"The logical core IDs.\"\"\"\n         return self._lcore_list\n \n     def _get_consecutive_lcores_range(self, lcore_ids_list: list[int]) -> list[str]:\n@@ -89,28 +123,30 @@ def _get_consecutive_lcores_range(self, lcore_ids_list: list[int]) -> list[str]:\n         return formatted_core_list\n \n     def __str__(self) -> str:\n+        \"\"\"The consecutive ranges of logical core IDs.\"\"\"\n         return self._lcore_str\n \n \n @dataclasses.dataclass(slots=True, frozen=True)\n class LogicalCoreCount(object):\n-    \"\"\"\n-    Define the number of logical cores to use.\n-    If sockets is not None, socket_count is ignored.\n-    \"\"\"\n+    \"\"\"Define the number of logical cores per physical cores per sockets.\"\"\"\n \n+    #: Use this many logical cores per each physical core.\n     lcores_per_core: int = 1\n+    #: Use this many physical cores per each socket.\n     cores_per_socket: int = 2\n+    #: Use this many sockets.\n     socket_count: int = 1\n+    #: Use exactly these sockets. This takes precedence over `socket_count`,\n+    #: so when `sockets` is not :data:`None`, `socket_count` is ignored.\n     sockets: list[int] | None = None\n \n \n class LogicalCoreFilter(ABC):\n-    \"\"\"\n-    Filter according to the input filter specifier. Each filter needs to be\n-    implemented in a derived class.\n-    This class only implements operations common to all filters, such as sorting\n-    the list to be filtered beforehand.\n+    \"\"\"Common filtering class.\n+\n+    Each filter needs to be implemented in a subclass. This base class sorts the list of cores\n+    and defines the filtering method, which must be implemented by subclasses.\n     \"\"\"\n \n     _filter_specifier: LogicalCoreCount | LogicalCoreList\n@@ -122,6 +158,17 @@ def __init__(\n         filter_specifier: LogicalCoreCount | LogicalCoreList,\n         ascending: bool = True,\n     ):\n+        \"\"\"Filter according to the input filter specifier.\n+\n+        The input `lcore_list` is copied and sorted by physical core before filtering.\n+        The list is copied so that the original is left intact.\n+\n+        Args:\n+            lcore_list: The logical CPU cores to filter.\n+            filter_specifier: Filter cores from `lcore_list` according to this filter.\n+            ascending: Sort cores in ascending order (lowest to highest IDs). If data:`False`,\n+                sort in descending order.\n+        \"\"\"\n         self._filter_specifier = filter_specifier\n \n         # sorting by core is needed in case hyperthreading is enabled\n@@ -132,31 +179,45 @@ def __init__(\n \n     @abstractmethod\n     def filter(self) -> list[LogicalCore]:\n-        \"\"\"\n-        Use self._filter_specifier to filter self._lcores_to_filter\n-        and return the list of filtered LogicalCores.\n-        self._lcores_to_filter is a sorted copy of the original list,\n-        so it may be modified.\n+        r\"\"\"Filter the cores.\n+\n+        Use `self._filter_specifier` to filter `self._lcores_to_filter` and return\n+        the filtered :class:`LogicalCore`\\s.\n+        `self._lcores_to_filter` is a sorted copy of the original list, so it may be modified.\n+\n+        Returns:\n+            The filtered cores.\n         \"\"\"\n \n \n class LogicalCoreCountFilter(LogicalCoreFilter):\n-    \"\"\"\n+    \"\"\"Filter cores by specified counts.\n+\n     Filter the input list of LogicalCores according to specified rules:\n-    Use cores from the specified number of sockets or from the specified socket ids.\n-    If sockets is specified, it takes precedence over socket_count.\n-    From each of those sockets, use only cores_per_socket of cores.\n-    And for each core, use lcores_per_core of logical cores. Hypertheading\n-    must be enabled for this to take effect.\n-    If ascending is True, use cores with the lowest numerical id first\n-    and continue in ascending order. If False, start with the highest\n-    id and continue in descending order. This ordering affects which\n-    sockets to consider first as well.\n+\n+        * The input `filter_specifier` is :class:`LogicalCoreCount`,\n+        * Use cores from the specified number of sockets or from the specified socket ids,\n+        * If `sockets` is specified, it takes precedence over `socket_count`,\n+        * From each of those sockets, use only `cores_per_socket` of cores,\n+        * And for each core, use `lcores_per_core` of logical cores. Hypertheading\n+          must be enabled for this to take effect.\n     \"\"\"\n \n     _filter_specifier: LogicalCoreCount\n \n     def filter(self) -> list[LogicalCore]:\n+        \"\"\"Filter the cores according to :class:`LogicalCoreCount`.\n+\n+        Start by filtering the allowed sockets. The cores matching the allowed socket are returned.\n+        The cores of each socket are stored in separate lists.\n+\n+        Then filter the allowed physical cores from those lists of cores per socket. When filtering\n+        physical cores, store the desired number of logical cores per physical core which then\n+        together constitute the final filtered list.\n+\n+        Returns:\n+            The filtered cores.\n+        \"\"\"\n         sockets_to_filter = self._filter_sockets(self._lcores_to_filter)\n         filtered_lcores = []\n         for socket_to_filter in sockets_to_filter:\n@@ -166,24 +227,37 @@ def filter(self) -> list[LogicalCore]:\n     def _filter_sockets(\n         self, lcores_to_filter: Iterable[LogicalCore]\n     ) -> ValuesView[list[LogicalCore]]:\n-        \"\"\"\n-        Remove all lcores that don't match the specified socket(s).\n-        If self._filter_specifier.sockets is not None, keep lcores from those sockets,\n-        otherwise keep lcores from the first\n-        self._filter_specifier.socket_count sockets.\n+        \"\"\"Filter a list of cores per each allowed socket.\n+\n+        The sockets may be specified in two ways, either a number or a specific list of sockets.\n+        In case of a specific list, we just need to return the cores from those sockets.\n+        If filtering a number of cores, we need to go through all cores and note which sockets\n+        appear and only filter from the first n that appear.\n+\n+        Args:\n+            lcores_to_filter: The cores to filter. These must be sorted by the physical core.\n+\n+        Returns:\n+            A list of lists of logical CPU cores. Each list contains cores from one socket.\n         \"\"\"\n         allowed_sockets: set[int] = set()\n         socket_count = self._filter_specifier.socket_count\n         if self._filter_specifier.sockets:\n+            # when sockets in filter is specified, the sockets are already set\n             socket_count = len(self._filter_specifier.sockets)\n             allowed_sockets = set(self._filter_specifier.sockets)\n \n+        # filter socket_count sockets from all sockets by checking the socket of each CPU\n         filtered_lcores: dict[int, list[LogicalCore]] = {}\n         for lcore in lcores_to_filter:\n             if not self._filter_specifier.sockets:\n+                # this is when sockets is not set, so we do the actual filtering\n+                # when it is set, allowed_sockets is already defined and can't be changed\n                 if len(allowed_sockets) < socket_count:\n+                    # allowed_sockets is a set, so adding an existing socket won't re-add it\n                     allowed_sockets.add(lcore.socket)\n             if lcore.socket in allowed_sockets:\n+                # separate sockets per socket; this makes it easier in further processing\n                 if lcore.socket in filtered_lcores:\n                     filtered_lcores[lcore.socket].append(lcore)\n                 else:\n@@ -200,12 +274,13 @@ def _filter_sockets(\n     def _filter_cores_from_socket(\n         self, lcores_to_filter: Iterable[LogicalCore]\n     ) -> list[LogicalCore]:\n-        \"\"\"\n-        Keep only the first self._filter_specifier.cores_per_socket cores.\n-        In multithreaded environments, keep only\n-        the first self._filter_specifier.lcores_per_core lcores of those cores.\n-        \"\"\"\n+        \"\"\"Filter a list of cores from the given socket.\n+\n+        Go through the cores and note how many logical cores per physical core have been filtered.\n \n+        Returns:\n+            The filtered logical CPU cores.\n+        \"\"\"\n         # no need to use ordered dict, from Python3.7 the dict\n         # insertion order is preserved (LIFO).\n         lcore_count_per_core_map: dict[int, int] = {}\n@@ -248,15 +323,21 @@ def _filter_cores_from_socket(\n \n \n class LogicalCoreListFilter(LogicalCoreFilter):\n-    \"\"\"\n-    Filter the input list of Logical Cores according to the input list of\n-    lcore indices.\n-    An empty LogicalCoreList won't filter anything.\n+    \"\"\"Filter the logical CPU cores by logical CPU core IDs.\n+\n+    This is a simple filter that looks at logical CPU IDs and only filter those that match.\n+\n+    The input filter is :class:`LogicalCoreList`. An empty LogicalCoreList won't filter anything.\n     \"\"\"\n \n     _filter_specifier: LogicalCoreList\n \n     def filter(self) -> list[LogicalCore]:\n+        \"\"\"Filter based on logical CPU core ID.\n+\n+        Return:\n+            The filtered logical CPU cores.\n+        \"\"\"\n         if not len(self._filter_specifier.lcore_list):\n             return self._lcores_to_filter\n \n@@ -279,6 +360,17 @@ def lcore_filter(\n     filter_specifier: LogicalCoreCount | LogicalCoreList,\n     ascending: bool,\n ) -> LogicalCoreFilter:\n+    \"\"\"Factory for using the right filter with `filter_specifier`.\n+\n+    Args:\n+        core_list: The logical CPU cores to filter.\n+        filter_specifier: The filter to use.\n+        ascending: Sort cores in ascending order (lowest to highest IDs). If :data:`False`,\n+            sort in descending order.\n+\n+    Returns:\n+        The filter matching `filter_specifier`.\n+    \"\"\"\n     if isinstance(filter_specifier, LogicalCoreList):\n         return LogicalCoreListFilter(core_list, filter_specifier, ascending)\n     elif isinstance(filter_specifier, LogicalCoreCount):\n",
    "prefixes": [
        "v5",
        "14/23"
    ]
}