Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/133915/?format=api
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" ] }{ "id": 133915, "url": "