From patchwork Mon May 23 13:07:13 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tyler Retzlaff X-Patchwork-Id: 111626 X-Patchwork-Delegate: david.marchand@redhat.com Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id E89B8A00C2; Mon, 23 May 2022 15:07:17 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 958564014F; Mon, 23 May 2022 15:07:17 +0200 (CEST) Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by mails.dpdk.org (Postfix) with ESMTP id 5BEE140141 for ; Mon, 23 May 2022 15:07:16 +0200 (CEST) Received: by linux.microsoft.com (Postfix, from userid 1086) id A1EE820B6C7C; Mon, 23 May 2022 06:07:15 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com A1EE820B6C7C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1653311235; bh=CE+AELR+NMUSch5CfJgvEaeCfyjr6KA/4jYNtfvv8AY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=YDoAV2tHy02zPK5mMfVQcQTwjKqqYUmDfQbt0tPSnQOAXejBh9bGR11b0jWZyoPsO GvIS93OnAWktAjGxo+nGtI5QQKvyqeRPt80Efz7wiaaqTVV4h3fM2SR68aWTJaNhxz a7nnDGfvutju5RjOSDkB9KrVw2tbplZyv/1Ln9oI= From: Tyler Retzlaff To: dev@dpdk.org Cc: thomas@monjalon.net, dmitry.kozliuk@gmail.com, anatoly.burakov@intel.com, Tyler Retzlaff , Narcisa Vasile Subject: [PATCH v2 1/2] eal: get/set thread priority per thread identifier Date: Mon, 23 May 2022 06:07:13 -0700 Message-Id: <1653311234-329-2-git-send-email-roretzla@linux.microsoft.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1653311234-329-1-git-send-email-roretzla@linux.microsoft.com> References: <1653302773-11043-1-git-send-email-roretzla@linux.microsoft.com> <1653311234-329-1-git-send-email-roretzla@linux.microsoft.com> X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Add functions for setting and getting the priority of a thread. Priorities on multiple platforms are similarly determined by a priority value and a priority class/policy. Currently in DPDK most threads operate at the OS-default priority level but there are cases when increasing the priority is useful. For example, high performance applications may require elevated priority levels. For these reasons, EAL will expose two priority levels which are named suggestively "normal" and "realtime_critical" and are computed as follows: On Linux, the following mapping is created: RTE_THREAD_PRIORITY_NORMAL corresponds to * policy SCHED_OTHER * priority value: (sched_get_priority_min(SCHED_OTHER) + sched_get_priority_max(SCHED_OTHER))/2; RTE_THREAD_PRIORITY_REALTIME_CRITICAL corresponds to * policy SCHED_RR * priority value: sched_get_priority_max(SCHED_RR); On Windows, the following mapping is created: RTE_THREAD_PRIORITY_NORMAL corresponds to * class NORMAL_PRIORITY_CLASS * priority THREAD_PRIORITY_NORMAL RTE_THREAD_PRIORITY_REALTIME_CRITICAL corresponds to * class REALTIME_PRIORITY_CLASS (when running with privileges) * class HIGH_PRIORITY_CLASS (when running without privileges) * priority THREAD_PRIORITY_TIME_CRITICAL Note that on Linux the resulting priority value will be 0, in accordance to the documentation that mention the value should be 0 for SCHED_OTHER policy. Signed-off-by: Narcisa Vasile Signed-off-by: Tyler Retzlaff --- lib/eal/include/rte_thread.h | 50 +++++++++++++++++ lib/eal/unix/rte_thread.c | 101 +++++++++++++++++++++++++++++++++ lib/eal/version.map | 2 + lib/eal/windows/rte_thread.c | 130 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 283 insertions(+) diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 7888f7a..9e261bf 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -31,6 +31,16 @@ } rte_thread_t; /** + * Thread priority values. + */ +enum rte_thread_priority { + RTE_THREAD_PRIORITY_NORMAL = 0, + /**< normal thread priority, the default */ + RTE_THREAD_PRIORITY_REALTIME_CRITICAL = 1, + /**< highest thread priority allowed */ +}; + +/** * TLS key type, an opaque pointer. */ typedef struct eal_tls_key *rte_thread_key; @@ -115,6 +125,46 @@ int rte_thread_get_affinity_by_id(rte_thread_t thread_id, #endif /* RTE_HAS_CPUSET */ /** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice. + * + * Get the priority of a thread. + * + * @param thread_id + * Id of the thread for which to get priority. + * + * @param priority + * Location to store the retrieved priority. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_get_priority(rte_thread_t thread_id, + enum rte_thread_priority *priority); + +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice. + * + * Set the priority of a thread. + * + * @param thread_id + * Id of the thread for which to set priority. + * + * @param priority + * Priority value to be set. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_set_priority(rte_thread_t thread_id, + enum rte_thread_priority priority); + +/** * Create a TLS data key visible to all threads in the process. * the created key is later used to get/set a value. * and optional destructor can be set to be called when a thread exits. diff --git a/lib/eal/unix/rte_thread.c b/lib/eal/unix/rte_thread.c index 9e5fa47..2b19f4a 100644 --- a/lib/eal/unix/rte_thread.c +++ b/lib/eal/unix/rte_thread.c @@ -16,6 +16,64 @@ struct eal_tls_key { pthread_key_t thread_index; }; +static int +thread_map_priority_to_os_value(enum rte_thread_priority eal_pri, + int *os_pri, int *pol) +{ + /* Clear the output parameters */ + *os_pri = sched_get_priority_min(SCHED_OTHER) - 1; + *pol = -1; + + switch (eal_pri) { + case RTE_THREAD_PRIORITY_NORMAL: + *pol = SCHED_OTHER; + + /* + * Choose the middle of the range to represent + * the priority 'normal'. + * On Linux, this should be 0, since both + * sched_get_priority_min/_max return 0 for SCHED_OTHER. + */ + *os_pri = (sched_get_priority_min(SCHED_OTHER) + + sched_get_priority_max(SCHED_OTHER))/2; + break; + case RTE_THREAD_PRIORITY_REALTIME_CRITICAL: + *pol = SCHED_RR; + *os_pri = sched_get_priority_max(SCHED_RR); + break; + default: + RTE_LOG(DEBUG, EAL, "The requested priority value is invalid.\n"); + return EINVAL; + } + return 0; +} + +static int +thread_map_os_priority_to_eal_priority(int policy, int os_pri, + enum rte_thread_priority *eal_pri) +{ + switch (policy) { + case SCHED_OTHER: + if (os_pri == (sched_get_priority_min(SCHED_OTHER) + + sched_get_priority_max(SCHED_OTHER))/2) { + *eal_pri = RTE_THREAD_PRIORITY_NORMAL; + return 0; + } + break; + case SCHED_RR: + if (os_pri == sched_get_priority_max(SCHED_RR)) { + *eal_pri = RTE_THREAD_PRIORITY_REALTIME_CRITICAL; + return 0; + } + break; + default: + RTE_LOG(DEBUG, EAL, "The OS priority value does not map to an EAL-defined priority.\n"); + return EINVAL; + } + + return 0; +} + rte_thread_t rte_thread_self(void) { @@ -29,6 +87,49 @@ struct eal_tls_key { } int +rte_thread_get_priority(rte_thread_t thread_id, + enum rte_thread_priority *priority) +{ + int ret; + int policy; + struct sched_param param; + + ret = pthread_getschedparam((pthread_t)thread_id.opaque_id, &policy, + ¶m); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_getschedparam failed\n"); + goto cleanup; + } + + return thread_map_os_priority_to_eal_priority(policy, + param.sched_priority, priority); + +cleanup: + return ret; +} + +int +rte_thread_set_priority(rte_thread_t thread_id, + enum rte_thread_priority priority) +{ + int ret; + int policy; + struct sched_param param; + +/* Realtime priority can cause crashes on non-Windows platforms. */ + if (priority == RTE_THREAD_PRIORITY_REALTIME_CRITICAL) + return ENOTSUP; + + ret = thread_map_priority_to_os_value(priority, ¶m.sched_priority, + &policy); + if (ret != 0) + return ret; + + return pthread_setschedparam((pthread_t)thread_id.opaque_id, + policy, ¶m); +} + +int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { int err; diff --git a/lib/eal/version.map b/lib/eal/version.map index d49e30b..ab27a4b 100644 --- a/lib/eal/version.map +++ b/lib/eal/version.map @@ -423,8 +423,10 @@ EXPERIMENTAL { # added in 22.07 rte_thread_get_affinity_by_id; + rte_thread_get_priority; rte_thread_self; rte_thread_set_affinity_by_id; + rte_thread_set_priority; }; INTERNAL { diff --git a/lib/eal/windows/rte_thread.c b/lib/eal/windows/rte_thread.c index a616703..495225e 100644 --- a/lib/eal/windows/rte_thread.c +++ b/lib/eal/windows/rte_thread.c @@ -61,6 +61,58 @@ struct eal_tls_key { return thread_translate_win32_error(error); } +static int +thread_map_priority_to_os_value(enum rte_thread_priority eal_pri, + int *os_pri, DWORD *pri_class) +{ + /* Clear the output parameters */ + *os_pri = -1; + *pri_class = -1; + + switch (eal_pri) { + case RTE_THREAD_PRIORITY_NORMAL: + *pri_class = NORMAL_PRIORITY_CLASS; + *os_pri = THREAD_PRIORITY_NORMAL; + break; + case RTE_THREAD_PRIORITY_REALTIME_CRITICAL: + *pri_class = REALTIME_PRIORITY_CLASS; + *os_pri = THREAD_PRIORITY_TIME_CRITICAL; + break; + default: + RTE_LOG(DEBUG, EAL, "The requested priority value is invalid.\n"); + return EINVAL; + } + + return 0; +} + +static int +thread_map_os_priority_to_eal_value(int os_pri, DWORD pri_class, + enum rte_thread_priority *eal_pri) +{ + switch (pri_class) { + case NORMAL_PRIORITY_CLASS: + if (os_pri == THREAD_PRIORITY_NORMAL) { + *eal_pri = RTE_THREAD_PRIORITY_NORMAL; + return 0; + } + break; + case HIGH_PRIORITY_CLASS: + RTE_LOG(WARNING, EAL, "The OS priority class is high not real-time.\n"); + /* FALLTHROUGH */ + case REALTIME_PRIORITY_CLASS: + if (os_pri == THREAD_PRIORITY_TIME_CRITICAL) { + *eal_pri = RTE_THREAD_PRIORITY_REALTIME_CRITICAL; + return 0; + } + break; + default: + RTE_LOG(DEBUG, EAL, "The OS priority value does not map to an EAL-defined priority.\n"); + return EINVAL; + } + return 0; +} + rte_thread_t rte_thread_self(void) { @@ -72,6 +124,84 @@ struct eal_tls_key { } int +rte_thread_get_priority(rte_thread_t thread_id, + enum rte_thread_priority *priority) +{ + HANDLE thread_handle = NULL; + DWORD pri_class; + int os_pri; + int ret; + + pri_class = GetPriorityClass(GetCurrentProcess()); + if (pri_class == 0) { + ret = thread_log_last_error("GetPriorityClass()"); + goto cleanup; + } + + thread_handle = OpenThread(THREAD_SET_INFORMATION | + THREAD_QUERY_INFORMATION, FALSE, thread_id.opaque_id); + if (thread_handle == NULL) { + ret = thread_log_last_error("OpenThread()"); + goto cleanup; + } + + os_pri = GetThreadPriority(thread_handle); + if (os_pri == THREAD_PRIORITY_ERROR_RETURN) { + ret = thread_log_last_error("GetThreadPriority()"); + goto cleanup; + } + + ret = thread_map_os_priority_to_eal_value(os_pri, pri_class, priority); + if (ret != 0) + goto cleanup; + +cleanup: + if (thread_handle != NULL) + CloseHandle(thread_handle); + + return ret; +} + +int +rte_thread_set_priority(rte_thread_t thread_id, + enum rte_thread_priority priority) +{ + HANDLE thread_handle; + DWORD priority_class; + int os_priority; + int ret = 0; + + thread_handle = OpenThread(THREAD_SET_INFORMATION | + THREAD_QUERY_INFORMATION, FALSE, + thread_id.opaque_id); + if (thread_handle == NULL) { + ret = thread_log_last_error("OpenThread()"); + goto cleanup; + } + + ret = thread_map_priority_to_os_value(priority, &os_priority, + &priority_class); + if (ret != 0) + goto cleanup; + + if (!SetPriorityClass(GetCurrentProcess(), priority_class)) { + ret = thread_log_last_error("SetPriorityClass()"); + goto cleanup; + } + + if (!SetThreadPriority(thread_handle, os_priority)) { + ret = thread_log_last_error("SetThreadPriority()"); + goto cleanup; + } + +cleanup: + if (thread_handle != NULL) + CloseHandle(thread_handle); + + return ret; +} + +int rte_thread_key_create(rte_thread_key *key, __rte_unused void (*destructor)(void *)) { From patchwork Mon May 23 13:07:14 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tyler Retzlaff X-Patchwork-Id: 111628 X-Patchwork-Delegate: david.marchand@redhat.com Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id 78634A00C2; Mon, 23 May 2022 15:07:27 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 44E6342685; Mon, 23 May 2022 15:07:19 +0200 (CEST) Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by mails.dpdk.org (Postfix) with ESMTP id B1BFE40156 for ; Mon, 23 May 2022 15:07:16 +0200 (CEST) Received: by linux.microsoft.com (Postfix, from userid 1086) id AE30720B87F3; Mon, 23 May 2022 06:07:15 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com AE30720B87F3 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1653311235; bh=XFawlqs0bP4s6Fgz4FWh6CiErLcUMjquf2KmwtF1DoE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=GFzbOEUkqDprvXUHiSVr7MYLt+gZYQx2HtkdMJkMlbLtZQhMNTBdyvrAsvs75CUCp 6fav4upliXVJYzhztVevOz6O1CK3kV9QvSTJHpCeNL71eRzuF7YwGjtKw3emIhsxoH YWJUbT1LUIk2BY1KszYSXyYEbyRNJ9mKRzcInOgU= From: Tyler Retzlaff To: dev@dpdk.org Cc: thomas@monjalon.net, dmitry.kozliuk@gmail.com, anatoly.burakov@intel.com, Tyler Retzlaff Subject: [PATCH v2 2/2] test/threads: add unit test for get set priority Date: Mon, 23 May 2022 06:07:14 -0700 Message-Id: <1653311234-329-3-git-send-email-roretzla@linux.microsoft.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1653311234-329-1-git-send-email-roretzla@linux.microsoft.com> References: <1653302773-11043-1-git-send-email-roretzla@linux.microsoft.com> <1653311234-329-1-git-send-email-roretzla@linux.microsoft.com> X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Add unit tests to exercise and demonstrate rte_thread_{get,set}_priority(). Signed-off-by: Tyler Retzlaff --- app/test/test_threads.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/app/test/test_threads.c b/app/test/test_threads.c index e1a2ea5..dbc20e6 100644 --- a/app/test/test_threads.c +++ b/app/test/test_threads.c @@ -24,6 +24,56 @@ } static int +test_thread_priority(void) +{ + pthread_t id; + rte_thread_t thread_id; + enum rte_thread_priority priority; + + thread_id_ready = 0; + RTE_TEST_ASSERT(pthread_create(&id, NULL, thread_main, &thread_id) == 0, + "Failed to create thread"); + + while (__atomic_load_n(&thread_id_ready, __ATOMIC_ACQUIRE) == 0) + ; + + priority = RTE_THREAD_PRIORITY_NORMAL; + RTE_TEST_ASSERT(rte_thread_set_priority(thread_id, priority) == 0, + "Failed to set thread priority"); + RTE_TEST_ASSERT(rte_thread_get_priority(thread_id, &priority) == 0, + "Failed to get thread priority"); + RTE_TEST_ASSERT(priority == RTE_THREAD_PRIORITY_NORMAL, + "Priority set mismatches priority get"); + + priority = RTE_THREAD_PRIORITY_REALTIME_CRITICAL; +#ifndef RTE_EXEC_ENV_WINDOWS + RTE_TEST_ASSERT(rte_thread_set_priority(thread_id, priority) == -ENOTSUP, + "Priority set to critical should fail"); + RTE_TEST_ASSERT(rte_thread_get_priority(thread_id, &priority) == 0, + "Failed to get thread priority"); + RTE_TEST_ASSERT(priority == RTE_THREAD_PRIORITY_NORMAL, + "Failed set to critical should have retained normal"); +#else + RTE_TEST_ASSERT(rte_thread_set_priority(thread_id, priority) == 0, + "Priority set to critical should succeed"); + RTE_TEST_ASSERT(rte_thread_get_priority(thread_id, &priority) == 0, + "Failed to get thread priority"); + RTE_TEST_ASSERT(priority == RTE_THREAD_PRIORITY_REALTIME_CRITICAL, + "Priority set mismatches priority get"); +#endif + + priority = RTE_THREAD_PRIORITY_NORMAL; + RTE_TEST_ASSERT(rte_thread_set_priority(thread_id, priority) == 0, + "Failed to set thread priority"); + RTE_TEST_ASSERT(rte_thread_get_priority(thread_id, &priority) == 0, + "Failed to get thread priority"); + RTE_TEST_ASSERT(priority == RTE_THREAD_PRIORITY_NORMAL, + "Priority set mismatches priority get"); + + return 0; +} + +static int test_thread_affinity(void) { pthread_t id; @@ -31,6 +81,7 @@ rte_cpuset_t cpuset0; rte_cpuset_t cpuset1; + thread_id_ready = 0; RTE_TEST_ASSERT(pthread_create(&id, NULL, thread_main, &thread_id) == 0, "Failed to create thread"); @@ -68,6 +119,7 @@ .teardown = NULL, .unit_test_cases = { TEST_CASE(test_thread_affinity), + TEST_CASE(test_thread_priority), TEST_CASES_END() } };