Re: [dpdk-dev] [PATCH v6 00/10] eal: Add new API for threading
On Mon, Apr 12, 2021 at 09:56:14AM +0200, David Marchand wrote: > On Sat, Apr 3, 2021 at 3:39 AM Narcisa Ana Maria Vasile > wrote: > > > > From: Narcisa Vasile > > > > EAL thread API > > > Thanks for taking on this huge work. > > There is no review on this series and I don't have time for it. > This is a core part of the EAL API. > Seeing how rc1 is at the end of this week, my recommendation is to > postpone to 21.08. > Thanks David! I think we've seen a lot of great contributions recently and maintainers had a lot of patches to look at. The reviewers will probably have more time to analyze this in the next period. > > -- > David Marchand
Re: [dpdk-dev] [PATCH v2] build: fix symlink of drivers for Windows
On Sat, Apr 10, 2021 at 09:01:43AM +0100, Nick Connolly wrote: > The symlink-drivers-solibs.sh script was disabled as part of 'install' > for Windows because there is no support for shell scripts. However, > this means that driver related DLLs are not present in the installed > 'libdir' directory. Add a python script to perform the install and use > it for Windows if the version of meson supports using an external > program with add_install_script (>= 0.55.0). > > On Windows, symbolic links are somewhat problematic since the > SeCreateSymbolicLinkPrivilege is required to be able to create them. > In addition, different cross-compilation environments handle symbolic > links differently, e.g. WSL, Msys2, Cygwin. Rather than trying to > distinguish these scenarios, the python script will perform a file copy > for any Windows specific names. > > On Windows, the shared library outputs have different names depending > upon which toolset has been used to build them. The script currently > handles Clang and GCC. > > On Linux the functionality is unchanged, but could be replaced with the > python script once the required minimum version of meson is >= 0.55.0. > > Fixes: 5c7d86948764 ("build: fix install on Windows") > Cc: sta...@dpdk.org > > Signed-off-by: Nick Connolly > --- > buildtools/symlink-drivers-solibs.py | 49 > config/meson.build | 4 +++ > 2 files changed, 53 insertions(+) > create mode 100644 buildtools/symlink-drivers-solibs.py > > diff --git a/buildtools/symlink-drivers-solibs.py > b/buildtools/symlink-drivers-solibs.py Tested-by: Narcisa Vasile Acked-by: Narcisa Vasile Note that it needs rebasing.
Re: [dpdk-dev] [PATCH v6 03/10] windows/eal: translate Windows errors to errno-style errors
On Thu, Apr 29, 2021 at 03:50:38AM +0300, Dmitry Kozlyuk wrote: > 2021-04-02 18:39 (UTC-0700), Narcisa Ana Maria Vasile: > > From: Narcisa Vasile > > > > Add function to translate Windows error codes to > > errno-style error codes. > > > > Signed-off-by: Narcisa Vasile > > Commit topic should be "eal/windows", not "windows/eal". > > > --- > > lib/librte_eal/include/rte_thread.h | 5 +- > > lib/librte_eal/windows/rte_thread.c | 75 ++--- > > 2 files changed, 60 insertions(+), 20 deletions(-) > > > > diff --git a/lib/librte_eal/include/rte_thread.h > > b/lib/librte_eal/include/rte_thread.h > > index bfdd8e1b1..2d7b3bc05 100644 > > > @@ -87,15 +132,13 @@ rte_thread_key_create(rte_thread_key *key, > > *key = malloc(sizeof(**key)); > > if ((*key) == NULL) { > > RTE_LOG(DEBUG, EAL, "Cannot allocate TLS key.\n"); > > - rte_errno = ENOMEM; > > - return -1; > > + return ENOMEM; > > } > > (*key)->thread_index = TlsAlloc(); > > if ((*key)->thread_index == TLS_OUT_OF_INDEXES) { > > RTE_LOG_WIN32_ERR("TlsAlloc()"); > > free(*key); > > - rte_errno = ENOEXEC; > > - return -1; > > + return rte_thread_translate_win32_error(); > > Logging above can overwrite GetLastError() value. > I suggest splitting rte_thread_translate_win32_error() into translation part > for cases when you have error number already, and a wrapper that calls > GetLastError() to shorten calling code. > Thanks Dmitry! I can split the translation function into something like: static int rte_thread_translate_win32_error(DWORD error) { switch(error) { [...] } } static int get_error_and_translate(void) { return rte_thread_translate_win32_error(GetLastError()); } Is the above what you meant? Here, however, I don't think the wrapper over GetLastError() will be of much help, as we still need to do something like: if ((*key)->thread_index == TLS_OUT_OF_INDEXES) { ret = GetLastError(); RTE_LOG(DEBUG, EAL, "TlsAlloc() failed, GetLastError()=%lu: ", ret); free(*key); return rte_thread_translate_win32_error(ret); } > Same applies below in this file. > > [...]
Re: [dpdk-dev] [PATCH v6 01/10] eal: add thread id and simple thread functions
On Thu, Apr 29, 2021 at 07:28:26PM +0300, Dmitry Kozlyuk wrote: > 2021-04-29 13:05 (UTC+0100), Kinsella, Ray: > > On 29/04/2021 08:44, Thomas Monjalon wrote: > > > 29/04/2021 02:50, Dmitry Kozlyuk: > > >> 2021-04-02 18:38 (UTC-0700), Narcisa Ana Maria Vasile: > > >>> --- /dev/null > > >>> +++ b/lib/librte_eal/windows/include/rte_windows_thread_types.h > > >>> @@ -0,0 +1,12 @@ > > >>> +/* SPDX-License-Identifier: BSD-3-Clause > > >>> + * Copyright(c) 2021 Microsoft Corporation > > >>> + */ > > >>> + > > >>> +#ifndef _RTE_THREAD_TYPES_H_ > > >>> +#define _RTE_THREAD_TYPES_H_ > > >>> + > > >>> +#include > > >>> + > > >>> +typedef DWORD rte_thread_t; > > >>> + > > >>> +#endif /* _RTE_THREAD_TYPES_H_ */ > > >> > > >> pthread_t type in pthreads-win32 and winpthread is not 32 bit. > > >> DPDK will have different ABI depending on a threading backend used. > > >> Apps must know it at build time then. How do they discover it? > > >> This is worth a warning in commit log and docs. > > > > > > Not sure this is an acceptable behaviour. > > > In my opinion, ABI should not vary. > > > +Cc Ray > > > > > > > So pthread_t on Win32 should just map to the HANDLE datatype. > > Which if memory serves is in fact a DWORD on Win32. > > DWORD = uint32_t, HANDLE = void*, which are of different size on x64. > I suggest an opaque 64-bit value to fit pthread_t from MinGW's winpthread. > Only pthreads-win32 has a bigger pthread_t, but we don't have to support it. > > > So I suspect that pthreads indirection is probably be just providing a > > circuitous route to end up in the same place, a HANDLE > > > > IMHO > > To absolutely guarantee no ABI change, we ought to be passing back void * > > not rte_thread_t. > > Yes. Only I'd use a type-safe version: > > typedef struct rte_thread_tag { > void *opaque; /* or uintptr_t per Tyler's suggestion */ > } rte_thread_t; I agree we need a big enough value to fit different identifiers. However, just to clarify, on Windows, there are two distinct thread-related identifiers: thread id (DWORD) and thread HANDLE (void*). In this API implementation I've used the rte_thread_t as the DWORD thread identifier, as the HANDLE can be obtain from this DWORD using the OpenThread() function. I will implement an opaque value that will be assigned the thread id (not the HANDLE) in this API.
[dpdk-dev] [PATCH v10 0/9] eal: Add EAL API for threading
From: Narcisa Vasile EAL thread API **Problem Statement** DPDK currently uses the pthread interface to create and manage threads. Windows does not support the POSIX thread programming model, so it currently relies on a header file that hides the Windows calls under pthread matched interfaces. Given that EAL should isolate the environment specifics from the applications and libraries and mediate all the communication with the operating systems, a new EAL interface is needed for thread management. **Goals** * Introduce a generic EAL API for threading support that will remove the current Windows pthread.h shim. * Replace references to pthread_* across the DPDK codebase with the new RTE_THREAD_* API. * Allow users to choose between using the RTE_THREAD_* API or a 3rd party thread library through a configuration option. **Design plan** New API main files: * rte_thread.h (librte_eal/include) * rte_thread.c (librte_eal/windows) * rte_thread.c (librte_eal/common) **A schematic example of the design** -- lib/librte_eal/include/rte_thread.h int rte_thread_create(); lib/librte_eal/common/rte_thread.c int rte_thread_create() { return pthread_create(); } lib/librte_eal/windows/rte_thread.c int rte_thread_create() { return CreateThread(); } - **Thread attributes** When or after a thread is created, specific characteristics of the thread can be adjusted. Given that the thread characteristics that are of interest for DPDK applications are affinity and priority, the following structure that represents thread attributes has been defined: typedef struct { enum rte_thread_priority priority; rte_cpuset_t cpuset; } rte_thread_attr_t; The *rte_thread_create()* function can optionally receive an rte_thread_attr_t object that will cause the thread to be created with the affinity and priority described by the attributes object. If no rte_thread_attr_t is passed (parameter is NULL), the default affinity and priority are used. An rte_thread_attr_t object can also be set to the default values by calling *rte_thread_attr_init()*. *Priority* is represented through an enum that currently advertises two values for priority: - RTE_THREAD_PRIORITY_NORMAL - RTE_THREAD_PRIORITY_REALTIME_CRITICAL The enum can be extended to allow for multiple priority levels. rte_thread_set_priority - sets the priority of a thread rte_thread_attr_set_priority - updates an rte_thread_attr_t object with a new value for priority The user can choose thread priority through an EAL parameter, when starting an application. If EAL parameter is not used, the per-platform default value for thread priority is used. Otherwise administrator has an option to set one of available options: --thread-prio normal --thread-prio realtime Example: ./dpdk-l2fwd -l 0-3 -n 4 –thread-prio normal -- -q 8 -p *Affinity* is described by the already known “rte_cpuset_t” type. rte_thread_attr_set/get_affinity - sets/gets the affinity field in a rte_thread_attr_t object rte_thread_set/get_affinity – sets/gets the affinity of a thread **Errors** A translation function that maps Windows error codes to errno-style error codes is provided. **Future work** The long term plan is for EAL to provide full threading support: * Add support for conditional variables * Add support for pthread_mutex_trylock * Additional functionality offered by pthread_* (such as pthread_setname_np, etc.) v10: - Remove patch no. 10. It will be broken down in subpatches and sent as a different patchset that depends on this one. This is done due to the ABI breaks that would be caused by patch 10. - Replace unix/rte_thread.c with common/rte_thread.c - Remove initializations that may prevent compiler from issuing useful warnings. - Remove rte_thread_types.h and rte_windows_thread_types.h - Remove unneeded priority macros (EAL_THREAD_PRIORITY*) - Remove functions that retrieves thread handle from process handle - Remove rte_thread_cancel() until same behavior is obtained on all platforms. - Fix rte_thread_detach() function description, return value and remove empty line. - Reimplement mutex functions. Add compatible representation for mutex identifier. Add macro to replace static mutex initialization instances. - Fix commit messages (lines too long, remove unicode symbols) v9: - Sign patches v8: - Rebase - Add rte_thread_detach() API - Set default priority, when user did not specify a value v7: Based on DmitryK's review: - Change thread id representation - Change mutex id representation - Implement static mutex inititalizer for Windows - Change barrier identifier representation - Improve commit messages - Add missing doxygen comments - Split error translation function - Improve name for affinity function - Remove cpuset_size parameter - Fi
[dpdk-dev] [PATCH v10 2/9] eal: add thread attributes
From: Narcisa Vasile Implement thread attributes for: * thread affinity * thread priority Implement functions for managing thread attributes. Priority is represented through an enum that allows for two levels: - RTE_THREAD_PRIORITY_NORMAL - RTE_THREAD_PRIORITY_REALTIME_CRITICAL Affinity is described by the rte_cpuset_t type. An rte_thread_attr_t object can be set to the default values by calling rte_thread_attr_init(). Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 46 ++ lib/eal/include/rte_thread.h | 93 lib/eal/version.map | 4 ++ lib/eal/windows/rte_thread.c | 44 + 4 files changed, 187 insertions(+) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index 92a7451b0a..e1a4d7eae4 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,51 @@ rte_thread_equal(rte_thread_t t1, rte_thread_t t2) return pthread_equal((pthread_t)t1.opaque_id, (pthread_t)t2.opaque_id); } +int +rte_thread_attr_init(rte_thread_attr_t *attr) +{ + RTE_VERIFY(attr != NULL); + + CPU_ZERO(&attr->cpuset); + attr->priority = RTE_THREAD_PRIORITY_NORMAL; + + return 0; +} + +int +rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, +rte_cpuset_t *cpuset) +{ + RTE_VERIFY(thread_attr != NULL); + RTE_VERIFY(cpuset != NULL); + + thread_attr->cpuset = *cpuset; + + return 0; +} + +int +rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, +rte_cpuset_t *cpuset) +{ + RTE_VERIFY(thread_attr != NULL); + RTE_VERIFY(cpuset != NULL); + + *cpuset = thread_attr->cpuset; + + return 0; +} + +int +rte_thread_attr_set_priority(rte_thread_attr_t *thread_attr, +enum rte_thread_priority priority) +{ + RTE_VERIFY(thread_attr != NULL); + + thread_attr->priority = priority; + return 0; +} + int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 748f64d230..032ff73b36 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -31,6 +31,30 @@ typedef struct rte_thread_tag { uintptr_t opaque_id; /**< thread identifier */ } rte_thread_t; +/** + * Thread priority values. + */ +enum rte_thread_priority { + RTE_THREAD_PRIORITY_UNDEFINED = 0, + /**< priority hasn't been defined */ + RTE_THREAD_PRIORITY_NORMAL= 1, + /**< normal thread priority, the default */ + RTE_THREAD_PRIORITY_REALTIME_CRITICAL = 2, + /**< highest thread priority allowed */ +}; + +#ifdef RTE_HAS_CPUSET + +/** + * Representation for thread attributes. + */ +typedef struct { + enum rte_thread_priority priority; /**< thread priority */ + rte_cpuset_t cpuset; /**< thread affinity */ +} rte_thread_attr_t; + +#endif /* RTE_HAS_CPUSET */ + /** * TLS key type, an opaque pointer. */ @@ -63,6 +87,75 @@ int rte_thread_equal(rte_thread_t t1, rte_thread_t t2); #ifdef RTE_HAS_CPUSET +/** + * Initialize the attributes of a thread. + * These attributes can be passed to the rte_thread_create() function + * that will create a new thread and set its attributes according to attr. + * + * @param attr + * Thread attributes to initialize. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_attr_init(rte_thread_attr_t *attr); + +/** + * Set the CPU affinity value in the thread attributes pointed to + * by 'thread_attr'. + * + * @param thread_attr + * Points to the thread attributes in which affinity will be updated. + * + * @param cpuset + * Points to the value of the affinity to be set. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, + rte_cpuset_t *cpuset); + +/** + * Get the value of CPU affinity that is set in the thread attributes pointed + * to by 'thread_attr'. + * + * @param thread_attr + * Points to the thread attributes from which affinity will be retrieved. + * + * @param cpuset + * Pointer to the memory that will store the affinity. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, + rte_cpuset_t *cpuset); + +/** + * Set the thread priority value in the thread attributes pointed to + * by 'thread_attr'. + * + * @param thread_attr + * Points to the thread attributes in which priority will be updated. + * + * @param priority + * Po
[dpdk-dev] [PATCH v10 5/9] eal: implement thread priority management functions
From: Narcisa Vasile Add function for setting the priority for a thread. Priorities on multiple platforms are similarly determined by a priority value and a priority class/policy. 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 * priority THREAD_PRIORITY_TIME_CRITICAL Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 49 ++ lib/eal/include/rte_thread.h | 17 ++ lib/eal/version.map | 1 + lib/eal/windows/rte_thread.c | 66 4 files changed, 133 insertions(+) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index 73b7b3141c..fcebf7097c 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -50,6 +50,55 @@ rte_thread_get_affinity_by_id(rte_thread_t thread_id, sizeof(*cpuset), cpuset); } +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; +} + +int +rte_thread_set_priority(rte_thread_t thread_id, + enum rte_thread_priority priority) +{ + int ret; + int policy; + struct sched_param param; + + 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_attr_init(rte_thread_attr_t *attr) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index ca4ade60e2..5514b2f57f 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -215,6 +215,23 @@ void rte_thread_get_affinity(rte_cpuset_t *cpusetp); #endif /* RTE_HAS_CPUSET */ +/** + * 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. diff --git a/lib/eal/version.map b/lib/eal/version.map index 4f834d8a40..d091aacc47 100644 --- a/lib/eal/version.map +++ b/lib/eal/version.map @@ -432,6 +432,7 @@ EXPERIMENTAL { rte_thread_attr_set_priority; rte_thread_get_affinity_by_id; 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 0127119f49..fb04718f58 100644 --- a/lib/eal/windows/rte_thread.c +++ b/lib/eal/windows/rte_thread.c @@ -200,6 +200,72 @@ rte_thread_get_affinity_by_id(rte_thread_t thread_id, return ret; } +static int +thread_map_priority_to_os_value(enum rte_thread_priority eal_pri, + int *os_pri, int *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
[dpdk-dev] [PATCH v10 1/9] eal: add basic threading functions
From: Narcisa Vasile Use a portable, type-safe representation for the thread identifier. Add functions for comparing thread ids and obtaining the thread id for the current thread. Signed-off-by: Narcisa Vasile --- lib/eal/common/meson.build| 1 + lib/eal/{unix => common}/rte_thread.c | 57 --- lib/eal/include/rte_thread.h | 48 +- lib/eal/unix/meson.build | 1 - lib/eal/version.map | 3 ++ lib/eal/windows/rte_thread.c | 17 6 files changed, 95 insertions(+), 32 deletions(-) rename lib/eal/{unix => common}/rte_thread.c (66%) diff --git a/lib/eal/common/meson.build b/lib/eal/common/meson.build index edfca9..eda250247b 100644 --- a/lib/eal/common/meson.build +++ b/lib/eal/common/meson.build @@ -80,6 +80,7 @@ sources += files( 'rte_random.c', 'rte_reciprocal.c', 'rte_service.c', +'rte_thread.c', 'rte_version.c', ) diff --git a/lib/eal/unix/rte_thread.c b/lib/eal/common/rte_thread.c similarity index 66% rename from lib/eal/unix/rte_thread.c rename to lib/eal/common/rte_thread.c index c72d619ec1..92a7451b0a 100644 --- a/lib/eal/unix/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause * Copyright 2021 Mellanox Technologies, Ltd + * Copyright(c) 2021 Microsoft Corporation */ #include @@ -16,25 +17,41 @@ struct eal_tls_key { pthread_key_t thread_index; }; +rte_thread_t +rte_thread_self(void) +{ + rte_thread_t thread_id; + + thread_id.opaque_id = (uintptr_t)pthread_self(); + + return thread_id; +} + +int +rte_thread_equal(rte_thread_t t1, rte_thread_t t2) +{ + return pthread_equal((pthread_t)t1.opaque_id, (pthread_t)t2.opaque_id); +} + int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { int err; + rte_thread_key k; - *key = malloc(sizeof(**key)); - if ((*key) == NULL) { + k = malloc(sizeof(*k)); + if (k == NULL) { RTE_LOG(DEBUG, EAL, "Cannot allocate TLS key.\n"); - rte_errno = ENOMEM; - return -1; + return EINVAL; } - err = pthread_key_create(&((*key)->thread_index), destructor); - if (err) { + err = pthread_key_create(&(k->thread_index), destructor); + if (err != 0) { RTE_LOG(DEBUG, EAL, "pthread_key_create failed: %s\n", strerror(err)); - free(*key); - rte_errno = ENOEXEC; - return -1; + free(k); + return err; } + *key = k; return 0; } @@ -43,18 +60,16 @@ rte_thread_key_delete(rte_thread_key key) { int err; - if (!key) { + if (key == NULL) { RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); - rte_errno = EINVAL; - return -1; + return EINVAL; } err = pthread_key_delete(key->thread_index); - if (err) { + if (err != 0) { RTE_LOG(DEBUG, EAL, "pthread_key_delete failed: %s\n", strerror(err)); free(key); - rte_errno = ENOEXEC; - return -1; + return err; } free(key); return 0; @@ -65,17 +80,15 @@ rte_thread_value_set(rte_thread_key key, const void *value) { int err; - if (!key) { + if (key == NULL) { RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); - rte_errno = EINVAL; - return -1; + return EINVAL; } err = pthread_setspecific(key->thread_index, value); - if (err) { + if (err != 0) { RTE_LOG(DEBUG, EAL, "pthread_setspecific failed: %s\n", strerror(err)); - rte_errno = ENOEXEC; - return -1; + return err; } return 0; } @@ -83,7 +96,7 @@ rte_thread_value_set(rte_thread_key key, const void *value) void * rte_thread_value_get(rte_thread_key key) { - if (!key) { + if (key == NULL) { RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); rte_errno = EINVAL; return NULL; diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 8be8ed8f36..748f64d230 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2021 Mellanox Technologies, Ltd + * Copyright(c) 2021 Microsoft Corporation */ +#include #include #include @@ -20,11 +22,45 @@ extern "C" { #endif +#include + +/** + * Thread id descriptor. + */ +typedef struct rte_thread_tag { + uintptr_t opaque_id; /**< thread identifier */ +} rte_thread_t; + /** * TLS key type, an opaque pointer. */ typedef struct eal_tls_key *
[dpdk-dev] [PATCH v10 4/9] eal: implement functions for thread affinity management
From: Narcisa Vasile Implement functions for getting/setting thread affinity. Threads can be pinned to specific cores by setting their affinity attribute. Signed-off-by: Narcisa Vasile Signed-off-by: Dmitry Malloy --- lib/eal/common/rte_thread.c | 16 lib/eal/include/rte_thread.h | 36 +++ lib/eal/version.map | 2 + lib/eal/windows/eal_lcore.c | 176 +- lib/eal/windows/eal_windows.h | 10 ++ lib/eal/windows/rte_thread.c | 125 +++- 6 files changed, 319 insertions(+), 46 deletions(-) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index 27ad1c7eb0..73b7b3141c 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -34,6 +34,22 @@ rte_thread_equal(rte_thread_t t1, rte_thread_t t2) return pthread_equal((pthread_t)t1.opaque_id, (pthread_t)t2.opaque_id); } +int +rte_thread_set_affinity_by_id(rte_thread_t thread_id, + const rte_cpuset_t *cpuset) +{ + return pthread_setaffinity_np((pthread_t)thread_id.opaque_id, + sizeof(*cpuset), cpuset); +} + +int +rte_thread_get_affinity_by_id(rte_thread_t thread_id, + rte_cpuset_t *cpuset) +{ + return pthread_getaffinity_np((pthread_t)thread_id.opaque_id, + sizeof(*cpuset), cpuset); +} + int rte_thread_attr_init(rte_thread_attr_t *attr) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index bf649c2fe6..ca4ade60e2 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -87,6 +87,42 @@ int rte_thread_equal(rte_thread_t t1, rte_thread_t t2); #ifdef RTE_HAS_CPUSET +/** + * Set the affinity of thread 'thread_id' to the cpu set + * specified by 'cpuset'. + * + * @param thread_id + *Id of the thread for which to set the affinity. + * + * @param cpuset + * Pointer to CPU affinity to set. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_set_affinity_by_id(rte_thread_t thread_id, + const rte_cpuset_t *cpuset); + +/** + * Get the affinity of thread 'thread_id' and store it + * in 'cpuset'. + * + * @param thread_id + *Id of the thread for which to get the affinity. + * + * @param cpuset + * Pointer for storing the affinity value. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_get_affinity_by_id(rte_thread_t thread_id, + rte_cpuset_t *cpuset); + /** * Initialize the attributes of a thread. * These attributes can be passed to the rte_thread_create() function diff --git a/lib/eal/version.map b/lib/eal/version.map index a74975c437..4f834d8a40 100644 --- a/lib/eal/version.map +++ b/lib/eal/version.map @@ -430,6 +430,8 @@ EXPERIMENTAL { rte_thread_attr_get_affinity; rte_thread_attr_set_affinity; rte_thread_attr_set_priority; + rte_thread_get_affinity_by_id; + rte_thread_set_affinity_by_id; }; INTERNAL { diff --git a/lib/eal/windows/eal_lcore.c b/lib/eal/windows/eal_lcore.c index 476c2d2bdf..295af50698 100644 --- a/lib/eal/windows/eal_lcore.c +++ b/lib/eal/windows/eal_lcore.c @@ -2,7 +2,6 @@ * Copyright(c) 2019 Intel Corporation */ -#include #include #include @@ -27,13 +26,15 @@ struct socket_map { }; struct cpu_map { - unsigned int socket_count; unsigned int lcore_count; + unsigned int socket_count; + unsigned int cpu_count; struct lcore_map lcores[RTE_MAX_LCORE]; struct socket_map sockets[RTE_MAX_NUMA_NODES]; + GROUP_AFFINITY cpus[CPU_SETSIZE]; }; -static struct cpu_map cpu_map = { 0 }; +static struct cpu_map cpu_map; /* eal_create_cpu_map() is called before logging is initialized */ static void @@ -47,13 +48,118 @@ log_early(const char *format, ...) va_end(va); } +static int +eal_query_group_affinity(void) +{ + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *infos = NULL; + unsigned int *cpu_count = &cpu_map.cpu_count; + DWORD infos_size = 0; + int ret = 0; + USHORT group_count; + KAFFINITY affinity; + USHORT group_no; + unsigned int i; + + if (!GetLogicalProcessorInformationEx(RelationGroup, NULL, + &infos_size)) { + DWORD error = GetLastError(); + if (error != ERROR_INSUFFICIENT_BUFFER) { + log_early("Cannot get group information size, " + "error %lu\n", error); + rte_errno = EINVAL; + ret = -1; + goto cleanup; + } + } + + infos = malloc(infos_size); + if (infos == NULL) { + log_early("Cannot allocate memory for NUMA node information\n"); + rte_errno = ENOMEM; +
[dpdk-dev] [PATCH v10 3/9] eal/windows: translate Windows errors to errno-style errors
From: Narcisa Vasile Add function to translate Windows error codes to errno-style error codes. The possible return values are chosen so that we have as much semantical compatibility between platforms as possible. Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 6 +-- lib/eal/include/rte_thread.h | 5 +- lib/eal/windows/rte_thread.c | 95 +++- 3 files changed, 76 insertions(+), 30 deletions(-) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index e1a4d7eae4..27ad1c7eb0 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -47,7 +47,7 @@ rte_thread_attr_init(rte_thread_attr_t *attr) int rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, -rte_cpuset_t *cpuset) + rte_cpuset_t *cpuset) { RTE_VERIFY(thread_attr != NULL); RTE_VERIFY(cpuset != NULL); @@ -59,7 +59,7 @@ rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, int rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, -rte_cpuset_t *cpuset) + rte_cpuset_t *cpuset) { RTE_VERIFY(thread_attr != NULL); RTE_VERIFY(cpuset != NULL); @@ -71,7 +71,7 @@ rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, int rte_thread_attr_set_priority(rte_thread_attr_t *thread_attr, -enum rte_thread_priority priority) + enum rte_thread_priority priority) { RTE_VERIFY(thread_attr != NULL); diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 032ff73b36..bf649c2fe6 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -235,9 +235,8 @@ int rte_thread_value_set(rte_thread_key key, const void *value); * * @return * On success, value data pointer (can also be NULL). - * On failure, NULL and an error number is set in rte_errno. - * rte_errno can be: EINVAL - Invalid parameter passed. - * ENOEXEC - Specific OS error. + * On failure, NULL and a positive error number is set in rte_errno. + * */ __rte_experimental void *rte_thread_value_get(rte_thread_key key); diff --git a/lib/eal/windows/rte_thread.c b/lib/eal/windows/rte_thread.c index 01966e7745..c1ecfbd6ae 100644 --- a/lib/eal/windows/rte_thread.c +++ b/lib/eal/windows/rte_thread.c @@ -13,6 +13,54 @@ struct eal_tls_key { DWORD thread_index; }; +/* Translates the most common error codes related to threads */ +static int +thread_translate_win32_error(DWORD error) +{ + switch (error) { + case ERROR_SUCCESS: + return 0; + + case ERROR_INVALID_PARAMETER: + return EINVAL; + + case ERROR_INVALID_HANDLE: + return EFAULT; + + case ERROR_NOT_ENOUGH_MEMORY: + /* FALLTHROUGH */ + case ERROR_NO_SYSTEM_RESOURCES: + return ENOMEM; + + case ERROR_PRIVILEGE_NOT_HELD: + /* FALLTHROUGH */ + case ERROR_ACCESS_DENIED: + return EACCES; + + case ERROR_ALREADY_EXISTS: + return EEXIST; + + case ERROR_POSSIBLE_DEADLOCK: + return EDEADLK; + + case ERROR_INVALID_FUNCTION: + /* FALLTHROUGH */ + case ERROR_CALL_NOT_IMPLEMENTED: + return ENOSYS; + } + + return EINVAL; +} + +static int +thread_log_last_error(const char *message) +{ + DWORD error = GetLastError(); + RTE_LOG(DEBUG, EAL, "GetLastError()=%lu: %s\n", error, message); + + return thread_translate_win32_error(error); +} + rte_thread_t rte_thread_self(void) { @@ -42,7 +90,7 @@ rte_thread_attr_init(rte_thread_attr_t *attr) int rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, -rte_cpuset_t *cpuset) + rte_cpuset_t *cpuset) { RTE_VERIFY(thread_attr != NULL); thread_attr->cpuset = *cpuset; @@ -52,7 +100,7 @@ rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, int rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, -rte_cpuset_t *cpuset) + rte_cpuset_t *cpuset) { RTE_VERIFY(thread_attr != NULL); @@ -63,7 +111,7 @@ rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, int rte_thread_attr_set_priority(rte_thread_attr_t *thread_attr, -enum rte_thread_priority priority) + enum rte_thread_priority priority) { RTE_VERIFY(thread_attr != NULL); @@ -76,18 +124,18 @@ int rte_thread_key_create(rte_thread_key *key, __rte_unused void (*destructor)(void *)) { + int ret; + *key = malloc(sizeof(**key)); if ((*key) == NULL) { RTE_LOG(DEBUG, EAL, "Cannot allocate TLS key.\n"); - rte_errno = ENOMEM; - return -1; + return ENOMEM; } (*key)->thread_index = TlsAlloc();
[dpdk-dev] [PATCH v10 7/9] eal: implement functions for mutex management
From: Narcisa Vasile Add functions for mutex init, destroy, lock, unlock. Add RTE_STATIC_MUTEX macro to replace static initialization of mutexes. Windows does not have a static initializer. Initialization is only done through InitializeCriticalSection(). The RTE_STATIC_MUTEX calls into the rte_thread_mutex_init() function that performs the actual mutex initialization. Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 61 +++ lib/eal/include/rte_thread.h | 94 lib/eal/version.map | 4 ++ lib/eal/windows/rte_thread.c | 53 4 files changed, 212 insertions(+) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index a0a51bc190..ebae4a8af1 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -251,6 +251,67 @@ rte_thread_detach(rte_thread_t thread_id) return pthread_detach((pthread_t)thread_id.opaque_id); } +int +rte_thread_mutex_init(rte_thread_mutex *mutex) +{ + int ret = 0; + pthread_mutex_t *m = NULL; + + RTE_VERIFY(mutex != NULL); + + m = calloc(1, sizeof(*m)); + if (m == NULL) { + RTE_LOG(DEBUG, EAL, "Unable to initialize mutex. Insufficient memory!\n"); + ret = ENOMEM; + goto cleanup; + } + + ret = pthread_mutex_init(m, NULL); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "Failed to init mutex. ret = %d\n", ret); + goto cleanup; + } + + mutex->mutex_id = m; + m = NULL; + +cleanup: + free(m); + return ret; +} + +int +rte_thread_mutex_lock(rte_thread_mutex *mutex) +{ + RTE_VERIFY(mutex != NULL); + + return pthread_mutex_lock((pthread_mutex_t *)mutex->mutex_id); +} + +int +rte_thread_mutex_unlock(rte_thread_mutex *mutex) +{ + RTE_VERIFY(mutex != NULL); + + return pthread_mutex_unlock((pthread_mutex_t *)mutex->mutex_id); +} + +int +rte_thread_mutex_destroy(rte_thread_mutex *mutex) +{ + int ret = 0; + RTE_VERIFY(mutex != NULL); + + ret = pthread_mutex_destroy((pthread_mutex_t *)mutex->mutex_id); + if (ret != 0) + RTE_LOG(DEBUG, EAL, "Unable to destroy mutex, ret = %d\n", ret); + + free(mutex->mutex_id); + mutex->mutex_id = NULL; + + return ret; +} + int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 098c3ba343..7e813b573d 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -56,6 +56,26 @@ typedef struct { #endif /* RTE_HAS_CPUSET */ +#define RTE_DECLARE_MUTEX(private_lock) rte_thread_mutex private_lock + +#define RTE_DEFINE_MUTEX(private_lock)\ +RTE_INIT(__rte_ ## private_lock ## _init)\ +{\ + RTE_VERIFY(rte_thread_mutex_init(&private_lock) == 0);\ +} + +#define RTE_STATIC_MUTEX(private_lock)\ +static RTE_DECLARE_MUTEX(private_lock);\ +RTE_DEFINE_MUTEX(private_lock) + + +/** + * Thread mutex representation. + */ +typedef struct rte_thread_mutex_tag { + void *mutex_id; /**< mutex identifier */ +} rte_thread_mutex; + /** * TLS key type, an opaque pointer. */ @@ -268,6 +288,28 @@ int rte_thread_join(rte_thread_t thread_id, unsigned long *value_ptr); __rte_experimental int rte_thread_detach(rte_thread_t thread_id); +/** + * Set core affinity of the current thread. + * Support both EAL and non-EAL thread and update TLS. + * + * @param cpusetp + * Pointer to CPU affinity to set. + * + * @return + * On success, return 0; otherwise return -1; + */ +int rte_thread_set_affinity(rte_cpuset_t *cpusetp); + +/** + * Get core affinity of the current thread. + * + * @param cpusetp + * Pointer to CPU affinity of current thread. + * It presumes input is not NULL, otherwise it causes panic. + * + */ +void rte_thread_get_affinity(rte_cpuset_t *cpusetp); + #endif /* RTE_HAS_CPUSET */ /** @@ -287,6 +329,58 @@ __rte_experimental int rte_thread_set_priority(rte_thread_t thread_id, enum rte_thread_priority priority); +/** + * Initializes a mutex. + * + * @param mutex + *The mutex to be initialized. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_mutex_init(rte_thread_mutex *mutex); + +/** + * Locks a mutex. + * + * @param mutex + *The mutex to be locked. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_mutex_lock(rte_thread_mutex *mutex); + +/** + * Unlocks a mutex. + * + * @param mutex + *The mutex to be unlocked. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_mutex_unlock(rte_thread_mutex *mutex); + +/** + * Releases all resources associated with a
[dpdk-dev] [PATCH v10 6/9] eal: add thread lifetime management
From: Narcisa Vasile Add functions for thread creation, joining, detaching. The *rte_thread_create()* function can optionally receive an rte_thread_attr_t object that will cause the thread to be created with the affinity and priority described by the attributes object. If no rte_thread_attr_t is passed (parameter is NULL), the default affinity and priority are used. On Windows, the function executed by a thread when the thread starts is represeneted by a function pointer of type DWORD (*func) (void*). On other platforms, the function pointer is a void* (*func) (void*). Performing a cast between these two types of function pointers to uniformize the API on all platforms may result in undefined behavior. TO fix this issue, a wrapper that respects the signature required by CreateThread() has been created on Windows. Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 107 + lib/eal/include/rte_thread.h| 55 + lib/eal/version.map | 3 + lib/eal/windows/include/sched.h | 2 +- lib/eal/windows/rte_thread.c| 138 5 files changed, 304 insertions(+), 1 deletion(-) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index fcebf7097c..a0a51bc190 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -144,6 +144,113 @@ rte_thread_attr_set_priority(rte_thread_attr_t *thread_attr, return 0; } +int +rte_thread_create(rte_thread_t *thread_id, + const rte_thread_attr_t *thread_attr, + rte_thread_func thread_func, void *args) +{ + int ret = 0; + pthread_attr_t attr; + pthread_attr_t *attrp = NULL; + struct sched_param param = { + .sched_priority = 0, + }; + int policy = SCHED_OTHER; + + if (thread_attr != NULL) { + ret = pthread_attr_init(&attr); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_init failed\n"); + goto cleanup; + } + + attrp = &attr; + + if (thread_attr->priority != RTE_THREAD_PRIORITY_UNDEFINED) { + /* +* Set the inherit scheduler parameter to explicit, +* otherwise the priority attribute is ignored. +*/ + ret = pthread_attr_setinheritsched(attrp, + PTHREAD_EXPLICIT_SCHED); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_setinheritsched failed\n"); + goto cleanup; + } + + ret = thread_map_priority_to_os_value( + thread_attr->priority, + ¶m.sched_priority, &policy + ); + if (ret != 0) + goto cleanup; + + ret = pthread_attr_setschedpolicy(attrp, policy); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_setschedpolicy failed\n"); + goto cleanup; + } + + ret = pthread_attr_setschedparam(attrp, ¶m); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_setschedparam failed\n"); + goto cleanup; + } + } + + if (CPU_COUNT(&thread_attr->cpuset) > 0) { + ret = pthread_attr_setaffinity_np(attrp, + sizeof(thread_attr->cpuset), + &thread_attr->cpuset); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_setaffinity_np failed\n"); + goto cleanup; + } + } + } + + ret = pthread_create((pthread_t *)&thread_id->opaque_id, attrp, + thread_func, args); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_create failed\n"); + goto cleanup; + } + +cleanup: + if (attrp != NULL) + pthread_attr_destroy(&attr); + + return ret; +} + +int +rte_thread_join(rte_thread_t thread_id, unsigned long *value_ptr) +{ + int ret = 0; + void *res = NULL; + void **pres = NULL; + + if (value_ptr != NULL) + pres = &res; + + ret = pthread_join((pthread_t)thread_id.opaque_id, pres); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_join failed\n"); + return ret; + } + + if (pres != NULL) + *value_ptr = *(unsigned long *)(*pres); + + return 0; +} + +i
[dpdk-dev] [PATCH v10 8/9] eal: implement functions for thread barrier management
From: Narcisa Vasile Add functions for barrier init, destroy, wait. A portable type is used to represent a barrier identifier. The rte_thread_barrier_wait() function returns the same value on all platforms. Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 61 lib/eal/include/rte_thread.h | 58 ++ lib/eal/version.map | 3 ++ lib/eal/windows/rte_thread.c | 56 + 4 files changed, 178 insertions(+) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index ebae4a8af1..3fdb267337 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -312,6 +312,67 @@ rte_thread_mutex_destroy(rte_thread_mutex *mutex) return ret; } +int +rte_thread_barrier_init(rte_thread_barrier *barrier, int count) +{ + int ret = 0; + pthread_barrier_t *pthread_barrier = NULL; + + RTE_VERIFY(barrier != NULL); + RTE_VERIFY(count > 0); + + pthread_barrier = calloc(1, sizeof(*pthread_barrier)); + if (pthread_barrier == NULL) { + RTE_LOG(DEBUG, EAL, "Unable to initialize barrier. Insufficient memory!\n"); + ret = ENOMEM; + goto cleanup; + } + ret = pthread_barrier_init(pthread_barrier, NULL, count); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "Failed to init barrier, ret = %d\n", ret); + goto cleanup; + } + + barrier->barrier_id = pthread_barrier; + pthread_barrier = NULL; + +cleanup: + free(pthread_barrier); + return ret; +} + +int +rte_thread_barrier_wait(rte_thread_barrier *barrier) +{ + int ret = 0; + + RTE_VERIFY(barrier != NULL); + RTE_VERIFY(barrier->barrier_id != NULL); + + ret = pthread_barrier_wait(barrier->barrier_id); + if (ret == PTHREAD_BARRIER_SERIAL_THREAD) + ret = RTE_THREAD_BARRIER_SERIAL_THREAD; + + return ret; +} + +int +rte_thread_barrier_destroy(rte_thread_barrier *barrier) +{ + int ret = 0; + + RTE_VERIFY(barrier != NULL); + + ret = pthread_barrier_destroy(barrier->barrier_id); + if (ret != 0) + RTE_LOG(DEBUG, EAL, "Failed to destroy barrier: %d\n", ret); + + free(barrier->barrier_id); + barrier->barrier_id = NULL; + + return ret; +} + int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 7e813b573d..40da83467b 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -76,6 +76,18 @@ typedef struct rte_thread_mutex_tag { void *mutex_id; /**< mutex identifier */ } rte_thread_mutex; +/** + * Returned by rte_thread_barrier_wait() when call is successful. + */ +#define RTE_THREAD_BARRIER_SERIAL_THREAD -1 + +/** + * Thread barrier representation. + */ +typedef struct rte_thread_barrier_tag { + void *barrier_id; /**< barrrier identifier */ +} rte_thread_barrier; + /** * TLS key type, an opaque pointer. */ @@ -381,6 +393,52 @@ int rte_thread_mutex_unlock(rte_thread_mutex *mutex); __rte_experimental int rte_thread_mutex_destroy(rte_thread_mutex *mutex); +/** + * Initializes a synchronization barrier. + * + * @param barrier + *A pointer that references the newly created 'barrier' object. + * + * @param count + *The number of threads that must enter the barrier before + *the threads can continue execution. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_barrier_init(rte_thread_barrier *barrier, int count); + +/** + * Causes the calling thread to wait at the synchronization barrier 'barrier'. + * + * @param barrier + *The barrier used for synchronizing the threads. + * + * @return + * Return RTE_THREAD_BARRIER_SERIAL_THREAD for the thread synchronized + * at the barrier. + * Return 0 for all other threads. + * Return a positive errno-style error number, in case of failure. + */ +__rte_experimental +int rte_thread_barrier_wait(rte_thread_barrier *barrier); + +/** + * Releases all resources used by a synchronization barrier + * and uninitializes it. + * + * @param barrier + *The barrier to be destroyed. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_barrier_destroy(rte_thread_barrier *barrier); + /** * Create a TLS data key visible to all threads in the process. * the created key is later used to get/set a value. diff --git a/lib/eal/version.map b/lib/eal/version.map index 75bfa5c02e..6645f60a78 100644 --- a/lib/eal/version.map +++ b/lib/eal/version.map @@ -440,6 +440,9 @@ EXPERIMENTAL { rte_thread_mutex_lock; rte_thread_mutex_unlock; rte_thread_mutex_destroy; + rte_thread_barrier_init; +
[dpdk-dev] [PATCH v10 9/9] eal: add EAL argument for setting thread priority
From: Narcisa Vasile Allow the user to choose the thread priority through an EAL command line argument. The user can choose thread priority through an EAL parameter, when starting an application. If EAL parameter is not used, the per-platform default value for thread priority is used. Otherwise administrator has an option to set one of available options: --thread-prio normal --thread-prio realtime Example: ./dpdk-l2fwd -l 0-3 -n 4 --thread-prio normal -- -q 8 -p Signed-off-by: Narcisa Vasile --- lib/eal/common/eal_common_options.c | 28 +++- lib/eal/common/eal_internal_cfg.h | 2 ++ lib/eal/common/eal_options.h| 2 ++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/eal/common/eal_common_options.c b/lib/eal/common/eal_common_options.c index ff5861b5f3..9d29696b84 100644 --- a/lib/eal/common/eal_common_options.c +++ b/lib/eal/common/eal_common_options.c @@ -107,6 +107,7 @@ eal_long_options[] = { {OPT_TELEMETRY, 0, NULL, OPT_TELEMETRY_NUM}, {OPT_NO_TELEMETRY, 0, NULL, OPT_NO_TELEMETRY_NUM }, {OPT_FORCE_MAX_SIMD_BITWIDTH, 1, NULL, OPT_FORCE_MAX_SIMD_BITWIDTH_NUM}, + {OPT_THREAD_PRIORITY, 1, NULL, OPT_THREAD_PRIORITY_NUM}, /* legacy options that will be removed in future */ {OPT_PCI_BLACKLIST, 1, NULL, OPT_PCI_BLACKLIST_NUM}, @@ -1412,6 +1413,24 @@ eal_parse_simd_bitwidth(const char *arg) return 0; } +static int +eal_parse_thread_priority(const char *arg) +{ + struct internal_config *internal_conf = + eal_get_internal_configuration(); + enum rte_thread_priority priority; + + if (!strncmp("normal", arg, sizeof("normal"))) + priority = RTE_THREAD_PRIORITY_NORMAL; + else if (!strncmp("realtime", arg, sizeof("realtime"))) + priority = RTE_THREAD_PRIORITY_REALTIME_CRITICAL; + else + return -1; + + internal_conf->thread_priority = priority; + return 0; +} + static int eal_parse_base_virtaddr(const char *arg) { @@ -1825,7 +1844,13 @@ eal_parse_common_option(int opt, const char *optarg, return -1; } break; - + case OPT_THREAD_PRIORITY_NUM: + if (eal_parse_thread_priority(optarg) < 0) { + RTE_LOG(ERR, EAL, "invalid parameter for --" + OPT_THREAD_PRIORITY "\n"); + return -1; + } + break; /* don't know what to do, leave this to caller */ default: return 1; @@ -2088,6 +2113,7 @@ eal_common_usage(void) " (can be used multiple times)\n" " --"OPT_VMWARE_TSC_MAP"Use VMware TSC map instead of native RDTSC\n" " --"OPT_PROC_TYPE" Type of this process (primary|secondary|auto)\n" + " --"OPT_THREAD_PRIORITY" Set threads priority (normal|realtime)\n" #ifndef RTE_EXEC_ENV_WINDOWS " --"OPT_SYSLOG"Set syslog facility\n" #endif diff --git a/lib/eal/common/eal_internal_cfg.h b/lib/eal/common/eal_internal_cfg.h index d6c0470eb8..b2996cd65b 100644 --- a/lib/eal/common/eal_internal_cfg.h +++ b/lib/eal/common/eal_internal_cfg.h @@ -94,6 +94,8 @@ struct internal_config { unsigned int no_telemetry; /**< true to disable Telemetry */ struct simd_bitwidth max_simd_bitwidth; /**< max simd bitwidth path to use */ + enum rte_thread_priority thread_priority; + /**< thread priority to configure */ }; void eal_reset_internal_config(struct internal_config *internal_cfg); diff --git a/lib/eal/common/eal_options.h b/lib/eal/common/eal_options.h index 7b348e707f..9f5b209f64 100644 --- a/lib/eal/common/eal_options.h +++ b/lib/eal/common/eal_options.h @@ -93,6 +93,8 @@ enum { OPT_NO_TELEMETRY_NUM, #define OPT_FORCE_MAX_SIMD_BITWIDTH "force-max-simd-bitwidth" OPT_FORCE_MAX_SIMD_BITWIDTH_NUM, +#define OPT_THREAD_PRIORITY "thread-prio" + OPT_THREAD_PRIORITY_NUM, /* legacy option that will be removed in future */ #define OPT_PCI_BLACKLIST "pci-blacklist" -- 2.31.0.vfs.0.1
Re: [dpdk-dev] [PATCH v9 01/10] eal: add thread id and simple thread functions
On Wed, Jun 09, 2021 at 02:03:48AM +0300, Dmitry Kozlyuk wrote: > 2021-06-04 16:44 (UTC-0700), Narcisa Ana Maria Vasile: > > From: Narcisa Vasile > > > > Use a portable, type-safe representation for the thread identifier. > > Add functions for comparing thread ids and obtaining the thread id > > for the current thread. > > > > Signed-off-by: Narcisa Vasile > > --- > > lib/eal/common/rte_thread.c | 105 ++ > > lib/eal/include/rte_thread.h | 53 +++-- > > lib/eal/include/rte_thread_types.h| 10 ++ > > .../include/rte_windows_thread_types.h| 10 ++ > > lib/eal/windows/rte_thread.c | 17 +++ > > 5 files changed, 186 insertions(+), 9 deletions(-) > > create mode 100644 lib/eal/common/rte_thread.c > > create mode 100644 lib/eal/include/rte_thread_types.h > > create mode 100644 lib/eal/windows/include/rte_windows_thread_types.h > > It is strange that new files are being filled in the series, but are neither > compiled nor installed until the last patch. Any reason not to replace > lib/eal/unix/rte_thread.c starting from this patch? > Replaced unix/rte_thread.c starting from here. > A better name for rte_thread_types.h would be rte_posix_thread_types.h > to indicate this file is not really common. > > > > > +rte_thread_t > > +rte_thread_self(void) > > +{ > > + rte_thread_t thread_id = { 0 }; > > (Applies to entire series.) > Please do not initialize variables when you intend to overwrite them. > This prevents compiler from reporting code paths where the variable is used > before a proper assignment. Thanks for the explanation, Dmitry! > > > [...] > > diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h > > index 8be8ed8f36..347df1a6ae 100644 > directly, see lib/eal/$arch/rte_atomic_64.h for examples. > > Note that rte_*_thread_types.h should be `indirect_headers`, not `headers` > in `meson.build` so that they're not checked by `buildtools/chkincs` when is > gets enabled for Windows. This is especially > true for lib/eal/common/rte_thread.h that cannot work without pthread. > > However, in later patches these headers only contain mutex bits, > see the comment to pathc 07/10 about them. Maybe we don't need these files > after all. Agreed, I was able to remove them after reimplementing the mutex functions.
Re: [dpdk-dev] [PATCH v9 06/10] eal: add thread lifetime management
On Wed, Jun 09, 2021 at 02:04:09AM +0300, Dmitry Kozlyuk wrote: > 2021-06-04 16:44 (UTC-0700), Narcisa Ana Maria Vasile: > [...] > > diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h > > index 5c54cd9d67..1d481b9ad5 100644 > > --- a/lib/eal/include/rte_thread.h > > +++ b/lib/eal/include/rte_thread.h > > +__rte_experimental > > +int rte_thread_create(rte_thread_t *thread_id, > > + const rte_thread_attr_t *thread_attr, > > + void *(*thread_func)(void *), void *args); > > 1. Thread function prototype is used at least in 4 places, > maybe give it a name, like `rte_thread_func`? > > 2. We can't easily support it's `void*` return type on Windows, > because it doesn't fit in DWORD. In `rte_thread_join` below you use `int`. > All `pthread_join` usages in DPDK ignore return value, but I'd rather keep it. > Do you think it's OK to stick to `int`? > Thank you, I agree that we should keep it. I've changed it to unsigned long to fit with Windows's DWORD as well. > [...] > > +/** > > + * Terminates a thread. > > + * > > + * @param thread_id > > + *The id of the thread to be cancelled. > > + * > > + * @return > > + * On success, return 0. > > + * On failure, return a positive errno-style error number. > > + */ > > +__rte_experimental > > +int rte_thread_cancel(rte_thread_t thread_id); > > What do you think of making this function internal for now? > We don't want applications to rely on this prototype. > To hide it from Doxygen, `/*` comment or #ifndef __DOXYGEN__ can be used. > It is worth noting in commit message > that it's not implemented for Windows and why. > Thank you, I've removed it for now. > > + > > + HANDLE thread_handle = NULL; > > + GROUP_AFFINITY thread_affinity; > > + struct thread_routine_ctx *ctx = NULL; > > + > > + ctx = calloc(1, sizeof(*ctx)); > > Why use `calloc()` for a scalar? ctx is pointer to struct that holds the thread function pointer and its arguments. Did I misunderstand what you meant? >
Re: [dpdk-dev] [PATCH v9 04/10] eal: implement functions for thread affinity management
On Wed, Jun 09, 2021 at 02:03:57AM +0300, Dmitry Kozlyuk wrote: > 2021-06-04 16:44 (UTC-0700), Narcisa Ana Maria Vasile: > [...] > > diff --git a/lib/eal/windows/rte_thread.c b/lib/eal/windows/rte_thread.c > > index 6ea1dc2a05..9e74a538c2 100644 > > --- a/lib/eal/windows/rte_thread.c > > +++ b/lib/eal/windows/rte_thread.c > > @@ -7,7 +7,8 @@ > > #include > > #include > > #include > > -#include > > + > > +#include "eal_windows.h" > > > > struct eal_tls_key { > > DWORD thread_index; > > @@ -77,6 +78,130 @@ rte_thread_equal(rte_thread_t t1, rte_thread_t t2) > > return t1.opaque_id == t2.opaque_id; > > } > > > > +static int > > +rte_convert_cpuset_to_affinity(const rte_cpuset_t *cpuset, > > + PGROUP_AFFINITY affinity) > > +{ > > + int ret = 0; > > + PGROUP_AFFINITY cpu_affinity = NULL; > > + > > + memset(affinity, 0, sizeof(GROUP_AFFINITY)); > > + affinity->Group = (USHORT)-1; > > + > > + /* Check that all cpus of the set belong to the same processor group and > > +* accumulate thread affinity to be applied. > > +*/ > > + for (unsigned int cpu_idx = 0; cpu_idx < CPU_SETSIZE; cpu_idx++) { > > + if (!CPU_ISSET(cpu_idx, cpuset)) > > + continue; > > + > > + cpu_affinity = eal_get_cpu_affinity(cpu_idx); > > + > > + if (affinity->Group == (USHORT)-1) { > > + affinity->Group = cpu_affinity->Group; > > + } else if (affinity->Group != cpu_affinity->Group) { > > + ret = EINVAL; > > + goto cleanup; > > + } > > + > > + affinity->Mask |= cpu_affinity->Mask; > > + } > > + > > + if (affinity->Mask == 0) { > > + ret = EINVAL; > > + goto cleanup; > > + } > > + > > +cleanup: > > + return ret; > > +} > > For v5 I asked a question that possibly got lost among other comments. > Repeating the question for convenience: > > Just to be clear: is it a kernel limitation that a thread can only > run on cores of one processor group, or do we impose it so that API > is atomic (transactional), i.e. because one of multiple > SetThreadGroupAffinity() calls may fail and leave thread partially > affinitized? The second reason (to ensure full affinitization). I am not aware of a kernel limitation, but I'll double check with Dmitry as we co-engineered this patch.
Re: [dpdk-dev] [PATCH v9 10/10] Enable the new EAL thread API
On Wed, Jun 09, 2021 at 02:08:22AM +0300, Dmitry Kozlyuk wrote: > 2021-06-07 22:50 (UTC-0700), Narcisa Ana Maria Vasile: > > On Fri, Jun 04, 2021 at 04:44:34PM -0700, Narcisa Ana Maria Vasile wrote: > > > From: Narcisa Vasile > > > > > > Rename pthread_* occurrences with the new rte_thread_* API. > > > Enable the new API in the build system. > > > > > > Signed-off-by: Narcisa Vasile > > > --- > > > > I'll send v10. > > Please also run devtools/checkpatches.sh > and fix numerous warnings across the series. > Some patches could be acked already if not this issue. Thanks, I've fixed the CS errors. There will be a few warnings that I can't fix (the negative errors for example).
Re: [dpdk-dev] [PATCH v9 10/10] Enable the new EAL thread API
On Tue, Jun 08, 2021 at 09:45:44AM +0200, David Marchand wrote: > On Tue, Jun 8, 2021 at 7:50 AM Narcisa Ana Maria Vasile > wrote: > > > > On Fri, Jun 04, 2021 at 04:44:34PM -0700, Narcisa Ana Maria Vasile wrote: > > > From: Narcisa Vasile > > > > > > Rename pthread_* occurrences with the new rte_thread_* API. > > > Enable the new API in the build system. > > > > > > Signed-off-by: Narcisa Vasile > > > --- > > > > I'll send v10. > > Can someone please help with an example on how to check for ABI breaks? > > Thank you! > > > > I've run: > > DPDK_ABI_REF_VERSION=v21.05 DPDK_ABI_REF_DIR=~/ref > > ./devtools/test-meson-builds.sh > > which doesn't give any warnings about the ABI break. > > This should work the way you tried if you have working toolchains and > libabigail installed. > Something is off in your env. > > Side note: ovsrobot is out those days (we have some trouble in one of > RH labs and it happens ovsrobot is hosted there), but you could try > with a github repo of yours + GHA, and the ABI failure should be > caught too. > > > I just tried on my rhel7 (gcc 4.8.5 + libabigail 1.8.2) with your > series applied. > $ DPDK_ABI_REF_VERSION=v21.05 > DPDK_ABI_REF_DIR=~/git/pub/dpdk.org/reference > ./devtools/test-meson-builds.sh > ... > Error: ABI issue reported for 'abidiff --suppr > /home/dmarchan/git/pub/dpdk.org/devtools/../devtools/libabigail.abignore > --no-added-syms --headers-dir1 > /home/dmarchan/git/pub/dpdk.org/reference/v21.05/build-gcc-shared/usr/local/include > --headers-dir2 > /home/dmarchan/git/pub/dpdk.org/build-gcc-shared/install/usr/local/include > /home/dmarchan/git/pub/dpdk.org/reference/v21.05/build-gcc-shared/dump/librte_eal.dump > /home/dmarchan/git/pub/dpdk.org/build-gcc-shared/install/dump/librte_eal.dump' > ABIDIFF_ABI_CHANGE, this change requires a review (abidiff flagged > this as a potential issue). > > > $ abidiff --suppr > /home/dmarchan/git/pub/dpdk.org/devtools/../devtools/libabigail.abignore > --no-added-syms --headers-dir1 > /home/dmarchan/git/pub/dpdk.org/reference/v21.05/build-gcc-shared/usr/local/include > --headers-dir2 > /home/dmarchan/git/pub/dpdk.org/build-gcc-shared/install/usr/local/include > /home/dmarchan/git/pub/dpdk.org/reference/v21.05/build-gcc-shared/dump/librte_eal.dump > /home/dmarchan/git/pub/dpdk.org/build-gcc-shared/install/dump/librte_eal.dump > Functions changes summary: 0 Removed, 2 Changed (1 filtered out), 0 > Added (20 filtered out) functions > Variables changes summary: 0 Removed, 0 Changed, 0 Added variable > > 2 functions with some indirect sub-type change: > > [C] 'function int rte_ctrl_thread_create(pthread_t*, const char*, > const pthread_attr_t*, void* (void*)*, void*)' at rte_lcore.h:443:1 > has some indirect sub-type changes: > parameter 1 of type 'pthread_t*' changed: > in pointed to type 'typedef pthread_t' at rte_thread.h:42:1: > typedef name changed from pthread_t to rte_thread_t at > rte_thread.h:42:1 > underlying type 'unsigned long int' changed: > entity changed from 'unsigned long int' to 'struct > rte_thread_tag' at rte_thread.h:40:1 > type size hasn't changed > parameter 3 of type 'const pthread_attr_t*' changed: > in pointed to type 'const pthread_attr_t': > 'const pthread_attr_t' changed to 'const rte_thread_attr_t' > > [C] 'function int rte_thread_setname(pthread_t, const char*)' at > rte_lcore.h:377:1 has some indirect sub-type changes: > parameter 1 of type 'typedef pthread_t' changed: > typedef name changed from pthread_t to rte_thread_t at rte_thread.h:42:1 > underlying type 'unsigned long int' changed: > entity changed from 'unsigned long int' to 'struct > rte_thread_tag' at rte_thread.h:40:1 > type size hasn't changed > > > > Can you check that in your env build-gcc-shared/ and the build > directory for references are configured with debug symbols? > You should see: > $ meson configure build-gcc-shared | awk '$1=="buildtype" {print $2}' > debugoptimized > $ meson configure reference/v21.05/build | awk '$1=="buildtype" {print $2}' > debugoptimized > > Thank you very much David! There was something wrong with my local reference. Using your commands, I am able to run the tools now. >
[dpdk-dev] [PATCH 0/6] Enable the internal EAL thread API
From: Narcisa Vasile This patchset enables the new EAL thread API. Rename pthread* with rte_thread* corresponding symbols. Set thread attributes. Add option for choosing between internal API or external lib. Depends-on: series-17402 ("eal: Add EAL API for threading") Narcisa Vasile (6): eal: add function that sets thread name eal: add function for control thread creation Enable the new EAL thread API in app, drivers and examples lib: enable the new EAL thread API eal: set affinity and priority attributes Allow choice between internal EAL thread API and external lib app/test/process.h| 8 +- app/test/test_lcores.c| 18 +- app/test/test_link_bonding.c | 14 +- app/test/test_lpm_perf.c | 12 +- config/meson.build| 1 - drivers/bus/dpaa/base/qbman/bman_driver.c | 5 +- drivers/bus/dpaa/base/qbman/dpaa_sys.c| 14 +- drivers/bus/dpaa/base/qbman/process.c | 6 +- drivers/bus/dpaa/dpaa_bus.c | 14 +- drivers/bus/fslmc/portal/dpaa2_hw_dpio.c | 19 +- drivers/common/dpaax/compat.h | 2 +- drivers/common/mlx5/windows/mlx5_common_os.h | 1 + drivers/compress/mlx5/mlx5_compress.c | 10 +- drivers/event/dlb2/dlb2.c | 2 +- drivers/event/dlb2/pf/base/dlb2_osdep.h | 7 +- drivers/mempool/dpaa/dpaa_mempool.c | 2 +- drivers/net/af_xdp/rte_eth_af_xdp.c | 18 +- drivers/net/ark/ark_ethdev.c | 4 +- drivers/net/ark/ark_pktgen.c | 4 +- drivers/net/atlantic/atl_ethdev.c | 4 +- drivers/net/atlantic/atl_types.h | 4 +- .../net/atlantic/hw_atl/hw_atl_utils_fw2x.c | 26 +-- drivers/net/axgbe/axgbe_common.h | 2 +- drivers/net/axgbe/axgbe_dev.c | 8 +- drivers/net/axgbe/axgbe_ethdev.c | 8 +- drivers/net/axgbe/axgbe_ethdev.h | 8 +- drivers/net/axgbe/axgbe_i2c.c | 4 +- drivers/net/axgbe/axgbe_mdio.c| 8 +- drivers/net/axgbe/axgbe_phy_impl.c| 6 +- drivers/net/bnxt/bnxt.h | 16 +- drivers/net/bnxt/bnxt_cpr.c | 4 +- drivers/net/bnxt/bnxt_ethdev.c| 54 ++--- drivers/net/bnxt/bnxt_irq.c | 8 +- drivers/net/bnxt/bnxt_reps.c | 10 +- drivers/net/bnxt/tf_ulp/bnxt_ulp.c| 34 ++-- drivers/net/bnxt/tf_ulp/bnxt_ulp.h| 4 +- drivers/net/bnxt/tf_ulp/ulp_fc_mgr.c | 28 +-- drivers/net/bnxt/tf_ulp/ulp_fc_mgr.h | 2 +- drivers/net/dpaa/dpaa_ethdev.c| 2 +- drivers/net/dpaa/dpaa_rxtx.c | 2 +- drivers/net/ena/base/ena_plat_dpdk.h | 15 +- drivers/net/enic/enic.h | 2 +- drivers/net/ice/ice_dcf_parent.c | 8 +- drivers/net/ixgbe/ixgbe_ethdev.c | 6 +- drivers/net/ixgbe/ixgbe_ethdev.h | 2 +- drivers/net/mlx5/linux/mlx5_os.c | 2 +- drivers/net/mlx5/mlx5.c | 20 +- drivers/net/mlx5/mlx5.h | 2 +- drivers/net/mlx5/mlx5_txpp.c | 8 +- drivers/net/mlx5/windows/mlx5_flow_os.c | 10 +- drivers/net/mlx5/windows/mlx5_os.c| 2 +- drivers/net/qede/base/bcm_osal.h | 8 +- drivers/net/vhost/rte_eth_vhost.c | 24 +-- .../net/virtio/virtio_user/virtio_user_dev.c | 30 +-- .../net/virtio/virtio_user/virtio_user_dev.h | 2 +- drivers/vdpa/ifc/ifcvf_vdpa.c | 49 +++-- drivers/vdpa/mlx5/mlx5_vdpa.c | 24 +-- drivers/vdpa/mlx5/mlx5_vdpa.h | 4 +- drivers/vdpa/mlx5/mlx5_vdpa_event.c | 51 ++--- examples/kni/main.c | 1 + .../pthread_shim/pthread_shim.h | 1 + lib/eal/common/eal_common_options.c | 6 +- lib/eal/common/eal_common_thread.c| 105 +- lib/eal/common/eal_common_trace.c | 1 + lib/eal/common/eal_private.h | 2 +- lib/eal/common/eal_thread.h | 6 + lib/eal/common/malloc_mp.c| 2 + lib/eal/common/rte_thread.c | 17 ++ lib/eal/freebsd/eal.c | 53 +++-- lib/eal/freebsd/eal_alarm.c | 12 +- lib/eal/freebsd/eal_interrupts.c | 6 +- lib/eal/freebsd/eal_thread.c | 10 +- lib/eal/include/rte_lcore.h | 6 + lib/eal/include/rte_per_lcore.h | 2 +- lib/eal/include/rte_thread.h | 45 lib/eal/linux/eal.c | 55 +++-- lib/eal/linux/eal_alarm.c | 10 +- lib/eal/linux/eal_interrupts.c| 8 +- lib/eal/linux/eal
[dpdk-dev] [PATCH 1/6] eal: add function that sets thread name
From: Narcisa Vasile Implement function that sets the name of a thread. On Windows, SetThreadDescription() is used. Use GetProcAddress() to obtain the address of the function for MinGW compatibility. Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 17 ++ lib/eal/include/rte_thread.h | 18 +++ lib/eal/version.map | 1 + lib/eal/windows/rte_thread.c | 60 4 files changed, 96 insertions(+) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index 3fdb267337..fbff1168e5 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -373,6 +373,23 @@ rte_thread_barrier_destroy(rte_thread_barrier *barrier) return ret; } +int +rte_thread_name_set(rte_thread_t thread_id, const char *name) +{ + int ret = ENOSYS; +#if defined(__GLIBC__) && defined(__GLIBC_PREREQ) +#if __GLIBC_PREREQ(2, 12) + char truncated[RTE_THREAD_MAX_DESCRIPTION_LENGTH]; + + memcpy(truncated, name, sizeof(truncated)); + ret = pthread_setname_np((pthread_t)thread_id.opaque_id, truncated); +#endif +#endif + RTE_SET_USED(thread_id); + RTE_SET_USED(name); + return ret; +} + int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 40da83467b..c65cfd8c9e 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -24,6 +24,8 @@ extern "C" { #include +#define RTE_THREAD_MAX_DESCRIPTION_LENGTH 16 + /** * Thread id descriptor. */ @@ -439,6 +441,22 @@ int rte_thread_barrier_wait(rte_thread_barrier *barrier); __rte_experimental int rte_thread_barrier_destroy(rte_thread_barrier *barrier); +/** + * Set the name of the thread represented by 'thread_id'. + * + * @param thread_id + * The id of the thread. + * + * @param name + * Thread name to set. + * + * @return + * On success, return 0. + *On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_name_set(rte_thread_t thread_id, const char *name); + /** * Create a TLS data key visible to all threads in the process. * the created key is later used to get/set a value. diff --git a/lib/eal/version.map b/lib/eal/version.map index 6645f60a78..2a566c04af 100644 --- a/lib/eal/version.map +++ b/lib/eal/version.map @@ -443,6 +443,7 @@ EXPERIMENTAL { rte_thread_barrier_init; rte_thread_barrier_wait; rte_thread_barrier_destroy; + rte_thread_name_set; }; INTERNAL { diff --git a/lib/eal/windows/rte_thread.c b/lib/eal/windows/rte_thread.c index b2ff16f51f..180ac126af 100644 --- a/lib/eal/windows/rte_thread.c +++ b/lib/eal/windows/rte_thread.c @@ -556,6 +556,66 @@ rte_thread_barrier_destroy(rte_thread_barrier *barrier) return 0; } +typedef HRESULT +(*SetThreadDescription_type)(HANDLE thread_handle, PCWSTR thread_descirption); + +int +rte_thread_name_set(rte_thread_t thread_id, const char *name) +{ + int ret = 0; + size_t count; + HRESULT hr; + HANDLE thread_handle = NULL; + WCHAR w_name[RTE_THREAD_MAX_DESCRIPTION_LENGTH]; + HMODULE kernel_lib = NULL; + SetThreadDescription_type SetThreadDescription_ptr; + + static const char library_name[] = "kernel32.dll"; + static const char function[] = "SetThreadDescription"; + + kernel_lib = LoadLibraryA(library_name); + if (kernel_lib == NULL) { + ret = thread_log_last_error("LoadLibraryA(\"kernel32.dll\")"); + goto cleanup; + } + + SetThreadDescription_ptr = (SetThreadDescription_type)( + (void *)GetProcAddress(kernel_lib, function)); + if (SetThreadDescription_ptr == NULL) { + ret = thread_log_last_error("GetProcAddress(\"kernel32.dll\", \"SetThreadDescription\")"); + goto cleanup; + } + + thread_handle = OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, + thread_id.opaque_id); + if (thread_handle == NULL) { + ret = thread_log_last_error("OpenThread()"); + goto cleanup; + } + + count = mbstowcs(w_name, name, RTE_THREAD_MAX_DESCRIPTION_LENGTH); + if (count < 0) { + RTE_LOG(DEBUG, EAL, "Invalid thread name!\n"); + ret = EINVAL; + goto cleanup; + } + + hr = SetThreadDescription_ptr(thread_handle, w_name); + if (FAILED(hr)) { + ret = thread_log_last_error("SetThreadDescription()"); + goto cleanup; + } + +cleanup: + if (kernel_lib != NULL) + FreeLibrary(kernel_lib); + if (thread_handle != NULL) { + CloseHandle(thread_handle); + thread_handle = NULL; + } + return ret; +} + int rte_thread_key_create(rte_thread_key *key, __rte_unused void (*destructor)(void *)) -- 2.
[dpdk-dev] [PATCH 2/6] eal: add function for control thread creation
From: Narcisa Vasile The existing rte_ctrl_thread_create() function will be replaced with rte_thread_ctrl_thread_create() that uses the internal EAL thread API. This patch only introduces the new control thread creation function. Replacing of the old function needs to be done according to the ABI change procedures, to avoid an ABI break. Signed-off-by: Narcisa Vasile --- lib/eal/common/eal_common_thread.c | 81 ++ lib/eal/include/rte_thread.h | 27 ++ lib/eal/version.map| 1 + 3 files changed, 109 insertions(+) diff --git a/lib/eal/common/eal_common_thread.c b/lib/eal/common/eal_common_thread.c index 1a52f42a2b..79545c67d9 100644 --- a/lib/eal/common/eal_common_thread.c +++ b/lib/eal/common/eal_common_thread.c @@ -259,6 +259,87 @@ rte_ctrl_thread_create(pthread_t *thread, const char *name, return -ret; } +struct rte_thread_ctrl_ctx { + rte_thread_func start_routine; + void *arg; + const char *name; +}; + +static void *ctrl_thread_wrapper(void *arg) +{ + struct internal_config *conf = eal_get_internal_configuration(); + rte_cpuset_t *cpuset = &conf->ctrl_cpuset; + struct rte_thread_ctrl_ctx *ctx = arg; + rte_thread_func start_routine = ctx->start_routine; + void *routine_arg = ctx->arg; + + __rte_thread_init(rte_lcore_id(), cpuset); + + if (ctx->name != NULL) { + if (rte_thread_name_set(rte_thread_self(), ctx->name) < 0) + RTE_LOG(DEBUG, EAL, "Cannot set name for ctrl thread\n"); + } + + free(arg); + + return start_routine(routine_arg); +} + +int +rte_thread_ctrl_thread_create(rte_thread_t *thread, const char *name, + rte_thread_func start_routine, void *arg) +{ + int ret; + rte_thread_attr_t attr; + struct internal_config *conf = eal_get_internal_configuration(); + rte_cpuset_t *cpuset = &conf->ctrl_cpuset; + struct rte_thread_ctrl_ctx *ctx = NULL; + + if (start_routine == NULL) { + ret = EINVAL; + goto cleanup; + } + + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) { + ret = ENOMEM; + goto cleanup; + } + + ctx->start_routine = start_routine; + ctx->arg = arg; + ctx->name = name; + + ret = rte_thread_attr_init(&attr); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "Cannot init ctrl thread attributes\n"); + goto cleanup; + } + + ret = rte_thread_attr_set_affinity(&attr, cpuset); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "Cannot set afifnity attribute for ctrl thread\n"); + goto cleanup; + } + ret = rte_thread_attr_set_priority(&attr, RTE_THREAD_PRIORITY_NORMAL); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "Cannot set priority attribute for ctrl thread\n"); + goto cleanup; + } + + ret = rte_thread_create(thread, &attr, ctrl_thread_wrapper, ctx); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "Cannot create ctrl thread\n"); + goto cleanup; + } + + return 0; + +cleanup: + free(ctx); + return ret; +} + int rte_thread_register(void) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index c65cfd8c9e..4da800ae27 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -457,6 +457,33 @@ int rte_thread_barrier_destroy(rte_thread_barrier *barrier); __rte_experimental int rte_thread_name_set(rte_thread_t thread_id, const char *name); +/** + * Create a control thread. + * + * Set affinity and thread name. The affinity of the new thread is based + * on the CPU affinity retrieved at the time rte_eal_init() was called, + * the dataplane and service lcores are then excluded. + * + * @param thread + * Filled with the thread id of the new created thread. + * + * @param name + * The name of the control thread (max 16 characters including '\0'). + * + * @param start_routine + * Function to be executed by the new thread. + * + * @param arg + * Argument passed to start_routine. + * + * @return + * On success, return 0; + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_ctrl_thread_create(rte_thread_t *thread, const char *name, + rte_thread_func start_routine, void *arg); + /** * Create a TLS data key visible to all threads in the process. * the created key is later used to get/set a value. diff --git a/lib/eal/version.map b/lib/eal/version.map index 2a566c04af..02455a1c8d 100644 --- a/lib/eal/version.map +++ b/lib/eal/version.map @@ -444,6 +444,7 @@ EXPERIMENTAL { rte_thread_barrier_wait; rte_thread_barrier_destroy; rte_thread_name_set; + rte_thread_ctrl_thread_create; }; INTERNAL { -- 2.31.0.vfs.0.1
[dpdk-dev] [PATCH 4/6] lib: enable the new EAL thread API
From: Narcisa Vasile Rename pthread* with the new rte_thread* API. Signed-off-by: Narcisa Vasile --- lib/eal/common/eal_common_options.c | 6 ++--- lib/eal/common/eal_common_thread.c | 13 + lib/eal/common/eal_common_trace.c | 1 + lib/eal/common/eal_private.h| 2 +- lib/eal/common/malloc_mp.c | 2 ++ lib/eal/freebsd/eal.c | 18 ++--- lib/eal/freebsd/eal_alarm.c | 12 - lib/eal/freebsd/eal_interrupts.c| 6 ++--- lib/eal/freebsd/eal_thread.c| 10 --- lib/eal/include/rte_lcore.h | 6 + lib/eal/include/rte_per_lcore.h | 2 +- lib/eal/linux/eal.c | 22 lib/eal/linux/eal_alarm.c | 10 --- lib/eal/linux/eal_interrupts.c | 8 +++--- lib/eal/linux/eal_thread.c | 11 +--- lib/eal/linux/eal_timer.c | 6 ++--- lib/eal/version.map | 4 +-- lib/eal/windows/eal.c | 4 ++- lib/eal/windows/eal_interrupts.c| 10 +++ lib/eal/windows/eal_thread.c| 35 - lib/eal/windows/eal_windows.h | 10 --- lib/eal/windows/include/rte_windows.h | 1 + lib/eal/windows/rte_thread.c| 2 +- lib/ethdev/rte_ethdev.c | 4 +-- lib/ethdev/rte_ethdev_core.h| 4 +-- lib/ethdev/rte_flow.c | 4 +-- lib/eventdev/rte_event_eth_rx_adapter.c | 1 + lib/vhost/vhost.c | 1 + 28 files changed, 101 insertions(+), 114 deletions(-) diff --git a/lib/eal/common/eal_common_options.c b/lib/eal/common/eal_common_options.c index 9d29696b84..1f6aba498c 100644 --- a/lib/eal/common/eal_common_options.c +++ b/lib/eal/common/eal_common_options.c @@ -1872,8 +1872,7 @@ eal_auto_detect_cores(struct rte_config *cfg) unsigned int removed = 0; rte_cpuset_t affinity_set; - if (pthread_getaffinity_np(pthread_self(), sizeof(rte_cpuset_t), - &affinity_set)) + if (rte_thread_get_affinity_by_id(rte_thread_self(), &affinity_set)) CPU_ZERO(&affinity_set); for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { @@ -1901,8 +1900,7 @@ compute_ctrl_threads_cpuset(struct internal_config *internal_cfg) } RTE_CPU_NOT(cpuset, cpuset); - if (pthread_getaffinity_np(pthread_self(), sizeof(rte_cpuset_t), - &default_set)) + if (rte_thread_get_affinity_by_id(rte_thread_self(), &default_set)) CPU_ZERO(&default_set); RTE_CPU_AND(cpuset, cpuset, &default_set); diff --git a/lib/eal/common/eal_common_thread.c b/lib/eal/common/eal_common_thread.c index 79545c67d9..62d28e7b28 100644 --- a/lib/eal/common/eal_common_thread.c +++ b/lib/eal/common/eal_common_thread.c @@ -6,7 +6,10 @@ #include #include #include +#ifndef RTE_EXEC_ENV_WINDOWS #include +#endif /* RTE_EXEC_ENV_WINDOWS */ +#include #include #include #include @@ -86,9 +89,8 @@ thread_update_affinity(rte_cpuset_t *cpusetp) int rte_thread_set_affinity(rte_cpuset_t *cpusetp) { - if (pthread_setaffinity_np(pthread_self(), sizeof(rte_cpuset_t), - cpusetp) != 0) { - RTE_LOG(ERR, EAL, "pthread_setaffinity_np failed\n"); + if (rte_thread_set_affinity_by_id(rte_thread_self(), cpusetp) != 0) { + RTE_LOG(ERR, EAL, "rte_thread_set_affinity_by_id failed\n"); return -1; } @@ -166,6 +168,7 @@ __rte_thread_uninit(void) RTE_PER_LCORE(_lcore_id) = LCORE_ID_ANY; } +#ifndef RTE_EXEC_ENV_WINDOWS struct rte_thread_ctrl_params { void *(*start_routine)(void *); void *arg; @@ -258,6 +261,7 @@ rte_ctrl_thread_create(pthread_t *thread, const char *name, return -ret; } +#endif struct rte_thread_ctrl_ctx { rte_thread_func start_routine; @@ -357,8 +361,7 @@ rte_thread_register(void) rte_errno = EINVAL; return -1; } - if (pthread_getaffinity_np(pthread_self(), sizeof(cpuset), - &cpuset) != 0) + if (rte_thread_get_affinity_by_id(rte_thread_self(), &cpuset) != 0) CPU_ZERO(&cpuset); lcore_id = eal_lcore_non_eal_allocate(); if (lcore_id >= RTE_MAX_LCORE) diff --git a/lib/eal/common/eal_common_trace.c b/lib/eal/common/eal_common_trace.c index 24e27387b1..ddbc692cf2 100644 --- a/lib/eal/common/eal_common_trace.c +++ b/lib/eal/common/eal_common_trace.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include diff --git a/lib/eal/common/eal_private.h b/lib/eal/common/eal_private.h index 64cf4e81c8..4b95001d7d 100644 --- a/lib/eal/common/eal_private.h +++ b/lib/eal/common/eal_private.h @@ -19,7 +19,7 @@ * Structure storing internal configuration (per-lcore) */ struct lcore_config { - pt
[dpdk-dev] [PATCH 5/6] eal: set affinity and priority attributes
From: Narcisa Vasile If the user doesn't specify the priority through the command line arguments, initialize the thread priority to 'normal'. Set thread priority for eal threads. Signed-off-by: Narcisa Vasile --- lib/eal/common/eal_common_thread.c | 11 lib/eal/common/eal_thread.h| 6 lib/eal/freebsd/eal.c | 41 +++- lib/eal/linux/eal.c| 39 +- lib/eal/windows/eal.c | 44 -- 5 files changed, 125 insertions(+), 16 deletions(-) diff --git a/lib/eal/common/eal_common_thread.c b/lib/eal/common/eal_common_thread.c index 62d28e7b28..fa537db7ac 100644 --- a/lib/eal/common/eal_common_thread.c +++ b/lib/eal/common/eal_common_thread.c @@ -388,3 +388,14 @@ rte_thread_unregister(void) RTE_LOG(DEBUG, EAL, "Unregistered non-EAL thread (was lcore %u).\n", lcore_id); } + +void rte_thread_priority_init(void) +{ + struct internal_config *internal_conf = + eal_get_internal_configuration(); + + /* If the user doesn't specify the priority through the command +* line arguments, the default 'normal' value will be used. +*/ + internal_conf->thread_priority = RTE_THREAD_PRIORITY_NORMAL; +} diff --git a/lib/eal/common/eal_thread.h b/lib/eal/common/eal_thread.h index 4a49117be8..7b3b884463 100644 --- a/lib/eal/common/eal_thread.h +++ b/lib/eal/common/eal_thread.h @@ -58,4 +58,10 @@ eal_thread_dump_affinity(rte_cpuset_t *cpuset, char *str, unsigned int size); int eal_thread_dump_current_affinity(char *str, unsigned int size); +/** + * Set the initial thread priority in the internal configuration + * to the default value of RTE_THREAD_PRIORITY_NORMAL. + */ +void rte_thread_priority_init(void); + #endif /* EAL_THREAD_H */ diff --git a/lib/eal/freebsd/eal.c b/lib/eal/freebsd/eal.c index b5cf050697..7303d62afb 100644 --- a/lib/eal/freebsd/eal.c +++ b/lib/eal/freebsd/eal.c @@ -676,6 +676,8 @@ rte_eal_init(int argc, char **argv) struct internal_config *internal_conf = eal_get_internal_configuration(); + rte_thread_priority_init(); + /* checks if the machine is adequate */ if (!rte_cpu_is_supported()) { rte_eal_init_alert("unsupported cpu type."); @@ -854,6 +856,14 @@ rte_eal_init(int argc, char **argv) eal_check_mem_on_local_socket(); + ret = rte_thread_set_priority(rte_thread_self(), + internal_conf->thread_priority); + if (ret != 0) { + rte_eal_init_alert("Cannot set thread priority"); + rte_errno = ret; + return -1; + } + if (rte_thread_set_affinity_by_id(rte_thread_self(), &lcore_config[config->main_lcore].cpuset) != 0) { rte_eal_init_alert("Cannot set affinity"); @@ -869,6 +879,22 @@ rte_eal_init(int argc, char **argv) config->main_lcore, (void *)thread_id.opaque_id, cpuset, ret == 0 ? "" : "..."); + rte_thread_attr_t thread_attr; + ret = rte_thread_attr_init(&thread_attr); + if (ret != 0) { + rte_eal_init_alert("Cannot initialize thread attributes"); + rte_errno = ret; + return -1; + } + + ret = rte_thread_attr_set_priority(&thread_attr, + internal_conf->thread_priority); + if (ret != 0) { + rte_eal_init_alert("Cannot set thread priority attribute"); + rte_errno = ret; + return -1; + } + RTE_LCORE_FOREACH_WORKER(i) { /* @@ -882,9 +908,15 @@ rte_eal_init(int argc, char **argv) lcore_config[i].state = WAIT; + ret = rte_thread_attr_set_affinity(&thread_attr, +&lcore_config[i].cpuset); + + if (ret != 0) + rte_panic("Cannot set affinity\n"); + /* create a thread for each lcore */ - ret = rte_thread_create(&lcore_config[i].thread_id, NULL, -eal_thread_loop, NULL); + ret = rte_thread_create(&lcore_config[i].thread_id, + &thread_attr, eal_thread_loop, NULL); if (ret != 0) rte_panic("Cannot create thread\n"); @@ -892,11 +924,6 @@ rte_eal_init(int argc, char **argv) snprintf(thread_name, sizeof(thread_name), "lcore-worker-%d", i); rte_thread_name_set(lcore_config[i].thread_id, thread_name); - - ret = rte_thread_set_affinity_by_id(lcore_config[i].thread_id, - &lcore_config[i].cpuset); - if (ret != 0) - rte_panic("Cannot set affinity\n"); } /* diff --git a/lib/eal/linux/eal.c b/li
[dpdk-dev] [PATCH 3/6] Enable the new EAL thread API in app, drivers and examples
From: Narcisa Vasile Rename pthread* with the new rte_thread* API. Signed-off-by: Narcisa Vasile --- app/test/process.h| 8 +-- app/test/test_lcores.c| 18 +++ app/test/test_link_bonding.c | 14 ++--- app/test/test_lpm_perf.c | 12 ++--- drivers/bus/dpaa/base/qbman/bman_driver.c | 5 +- drivers/bus/dpaa/base/qbman/dpaa_sys.c| 14 ++--- drivers/bus/dpaa/base/qbman/process.c | 6 +-- drivers/bus/dpaa/dpaa_bus.c | 14 ++--- drivers/bus/fslmc/portal/dpaa2_hw_dpio.c | 19 --- drivers/common/dpaax/compat.h | 2 +- drivers/common/mlx5/windows/mlx5_common_os.h | 1 + drivers/compress/mlx5/mlx5_compress.c | 10 ++-- drivers/event/dlb2/dlb2.c | 2 +- drivers/event/dlb2/pf/base/dlb2_osdep.h | 7 ++- drivers/mempool/dpaa/dpaa_mempool.c | 2 +- drivers/net/af_xdp/rte_eth_af_xdp.c | 18 +++ drivers/net/ark/ark_ethdev.c | 4 +- drivers/net/ark/ark_pktgen.c | 4 +- drivers/net/atlantic/atl_ethdev.c | 4 +- drivers/net/atlantic/atl_types.h | 4 +- .../net/atlantic/hw_atl/hw_atl_utils_fw2x.c | 26 - drivers/net/axgbe/axgbe_common.h | 2 +- drivers/net/axgbe/axgbe_dev.c | 8 +-- drivers/net/axgbe/axgbe_ethdev.c | 8 +-- drivers/net/axgbe/axgbe_ethdev.h | 8 +-- drivers/net/axgbe/axgbe_i2c.c | 4 +- drivers/net/axgbe/axgbe_mdio.c| 8 +-- drivers/net/axgbe/axgbe_phy_impl.c| 6 +-- drivers/net/bnxt/bnxt.h | 16 +++--- drivers/net/bnxt/bnxt_cpr.c | 4 +- drivers/net/bnxt/bnxt_ethdev.c| 54 +-- drivers/net/bnxt/bnxt_irq.c | 8 +-- drivers/net/bnxt/bnxt_reps.c | 10 ++-- drivers/net/bnxt/tf_ulp/bnxt_ulp.c| 34 ++-- drivers/net/bnxt/tf_ulp/bnxt_ulp.h| 4 +- drivers/net/bnxt/tf_ulp/ulp_fc_mgr.c | 28 +- drivers/net/bnxt/tf_ulp/ulp_fc_mgr.h | 2 +- drivers/net/dpaa/dpaa_ethdev.c| 2 +- drivers/net/dpaa/dpaa_rxtx.c | 2 +- drivers/net/ena/base/ena_plat_dpdk.h | 15 +++--- drivers/net/enic/enic.h | 2 +- drivers/net/ice/ice_dcf_parent.c | 8 +-- drivers/net/ixgbe/ixgbe_ethdev.c | 6 +-- drivers/net/ixgbe/ixgbe_ethdev.h | 2 +- drivers/net/mlx5/linux/mlx5_os.c | 2 +- drivers/net/mlx5/mlx5.c | 20 +++ drivers/net/mlx5/mlx5.h | 2 +- drivers/net/mlx5/mlx5_txpp.c | 8 +-- drivers/net/mlx5/windows/mlx5_flow_os.c | 10 ++-- drivers/net/mlx5/windows/mlx5_os.c| 2 +- drivers/net/qede/base/bcm_osal.h | 8 +-- drivers/net/vhost/rte_eth_vhost.c | 24 - .../net/virtio/virtio_user/virtio_user_dev.c | 30 +-- .../net/virtio/virtio_user/virtio_user_dev.h | 2 +- drivers/vdpa/ifc/ifcvf_vdpa.c | 49 - drivers/vdpa/mlx5/mlx5_vdpa.c | 24 - drivers/vdpa/mlx5/mlx5_vdpa.h | 4 +- drivers/vdpa/mlx5/mlx5_vdpa_event.c | 51 -- examples/kni/main.c | 1 + .../pthread_shim/pthread_shim.h | 1 + 60 files changed, 334 insertions(+), 339 deletions(-) diff --git a/app/test/process.h b/app/test/process.h index a09a088477..9e4be17bad 100644 --- a/app/test/process.h +++ b/app/test/process.h @@ -26,7 +26,7 @@ #ifdef RTE_LIB_PDUMP #ifdef RTE_NET_RING -#include +#include extern void *send_pkts(void *empty); extern uint16_t flag_for_send_pkts; #endif @@ -47,7 +47,7 @@ process_dup(const char *const argv[], int numargs, const char *env_value) char path[32]; #ifdef RTE_LIB_PDUMP #ifdef RTE_NET_RING - pthread_t thread; + rte_thread_t thread; int rc; #endif #endif @@ -128,7 +128,7 @@ process_dup(const char *const argv[], int numargs, const char *env_value) #ifdef RTE_LIB_PDUMP #ifdef RTE_NET_RING if ((strcmp(env_value, "run_pdump_server_tests") == 0)) { - rc = pthread_create(&thread, NULL, &send_pkts, NULL); + rc = rte_thread_create(&thread, NULL, &send_pkts, NULL); if (rc != 0) { rte_panic("Cannot start send pkts thread: %s\n", strerror(rc)); @@ -143,7 +143,7 @@ process_dup(const char *const argv[], int numargs, const char *env_value) #ifdef RTE_NET_RING if ((strcmp(env_value, "run_pdump_server_tests") == 0)) { flag_for_send_pkts = 0; - pthread_join(thread, NULL); + rte_thread_join(thread, NULL); } #endif #
[dpdk-dev] [PATCH 6/6] Allow choice between internal EAL thread API and external lib
From: Narcisa Vasile The user is offered the option of either using the RTE_THREAD_* API or a 3rd party thread library, through a meson flag called "use_external_thread_lib". By default, this flag is set to FALSE, which means Windows libraries and applications will use the RTE_THREAD_* API for managing threads. If compiling on Windows and the "use_external_thread_lib" is *not* set, the following files will be parsed: * include/rte_thread.h * windows/rte_thread.c In all other cases, the compilation/parsing includes the following files: * include/rte_thread.h * common/rte_thread.c Signed-off-by: Narcisa Vasile --- config/meson.build| 1 - lib/eal/windows/include/pthread.h | 192 -- lib/eal/windows/meson.build | 7 +- meson_options.txt | 2 + 4 files changed, 8 insertions(+), 194 deletions(-) delete mode 100644 lib/eal/windows/include/pthread.h diff --git a/config/meson.build b/config/meson.build index 017bb2efbb..9309010f21 100644 --- a/config/meson.build +++ b/config/meson.build @@ -262,7 +262,6 @@ else # for 32-bit we need smaller reserved memory areas dpdk_conf.set('RTE_MAX_MEM_MB', 2048) endif - compile_time_cpuflags = [] subdir(arch_subdir) dpdk_conf.set('RTE_COMPILE_TIME_CPUFLAGS', ','.join(compile_time_cpuflags)) diff --git a/lib/eal/windows/include/pthread.h b/lib/eal/windows/include/pthread.h deleted file mode 100644 index 27fd2cca52..00 --- a/lib/eal/windows/include/pthread.h +++ /dev/null @@ -1,192 +0,0 @@ -/* SPDX-License-Identifier: BSD-3-Clause - * Copyright(c) 2019 Intel Corporation - */ - -#ifndef _PTHREAD_H_ -#define _PTHREAD_H_ - -#include -#include - -/** - * This file is required to support the common code in eal_common_proc.c, - * eal_common_thread.c and common\include\rte_per_lcore.h as Microsoft libc - * does not contain pthread.h. This may be removed in future releases. - */ -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -#define PTHREAD_BARRIER_SERIAL_THREAD TRUE - -/* defining pthread_t type on Windows since there is no in Microsoft libc*/ -typedef uintptr_t pthread_t; - -/* defining pthread_attr_t type on Windows since there is no in Microsoft libc*/ -typedef void *pthread_attr_t; - -typedef void *pthread_mutexattr_t; - -typedef CRITICAL_SECTION pthread_mutex_t; - -typedef SYNCHRONIZATION_BARRIER pthread_barrier_t; - -#define pthread_barrier_init(barrier, attr, count) \ - !InitializeSynchronizationBarrier(barrier, count, -1) -#define pthread_barrier_wait(barrier) EnterSynchronizationBarrier(barrier, \ - SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY) -#define pthread_barrier_destroy(barrier) \ - !DeleteSynchronizationBarrier(barrier) -#define pthread_cancel(thread) !TerminateThread((HANDLE) thread, 0) - -/* pthread function overrides */ -#define pthread_self() \ - ((pthread_t)GetCurrentThreadId()) - - -static inline int -pthread_equal(pthread_t t1, pthread_t t2) -{ - return t1 == t2; -} - -static inline int -pthread_setaffinity_np(pthread_t threadid, size_t cpuset_size, - rte_cpuset_t *cpuset) -{ - DWORD_PTR ret = 0; - HANDLE thread_handle; - - if (cpuset == NULL || cpuset_size == 0) - return -1; - - thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, threadid); - if (thread_handle == NULL) { - RTE_LOG_WIN32_ERR("OpenThread()"); - return -1; - } - - ret = SetThreadAffinityMask(thread_handle, *cpuset->_bits); - if (ret == 0) { - RTE_LOG_WIN32_ERR("SetThreadAffinityMask()"); - goto close_handle; - } - -close_handle: - if (CloseHandle(thread_handle) == 0) { - RTE_LOG_WIN32_ERR("CloseHandle()"); - return -1; - } - return (ret == 0) ? -1 : 0; -} - -static inline int -pthread_getaffinity_np(pthread_t threadid, size_t cpuset_size, - rte_cpuset_t *cpuset) -{ - /* Workaround for the lack of a GetThreadAffinityMask() -*API in Windows -*/ - DWORD_PTR prev_affinity_mask; - HANDLE thread_handle; - DWORD_PTR ret = 0; - - if (cpuset == NULL || cpuset_size == 0) - return -1; - - thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, threadid); - if (thread_handle == NULL) { - RTE_LOG_WIN32_ERR("OpenThread()"); - return -1; - } - - /* obtain previous mask by setting dummy mask */ - prev_affinity_mask = SetThreadAffinityMask(thread_handle, 0x1); - if (prev_affinity_mask == 0) { - RTE_LOG_WIN32_ERR("SetThreadAffinityMask()"); - goto close_handle; - } - - /* set it back! */ - ret = SetThreadAffinityMask(thread_handle, prev_affinity_mask); - if (ret == 0) { - RTE_LOG_WIN32_ERR("SetThreadAffinityMask()"); - goto close_handle; - } - - mem
[dpdk-dev] [PATCH v2 2/6] eal: add function for control thread creation
From: Narcisa Vasile The existing rte_ctrl_thread_create() function will be replaced with rte_thread_ctrl_thread_create() that uses the internal EAL thread API. This patch only introduces the new control thread creation function. Replacing of the old function needs to be done according to the ABI change procedures, to avoid an ABI break. Depends-on: series-17402 ("eal: Add EAL API for threading") Signed-off-by: Narcisa Vasile --- lib/eal/common/eal_common_thread.c | 81 ++ lib/eal/include/rte_thread.h | 27 ++ lib/eal/version.map| 1 + 3 files changed, 109 insertions(+) diff --git a/lib/eal/common/eal_common_thread.c b/lib/eal/common/eal_common_thread.c index 1a52f42a2b..79545c67d9 100644 --- a/lib/eal/common/eal_common_thread.c +++ b/lib/eal/common/eal_common_thread.c @@ -259,6 +259,87 @@ rte_ctrl_thread_create(pthread_t *thread, const char *name, return -ret; } +struct rte_thread_ctrl_ctx { + rte_thread_func start_routine; + void *arg; + const char *name; +}; + +static void *ctrl_thread_wrapper(void *arg) +{ + struct internal_config *conf = eal_get_internal_configuration(); + rte_cpuset_t *cpuset = &conf->ctrl_cpuset; + struct rte_thread_ctrl_ctx *ctx = arg; + rte_thread_func start_routine = ctx->start_routine; + void *routine_arg = ctx->arg; + + __rte_thread_init(rte_lcore_id(), cpuset); + + if (ctx->name != NULL) { + if (rte_thread_name_set(rte_thread_self(), ctx->name) < 0) + RTE_LOG(DEBUG, EAL, "Cannot set name for ctrl thread\n"); + } + + free(arg); + + return start_routine(routine_arg); +} + +int +rte_thread_ctrl_thread_create(rte_thread_t *thread, const char *name, + rte_thread_func start_routine, void *arg) +{ + int ret; + rte_thread_attr_t attr; + struct internal_config *conf = eal_get_internal_configuration(); + rte_cpuset_t *cpuset = &conf->ctrl_cpuset; + struct rte_thread_ctrl_ctx *ctx = NULL; + + if (start_routine == NULL) { + ret = EINVAL; + goto cleanup; + } + + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) { + ret = ENOMEM; + goto cleanup; + } + + ctx->start_routine = start_routine; + ctx->arg = arg; + ctx->name = name; + + ret = rte_thread_attr_init(&attr); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "Cannot init ctrl thread attributes\n"); + goto cleanup; + } + + ret = rte_thread_attr_set_affinity(&attr, cpuset); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "Cannot set afifnity attribute for ctrl thread\n"); + goto cleanup; + } + ret = rte_thread_attr_set_priority(&attr, RTE_THREAD_PRIORITY_NORMAL); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "Cannot set priority attribute for ctrl thread\n"); + goto cleanup; + } + + ret = rte_thread_create(thread, &attr, ctrl_thread_wrapper, ctx); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "Cannot create ctrl thread\n"); + goto cleanup; + } + + return 0; + +cleanup: + free(ctx); + return ret; +} + int rte_thread_register(void) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index c65cfd8c9e..4da800ae27 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -457,6 +457,33 @@ int rte_thread_barrier_destroy(rte_thread_barrier *barrier); __rte_experimental int rte_thread_name_set(rte_thread_t thread_id, const char *name); +/** + * Create a control thread. + * + * Set affinity and thread name. The affinity of the new thread is based + * on the CPU affinity retrieved at the time rte_eal_init() was called, + * the dataplane and service lcores are then excluded. + * + * @param thread + * Filled with the thread id of the new created thread. + * + * @param name + * The name of the control thread (max 16 characters including '\0'). + * + * @param start_routine + * Function to be executed by the new thread. + * + * @param arg + * Argument passed to start_routine. + * + * @return + * On success, return 0; + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_ctrl_thread_create(rte_thread_t *thread, const char *name, + rte_thread_func start_routine, void *arg); + /** * Create a TLS data key visible to all threads in the process. * the created key is later used to get/set a value. diff --git a/lib/eal/version.map b/lib/eal/version.map index 2a566c04af..02455a1c8d 100644 --- a/lib/eal/version.map +++ b/lib/eal/version.map @@ -444,6 +444,7 @@ EXPERIMENTAL { rte_thread_barrier_wait; rte_thread_barrier_destroy; rte_thread_name_set; + rte_thread_ctrl_thread_create; }; INTERNAL { --
[dpdk-dev] [PATCH v2 1/6] eal: add function that sets thread name
From: Narcisa Vasile Implement function that sets the name of a thread. On Windows, SetThreadDescription() is used. Use GetProcAddress() to obtain the address of the function for MinGW compatibility. Depends-on: series-17402 ("eal: Add EAL API for threading") Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 17 ++ lib/eal/include/rte_thread.h | 18 +++ lib/eal/version.map | 1 + lib/eal/windows/rte_thread.c | 60 4 files changed, 96 insertions(+) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index 3fdb267337..fbff1168e5 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -373,6 +373,23 @@ rte_thread_barrier_destroy(rte_thread_barrier *barrier) return ret; } +int +rte_thread_name_set(rte_thread_t thread_id, const char *name) +{ + int ret = ENOSYS; +#if defined(__GLIBC__) && defined(__GLIBC_PREREQ) +#if __GLIBC_PREREQ(2, 12) + char truncated[RTE_THREAD_MAX_DESCRIPTION_LENGTH]; + + memcpy(truncated, name, sizeof(truncated)); + ret = pthread_setname_np((pthread_t)thread_id.opaque_id, truncated); +#endif +#endif + RTE_SET_USED(thread_id); + RTE_SET_USED(name); + return ret; +} + int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 40da83467b..c65cfd8c9e 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -24,6 +24,8 @@ extern "C" { #include +#define RTE_THREAD_MAX_DESCRIPTION_LENGTH 16 + /** * Thread id descriptor. */ @@ -439,6 +441,22 @@ int rte_thread_barrier_wait(rte_thread_barrier *barrier); __rte_experimental int rte_thread_barrier_destroy(rte_thread_barrier *barrier); +/** + * Set the name of the thread represented by 'thread_id'. + * + * @param thread_id + * The id of the thread. + * + * @param name + * Thread name to set. + * + * @return + * On success, return 0. + *On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_name_set(rte_thread_t thread_id, const char *name); + /** * Create a TLS data key visible to all threads in the process. * the created key is later used to get/set a value. diff --git a/lib/eal/version.map b/lib/eal/version.map index 6645f60a78..2a566c04af 100644 --- a/lib/eal/version.map +++ b/lib/eal/version.map @@ -443,6 +443,7 @@ EXPERIMENTAL { rte_thread_barrier_init; rte_thread_barrier_wait; rte_thread_barrier_destroy; + rte_thread_name_set; }; INTERNAL { diff --git a/lib/eal/windows/rte_thread.c b/lib/eal/windows/rte_thread.c index b2ff16f51f..995ae2491d 100644 --- a/lib/eal/windows/rte_thread.c +++ b/lib/eal/windows/rte_thread.c @@ -556,6 +556,66 @@ rte_thread_barrier_destroy(rte_thread_barrier *barrier) return 0; } +typedef HRESULT +(*SetThreadDescription_type)(HANDLE thread_handle, PCWSTR thread_description); + +int +rte_thread_name_set(rte_thread_t thread_id, const char *name) +{ + int ret = 0; + size_t count; + HRESULT hr; + HANDLE thread_handle = NULL; + WCHAR w_name[RTE_THREAD_MAX_DESCRIPTION_LENGTH]; + HMODULE kernel_lib = NULL; + SetThreadDescription_type SetThreadDescription_ptr; + + static const char library_name[] = "kernel32.dll"; + static const char function[] = "SetThreadDescription"; + + kernel_lib = LoadLibraryA(library_name); + if (kernel_lib == NULL) { + ret = thread_log_last_error("LoadLibraryA(\"kernel32.dll\")"); + goto cleanup; + } + + SetThreadDescription_ptr = (SetThreadDescription_type)( + (void *)GetProcAddress(kernel_lib, function)); + if (SetThreadDescription_ptr == NULL) { + ret = thread_log_last_error("GetProcAddress(\"kernel32.dll\", \"SetThreadDescription\")"); + goto cleanup; + } + + thread_handle = OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, + thread_id.opaque_id); + if (thread_handle == NULL) { + ret = thread_log_last_error("OpenThread()"); + goto cleanup; + } + + count = mbstowcs(w_name, name, RTE_THREAD_MAX_DESCRIPTION_LENGTH); + if (count < 0) { + RTE_LOG(DEBUG, EAL, "Invalid thread name!\n"); + ret = EINVAL; + goto cleanup; + } + + hr = SetThreadDescription_ptr(thread_handle, w_name); + if (FAILED(hr)) { + ret = thread_log_last_error("SetThreadDescription()"); + goto cleanup; + } + +cleanup: + if (kernel_lib != NULL) + FreeLibrary(kernel_lib); + if (thread_handle != NULL) { + CloseHandle(thread_handle); + thread_handle = NULL; + } + return ret; +} + int rte_thread_key_create(rte_thread_key *key,
[dpdk-dev] [PATCH v2 0/6] Enable the internal EAL thread API
From: Narcisa Vasile This patchset enables the new EAL thread API. The newly defined thread attributes, priority and affinity, are used in eal/windows when creating the threads. Similarly, some changes have been done in eal/linux/eal.c and eal/freebsd/eal.c to initialize priority to a default value and set thread attributes. The user is offered the option of either using the rte_thread_* API or a 3rd party thread library, through a meson flag called "use_external_thread_lib". By default, this flag is set to FALSE, which means Windows libraries and applications will use the EAL rte_thread_* API defined in windows/rte_thread.c for managing threads. When the flag is set to TRUE, the common/rte_thread.c file is compiled and an external thread library is used. This patchset adds a new function for creating control threads that uses the new thread API. It enables the usage of the new function in Windows code and common code. The old function is kept to avoid ABI break, however, its definition is commented away on Windows, since the pthread_t and pthread_attr_t arguments that it receives have been replaced with the new API on Windows. This allows testing the "eal: Add EAL API for threading" that this patchset depends on. The ethdev lib also contains some changes that break the ABI. Enabling the new EAL thread API will probably require going through the proper process of ABI changes. Depends-on: series-17402 ("eal: Add EAL API for threading") v2: - fix typo in SetThreadDescription_type function pointer - add Depends-on on all patches to fix apply errors. - modify cover letter Narcisa Vasile (6): eal: add function that sets thread name eal: add function for control thread creation Enable the new EAL thread API in app, drivers and examples lib: enable the new EAL thread API eal: set affinity and priority attributes Allow choice between internal EAL thread API and external lib app/test/process.h| 8 +- app/test/test_lcores.c| 18 +- app/test/test_link_bonding.c | 14 +- app/test/test_lpm_perf.c | 12 +- config/meson.build| 1 - drivers/bus/dpaa/base/qbman/bman_driver.c | 5 +- drivers/bus/dpaa/base/qbman/dpaa_sys.c| 14 +- drivers/bus/dpaa/base/qbman/process.c | 6 +- drivers/bus/dpaa/dpaa_bus.c | 14 +- drivers/bus/fslmc/portal/dpaa2_hw_dpio.c | 19 +- drivers/common/dpaax/compat.h | 2 +- drivers/common/mlx5/windows/mlx5_common_os.h | 1 + drivers/compress/mlx5/mlx5_compress.c | 10 +- drivers/event/dlb2/dlb2.c | 2 +- drivers/event/dlb2/pf/base/dlb2_osdep.h | 7 +- drivers/mempool/dpaa/dpaa_mempool.c | 2 +- drivers/net/af_xdp/rte_eth_af_xdp.c | 18 +- drivers/net/ark/ark_ethdev.c | 4 +- drivers/net/ark/ark_pktgen.c | 4 +- drivers/net/atlantic/atl_ethdev.c | 4 +- drivers/net/atlantic/atl_types.h | 4 +- .../net/atlantic/hw_atl/hw_atl_utils_fw2x.c | 26 +-- drivers/net/axgbe/axgbe_common.h | 2 +- drivers/net/axgbe/axgbe_dev.c | 8 +- drivers/net/axgbe/axgbe_ethdev.c | 8 +- drivers/net/axgbe/axgbe_ethdev.h | 8 +- drivers/net/axgbe/axgbe_i2c.c | 4 +- drivers/net/axgbe/axgbe_mdio.c| 8 +- drivers/net/axgbe/axgbe_phy_impl.c| 6 +- drivers/net/bnxt/bnxt.h | 16 +- drivers/net/bnxt/bnxt_cpr.c | 4 +- drivers/net/bnxt/bnxt_ethdev.c| 54 ++--- drivers/net/bnxt/bnxt_irq.c | 8 +- drivers/net/bnxt/bnxt_reps.c | 10 +- drivers/net/bnxt/tf_ulp/bnxt_ulp.c| 34 ++-- drivers/net/bnxt/tf_ulp/bnxt_ulp.h| 4 +- drivers/net/bnxt/tf_ulp/ulp_fc_mgr.c | 28 +-- drivers/net/bnxt/tf_ulp/ulp_fc_mgr.h | 2 +- drivers/net/dpaa/dpaa_ethdev.c| 2 +- drivers/net/dpaa/dpaa_rxtx.c | 2 +- drivers/net/ena/base/ena_plat_dpdk.h | 15 +- drivers/net/enic/enic.h | 2 +- drivers/net/ice/ice_dcf_parent.c | 8 +- drivers/net/ixgbe/ixgbe_ethdev.c | 6 +- drivers/net/ixgbe/ixgbe_ethdev.h | 2 +- drivers/net/mlx5/linux/mlx5_os.c | 2 +- drivers/net/mlx5/mlx5.c | 20 +- drivers/net/mlx5/mlx5.h | 2 +- drivers/net/mlx5/mlx5_txpp.c | 8 +- drivers/net/mlx5/windows/mlx5_flow_os.c | 10 +- drivers/net/mlx5/windows/mlx5_os.c| 2 +- drivers/net/qede/base/bcm_osal.h | 8 +- drivers/net/vhost/rte_eth_vhost.c | 24 +-- .../net/virtio/virtio_user/virtio_user_dev.c | 30 +-- .../net/virtio/virtio_user/virtio_user_dev.h
[dpdk-dev] [PATCH v2 4/6] lib: enable the new EAL thread API
From: Narcisa Vasile Rename pthread* with the new rte_thread* API. Depends-on: series-17402 ("eal: Add EAL API for threading") Signed-off-by: Narcisa Vasile --- lib/eal/common/eal_common_options.c | 6 ++--- lib/eal/common/eal_common_thread.c | 13 + lib/eal/common/eal_common_trace.c | 1 + lib/eal/common/eal_private.h| 2 +- lib/eal/common/malloc_mp.c | 2 ++ lib/eal/freebsd/eal.c | 18 ++--- lib/eal/freebsd/eal_alarm.c | 12 - lib/eal/freebsd/eal_interrupts.c| 6 ++--- lib/eal/freebsd/eal_thread.c| 10 --- lib/eal/include/rte_lcore.h | 6 + lib/eal/include/rte_per_lcore.h | 2 +- lib/eal/linux/eal.c | 22 lib/eal/linux/eal_alarm.c | 10 --- lib/eal/linux/eal_interrupts.c | 8 +++--- lib/eal/linux/eal_thread.c | 11 +--- lib/eal/linux/eal_timer.c | 6 ++--- lib/eal/version.map | 4 +-- lib/eal/windows/eal.c | 4 ++- lib/eal/windows/eal_interrupts.c| 10 +++ lib/eal/windows/eal_thread.c| 35 - lib/eal/windows/eal_windows.h | 10 --- lib/eal/windows/include/rte_windows.h | 1 + lib/eal/windows/rte_thread.c| 2 +- lib/ethdev/rte_ethdev.c | 4 +-- lib/ethdev/rte_ethdev_core.h| 4 +-- lib/ethdev/rte_flow.c | 4 +-- lib/eventdev/rte_event_eth_rx_adapter.c | 1 + lib/vhost/vhost.c | 1 + 28 files changed, 101 insertions(+), 114 deletions(-) diff --git a/lib/eal/common/eal_common_options.c b/lib/eal/common/eal_common_options.c index 9d29696b84..1f6aba498c 100644 --- a/lib/eal/common/eal_common_options.c +++ b/lib/eal/common/eal_common_options.c @@ -1872,8 +1872,7 @@ eal_auto_detect_cores(struct rte_config *cfg) unsigned int removed = 0; rte_cpuset_t affinity_set; - if (pthread_getaffinity_np(pthread_self(), sizeof(rte_cpuset_t), - &affinity_set)) + if (rte_thread_get_affinity_by_id(rte_thread_self(), &affinity_set)) CPU_ZERO(&affinity_set); for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { @@ -1901,8 +1900,7 @@ compute_ctrl_threads_cpuset(struct internal_config *internal_cfg) } RTE_CPU_NOT(cpuset, cpuset); - if (pthread_getaffinity_np(pthread_self(), sizeof(rte_cpuset_t), - &default_set)) + if (rte_thread_get_affinity_by_id(rte_thread_self(), &default_set)) CPU_ZERO(&default_set); RTE_CPU_AND(cpuset, cpuset, &default_set); diff --git a/lib/eal/common/eal_common_thread.c b/lib/eal/common/eal_common_thread.c index 79545c67d9..62d28e7b28 100644 --- a/lib/eal/common/eal_common_thread.c +++ b/lib/eal/common/eal_common_thread.c @@ -6,7 +6,10 @@ #include #include #include +#ifndef RTE_EXEC_ENV_WINDOWS #include +#endif /* RTE_EXEC_ENV_WINDOWS */ +#include #include #include #include @@ -86,9 +89,8 @@ thread_update_affinity(rte_cpuset_t *cpusetp) int rte_thread_set_affinity(rte_cpuset_t *cpusetp) { - if (pthread_setaffinity_np(pthread_self(), sizeof(rte_cpuset_t), - cpusetp) != 0) { - RTE_LOG(ERR, EAL, "pthread_setaffinity_np failed\n"); + if (rte_thread_set_affinity_by_id(rte_thread_self(), cpusetp) != 0) { + RTE_LOG(ERR, EAL, "rte_thread_set_affinity_by_id failed\n"); return -1; } @@ -166,6 +168,7 @@ __rte_thread_uninit(void) RTE_PER_LCORE(_lcore_id) = LCORE_ID_ANY; } +#ifndef RTE_EXEC_ENV_WINDOWS struct rte_thread_ctrl_params { void *(*start_routine)(void *); void *arg; @@ -258,6 +261,7 @@ rte_ctrl_thread_create(pthread_t *thread, const char *name, return -ret; } +#endif struct rte_thread_ctrl_ctx { rte_thread_func start_routine; @@ -357,8 +361,7 @@ rte_thread_register(void) rte_errno = EINVAL; return -1; } - if (pthread_getaffinity_np(pthread_self(), sizeof(cpuset), - &cpuset) != 0) + if (rte_thread_get_affinity_by_id(rte_thread_self(), &cpuset) != 0) CPU_ZERO(&cpuset); lcore_id = eal_lcore_non_eal_allocate(); if (lcore_id >= RTE_MAX_LCORE) diff --git a/lib/eal/common/eal_common_trace.c b/lib/eal/common/eal_common_trace.c index 24e27387b1..ddbc692cf2 100644 --- a/lib/eal/common/eal_common_trace.c +++ b/lib/eal/common/eal_common_trace.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include diff --git a/lib/eal/common/eal_private.h b/lib/eal/common/eal_private.h index 64cf4e81c8..4b95001d7d 100644 --- a/lib/eal/common/eal_private.h +++ b/lib/eal/common/eal_private.h @@ -19,7 +19,7 @@ * Structure storing internal con
[dpdk-dev] [PATCH v2 3/6] Enable the new EAL thread API in app, drivers and examples
From: Narcisa Vasile Rename pthread* with the new rte_thread* API. Depends-on: series-17402 ("eal: Add EAL API for threading") Signed-off-by: Narcisa Vasile --- app/test/process.h| 8 +-- app/test/test_lcores.c| 18 +++ app/test/test_link_bonding.c | 14 ++--- app/test/test_lpm_perf.c | 12 ++--- drivers/bus/dpaa/base/qbman/bman_driver.c | 5 +- drivers/bus/dpaa/base/qbman/dpaa_sys.c| 14 ++--- drivers/bus/dpaa/base/qbman/process.c | 6 +-- drivers/bus/dpaa/dpaa_bus.c | 14 ++--- drivers/bus/fslmc/portal/dpaa2_hw_dpio.c | 19 --- drivers/common/dpaax/compat.h | 2 +- drivers/common/mlx5/windows/mlx5_common_os.h | 1 + drivers/compress/mlx5/mlx5_compress.c | 10 ++-- drivers/event/dlb2/dlb2.c | 2 +- drivers/event/dlb2/pf/base/dlb2_osdep.h | 7 ++- drivers/mempool/dpaa/dpaa_mempool.c | 2 +- drivers/net/af_xdp/rte_eth_af_xdp.c | 18 +++ drivers/net/ark/ark_ethdev.c | 4 +- drivers/net/ark/ark_pktgen.c | 4 +- drivers/net/atlantic/atl_ethdev.c | 4 +- drivers/net/atlantic/atl_types.h | 4 +- .../net/atlantic/hw_atl/hw_atl_utils_fw2x.c | 26 - drivers/net/axgbe/axgbe_common.h | 2 +- drivers/net/axgbe/axgbe_dev.c | 8 +-- drivers/net/axgbe/axgbe_ethdev.c | 8 +-- drivers/net/axgbe/axgbe_ethdev.h | 8 +-- drivers/net/axgbe/axgbe_i2c.c | 4 +- drivers/net/axgbe/axgbe_mdio.c| 8 +-- drivers/net/axgbe/axgbe_phy_impl.c| 6 +-- drivers/net/bnxt/bnxt.h | 16 +++--- drivers/net/bnxt/bnxt_cpr.c | 4 +- drivers/net/bnxt/bnxt_ethdev.c| 54 +-- drivers/net/bnxt/bnxt_irq.c | 8 +-- drivers/net/bnxt/bnxt_reps.c | 10 ++-- drivers/net/bnxt/tf_ulp/bnxt_ulp.c| 34 ++-- drivers/net/bnxt/tf_ulp/bnxt_ulp.h| 4 +- drivers/net/bnxt/tf_ulp/ulp_fc_mgr.c | 28 +- drivers/net/bnxt/tf_ulp/ulp_fc_mgr.h | 2 +- drivers/net/dpaa/dpaa_ethdev.c| 2 +- drivers/net/dpaa/dpaa_rxtx.c | 2 +- drivers/net/ena/base/ena_plat_dpdk.h | 15 +++--- drivers/net/enic/enic.h | 2 +- drivers/net/ice/ice_dcf_parent.c | 8 +-- drivers/net/ixgbe/ixgbe_ethdev.c | 6 +-- drivers/net/ixgbe/ixgbe_ethdev.h | 2 +- drivers/net/mlx5/linux/mlx5_os.c | 2 +- drivers/net/mlx5/mlx5.c | 20 +++ drivers/net/mlx5/mlx5.h | 2 +- drivers/net/mlx5/mlx5_txpp.c | 8 +-- drivers/net/mlx5/windows/mlx5_flow_os.c | 10 ++-- drivers/net/mlx5/windows/mlx5_os.c| 2 +- drivers/net/qede/base/bcm_osal.h | 8 +-- drivers/net/vhost/rte_eth_vhost.c | 24 - .../net/virtio/virtio_user/virtio_user_dev.c | 30 +-- .../net/virtio/virtio_user/virtio_user_dev.h | 2 +- drivers/vdpa/ifc/ifcvf_vdpa.c | 49 - drivers/vdpa/mlx5/mlx5_vdpa.c | 24 - drivers/vdpa/mlx5/mlx5_vdpa.h | 4 +- drivers/vdpa/mlx5/mlx5_vdpa_event.c | 51 -- examples/kni/main.c | 1 + .../pthread_shim/pthread_shim.h | 1 + 60 files changed, 334 insertions(+), 339 deletions(-) diff --git a/app/test/process.h b/app/test/process.h index a09a088477..9e4be17bad 100644 --- a/app/test/process.h +++ b/app/test/process.h @@ -26,7 +26,7 @@ #ifdef RTE_LIB_PDUMP #ifdef RTE_NET_RING -#include +#include extern void *send_pkts(void *empty); extern uint16_t flag_for_send_pkts; #endif @@ -47,7 +47,7 @@ process_dup(const char *const argv[], int numargs, const char *env_value) char path[32]; #ifdef RTE_LIB_PDUMP #ifdef RTE_NET_RING - pthread_t thread; + rte_thread_t thread; int rc; #endif #endif @@ -128,7 +128,7 @@ process_dup(const char *const argv[], int numargs, const char *env_value) #ifdef RTE_LIB_PDUMP #ifdef RTE_NET_RING if ((strcmp(env_value, "run_pdump_server_tests") == 0)) { - rc = pthread_create(&thread, NULL, &send_pkts, NULL); + rc = rte_thread_create(&thread, NULL, &send_pkts, NULL); if (rc != 0) { rte_panic("Cannot start send pkts thread: %s\n", strerror(rc)); @@ -143,7 +143,7 @@ process_dup(const char *const argv[], int numargs, const char *env_value) #ifdef RTE_NET_RING if ((strcmp(env_value, "run_pdump_server_tests") == 0)) { flag_for_send_pkts = 0; - pthread_join(thread, NULL); +
[dpdk-dev] [PATCH v2 5/6] eal: set affinity and priority attributes
From: Narcisa Vasile If the user doesn't specify the priority through the command line arguments, initialize the thread priority to 'normal'. Set thread priority for eal threads. Depends-on: series-17402 ("eal: Add EAL API for threading") Signed-off-by: Narcisa Vasile --- lib/eal/common/eal_common_thread.c | 11 lib/eal/common/eal_thread.h| 6 lib/eal/freebsd/eal.c | 41 +++- lib/eal/linux/eal.c| 39 +- lib/eal/windows/eal.c | 44 -- 5 files changed, 125 insertions(+), 16 deletions(-) diff --git a/lib/eal/common/eal_common_thread.c b/lib/eal/common/eal_common_thread.c index 62d28e7b28..fa537db7ac 100644 --- a/lib/eal/common/eal_common_thread.c +++ b/lib/eal/common/eal_common_thread.c @@ -388,3 +388,14 @@ rte_thread_unregister(void) RTE_LOG(DEBUG, EAL, "Unregistered non-EAL thread (was lcore %u).\n", lcore_id); } + +void rte_thread_priority_init(void) +{ + struct internal_config *internal_conf = + eal_get_internal_configuration(); + + /* If the user doesn't specify the priority through the command +* line arguments, the default 'normal' value will be used. +*/ + internal_conf->thread_priority = RTE_THREAD_PRIORITY_NORMAL; +} diff --git a/lib/eal/common/eal_thread.h b/lib/eal/common/eal_thread.h index 4a49117be8..7b3b884463 100644 --- a/lib/eal/common/eal_thread.h +++ b/lib/eal/common/eal_thread.h @@ -58,4 +58,10 @@ eal_thread_dump_affinity(rte_cpuset_t *cpuset, char *str, unsigned int size); int eal_thread_dump_current_affinity(char *str, unsigned int size); +/** + * Set the initial thread priority in the internal configuration + * to the default value of RTE_THREAD_PRIORITY_NORMAL. + */ +void rte_thread_priority_init(void); + #endif /* EAL_THREAD_H */ diff --git a/lib/eal/freebsd/eal.c b/lib/eal/freebsd/eal.c index b5cf050697..7303d62afb 100644 --- a/lib/eal/freebsd/eal.c +++ b/lib/eal/freebsd/eal.c @@ -676,6 +676,8 @@ rte_eal_init(int argc, char **argv) struct internal_config *internal_conf = eal_get_internal_configuration(); + rte_thread_priority_init(); + /* checks if the machine is adequate */ if (!rte_cpu_is_supported()) { rte_eal_init_alert("unsupported cpu type."); @@ -854,6 +856,14 @@ rte_eal_init(int argc, char **argv) eal_check_mem_on_local_socket(); + ret = rte_thread_set_priority(rte_thread_self(), + internal_conf->thread_priority); + if (ret != 0) { + rte_eal_init_alert("Cannot set thread priority"); + rte_errno = ret; + return -1; + } + if (rte_thread_set_affinity_by_id(rte_thread_self(), &lcore_config[config->main_lcore].cpuset) != 0) { rte_eal_init_alert("Cannot set affinity"); @@ -869,6 +879,22 @@ rte_eal_init(int argc, char **argv) config->main_lcore, (void *)thread_id.opaque_id, cpuset, ret == 0 ? "" : "..."); + rte_thread_attr_t thread_attr; + ret = rte_thread_attr_init(&thread_attr); + if (ret != 0) { + rte_eal_init_alert("Cannot initialize thread attributes"); + rte_errno = ret; + return -1; + } + + ret = rte_thread_attr_set_priority(&thread_attr, + internal_conf->thread_priority); + if (ret != 0) { + rte_eal_init_alert("Cannot set thread priority attribute"); + rte_errno = ret; + return -1; + } + RTE_LCORE_FOREACH_WORKER(i) { /* @@ -882,9 +908,15 @@ rte_eal_init(int argc, char **argv) lcore_config[i].state = WAIT; + ret = rte_thread_attr_set_affinity(&thread_attr, +&lcore_config[i].cpuset); + + if (ret != 0) + rte_panic("Cannot set affinity\n"); + /* create a thread for each lcore */ - ret = rte_thread_create(&lcore_config[i].thread_id, NULL, -eal_thread_loop, NULL); + ret = rte_thread_create(&lcore_config[i].thread_id, + &thread_attr, eal_thread_loop, NULL); if (ret != 0) rte_panic("Cannot create thread\n"); @@ -892,11 +924,6 @@ rte_eal_init(int argc, char **argv) snprintf(thread_name, sizeof(thread_name), "lcore-worker-%d", i); rte_thread_name_set(lcore_config[i].thread_id, thread_name); - - ret = rte_thread_set_affinity_by_id(lcore_config[i].thread_id, - &lcore_config[i].cpuset); - if (ret != 0) - rte_panic("Cannot set affinity\n");
[dpdk-dev] [PATCH v2 6/6] Allow choice between internal EAL thread API and external lib
From: Narcisa Vasile The user is offered the option of either using the RTE_THREAD_* API or a 3rd party thread library, through a meson flag called "use_external_thread_lib". By default, this flag is set to FALSE, which means Windows libraries and applications will use the RTE_THREAD_* API for managing threads. If compiling on Windows and the "use_external_thread_lib" is *not* set, the following files will be parsed: * include/rte_thread.h * windows/rte_thread.c In all other cases, the compilation/parsing includes the following files: * include/rte_thread.h * common/rte_thread.c Depends-on: series-17402 ("eal: Add EAL API for threading") Signed-off-by: Narcisa Vasile --- config/meson.build| 1 - lib/eal/windows/include/pthread.h | 192 -- lib/eal/windows/meson.build | 7 +- meson_options.txt | 2 + 4 files changed, 8 insertions(+), 194 deletions(-) delete mode 100644 lib/eal/windows/include/pthread.h diff --git a/config/meson.build b/config/meson.build index 017bb2efbb..9309010f21 100644 --- a/config/meson.build +++ b/config/meson.build @@ -262,7 +262,6 @@ else # for 32-bit we need smaller reserved memory areas dpdk_conf.set('RTE_MAX_MEM_MB', 2048) endif - compile_time_cpuflags = [] subdir(arch_subdir) dpdk_conf.set('RTE_COMPILE_TIME_CPUFLAGS', ','.join(compile_time_cpuflags)) diff --git a/lib/eal/windows/include/pthread.h b/lib/eal/windows/include/pthread.h deleted file mode 100644 index 27fd2cca52..00 --- a/lib/eal/windows/include/pthread.h +++ /dev/null @@ -1,192 +0,0 @@ -/* SPDX-License-Identifier: BSD-3-Clause - * Copyright(c) 2019 Intel Corporation - */ - -#ifndef _PTHREAD_H_ -#define _PTHREAD_H_ - -#include -#include - -/** - * This file is required to support the common code in eal_common_proc.c, - * eal_common_thread.c and common\include\rte_per_lcore.h as Microsoft libc - * does not contain pthread.h. This may be removed in future releases. - */ -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -#define PTHREAD_BARRIER_SERIAL_THREAD TRUE - -/* defining pthread_t type on Windows since there is no in Microsoft libc*/ -typedef uintptr_t pthread_t; - -/* defining pthread_attr_t type on Windows since there is no in Microsoft libc*/ -typedef void *pthread_attr_t; - -typedef void *pthread_mutexattr_t; - -typedef CRITICAL_SECTION pthread_mutex_t; - -typedef SYNCHRONIZATION_BARRIER pthread_barrier_t; - -#define pthread_barrier_init(barrier, attr, count) \ - !InitializeSynchronizationBarrier(barrier, count, -1) -#define pthread_barrier_wait(barrier) EnterSynchronizationBarrier(barrier, \ - SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY) -#define pthread_barrier_destroy(barrier) \ - !DeleteSynchronizationBarrier(barrier) -#define pthread_cancel(thread) !TerminateThread((HANDLE) thread, 0) - -/* pthread function overrides */ -#define pthread_self() \ - ((pthread_t)GetCurrentThreadId()) - - -static inline int -pthread_equal(pthread_t t1, pthread_t t2) -{ - return t1 == t2; -} - -static inline int -pthread_setaffinity_np(pthread_t threadid, size_t cpuset_size, - rte_cpuset_t *cpuset) -{ - DWORD_PTR ret = 0; - HANDLE thread_handle; - - if (cpuset == NULL || cpuset_size == 0) - return -1; - - thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, threadid); - if (thread_handle == NULL) { - RTE_LOG_WIN32_ERR("OpenThread()"); - return -1; - } - - ret = SetThreadAffinityMask(thread_handle, *cpuset->_bits); - if (ret == 0) { - RTE_LOG_WIN32_ERR("SetThreadAffinityMask()"); - goto close_handle; - } - -close_handle: - if (CloseHandle(thread_handle) == 0) { - RTE_LOG_WIN32_ERR("CloseHandle()"); - return -1; - } - return (ret == 0) ? -1 : 0; -} - -static inline int -pthread_getaffinity_np(pthread_t threadid, size_t cpuset_size, - rte_cpuset_t *cpuset) -{ - /* Workaround for the lack of a GetThreadAffinityMask() -*API in Windows -*/ - DWORD_PTR prev_affinity_mask; - HANDLE thread_handle; - DWORD_PTR ret = 0; - - if (cpuset == NULL || cpuset_size == 0) - return -1; - - thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, threadid); - if (thread_handle == NULL) { - RTE_LOG_WIN32_ERR("OpenThread()"); - return -1; - } - - /* obtain previous mask by setting dummy mask */ - prev_affinity_mask = SetThreadAffinityMask(thread_handle, 0x1); - if (prev_affinity_mask == 0) { - RTE_LOG_WIN32_ERR("SetThreadAffinityMask()"); - goto close_handle; - } - - /* set it back! */ - ret = SetThreadAffinityMask(thread_handle, prev_affinity_mask); - if (ret == 0) { - RTE_LOG_WIN32_ERR("SetThreadAffinityMask()"
[dpdk-dev] [PATCH] eal/windows: ensure all the CPUs in the set are checked
From: Narcisa Vasile Fix count_cpu() to ensure it iterates through all the CPUs in a set. count_cpu() iterates through the CPUs in the set 's' and counts the selected ones. Previously, it was incorrectly using the number of CPUSETS to iterate through the CPUs. Signed-off-by: Narcisa Vasile --- lib/eal/windows/include/sched.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/eal/windows/include/sched.h b/lib/eal/windows/include/sched.h index ff572b5dcb..bc31cc8465 100644 --- a/lib/eal/windows/include/sched.h +++ b/lib/eal/windows/include/sched.h @@ -49,7 +49,7 @@ count_cpu(rte_cpuset_t *s) unsigned int _i; int count = 0; - for (_i = 0; _i < _NUM_SETS(CPU_SETSIZE); _i++) + for (_i = 0; _i < CPU_SETSIZE; _i++) if (CPU_ISSET(_i, s) != 0LL) count++; return count; -- 2.31.0.vfs.0.1
[dpdk-dev] [PATCH v2] eal/windows: ensure all enabled CPUs are counted
From: Narcisa Vasile rte_cpuset_t describes a set of CPUs by using an array of masks named '_bits'. Each element in the '_bits' array represents a bit mask, with each bit corresponding to a CPU. The maximum number of CPUs is given by 'CPU_SETSIZE'. The number of bit masks is computed using '_NUM_SETS(CPU_SETSIZE)'. count_cpu() should count the number of CPUs enabled in the set 's'. Currently, it iterates through the number of masks in the set 's', instead of iterating through all the bits in all the masks. For example, if '_NUM_SETS(CPU_SETSIZE)' returns 2, which means there are 2 bit masks: _bits[0] and _bits[1], count_cpu() would only check if CPUs '0' and '1' are enabled. The correct behavior is to iterate through all the CPUs in the set and count the ones that are enabled. This patch fixes count_cpu() to ensure all the bits in all the masks are checked to compute the correct number of CPUs enabled in 's'. Fixes: e8428a9d89f1 ("eal/windows: add some basic functions and macros") Cc: pallavi.ka...@intel.com Cc: sta...@dpdk.org Signed-off-by: Narcisa Vasile --- v2: * Fix commit message. lib/eal/windows/include/sched.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/eal/windows/include/sched.h b/lib/eal/windows/include/sched.h index ff572b5dcb..bc31cc8465 100644 --- a/lib/eal/windows/include/sched.h +++ b/lib/eal/windows/include/sched.h @@ -49,7 +49,7 @@ count_cpu(rte_cpuset_t *s) unsigned int _i; int count = 0; - for (_i = 0; _i < _NUM_SETS(CPU_SETSIZE); _i++) + for (_i = 0; _i < CPU_SETSIZE; _i++) if (CPU_ISSET(_i, s) != 0LL) count++; return count; -- 2.31.0.vfs.0.1
Re: [dpdk-dev] [PATCH] eal/windows: ensure all the CPUs in the set are checked
On Fri, Jun 25, 2021 at 11:36:21AM +0300, Dmitry Kozlyuk wrote: > 2021-06-24 17:27 (UTC-0700), Narcisa Ana Maria Vasile: > > From: Narcisa Vasile > > > > Fix count_cpu() to ensure it iterates through all the CPUs in a set. > > count_cpu() iterates through the CPUs in the set 's' and counts the > > selected ones. > > > > Previously, it was incorrectly using the number of CPUSETS to iterate > > through the CPUs. > > > > Signed-off-by: Narcisa Vasile > > --- > > lib/eal/windows/include/sched.h | 2 +- > > 1 file changed, 1 insertion(+), 1 deletion(-) > > Hi Naty, > > Thank you for the fix, but we also need a proper commit message: > > https://doc.dpdk.org/guides/contributing/patches.html#commit-messages-body > > Specifically, please, describe what was the observable issue (usually first > comes what was wrong, then how it is fixed now) and add "Fixes" tag and Cc. > Also, "number of CPUSETS" sounds unclear, as there's no "CPUSET". > Suggestion: "number of bitset limbs" or maybe if you describe what was > wrong with the result you won't need to describe its reason precisely at all. Ah, I've mixed some terminology here.. Thank you Dmitry for the feedback!
Re: [dpdk-dev] [PATCH v5 03/10] windows/eal: translate Windows errors to errno-style errors
On Wed, Mar 31, 2021 at 01:56:09PM +, Tal Shnaiderman wrote: > > Subject: [PATCH v5 03/10] windows/eal: translate Windows errors to errno- > > style errors > > > > External email: Use caution opening links or attachments > > > > > > From: Narcisa Vasile > > > > Add function to translate Windows error codes to errno-style error codes. > > > > Signed-off-by: Narcisa Vasile > > --- > > lib/librte_eal/windows/rte_thread.c | 65 ++--- > > 1 file changed, 50 insertions(+), 15 deletions(-) > > > > diff --git a/lib/librte_eal/windows/rte_thread.c > > b/lib/librte_eal/windows/rte_thread.c > > index b29336cbd..e9181b47f 100644 > > --- a/lib/librte_eal/windows/rte_thread.c > > +++ b/lib/librte_eal/windows/rte_thread.c > > @@ -12,6 +12,47 @@ struct eal_tls_key { > > DWORD thread_index; > > }; > > > > +/* Translates the most common error codes related to threads */ static > > +int rte_thread_translate_win32_error(DWORD error) { > > This DWORD error will always the output of GetLastError()? If so can we move > it inside the function? Yes, I think we can move the call to GetLastError() inside the function, thanks Tal. > > Also, I don't think this is a thread specific function, other implementations > can use it in the future, maybe move it to rte_windows.h? I think it's better to keep these error-translation functions inside a specific module (threads, memory, etc.). The reason for that is that the same error code may mean different things in different modules. When I implemented this function I've went through all the Windows threads functions and noted down the type of errors that GetLastError() returns for them and what they mean. For a different module, a different set of errors might be more suitable, so to keep the translations as semantically compatible as possible to Linux, it's probably better to keep it at the module level (threading in this case). > > > + switch (error) { > > + case ERROR_SUCCESS: > > + return 0; > > + > > + case ERROR_INVALID_PARAMETER: > > + return EINVAL; > > + > > + case ERROR_INVALID_HANDLE: > > + return EFAULT; > > + > > + case ERROR_NOT_ENOUGH_MEMORY: > > + /* FALLTHROUGH */ > > + case ERROR_NO_SYSTEM_RESOURCES: > > + return ENOMEM; > > + > > + case ERROR_PRIVILEGE_NOT_HELD: > > + /* FALLTHROUGH */ > > + case ERROR_ACCESS_DENIED: > > + return EACCES; > > + > > + case ERROR_ALREADY_EXISTS: > > + return EEXIST; > > + > > + case ERROR_POSSIBLE_DEADLOCK: > > + return EDEADLK; > > + > > + case ERROR_INVALID_FUNCTION: > > + /* FALLTHROUGH */ > > + case ERROR_CALL_NOT_IMPLEMENTED: > > + return ENOSYS; > > + > > + default: > > + return EINVAL; > > + } > > + > > + return EINVAL; > > +} > > + > > @@ -143,7 +178,7 @@ rte_thread_value_get(rte_thread_key key) { > > void *output; > > > > This function is missing the change to rte_thread_translate_win32_error. > Aldo need to change function docu. Thanks, will fix and send v6. > > > - if (!key) { > > + if (key == NULL) { > > RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); > > rte_errno = EINVAL; > > return NULL; > > -- > > 2.30.0.vfs.0.2
[dpdk-dev] [PATCH v6 01/10] eal: add thread id and simple thread functions
From: Narcisa Vasile Add the thread identifier type. Add functions for comparing thread ids and obtaining the thread id for the current thread. Signed-off-by: Narcisa Vasile --- lib/librte_eal/common/rte_thread.c| 101 ++ lib/librte_eal/include/rte_thread.h | 45 ++-- lib/librte_eal/include/rte_thread_types.h | 12 +++ .../include/rte_windows_thread_types.h| 12 +++ lib/librte_eal/windows/rte_thread.c | 13 +++ 5 files changed, 174 insertions(+), 9 deletions(-) create mode 100644 lib/librte_eal/common/rte_thread.c create mode 100644 lib/librte_eal/include/rte_thread_types.h create mode 100644 lib/librte_eal/windows/include/rte_windows_thread_types.h diff --git a/lib/librte_eal/common/rte_thread.c b/lib/librte_eal/common/rte_thread.c new file mode 100644 index 0..5ec382949 --- /dev/null +++ b/lib/librte_eal/common/rte_thread.c @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright 2021 Mellanox Technologies, Ltd + * Copyright(c) 2021 Microsoft Corporation + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +struct eal_tls_key { + pthread_key_t thread_index; +}; + +rte_thread_t +rte_thread_self(void) +{ + return pthread_self(); +} + +int +rte_thread_equal(rte_thread_t t1, rte_thread_t t2) +{ + return pthread_equal(t1, t2); +} + +int +rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) +{ + int err; + rte_thread_key k; + + k = malloc(sizeof(*k)); + if (k == NULL) { + RTE_LOG(DEBUG, EAL, "Cannot allocate TLS key.\n"); + return EINVAL; + } + err = pthread_key_create(&(k->thread_index), destructor); + if (err != 0) { + RTE_LOG(DEBUG, EAL, "pthread_key_create failed: %s\n", +strerror(err)); + free(k); + return err; + } + *key = k; + return 0; +} + +int +rte_thread_key_delete(rte_thread_key key) +{ + int err; + + if (key == NULL) { + RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); + return EINVAL; + } + err = pthread_key_delete(key->thread_index); + if (err != 0) { + RTE_LOG(DEBUG, EAL, "pthread_key_delete failed: %s\n", +strerror(err)); + free(key); + return err; + } + free(key); + return 0; +} + +int +rte_thread_value_set(rte_thread_key key, const void *value) +{ + int err; + + if (key == NULL) { + RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); + return EINVAL; + } + err = pthread_setspecific(key->thread_index, value); + if (err != 0) { + RTE_LOG(DEBUG, EAL, "pthread_setspecific failed: %s\n", + strerror(err)); + return err; + } + return 0; +} + +void * +rte_thread_value_get(rte_thread_key key) +{ + if (key == NULL) { + RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); + rte_errno = EINVAL; + return NULL; + } + return pthread_getspecific(key->thread_index); +} diff --git a/lib/librte_eal/include/rte_thread.h b/lib/librte_eal/include/rte_thread.h index 8be8ed8f3..cbc07f739 100644 --- a/lib/librte_eal/include/rte_thread.h +++ b/lib/librte_eal/include/rte_thread.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2021 Mellanox Technologies, Ltd + * Copyright(c) 2021 Microsoft Corporation */ #include @@ -20,6 +21,13 @@ extern "C" { #endif +#include +#if defined(RTE_USE_WINDOWS_THREAD_TYPES) +#include +#else +#include +#endif + /** * TLS key type, an opaque pointer. */ @@ -27,6 +35,31 @@ typedef struct eal_tls_key *rte_thread_key; #ifdef RTE_HAS_CPUSET +/** + * Get the id of the calling thread. + * + * @return + * Return the thread id of the calling thread. + */ +__rte_experimental +rte_thread_t rte_thread_self(void); + +/** + * Check if 2 thread ids are equal. + * + * @param t1 + * First thread id. + * + * @param t2 + * Second thread id. + * + * @return + * If the ids are equal, return nonzero. + * Otherwise, return 0. + */ +__rte_experimental +int rte_thread_equal(rte_thread_t t1, rte_thread_t t2); + /** * Set core affinity of the current thread. * Support both EAL and non-EAL thread and update TLS. @@ -63,9 +96,7 @@ void rte_thread_get_affinity(rte_cpuset_t *cpusetp); * * @return * On success, zero. - * On failure, a negative number and an error number is set in rte_errno. - * rte_errno can be: ENOMEM - Memory allocation error. - * ENOEXEC - Specific OS error. + * On failure, return a positive errno-style error number. */ __rte_experimental @@ -80,9 +111,7 @@ int rte_thread_key_create(rte_thread_key *key, * * @return * On success, zero. - * On failure, a negative nu
[dpdk-dev] [PATCH v6 00/10] eal: Add new API for threading
From: Narcisa Vasile EAL thread API **Problem Statement** DPDK currently uses the pthread interface to create and manage threads. Windows does not support the POSIX thread programming model, so it currently relies on a header file that hides the Windows calls under pthread matched interfaces. Given that EAL should isolate the environment specifics from the applications and libraries and mediate all the communication with the operating systems, a new EAL interface is needed for thread management. **Goals** * Introduce a generic EAL API for threading support that will remove the current Windows pthread.h shim. * Replace references to pthread_* across the DPDK codebase with the new RTE_THREAD_* API. * Allow users to choose between using the RTE_THREAD_* API or a 3rd party thread library through a configuration option. **Design plan** New API main files: * rte_thread.h (librte_eal/include) * rte_thread_types.h (librte_eal/include) * rte_thread_windows_types.h (librte_eal/windows/include) * rte_thread.c (librte_eal/windows) * rte_thread.c (librte_eal/common) For flexibility, the user is offered the option of either using the RTE_THREAD_* API or a 3rd party thread library, through a meson flag “use_external_thread_lib”. By default, this flag is set to FALSE, which means Windows libraries and applications will use the RTE_THREAD_* API for managing threads. If compiling on Windows and the “use_external_thread_lib” is *not* set, the following files will be parsed: * include/rte_thread.h * windows/include/rte_thread_windows_types.h * windows/rte_thread.c In all other cases, the compilation/parsing includes the following files: * include/rte_thread.h * include/rte_thread_types.h * common/rte_thread.c **A schematic example of the design** -- lib/librte_eal/include/rte_thread.h int rte_thread_create(); lib/librte_eal/common/rte_thread.c int rte_thread_create() { return pthread_create(); } lib/librte_eal/windows/rte_thread.c int rte_thread_create() { return CreateThread(); } lib/librte_eal/windows/meson.build if get_option('use_external_thread_lib') sources += 'librte_eal/common/rte_thread.c' else sources += 'librte_eal/windows/rte_thread.c' endif - **Thread attributes** When or after a thread is created, specific characteristics of the thread can be adjusted. Given that the thread characteristics that are of interest for DPDK applications are affinity and priority, the following structure that represents thread attributes has been defined: typedef struct { enum rte_thread_priority priority; rte_cpuset_t cpuset; } rte_thread_attr_t; The *rte_thread_create()* function can optionally receive an rte_thread_attr_t object that will cause the thread to be created with the affinity and priority described by the attributes object. If no rte_thread_attr_t is passed (parameter is NULL), the default affinity and priority are used. An rte_thread_attr_t object can also be set to the default values by calling *rte_thread_attr_init()*. *Priority* is represented through an enum that currently advertises two values for priority: - RTE_THREAD_PRIORITY_NORMAL - RTE_THREAD_PRIORITY_REALTIME_CRITICAL The enum can be extended to allow for multiple priority levels. rte_thread_set_priority - sets the priority of a thread rte_thread_attr_set_priority - updates an rte_thread_attr_t object with a new value for priority The user can choose thread priority through an EAL parameter, when starting an application. If EAL parameter is not used, the per-platform default value for thread priority is used. Otherwise administrator has an option to set one of available options: --thread-prio normal --thread-prio realtime Example: ./dpdk-l2fwd -l 0-3 -n 4 –thread-prio normal -- -q 8 -p *Affinity* is described by the already known “rte_cpuset_t” type. rte_thread_attr_set/get_affinity - sets/gets the affinity field in a rte_thread_attr_t object rte_thread_set/get_affinity – sets/gets the affinity of a thread **Errors** A translation function that maps Windows error codes to errno-style error codes is provided. **Future work** Note that this patchset was focused on introducing new API that will remove the Windows pthread.h shim. In DPDK, there are still a few references to pthread_* that were not implemented in the shim. The long term plan is for EAL to provide full threading support: * Adding support for conditional variables * Additional functionality offered by pthread_* (such as pthread_setname_np, etc.) * Static mutex initializers are not used on Windows. If we must continue using them, they need to be platform dependent and an implementation will need to be provided for Windows. v6: - improve error-translation function - call the error translation function in rte_thread_va
[dpdk-dev] [PATCH v6 03/10] windows/eal: translate Windows errors to errno-style errors
From: Narcisa Vasile Add function to translate Windows error codes to errno-style error codes. Signed-off-by: Narcisa Vasile --- lib/librte_eal/include/rte_thread.h | 5 +- lib/librte_eal/windows/rte_thread.c | 75 ++--- 2 files changed, 60 insertions(+), 20 deletions(-) diff --git a/lib/librte_eal/include/rte_thread.h b/lib/librte_eal/include/rte_thread.h index bfdd8e1b1..2d7b3bc05 100644 --- a/lib/librte_eal/include/rte_thread.h +++ b/lib/librte_eal/include/rte_thread.h @@ -221,9 +221,8 @@ int rte_thread_value_set(rte_thread_key key, const void *value); * * @return * On success, value data pointer (can also be NULL). - * On failure, NULL and an error number is set in rte_errno. - * rte_errno can be: EINVAL - Invalid parameter passed. - * ENOEXEC - Specific OS error. + * On failure, NULL and a positive error number is set in rte_errno. + * */ __rte_experimental void *rte_thread_value_get(rte_thread_key key); diff --git a/lib/librte_eal/windows/rte_thread.c b/lib/librte_eal/windows/rte_thread.c index b29336cbd..ecd2f810f 100644 --- a/lib/librte_eal/windows/rte_thread.c +++ b/lib/librte_eal/windows/rte_thread.c @@ -12,6 +12,51 @@ struct eal_tls_key { DWORD thread_index; }; +/* Translates the most common error codes related to threads */ +static int rte_thread_translate_win32_error(void) +{ + DWORD error = 0; + + error = GetLastError(); + + switch (error) { + case ERROR_SUCCESS: + return 0; + + case ERROR_INVALID_PARAMETER: + return EINVAL; + + case ERROR_INVALID_HANDLE: + return EFAULT; + + case ERROR_NOT_ENOUGH_MEMORY: + /* FALLTHROUGH */ + case ERROR_NO_SYSTEM_RESOURCES: + return ENOMEM; + + case ERROR_PRIVILEGE_NOT_HELD: + /* FALLTHROUGH */ + case ERROR_ACCESS_DENIED: + return EACCES; + + case ERROR_ALREADY_EXISTS: + return EEXIST; + + case ERROR_POSSIBLE_DEADLOCK: + return EDEADLK; + + case ERROR_INVALID_FUNCTION: + /* FALLTHROUGH */ + case ERROR_CALL_NOT_IMPLEMENTED: + return ENOSYS; + + default: + return EINVAL; + } + + return EINVAL; +} + rte_thread_t rte_thread_self(void) { @@ -87,15 +132,13 @@ rte_thread_key_create(rte_thread_key *key, *key = malloc(sizeof(**key)); if ((*key) == NULL) { RTE_LOG(DEBUG, EAL, "Cannot allocate TLS key.\n"); - rte_errno = ENOMEM; - return -1; + return ENOMEM; } (*key)->thread_index = TlsAlloc(); if ((*key)->thread_index == TLS_OUT_OF_INDEXES) { RTE_LOG_WIN32_ERR("TlsAlloc()"); free(*key); - rte_errno = ENOEXEC; - return -1; + return rte_thread_translate_win32_error(); } return 0; } @@ -103,16 +146,14 @@ rte_thread_key_create(rte_thread_key *key, int rte_thread_key_delete(rte_thread_key key) { - if (!key) { + if (key == NULL) { RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); - rte_errno = EINVAL; - return -1; + return EINVAL; } if (!TlsFree(key->thread_index)) { RTE_LOG_WIN32_ERR("TlsFree()"); free(key); - rte_errno = ENOEXEC; - return -1; + return rte_thread_translate_win32_error(); } free(key); return 0; @@ -123,17 +164,15 @@ rte_thread_value_set(rte_thread_key key, const void *value) { char *p; - if (!key) { + if (key == NULL) { RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); - rte_errno = EINVAL; - return -1; + return EINVAL; } /* discard const qualifier */ p = (char *) (uintptr_t) value; if (!TlsSetValue(key->thread_index, p)) { RTE_LOG_WIN32_ERR("TlsSetValue()"); - rte_errno = ENOEXEC; - return -1; + return rte_thread_translate_win32_error(); } return 0; } @@ -142,16 +181,18 @@ void * rte_thread_value_get(rte_thread_key key) { void *output; + int ret = 0; - if (!key) { + if (key == NULL) { RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); rte_errno = EINVAL; return NULL; } output = TlsGetValue(key->thread_index); - if (GetLastError() != ERROR_SUCCESS) { + ret = rte_thread_translate_win32_error(); + if (ret != 0) { RTE_LOG_WIN32_ERR("TlsGetValue()"); - rte_errno = ENOEXEC; + rte_errno = ret; return NULL; } return output; -- 2.30.0.vfs.0.2
[dpdk-dev] [PATCH v6 04/10] eal: implement functions for thread affinity management
From: Narcisa Vasile Implement functions for getting/setting thread affinity. Signed-off-by: Narcisa Vasile Signed-off-by: Dmitry Malloy --- lib/librte_eal/common/rte_thread.c | 13 ++ lib/librte_eal/include/rte_thread.h | 41 +++ lib/librte_eal/windows/eal_lcore.c | 170 +++ lib/librte_eal/windows/eal_windows.h | 10 ++ lib/librte_eal/windows/rte_thread.c | 132 - 5 files changed, 318 insertions(+), 48 deletions(-) diff --git a/lib/librte_eal/common/rte_thread.c b/lib/librte_eal/common/rte_thread.c index 0bd1b115d..4f93e3ff1 100644 --- a/lib/librte_eal/common/rte_thread.c +++ b/lib/librte_eal/common/rte_thread.c @@ -29,6 +29,19 @@ rte_thread_equal(rte_thread_t t1, rte_thread_t t2) return pthread_equal(t1, t2); } +int +rte_thread_set_affinity_by_id(rte_thread_t thread_id, size_t cpuset_size, + const rte_cpuset_t *cpuset) +{ + return pthread_setaffinity_np(thread_id, cpuset_size, cpuset); +} + +int rte_thread_get_affinity_by_id(rte_thread_t threadid, size_t cpuset_size, + rte_cpuset_t *cpuset) +{ + return pthread_getaffinity_np(threadid, cpuset_size, cpuset); +} + int rte_thread_attr_init(rte_thread_attr_t *attr) { diff --git a/lib/librte_eal/include/rte_thread.h b/lib/librte_eal/include/rte_thread.h index 2d7b3bc05..4b1e3dfe8 100644 --- a/lib/librte_eal/include/rte_thread.h +++ b/lib/librte_eal/include/rte_thread.h @@ -73,6 +73,47 @@ rte_thread_t rte_thread_self(void); __rte_experimental int rte_thread_equal(rte_thread_t t1, rte_thread_t t2); +/** + * Set the affinity of thread 'thread_id' to the cpu set + * specified by 'cpuset'. + * + * @param thread_id + *Id of the thread for which to set the affinity. + * + * @param cpuset_size + * + * @param cpuset + * Pointer to CPU affinity to set. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_set_affinity_by_id(rte_thread_t thread_id, size_t cpuset_size, + const rte_cpuset_t *cpuset); + +/** + * Get the affinity of thread 'thread_id' and store it + * in 'cpuset'. + * + * @param thread_id + *Id of the thread for which to get the affinity. + * + * @param cpuset_size + *Size of the cpu set. + * + * @param cpuset + * Pointer for storing the affinity value. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_get_affinity_by_id(rte_thread_t thread_id, size_t cpuset_size, + rte_cpuset_t *cpuset); + /** * Initialize the attributes of a thread. * These attributes can be passed to the rte_thread_create() function diff --git a/lib/librte_eal/windows/eal_lcore.c b/lib/librte_eal/windows/eal_lcore.c index a85149be9..023c5c895 100644 --- a/lib/librte_eal/windows/eal_lcore.c +++ b/lib/librte_eal/windows/eal_lcore.c @@ -2,7 +2,6 @@ * Copyright(c) 2019 Intel Corporation */ -#include #include #include @@ -28,13 +27,15 @@ struct socket_map { }; struct cpu_map { - unsigned int socket_count; unsigned int lcore_count; + unsigned int socket_count; + unsigned int cpu_count; struct lcore_map lcores[RTE_MAX_LCORE]; struct socket_map sockets[RTE_MAX_NUMA_NODES]; + GROUP_AFFINITY cpus[CPU_SETSIZE]; }; -static struct cpu_map cpu_map = { 0 }; +static struct cpu_map cpu_map; /* eal_create_cpu_map() is called before logging is initialized */ static void @@ -48,13 +49,111 @@ log_early(const char *format, ...) va_end(va); } +static int +eal_query_group_affinity(void) +{ + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *infos = NULL; + DWORD infos_size = 0; + int ret = 0; + + if (!GetLogicalProcessorInformationEx(RelationGroup, NULL, + &infos_size)) { + DWORD error = GetLastError(); + if (error != ERROR_INSUFFICIENT_BUFFER) { + log_early("Cannot get group information size, " + "error %lu\n", error); + rte_errno = EINVAL; + ret = -1; + goto cleanup; + } + } + + infos = malloc(infos_size); + if (infos == NULL) { + log_early("Cannot allocate memory for NUMA node information\n"); + rte_errno = ENOMEM; + ret = -1; + goto cleanup; + } + + if (!GetLogicalProcessorInformationEx(RelationGroup, infos, + &infos_size)) { + log_early("Cannot get group information, error %lu\n", + GetLastError()); + rte_errno = EINVAL; + ret = -1; + goto cleanup; + } + + cpu_map.cpu_count = 0; + USHO
[dpdk-dev] [PATCH v6 02/10] eal: add thread attributes
From: Narcisa Vasile Implement thread attributes for: * thread affinity * thread priority Implement functions for managing thread attributes. Signed-off-by: Narcisa Vasile --- lib/librte_eal/common/rte_thread.c| 53 lib/librte_eal/include/rte_thread.h | 82 +++ lib/librte_eal/include/rte_thread_types.h | 3 + .../include/rte_windows_thread_types.h| 3 + lib/librte_eal/windows/rte_thread.c | 56 + 5 files changed, 197 insertions(+) diff --git a/lib/librte_eal/common/rte_thread.c b/lib/librte_eal/common/rte_thread.c index 5ec382949..0bd1b115d 100644 --- a/lib/librte_eal/common/rte_thread.c +++ b/lib/librte_eal/common/rte_thread.c @@ -29,6 +29,59 @@ rte_thread_equal(rte_thread_t t1, rte_thread_t t2) return pthread_equal(t1, t2); } +int +rte_thread_attr_init(rte_thread_attr_t *attr) +{ + if (attr == NULL) { + RTE_LOG(DEBUG, EAL, "Invalid thread attributes parameter\n"); + return EINVAL; + } + + CPU_ZERO(&attr->cpuset); + attr->priority = RTE_THREAD_PRIORITY_NORMAL; + + return 0; +} + +int +rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, +rte_cpuset_t *cpuset) +{ + if (thread_attr == NULL || cpuset == NULL) { + RTE_LOG(DEBUG, EAL, "Invalid thread attributes parameter\n"); + return EINVAL; + } + thread_attr->cpuset = *cpuset; + return 0; +} + +int +rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, +rte_cpuset_t *cpuset) +{ + if ((thread_attr == NULL) || (cpuset == NULL)) { + RTE_LOG(DEBUG, EAL, "Invalid thread attributes parameter\n"); + return EINVAL; + } + + *cpuset = thread_attr->cpuset; + return 0; +} + +int +rte_thread_attr_set_priority(rte_thread_attr_t *thread_attr, +enum rte_thread_priority priority) +{ + if (thread_attr == NULL) { + RTE_LOG(DEBUG, EAL, + "Unable to set priority attribute, invalid parameter\n"); + return EINVAL; + } + + thread_attr->priority = priority; + return 0; +} + int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { diff --git a/lib/librte_eal/include/rte_thread.h b/lib/librte_eal/include/rte_thread.h index cbc07f739..bfdd8e1b1 100644 --- a/lib/librte_eal/include/rte_thread.h +++ b/lib/librte_eal/include/rte_thread.h @@ -28,6 +28,19 @@ extern "C" { #include #endif +enum rte_thread_priority { + RTE_THREAD_PRIORITY_NORMAL= EAL_THREAD_PRIORITY_NORMAL, + RTE_THREAD_PRIORITY_REALTIME_CRITICAL = EAL_THREAD_PRIORITY_REALTIME_CIRTICAL, + /* +* This enum can be extended to allow more priority levels. +*/ +}; + +typedef struct { + enum rte_thread_priority priority; + rte_cpuset_t cpuset; +} rte_thread_attr_t; + /** * TLS key type, an opaque pointer. */ @@ -60,6 +73,75 @@ rte_thread_t rte_thread_self(void); __rte_experimental int rte_thread_equal(rte_thread_t t1, rte_thread_t t2); +/** + * Initialize the attributes of a thread. + * These attributes can be passed to the rte_thread_create() function + * that will create a new thread and set its attributes according to attr; + * + * @param attr + * Thread attributes to initialize. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_attr_init(rte_thread_attr_t *attr); + +/** + * Set the CPU affinity value in the thread attributes pointed to + * by 'thread_attr'. + * + * @param thread_attr + * Points to the thread attributes in which affinity will be updated. + * + * @param cpuset + * Points to the value of the affinity to be set. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, +rte_cpuset_t *cpuset); + +/** + * Get the value of CPU affinity that is set in the thread attributes pointed + * to by 'thread_attr'. + * + * @param thread_attr + * Points to the thread attributes from which affinity will be retrieved. + * + * @param cpuset + * Pointer to the memory that will store the affinity. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, +rte_cpuset_t *cpuset); + +/** + * Set the thread priority value in the thread attributes pointed to + * by 'thread_attr'. + * + * @param thread_attr + * Points to the thread attributes in which priority will be updated. + * + * @param priority + * Points to the value of the priority to be set. + * + * @return + *
[dpdk-dev] [PATCH v6 07/10] eal: implement functions for mutex management
From: Narcisa Vasile Add functions for mutex init, destroy, lock, unlock. Signed-off-by: Narcisa Vasile --- lib/librte_eal/common/rte_thread.c| 24 + lib/librte_eal/include/rte_thread.h | 53 +++ lib/librte_eal/include/rte_thread_types.h | 3 ++ .../include/rte_windows_thread_types.h| 1 + lib/librte_eal/windows/rte_thread.c | 28 ++ 5 files changed, 109 insertions(+) diff --git a/lib/librte_eal/common/rte_thread.c b/lib/librte_eal/common/rte_thread.c index 29d38d193..8e963ed65 100644 --- a/lib/librte_eal/common/rte_thread.c +++ b/lib/librte_eal/common/rte_thread.c @@ -220,6 +220,30 @@ rte_thread_join(rte_thread_t thread_id, int *value_ptr) return 0; } +int +rte_thread_mutex_init(rte_thread_mutex_t *mutex) +{ + return pthread_mutex_init(mutex, NULL); +} + +int +rte_thread_mutex_lock(rte_thread_mutex_t *mutex) +{ + return pthread_mutex_lock(mutex); +} + +int +rte_thread_mutex_unlock(rte_thread_mutex_t *mutex) +{ + return pthread_mutex_unlock(mutex); +} + +int +rte_thread_mutex_destroy(rte_thread_mutex_t *mutex) +{ + return pthread_mutex_destroy(mutex); +} + int rte_thread_cancel(rte_thread_t thread_id) { /* diff --git a/lib/librte_eal/include/rte_thread.h b/lib/librte_eal/include/rte_thread.h index fa643433a..4ec7feca8 100644 --- a/lib/librte_eal/include/rte_thread.h +++ b/lib/librte_eal/include/rte_thread.h @@ -240,6 +240,58 @@ int rte_thread_create(rte_thread_t *thread_id, __rte_experimental int rte_thread_join(rte_thread_t thread_id, int *value_ptr); +/** + * Initializes a mutex. + * + * @param mutex + *The mutex to be initialized. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_mutex_init(rte_thread_mutex_t *mutex); + +/** + * Locks a mutex. + * + * @param mutex + *The mutex to be locked. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_mutex_lock(rte_thread_mutex_t *mutex); + +/** + * Unlocks a mutex. + * + * @param mutex + *The mutex to be unlocked. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_mutex_unlock(rte_thread_mutex_t *mutex); + +/** + * Releases all resources associated with a mutex. + * + * @param mutex + *The mutex to be uninitialized. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_mutex_destroy(rte_thread_mutex_t *mutex); + /** * Terminates a thread. * @@ -259,6 +311,7 @@ int rte_thread_cancel(rte_thread_t thread_id); * * @param cpusetp * Pointer to CPU affinity to set. + * * @return * On success, return 0; otherwise return -1; */ diff --git a/lib/librte_eal/include/rte_thread_types.h b/lib/librte_eal/include/rte_thread_types.h index a884daf17..37bc7af2b 100644 --- a/lib/librte_eal/include/rte_thread_types.h +++ b/lib/librte_eal/include/rte_thread_types.h @@ -7,9 +7,12 @@ #include +#define RTE_THREAD_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER + #define EAL_THREAD_PRIORITY_NORMAL 0 #define EAL_THREAD_PRIORITY_REALTIME_CIRTICAL99 typedef pthread_t rte_thread_t; +typedef pthread_mutex_t rte_thread_mutex_t; #endif /* _RTE_THREAD_TYPES_H_ */ diff --git a/lib/librte_eal/windows/include/rte_windows_thread_types.h b/lib/librte_eal/windows/include/rte_windows_thread_types.h index 8cb4b3856..47c6b2664 100644 --- a/lib/librte_eal/windows/include/rte_windows_thread_types.h +++ b/lib/librte_eal/windows/include/rte_windows_thread_types.h @@ -11,5 +11,6 @@ #define EAL_THREAD_PRIORITY_REALTIME_CIRTICAL THREAD_PRIORITY_TIME_CRITICAL typedef DWORD rte_thread_t; +typedef CRITICAL_SECTIONrte_thread_mutex_t; #endif /* _RTE_THREAD_TYPES_H_ */ diff --git a/lib/librte_eal/windows/rte_thread.c b/lib/librte_eal/windows/rte_thread.c index 86bbd7bc2..c1221c2ea 100644 --- a/lib/librte_eal/windows/rte_thread.c +++ b/lib/librte_eal/windows/rte_thread.c @@ -421,6 +421,34 @@ rte_thread_join(rte_thread_t thread_id, int *value_ptr) return ret; } +int +rte_thread_mutex_init(rte_thread_mutex_t *mutex) +{ + InitializeCriticalSection(mutex); + return 0; +} + +int +rte_thread_mutex_lock(rte_thread_mutex_t *mutex) +{ + EnterCriticalSection(mutex); + return 0; +} + +int +rte_thread_mutex_unlock(rte_thread_mutex_t *mutex) +{ + LeaveCriticalSection(mutex); + return 0; +} + +int +rte_thread_mutex_destroy(rte_thread_mutex_t *mutex) +{ + DeleteCriticalSection(mutex); + return 0; +} + int rte_thread_cancel(rte_thread_t thread_id) { -- 2.30.0.vfs.0.2
[dpdk-dev] [PATCH v6 08/10] eal: implement functions for thread barrier management
From: Narcisa Vasile Add functions for barrier init, destroy, wait. Signed-off-by: Narcisa Vasile --- lib/librte_eal/common/rte_thread.c| 16 +++ lib/librte_eal/include/rte_thread.h | 46 +++ lib/librte_eal/include/rte_thread_types.h | 2 + .../include/rte_windows_thread_types.h| 3 ++ lib/librte_eal/windows/rte_thread.c | 27 +++ 5 files changed, 94 insertions(+) diff --git a/lib/librte_eal/common/rte_thread.c b/lib/librte_eal/common/rte_thread.c index 8e963ed65..d23d3b868 100644 --- a/lib/librte_eal/common/rte_thread.c +++ b/lib/librte_eal/common/rte_thread.c @@ -244,6 +244,22 @@ rte_thread_mutex_destroy(rte_thread_mutex_t *mutex) return pthread_mutex_destroy(mutex); } +int +rte_thread_barrier_init(rte_thread_barrier_t *barrier, int count) +{ + return pthread_barrier_init(barrier, NULL, count); +} + +int rte_thread_barrier_wait(rte_thread_barrier_t *barrier) +{ + return pthread_barrier_wait(barrier); +} + +int rte_thread_barrier_destroy(rte_thread_barrier_t *barrier) +{ + return pthread_barrier_destroy(barrier); +} + int rte_thread_cancel(rte_thread_t thread_id) { /* diff --git a/lib/librte_eal/include/rte_thread.h b/lib/librte_eal/include/rte_thread.h index 4ec7feca8..ddde3f3f8 100644 --- a/lib/librte_eal/include/rte_thread.h +++ b/lib/librte_eal/include/rte_thread.h @@ -292,6 +292,52 @@ int rte_thread_mutex_unlock(rte_thread_mutex_t *mutex); __rte_experimental int rte_thread_mutex_destroy(rte_thread_mutex_t *mutex); +/** + * Initializes a synchronization barrier. + * + * @param barrier + *A pointer that references the newly created 'barrier' object. + * + * @param count + *The number of threads that must enter the barrier before + *the threads can continue execution. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_barrier_init(rte_thread_barrier_t *barrier, int count); + +/** + * Causes the calling thread to wait at the synchronization barrier 'barrier'. + * + * @param barrier + *The barrier used for synchronizing the threads. + * + * @return + * Return RTE_THREAD_BARRIER_SERIAL_THREAD for the thread synchronized + * at the barrier. + * Return 0 for all other threads. + * Return a positive errno-style error number, in case of failure. + */ +__rte_experimental +int rte_thread_barrier_wait(rte_thread_barrier_t *barrier); + +/** + * Releases all resources used by a synchronization barrier + * and uninitializes it. + * + * @param barrier + *The barrier to be destroyed. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_barrier_destroy(rte_thread_barrier_t *barrier); + /** * Terminates a thread. * diff --git a/lib/librte_eal/include/rte_thread_types.h b/lib/librte_eal/include/rte_thread_types.h index 37bc7af2b..b055bbf67 100644 --- a/lib/librte_eal/include/rte_thread_types.h +++ b/lib/librte_eal/include/rte_thread_types.h @@ -7,6 +7,7 @@ #include +#define RTE_THREAD_BARRIER_SERIAL_THREAD PTHREAD_BARRIER_SERIAL_THREAD #define RTE_THREAD_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER #define EAL_THREAD_PRIORITY_NORMAL 0 @@ -14,5 +15,6 @@ typedef pthread_t rte_thread_t; typedef pthread_mutex_t rte_thread_mutex_t; +typedef pthread_barrier_t rte_thread_barrier_t; #endif /* _RTE_THREAD_TYPES_H_ */ diff --git a/lib/librte_eal/windows/include/rte_windows_thread_types.h b/lib/librte_eal/windows/include/rte_windows_thread_types.h index 47c6b2664..b6209e6eb 100644 --- a/lib/librte_eal/windows/include/rte_windows_thread_types.h +++ b/lib/librte_eal/windows/include/rte_windows_thread_types.h @@ -7,10 +7,13 @@ #include +#define RTE_THREAD_BARRIER_SERIAL_THREAD TRUE + #define EAL_THREAD_PRIORITY_NORMAL THREAD_PRIORITY_NORMAL #define EAL_THREAD_PRIORITY_REALTIME_CIRTICAL THREAD_PRIORITY_TIME_CRITICAL typedef DWORD rte_thread_t; typedef CRITICAL_SECTIONrte_thread_mutex_t; +typedef SYNCHRONIZATION_BARRIER rte_thread_barrier_t; #endif /* _RTE_THREAD_TYPES_H_ */ diff --git a/lib/librte_eal/windows/rte_thread.c b/lib/librte_eal/windows/rte_thread.c index c1221c2ea..62849fc61 100644 --- a/lib/librte_eal/windows/rte_thread.c +++ b/lib/librte_eal/windows/rte_thread.c @@ -449,6 +449,33 @@ rte_thread_mutex_destroy(rte_thread_mutex_t *mutex) return 0; } +int +rte_thread_barrier_init(rte_thread_barrier_t *barrier, int count) +{ + int ret = 0; + + if (!InitializeSynchronizationBarrier(barrier, count, -1)) { + ret = rte_thread_translate_win32_error(); + RTE_LOG_WIN32_ERR("InitializeSynchronizationBarrier()"); + return ret; + } + return 0; +} + +int +rte_thread_b
[dpdk-dev] [PATCH v6 06/10] eal: add thread lifetime management
From: Narcisa Vasile Add function for thread creation, join, canceling. Signed-off-by: Narcisa Vasile --- lib/librte_eal/common/rte_thread.c | 110 lib/librte_eal/include/rte_thread.h | 53 lib/librte_eal/windows/rte_thread.c | 125 3 files changed, 288 insertions(+) diff --git a/lib/librte_eal/common/rte_thread.c b/lib/librte_eal/common/rte_thread.c index 26c5b1f3c..29d38d193 100644 --- a/lib/librte_eal/common/rte_thread.c +++ b/lib/librte_eal/common/rte_thread.c @@ -120,6 +120,116 @@ rte_thread_attr_set_priority(rte_thread_attr_t *thread_attr, return 0; } +int +rte_thread_create(rte_thread_t *thread_id, + const rte_thread_attr_t *thread_attr, + void *(*thread_func)(void *), void *args) +{ + int ret = 0; + pthread_attr_t attr; + pthread_attr_t *attrp = NULL; + struct sched_param param = { + .sched_priority = 0, + }; + int policy = SCHED_OTHER; + + if (thread_attr != NULL) { + ret = pthread_attr_init(&attr); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_init failed\n"); + goto cleanup; + } + + attrp = &attr; + + /* +* Set the inherit scheduler parameter to explicit, +* otherwise the priority attribute is ignored. +*/ + ret = pthread_attr_setinheritsched(attrp, + PTHREAD_EXPLICIT_SCHED); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_setinheritsched failed\n"); + goto cleanup; + } + + /* +* In case a realtime scheduling policy is requested, +* the sched_priority parameter is set to the value stored in +* thread_attr. Otherwise, for the default scheduling policy +* (SCHED_OTHER) sched_priority needs to be initialized to 0. +*/ + if (thread_attr->priority == RTE_THREAD_PRIORITY_REALTIME_CRITICAL) { + policy = SCHED_RR; + param.sched_priority = thread_attr->priority; + } + + ret = pthread_attr_setschedpolicy(attrp, policy); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_setschedpolicy failed\n"); + goto cleanup; + } + + ret = pthread_attr_setschedparam(attrp, ¶m); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_setschedparam failed\n"); + goto cleanup; + } + + ret = pthread_attr_setaffinity_np(attrp, + sizeof(thread_attr->cpuset), + &thread_attr->cpuset); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_setaffinity_np failed\n"); + goto cleanup; + } + } + + ret = pthread_create(thread_id, attrp, thread_func, args); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_create failed\n"); + goto cleanup; + } + +cleanup: + if (attrp != NULL) + pthread_attr_destroy(&attr); + + return ret; +} + +int +rte_thread_join(rte_thread_t thread_id, int *value_ptr) +{ + int ret = 0; + void *res = NULL; + void **pres = NULL; + + if (value_ptr != NULL) + pres = &res; + + ret = pthread_join(thread_id, pres); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_join failed\n"); + return ret; + } + + if (pres != NULL) + *value_ptr = *(int *)(*pres); + + return 0; +} + +int rte_thread_cancel(rte_thread_t thread_id) +{ + /* +* TODO: Behavior is different between POSIX and Windows threads. +* POSIX threads wait for a cancellation point. +* Current Windows emulation kills thread at any point. +*/ + return pthread_cancel(thread_id); +} + int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { diff --git a/lib/librte_eal/include/rte_thread.h b/lib/librte_eal/include/rte_thread.h index f95efb319..fa643433a 100644 --- a/lib/librte_eal/include/rte_thread.h +++ b/lib/librte_eal/include/rte_thread.h @@ -200,6 +200,59 @@ __rte_experimental int rte_thread_attr_set_priority(rte_thread_attr_t *thread_attr, enum rte_thread_priority priority); +/** + * Create a new thread that will invoke the 'thread_func' routine. + * + * @param thread_id + *A pointer that will store the id of the newly created thread. + * + * @param thread_attr + *Attributes that
[dpdk-dev] [PATCH v6 09/10] eal: add EAL argument for setting thread priority
From: Narcisa Vasile Allow the user to choose the thread priority through an EAL command line argument. The user can select the thread priority to be either 'normal' or 'critical': --thread-prio normal --thread-prio realtime Signed-off-by: Narcisa Vasile --- lib/librte_eal/common/eal_common_options.c | 28 +- lib/librte_eal/common/eal_internal_cfg.h | 2 ++ lib/librte_eal/common/eal_options.h| 2 ++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/librte_eal/common/eal_common_options.c b/lib/librte_eal/common/eal_common_options.c index 622c7bc42..287a89a75 100644 --- a/lib/librte_eal/common/eal_common_options.c +++ b/lib/librte_eal/common/eal_common_options.c @@ -106,6 +106,7 @@ eal_long_options[] = { {OPT_TELEMETRY, 0, NULL, OPT_TELEMETRY_NUM}, {OPT_NO_TELEMETRY, 0, NULL, OPT_NO_TELEMETRY_NUM }, {OPT_FORCE_MAX_SIMD_BITWIDTH, 1, NULL, OPT_FORCE_MAX_SIMD_BITWIDTH_NUM}, + {OPT_THREAD_PRIORITY, 1, NULL, OPT_THREAD_PRIORITY_NUM}, /* legacy options that will be removed in future */ {OPT_PCI_BLACKLIST, 1, NULL, OPT_PCI_BLACKLIST_NUM}, @@ -1383,6 +1384,24 @@ eal_parse_simd_bitwidth(const char *arg) return 0; } +static int +eal_parse_thread_priority(const char *arg) +{ + struct internal_config *internal_conf = + eal_get_internal_configuration(); + enum rte_thread_priority priority; + + if (!strncmp("normal", arg, sizeof("normal"))) + priority = RTE_THREAD_PRIORITY_NORMAL; + else if (!strncmp("realtime", arg, sizeof("realtime"))) + priority = RTE_THREAD_PRIORITY_REALTIME_CRITICAL; + else + return -1; + + internal_conf->thread_priority = priority; + return 0; +} + static int eal_parse_base_virtaddr(const char *arg) { @@ -1796,7 +1815,13 @@ eal_parse_common_option(int opt, const char *optarg, return -1; } break; - + case OPT_THREAD_PRIORITY_NUM: + if (eal_parse_thread_priority(optarg) < 0) { + RTE_LOG(ERR, EAL, "invalid parameter for --" + OPT_THREAD_PRIORITY "\n"); + return -1; + } + break; /* don't know what to do, leave this to caller */ default: return 1; @@ -2059,6 +2084,7 @@ eal_common_usage(void) " (can be used multiple times)\n" " --"OPT_VMWARE_TSC_MAP"Use VMware TSC map instead of native RDTSC\n" " --"OPT_PROC_TYPE" Type of this process (primary|secondary|auto)\n" + " --"OPT_THREAD_PRIORITY" Set threads priority (normal|realtime)\n" #ifndef RTE_EXEC_ENV_WINDOWS " --"OPT_SYSLOG"Set syslog facility\n" #endif diff --git a/lib/librte_eal/common/eal_internal_cfg.h b/lib/librte_eal/common/eal_internal_cfg.h index 51dbe86e2..7ab1d0008 100644 --- a/lib/librte_eal/common/eal_internal_cfg.h +++ b/lib/librte_eal/common/eal_internal_cfg.h @@ -93,6 +93,8 @@ struct internal_config { unsigned int no_telemetry; /**< true to disable Telemetry */ struct simd_bitwidth max_simd_bitwidth; /**< max simd bitwidth path to use */ + enum rte_thread_priority thread_priority; + /**< thread priority to configure */ }; void eal_reset_internal_config(struct internal_config *internal_cfg); diff --git a/lib/librte_eal/common/eal_options.h b/lib/librte_eal/common/eal_options.h index 7b348e707..9f5b209f6 100644 --- a/lib/librte_eal/common/eal_options.h +++ b/lib/librte_eal/common/eal_options.h @@ -93,6 +93,8 @@ enum { OPT_NO_TELEMETRY_NUM, #define OPT_FORCE_MAX_SIMD_BITWIDTH "force-max-simd-bitwidth" OPT_FORCE_MAX_SIMD_BITWIDTH_NUM, +#define OPT_THREAD_PRIORITY "thread-prio" + OPT_THREAD_PRIORITY_NUM, /* legacy option that will be removed in future */ #define OPT_PCI_BLACKLIST "pci-blacklist" -- 2.30.0.vfs.0.2
[dpdk-dev] [PATCH v6 05/10] eal: implement thread priority management functions
From: Narcisa Vasile Add function for setting the priority for a thread. Signed-off-by: Narcisa Vasile --- lib/librte_eal/common/rte_thread.c | 25 ++ lib/librte_eal/include/rte_thread.h | 17 +++ lib/librte_eal/windows/rte_thread.c | 76 + 3 files changed, 118 insertions(+) diff --git a/lib/librte_eal/common/rte_thread.c b/lib/librte_eal/common/rte_thread.c index 4f93e3ff1..26c5b1f3c 100644 --- a/lib/librte_eal/common/rte_thread.c +++ b/lib/librte_eal/common/rte_thread.c @@ -42,6 +42,31 @@ int rte_thread_get_affinity_by_id(rte_thread_t threadid, size_t cpuset_size, return pthread_getaffinity_np(threadid, cpuset_size, cpuset); } +int +rte_thread_set_priority(rte_thread_t thread_id, + enum rte_thread_priority priority) +{ + int policy; + struct sched_param param = { + .sched_priority = 0, + }; + + + if (priority == RTE_THREAD_PRIORITY_REALTIME_CRITICAL) { + policy = SCHED_RR; + param.sched_priority = priority; + } else if (priority == RTE_THREAD_PRIORITY_NORMAL) { + policy = SCHED_OTHER; + param.sched_priority = priority; + } else { + RTE_LOG(DEBUG, EAL, "Invalid priority to set." + "Defaulting to priority 'normal'.\n"); + policy = SCHED_OTHER; + } + + return pthread_setschedparam(thread_id, policy, ¶m); +} + int rte_thread_attr_init(rte_thread_attr_t *attr) { diff --git a/lib/librte_eal/include/rte_thread.h b/lib/librte_eal/include/rte_thread.h index 4b1e3dfe8..f95efb319 100644 --- a/lib/librte_eal/include/rte_thread.h +++ b/lib/librte_eal/include/rte_thread.h @@ -114,6 +114,23 @@ __rte_experimental int rte_thread_get_affinity_by_id(rte_thread_t thread_id, size_t cpuset_size, rte_cpuset_t *cpuset); +/** + * 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); + /** * Initialize the attributes of a thread. * These attributes can be passed to the rte_thread_create() function diff --git a/lib/librte_eal/windows/rte_thread.c b/lib/librte_eal/windows/rte_thread.c index 2fa130b1f..f61103bbc 100644 --- a/lib/librte_eal/windows/rte_thread.c +++ b/lib/librte_eal/windows/rte_thread.c @@ -197,6 +197,82 @@ rte_thread_get_affinity_by_id(rte_thread_t thread_id, size_t cpuset_size, return ret; } +static HANDLE +get_process_handle_from_thread_handle(HANDLE thread_handle) +{ + DWORD process_id = 0; + + process_id = GetProcessIdOfThread(thread_handle); + if (process_id == 0) { + RTE_LOG_WIN32_ERR("GetProcessIdOfThread()"); + return NULL; + } + + return OpenProcess(PROCESS_SET_INFORMATION, FALSE, process_id); +} + +int +rte_thread_set_priority(rte_thread_t thread_id, + enum rte_thread_priority priority) +{ + HANDLE thread_handle = NULL; + HANDLE process_handle = NULL; + DWORD priority_class = NORMAL_PRIORITY_CLASS; + int ret = 0; + + thread_handle = OpenThread(THREAD_SET_INFORMATION | + THREAD_QUERY_INFORMATION, FALSE, thread_id); + if (thread_handle == NULL) { + ret = rte_thread_translate_win32_error(); + RTE_LOG_WIN32_ERR("OpenThread()"); + goto cleanup; + } + + switch (priority) { + + case RTE_THREAD_PRIORITY_REALTIME_CRITICAL: + priority_class = REALTIME_PRIORITY_CLASS; + break; + + case RTE_THREAD_PRIORITY_NORMAL: + /* FALLTHROUGH */ + default: + priority_class = NORMAL_PRIORITY_CLASS; + priority = RTE_THREAD_PRIORITY_NORMAL; + break; + } + + process_handle = get_process_handle_from_thread_handle(thread_handle); + if (process_handle == NULL) { + ret = rte_thread_translate_win32_error(); + RTE_LOG_WIN32_ERR("get_process_handle_from_thread_handle()"); + goto cleanup; + } + + if (!SetPriorityClass(process_handle, priority_class)) { + ret = rte_thread_translate_win32_error(); + RTE_LOG_WIN32_ERR("SetPriorityClass()"); + goto cleanup; + } + + if (!SetThreadPriority(thread_handle, priority)) { + ret = rte_thread_translate_win32_error(); + RTE_LOG_WIN32_ERR("SetThreadPriority()"); + goto cleanup; + } + +cleanup: + if (thread_handle != NULL) { + CloseHandle(thread_handle); +
Re: [PATCH] eal/windows: set pthread affinity
On Thu, Jan 20, 2022 at 04:17:49PM -0800, Pallavi Kadam wrote: > Sometimes OS tries to switch the core. So, bind the lcore thread > to a fixed core. > Implement affinity call on Windows similar to Linux. > > Signed-off-by: Qiao Liu > Signed-off-by: Pallavi Kadam > --- > lib/eal/windows/eal.c | 4 > 1 file changed, 4 insertions(+) > > diff --git a/lib/eal/windows/eal.c b/lib/eal/windows/eal.c > index 67db7f099a..ca3c41aaa7 100644 > --- a/lib/eal/windows/eal.c > +++ b/lib/eal/windows/eal.c > @@ -422,6 +422,10 @@ rte_eal_init(int argc, char **argv) > /* create a thread for each lcore */ > if (eal_thread_create(&lcore_config[i].thread_id) != 0) > rte_panic("Cannot create thread\n"); > + ret = pthread_setaffinity_np(lcore_config[i].thread_id, > + sizeof(rte_cpuset_t), &lcore_config[i].cpuset); > + if (ret != 0) > + RTE_LOG(DEBUG, EAL, "Cannot set affinity\n"); > } > Acked-by: Narcisa Vasile
Re: [PATCH v18 8/8] eal: implement functions for mutex management
On Tue, Feb 08, 2022 at 02:21:49AM +, Ananyev, Konstantin wrote: > > > > > + > > > +/** > > > + * Thread mutex representation. > > > > Actually, please scrap that comment. > Obviously it wouldn't work for static variables, > and doesn't make much sense. > Though few thoughts remain: > for posix we probably don't need an indirection and > rte_thread_mutex can be just typedef of pthread_mutex_t. > also for posix we don't need RTE_INIT constructor for each > static mutex initialization. > Something like: > #define RTE_STATIC_INITIALIZED_MUTEX(mx) \ > rte_thread_mutex_t mx = PTHREAD_MUTEX_INITIALIZER > should work, I think. > Konstantin Thank you for reviewing, Konstantin! Some context for the current representation of mutex can be found in v9, patch 7/10 of this patchset. Originally we've typedef'ed the pthread_mutex_t on POSIX, just like you are suggesting here. However, on Windows there's no static initializer similar to the pthread one. Still, we want ABI compatibility and same thread behavior between platforms. The most elegant solution we found was the current representation, as suggested by Dmitry K. I will address your other comments on the other thread. Link to v9: http://patchwork.dpdk.org/project/dpdk/patch/1622850274-6946-8-git-send-email-navas...@linux.microsoft.com/ > >
Re: [PATCH v18 8/8] eal: implement functions for mutex management
On Mon, Feb 07, 2022 at 04:02:54PM +, Ananyev, Konstantin wrote: > > Add functions for mutex init, destroy, lock, unlock, trylock. > > > > Windows does not have a static initializer. Initialization > > is only done through InitializeCriticalSection(). To overcome this, > > RTE_INIT_MUTEX macro is added to replace static initialization > > of mutexes. The macro calls rte_thread_mutex_init(). > > > > Add unit tests to verify that the mutex correctly locks/unlocks > > and protects the data. Check both static and dynamic mutexes. > > Signed-off-by: Narcisa Vasile > > Few comments from me below. > I am not sure was such approach already discussed, > if so - apologies for repetition. > No worries, I appreciate your review! > > --- > > app/test/test_threads.c | 106 +++ > > lib/eal/common/rte_thread.c | 69 +++ > > lib/eal/include/rte_thread.h | 85 > > lib/eal/version.map | 5 ++ > > lib/eal/windows/rte_thread.c | 64 + > > 5 files changed, 329 insertions(+) > > > > }; > > diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c > > index d30a8a7ca3..4a9a1b6e07 100644 > > --- a/lib/eal/common/rte_thread.c > > +++ b/lib/eal/common/rte_thread.c > > @@ -309,6 +309,75 @@ rte_thread_detach(rte_thread_t thread_id) > > return pthread_detach((pthread_t)thread_id.opaque_id); > > } > > > > +int > > +rte_thread_mutex_init(rte_thread_mutex *mutex) > > Don't we need some sort of mutex_attr here too? > To be able to create PROCESS_SHARED mutexes? Attributes are tricky to implement on Windows. In order to not overcomplicate this patchset and since the drivers that need them don't compile on Windows anyway, I decided to omit them from this patchset. In the future, after enabling the new thread API, we can consider implementing them as well. > > > +{ > > + int ret = 0; > > + pthread_mutex_t *m = NULL; > > + > > + RTE_VERIFY(mutex != NULL); > > + > > + m = calloc(1, sizeof(*m)); > > But is that what we really want for the mutexes? > It means actual mutex will always be allocated on process heap, > away from the data it is supposed to guard. > Even if we'll put performance considerations away, > that wouldn't work for MP case. > Is that considered as ok? Are you refering to the fact that all mutexes will be dynamically allocated, due to the static intializer calling _mutex_init() in the background? Why wouldn't it work in the MP case? > > > + if (m == NULL) { > > + RTE_LOG(DEBUG, EAL, "Unable to initialize mutex. Insufficient > > memory!\n"); > > + ret = ENOMEM; > > + goto cleanup; > > + } > > + > > + > > + return ret; > > +} > > + return pthread_mutex_trylock((pthread_mutex_t *)mutex->mutex_id);
Re: [dpdk-dev] [PATCH v16 0/9] eal: Add EAL API for threading
On Tue, Oct 12, 2021 at 06:07:06PM +0200, Thomas Monjalon wrote: > 09/10/2021 09:41, Narcisa Ana Maria Vasile: > > From: Narcisa Vasile > > > > EAL thread API > > > > **Problem Statement** > > DPDK currently uses the pthread interface to create and manage threads. > > Windows does not support the POSIX thread programming model, > > so it currently > > relies on a header file that hides the Windows calls under > > pthread matched interfaces. Given that EAL should isolate the environment > > specifics from the applications and libraries and mediate > > all the communication with the operating systems, a new EAL interface > > is needed for thread management. > > > > **Goals** > > * Introduce a generic EAL API for threading support that will remove > > the current Windows pthread.h shim. > > * Replace references to pthread_* across the DPDK codebase with the new > > RTE_THREAD_* API. > > * Allow users to choose between using the RTE_THREAD_* API or a > > 3rd party thread library through a configuration option. > > > > **Design plan** > > New API main files: > > * rte_thread.h (librte_eal/include) > > * rte_thread.c (librte_eal/windows) > > * rte_thread.c (librte_eal/common) > > Why this file is not in lib/eal/unix/ ? > Thank you Thomas for reviewing these patches! Your guidance is very much appreciated as I want to bring this patchset on the good path towards merging. Based on community meeting discussions, multiple users have requested an option to allow them to use a 3rd party thread library. At the same time, we want to remove the Windows shim that we currently use for threading in DPDK, so we decided to do the following: A new rte_thread_* API is introduced, which will be used uniformly across DPDK. - for unix-based platforms, the code in eal/common will be compiled - for windows, the code in eal/windows will be compiled. For all cases when a 3rd party library is needed, the code from eal/common will be used. For example, if winpthreads or pthreads4w are used, at build time the code from 'eal/common' will be selected and the rte_thread_* API will point to pthread_* functions described by the "pthread.h" header file provided by the 3rd party library. Using a 3rd party library will not require any changes in the DPDK code, except for adding an option in the meson files. Therefore code from common will work both on unix and windows. Nick explains even better in his RFC from that time: [RFC] pthread on Windows - Patchwork (dpdk.org). I will improve the cover letter and the commit messages to better explain this. > > > **A schematic example of the design** > > -- > > lib/librte_eal/include/rte_thread.h > > int rte_thread_create(); > > > > lib/librte_eal/common/rte_thread.c > > int rte_thread_create() > > { > > return pthread_create(); > > } > > > > lib/librte_eal/windows/rte_thread.c > > int rte_thread_create() > > { > > return CreateThread(); > > } > > - > > We must have the same error code, no matter the underlying implementation. > So you cannot return directly pthread or win32 error codes. > The approach here is to translate the Windows errors to POSIX-style ones to have uniformity across the entire threading module. > > > **Thread attributes** > > > > When or after a thread is created, specific characteristics of the thread > > can be adjusted. Given that the thread characteristics that are of interest > > for DPDK applications are affinity and priority, the following structure > > that represents thread attributes has been defined: > > > > typedef struct > > { > > enum rte_thread_priority priority; > > rte_cpuset_t cpuset; > > } rte_thread_attr_t; > > > > The *rte_thread_create()* function can optionally receive > > an rte_thread_attr_t > > object that will cause the thread to be created with the > > affinity and priority > > described by the attributes object. If no rte_thread_attr_t is passed > > (parameter is NULL), the default affinity and priority are used. > > An rte_thread_attr_t object can also be set to the default values > > by calling *rte_thread_attr_init()*. > > > > *Priority* is represented through an enum that currently advertises > > two values for priority: > > - RTE_THREAD_PRIORITY_NORMAL > > - RTE_THREAD_PRIORITY_REALTIME_CRITICAL > > The priority level realtime should never used. > >
Re: [dpdk-dev] [PATCH v16 2/9] eal: add thread attributes
On Tue, Oct 12, 2021 at 06:12:21PM +0200, Thomas Monjalon wrote: > 09/10/2021 09:41, Narcisa Ana Maria Vasile: > > From: Narcisa Vasile > > > > Implement thread attributes for: > > * thread affinity > > * thread priority > > Implement functions for managing thread attributes. > > > > Priority is represented through an enum that allows for two levels: > > - RTE_THREAD_PRIORITY_NORMAL > > - RTE_THREAD_PRIORITY_REALTIME_CRITICAL > > It doesn't say how do you translate these priorites in POSIX and win32. I'll send a new version with a better commit message. Thread priorities on both Linux-based and Windows platforms are similarly constructed from a class/policy + priority value. Currently in DPDK, most threads operate at the OS-default priority level but there are cases when increasing the priority is useful. For example, the Mellanox data path acceleration driver requires realtime thread priority. Similarly, some Windows applications will require elevated priority. For these reasons, EAL will advertise 2 priority levels which are named suggestively "normal" and "realtime_critical" and are computed as follows: For Linux and similar platforms: * EAL "normal" priority corresponds to the (default) SCHED_OTHER policy + a priority value of (sched_get_priority_min(SCHED_OTHER) + sched_get_priority_max(SCHED_OTHER))/2. Note that on Linux the resulting priority value will be 0, in accordance to the docs guidance that mention the value should be 0 for SCHED_OTHER policy. * EAL "realtime" priority corresponds to the SCHED_RR policy + a priority value of sched_get_priority_max(SCHED_RR); For Windows: * EAL "normal" corresponds to class NORMAL_PRIORITY_CLASS + priority THREAD_PRIORITY_NORMAL * EAL "realtime_critical" corresponds to class REALTIME_PRIORITY_CLASS + priority THREAD_PRIORITY_TIME_CRITICAL > > > Affinity is described by the rte_cpuset_t type. > > > > An rte_thread_attr_t object can be set to the default values > > by calling rte_thread_attr_init(). > > > > Signed-off-by: Narcisa Vasile > [...] > > lib/eal/common/rte_thread.c | 46 ++ > > lib/eal/windows/rte_thread.c | 44 + > > These 2 files look like code duplication. > >
Re: [dpdk-dev] [PATCH v16 3/9] eal/windows: translate Windows errors to errno-style errors
On Tue, Oct 12, 2021 at 06:16:19PM +0200, Thomas Monjalon wrote: > 09/10/2021 09:41, Narcisa Ana Maria Vasile: > > From: Narcisa Vasile > > > > Add function to translate Windows error codes to > > errno-style error codes. The possible return values are chosen > > so that we have as much semantical compatibility between platforms as > > possible. > > > > Signed-off-by: Narcisa Vasile > > --- > > lib/eal/common/rte_thread.c | 6 +-- > > lib/eal/include/rte_thread.h | 5 +- > > lib/eal/windows/rte_thread.c | 95 +++- > > 3 files changed, 76 insertions(+), 30 deletions(-) > > > > diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c > > index e1a4d7eae4..27ad1c7eb0 100644 > > --- a/lib/eal/common/rte_thread.c > > +++ b/lib/eal/common/rte_thread.c > > @@ -47,7 +47,7 @@ rte_thread_attr_init(rte_thread_attr_t *attr) > > > > int > > rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, > > -rte_cpuset_t *cpuset) > > + rte_cpuset_t *cpuset) > > { > > RTE_VERIFY(thread_attr != NULL); > > RTE_VERIFY(cpuset != NULL); > > @@ -59,7 +59,7 @@ rte_thread_attr_set_affinity(rte_thread_attr_t > > *thread_attr, > > > > int > > rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, > > -rte_cpuset_t *cpuset) > > + rte_cpuset_t *cpuset) > > { > > RTE_VERIFY(thread_attr != NULL); > > RTE_VERIFY(cpuset != NULL); > > @@ -71,7 +71,7 @@ rte_thread_attr_get_affinity(rte_thread_attr_t > > *thread_attr, > > > > int > > rte_thread_attr_set_priority(rte_thread_attr_t *thread_attr, > > -enum rte_thread_priority priority) > > + enum rte_thread_priority priority) > > Above are unrelated changes. > Thanks, will fix! > > --- a/lib/eal/windows/rte_thread.c > > +++ b/lib/eal/windows/rte_thread.c > > @@ -13,6 +13,54 @@ struct eal_tls_key { > > DWORD thread_index; > > }; > > > > +/* Translates the most common error codes related to threads */ > > +static int > > +thread_translate_win32_error(DWORD error) > > So you decide to adopt POSIX error codes for the DPDK API. OK > > > +{ > > + switch (error) { > > + case ERROR_SUCCESS: > > + return 0; > > + > > + case ERROR_INVALID_PARAMETER: > > + return EINVAL; > > + > > + case ERROR_INVALID_HANDLE: > > + return EFAULT; > > + > > + case ERROR_NOT_ENOUGH_MEMORY: > > + /* FALLTHROUGH */ > > + case ERROR_NO_SYSTEM_RESOURCES: > > + return ENOMEM; > > + > > + case ERROR_PRIVILEGE_NOT_HELD: > > + /* FALLTHROUGH */ > > + case ERROR_ACCESS_DENIED: > > + return EACCES; > > + > > + case ERROR_ALREADY_EXISTS: > > + return EEXIST; > > + > > + case ERROR_POSSIBLE_DEADLOCK: > > + return EDEADLK; > > + > > + case ERROR_INVALID_FUNCTION: > > + /* FALLTHROUGH */ > > + case ERROR_CALL_NOT_IMPLEMENTED: > > + return ENOSYS; > > + } > > + > > + return EINVAL; > > +} > [...] > > rte_thread_key_create(rte_thread_key *key, > > __rte_unused void (*destructor)(void *)) > > { > > + int ret; > > + > > *key = malloc(sizeof(**key)); > > if ((*key) == NULL) { > > RTE_LOG(DEBUG, EAL, "Cannot allocate TLS key.\n"); > > - rte_errno = ENOMEM; > > - return -1; > > + return ENOMEM; > > } > > Why this change? rte_errno and negative error code are good. > This error could have been handled using rte_errno and negative return, but for consistency, a positive error number is returned. As different platforms have different error codes, the approach here is to translate the Windows error to POSIX-style ones to have uniformity over the values returned. All functions in this thread module return the possible error through the return value. >
Re: [dpdk-dev] [PATCH v16 7/9] eal: implement functions for mutex management
On Tue, Oct 12, 2021 at 06:28:56PM +0200, Thomas Monjalon wrote: > 09/10/2021 09:41, Narcisa Ana Maria Vasile: > > From: Narcisa Vasile > > > > Add functions for mutex init, destroy, lock, unlock, trylock. > > > > Add RTE_STATIC_MUTEX macro to replace static initialization > > of mutexes. > > Windows does not have a static initializer. > > Initialization is only done through InitializeCriticalSection(). > > > > The RTE_STATIC_MUTEX calls into the rte_thread_mutex_init() > > function that performs the actual mutex initialization. > [...] > > --- a/lib/eal/include/rte_thread.h > > +++ b/lib/eal/include/rte_thread.h > > +#define RTE_DECLARE_MUTEX(private_lock) rte_thread_mutex > > private_lock > > + > > +#define RTE_DEFINE_MUTEX(private_lock)\ > > +RTE_INIT(__rte_ ## private_lock ## _init)\ > > +{\ > > + RTE_VERIFY(rte_thread_mutex_init(&private_lock) == 0);\ > > +} > > + > > +#define RTE_STATIC_MUTEX(private_lock)\ > > +static RTE_DECLARE_MUTEX(private_lock);\ > > +RTE_DEFINE_MUTEX(private_lock) > > This is not truly static. > It is a wrapper to init the mutex in the constructor. > Should we rename? > Agreed, I'll rename to RTE_MUTEX_INIT or something like this. Thanks!
Re: [dpdk-dev] [PATCH v16 8/9] eal: implement functions for thread barrier management
On Tue, Oct 12, 2021 at 06:32:09PM +0200, Thomas Monjalon wrote: > 09/10/2021 09:41, Narcisa Ana Maria Vasile: > > From: Narcisa Vasile > > > > Add functions for barrier init, destroy, wait. > > > > A portable type is used to represent a barrier identifier. > > The rte_thread_barrier_wait() function returns the same value > > on all platforms. > > > > Signed-off-by: Narcisa Vasile > > --- > > lib/eal/common/rte_thread.c | 61 > > lib/eal/include/rte_thread.h | 58 ++ > > lib/eal/version.map | 3 ++ > > lib/eal/windows/rte_thread.c | 56 + > > 4 files changed, 178 insertions(+) > > It doesn't need to be part of the API. > The pthread barrier is used only as part of the control thread implementation. > The need disappear if you implement control thread on Windows. > Actually I think I have the implementation already. I've worked at this some time ago, I have this patch: [v4,2/6] eal: add function for control thread creation The issue is I will break ABI so I cannot merge it as part of this patchset. I'll see if I can remove this barrier patch though.
Re: [dpdk-dev] [PATCH v16 9/9] Add unit tests for thread API
On Tue, Oct 12, 2021 at 06:33:16PM +0200, Thomas Monjalon wrote: > 09/10/2021 09:41, Narcisa Ana Maria Vasile: > > From: Narcisa Vasile > > > > As a new API for threading is introduced, > > a set of unit tests have been added to test the new interface. > > The tests verify that: > > * mutexes and barriers behave as expected > > * thread properties are applied correctly > > * the thread id is retrieved correctly > > * thread creation/destruction works properly > > Please make each test part of the patch implementing the feature. > Thanks > Makes sense, but most of these unit tests use rte_thread_create and rte_thread_join to handle the creation and cleanup of the threads that are being tested, so I'm forced to have this test patch at the end. I could still break it up into smaller patches, one for each test category (mutex, attributes, etc) if you want.
[dpdk-dev] [PATCH v17 00/13] eal: Add EAL API for threading
From: Narcisa Vasile EAL thread API **Problem Statement** DPDK currently uses the pthread interface to create and manage threads. Windows does not support the POSIX thread programming model, so it currently relies on a header file that hides the Windows calls under pthread matched interfaces. Given that EAL should isolate the environment specifics from the applications and libraries and mediate all the communication with the operating systems, a new EAL interface is needed for thread management. **Goals** * Introduce a generic EAL API for threading support that will remove the current Windows pthread.h shim. * Replace references to pthread_* across the DPDK codebase with the new RTE_THREAD_* API. * Allow users to choose between using the RTE_THREAD_* API or a 3rd party thread library through a configuration option. **Design plan** New API main files: * rte_thread.h (librte_eal/include) * rte_thread.c (librte_eal/windows) * rte_thread.c (librte_eal/common) **A schematic example of the design** -- lib/librte_eal/include/rte_thread.h int rte_thread_create(); lib/librte_eal/common/rte_thread.c int rte_thread_create() { return pthread_create(); } lib/librte_eal/windows/rte_thread.c int rte_thread_create() { return CreateThread(); } - **Thread attributes** When or after a thread is created, specific characteristics of the thread can be adjusted. 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 require elevated priority to avoid being preempted by other threads on the system. The following structure that represents thread attributes has been defined: typedef struct { enum rte_thread_priority priority; rte_cpuset_t cpuset; } rte_thread_attr_t; The *rte_thread_create()* function can optionally receive an rte_thread_attr_t object that will cause the thread to be created with the affinity and priority described by the attributes object. If no rte_thread_attr_t is passed (parameter is NULL), the default affinity and priority are used. An rte_thread_attr_t object can also be set to the default values by calling *rte_thread_attr_init()*. *Priority* is represented through an enum that currently advertises two values for priority: - RTE_THREAD_PRIORITY_NORMAL - RTE_THREAD_PRIORITY_REALTIME_CRITICAL The enum can be extended to allow for multiple priority levels. rte_thread_set_priority - sets the priority of a thread rte_thread_get_priority - retrieves the priority of a thread from the OS rte_thread_attr_set_priority - updates an rte_thread_attr_t object with a new value for priority *Affinity* is described by the already known “rte_cpuset_t” type. rte_thread_attr_set/get_affinity - sets/gets the affinity field in a rte_thread_attr_t object rte_thread_set/get_affinity – sets/gets the affinity of a thread **Errors** As different platforms have different error codes, the approach here is to translate the Windows error to POSIX-style ones to have uniformity over the values returned. **Future work** The long term plan is for EAL to provide full threading support: * Add support for conditional variables * Additional functionality offered by pthread_* (such as pthread_setname_np, etc.) v17: - Move unrelated changes to the correct patch. - Rename RTE_STATIC_MUTEX to avoid confusion, since the mutex is still dynamically initialized behind the scenes. - Break down the unit tests into smaller patches and reorder them. - Remove duplicated code in header. - Improve commit messages and cover letter. v16: - Fix warning on freebsd by adding cast - Change affinity unit test to consider ases when the requested CPU are not available on the system. - Fix priority unit test to avoid termination of thread before the priority is checked. v15: - Add try_lock mutex functionality. If the mutex is already owned by a different thread, the function returns immediately. Otherwise, the mutex will be acquired. - Add function for getting the priority of a thread. An auxiliary function that translates the OS priority to the EAL accepted ones is added. - Fix unit tests logging, add descriptive asserts that mark test failures. Verify mutex locking, verify barrier return values. Add test for statically initialized mutexes. - Fix Alpine build by removing the use of pthread_attr_set_affinity() and using pthread_set_affinity() after the thread is created. v14: - Remove patch "eal: add EAL argument for setting thread priority" This will be added later when enabling the new threading API. - Remove priority enum value "_UNDEFINED". NORMAL is used as the default. - Fix issue with thread return value. v13: - Fix syntax error
[dpdk-dev] [PATCH v17 01/13] eal: add basic threading functions
From: Narcisa Vasile Use a portable, type-safe representation for the thread identifier. Add functions for comparing thread ids and obtaining the thread id for the current thread. Signed-off-by: Narcisa Vasile --- lib/eal/common/meson.build| 1 + lib/eal/{unix => common}/rte_thread.c | 57 -- lib/eal/include/rte_thread.h | 53 ++-- lib/eal/unix/meson.build | 1 - lib/eal/version.map | 3 ++ lib/eal/windows/rte_thread.c | 58 +-- 6 files changed, 117 insertions(+), 56 deletions(-) rename lib/eal/{unix => common}/rte_thread.c (66%) diff --git a/lib/eal/common/meson.build b/lib/eal/common/meson.build index 917758cc65..6bdc9cd854 100644 --- a/lib/eal/common/meson.build +++ b/lib/eal/common/meson.build @@ -52,5 +52,6 @@ if not is_windows 'hotplug_mp.c', 'malloc_mp.c', 'rte_keepalive.c', +'rte_thread.c' ) endif diff --git a/lib/eal/unix/rte_thread.c b/lib/eal/common/rte_thread.c similarity index 66% rename from lib/eal/unix/rte_thread.c rename to lib/eal/common/rte_thread.c index c72d619ec1..92a7451b0a 100644 --- a/lib/eal/unix/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause * Copyright 2021 Mellanox Technologies, Ltd + * Copyright(c) 2021 Microsoft Corporation */ #include @@ -16,25 +17,41 @@ struct eal_tls_key { pthread_key_t thread_index; }; +rte_thread_t +rte_thread_self(void) +{ + rte_thread_t thread_id; + + thread_id.opaque_id = (uintptr_t)pthread_self(); + + return thread_id; +} + +int +rte_thread_equal(rte_thread_t t1, rte_thread_t t2) +{ + return pthread_equal((pthread_t)t1.opaque_id, (pthread_t)t2.opaque_id); +} + int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { int err; + rte_thread_key k; - *key = malloc(sizeof(**key)); - if ((*key) == NULL) { + k = malloc(sizeof(*k)); + if (k == NULL) { RTE_LOG(DEBUG, EAL, "Cannot allocate TLS key.\n"); - rte_errno = ENOMEM; - return -1; + return EINVAL; } - err = pthread_key_create(&((*key)->thread_index), destructor); - if (err) { + err = pthread_key_create(&(k->thread_index), destructor); + if (err != 0) { RTE_LOG(DEBUG, EAL, "pthread_key_create failed: %s\n", strerror(err)); - free(*key); - rte_errno = ENOEXEC; - return -1; + free(k); + return err; } + *key = k; return 0; } @@ -43,18 +60,16 @@ rte_thread_key_delete(rte_thread_key key) { int err; - if (!key) { + if (key == NULL) { RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); - rte_errno = EINVAL; - return -1; + return EINVAL; } err = pthread_key_delete(key->thread_index); - if (err) { + if (err != 0) { RTE_LOG(DEBUG, EAL, "pthread_key_delete failed: %s\n", strerror(err)); free(key); - rte_errno = ENOEXEC; - return -1; + return err; } free(key); return 0; @@ -65,17 +80,15 @@ rte_thread_value_set(rte_thread_key key, const void *value) { int err; - if (!key) { + if (key == NULL) { RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); - rte_errno = EINVAL; - return -1; + return EINVAL; } err = pthread_setspecific(key->thread_index, value); - if (err) { + if (err != 0) { RTE_LOG(DEBUG, EAL, "pthread_setspecific failed: %s\n", strerror(err)); - rte_errno = ENOEXEC; - return -1; + return err; } return 0; } @@ -83,7 +96,7 @@ rte_thread_value_set(rte_thread_key key, const void *value) void * rte_thread_value_get(rte_thread_key key) { - if (!key) { + if (key == NULL) { RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); rte_errno = EINVAL; return NULL; diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 8be8ed8f36..c9cdeb07aa 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2021 Mellanox Technologies, Ltd + * Copyright(c) 2021 Microsoft Corporation */ +#include #include #include @@ -20,11 +22,45 @@ extern "C" { #endif +#include + +/** + * Thread id descriptor. + */ +typedef struct rte_thread_tag { + uintptr_t opaque_id; /**< thread identifier */ +} rte_thread_t; + /** * TLS key type, an opaque pointer. */ typedef stru
[dpdk-dev] [PATCH v17 02/13] eal: add thread attributes
From: Narcisa Vasile Implement thread attributes for: * thread affinity * thread priority Implement functions for managing thread attributes. Priority is represented through an enum that allows for two levels: - RTE_THREAD_PRIORITY_NORMAL - RTE_THREAD_PRIORITY_REALTIME_CRITICAL Affinity is described by the rte_cpuset_t type. An rte_thread_attr_t object can be set to the default values by calling rte_thread_attr_init(). Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 46 ++ lib/eal/include/rte_thread.h | 91 lib/eal/version.map | 4 ++ lib/eal/windows/rte_thread.c | 44 + 4 files changed, 185 insertions(+) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index 92a7451b0a..27ad1c7eb0 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,51 @@ rte_thread_equal(rte_thread_t t1, rte_thread_t t2) return pthread_equal((pthread_t)t1.opaque_id, (pthread_t)t2.opaque_id); } +int +rte_thread_attr_init(rte_thread_attr_t *attr) +{ + RTE_VERIFY(attr != NULL); + + CPU_ZERO(&attr->cpuset); + attr->priority = RTE_THREAD_PRIORITY_NORMAL; + + return 0; +} + +int +rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, + rte_cpuset_t *cpuset) +{ + RTE_VERIFY(thread_attr != NULL); + RTE_VERIFY(cpuset != NULL); + + thread_attr->cpuset = *cpuset; + + return 0; +} + +int +rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, + rte_cpuset_t *cpuset) +{ + RTE_VERIFY(thread_attr != NULL); + RTE_VERIFY(cpuset != NULL); + + *cpuset = thread_attr->cpuset; + + return 0; +} + +int +rte_thread_attr_set_priority(rte_thread_attr_t *thread_attr, + enum rte_thread_priority priority) +{ + RTE_VERIFY(thread_attr != NULL); + + thread_attr->priority = priority; + return 0; +} + int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index c9cdeb07aa..8a20215a94 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -31,6 +31,28 @@ typedef struct rte_thread_tag { uintptr_t opaque_id; /**< thread identifier */ } 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 */ +}; + +#ifdef RTE_HAS_CPUSET + +/** + * Representation for thread attributes. + */ +typedef struct { + enum rte_thread_priority priority; /**< thread priority */ + rte_cpuset_t cpuset; /**< thread affinity */ +} rte_thread_attr_t; + +#endif /* RTE_HAS_CPUSET */ + /** * TLS key type, an opaque pointer. */ @@ -63,6 +85,75 @@ int rte_thread_equal(rte_thread_t t1, rte_thread_t t2); #ifdef RTE_HAS_CPUSET +/** + * Initialize the attributes of a thread. + * These attributes can be passed to the rte_thread_create() function + * that will create a new thread and set its attributes according to attr. + * + * @param attr + * Thread attributes to initialize. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_attr_init(rte_thread_attr_t *attr); + +/** + * Set the CPU affinity value in the thread attributes pointed to + * by 'thread_attr'. + * + * @param thread_attr + * Points to the thread attributes in which affinity will be updated. + * + * @param cpuset + * Points to the value of the affinity to be set. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, + rte_cpuset_t *cpuset); + +/** + * Get the value of CPU affinity that is set in the thread attributes pointed + * to by 'thread_attr'. + * + * @param thread_attr + * Points to the thread attributes from which affinity will be retrieved. + * + * @param cpuset + * Pointer to the memory that will store the affinity. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, + rte_cpuset_t *cpuset); + +/** + * Set the thread priority value in the thread attributes pointed to + * by 'thread_attr'. + * + * @param thread_attr + * Points to the thread attributes in which priority will be updated. + * + * @param priority + * Points to the value of the priority to be set. + * + * @return + * On success, return 0. + * On failure, return a positive errno-styl
[dpdk-dev] [PATCH v17 03/13] eal/windows: translate Windows errors to errno-style errors
From: Narcisa Vasile Add function to translate Windows error codes to errno-style error codes. The possible return values are chosen so that we have as much semantical compatibility between platforms as possible. Signed-off-by: Narcisa Vasile --- lib/eal/windows/rte_thread.c | 48 1 file changed, 48 insertions(+) diff --git a/lib/eal/windows/rte_thread.c b/lib/eal/windows/rte_thread.c index f65919f46d..c1ecfbd6ae 100644 --- a/lib/eal/windows/rte_thread.c +++ b/lib/eal/windows/rte_thread.c @@ -13,6 +13,54 @@ struct eal_tls_key { DWORD thread_index; }; +/* Translates the most common error codes related to threads */ +static int +thread_translate_win32_error(DWORD error) +{ + switch (error) { + case ERROR_SUCCESS: + return 0; + + case ERROR_INVALID_PARAMETER: + return EINVAL; + + case ERROR_INVALID_HANDLE: + return EFAULT; + + case ERROR_NOT_ENOUGH_MEMORY: + /* FALLTHROUGH */ + case ERROR_NO_SYSTEM_RESOURCES: + return ENOMEM; + + case ERROR_PRIVILEGE_NOT_HELD: + /* FALLTHROUGH */ + case ERROR_ACCESS_DENIED: + return EACCES; + + case ERROR_ALREADY_EXISTS: + return EEXIST; + + case ERROR_POSSIBLE_DEADLOCK: + return EDEADLK; + + case ERROR_INVALID_FUNCTION: + /* FALLTHROUGH */ + case ERROR_CALL_NOT_IMPLEMENTED: + return ENOSYS; + } + + return EINVAL; +} + +static int +thread_log_last_error(const char *message) +{ + DWORD error = GetLastError(); + RTE_LOG(DEBUG, EAL, "GetLastError()=%lu: %s\n", error, message); + + return thread_translate_win32_error(error); +} + rte_thread_t rte_thread_self(void) { -- 2.31.0.vfs.0.1
[dpdk-dev] [PATCH v17 04/13] eal: implement functions for thread affinity management
From: Narcisa Vasile Implement functions for getting/setting thread affinity. Threads can be pinned to specific cores by setting their affinity attribute. Signed-off-by: Narcisa Vasile Signed-off-by: Dmitry Malloy --- lib/eal/common/rte_thread.c | 16 lib/eal/include/rte_thread.h | 36 +++ lib/eal/version.map | 2 + lib/eal/windows/eal_lcore.c | 176 +- lib/eal/windows/eal_windows.h | 10 ++ lib/eal/windows/rte_thread.c | 125 +++- 6 files changed, 319 insertions(+), 46 deletions(-) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index 27ad1c7eb0..73b7b3141c 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -34,6 +34,22 @@ rte_thread_equal(rte_thread_t t1, rte_thread_t t2) return pthread_equal((pthread_t)t1.opaque_id, (pthread_t)t2.opaque_id); } +int +rte_thread_set_affinity_by_id(rte_thread_t thread_id, + const rte_cpuset_t *cpuset) +{ + return pthread_setaffinity_np((pthread_t)thread_id.opaque_id, + sizeof(*cpuset), cpuset); +} + +int +rte_thread_get_affinity_by_id(rte_thread_t thread_id, + rte_cpuset_t *cpuset) +{ + return pthread_getaffinity_np((pthread_t)thread_id.opaque_id, + sizeof(*cpuset), cpuset); +} + int rte_thread_attr_init(rte_thread_attr_t *attr) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 8a20215a94..5b100cafda 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -85,6 +85,42 @@ int rte_thread_equal(rte_thread_t t1, rte_thread_t t2); #ifdef RTE_HAS_CPUSET +/** + * Set the affinity of thread 'thread_id' to the cpu set + * specified by 'cpuset'. + * + * @param thread_id + *Id of the thread for which to set the affinity. + * + * @param cpuset + * Pointer to CPU affinity to set. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_set_affinity_by_id(rte_thread_t thread_id, + const rte_cpuset_t *cpuset); + +/** + * Get the affinity of thread 'thread_id' and store it + * in 'cpuset'. + * + * @param thread_id + *Id of the thread for which to get the affinity. + * + * @param cpuset + * Pointer for storing the affinity value. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_get_affinity_by_id(rte_thread_t thread_id, + rte_cpuset_t *cpuset); + /** * Initialize the attributes of a thread. * These attributes can be passed to the rte_thread_create() function diff --git a/lib/eal/version.map b/lib/eal/version.map index 3fc33fcd70..193a79a4d2 100644 --- a/lib/eal/version.map +++ b/lib/eal/version.map @@ -427,6 +427,8 @@ EXPERIMENTAL { rte_thread_attr_get_affinity; rte_thread_attr_set_affinity; rte_thread_attr_set_priority; + rte_thread_get_affinity_by_id; + rte_thread_set_affinity_by_id; }; INTERNAL { diff --git a/lib/eal/windows/eal_lcore.c b/lib/eal/windows/eal_lcore.c index 476c2d2bdf..295af50698 100644 --- a/lib/eal/windows/eal_lcore.c +++ b/lib/eal/windows/eal_lcore.c @@ -2,7 +2,6 @@ * Copyright(c) 2019 Intel Corporation */ -#include #include #include @@ -27,13 +26,15 @@ struct socket_map { }; struct cpu_map { - unsigned int socket_count; unsigned int lcore_count; + unsigned int socket_count; + unsigned int cpu_count; struct lcore_map lcores[RTE_MAX_LCORE]; struct socket_map sockets[RTE_MAX_NUMA_NODES]; + GROUP_AFFINITY cpus[CPU_SETSIZE]; }; -static struct cpu_map cpu_map = { 0 }; +static struct cpu_map cpu_map; /* eal_create_cpu_map() is called before logging is initialized */ static void @@ -47,13 +48,118 @@ log_early(const char *format, ...) va_end(va); } +static int +eal_query_group_affinity(void) +{ + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *infos = NULL; + unsigned int *cpu_count = &cpu_map.cpu_count; + DWORD infos_size = 0; + int ret = 0; + USHORT group_count; + KAFFINITY affinity; + USHORT group_no; + unsigned int i; + + if (!GetLogicalProcessorInformationEx(RelationGroup, NULL, + &infos_size)) { + DWORD error = GetLastError(); + if (error != ERROR_INSUFFICIENT_BUFFER) { + log_early("Cannot get group information size, " + "error %lu\n", error); + rte_errno = EINVAL; + ret = -1; + goto cleanup; + } + } + + infos = malloc(infos_size); + if (infos == NULL) { + log_early("Cannot allocate memory for NUMA node information\n"); + rte_errno = ENOMEM; +
[dpdk-dev] [PATCH v17 09/13] app/test: add unit tests for thread lifetime management
From: Narcisa Vasile As a new API for threading is introduced, a set of unit tests have been added to test the new interface. Verify that threads are created and cleaned up correctly. Signed-off-by: Narcisa Vasile --- app/test/test_threads.c | 29 + 1 file changed, 29 insertions(+) diff --git a/app/test/test_threads.c b/app/test/test_threads.c index 53e5892793..9fcae34179 100644 --- a/app/test/test_threads.c +++ b/app/test/test_threads.c @@ -167,6 +167,34 @@ test_thread_attributes_priority(void) return ret; } +static void * +thread_loop_return(void *arg) +{ + RTE_SET_USED(arg); + return NULL; +} + +static int +test_thread_detach(void) +{ + rte_thread_t threads_ids[THREADS_COUNT]; + size_t i; + int ret = 0; + + for (i = 0; i < THREADS_COUNT; ++i) { + ret = rte_thread_create(&threads_ids[i], NULL, + thread_loop_return, NULL); + RTE_TEST_ASSERT(ret == 0, "Failed to create threads!"); + } + + for (i = 0; i < THREADS_COUNT; ++i) { + ret = rte_thread_detach(threads_ids[i]); + RTE_TEST_ASSERT(ret == 0, "Failed to detach thread!"); + } + + return ret; +} + static struct unit_test_suite threads_test_suite = { .suite_name = "threads autotest", .setup = NULL, @@ -175,6 +203,7 @@ static struct unit_test_suite threads_test_suite = { TEST_CASE(test_thread_self), TEST_CASE(test_thread_attributes_affinity), TEST_CASE(test_thread_attributes_priority), + TEST_CASE(test_thread_detach), TEST_CASES_END() } }; -- 2.31.0.vfs.0.1
[dpdk-dev] [PATCH v17 11/13] app/test: add unit tests for barrier
From: Narcisa Vasile As a new API for threading is introduced, a set of unit tests have been added to test the new interface. Verify that the barrier correctly synchronizes all threads. Verify that the threads are unblocked after the required number of threads have called barrier_wait(). Signed-off-by: Narcisa Vasile --- app/test/test_threads.c | 49 + 1 file changed, 49 insertions(+) diff --git a/app/test/test_threads.c b/app/test/test_threads.c index 9fcae34179..00f604ab7e 100644 --- a/app/test/test_threads.c +++ b/app/test/test_threads.c @@ -195,6 +195,54 @@ test_thread_detach(void) return ret; } +struct thread_context { + rte_thread_barrier *barrier; + int barrier_result; +}; + +static void * +thread_loop_barrier(void *arg) +{ + struct thread_context *ctx = arg; + + ctx->barrier_result = rte_thread_barrier_wait(ctx->barrier); + if (ctx->barrier_result > 0) + rte_log(RTE_LOG_DEBUG, threads_logtype_test, "Failed to wait at barrier!"); + + return NULL; +} + +static int +test_thread_barrier(void) +{ + rte_thread_t thread_id; + struct thread_context ctx; + rte_thread_barrier barrier; + int ret = 0; + int result = 0; + + ret = rte_thread_barrier_init(&barrier, 2); + RTE_TEST_ASSERT(ret == 0, "Failed to initialize barrier!"); + + ctx.barrier = &barrier; + ret = rte_thread_create(&thread_id, NULL, thread_loop_barrier, &ctx); + RTE_TEST_ASSERT(ret == 0, "Failed to create thread!"); + + result = rte_thread_barrier_wait(&barrier); + RTE_TEST_ASSERT(result <= 0, "Failed to wait at the barrier!"); + + ret = rte_thread_join(thread_id, NULL); + RTE_TEST_ASSERT(ret == 0, "Failed to join threads!"); + + ret = rte_thread_barrier_destroy(&barrier); + RTE_TEST_ASSERT(ret == 0, "Failed to destroy barrier!"); + + RTE_TEST_ASSERT(ctx.barrier_result <= 0, "Child thread failed to wait at the barrier!"); + RTE_TEST_ASSERT_NOT_EQUAL(ctx.barrier_result, result, "Threads were not blocked at the barrier!"); + + return 0; +} + static struct unit_test_suite threads_test_suite = { .suite_name = "threads autotest", .setup = NULL, @@ -204,6 +252,7 @@ static struct unit_test_suite threads_test_suite = { TEST_CASE(test_thread_attributes_affinity), TEST_CASE(test_thread_attributes_priority), TEST_CASE(test_thread_detach), + TEST_CASE(test_thread_barrier), TEST_CASES_END() } }; -- 2.31.0.vfs.0.1
[dpdk-dev] [PATCH v17 08/13] app/test: add unit tests for thread attributes
From: Narcisa Vasile As a new API for threading is introduced, a set of unit tests have been added to test the new interface. Verify that affinity and priority can be set successfully. Signed-off-by: Narcisa Vasile --- app/test/test_threads.c | 125 1 file changed, 125 insertions(+) diff --git a/app/test/test_threads.c b/app/test/test_threads.c index e7ee50741c..53e5892793 100644 --- a/app/test/test_threads.c +++ b/app/test/test_threads.c @@ -44,12 +44,137 @@ test_thread_self(void) return 0; } +struct thread_affinity_ctx { + rte_cpuset_t *cpuset; + unsigned int result; +}; + +static void * +thread_loop_attributes_affinity(void *arg) +{ + struct thread_affinity_ctx *ctx = arg; + rte_cpuset_t cpuset; + size_t i; + + ctx->result = 0; + + CPU_ZERO(&cpuset); + if (rte_thread_get_affinity_by_id(rte_thread_self(), &cpuset) != 0) { + ctx->result = 1; + rte_log(RTE_LOG_DEBUG, threads_logtype_test, "Failed to get thread affinity!"); + return NULL; + } + + /* +* Check that the thread is not running on CPUs which were not +* specified in the affinity mask. Note that the CPU mask +* retrieved above can be different than the original mask specified +* with rte_thread_attr_set_affinity(), since some CPUs may not be +* available on the system. +*/ + for (i = 0; i < CPU_SETSIZE; ++i) { + if (!CPU_ISSET(i, ctx->cpuset) && CPU_ISSET(i, &cpuset)) { + ctx->result = 1; + rte_log(RTE_LOG_DEBUG, threads_logtype_test, "CPU %zu should not be set for this thread!\n", + i); + return NULL; + } + } + + return NULL; +} + +static int +test_thread_attributes_affinity(void) +{ + rte_thread_t threads_ids[THREADS_COUNT]; + struct thread_affinity_ctx ctx[THREADS_COUNT] = {}; + rte_thread_attr_t attr; + rte_cpuset_t cpuset; + size_t i; + int ret = 0; + + ret = rte_thread_attr_init(&attr); + RTE_TEST_ASSERT(ret == 0, "Failed to initialize thread attributes!"); + + CPU_ZERO(&cpuset); + ret = rte_thread_get_affinity_by_id(rte_thread_self(), &cpuset); + RTE_TEST_ASSERT(ret == 0, "Failed to get main thread affinity!"); + + ret = rte_thread_attr_set_affinity(&attr, &cpuset); + RTE_TEST_ASSERT(ret == 0, "Failed to set thread attributes!"); + + for (i = 0; i < THREADS_COUNT; ++i) { + ctx[i].cpuset = &cpuset; + ret = rte_thread_create(&threads_ids[i], &attr, + thread_loop_attributes_affinity, &ctx[i]); + RTE_TEST_ASSERT(ret == 0, "Failed to create threads!"); + } + + for (i = 0; i < THREADS_COUNT; ++i) { + ret = rte_thread_join(threads_ids[i], NULL); + RTE_TEST_ASSERT(ret == 0, "Failed to join threads!"); + + RTE_TEST_ASSERT_EQUAL(ctx[i].result, 0, "Unexpected thread affinity!"); + } + + return ret; +} + +static void * +thread_loop_priority(void *arg) +{ + int ret; + enum rte_thread_priority priority; + int *result = arg; + + *result = 1; + ret = rte_thread_get_priority(rte_thread_self(), &priority); + if (ret != 0 || priority != RTE_THREAD_PRIORITY_NORMAL) + *result = 2; + + return NULL; +} + +static int +test_thread_attributes_priority(void) +{ + rte_thread_t threads_ids[THREADS_COUNT]; + rte_thread_attr_t attr; + size_t i; + int ret = 0; + int results[THREADS_COUNT] = {}; + + ret = rte_thread_attr_init(&attr); + RTE_TEST_ASSERT(ret == 0, "Failed to initialize thread attributes!"); + + ret = rte_thread_attr_set_priority(&attr, RTE_THREAD_PRIORITY_NORMAL); + RTE_TEST_ASSERT(ret == 0, "Failed to set thread priority!"); + + for (i = 0; i < THREADS_COUNT; ++i) { + ret = rte_thread_create(&threads_ids[i], &attr, + thread_loop_priority, &results[i]); + RTE_TEST_ASSERT(ret == 0, "Failed to create threads!"); + } + + for (i = 0; i < THREADS_COUNT; ++i) { + ret = rte_thread_join(threads_ids[i], NULL); + RTE_TEST_ASSERT(ret == 0, "Failed to join threads!"); + + RTE_TEST_ASSERT_EQUAL(results[i], 1, "Unexpected priority value!"); + } + + return ret; +} + static struct unit_test_suite threads_test_suite = { .suite_name = "threads autotest", .setup = NULL, .teardown = NULL, .unit_test_cases = { TEST_CASE(test_thread_self), + TEST_CASE(test_thread_attributes_affinity), + TEST_CASE(test_thread_attributes_priority), TEST_CASES_END()
[dpdk-dev] [PATCH v17 05/13] eal: implement thread priority management functions
From: Narcisa Vasile 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 * priority THREAD_PRIORITY_TIME_CRITICAL Note that on Linux the resulting priority value will be 0, in accordance to the docs that mention the value should be 0 for SCHED_OTHER policy. Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 97 ++ lib/eal/include/rte_thread.h | 34 ++ lib/eal/version.map | 2 + lib/eal/windows/rte_thread.c | 127 +++ 4 files changed, 260 insertions(+) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index 73b7b3141c..7ab08561a5 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -50,6 +50,103 @@ rte_thread_get_affinity_by_id(rte_thread_t thread_id, sizeof(*cpuset), cpuset); } +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; +} + +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; + + 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_attr_init(rte_thread_attr_t *attr) { diff --g
[dpdk-dev] [PATCH v17 07/13] app/test: add unit tests for rte_thread_self
From: Narcisa Vasile As a new API for threading is introduced, a set of unit tests have been added to test the new interface. Test the rte_thread_self() functionality used to retrieve the thread id. Signed-off-by: Narcisa Vasile --- app/test/meson.build| 2 ++ app/test/test_threads.c | 63 + 2 files changed, 65 insertions(+) create mode 100644 app/test/test_threads.c diff --git a/app/test/meson.build b/app/test/meson.build index 96670c3504..9fd34459e9 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -146,6 +146,7 @@ test_sources = files( 'test_tailq.c', 'test_thash.c', 'test_thash_perf.c', +'test_threads.c', 'test_timer.c', 'test_timer_perf.c', 'test_timer_racecond.c', @@ -287,6 +288,7 @@ fast_tests = [ ['reorder_autotest', true], ['service_autotest', true], ['thash_autotest', true], +['threads_autotest', true], ['trace_autotest', true], ] diff --git a/app/test/test_threads.c b/app/test/test_threads.c new file mode 100644 index 00..e7ee50741c --- /dev/null +++ b/app/test/test_threads.c @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) 2021 Microsoft. + */ + +#include +#include + +#include "test.h" + +#define THREADS_COUNT 20 + +RTE_LOG_REGISTER(threads_logtype_test, test.threads, INFO); + +static void * +thread_loop_self(void *arg) +{ + rte_thread_t *id = arg; + + *id = rte_thread_self(); + + return NULL; +} + +static int +test_thread_self(void) +{ + rte_thread_t threads_ids[THREADS_COUNT]; + rte_thread_t self_ids[THREADS_COUNT] = {}; + int ret; + int i; + + for (i = 0; i < THREADS_COUNT; ++i) { + ret = rte_thread_create(&threads_ids[i], NULL, thread_loop_self, + &self_ids[i]); + RTE_TEST_ASSERT(ret == 0, "Failed to create threads!"); + } + + for (i = 0; i < THREADS_COUNT; ++i) { + RTE_TEST_ASSERT(rte_thread_join(threads_ids[i], NULL) == 0, "Failed to join thread!"); + RTE_TEST_ASSERT_EQUAL(threads_ids[i].opaque_id, + self_ids[i].opaque_id, "Unexpected thread id!"); + } + + return 0; +} + +static struct unit_test_suite threads_test_suite = { + .suite_name = "threads autotest", + .setup = NULL, + .teardown = NULL, + .unit_test_cases = { + TEST_CASE(test_thread_self), + TEST_CASES_END() + } +}; + +static int +test_threads(void) +{ + return unit_test_suite_runner(&threads_test_suite); +} + +REGISTER_TEST_COMMAND(threads_autotest, test_threads); -- 2.31.0.vfs.0.1
[dpdk-dev] [PATCH v17 06/13] eal: add thread lifetime management
From: Narcisa Vasile Add functions for thread creation, joining, detaching. The *rte_thread_create()* function can optionally receive an rte_thread_attr_t object that will cause the thread to be created with the affinity and priority described by the attributes object. If no rte_thread_attr_t is passed (parameter is NULL), the default affinity and priority are used. On Windows, the function executed by a thread when the thread starts is represeneted by a function pointer of type DWORD (*func) (void*). On other platforms, the function pointer is a void* (*func) (void*). Performing a cast between these two types of function pointers to uniformize the API on all platforms may result in undefined behavior. TO fix this issue, a wrapper that respects the signature required by CreateThread() has been created on Windows. Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 103 lib/eal/include/rte_thread.h| 55 + lib/eal/version.map | 3 + lib/eal/windows/include/sched.h | 2 +- lib/eal/windows/rte_thread.c| 134 5 files changed, 296 insertions(+), 1 deletion(-) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index 7ab08561a5..b3a9d4b47e 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -192,6 +192,109 @@ rte_thread_attr_set_priority(rte_thread_attr_t *thread_attr, return 0; } +int +rte_thread_create(rte_thread_t *thread_id, + const rte_thread_attr_t *thread_attr, + rte_thread_func thread_func, void *args) +{ + int ret = 0; + pthread_attr_t attr; + pthread_attr_t *attrp = NULL; + struct sched_param param = { + .sched_priority = 0, + }; + int policy = SCHED_OTHER; + + if (thread_attr != NULL) { + ret = pthread_attr_init(&attr); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_init failed\n"); + goto cleanup; + } + + attrp = &attr; + + /* +* Set the inherit scheduler parameter to explicit, +* otherwise the priority attribute is ignored. +*/ + ret = pthread_attr_setinheritsched(attrp, + PTHREAD_EXPLICIT_SCHED); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_setinheritsched failed\n"); + goto cleanup; + } + + ret = thread_map_priority_to_os_value(thread_attr->priority, + ¶m.sched_priority, &policy); + if (ret != 0) + goto cleanup; + + ret = pthread_attr_setschedpolicy(attrp, policy); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_setschedpolicy failed\n"); + goto cleanup; + } + + ret = pthread_attr_setschedparam(attrp, ¶m); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_setschedparam failed\n"); + goto cleanup; + } + } + + ret = pthread_create((pthread_t *)&thread_id->opaque_id, attrp, + thread_func, args); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_create failed\n"); + goto cleanup; + } + + if (thread_attr != NULL && CPU_COUNT(&thread_attr->cpuset) > 0) { + ret = pthread_setaffinity_np((pthread_t)thread_id->opaque_id, + sizeof(thread_attr->cpuset), + &thread_attr->cpuset); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_setaffinity_np failed\n"); + goto cleanup; + } + } + +cleanup: + if (attrp != NULL) + pthread_attr_destroy(&attr); + + return ret; +} + +int +rte_thread_join(rte_thread_t thread_id, unsigned long *value_ptr) +{ + int ret = 0; + void *res = NULL; + void **pres = NULL; + + if (value_ptr != NULL) + pres = &res; + + ret = pthread_join((pthread_t)thread_id.opaque_id, pres); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_join failed\n"); + return ret; + } + + if (value_ptr != NULL && *pres != NULL) + *value_ptr = *(unsigned long *)(*pres); + + return 0; +} + +int +rte_thread_detach(rte_thread_t thread_id) +{ + return pthread_detach((pthread_t)thread_id.opaque_id); +} + int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 7077c9ce46..e841321819 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -31,
[dpdk-dev] [PATCH v17 10/13] eal: implement functions for thread barrier management
From: Narcisa Vasile Add functions for barrier init, destroy, wait. A portable type is used to represent a barrier identifier. The rte_thread_barrier_wait() function returns the same value on all platforms. Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 61 lib/eal/include/rte_thread.h | 58 ++ lib/eal/version.map | 3 ++ lib/eal/windows/rte_thread.c | 56 + 4 files changed, 178 insertions(+) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index b3a9d4b47e..30e2ca79d1 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -295,6 +295,67 @@ rte_thread_detach(rte_thread_t thread_id) return pthread_detach((pthread_t)thread_id.opaque_id); } +int +rte_thread_barrier_init(rte_thread_barrier *barrier, int count) +{ + int ret = 0; + pthread_barrier_t *pthread_barrier = NULL; + + RTE_VERIFY(barrier != NULL); + RTE_VERIFY(count > 0); + + pthread_barrier = calloc(1, sizeof(*pthread_barrier)); + if (pthread_barrier == NULL) { + RTE_LOG(DEBUG, EAL, "Unable to initialize barrier. Insufficient memory!\n"); + ret = ENOMEM; + goto cleanup; + } + ret = pthread_barrier_init(pthread_barrier, NULL, count); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "Failed to init barrier, ret = %d\n", ret); + goto cleanup; + } + + barrier->barrier_id = pthread_barrier; + pthread_barrier = NULL; + +cleanup: + free(pthread_barrier); + return ret; +} + +int +rte_thread_barrier_wait(rte_thread_barrier *barrier) +{ + int ret = 0; + + RTE_VERIFY(barrier != NULL); + RTE_VERIFY(barrier->barrier_id != NULL); + + ret = pthread_barrier_wait(barrier->barrier_id); + if (ret == PTHREAD_BARRIER_SERIAL_THREAD) + ret = RTE_THREAD_BARRIER_SERIAL_THREAD; + + return ret; +} + +int +rte_thread_barrier_destroy(rte_thread_barrier *barrier) +{ + int ret = 0; + + RTE_VERIFY(barrier != NULL); + + ret = pthread_barrier_destroy(barrier->barrier_id); + if (ret != 0) + RTE_LOG(DEBUG, EAL, "Failed to destroy barrier: %d\n", ret); + + free(barrier->barrier_id); + barrier->barrier_id = NULL; + + return ret; +} + int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index e841321819..7c84e32988 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -54,6 +54,18 @@ typedef struct { #endif /* RTE_HAS_CPUSET */ +/** + * Returned by rte_thread_barrier_wait() when call is successful. + */ +#define RTE_THREAD_BARRIER_SERIAL_THREAD -1 + +/** + * Thread barrier representation. + */ +typedef struct rte_thread_barrier_tag { + void *barrier_id; /**< barrrier identifier */ +} rte_thread_barrier; + /** * TLS key type, an opaque pointer. */ @@ -302,6 +314,52 @@ __rte_experimental int rte_thread_set_priority(rte_thread_t thread_id, enum rte_thread_priority priority); +/** + * Initializes a synchronization barrier. + * + * @param barrier + *A pointer that references the newly created 'barrier' object. + * + * @param count + *The number of threads that must enter the barrier before + *the threads can continue execution. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_barrier_init(rte_thread_barrier *barrier, int count); + +/** + * Causes the calling thread to wait at the synchronization barrier 'barrier'. + * + * @param barrier + *The barrier used for synchronizing the threads. + * + * @return + * Return RTE_THREAD_BARRIER_SERIAL_THREAD for the thread synchronized + * at the barrier. + * Return 0 for all other threads. + * Return a positive errno-style error number, in case of failure. + */ +__rte_experimental +int rte_thread_barrier_wait(rte_thread_barrier *barrier); + +/** + * Releases all resources used by a synchronization barrier + * and uninitializes it. + * + * @param barrier + *The barrier to be destroyed. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_barrier_destroy(rte_thread_barrier *barrier); + /** * Create a TLS data key visible to all threads in the process. * the created key is later used to get/set a value. diff --git a/lib/eal/version.map b/lib/eal/version.map index 0384a09fa2..06e5f82da2 100644 --- a/lib/eal/version.map +++ b/lib/eal/version.map @@ -427,6 +427,9 @@ EXPERIMENTAL { rte_thread_attr_get_affinity; rte_thread_attr_set_affinity; rte_thread_attr_set_priority; + rte_thread_barrier_init; + rte_thread_b
[dpdk-dev] [PATCH v17 12/13] eal: implement functions for mutex management
From: Narcisa Vasile Add functions for mutex init, destroy, lock, unlock, trylock. Windows does not have a static initializer. Initialization is only done through InitializeCriticalSection(). To overcome this, RTE_INIT_MUTEX macro is added to replace static initialization of mutexes. The macro calls rte_thread_mutex_init(). Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 69 + lib/eal/include/rte_thread.h | 85 lib/eal/version.map | 5 +++ lib/eal/windows/rte_thread.c | 64 +++ 4 files changed, 223 insertions(+) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index 30e2ca79d1..580448d8bf 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -295,6 +295,75 @@ rte_thread_detach(rte_thread_t thread_id) return pthread_detach((pthread_t)thread_id.opaque_id); } +int +rte_thread_mutex_init(rte_thread_mutex *mutex) +{ + int ret = 0; + pthread_mutex_t *m = NULL; + + RTE_VERIFY(mutex != NULL); + + m = calloc(1, sizeof(*m)); + if (m == NULL) { + RTE_LOG(DEBUG, EAL, "Unable to initialize mutex. Insufficient memory!\n"); + ret = ENOMEM; + goto cleanup; + } + + ret = pthread_mutex_init(m, NULL); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "Failed to init mutex. ret = %d\n", ret); + goto cleanup; + } + + mutex->mutex_id = m; + m = NULL; + +cleanup: + free(m); + return ret; +} + +int +rte_thread_mutex_lock(rte_thread_mutex *mutex) +{ + RTE_VERIFY(mutex != NULL); + + return pthread_mutex_lock((pthread_mutex_t *)mutex->mutex_id); +} + +int +rte_thread_mutex_unlock(rte_thread_mutex *mutex) +{ + RTE_VERIFY(mutex != NULL); + + return pthread_mutex_unlock((pthread_mutex_t *)mutex->mutex_id); +} + +int +rte_thread_mutex_try_lock(rte_thread_mutex *mutex) +{ + RTE_VERIFY(mutex != NULL); + + return pthread_mutex_trylock((pthread_mutex_t *)mutex->mutex_id); +} + +int +rte_thread_mutex_destroy(rte_thread_mutex *mutex) +{ + int ret = 0; + RTE_VERIFY(mutex != NULL); + + ret = pthread_mutex_destroy((pthread_mutex_t *)mutex->mutex_id); + if (ret != 0) + RTE_LOG(DEBUG, EAL, "Unable to destroy mutex, ret = %d\n", ret); + + free(mutex->mutex_id); + mutex->mutex_id = NULL; + + return ret; +} + int rte_thread_barrier_init(rte_thread_barrier *barrier, int count) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 7c84e32988..09a5fd8add 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -54,6 +54,25 @@ typedef struct { #endif /* RTE_HAS_CPUSET */ +#define RTE_DECLARE_MUTEX(private_lock) rte_thread_mutex private_lock + +#define RTE_DEFINE_MUTEX(private_lock)\ +RTE_INIT(__rte_ ## private_lock ## _init)\ +{\ + RTE_VERIFY(rte_thread_mutex_init(&private_lock) == 0);\ +} + +#define RTE_INIT_MUTEX(private_lock)\ +static RTE_DECLARE_MUTEX(private_lock);\ +RTE_DEFINE_MUTEX(private_lock) + +/** + * Thread mutex representation. + */ +typedef struct rte_thread_mutex_tag { + void *mutex_id; /**< mutex identifier */ +} rte_thread_mutex; + /** * Returned by rte_thread_barrier_wait() when call is successful. */ @@ -314,6 +333,72 @@ __rte_experimental int rte_thread_set_priority(rte_thread_t thread_id, enum rte_thread_priority priority); +/** + * Initializes a mutex. + * + * @param mutex + *The mutex to be initialized. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_mutex_init(rte_thread_mutex *mutex); + +/** + * Locks a mutex. + * + * @param mutex + *The mutex to be locked. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_mutex_lock(rte_thread_mutex *mutex); + +/** + * Unlocks a mutex. + * + * @param mutex + *The mutex to be unlocked. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_mutex_unlock(rte_thread_mutex *mutex); + +/** + * Tries to lock a mutex.If the mutex is already held by a different thread, + * the function returns without blocking. + * + * @param mutex + *The mutex that will be acquired, if not already locked. + * + * @return + * On success, if the mutex is acquired, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_mutex_try_lock(rte_thread_mutex *mutex); + +/** + * Releases all resources associated with a mutex. + * + * @param mutex + *The mutex to be uninitialized. + * + * @return + * On success, return 0. + * On failure, return a positive errno-
[dpdk-dev] [PATCH v17 13/13] app/test: add unit tests for mutex
From: Narcisa Vasile As a new API for threading is introduced, a set of unit tests have been added to test the new interface. Verify that the mutex correctly locks/unlocks and protects the data. Check both static and dynamic mutexes. Signed-off-by: Narcisa Vasile --- app/test/test_threads.c | 106 1 file changed, 106 insertions(+) diff --git a/app/test/test_threads.c b/app/test/test_threads.c index 00f604ab7e..91155a04e3 100644 --- a/app/test/test_threads.c +++ b/app/test/test_threads.c @@ -243,6 +243,110 @@ test_thread_barrier(void) return 0; } +RTE_INIT_MUTEX(static_mutex); + +struct mutex_loop_args { + rte_thread_barrier *barrier; + rte_thread_mutex *mutex; + unsigned long result_A; + unsigned long result_B; +}; + +static void * +thread_loop_mutex_B(void *arg) +{ + struct mutex_loop_args *args = arg; + + if (rte_thread_mutex_try_lock(args->mutex) == 0) { + rte_thread_barrier_wait(args->barrier); + rte_thread_mutex_unlock(args->mutex); + args->result_B = 1; + } else { + rte_thread_barrier_wait(args->barrier); + args->result_B = 2; + } + + return NULL; +} + +static void * +thread_loop_mutex_A(void *arg) +{ + struct mutex_loop_args *args = arg; + + if (rte_thread_mutex_try_lock(args->mutex) != 0) { + rte_thread_barrier_wait(args->barrier); + args->result_A = 2; + } else { + rte_thread_barrier_wait(args->barrier); + rte_thread_mutex_unlock(args->mutex); + args->result_A = 1; + } + + return NULL; +} + +static int +test_thread_mutex(rte_thread_mutex *pmutex) +{ + rte_thread_t thread_A; + rte_thread_t thread_B; + rte_thread_mutex mutex; + rte_thread_barrier barrier; + struct mutex_loop_args args; + int ret = 0; + + /* If mutex is not statically initialized */ + if (pmutex == NULL) { + ret = rte_thread_mutex_init(&mutex); + RTE_TEST_ASSERT(ret == 0, "Failed to initialize mutex!"); + } else + mutex = *pmutex; + + ret = rte_thread_barrier_init(&barrier, 2); + RTE_TEST_ASSERT(ret == 0, "Failed to initialize barrier!"); + + args.mutex = &mutex; + args.barrier = &barrier; + + ret = rte_thread_create(&thread_A, NULL, thread_loop_mutex_A, &args); + RTE_TEST_ASSERT(ret == 0, "Failed to create thread!"); + + ret = rte_thread_create(&thread_B, NULL, thread_loop_mutex_B, &args); + RTE_TEST_ASSERT(ret == 0, "Failed to create thread!"); + + ret = rte_thread_join(thread_A, NULL); + RTE_TEST_ASSERT(ret == 0, "Failed to join thread!"); + + ret = rte_thread_join(thread_B, NULL); + RTE_TEST_ASSERT(ret == 0, "Failed to join thread!"); + + RTE_TEST_ASSERT(args.result_A != args.result_B, "Mutex failed to be acquired or was acquired by both threads!"); + + /* Destroy if dynamically initialized */ + if (pmutex == NULL) { + ret = rte_thread_mutex_destroy(&mutex); + RTE_TEST_ASSERT(ret == 0, "Failed to destroy mutex!"); + } + + ret = rte_thread_barrier_destroy(&barrier); + RTE_TEST_ASSERT(ret == 0, "Failed to destroy barrier!"); + + return ret; +} + +static int +test_thread_mutex_static(void) +{ + return test_thread_mutex(&static_mutex); +} + +static int +test_thread_mutex_dynamic(void) +{ + return test_thread_mutex(NULL); +} + static struct unit_test_suite threads_test_suite = { .suite_name = "threads autotest", .setup = NULL, @@ -253,6 +357,8 @@ static struct unit_test_suite threads_test_suite = { TEST_CASE(test_thread_attributes_priority), TEST_CASE(test_thread_detach), TEST_CASE(test_thread_barrier), + TEST_CASE(test_thread_mutex_static), + TEST_CASE(test_thread_mutex_dynamic), TEST_CASES_END() } }; -- 2.31.0.vfs.0.1
Re: [dpdk-dev] [PATCH v16 2/9] eal: add thread attributes
On Tue, Nov 09, 2021 at 09:27:09AM +0100, Thomas Monjalon wrote: > 09/11/2021 02:59, Narcisa Ana Maria Vasile: > > On Tue, Oct 12, 2021 at 06:12:21PM +0200, Thomas Monjalon wrote: > > > 09/10/2021 09:41, Narcisa Ana Maria Vasile: > > > > From: Narcisa Vasile > > > > > > > > Implement thread attributes for: > > > > * thread affinity > > > > * thread priority > > > > Implement functions for managing thread attributes. > > > > > > > > Priority is represented through an enum that allows for two levels: > > > > - RTE_THREAD_PRIORITY_NORMAL > > > > - RTE_THREAD_PRIORITY_REALTIME_CRITICAL > > > > > > It doesn't say how do you translate these priorites in POSIX and win32. > > > > I'll send a new version with a better commit message. > > Thread priorities on both Linux-based and Windows platforms are similarly > > constructed from a class/policy + priority value. Currently in DPDK, most > > threads > > operate at the OS-default priority level but there are cases when > > increasing the > > priority is useful. For example, the Mellanox data path acceleration > > driver requires > > realtime thread priority. Similarly, some Windows applications will > > require elevated > > priority. > > It should not. We should not use realtime priority. Thomas, can you join the community sync tomorrow? I'll bring this up to discuss. High performance applications benefit from an option to raise the priority of their threads to avoid being preemted by other threads on the system. If there are issues with realtime priority on some of the platforms, maybe we can add a warning for the user to make them aware of possible crashes as Stephen H. suggested some time ago. Note that this patch doesn't change the priority of EAL threads, enabling the higher priority will be done through a command line option when starting the application. Maybe we can explore raising the priority but not to the realtime level. > > > For these reasons, EAL will advertise 2 priority levels which are named > > suggestively > > "normal" and "realtime_critical" and are computed as follows: > > > > For Linux and similar platforms: > > * EAL "normal" priority corresponds to the (default) SCHED_OTHER policy > > + a priority value of > > (sched_get_priority_min(SCHED_OTHER) + > > sched_get_priority_max(SCHED_OTHER))/2. > > Note that on Linux the resulting priority value will be 0, > > in accordance to the docs guidance that mention the value should be 0 > > for SCHED_OTHER policy. > > > > * EAL "realtime" priority corresponds to the SCHED_RR policy + a > > priority value of > > sched_get_priority_max(SCHED_RR); > > > > For Windows: > > * EAL "normal" corresponds to class NORMAL_PRIORITY_CLASS + > > priority THREAD_PRIORITY_NORMAL > > * EAL "realtime_critical" corresponds to class REALTIME_PRIORITY_CLASS + > > priority THREAD_PRIORITY_TIME_CRITICAL > >
Re: [dpdk-dev] [PATCH v16 9/9] Add unit tests for thread API
On Tue, Nov 09, 2021 at 09:32:08AM +0100, Thomas Monjalon wrote: > 09/11/2021 03:10, Narcisa Ana Maria Vasile: > > On Tue, Oct 12, 2021 at 06:33:16PM +0200, Thomas Monjalon wrote: > > > 09/10/2021 09:41, Narcisa Ana Maria Vasile: > > > > From: Narcisa Vasile > > > > > > > > As a new API for threading is introduced, > > > > a set of unit tests have been added to test the new interface. > > > > The tests verify that: > > > > * mutexes and barriers behave as expected > > > > * thread properties are applied correctly > > > > * the thread id is retrieved correctly > > > > * thread creation/destruction works properly > > > > > > Please make each test part of the patch implementing the feature. > > > Thanks > > > > > Makes sense, but most of these unit tests use rte_thread_create and > > rte_thread_join to handle the creation and cleanup of the threads > > that are being tested, so I'm forced to have this test patch at the end. > > You mean you cannot start the series with implementing these 2 functions? Yes, rte_thread_create() depends on thread attributes. However, some of the other components can still be tested separately. I've broken it down in v17 to allow for better progressive testing, but the first 3 tests follow after the first 3 features. > > > I could still break it up into smaller patches, one for each test category > > (mutex, attributes, etc) if you want. > > I would like to see features built & tested atomically and progressively. >
Re: [dpdk-dev] [PATCH v16 8/9] eal: implement functions for thread barrier management
On Mon, Nov 08, 2021 at 06:07:34PM -0800, Narcisa Ana Maria Vasile wrote: > On Tue, Oct 12, 2021 at 06:32:09PM +0200, Thomas Monjalon wrote: > > 09/10/2021 09:41, Narcisa Ana Maria Vasile: > > > From: Narcisa Vasile > > > > > > Add functions for barrier init, destroy, wait. > > > > > > A portable type is used to represent a barrier identifier. > > > The rte_thread_barrier_wait() function returns the same value > > > on all platforms. > > > > > > Signed-off-by: Narcisa Vasile > > > --- > > > lib/eal/common/rte_thread.c | 61 > > > lib/eal/include/rte_thread.h | 58 ++ > > > lib/eal/version.map | 3 ++ > > > lib/eal/windows/rte_thread.c | 56 + > > > 4 files changed, 178 insertions(+) > > > > It doesn't need to be part of the API. > > The pthread barrier is used only as part of the control thread > > implementation. > > The need disappear if you implement control thread on Windows. > > > Actually I think I have the implementation already. I've worked at this some > time ago, > I have this patch: > [v4,2/6] eal: add function for control thread creation > > The issue is I will break ABI so I cannot merge it as part of this patchset. > I'll see if I can remove this barrier patch though. I couldn't find a good way to test mutexes without barriers, so I kept this for now.
Re: [dpdk-dev] [PATCH v9] eal: remove sys/queue.h from public headers
On Tue, Aug 24, 2021 at 04:21:03PM +, William Tu wrote: > Currently there are some public headers that include 'sys/queue.h', which > is not POSIX, but usually provided by the Linux/BSD system library. > (Not in POSIX.1, POSIX.1-2001, or POSIX.1-2008. Present on the BSDs.) > The file is missing on Windows. During the Windows build, DPDK uses a > bundled copy, so building a DPDK library works fine. But when OVS or other > applications use DPDK as a library, because some DPDK public headers > include 'sys/queue.h', on Windows, it triggers an error due to no such > file. > > One solution is to install the 'lib/eal/windows/include/sys/queue.h' into > Windows environment, such as [1]. However, this means DPDK exports the > functionalities of 'sys/queue.h' into the environment, which might cause > symbols, macros, headers clashing with other applications. > > The patch fixes it by removing the "#include " from > DPDK public headers, so programs including DPDK headers don't depend > on the system to provide 'sys/queue.h'. When these public headers use > macros such as TAILQ_xxx, we replace it by the ones with RTE_ prefix. > For Windows, we copy the definitions from to rte_os.h > in Windows EAL. Note that these RTE_ macros are compatible with > , both at the level of API (to use with > macros in C files) and ABI (to avoid breaking it). > > Additionally, the TAILQ_FOREACH_SAFE is not part of , > the patch replaces it with RTE_TAILQ_FOREACH_SAFE. > > [1] http://mails.dpdk.org/archives/dev/2021-August/216304.html > > Suggested-by: Nick Connolly > Suggested-by: Dmitry Kozliuk > Acked-by: Dmitry Kozliuk > Signed-off-by: William Tu > --- Acked-by: Narcisa Vasile
[dpdk-dev] [PATCH v11 00/10] eal: Add EAL API for threading
From: Narcisa Vasile EAL thread API **Problem Statement** DPDK currently uses the pthread interface to create and manage threads. Windows does not support the POSIX thread programming model, so it currently relies on a header file that hides the Windows calls under pthread matched interfaces. Given that EAL should isolate the environment specifics from the applications and libraries and mediate all the communication with the operating systems, a new EAL interface is needed for thread management. **Goals** * Introduce a generic EAL API for threading support that will remove the current Windows pthread.h shim. * Replace references to pthread_* across the DPDK codebase with the new RTE_THREAD_* API. * Allow users to choose between using the RTE_THREAD_* API or a 3rd party thread library through a configuration option. **Design plan** New API main files: * rte_thread.h (librte_eal/include) * rte_thread.c (librte_eal/windows) * rte_thread.c (librte_eal/common) **A schematic example of the design** -- lib/librte_eal/include/rte_thread.h int rte_thread_create(); lib/librte_eal/common/rte_thread.c int rte_thread_create() { return pthread_create(); } lib/librte_eal/windows/rte_thread.c int rte_thread_create() { return CreateThread(); } - **Thread attributes** When or after a thread is created, specific characteristics of the thread can be adjusted. Given that the thread characteristics that are of interest for DPDK applications are affinity and priority, the following structure that represents thread attributes has been defined: typedef struct { enum rte_thread_priority priority; rte_cpuset_t cpuset; } rte_thread_attr_t; The *rte_thread_create()* function can optionally receive an rte_thread_attr_t object that will cause the thread to be created with the affinity and priority described by the attributes object. If no rte_thread_attr_t is passed (parameter is NULL), the default affinity and priority are used. An rte_thread_attr_t object can also be set to the default values by calling *rte_thread_attr_init()*. *Priority* is represented through an enum that currently advertises two values for priority: - RTE_THREAD_PRIORITY_NORMAL - RTE_THREAD_PRIORITY_REALTIME_CRITICAL The enum can be extended to allow for multiple priority levels. rte_thread_set_priority - sets the priority of a thread rte_thread_attr_set_priority - updates an rte_thread_attr_t object with a new value for priority The user can choose thread priority through an EAL parameter, when starting an application. If EAL parameter is not used, the per-platform default value for thread priority is used. Otherwise administrator has an option to set one of available options: --thread-prio normal --thread-prio realtime Example: ./dpdk-l2fwd -l 0-3 -n 4 –thread-prio normal -- -q 8 -p *Affinity* is described by the already known “rte_cpuset_t” type. rte_thread_attr_set/get_affinity - sets/gets the affinity field in a rte_thread_attr_t object rte_thread_set/get_affinity – sets/gets the affinity of a thread **Errors** A translation function that maps Windows error codes to errno-style error codes is provided. **Future work** The long term plan is for EAL to provide full threading support: * Add support for conditional variables * Add support for pthread_mutex_trylock * Additional functionality offered by pthread_* (such as pthread_setname_np, etc.) v11: - Add unit tests for thread API - Rebase v10: - Remove patch no. 10. It will be broken down in subpatches and sent as a different patchset that depends on this one. This is done due to the ABI breaks that would be caused by patch 10. - Replace unix/rte_thread.c with common/rte_thread.c - Remove initializations that may prevent compiler from issuing useful warnings. - Remove rte_thread_types.h and rte_windows_thread_types.h - Remove unneeded priority macros (EAL_THREAD_PRIORITY*) - Remove functions that retrieves thread handle from process handle - Remove rte_thread_cancel() until same behavior is obtained on all platforms. - Fix rte_thread_detach() function description, return value and remove empty line. - Reimplement mutex functions. Add compatible representation for mutex identifier. Add macro to replace static mutex initialization instances. - Fix commit messages (lines too long, remove unicode symbols) v9: - Sign patches v8: - Rebase - Add rte_thread_detach() API - Set default priority, when user did not specify a value v7: Based on DmitryK's review: - Change thread id representation - Change mutex id representation - Implement static mutex inititalizer for Windows - Change barrier identifier representation - Improve commit messages - Add missing doxygen comments - Split error translation function - Improve name for affi
[dpdk-dev] [PATCH v11 01/10] eal: add basic threading functions
From: Narcisa Vasile Use a portable, type-safe representation for the thread identifier. Add functions for comparing thread ids and obtaining the thread id for the current thread. Signed-off-by: Narcisa Vasile --- lib/eal/common/meson.build| 1 + lib/eal/{unix => common}/rte_thread.c | 57 --- lib/eal/include/rte_thread.h | 48 +- lib/eal/unix/meson.build | 1 - lib/eal/version.map | 3 ++ lib/eal/windows/rte_thread.c | 17 6 files changed, 95 insertions(+), 32 deletions(-) rename lib/eal/{unix => common}/rte_thread.c (66%) diff --git a/lib/eal/common/meson.build b/lib/eal/common/meson.build index edfca9..eda250247b 100644 --- a/lib/eal/common/meson.build +++ b/lib/eal/common/meson.build @@ -80,6 +80,7 @@ sources += files( 'rte_random.c', 'rte_reciprocal.c', 'rte_service.c', +'rte_thread.c', 'rte_version.c', ) diff --git a/lib/eal/unix/rte_thread.c b/lib/eal/common/rte_thread.c similarity index 66% rename from lib/eal/unix/rte_thread.c rename to lib/eal/common/rte_thread.c index c72d619ec1..92a7451b0a 100644 --- a/lib/eal/unix/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause * Copyright 2021 Mellanox Technologies, Ltd + * Copyright(c) 2021 Microsoft Corporation */ #include @@ -16,25 +17,41 @@ struct eal_tls_key { pthread_key_t thread_index; }; +rte_thread_t +rte_thread_self(void) +{ + rte_thread_t thread_id; + + thread_id.opaque_id = (uintptr_t)pthread_self(); + + return thread_id; +} + +int +rte_thread_equal(rte_thread_t t1, rte_thread_t t2) +{ + return pthread_equal((pthread_t)t1.opaque_id, (pthread_t)t2.opaque_id); +} + int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { int err; + rte_thread_key k; - *key = malloc(sizeof(**key)); - if ((*key) == NULL) { + k = malloc(sizeof(*k)); + if (k == NULL) { RTE_LOG(DEBUG, EAL, "Cannot allocate TLS key.\n"); - rte_errno = ENOMEM; - return -1; + return EINVAL; } - err = pthread_key_create(&((*key)->thread_index), destructor); - if (err) { + err = pthread_key_create(&(k->thread_index), destructor); + if (err != 0) { RTE_LOG(DEBUG, EAL, "pthread_key_create failed: %s\n", strerror(err)); - free(*key); - rte_errno = ENOEXEC; - return -1; + free(k); + return err; } + *key = k; return 0; } @@ -43,18 +60,16 @@ rte_thread_key_delete(rte_thread_key key) { int err; - if (!key) { + if (key == NULL) { RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); - rte_errno = EINVAL; - return -1; + return EINVAL; } err = pthread_key_delete(key->thread_index); - if (err) { + if (err != 0) { RTE_LOG(DEBUG, EAL, "pthread_key_delete failed: %s\n", strerror(err)); free(key); - rte_errno = ENOEXEC; - return -1; + return err; } free(key); return 0; @@ -65,17 +80,15 @@ rte_thread_value_set(rte_thread_key key, const void *value) { int err; - if (!key) { + if (key == NULL) { RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); - rte_errno = EINVAL; - return -1; + return EINVAL; } err = pthread_setspecific(key->thread_index, value); - if (err) { + if (err != 0) { RTE_LOG(DEBUG, EAL, "pthread_setspecific failed: %s\n", strerror(err)); - rte_errno = ENOEXEC; - return -1; + return err; } return 0; } @@ -83,7 +96,7 @@ rte_thread_value_set(rte_thread_key key, const void *value) void * rte_thread_value_get(rte_thread_key key) { - if (!key) { + if (key == NULL) { RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); rte_errno = EINVAL; return NULL; diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 8be8ed8f36..748f64d230 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2021 Mellanox Technologies, Ltd + * Copyright(c) 2021 Microsoft Corporation */ +#include #include #include @@ -20,11 +22,45 @@ extern "C" { #endif +#include + +/** + * Thread id descriptor. + */ +typedef struct rte_thread_tag { + uintptr_t opaque_id; /**< thread identifier */ +} rte_thread_t; + /** * TLS key type, an opaque pointer. */ typedef struct eal_tls_key *
[dpdk-dev] [PATCH v11 02/10] eal: add thread attributes
From: Narcisa Vasile Implement thread attributes for: * thread affinity * thread priority Implement functions for managing thread attributes. Priority is represented through an enum that allows for two levels: - RTE_THREAD_PRIORITY_NORMAL - RTE_THREAD_PRIORITY_REALTIME_CRITICAL Affinity is described by the rte_cpuset_t type. An rte_thread_attr_t object can be set to the default values by calling rte_thread_attr_init(). Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 46 ++ lib/eal/include/rte_thread.h | 93 lib/eal/version.map | 4 ++ lib/eal/windows/rte_thread.c | 44 + 4 files changed, 187 insertions(+) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index 92a7451b0a..e1a4d7eae4 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,51 @@ rte_thread_equal(rte_thread_t t1, rte_thread_t t2) return pthread_equal((pthread_t)t1.opaque_id, (pthread_t)t2.opaque_id); } +int +rte_thread_attr_init(rte_thread_attr_t *attr) +{ + RTE_VERIFY(attr != NULL); + + CPU_ZERO(&attr->cpuset); + attr->priority = RTE_THREAD_PRIORITY_NORMAL; + + return 0; +} + +int +rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, +rte_cpuset_t *cpuset) +{ + RTE_VERIFY(thread_attr != NULL); + RTE_VERIFY(cpuset != NULL); + + thread_attr->cpuset = *cpuset; + + return 0; +} + +int +rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, +rte_cpuset_t *cpuset) +{ + RTE_VERIFY(thread_attr != NULL); + RTE_VERIFY(cpuset != NULL); + + *cpuset = thread_attr->cpuset; + + return 0; +} + +int +rte_thread_attr_set_priority(rte_thread_attr_t *thread_attr, +enum rte_thread_priority priority) +{ + RTE_VERIFY(thread_attr != NULL); + + thread_attr->priority = priority; + return 0; +} + int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 748f64d230..032ff73b36 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -31,6 +31,30 @@ typedef struct rte_thread_tag { uintptr_t opaque_id; /**< thread identifier */ } rte_thread_t; +/** + * Thread priority values. + */ +enum rte_thread_priority { + RTE_THREAD_PRIORITY_UNDEFINED = 0, + /**< priority hasn't been defined */ + RTE_THREAD_PRIORITY_NORMAL= 1, + /**< normal thread priority, the default */ + RTE_THREAD_PRIORITY_REALTIME_CRITICAL = 2, + /**< highest thread priority allowed */ +}; + +#ifdef RTE_HAS_CPUSET + +/** + * Representation for thread attributes. + */ +typedef struct { + enum rte_thread_priority priority; /**< thread priority */ + rte_cpuset_t cpuset; /**< thread affinity */ +} rte_thread_attr_t; + +#endif /* RTE_HAS_CPUSET */ + /** * TLS key type, an opaque pointer. */ @@ -63,6 +87,75 @@ int rte_thread_equal(rte_thread_t t1, rte_thread_t t2); #ifdef RTE_HAS_CPUSET +/** + * Initialize the attributes of a thread. + * These attributes can be passed to the rte_thread_create() function + * that will create a new thread and set its attributes according to attr. + * + * @param attr + * Thread attributes to initialize. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_attr_init(rte_thread_attr_t *attr); + +/** + * Set the CPU affinity value in the thread attributes pointed to + * by 'thread_attr'. + * + * @param thread_attr + * Points to the thread attributes in which affinity will be updated. + * + * @param cpuset + * Points to the value of the affinity to be set. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, + rte_cpuset_t *cpuset); + +/** + * Get the value of CPU affinity that is set in the thread attributes pointed + * to by 'thread_attr'. + * + * @param thread_attr + * Points to the thread attributes from which affinity will be retrieved. + * + * @param cpuset + * Pointer to the memory that will store the affinity. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, + rte_cpuset_t *cpuset); + +/** + * Set the thread priority value in the thread attributes pointed to + * by 'thread_attr'. + * + * @param thread_attr + * Points to the thread attributes in which priority will be updated. + * + * @param priority + * Po
[dpdk-dev] [PATCH v11 03/10] eal/windows: translate Windows errors to errno-style errors
From: Narcisa Vasile Add function to translate Windows error codes to errno-style error codes. The possible return values are chosen so that we have as much semantical compatibility between platforms as possible. Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 6 +-- lib/eal/include/rte_thread.h | 5 +- lib/eal/windows/rte_thread.c | 95 +++- 3 files changed, 76 insertions(+), 30 deletions(-) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index e1a4d7eae4..27ad1c7eb0 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -47,7 +47,7 @@ rte_thread_attr_init(rte_thread_attr_t *attr) int rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, -rte_cpuset_t *cpuset) + rte_cpuset_t *cpuset) { RTE_VERIFY(thread_attr != NULL); RTE_VERIFY(cpuset != NULL); @@ -59,7 +59,7 @@ rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, int rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, -rte_cpuset_t *cpuset) + rte_cpuset_t *cpuset) { RTE_VERIFY(thread_attr != NULL); RTE_VERIFY(cpuset != NULL); @@ -71,7 +71,7 @@ rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, int rte_thread_attr_set_priority(rte_thread_attr_t *thread_attr, -enum rte_thread_priority priority) + enum rte_thread_priority priority) { RTE_VERIFY(thread_attr != NULL); diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 032ff73b36..bf649c2fe6 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -235,9 +235,8 @@ int rte_thread_value_set(rte_thread_key key, const void *value); * * @return * On success, value data pointer (can also be NULL). - * On failure, NULL and an error number is set in rte_errno. - * rte_errno can be: EINVAL - Invalid parameter passed. - * ENOEXEC - Specific OS error. + * On failure, NULL and a positive error number is set in rte_errno. + * */ __rte_experimental void *rte_thread_value_get(rte_thread_key key); diff --git a/lib/eal/windows/rte_thread.c b/lib/eal/windows/rte_thread.c index 01966e7745..c1ecfbd6ae 100644 --- a/lib/eal/windows/rte_thread.c +++ b/lib/eal/windows/rte_thread.c @@ -13,6 +13,54 @@ struct eal_tls_key { DWORD thread_index; }; +/* Translates the most common error codes related to threads */ +static int +thread_translate_win32_error(DWORD error) +{ + switch (error) { + case ERROR_SUCCESS: + return 0; + + case ERROR_INVALID_PARAMETER: + return EINVAL; + + case ERROR_INVALID_HANDLE: + return EFAULT; + + case ERROR_NOT_ENOUGH_MEMORY: + /* FALLTHROUGH */ + case ERROR_NO_SYSTEM_RESOURCES: + return ENOMEM; + + case ERROR_PRIVILEGE_NOT_HELD: + /* FALLTHROUGH */ + case ERROR_ACCESS_DENIED: + return EACCES; + + case ERROR_ALREADY_EXISTS: + return EEXIST; + + case ERROR_POSSIBLE_DEADLOCK: + return EDEADLK; + + case ERROR_INVALID_FUNCTION: + /* FALLTHROUGH */ + case ERROR_CALL_NOT_IMPLEMENTED: + return ENOSYS; + } + + return EINVAL; +} + +static int +thread_log_last_error(const char *message) +{ + DWORD error = GetLastError(); + RTE_LOG(DEBUG, EAL, "GetLastError()=%lu: %s\n", error, message); + + return thread_translate_win32_error(error); +} + rte_thread_t rte_thread_self(void) { @@ -42,7 +90,7 @@ rte_thread_attr_init(rte_thread_attr_t *attr) int rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, -rte_cpuset_t *cpuset) + rte_cpuset_t *cpuset) { RTE_VERIFY(thread_attr != NULL); thread_attr->cpuset = *cpuset; @@ -52,7 +100,7 @@ rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, int rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, -rte_cpuset_t *cpuset) + rte_cpuset_t *cpuset) { RTE_VERIFY(thread_attr != NULL); @@ -63,7 +111,7 @@ rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, int rte_thread_attr_set_priority(rte_thread_attr_t *thread_attr, -enum rte_thread_priority priority) + enum rte_thread_priority priority) { RTE_VERIFY(thread_attr != NULL); @@ -76,18 +124,18 @@ int rte_thread_key_create(rte_thread_key *key, __rte_unused void (*destructor)(void *)) { + int ret; + *key = malloc(sizeof(**key)); if ((*key) == NULL) { RTE_LOG(DEBUG, EAL, "Cannot allocate TLS key.\n"); - rte_errno = ENOMEM; - return -1; + return ENOMEM; } (*key)->thread_index = TlsAlloc();
[dpdk-dev] [PATCH v11 05/10] eal: implement thread priority management functions
From: Narcisa Vasile Add function for setting the priority for a thread. Priorities on multiple platforms are similarly determined by a priority value and a priority class/policy. 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 * priority THREAD_PRIORITY_TIME_CRITICAL Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 49 ++ lib/eal/include/rte_thread.h | 17 ++ lib/eal/version.map | 1 + lib/eal/windows/rte_thread.c | 66 4 files changed, 133 insertions(+) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index 73b7b3141c..fcebf7097c 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -50,6 +50,55 @@ rte_thread_get_affinity_by_id(rte_thread_t thread_id, sizeof(*cpuset), cpuset); } +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; +} + +int +rte_thread_set_priority(rte_thread_t thread_id, + enum rte_thread_priority priority) +{ + int ret; + int policy; + struct sched_param param; + + 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_attr_init(rte_thread_attr_t *attr) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index ca4ade60e2..5514b2f57f 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -215,6 +215,23 @@ void rte_thread_get_affinity(rte_cpuset_t *cpusetp); #endif /* RTE_HAS_CPUSET */ +/** + * 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. diff --git a/lib/eal/version.map b/lib/eal/version.map index 7ed4cd779e..df01e4 100644 --- a/lib/eal/version.map +++ b/lib/eal/version.map @@ -435,6 +435,7 @@ EXPERIMENTAL { rte_thread_attr_set_priority; rte_thread_get_affinity_by_id; 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 0127119f49..fb04718f58 100644 --- a/lib/eal/windows/rte_thread.c +++ b/lib/eal/windows/rte_thread.c @@ -200,6 +200,72 @@ rte_thread_get_affinity_by_id(rte_thread_t thread_id, return ret; } +static int +thread_map_priority_to_os_value(enum rte_thread_priority eal_pri, + int *os_pri, int *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
[dpdk-dev] [PATCH v11 04/10] eal: implement functions for thread affinity management
From: Narcisa Vasile Implement functions for getting/setting thread affinity. Threads can be pinned to specific cores by setting their affinity attribute. Signed-off-by: Narcisa Vasile Signed-off-by: Dmitry Malloy --- lib/eal/common/rte_thread.c | 16 lib/eal/include/rte_thread.h | 36 +++ lib/eal/version.map | 2 + lib/eal/windows/eal_lcore.c | 176 +- lib/eal/windows/eal_windows.h | 10 ++ lib/eal/windows/rte_thread.c | 125 +++- 6 files changed, 319 insertions(+), 46 deletions(-) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index 27ad1c7eb0..73b7b3141c 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -34,6 +34,22 @@ rte_thread_equal(rte_thread_t t1, rte_thread_t t2) return pthread_equal((pthread_t)t1.opaque_id, (pthread_t)t2.opaque_id); } +int +rte_thread_set_affinity_by_id(rte_thread_t thread_id, + const rte_cpuset_t *cpuset) +{ + return pthread_setaffinity_np((pthread_t)thread_id.opaque_id, + sizeof(*cpuset), cpuset); +} + +int +rte_thread_get_affinity_by_id(rte_thread_t thread_id, + rte_cpuset_t *cpuset) +{ + return pthread_getaffinity_np((pthread_t)thread_id.opaque_id, + sizeof(*cpuset), cpuset); +} + int rte_thread_attr_init(rte_thread_attr_t *attr) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index bf649c2fe6..ca4ade60e2 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -87,6 +87,42 @@ int rte_thread_equal(rte_thread_t t1, rte_thread_t t2); #ifdef RTE_HAS_CPUSET +/** + * Set the affinity of thread 'thread_id' to the cpu set + * specified by 'cpuset'. + * + * @param thread_id + *Id of the thread for which to set the affinity. + * + * @param cpuset + * Pointer to CPU affinity to set. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_set_affinity_by_id(rte_thread_t thread_id, + const rte_cpuset_t *cpuset); + +/** + * Get the affinity of thread 'thread_id' and store it + * in 'cpuset'. + * + * @param thread_id + *Id of the thread for which to get the affinity. + * + * @param cpuset + * Pointer for storing the affinity value. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_get_affinity_by_id(rte_thread_t thread_id, + rte_cpuset_t *cpuset); + /** * Initialize the attributes of a thread. * These attributes can be passed to the rte_thread_create() function diff --git a/lib/eal/version.map b/lib/eal/version.map index 9ffa5eb15e..7ed4cd779e 100644 --- a/lib/eal/version.map +++ b/lib/eal/version.map @@ -433,6 +433,8 @@ EXPERIMENTAL { rte_thread_attr_get_affinity; rte_thread_attr_set_affinity; rte_thread_attr_set_priority; + rte_thread_get_affinity_by_id; + rte_thread_set_affinity_by_id; }; INTERNAL { diff --git a/lib/eal/windows/eal_lcore.c b/lib/eal/windows/eal_lcore.c index 476c2d2bdf..295af50698 100644 --- a/lib/eal/windows/eal_lcore.c +++ b/lib/eal/windows/eal_lcore.c @@ -2,7 +2,6 @@ * Copyright(c) 2019 Intel Corporation */ -#include #include #include @@ -27,13 +26,15 @@ struct socket_map { }; struct cpu_map { - unsigned int socket_count; unsigned int lcore_count; + unsigned int socket_count; + unsigned int cpu_count; struct lcore_map lcores[RTE_MAX_LCORE]; struct socket_map sockets[RTE_MAX_NUMA_NODES]; + GROUP_AFFINITY cpus[CPU_SETSIZE]; }; -static struct cpu_map cpu_map = { 0 }; +static struct cpu_map cpu_map; /* eal_create_cpu_map() is called before logging is initialized */ static void @@ -47,13 +48,118 @@ log_early(const char *format, ...) va_end(va); } +static int +eal_query_group_affinity(void) +{ + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *infos = NULL; + unsigned int *cpu_count = &cpu_map.cpu_count; + DWORD infos_size = 0; + int ret = 0; + USHORT group_count; + KAFFINITY affinity; + USHORT group_no; + unsigned int i; + + if (!GetLogicalProcessorInformationEx(RelationGroup, NULL, + &infos_size)) { + DWORD error = GetLastError(); + if (error != ERROR_INSUFFICIENT_BUFFER) { + log_early("Cannot get group information size, " + "error %lu\n", error); + rte_errno = EINVAL; + ret = -1; + goto cleanup; + } + } + + infos = malloc(infos_size); + if (infos == NULL) { + log_early("Cannot allocate memory for NUMA node information\n"); + rte_errno = ENOMEM; +
[dpdk-dev] [PATCH v11 07/10] eal: implement functions for mutex management
From: Narcisa Vasile Add functions for mutex init, destroy, lock, unlock. Add RTE_STATIC_MUTEX macro to replace static initialization of mutexes. Windows does not have a static initializer. Initialization is only done through InitializeCriticalSection(). The RTE_STATIC_MUTEX calls into the rte_thread_mutex_init() function that performs the actual mutex initialization. Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 61 +++ lib/eal/include/rte_thread.h | 94 lib/eal/version.map | 4 ++ lib/eal/windows/rte_thread.c | 53 4 files changed, 212 insertions(+) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index a0a51bc190..ebae4a8af1 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -251,6 +251,67 @@ rte_thread_detach(rte_thread_t thread_id) return pthread_detach((pthread_t)thread_id.opaque_id); } +int +rte_thread_mutex_init(rte_thread_mutex *mutex) +{ + int ret = 0; + pthread_mutex_t *m = NULL; + + RTE_VERIFY(mutex != NULL); + + m = calloc(1, sizeof(*m)); + if (m == NULL) { + RTE_LOG(DEBUG, EAL, "Unable to initialize mutex. Insufficient memory!\n"); + ret = ENOMEM; + goto cleanup; + } + + ret = pthread_mutex_init(m, NULL); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "Failed to init mutex. ret = %d\n", ret); + goto cleanup; + } + + mutex->mutex_id = m; + m = NULL; + +cleanup: + free(m); + return ret; +} + +int +rte_thread_mutex_lock(rte_thread_mutex *mutex) +{ + RTE_VERIFY(mutex != NULL); + + return pthread_mutex_lock((pthread_mutex_t *)mutex->mutex_id); +} + +int +rte_thread_mutex_unlock(rte_thread_mutex *mutex) +{ + RTE_VERIFY(mutex != NULL); + + return pthread_mutex_unlock((pthread_mutex_t *)mutex->mutex_id); +} + +int +rte_thread_mutex_destroy(rte_thread_mutex *mutex) +{ + int ret = 0; + RTE_VERIFY(mutex != NULL); + + ret = pthread_mutex_destroy((pthread_mutex_t *)mutex->mutex_id); + if (ret != 0) + RTE_LOG(DEBUG, EAL, "Unable to destroy mutex, ret = %d\n", ret); + + free(mutex->mutex_id); + mutex->mutex_id = NULL; + + return ret; +} + int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 098c3ba343..7e813b573d 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -56,6 +56,26 @@ typedef struct { #endif /* RTE_HAS_CPUSET */ +#define RTE_DECLARE_MUTEX(private_lock) rte_thread_mutex private_lock + +#define RTE_DEFINE_MUTEX(private_lock)\ +RTE_INIT(__rte_ ## private_lock ## _init)\ +{\ + RTE_VERIFY(rte_thread_mutex_init(&private_lock) == 0);\ +} + +#define RTE_STATIC_MUTEX(private_lock)\ +static RTE_DECLARE_MUTEX(private_lock);\ +RTE_DEFINE_MUTEX(private_lock) + + +/** + * Thread mutex representation. + */ +typedef struct rte_thread_mutex_tag { + void *mutex_id; /**< mutex identifier */ +} rte_thread_mutex; + /** * TLS key type, an opaque pointer. */ @@ -268,6 +288,28 @@ int rte_thread_join(rte_thread_t thread_id, unsigned long *value_ptr); __rte_experimental int rte_thread_detach(rte_thread_t thread_id); +/** + * Set core affinity of the current thread. + * Support both EAL and non-EAL thread and update TLS. + * + * @param cpusetp + * Pointer to CPU affinity to set. + * + * @return + * On success, return 0; otherwise return -1; + */ +int rte_thread_set_affinity(rte_cpuset_t *cpusetp); + +/** + * Get core affinity of the current thread. + * + * @param cpusetp + * Pointer to CPU affinity of current thread. + * It presumes input is not NULL, otherwise it causes panic. + * + */ +void rte_thread_get_affinity(rte_cpuset_t *cpusetp); + #endif /* RTE_HAS_CPUSET */ /** @@ -287,6 +329,58 @@ __rte_experimental int rte_thread_set_priority(rte_thread_t thread_id, enum rte_thread_priority priority); +/** + * Initializes a mutex. + * + * @param mutex + *The mutex to be initialized. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_mutex_init(rte_thread_mutex *mutex); + +/** + * Locks a mutex. + * + * @param mutex + *The mutex to be locked. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_mutex_lock(rte_thread_mutex *mutex); + +/** + * Unlocks a mutex. + * + * @param mutex + *The mutex to be unlocked. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_mutex_unlock(rte_thread_mutex *mutex); + +/** + * Releases all resources associated with a
[dpdk-dev] [PATCH v11 08/10] eal: implement functions for thread barrier management
From: Narcisa Vasile Add functions for barrier init, destroy, wait. A portable type is used to represent a barrier identifier. The rte_thread_barrier_wait() function returns the same value on all platforms. Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 61 lib/eal/include/rte_thread.h | 58 ++ lib/eal/version.map | 3 ++ lib/eal/windows/rte_thread.c | 56 + 4 files changed, 178 insertions(+) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index ebae4a8af1..3fdb267337 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -312,6 +312,67 @@ rte_thread_mutex_destroy(rte_thread_mutex *mutex) return ret; } +int +rte_thread_barrier_init(rte_thread_barrier *barrier, int count) +{ + int ret = 0; + pthread_barrier_t *pthread_barrier = NULL; + + RTE_VERIFY(barrier != NULL); + RTE_VERIFY(count > 0); + + pthread_barrier = calloc(1, sizeof(*pthread_barrier)); + if (pthread_barrier == NULL) { + RTE_LOG(DEBUG, EAL, "Unable to initialize barrier. Insufficient memory!\n"); + ret = ENOMEM; + goto cleanup; + } + ret = pthread_barrier_init(pthread_barrier, NULL, count); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "Failed to init barrier, ret = %d\n", ret); + goto cleanup; + } + + barrier->barrier_id = pthread_barrier; + pthread_barrier = NULL; + +cleanup: + free(pthread_barrier); + return ret; +} + +int +rte_thread_barrier_wait(rte_thread_barrier *barrier) +{ + int ret = 0; + + RTE_VERIFY(barrier != NULL); + RTE_VERIFY(barrier->barrier_id != NULL); + + ret = pthread_barrier_wait(barrier->barrier_id); + if (ret == PTHREAD_BARRIER_SERIAL_THREAD) + ret = RTE_THREAD_BARRIER_SERIAL_THREAD; + + return ret; +} + +int +rte_thread_barrier_destroy(rte_thread_barrier *barrier) +{ + int ret = 0; + + RTE_VERIFY(barrier != NULL); + + ret = pthread_barrier_destroy(barrier->barrier_id); + if (ret != 0) + RTE_LOG(DEBUG, EAL, "Failed to destroy barrier: %d\n", ret); + + free(barrier->barrier_id); + barrier->barrier_id = NULL; + + return ret; +} + int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 7e813b573d..40da83467b 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -76,6 +76,18 @@ typedef struct rte_thread_mutex_tag { void *mutex_id; /**< mutex identifier */ } rte_thread_mutex; +/** + * Returned by rte_thread_barrier_wait() when call is successful. + */ +#define RTE_THREAD_BARRIER_SERIAL_THREAD -1 + +/** + * Thread barrier representation. + */ +typedef struct rte_thread_barrier_tag { + void *barrier_id; /**< barrrier identifier */ +} rte_thread_barrier; + /** * TLS key type, an opaque pointer. */ @@ -381,6 +393,52 @@ int rte_thread_mutex_unlock(rte_thread_mutex *mutex); __rte_experimental int rte_thread_mutex_destroy(rte_thread_mutex *mutex); +/** + * Initializes a synchronization barrier. + * + * @param barrier + *A pointer that references the newly created 'barrier' object. + * + * @param count + *The number of threads that must enter the barrier before + *the threads can continue execution. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_barrier_init(rte_thread_barrier *barrier, int count); + +/** + * Causes the calling thread to wait at the synchronization barrier 'barrier'. + * + * @param barrier + *The barrier used for synchronizing the threads. + * + * @return + * Return RTE_THREAD_BARRIER_SERIAL_THREAD for the thread synchronized + * at the barrier. + * Return 0 for all other threads. + * Return a positive errno-style error number, in case of failure. + */ +__rte_experimental +int rte_thread_barrier_wait(rte_thread_barrier *barrier); + +/** + * Releases all resources used by a synchronization barrier + * and uninitializes it. + * + * @param barrier + *The barrier to be destroyed. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_barrier_destroy(rte_thread_barrier *barrier); + /** * Create a TLS data key visible to all threads in the process. * the created key is later used to get/set a value. diff --git a/lib/eal/version.map b/lib/eal/version.map index a1c7a8e87d..c081fdd96c 100644 --- a/lib/eal/version.map +++ b/lib/eal/version.map @@ -443,6 +443,9 @@ EXPERIMENTAL { rte_thread_mutex_lock; rte_thread_mutex_unlock; rte_thread_mutex_destroy; + rte_thread_barrier_init; +
[dpdk-dev] [PATCH v11 06/10] eal: add thread lifetime management
From: Narcisa Vasile Add functions for thread creation, joining, detaching. The *rte_thread_create()* function can optionally receive an rte_thread_attr_t object that will cause the thread to be created with the affinity and priority described by the attributes object. If no rte_thread_attr_t is passed (parameter is NULL), the default affinity and priority are used. On Windows, the function executed by a thread when the thread starts is represeneted by a function pointer of type DWORD (*func) (void*). On other platforms, the function pointer is a void* (*func) (void*). Performing a cast between these two types of function pointers to uniformize the API on all platforms may result in undefined behavior. TO fix this issue, a wrapper that respects the signature required by CreateThread() has been created on Windows. Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 107 + lib/eal/include/rte_thread.h| 55 + lib/eal/version.map | 3 + lib/eal/windows/include/sched.h | 2 +- lib/eal/windows/rte_thread.c| 138 5 files changed, 304 insertions(+), 1 deletion(-) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index fcebf7097c..a0a51bc190 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -144,6 +144,113 @@ rte_thread_attr_set_priority(rte_thread_attr_t *thread_attr, return 0; } +int +rte_thread_create(rte_thread_t *thread_id, + const rte_thread_attr_t *thread_attr, + rte_thread_func thread_func, void *args) +{ + int ret = 0; + pthread_attr_t attr; + pthread_attr_t *attrp = NULL; + struct sched_param param = { + .sched_priority = 0, + }; + int policy = SCHED_OTHER; + + if (thread_attr != NULL) { + ret = pthread_attr_init(&attr); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_init failed\n"); + goto cleanup; + } + + attrp = &attr; + + if (thread_attr->priority != RTE_THREAD_PRIORITY_UNDEFINED) { + /* +* Set the inherit scheduler parameter to explicit, +* otherwise the priority attribute is ignored. +*/ + ret = pthread_attr_setinheritsched(attrp, + PTHREAD_EXPLICIT_SCHED); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_setinheritsched failed\n"); + goto cleanup; + } + + ret = thread_map_priority_to_os_value( + thread_attr->priority, + ¶m.sched_priority, &policy + ); + if (ret != 0) + goto cleanup; + + ret = pthread_attr_setschedpolicy(attrp, policy); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_setschedpolicy failed\n"); + goto cleanup; + } + + ret = pthread_attr_setschedparam(attrp, ¶m); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_setschedparam failed\n"); + goto cleanup; + } + } + + if (CPU_COUNT(&thread_attr->cpuset) > 0) { + ret = pthread_attr_setaffinity_np(attrp, + sizeof(thread_attr->cpuset), + &thread_attr->cpuset); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_setaffinity_np failed\n"); + goto cleanup; + } + } + } + + ret = pthread_create((pthread_t *)&thread_id->opaque_id, attrp, + thread_func, args); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_create failed\n"); + goto cleanup; + } + +cleanup: + if (attrp != NULL) + pthread_attr_destroy(&attr); + + return ret; +} + +int +rte_thread_join(rte_thread_t thread_id, unsigned long *value_ptr) +{ + int ret = 0; + void *res = NULL; + void **pres = NULL; + + if (value_ptr != NULL) + pres = &res; + + ret = pthread_join((pthread_t)thread_id.opaque_id, pres); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_join failed\n"); + return ret; + } + + if (pres != NULL) + *value_ptr = *(unsigned long *)(*pres); + + return 0; +} + +i
[dpdk-dev] [PATCH v11 09/10] eal: add EAL argument for setting thread priority
From: Narcisa Vasile Allow the user to choose the thread priority through an EAL command line argument. The user can choose thread priority through an EAL parameter, when starting an application. If EAL parameter is not used, the per-platform default value for thread priority is used. Otherwise administrator has an option to set one of available options: --thread-prio normal --thread-prio realtime Example: ./dpdk-l2fwd -l 0-3 -n 4 --thread-prio normal -- -q 8 -p Signed-off-by: Narcisa Vasile --- lib/eal/common/eal_common_options.c | 28 +++- lib/eal/common/eal_internal_cfg.h | 2 ++ lib/eal/common/eal_options.h| 2 ++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/eal/common/eal_common_options.c b/lib/eal/common/eal_common_options.c index ff5861b5f3..9d29696b84 100644 --- a/lib/eal/common/eal_common_options.c +++ b/lib/eal/common/eal_common_options.c @@ -107,6 +107,7 @@ eal_long_options[] = { {OPT_TELEMETRY, 0, NULL, OPT_TELEMETRY_NUM}, {OPT_NO_TELEMETRY, 0, NULL, OPT_NO_TELEMETRY_NUM }, {OPT_FORCE_MAX_SIMD_BITWIDTH, 1, NULL, OPT_FORCE_MAX_SIMD_BITWIDTH_NUM}, + {OPT_THREAD_PRIORITY, 1, NULL, OPT_THREAD_PRIORITY_NUM}, /* legacy options that will be removed in future */ {OPT_PCI_BLACKLIST, 1, NULL, OPT_PCI_BLACKLIST_NUM}, @@ -1412,6 +1413,24 @@ eal_parse_simd_bitwidth(const char *arg) return 0; } +static int +eal_parse_thread_priority(const char *arg) +{ + struct internal_config *internal_conf = + eal_get_internal_configuration(); + enum rte_thread_priority priority; + + if (!strncmp("normal", arg, sizeof("normal"))) + priority = RTE_THREAD_PRIORITY_NORMAL; + else if (!strncmp("realtime", arg, sizeof("realtime"))) + priority = RTE_THREAD_PRIORITY_REALTIME_CRITICAL; + else + return -1; + + internal_conf->thread_priority = priority; + return 0; +} + static int eal_parse_base_virtaddr(const char *arg) { @@ -1825,7 +1844,13 @@ eal_parse_common_option(int opt, const char *optarg, return -1; } break; - + case OPT_THREAD_PRIORITY_NUM: + if (eal_parse_thread_priority(optarg) < 0) { + RTE_LOG(ERR, EAL, "invalid parameter for --" + OPT_THREAD_PRIORITY "\n"); + return -1; + } + break; /* don't know what to do, leave this to caller */ default: return 1; @@ -2088,6 +2113,7 @@ eal_common_usage(void) " (can be used multiple times)\n" " --"OPT_VMWARE_TSC_MAP"Use VMware TSC map instead of native RDTSC\n" " --"OPT_PROC_TYPE" Type of this process (primary|secondary|auto)\n" + " --"OPT_THREAD_PRIORITY" Set threads priority (normal|realtime)\n" #ifndef RTE_EXEC_ENV_WINDOWS " --"OPT_SYSLOG"Set syslog facility\n" #endif diff --git a/lib/eal/common/eal_internal_cfg.h b/lib/eal/common/eal_internal_cfg.h index d6c0470eb8..b2996cd65b 100644 --- a/lib/eal/common/eal_internal_cfg.h +++ b/lib/eal/common/eal_internal_cfg.h @@ -94,6 +94,8 @@ struct internal_config { unsigned int no_telemetry; /**< true to disable Telemetry */ struct simd_bitwidth max_simd_bitwidth; /**< max simd bitwidth path to use */ + enum rte_thread_priority thread_priority; + /**< thread priority to configure */ }; void eal_reset_internal_config(struct internal_config *internal_cfg); diff --git a/lib/eal/common/eal_options.h b/lib/eal/common/eal_options.h index 7b348e707f..9f5b209f64 100644 --- a/lib/eal/common/eal_options.h +++ b/lib/eal/common/eal_options.h @@ -93,6 +93,8 @@ enum { OPT_NO_TELEMETRY_NUM, #define OPT_FORCE_MAX_SIMD_BITWIDTH "force-max-simd-bitwidth" OPT_FORCE_MAX_SIMD_BITWIDTH_NUM, +#define OPT_THREAD_PRIORITY "thread-prio" + OPT_THREAD_PRIORITY_NUM, /* legacy option that will be removed in future */ #define OPT_PCI_BLACKLIST "pci-blacklist" -- 2.31.0.vfs.0.1
[dpdk-dev] [PATCH v11 10/10] Add unit tests for thread API
From: Narcisa Vasile As a new API for threading is introduced, a set of unit tests have been added to test the new interface. Signed-off-by: Narcisa Vasile --- app/test/meson.build| 2 + app/test/test_threads.c | 419 2 files changed, 421 insertions(+) create mode 100644 app/test/test_threads.c diff --git a/app/test/meson.build b/app/test/meson.build index a7611686ad..6fe8b02459 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -140,6 +140,7 @@ test_sources = files( 'test_table_tables.c', 'test_tailq.c', 'test_thash.c', + 'test_threads.c', 'test_timer.c', 'test_timer_perf.c', 'test_timer_racecond.c', @@ -276,6 +277,7 @@ fast_tests = [ ['reorder_autotest', true], ['service_autotest', true], ['thash_autotest', true], + ['threads_autotest, true'], ['trace_autotest', true], ] diff --git a/app/test/test_threads.c b/app/test/test_threads.c new file mode 100644 index 00..ce614942eb --- /dev/null +++ b/app/test/test_threads.c @@ -0,0 +1,419 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) 2021 Microsoft. + */ + +#include + +#include + +#include "test.h" + +#define THREADS_COUNT 20 + +#define TEST_THREADS_LOG(func) \ + printf("Error at line %d. %s failed!\n", __LINE__, func) + +static void * +thread_loop_self(void *arg) +{ + rte_thread_t *id = arg; + + *id = rte_thread_self(); + + return NULL; +} + +static int +test_thread_self(void) +{ + rte_thread_t threads_ids[THREADS_COUNT]; + rte_thread_t self_ids[THREADS_COUNT] = {0}; + size_t i; + size_t j; + int ret = 0; + + for (i = 0; i < THREADS_COUNT; ++i) { + if (rte_thread_create(&threads_ids[i], NULL, thread_loop_self, + &self_ids[i]) != 0) { + printf("Error, Only %zu threads created.\n", i); + break; + } + } + + for (j = 0; j < i; ++j) { + ret = rte_thread_join(threads_ids[j], NULL); + if (ret != 0) { + TEST_THREADS_LOG("rte_thread_join()"); + return -1; + } + + if (rte_thread_equal(threads_ids[j], self_ids[j]) == 0) + ret = -1; + } + + return ret; +} + +struct thread_context { + rte_thread_barrier *barrier; + size_t *thread_count; +}; + +static void * +thread_loop_barrier(void *arg) +{ + + struct thread_context *ctx = arg; + + (void)__atomic_add_fetch(ctx->thread_count, 1, __ATOMIC_RELAXED); + + if (rte_thread_barrier_wait(ctx->barrier) > 0) + TEST_THREADS_LOG("rte_thread_barrier_wait()"); + + return NULL; +} + +static int +test_thread_barrier(void) +{ + rte_thread_t threads_ids[THREADS_COUNT]; + struct thread_context ctx[THREADS_COUNT] = {0}; + rte_thread_barrier barrier; + size_t count = 0; + size_t i; + size_t j; + int ret = 0; + + ret = rte_thread_barrier_init(&barrier, THREADS_COUNT + 1); + if (ret != 0) { + TEST_THREADS_LOG("rte_thread_barrier_init()"); + return -1; + } + + for (i = 0; i < THREADS_COUNT; ++i) { + ctx[i].thread_count = &count; + ctx[i].barrier = &barrier; + if (rte_thread_create(&threads_ids[i], NULL, + thread_loop_barrier, &ctx[i]) != 0) { + printf("Error, Only %zu threads created.\n", i); + ret = -1; + goto error; + } + } + + ret = rte_thread_barrier_wait(ctx->barrier); + if (ret > 0) { + TEST_THREADS_LOG("rte_thread_barrier_wait()"); + ret = -1; + goto error; + } + + if (count != i) { + ret = -1; + printf("Error, expected thread count(%zu) to be equal " + "to the number of threads that wait at the barrier(%zu)\n", + count, i); + goto error; + } + +error: + for (j = 0; j < i; ++j) { + ret = rte_thread_join(threads_ids[j], NULL); + if (ret != 0) { + TEST_THREADS_LOG("rte_thread_join()"); + ret = -1; + break; + } + } + + ret = rte_thread_barrier_destroy(&barrier); + if (ret != 0) { + TEST_THREADS_LOG("rte_thread_barrier_destroy()"); + ret = -1; + } + + return ret; +} + +static size_t val; + +static void * +thread_loop_mutex(void *arg) +{ + rte_thread_mutex *mutex = arg; + + rte_thread_mutex_lock(mutex); + val++; + rte_thread_mutex_unlock(mutex); + + return NULL; +} + +static int +test_threa
[dpdk-dev] [PATCH v12 00/10] eal: Add EAL API for threading
From: Narcisa Vasile EAL thread API **Problem Statement** DPDK currently uses the pthread interface to create and manage threads. Windows does not support the POSIX thread programming model, so it currently relies on a header file that hides the Windows calls under pthread matched interfaces. Given that EAL should isolate the environment specifics from the applications and libraries and mediate all the communication with the operating systems, a new EAL interface is needed for thread management. **Goals** * Introduce a generic EAL API for threading support that will remove the current Windows pthread.h shim. * Replace references to pthread_* across the DPDK codebase with the new RTE_THREAD_* API. * Allow users to choose between using the RTE_THREAD_* API or a 3rd party thread library through a configuration option. **Design plan** New API main files: * rte_thread.h (librte_eal/include) * rte_thread.c (librte_eal/windows) * rte_thread.c (librte_eal/common) **A schematic example of the design** -- lib/librte_eal/include/rte_thread.h int rte_thread_create(); lib/librte_eal/common/rte_thread.c int rte_thread_create() { return pthread_create(); } lib/librte_eal/windows/rte_thread.c int rte_thread_create() { return CreateThread(); } - **Thread attributes** When or after a thread is created, specific characteristics of the thread can be adjusted. Given that the thread characteristics that are of interest for DPDK applications are affinity and priority, the following structure that represents thread attributes has been defined: typedef struct { enum rte_thread_priority priority; rte_cpuset_t cpuset; } rte_thread_attr_t; The *rte_thread_create()* function can optionally receive an rte_thread_attr_t object that will cause the thread to be created with the affinity and priority described by the attributes object. If no rte_thread_attr_t is passed (parameter is NULL), the default affinity and priority are used. An rte_thread_attr_t object can also be set to the default values by calling *rte_thread_attr_init()*. *Priority* is represented through an enum that currently advertises two values for priority: - RTE_THREAD_PRIORITY_NORMAL - RTE_THREAD_PRIORITY_REALTIME_CRITICAL The enum can be extended to allow for multiple priority levels. rte_thread_set_priority - sets the priority of a thread rte_thread_attr_set_priority - updates an rte_thread_attr_t object with a new value for priority The user can choose thread priority through an EAL parameter, when starting an application. If EAL parameter is not used, the per-platform default value for thread priority is used. Otherwise administrator has an option to set one of available options: --thread-prio normal --thread-prio realtime Example: ./dpdk-l2fwd -l 0-3 -n 4 –thread-prio normal -- -q 8 -p *Affinity* is described by the already known “rte_cpuset_t” type. rte_thread_attr_set/get_affinity - sets/gets the affinity field in a rte_thread_attr_t object rte_thread_set/get_affinity – sets/gets the affinity of a thread **Errors** A translation function that maps Windows error codes to errno-style error codes is provided. **Future work** The long term plan is for EAL to provide full threading support: * Add support for conditional variables * Add support for pthread_mutex_trylock * Additional functionality offered by pthread_* (such as pthread_setname_np, etc.) v12: - Fix freebsd warning about initializer in unit tests v11: - Add unit tests for thread API - Rebase v10: - Remove patch no. 10. It will be broken down in subpatches and sent as a different patchset that depends on this one. This is done due to the ABI breaks that would be caused by patch 10. - Replace unix/rte_thread.c with common/rte_thread.c - Remove initializations that may prevent compiler from issuing useful warnings. - Remove rte_thread_types.h and rte_windows_thread_types.h - Remove unneeded priority macros (EAL_THREAD_PRIORITY*) - Remove functions that retrieves thread handle from process handle - Remove rte_thread_cancel() until same behavior is obtained on all platforms. - Fix rte_thread_detach() function description, return value and remove empty line. - Reimplement mutex functions. Add compatible representation for mutex identifier. Add macro to replace static mutex initialization instances. - Fix commit messages (lines too long, remove unicode symbols) v9: - Sign patches v8: - Rebase - Add rte_thread_detach() API - Set default priority, when user did not specify a value v7: Based on DmitryK's review: - Change thread id representation - Change mutex id representation - Implement static mutex inititalizer for Windows - Change barrier identifier representation - Improve commit messages - Add missing doxygen commen
[dpdk-dev] [PATCH v12 01/10] eal: add basic threading functions
From: Narcisa Vasile Use a portable, type-safe representation for the thread identifier. Add functions for comparing thread ids and obtaining the thread id for the current thread. Signed-off-by: Narcisa Vasile --- lib/eal/common/meson.build| 1 + lib/eal/{unix => common}/rte_thread.c | 57 --- lib/eal/include/rte_thread.h | 48 +- lib/eal/unix/meson.build | 1 - lib/eal/version.map | 3 ++ lib/eal/windows/rte_thread.c | 17 6 files changed, 95 insertions(+), 32 deletions(-) rename lib/eal/{unix => common}/rte_thread.c (66%) diff --git a/lib/eal/common/meson.build b/lib/eal/common/meson.build index edfca9..eda250247b 100644 --- a/lib/eal/common/meson.build +++ b/lib/eal/common/meson.build @@ -80,6 +80,7 @@ sources += files( 'rte_random.c', 'rte_reciprocal.c', 'rte_service.c', +'rte_thread.c', 'rte_version.c', ) diff --git a/lib/eal/unix/rte_thread.c b/lib/eal/common/rte_thread.c similarity index 66% rename from lib/eal/unix/rte_thread.c rename to lib/eal/common/rte_thread.c index c72d619ec1..92a7451b0a 100644 --- a/lib/eal/unix/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause * Copyright 2021 Mellanox Technologies, Ltd + * Copyright(c) 2021 Microsoft Corporation */ #include @@ -16,25 +17,41 @@ struct eal_tls_key { pthread_key_t thread_index; }; +rte_thread_t +rte_thread_self(void) +{ + rte_thread_t thread_id; + + thread_id.opaque_id = (uintptr_t)pthread_self(); + + return thread_id; +} + +int +rte_thread_equal(rte_thread_t t1, rte_thread_t t2) +{ + return pthread_equal((pthread_t)t1.opaque_id, (pthread_t)t2.opaque_id); +} + int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { int err; + rte_thread_key k; - *key = malloc(sizeof(**key)); - if ((*key) == NULL) { + k = malloc(sizeof(*k)); + if (k == NULL) { RTE_LOG(DEBUG, EAL, "Cannot allocate TLS key.\n"); - rte_errno = ENOMEM; - return -1; + return EINVAL; } - err = pthread_key_create(&((*key)->thread_index), destructor); - if (err) { + err = pthread_key_create(&(k->thread_index), destructor); + if (err != 0) { RTE_LOG(DEBUG, EAL, "pthread_key_create failed: %s\n", strerror(err)); - free(*key); - rte_errno = ENOEXEC; - return -1; + free(k); + return err; } + *key = k; return 0; } @@ -43,18 +60,16 @@ rte_thread_key_delete(rte_thread_key key) { int err; - if (!key) { + if (key == NULL) { RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); - rte_errno = EINVAL; - return -1; + return EINVAL; } err = pthread_key_delete(key->thread_index); - if (err) { + if (err != 0) { RTE_LOG(DEBUG, EAL, "pthread_key_delete failed: %s\n", strerror(err)); free(key); - rte_errno = ENOEXEC; - return -1; + return err; } free(key); return 0; @@ -65,17 +80,15 @@ rte_thread_value_set(rte_thread_key key, const void *value) { int err; - if (!key) { + if (key == NULL) { RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); - rte_errno = EINVAL; - return -1; + return EINVAL; } err = pthread_setspecific(key->thread_index, value); - if (err) { + if (err != 0) { RTE_LOG(DEBUG, EAL, "pthread_setspecific failed: %s\n", strerror(err)); - rte_errno = ENOEXEC; - return -1; + return err; } return 0; } @@ -83,7 +96,7 @@ rte_thread_value_set(rte_thread_key key, const void *value) void * rte_thread_value_get(rte_thread_key key) { - if (!key) { + if (key == NULL) { RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); rte_errno = EINVAL; return NULL; diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 8be8ed8f36..748f64d230 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2021 Mellanox Technologies, Ltd + * Copyright(c) 2021 Microsoft Corporation */ +#include #include #include @@ -20,11 +22,45 @@ extern "C" { #endif +#include + +/** + * Thread id descriptor. + */ +typedef struct rte_thread_tag { + uintptr_t opaque_id; /**< thread identifier */ +} rte_thread_t; + /** * TLS key type, an opaque pointer. */ typedef struct eal_tls_key *
[dpdk-dev] [PATCH v12 02/10] eal: add thread attributes
From: Narcisa Vasile Implement thread attributes for: * thread affinity * thread priority Implement functions for managing thread attributes. Priority is represented through an enum that allows for two levels: - RTE_THREAD_PRIORITY_NORMAL - RTE_THREAD_PRIORITY_REALTIME_CRITICAL Affinity is described by the rte_cpuset_t type. An rte_thread_attr_t object can be set to the default values by calling rte_thread_attr_init(). Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 46 ++ lib/eal/include/rte_thread.h | 93 lib/eal/version.map | 4 ++ lib/eal/windows/rte_thread.c | 44 + 4 files changed, 187 insertions(+) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index 92a7451b0a..e1a4d7eae4 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,51 @@ rte_thread_equal(rte_thread_t t1, rte_thread_t t2) return pthread_equal((pthread_t)t1.opaque_id, (pthread_t)t2.opaque_id); } +int +rte_thread_attr_init(rte_thread_attr_t *attr) +{ + RTE_VERIFY(attr != NULL); + + CPU_ZERO(&attr->cpuset); + attr->priority = RTE_THREAD_PRIORITY_NORMAL; + + return 0; +} + +int +rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, +rte_cpuset_t *cpuset) +{ + RTE_VERIFY(thread_attr != NULL); + RTE_VERIFY(cpuset != NULL); + + thread_attr->cpuset = *cpuset; + + return 0; +} + +int +rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, +rte_cpuset_t *cpuset) +{ + RTE_VERIFY(thread_attr != NULL); + RTE_VERIFY(cpuset != NULL); + + *cpuset = thread_attr->cpuset; + + return 0; +} + +int +rte_thread_attr_set_priority(rte_thread_attr_t *thread_attr, +enum rte_thread_priority priority) +{ + RTE_VERIFY(thread_attr != NULL); + + thread_attr->priority = priority; + return 0; +} + int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 748f64d230..032ff73b36 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -31,6 +31,30 @@ typedef struct rte_thread_tag { uintptr_t opaque_id; /**< thread identifier */ } rte_thread_t; +/** + * Thread priority values. + */ +enum rte_thread_priority { + RTE_THREAD_PRIORITY_UNDEFINED = 0, + /**< priority hasn't been defined */ + RTE_THREAD_PRIORITY_NORMAL= 1, + /**< normal thread priority, the default */ + RTE_THREAD_PRIORITY_REALTIME_CRITICAL = 2, + /**< highest thread priority allowed */ +}; + +#ifdef RTE_HAS_CPUSET + +/** + * Representation for thread attributes. + */ +typedef struct { + enum rte_thread_priority priority; /**< thread priority */ + rte_cpuset_t cpuset; /**< thread affinity */ +} rte_thread_attr_t; + +#endif /* RTE_HAS_CPUSET */ + /** * TLS key type, an opaque pointer. */ @@ -63,6 +87,75 @@ int rte_thread_equal(rte_thread_t t1, rte_thread_t t2); #ifdef RTE_HAS_CPUSET +/** + * Initialize the attributes of a thread. + * These attributes can be passed to the rte_thread_create() function + * that will create a new thread and set its attributes according to attr. + * + * @param attr + * Thread attributes to initialize. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_attr_init(rte_thread_attr_t *attr); + +/** + * Set the CPU affinity value in the thread attributes pointed to + * by 'thread_attr'. + * + * @param thread_attr + * Points to the thread attributes in which affinity will be updated. + * + * @param cpuset + * Points to the value of the affinity to be set. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, + rte_cpuset_t *cpuset); + +/** + * Get the value of CPU affinity that is set in the thread attributes pointed + * to by 'thread_attr'. + * + * @param thread_attr + * Points to the thread attributes from which affinity will be retrieved. + * + * @param cpuset + * Pointer to the memory that will store the affinity. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, + rte_cpuset_t *cpuset); + +/** + * Set the thread priority value in the thread attributes pointed to + * by 'thread_attr'. + * + * @param thread_attr + * Points to the thread attributes in which priority will be updated. + * + * @param priority + * Po
[dpdk-dev] [PATCH v12 03/10] eal/windows: translate Windows errors to errno-style errors
From: Narcisa Vasile Add function to translate Windows error codes to errno-style error codes. The possible return values are chosen so that we have as much semantical compatibility between platforms as possible. Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 6 +-- lib/eal/include/rte_thread.h | 5 +- lib/eal/windows/rte_thread.c | 95 +++- 3 files changed, 76 insertions(+), 30 deletions(-) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index e1a4d7eae4..27ad1c7eb0 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -47,7 +47,7 @@ rte_thread_attr_init(rte_thread_attr_t *attr) int rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, -rte_cpuset_t *cpuset) + rte_cpuset_t *cpuset) { RTE_VERIFY(thread_attr != NULL); RTE_VERIFY(cpuset != NULL); @@ -59,7 +59,7 @@ rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, int rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, -rte_cpuset_t *cpuset) + rte_cpuset_t *cpuset) { RTE_VERIFY(thread_attr != NULL); RTE_VERIFY(cpuset != NULL); @@ -71,7 +71,7 @@ rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, int rte_thread_attr_set_priority(rte_thread_attr_t *thread_attr, -enum rte_thread_priority priority) + enum rte_thread_priority priority) { RTE_VERIFY(thread_attr != NULL); diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 032ff73b36..bf649c2fe6 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -235,9 +235,8 @@ int rte_thread_value_set(rte_thread_key key, const void *value); * * @return * On success, value data pointer (can also be NULL). - * On failure, NULL and an error number is set in rte_errno. - * rte_errno can be: EINVAL - Invalid parameter passed. - * ENOEXEC - Specific OS error. + * On failure, NULL and a positive error number is set in rte_errno. + * */ __rte_experimental void *rte_thread_value_get(rte_thread_key key); diff --git a/lib/eal/windows/rte_thread.c b/lib/eal/windows/rte_thread.c index 01966e7745..c1ecfbd6ae 100644 --- a/lib/eal/windows/rte_thread.c +++ b/lib/eal/windows/rte_thread.c @@ -13,6 +13,54 @@ struct eal_tls_key { DWORD thread_index; }; +/* Translates the most common error codes related to threads */ +static int +thread_translate_win32_error(DWORD error) +{ + switch (error) { + case ERROR_SUCCESS: + return 0; + + case ERROR_INVALID_PARAMETER: + return EINVAL; + + case ERROR_INVALID_HANDLE: + return EFAULT; + + case ERROR_NOT_ENOUGH_MEMORY: + /* FALLTHROUGH */ + case ERROR_NO_SYSTEM_RESOURCES: + return ENOMEM; + + case ERROR_PRIVILEGE_NOT_HELD: + /* FALLTHROUGH */ + case ERROR_ACCESS_DENIED: + return EACCES; + + case ERROR_ALREADY_EXISTS: + return EEXIST; + + case ERROR_POSSIBLE_DEADLOCK: + return EDEADLK; + + case ERROR_INVALID_FUNCTION: + /* FALLTHROUGH */ + case ERROR_CALL_NOT_IMPLEMENTED: + return ENOSYS; + } + + return EINVAL; +} + +static int +thread_log_last_error(const char *message) +{ + DWORD error = GetLastError(); + RTE_LOG(DEBUG, EAL, "GetLastError()=%lu: %s\n", error, message); + + return thread_translate_win32_error(error); +} + rte_thread_t rte_thread_self(void) { @@ -42,7 +90,7 @@ rte_thread_attr_init(rte_thread_attr_t *attr) int rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, -rte_cpuset_t *cpuset) + rte_cpuset_t *cpuset) { RTE_VERIFY(thread_attr != NULL); thread_attr->cpuset = *cpuset; @@ -52,7 +100,7 @@ rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, int rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, -rte_cpuset_t *cpuset) + rte_cpuset_t *cpuset) { RTE_VERIFY(thread_attr != NULL); @@ -63,7 +111,7 @@ rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, int rte_thread_attr_set_priority(rte_thread_attr_t *thread_attr, -enum rte_thread_priority priority) + enum rte_thread_priority priority) { RTE_VERIFY(thread_attr != NULL); @@ -76,18 +124,18 @@ int rte_thread_key_create(rte_thread_key *key, __rte_unused void (*destructor)(void *)) { + int ret; + *key = malloc(sizeof(**key)); if ((*key) == NULL) { RTE_LOG(DEBUG, EAL, "Cannot allocate TLS key.\n"); - rte_errno = ENOMEM; - return -1; + return ENOMEM; } (*key)->thread_index = TlsAlloc();
[dpdk-dev] [PATCH v12 04/10] eal: implement functions for thread affinity management
From: Narcisa Vasile Implement functions for getting/setting thread affinity. Threads can be pinned to specific cores by setting their affinity attribute. Signed-off-by: Narcisa Vasile Signed-off-by: Dmitry Malloy --- lib/eal/common/rte_thread.c | 16 lib/eal/include/rte_thread.h | 36 +++ lib/eal/version.map | 2 + lib/eal/windows/eal_lcore.c | 176 +- lib/eal/windows/eal_windows.h | 10 ++ lib/eal/windows/rte_thread.c | 125 +++- 6 files changed, 319 insertions(+), 46 deletions(-) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index 27ad1c7eb0..73b7b3141c 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -34,6 +34,22 @@ rte_thread_equal(rte_thread_t t1, rte_thread_t t2) return pthread_equal((pthread_t)t1.opaque_id, (pthread_t)t2.opaque_id); } +int +rte_thread_set_affinity_by_id(rte_thread_t thread_id, + const rte_cpuset_t *cpuset) +{ + return pthread_setaffinity_np((pthread_t)thread_id.opaque_id, + sizeof(*cpuset), cpuset); +} + +int +rte_thread_get_affinity_by_id(rte_thread_t thread_id, + rte_cpuset_t *cpuset) +{ + return pthread_getaffinity_np((pthread_t)thread_id.opaque_id, + sizeof(*cpuset), cpuset); +} + int rte_thread_attr_init(rte_thread_attr_t *attr) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index bf649c2fe6..ca4ade60e2 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -87,6 +87,42 @@ int rte_thread_equal(rte_thread_t t1, rte_thread_t t2); #ifdef RTE_HAS_CPUSET +/** + * Set the affinity of thread 'thread_id' to the cpu set + * specified by 'cpuset'. + * + * @param thread_id + *Id of the thread for which to set the affinity. + * + * @param cpuset + * Pointer to CPU affinity to set. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_set_affinity_by_id(rte_thread_t thread_id, + const rte_cpuset_t *cpuset); + +/** + * Get the affinity of thread 'thread_id' and store it + * in 'cpuset'. + * + * @param thread_id + *Id of the thread for which to get the affinity. + * + * @param cpuset + * Pointer for storing the affinity value. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_get_affinity_by_id(rte_thread_t thread_id, + rte_cpuset_t *cpuset); + /** * Initialize the attributes of a thread. * These attributes can be passed to the rte_thread_create() function diff --git a/lib/eal/version.map b/lib/eal/version.map index 9ffa5eb15e..7ed4cd779e 100644 --- a/lib/eal/version.map +++ b/lib/eal/version.map @@ -433,6 +433,8 @@ EXPERIMENTAL { rte_thread_attr_get_affinity; rte_thread_attr_set_affinity; rte_thread_attr_set_priority; + rte_thread_get_affinity_by_id; + rte_thread_set_affinity_by_id; }; INTERNAL { diff --git a/lib/eal/windows/eal_lcore.c b/lib/eal/windows/eal_lcore.c index 476c2d2bdf..295af50698 100644 --- a/lib/eal/windows/eal_lcore.c +++ b/lib/eal/windows/eal_lcore.c @@ -2,7 +2,6 @@ * Copyright(c) 2019 Intel Corporation */ -#include #include #include @@ -27,13 +26,15 @@ struct socket_map { }; struct cpu_map { - unsigned int socket_count; unsigned int lcore_count; + unsigned int socket_count; + unsigned int cpu_count; struct lcore_map lcores[RTE_MAX_LCORE]; struct socket_map sockets[RTE_MAX_NUMA_NODES]; + GROUP_AFFINITY cpus[CPU_SETSIZE]; }; -static struct cpu_map cpu_map = { 0 }; +static struct cpu_map cpu_map; /* eal_create_cpu_map() is called before logging is initialized */ static void @@ -47,13 +48,118 @@ log_early(const char *format, ...) va_end(va); } +static int +eal_query_group_affinity(void) +{ + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *infos = NULL; + unsigned int *cpu_count = &cpu_map.cpu_count; + DWORD infos_size = 0; + int ret = 0; + USHORT group_count; + KAFFINITY affinity; + USHORT group_no; + unsigned int i; + + if (!GetLogicalProcessorInformationEx(RelationGroup, NULL, + &infos_size)) { + DWORD error = GetLastError(); + if (error != ERROR_INSUFFICIENT_BUFFER) { + log_early("Cannot get group information size, " + "error %lu\n", error); + rte_errno = EINVAL; + ret = -1; + goto cleanup; + } + } + + infos = malloc(infos_size); + if (infos == NULL) { + log_early("Cannot allocate memory for NUMA node information\n"); + rte_errno = ENOMEM; +
[dpdk-dev] [PATCH v12 05/10] eal: implement thread priority management functions
From: Narcisa Vasile Add function for setting the priority for a thread. Priorities on multiple platforms are similarly determined by a priority value and a priority class/policy. 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 * priority THREAD_PRIORITY_TIME_CRITICAL Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 49 ++ lib/eal/include/rte_thread.h | 17 ++ lib/eal/version.map | 1 + lib/eal/windows/rte_thread.c | 66 4 files changed, 133 insertions(+) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index 73b7b3141c..fcebf7097c 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -50,6 +50,55 @@ rte_thread_get_affinity_by_id(rte_thread_t thread_id, sizeof(*cpuset), cpuset); } +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; +} + +int +rte_thread_set_priority(rte_thread_t thread_id, + enum rte_thread_priority priority) +{ + int ret; + int policy; + struct sched_param param; + + 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_attr_init(rte_thread_attr_t *attr) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index ca4ade60e2..5514b2f57f 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -215,6 +215,23 @@ void rte_thread_get_affinity(rte_cpuset_t *cpusetp); #endif /* RTE_HAS_CPUSET */ +/** + * 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. diff --git a/lib/eal/version.map b/lib/eal/version.map index 7ed4cd779e..df01e4 100644 --- a/lib/eal/version.map +++ b/lib/eal/version.map @@ -435,6 +435,7 @@ EXPERIMENTAL { rte_thread_attr_set_priority; rte_thread_get_affinity_by_id; 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 0127119f49..fb04718f58 100644 --- a/lib/eal/windows/rte_thread.c +++ b/lib/eal/windows/rte_thread.c @@ -200,6 +200,72 @@ rte_thread_get_affinity_by_id(rte_thread_t thread_id, return ret; } +static int +thread_map_priority_to_os_value(enum rte_thread_priority eal_pri, + int *os_pri, int *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
[dpdk-dev] [PATCH v12 08/10] eal: implement functions for thread barrier management
From: Narcisa Vasile Add functions for barrier init, destroy, wait. A portable type is used to represent a barrier identifier. The rte_thread_barrier_wait() function returns the same value on all platforms. Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 61 lib/eal/include/rte_thread.h | 58 ++ lib/eal/version.map | 3 ++ lib/eal/windows/rte_thread.c | 56 + 4 files changed, 178 insertions(+) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index ebae4a8af1..3fdb267337 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -312,6 +312,67 @@ rte_thread_mutex_destroy(rte_thread_mutex *mutex) return ret; } +int +rte_thread_barrier_init(rte_thread_barrier *barrier, int count) +{ + int ret = 0; + pthread_barrier_t *pthread_barrier = NULL; + + RTE_VERIFY(barrier != NULL); + RTE_VERIFY(count > 0); + + pthread_barrier = calloc(1, sizeof(*pthread_barrier)); + if (pthread_barrier == NULL) { + RTE_LOG(DEBUG, EAL, "Unable to initialize barrier. Insufficient memory!\n"); + ret = ENOMEM; + goto cleanup; + } + ret = pthread_barrier_init(pthread_barrier, NULL, count); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "Failed to init barrier, ret = %d\n", ret); + goto cleanup; + } + + barrier->barrier_id = pthread_barrier; + pthread_barrier = NULL; + +cleanup: + free(pthread_barrier); + return ret; +} + +int +rte_thread_barrier_wait(rte_thread_barrier *barrier) +{ + int ret = 0; + + RTE_VERIFY(barrier != NULL); + RTE_VERIFY(barrier->barrier_id != NULL); + + ret = pthread_barrier_wait(barrier->barrier_id); + if (ret == PTHREAD_BARRIER_SERIAL_THREAD) + ret = RTE_THREAD_BARRIER_SERIAL_THREAD; + + return ret; +} + +int +rte_thread_barrier_destroy(rte_thread_barrier *barrier) +{ + int ret = 0; + + RTE_VERIFY(barrier != NULL); + + ret = pthread_barrier_destroy(barrier->barrier_id); + if (ret != 0) + RTE_LOG(DEBUG, EAL, "Failed to destroy barrier: %d\n", ret); + + free(barrier->barrier_id); + barrier->barrier_id = NULL; + + return ret; +} + int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 7e813b573d..40da83467b 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -76,6 +76,18 @@ typedef struct rte_thread_mutex_tag { void *mutex_id; /**< mutex identifier */ } rte_thread_mutex; +/** + * Returned by rte_thread_barrier_wait() when call is successful. + */ +#define RTE_THREAD_BARRIER_SERIAL_THREAD -1 + +/** + * Thread barrier representation. + */ +typedef struct rte_thread_barrier_tag { + void *barrier_id; /**< barrrier identifier */ +} rte_thread_barrier; + /** * TLS key type, an opaque pointer. */ @@ -381,6 +393,52 @@ int rte_thread_mutex_unlock(rte_thread_mutex *mutex); __rte_experimental int rte_thread_mutex_destroy(rte_thread_mutex *mutex); +/** + * Initializes a synchronization barrier. + * + * @param barrier + *A pointer that references the newly created 'barrier' object. + * + * @param count + *The number of threads that must enter the barrier before + *the threads can continue execution. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_barrier_init(rte_thread_barrier *barrier, int count); + +/** + * Causes the calling thread to wait at the synchronization barrier 'barrier'. + * + * @param barrier + *The barrier used for synchronizing the threads. + * + * @return + * Return RTE_THREAD_BARRIER_SERIAL_THREAD for the thread synchronized + * at the barrier. + * Return 0 for all other threads. + * Return a positive errno-style error number, in case of failure. + */ +__rte_experimental +int rte_thread_barrier_wait(rte_thread_barrier *barrier); + +/** + * Releases all resources used by a synchronization barrier + * and uninitializes it. + * + * @param barrier + *The barrier to be destroyed. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_barrier_destroy(rte_thread_barrier *barrier); + /** * Create a TLS data key visible to all threads in the process. * the created key is later used to get/set a value. diff --git a/lib/eal/version.map b/lib/eal/version.map index a1c7a8e87d..c081fdd96c 100644 --- a/lib/eal/version.map +++ b/lib/eal/version.map @@ -443,6 +443,9 @@ EXPERIMENTAL { rte_thread_mutex_lock; rte_thread_mutex_unlock; rte_thread_mutex_destroy; + rte_thread_barrier_init; +
[dpdk-dev] [PATCH v12 06/10] eal: add thread lifetime management
From: Narcisa Vasile Add functions for thread creation, joining, detaching. The *rte_thread_create()* function can optionally receive an rte_thread_attr_t object that will cause the thread to be created with the affinity and priority described by the attributes object. If no rte_thread_attr_t is passed (parameter is NULL), the default affinity and priority are used. On Windows, the function executed by a thread when the thread starts is represeneted by a function pointer of type DWORD (*func) (void*). On other platforms, the function pointer is a void* (*func) (void*). Performing a cast between these two types of function pointers to uniformize the API on all platforms may result in undefined behavior. TO fix this issue, a wrapper that respects the signature required by CreateThread() has been created on Windows. Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 107 + lib/eal/include/rte_thread.h| 55 + lib/eal/version.map | 3 + lib/eal/windows/include/sched.h | 2 +- lib/eal/windows/rte_thread.c| 138 5 files changed, 304 insertions(+), 1 deletion(-) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index fcebf7097c..a0a51bc190 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -144,6 +144,113 @@ rte_thread_attr_set_priority(rte_thread_attr_t *thread_attr, return 0; } +int +rte_thread_create(rte_thread_t *thread_id, + const rte_thread_attr_t *thread_attr, + rte_thread_func thread_func, void *args) +{ + int ret = 0; + pthread_attr_t attr; + pthread_attr_t *attrp = NULL; + struct sched_param param = { + .sched_priority = 0, + }; + int policy = SCHED_OTHER; + + if (thread_attr != NULL) { + ret = pthread_attr_init(&attr); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_init failed\n"); + goto cleanup; + } + + attrp = &attr; + + if (thread_attr->priority != RTE_THREAD_PRIORITY_UNDEFINED) { + /* +* Set the inherit scheduler parameter to explicit, +* otherwise the priority attribute is ignored. +*/ + ret = pthread_attr_setinheritsched(attrp, + PTHREAD_EXPLICIT_SCHED); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_setinheritsched failed\n"); + goto cleanup; + } + + ret = thread_map_priority_to_os_value( + thread_attr->priority, + ¶m.sched_priority, &policy + ); + if (ret != 0) + goto cleanup; + + ret = pthread_attr_setschedpolicy(attrp, policy); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_setschedpolicy failed\n"); + goto cleanup; + } + + ret = pthread_attr_setschedparam(attrp, ¶m); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_setschedparam failed\n"); + goto cleanup; + } + } + + if (CPU_COUNT(&thread_attr->cpuset) > 0) { + ret = pthread_attr_setaffinity_np(attrp, + sizeof(thread_attr->cpuset), + &thread_attr->cpuset); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_attr_setaffinity_np failed\n"); + goto cleanup; + } + } + } + + ret = pthread_create((pthread_t *)&thread_id->opaque_id, attrp, + thread_func, args); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_create failed\n"); + goto cleanup; + } + +cleanup: + if (attrp != NULL) + pthread_attr_destroy(&attr); + + return ret; +} + +int +rte_thread_join(rte_thread_t thread_id, unsigned long *value_ptr) +{ + int ret = 0; + void *res = NULL; + void **pres = NULL; + + if (value_ptr != NULL) + pres = &res; + + ret = pthread_join((pthread_t)thread_id.opaque_id, pres); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "pthread_join failed\n"); + return ret; + } + + if (pres != NULL) + *value_ptr = *(unsigned long *)(*pres); + + return 0; +} + +i
[dpdk-dev] [PATCH v12 09/10] eal: add EAL argument for setting thread priority
From: Narcisa Vasile Allow the user to choose the thread priority through an EAL command line argument. The user can choose thread priority through an EAL parameter, when starting an application. If EAL parameter is not used, the per-platform default value for thread priority is used. Otherwise administrator has an option to set one of available options: --thread-prio normal --thread-prio realtime Example: ./dpdk-l2fwd -l 0-3 -n 4 --thread-prio normal -- -q 8 -p Signed-off-by: Narcisa Vasile --- lib/eal/common/eal_common_options.c | 28 +++- lib/eal/common/eal_internal_cfg.h | 2 ++ lib/eal/common/eal_options.h| 2 ++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/eal/common/eal_common_options.c b/lib/eal/common/eal_common_options.c index ff5861b5f3..9d29696b84 100644 --- a/lib/eal/common/eal_common_options.c +++ b/lib/eal/common/eal_common_options.c @@ -107,6 +107,7 @@ eal_long_options[] = { {OPT_TELEMETRY, 0, NULL, OPT_TELEMETRY_NUM}, {OPT_NO_TELEMETRY, 0, NULL, OPT_NO_TELEMETRY_NUM }, {OPT_FORCE_MAX_SIMD_BITWIDTH, 1, NULL, OPT_FORCE_MAX_SIMD_BITWIDTH_NUM}, + {OPT_THREAD_PRIORITY, 1, NULL, OPT_THREAD_PRIORITY_NUM}, /* legacy options that will be removed in future */ {OPT_PCI_BLACKLIST, 1, NULL, OPT_PCI_BLACKLIST_NUM}, @@ -1412,6 +1413,24 @@ eal_parse_simd_bitwidth(const char *arg) return 0; } +static int +eal_parse_thread_priority(const char *arg) +{ + struct internal_config *internal_conf = + eal_get_internal_configuration(); + enum rte_thread_priority priority; + + if (!strncmp("normal", arg, sizeof("normal"))) + priority = RTE_THREAD_PRIORITY_NORMAL; + else if (!strncmp("realtime", arg, sizeof("realtime"))) + priority = RTE_THREAD_PRIORITY_REALTIME_CRITICAL; + else + return -1; + + internal_conf->thread_priority = priority; + return 0; +} + static int eal_parse_base_virtaddr(const char *arg) { @@ -1825,7 +1844,13 @@ eal_parse_common_option(int opt, const char *optarg, return -1; } break; - + case OPT_THREAD_PRIORITY_NUM: + if (eal_parse_thread_priority(optarg) < 0) { + RTE_LOG(ERR, EAL, "invalid parameter for --" + OPT_THREAD_PRIORITY "\n"); + return -1; + } + break; /* don't know what to do, leave this to caller */ default: return 1; @@ -2088,6 +2113,7 @@ eal_common_usage(void) " (can be used multiple times)\n" " --"OPT_VMWARE_TSC_MAP"Use VMware TSC map instead of native RDTSC\n" " --"OPT_PROC_TYPE" Type of this process (primary|secondary|auto)\n" + " --"OPT_THREAD_PRIORITY" Set threads priority (normal|realtime)\n" #ifndef RTE_EXEC_ENV_WINDOWS " --"OPT_SYSLOG"Set syslog facility\n" #endif diff --git a/lib/eal/common/eal_internal_cfg.h b/lib/eal/common/eal_internal_cfg.h index d6c0470eb8..b2996cd65b 100644 --- a/lib/eal/common/eal_internal_cfg.h +++ b/lib/eal/common/eal_internal_cfg.h @@ -94,6 +94,8 @@ struct internal_config { unsigned int no_telemetry; /**< true to disable Telemetry */ struct simd_bitwidth max_simd_bitwidth; /**< max simd bitwidth path to use */ + enum rte_thread_priority thread_priority; + /**< thread priority to configure */ }; void eal_reset_internal_config(struct internal_config *internal_cfg); diff --git a/lib/eal/common/eal_options.h b/lib/eal/common/eal_options.h index 7b348e707f..9f5b209f64 100644 --- a/lib/eal/common/eal_options.h +++ b/lib/eal/common/eal_options.h @@ -93,6 +93,8 @@ enum { OPT_NO_TELEMETRY_NUM, #define OPT_FORCE_MAX_SIMD_BITWIDTH "force-max-simd-bitwidth" OPT_FORCE_MAX_SIMD_BITWIDTH_NUM, +#define OPT_THREAD_PRIORITY "thread-prio" + OPT_THREAD_PRIORITY_NUM, /* legacy option that will be removed in future */ #define OPT_PCI_BLACKLIST "pci-blacklist" -- 2.31.0.vfs.0.1
[dpdk-dev] [PATCH v12 07/10] eal: implement functions for mutex management
From: Narcisa Vasile Add functions for mutex init, destroy, lock, unlock. Add RTE_STATIC_MUTEX macro to replace static initialization of mutexes. Windows does not have a static initializer. Initialization is only done through InitializeCriticalSection(). The RTE_STATIC_MUTEX calls into the rte_thread_mutex_init() function that performs the actual mutex initialization. Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 61 +++ lib/eal/include/rte_thread.h | 94 lib/eal/version.map | 4 ++ lib/eal/windows/rte_thread.c | 53 4 files changed, 212 insertions(+) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index a0a51bc190..ebae4a8af1 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -251,6 +251,67 @@ rte_thread_detach(rte_thread_t thread_id) return pthread_detach((pthread_t)thread_id.opaque_id); } +int +rte_thread_mutex_init(rte_thread_mutex *mutex) +{ + int ret = 0; + pthread_mutex_t *m = NULL; + + RTE_VERIFY(mutex != NULL); + + m = calloc(1, sizeof(*m)); + if (m == NULL) { + RTE_LOG(DEBUG, EAL, "Unable to initialize mutex. Insufficient memory!\n"); + ret = ENOMEM; + goto cleanup; + } + + ret = pthread_mutex_init(m, NULL); + if (ret != 0) { + RTE_LOG(DEBUG, EAL, "Failed to init mutex. ret = %d\n", ret); + goto cleanup; + } + + mutex->mutex_id = m; + m = NULL; + +cleanup: + free(m); + return ret; +} + +int +rte_thread_mutex_lock(rte_thread_mutex *mutex) +{ + RTE_VERIFY(mutex != NULL); + + return pthread_mutex_lock((pthread_mutex_t *)mutex->mutex_id); +} + +int +rte_thread_mutex_unlock(rte_thread_mutex *mutex) +{ + RTE_VERIFY(mutex != NULL); + + return pthread_mutex_unlock((pthread_mutex_t *)mutex->mutex_id); +} + +int +rte_thread_mutex_destroy(rte_thread_mutex *mutex) +{ + int ret = 0; + RTE_VERIFY(mutex != NULL); + + ret = pthread_mutex_destroy((pthread_mutex_t *)mutex->mutex_id); + if (ret != 0) + RTE_LOG(DEBUG, EAL, "Unable to destroy mutex, ret = %d\n", ret); + + free(mutex->mutex_id); + mutex->mutex_id = NULL; + + return ret; +} + int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 098c3ba343..7e813b573d 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -56,6 +56,26 @@ typedef struct { #endif /* RTE_HAS_CPUSET */ +#define RTE_DECLARE_MUTEX(private_lock) rte_thread_mutex private_lock + +#define RTE_DEFINE_MUTEX(private_lock)\ +RTE_INIT(__rte_ ## private_lock ## _init)\ +{\ + RTE_VERIFY(rte_thread_mutex_init(&private_lock) == 0);\ +} + +#define RTE_STATIC_MUTEX(private_lock)\ +static RTE_DECLARE_MUTEX(private_lock);\ +RTE_DEFINE_MUTEX(private_lock) + + +/** + * Thread mutex representation. + */ +typedef struct rte_thread_mutex_tag { + void *mutex_id; /**< mutex identifier */ +} rte_thread_mutex; + /** * TLS key type, an opaque pointer. */ @@ -268,6 +288,28 @@ int rte_thread_join(rte_thread_t thread_id, unsigned long *value_ptr); __rte_experimental int rte_thread_detach(rte_thread_t thread_id); +/** + * Set core affinity of the current thread. + * Support both EAL and non-EAL thread and update TLS. + * + * @param cpusetp + * Pointer to CPU affinity to set. + * + * @return + * On success, return 0; otherwise return -1; + */ +int rte_thread_set_affinity(rte_cpuset_t *cpusetp); + +/** + * Get core affinity of the current thread. + * + * @param cpusetp + * Pointer to CPU affinity of current thread. + * It presumes input is not NULL, otherwise it causes panic. + * + */ +void rte_thread_get_affinity(rte_cpuset_t *cpusetp); + #endif /* RTE_HAS_CPUSET */ /** @@ -287,6 +329,58 @@ __rte_experimental int rte_thread_set_priority(rte_thread_t thread_id, enum rte_thread_priority priority); +/** + * Initializes a mutex. + * + * @param mutex + *The mutex to be initialized. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_mutex_init(rte_thread_mutex *mutex); + +/** + * Locks a mutex. + * + * @param mutex + *The mutex to be locked. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_mutex_lock(rte_thread_mutex *mutex); + +/** + * Unlocks a mutex. + * + * @param mutex + *The mutex to be unlocked. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_mutex_unlock(rte_thread_mutex *mutex); + +/** + * Releases all resources associated with a
[dpdk-dev] [PATCH v12 10/10] Add unit tests for thread API
From: Narcisa Vasile As a new API for threading is introduced, a set of unit tests have been added to test the new interface. Signed-off-by: Narcisa Vasile --- app/test/meson.build| 2 + app/test/test_threads.c | 419 2 files changed, 421 insertions(+) create mode 100644 app/test/test_threads.c diff --git a/app/test/meson.build b/app/test/meson.build index a7611686ad..bfa60773bd 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -140,6 +140,7 @@ test_sources = files( 'test_table_tables.c', 'test_tailq.c', 'test_thash.c', +'test_threads.c', 'test_timer.c', 'test_timer_perf.c', 'test_timer_racecond.c', @@ -276,6 +277,7 @@ fast_tests = [ ['reorder_autotest', true], ['service_autotest', true], ['thash_autotest', true], +['threads_autotest, true'], ['trace_autotest', true], ] diff --git a/app/test/test_threads.c b/app/test/test_threads.c new file mode 100644 index 00..beaa303506 --- /dev/null +++ b/app/test/test_threads.c @@ -0,0 +1,419 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) 2021 Microsoft. + */ + +#include + +#include + +#include "test.h" + +#define THREADS_COUNT 20 + +#define TEST_THREADS_LOG(func) \ + printf("Error at line %d. %s failed!\n", __LINE__, func) + +static void * +thread_loop_self(void *arg) +{ + rte_thread_t *id = arg; + + *id = rte_thread_self(); + + return NULL; +} + +static int +test_thread_self(void) +{ + rte_thread_t threads_ids[THREADS_COUNT]; + rte_thread_t self_ids[THREADS_COUNT] = {}; + size_t i; + size_t j; + int ret = 0; + + for (i = 0; i < THREADS_COUNT; ++i) { + if (rte_thread_create(&threads_ids[i], NULL, thread_loop_self, + &self_ids[i]) != 0) { + printf("Error, Only %zu threads created.\n", i); + break; + } + } + + for (j = 0; j < i; ++j) { + ret = rte_thread_join(threads_ids[j], NULL); + if (ret != 0) { + TEST_THREADS_LOG("rte_thread_join()"); + return -1; + } + + if (rte_thread_equal(threads_ids[j], self_ids[j]) == 0) + ret = -1; + } + + return ret; +} + +struct thread_context { + rte_thread_barrier *barrier; + size_t *thread_count; +}; + +static void * +thread_loop_barrier(void *arg) +{ + + struct thread_context *ctx = arg; + + (void)__atomic_add_fetch(ctx->thread_count, 1, __ATOMIC_RELAXED); + + if (rte_thread_barrier_wait(ctx->barrier) > 0) + TEST_THREADS_LOG("rte_thread_barrier_wait()"); + + return NULL; +} + +static int +test_thread_barrier(void) +{ + rte_thread_t threads_ids[THREADS_COUNT]; + struct thread_context ctx[THREADS_COUNT] = {}; + rte_thread_barrier barrier; + size_t count = 0; + size_t i; + size_t j; + int ret = 0; + + ret = rte_thread_barrier_init(&barrier, THREADS_COUNT + 1); + if (ret != 0) { + TEST_THREADS_LOG("rte_thread_barrier_init()"); + return -1; + } + + for (i = 0; i < THREADS_COUNT; ++i) { + ctx[i].thread_count = &count; + ctx[i].barrier = &barrier; + if (rte_thread_create(&threads_ids[i], NULL, + thread_loop_barrier, &ctx[i]) != 0) { + printf("Error, Only %zu threads created.\n", i); + ret = -1; + goto error; + } + } + + ret = rte_thread_barrier_wait(ctx->barrier); + if (ret > 0) { + TEST_THREADS_LOG("rte_thread_barrier_wait()"); + ret = -1; + goto error; + } + + if (count != i) { + ret = -1; + printf("Error, expected thread count(%zu) to be equal " + "to the number of threads that wait at the barrier(%zu)\n", + count, i); + goto error; + } + +error: + for (j = 0; j < i; ++j) { + ret = rte_thread_join(threads_ids[j], NULL); + if (ret != 0) { + TEST_THREADS_LOG("rte_thread_join()"); + ret = -1; + break; + } + } + + ret = rte_thread_barrier_destroy(&barrier); + if (ret != 0) { + TEST_THREADS_LOG("rte_thread_barrier_destroy()"); + ret = -1; + } + + return ret; +} + +static size_t val; + +static void * +thread_loop_mutex(void *arg) +{ + rte_thread_mutex *mutex = arg; + + rte_thread_mutex_lock(mutex); + val++; + rte_thread_mutex_unlock(mutex); + + return NULL; +} + +static int +test_threa
Re: [dpdk-dev] [PATCH v12 01/10] eal: add basic threading functions
On Mon, Aug 02, 2021 at 10:32:17AM -0700, Narcisa Ana Maria Vasile wrote: > From: Narcisa Vasile > > Use a portable, type-safe representation for the thread identifier. > Add functions for comparing thread ids and obtaining the thread id > for the current thread. > > Signed-off-by: Narcisa Vasile > --- > lib/eal/common/meson.build| 1 + > lib/eal/{unix => common}/rte_thread.c | 57 --- > lib/eal/include/rte_thread.h | 48 +- > lib/eal/unix/meson.build | 1 - > lib/eal/version.map | 3 ++ > lib/eal/windows/rte_thread.c | 17 > 6 files changed, 95 insertions(+), 32 deletions(-) > rename lib/eal/{unix => common}/rte_thread.c (66%) > > diff --git a/lib/eal/common/meson.build b/lib/eal/common/meson.build Hello, I see the following error on this patch: ninja: error: loading 'build.ninja': No such file or directory https://lab.dpdk.org/results/dashboard/patchsets/18090/ Locally, the build succeedes. How can I see more information about this build error?
[dpdk-dev] [PATCH v13 00/10] eal: Add EAL API for threading
From: Narcisa Vasile EAL thread API **Problem Statement** DPDK currently uses the pthread interface to create and manage threads. Windows does not support the POSIX thread programming model, so it currently relies on a header file that hides the Windows calls under pthread matched interfaces. Given that EAL should isolate the environment specifics from the applications and libraries and mediate all the communication with the operating systems, a new EAL interface is needed for thread management. **Goals** * Introduce a generic EAL API for threading support that will remove the current Windows pthread.h shim. * Replace references to pthread_* across the DPDK codebase with the new RTE_THREAD_* API. * Allow users to choose between using the RTE_THREAD_* API or a 3rd party thread library through a configuration option. **Design plan** New API main files: * rte_thread.h (librte_eal/include) * rte_thread.c (librte_eal/windows) * rte_thread.c (librte_eal/common) **A schematic example of the design** -- lib/librte_eal/include/rte_thread.h int rte_thread_create(); lib/librte_eal/common/rte_thread.c int rte_thread_create() { return pthread_create(); } lib/librte_eal/windows/rte_thread.c int rte_thread_create() { return CreateThread(); } - **Thread attributes** When or after a thread is created, specific characteristics of the thread can be adjusted. Given that the thread characteristics that are of interest for DPDK applications are affinity and priority, the following structure that represents thread attributes has been defined: typedef struct { enum rte_thread_priority priority; rte_cpuset_t cpuset; } rte_thread_attr_t; The *rte_thread_create()* function can optionally receive an rte_thread_attr_t object that will cause the thread to be created with the affinity and priority described by the attributes object. If no rte_thread_attr_t is passed (parameter is NULL), the default affinity and priority are used. An rte_thread_attr_t object can also be set to the default values by calling *rte_thread_attr_init()*. *Priority* is represented through an enum that currently advertises two values for priority: - RTE_THREAD_PRIORITY_NORMAL - RTE_THREAD_PRIORITY_REALTIME_CRITICAL The enum can be extended to allow for multiple priority levels. rte_thread_set_priority - sets the priority of a thread rte_thread_attr_set_priority - updates an rte_thread_attr_t object with a new value for priority The user can choose thread priority through an EAL parameter, when starting an application. If EAL parameter is not used, the per-platform default value for thread priority is used. Otherwise administrator has an option to set one of available options: --thread-prio normal --thread-prio realtime Example: ./dpdk-l2fwd -l 0-3 -n 4 –thread-prio normal -- -q 8 -p *Affinity* is described by the already known “rte_cpuset_t” type. rte_thread_attr_set/get_affinity - sets/gets the affinity field in a rte_thread_attr_t object rte_thread_set/get_affinity – sets/gets the affinity of a thread **Errors** A translation function that maps Windows error codes to errno-style error codes is provided. **Future work** The long term plan is for EAL to provide full threading support: * Add support for conditional variables * Add support for pthread_mutex_trylock * Additional functionality offered by pthread_* (such as pthread_setname_np, etc.) v13: - Fix syntax error in unit tests v12: - Fix freebsd warning about initializer in unit tests v11: - Add unit tests for thread API - Rebase v10: - Remove patch no. 10. It will be broken down in subpatches and sent as a different patchset that depends on this one. This is done due to the ABI breaks that would be caused by patch 10. - Replace unix/rte_thread.c with common/rte_thread.c - Remove initializations that may prevent compiler from issuing useful warnings. - Remove rte_thread_types.h and rte_windows_thread_types.h - Remove unneeded priority macros (EAL_THREAD_PRIORITY*) - Remove functions that retrieves thread handle from process handle - Remove rte_thread_cancel() until same behavior is obtained on all platforms. - Fix rte_thread_detach() function description, return value and remove empty line. - Reimplement mutex functions. Add compatible representation for mutex identifier. Add macro to replace static mutex initialization instances. - Fix commit messages (lines too long, remove unicode symbols) v9: - Sign patches v8: - Rebase - Add rte_thread_detach() API - Set default priority, when user did not specify a value v7: Based on DmitryK's review: - Change thread id representation - Change mutex id representation - Implement static mutex inititalizer for Windows - Change barrier identifier representation - Improve comm
[dpdk-dev] [PATCH v13 01/10] eal: add basic threading functions
From: Narcisa Vasile Use a portable, type-safe representation for the thread identifier. Add functions for comparing thread ids and obtaining the thread id for the current thread. Signed-off-by: Narcisa Vasile --- lib/eal/common/meson.build| 1 + lib/eal/{unix => common}/rte_thread.c | 57 --- lib/eal/include/rte_thread.h | 48 +- lib/eal/unix/meson.build | 1 - lib/eal/version.map | 3 ++ lib/eal/windows/rte_thread.c | 17 6 files changed, 95 insertions(+), 32 deletions(-) rename lib/eal/{unix => common}/rte_thread.c (66%) diff --git a/lib/eal/common/meson.build b/lib/eal/common/meson.build index edfca9..eda250247b 100644 --- a/lib/eal/common/meson.build +++ b/lib/eal/common/meson.build @@ -80,6 +80,7 @@ sources += files( 'rte_random.c', 'rte_reciprocal.c', 'rte_service.c', +'rte_thread.c', 'rte_version.c', ) diff --git a/lib/eal/unix/rte_thread.c b/lib/eal/common/rte_thread.c similarity index 66% rename from lib/eal/unix/rte_thread.c rename to lib/eal/common/rte_thread.c index c72d619ec1..92a7451b0a 100644 --- a/lib/eal/unix/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause * Copyright 2021 Mellanox Technologies, Ltd + * Copyright(c) 2021 Microsoft Corporation */ #include @@ -16,25 +17,41 @@ struct eal_tls_key { pthread_key_t thread_index; }; +rte_thread_t +rte_thread_self(void) +{ + rte_thread_t thread_id; + + thread_id.opaque_id = (uintptr_t)pthread_self(); + + return thread_id; +} + +int +rte_thread_equal(rte_thread_t t1, rte_thread_t t2) +{ + return pthread_equal((pthread_t)t1.opaque_id, (pthread_t)t2.opaque_id); +} + int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { int err; + rte_thread_key k; - *key = malloc(sizeof(**key)); - if ((*key) == NULL) { + k = malloc(sizeof(*k)); + if (k == NULL) { RTE_LOG(DEBUG, EAL, "Cannot allocate TLS key.\n"); - rte_errno = ENOMEM; - return -1; + return EINVAL; } - err = pthread_key_create(&((*key)->thread_index), destructor); - if (err) { + err = pthread_key_create(&(k->thread_index), destructor); + if (err != 0) { RTE_LOG(DEBUG, EAL, "pthread_key_create failed: %s\n", strerror(err)); - free(*key); - rte_errno = ENOEXEC; - return -1; + free(k); + return err; } + *key = k; return 0; } @@ -43,18 +60,16 @@ rte_thread_key_delete(rte_thread_key key) { int err; - if (!key) { + if (key == NULL) { RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); - rte_errno = EINVAL; - return -1; + return EINVAL; } err = pthread_key_delete(key->thread_index); - if (err) { + if (err != 0) { RTE_LOG(DEBUG, EAL, "pthread_key_delete failed: %s\n", strerror(err)); free(key); - rte_errno = ENOEXEC; - return -1; + return err; } free(key); return 0; @@ -65,17 +80,15 @@ rte_thread_value_set(rte_thread_key key, const void *value) { int err; - if (!key) { + if (key == NULL) { RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); - rte_errno = EINVAL; - return -1; + return EINVAL; } err = pthread_setspecific(key->thread_index, value); - if (err) { + if (err != 0) { RTE_LOG(DEBUG, EAL, "pthread_setspecific failed: %s\n", strerror(err)); - rte_errno = ENOEXEC; - return -1; + return err; } return 0; } @@ -83,7 +96,7 @@ rte_thread_value_set(rte_thread_key key, const void *value) void * rte_thread_value_get(rte_thread_key key) { - if (!key) { + if (key == NULL) { RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); rte_errno = EINVAL; return NULL; diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 8be8ed8f36..748f64d230 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2021 Mellanox Technologies, Ltd + * Copyright(c) 2021 Microsoft Corporation */ +#include #include #include @@ -20,11 +22,45 @@ extern "C" { #endif +#include + +/** + * Thread id descriptor. + */ +typedef struct rte_thread_tag { + uintptr_t opaque_id; /**< thread identifier */ +} rte_thread_t; + /** * TLS key type, an opaque pointer. */ typedef struct eal_tls_key *
[dpdk-dev] [PATCH v13 02/10] eal: add thread attributes
From: Narcisa Vasile Implement thread attributes for: * thread affinity * thread priority Implement functions for managing thread attributes. Priority is represented through an enum that allows for two levels: - RTE_THREAD_PRIORITY_NORMAL - RTE_THREAD_PRIORITY_REALTIME_CRITICAL Affinity is described by the rte_cpuset_t type. An rte_thread_attr_t object can be set to the default values by calling rte_thread_attr_init(). Signed-off-by: Narcisa Vasile --- lib/eal/common/rte_thread.c | 46 ++ lib/eal/include/rte_thread.h | 93 lib/eal/version.map | 4 ++ lib/eal/windows/rte_thread.c | 44 + 4 files changed, 187 insertions(+) diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c index 92a7451b0a..e1a4d7eae4 100644 --- a/lib/eal/common/rte_thread.c +++ b/lib/eal/common/rte_thread.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,51 @@ rte_thread_equal(rte_thread_t t1, rte_thread_t t2) return pthread_equal((pthread_t)t1.opaque_id, (pthread_t)t2.opaque_id); } +int +rte_thread_attr_init(rte_thread_attr_t *attr) +{ + RTE_VERIFY(attr != NULL); + + CPU_ZERO(&attr->cpuset); + attr->priority = RTE_THREAD_PRIORITY_NORMAL; + + return 0; +} + +int +rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, +rte_cpuset_t *cpuset) +{ + RTE_VERIFY(thread_attr != NULL); + RTE_VERIFY(cpuset != NULL); + + thread_attr->cpuset = *cpuset; + + return 0; +} + +int +rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, +rte_cpuset_t *cpuset) +{ + RTE_VERIFY(thread_attr != NULL); + RTE_VERIFY(cpuset != NULL); + + *cpuset = thread_attr->cpuset; + + return 0; +} + +int +rte_thread_attr_set_priority(rte_thread_attr_t *thread_attr, +enum rte_thread_priority priority) +{ + RTE_VERIFY(thread_attr != NULL); + + thread_attr->priority = priority; + return 0; +} + int rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) { diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h index 748f64d230..032ff73b36 100644 --- a/lib/eal/include/rte_thread.h +++ b/lib/eal/include/rte_thread.h @@ -31,6 +31,30 @@ typedef struct rte_thread_tag { uintptr_t opaque_id; /**< thread identifier */ } rte_thread_t; +/** + * Thread priority values. + */ +enum rte_thread_priority { + RTE_THREAD_PRIORITY_UNDEFINED = 0, + /**< priority hasn't been defined */ + RTE_THREAD_PRIORITY_NORMAL= 1, + /**< normal thread priority, the default */ + RTE_THREAD_PRIORITY_REALTIME_CRITICAL = 2, + /**< highest thread priority allowed */ +}; + +#ifdef RTE_HAS_CPUSET + +/** + * Representation for thread attributes. + */ +typedef struct { + enum rte_thread_priority priority; /**< thread priority */ + rte_cpuset_t cpuset; /**< thread affinity */ +} rte_thread_attr_t; + +#endif /* RTE_HAS_CPUSET */ + /** * TLS key type, an opaque pointer. */ @@ -63,6 +87,75 @@ int rte_thread_equal(rte_thread_t t1, rte_thread_t t2); #ifdef RTE_HAS_CPUSET +/** + * Initialize the attributes of a thread. + * These attributes can be passed to the rte_thread_create() function + * that will create a new thread and set its attributes according to attr. + * + * @param attr + * Thread attributes to initialize. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_attr_init(rte_thread_attr_t *attr); + +/** + * Set the CPU affinity value in the thread attributes pointed to + * by 'thread_attr'. + * + * @param thread_attr + * Points to the thread attributes in which affinity will be updated. + * + * @param cpuset + * Points to the value of the affinity to be set. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_attr_set_affinity(rte_thread_attr_t *thread_attr, + rte_cpuset_t *cpuset); + +/** + * Get the value of CPU affinity that is set in the thread attributes pointed + * to by 'thread_attr'. + * + * @param thread_attr + * Points to the thread attributes from which affinity will be retrieved. + * + * @param cpuset + * Pointer to the memory that will store the affinity. + * + * @return + * On success, return 0. + * On failure, return a positive errno-style error number. + */ +__rte_experimental +int rte_thread_attr_get_affinity(rte_thread_attr_t *thread_attr, + rte_cpuset_t *cpuset); + +/** + * Set the thread priority value in the thread attributes pointed to + * by 'thread_attr'. + * + * @param thread_attr + * Points to the thread attributes in which priority will be updated. + * + * @param priority + * Po