Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/134580/?format=api
http://patches.dpdk.org/api/patches/134580/?format=api", "web_url": "http://patches.dpdk.org/project/dpdk/patch/20231123151344.162812-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": "<20231123151344.162812-15-juraj.linkes@pantheon.tech>", "list_archive_url": "https://inbox.dpdk.org/dev/20231123151344.162812-15-juraj.linkes@pantheon.tech", "date": "2023-11-23T15:13:37", "name": "[v8,14/21] dts: cpu docstring update", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": true, "hash": "94cd204bb39e641b62b201c578c1d54aa696864a", "submitter": { "id": 1626, "url": "http://patches.dpdk.org/api/people/1626/?format=api", "name": "Juraj Linkeš", "email": "juraj.linkes@pantheon.tech" }, "delegate": { "id": 1, "url": "http://patches.dpdk.org/api/users/1/?format=api", "username": "tmonjalo", "first_name": "Thomas", "last_name": "Monjalon", "email": "thomas@monjalon.net" }, "mbox": "http://patches.dpdk.org/project/dpdk/patch/20231123151344.162812-15-juraj.linkes@pantheon.tech/mbox/", "series": [ { "id": 30375, "url": "http://patches.dpdk.org/api/series/30375/?format=api", "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=30375", "date": "2023-11-23T15:13:23", "name": "dts: docstrings update", "version": 8, "mbox": "http://patches.dpdk.org/series/30375/mbox/" } ], "comments": "http://patches.dpdk.org/api/patches/134580/comments/", "check": "success", "checks": "http://patches.dpdk.org/api/patches/134580/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 98AD1433AC;\n\tThu, 23 Nov 2023 16:16:02 +0100 (CET)", "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id E645042FD9;\n\tThu, 23 Nov 2023 16:14:22 +0100 (CET)", "from mail-wr1-f53.google.com (mail-wr1-f53.google.com\n [209.85.221.53]) by mails.dpdk.org (Postfix) with ESMTP id B9E8B42FE7\n for <dev@dpdk.org>; Thu, 23 Nov 2023 16:14:05 +0100 (CET)", "by mail-wr1-f53.google.com with SMTP id\n ffacd0b85a97d-3316bd84749so638471f8f.2\n for <dev@dpdk.org>; Thu, 23 Nov 2023 07:14:05 -0800 (PST)", "from jlinkes-PT-Latitude-5530.. ([84.245.121.10])\n by smtp.gmail.com with ESMTPSA id\n q4-20020adfea04000000b003296b488961sm1870143wrm.31.2023.11.23.07.14.04\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Thu, 23 Nov 2023 07:14:05 -0800 (PST)" ], "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=pantheon.tech; s=google; t=1700752445; x=1701357245; 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=lDAzXB13bmA5V61UH4hz6KNJGJbhuXFju4FDnIEpq4c=;\n b=BSypjx1JOT1gkqqognG1fTQ8gEItO2k/Njz7AzL9ODbtF6BiJerKtVFLZqMApkTVD1\n DGYPylWBEN9nolkdm6kTQyLxuOPIGUJocD5AIndq6GuvwGaSwLtIkqEOkz1nuLahZ9vA\n ySqwP4MR6Id9knjdDW04R9MrxWLmd1ySYKZwQc0in8BM2YZN4J5GCUctjaFO24SJaImF\n WYELSx4cnRifhneQNqFyX3bh/6V8Fj8c6pRJRDpktBGR4zXmLG8SxGhr9g/ntS4fcKtt\n G9rUGoHXV9Uymo7Hh25KD+RLzeNSlAZvipP3Q3JL/HBxIXJmN1pIEmki7MXjZG/GraNt\n ZbPA==", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20230601; t=1700752445; x=1701357245;\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=lDAzXB13bmA5V61UH4hz6KNJGJbhuXFju4FDnIEpq4c=;\n b=Hof1DRyGpds1w39kDMTuYnrUzD1j6fBlt50F1b4DCwxspk3lpCox5ouu2FA9ISTqmN\n PSnpDYIRWipF9S4opFfl7FcHJpp5DhNXd+8VSiOIlT774IjV7Ih/hRwAOyozU30LGkuj\n FlQ6WOaY8M17omXYarF/PJ+qFYsh9NklZOlCmSAUYwBtBow5Lu+wX2XVeOWEGjpOsK89\n RgWkvJX6IfUTvlDzr0dVx4TjANVr3r9851HDDWLujCkatfnIGEOqXTWxMDksPU9Nfzl6\n jMYnCQHGePOdKqq99auGVNwMAbQOLHUxtH6Wz/gopp6mbvMwFH+4JVk+aPQkT0+p1pEn\n 2Pjg==", "X-Gm-Message-State": "AOJu0YwC6FjEril01OQomiYwLVoDEWlo2Wiy9gMQvUg+i4p1YuRvfCuY\n zBPO0c0odjxBKJgEiOsejumDNA==", "X-Google-Smtp-Source": "\n AGHT+IEZ2F9gYo4NvV2y78ZhWSwZBZO3N1ZTkAoCNm30chYS880ScISj6j5qJYsslbEcaesMsQA+Zw==", "X-Received": "by 2002:a05:6000:1145:b0:32d:9df1:6f6d with SMTP id\n d5-20020a056000114500b0032d9df16f6dmr4134899wrx.17.1700752445315;\n Thu, 23 Nov 2023 07:14:05 -0800 (PST)", "From": "=?utf-8?q?Juraj_Linke=C5=A1?= <juraj.linkes@pantheon.tech>", "To": "thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, jspewock@iol.unh.edu,\n probb@iol.unh.edu, paul.szczepanek@arm.com, yoan.picchi@foss.arm.com,\n Luca.Vizzarro@arm.com", "Cc": "dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= <juraj.linkes@pantheon.tech>", "Subject": "[PATCH v8 14/21] dts: cpu docstring update", "Date": "Thu, 23 Nov 2023 16:13:37 +0100", "Message-Id": "<20231123151344.162812-15-juraj.linkes@pantheon.tech>", "X-Mailer": "git-send-email 2.34.1", "In-Reply-To": "<20231123151344.162812-1-juraj.linkes@pantheon.tech>", "References": "<20231115130959.39420-1-juraj.linkes@pantheon.tech>\n <20231123151344.162812-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 1b392689f5..9e33b2825d 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@@ -58,6 +91,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@@ -83,28 +117,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@@ -116,6 +152,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@@ -124,31 +171,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 sockets 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@@ -158,24 +219,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 lcores into sockets; this makes it easier in further processing\n if lcore.socket in filtered_lcores:\n filtered_lcores[lcore.socket].append(lcore)\n else:\n@@ -192,12 +266,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@@ -238,15 +313,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@@ -269,6 +350,17 @@ def lcore_filter(\n filter_specifier: LogicalCoreCount | LogicalCoreList,\n ascending: bool,\n ) -> LogicalCoreFilter:\n+ \"\"\"Factory for providing the filter that corresponds to `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 that corresponds to `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": [ "v8", "14/21" ] }{ "id": 134580, "url": "