[v3] eal: add generic thread-local-storage functions

Message ID 20201217174913.14280-1-talshn@nvidia.com (mailing list archive)
State Superseded, archived
Delegated to: Thomas Monjalon
Headers
Series [v3] eal: add generic thread-local-storage functions |

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/iol-broadcom-Functional success Functional Testing PASS
ci/iol-broadcom-Performance success Performance Testing PASS
ci/iol-intel-Functional success Functional Testing PASS
ci/Intel-compilation success Compilation OK
ci/iol-intel-Performance success Performance Testing PASS
ci/iol-testing success Testing PASS

Commit Message

Tal Shnaiderman Dec. 17, 2020, 5:49 p.m. UTC
  Add support for tls functionality in EAL.

The following functions are added:
rte_tls_create_key - function to create a tls data key.
rte_tls_delete_key - function to delete a tls data key.
rte_tls_set_thread_value - function to set value bound to the tls key
rte_tls_get_thread_value - function to get value bound to the tls key

tls key will be defied by the new type rte_tls_key_t

Signed-off-by: Tal Shnaiderman <talshn@nvidia.com>
---
v3: switch from pthread shim to generic eal implementation [DmitryK]
---
 lib/librte_eal/include/meson.build |  1 +
 lib/librte_eal/include/rte_tls.h   | 88 ++++++++++++++++++++++++++++++++++++++
 lib/librte_eal/rte_eal_exports.def |  5 +++
 lib/librte_eal/unix/eal_unix_tls.c | 79 ++++++++++++++++++++++++++++++++++
 lib/librte_eal/unix/meson.build    |  1 +
 lib/librte_eal/version.map         |  6 +++
 lib/librte_eal/windows/eal_tls.c   | 72 +++++++++++++++++++++++++++++++
 lib/librte_eal/windows/meson.build |  1 +
 8 files changed, 253 insertions(+)
 create mode 100644 lib/librte_eal/include/rte_tls.h
 create mode 100644 lib/librte_eal/unix/eal_unix_tls.c
 create mode 100644 lib/librte_eal/windows/eal_tls.c
  

Comments

Dmitry Kozlyuk Dec. 17, 2020, 8:56 p.m. UTC | #1
On Thu, 17 Dec 2020 19:49:13 +0200, Tal Shnaiderman wrote:
> Add support for tls functionality in EAL.
> 
> The following functions are added:
> rte_tls_create_key - function to create a tls data key.
> rte_tls_delete_key - function to delete a tls data key.
> rte_tls_set_thread_value - function to set value bound to the tls key
> rte_tls_get_thread_value - function to get value bound to the tls key
> 
> tls key will be defied by the new type rte_tls_key_t
> 
> Signed-off-by: Tal Shnaiderman <talshn@nvidia.com>
> ---
> v3: switch from pthread shim to generic eal implementation [DmitryK]

Hi Tal,

Unix code can be placed in common/ directory, so that it can be eventually
used on Windows with external pthread implementation.

See more comments inline.

> +++ b/lib/librte_eal/include/rte_tls.h
> @@ -0,0 +1,88 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2020 Mellanox Technologies, Ltd
> + */
> +
> +#include <rte_compat.h>
> +
> +#ifndef _RTE_TLS_H_
> +#define _RTE_TLS_H_
> +
> +/**
> + * @file
> + *
> + * TLS functions
> + *
> + * Simple TLS functionality supplied by eal.
> + */

These functions are supposed to be the first part of an API that will be
used instead of naked pthread in DPDK. Maybe more generic names are in order,
like rte_thread.h and rte_thread_tls_create/destroy/get/set(). In particular,
rte_tls_*() is confusing compared to rte_lcore_*().

> +
> +/**
> + * Opaque pointer for tls key.
> + */
> +typedef struct eal_tls_key *rte_tls_key_t;

"_t" suffix is reserved by POSIX, "rte_" prefix is sufficient.

> +
> +/**
> + * Function to create a tls data key visible to all threads in the process

Typos: "TLS", "EAL".

> + * function need to be called once to create a key usable by all threads.
> + * rte_tls_key_t is an opaque pointer used to store the allocated key.
> + * and optional destructor can be set to be called when a thread expires.
> + *
> + * @param key
> + *   The rte_tls_key_t will cantain the allocated key

Typo: "cantain". I'd say: "Pointer to store the allocated rte_tls_key".

> + * @param destructor
> + *   The function to be called when the thread expires

Typo: no period (line-break will be removed by Doxygen).

> + *   Not supported on Windows OS.

Some drivers (net/mlx5, bus/dpaa, bus/fsmlc) rely on this feature.
Admittedly, it would be hard to implement. You know net/mlx5 well, how will
it be affected? Do you plan to stop relying on this feature or implement it
in the future? Anyway, LGTM for now.

> + *
> + * @return
> + *   On success, zero.
> + *   On failure, a negative error number -ENOMEM or -ENOEXEC

Let's not make POSIX codes part of the API.

> + */
> +__rte_experimental
> +int
> +rte_tls_create_key(rte_tls_key_t *key, void (*destructor)(void *));

[...]
> +int
> +rte_tls_create_key(rte_tls_key_t *key, void (*destructor)(void *))
> +{
> +	int err;
> +
> +	*key = malloc(sizeof(struct eal_tls_key));
> +	if ((*key) == NULL) {
> +		RTE_LOG(DEBUG, EAL, "Cannot allocate tls key.");
> +		return -ENOMEM;
> +	}
> +	err = pthread_key_create(&((*key)->thread_index), destructor);
> +	if (err) {
> +		RTE_LOG(DEBUG, EAL, "pthread_key_create failed: %s\n",
> +			 rte_strerror(err));

Use strerror() for OS error codes here and below.

> +		free(*key);
> +		return -ENOEXEC;
> +	}
> +	return 0;
> +}
> +

[...]
> +int
> +rte_tls_set_thread_value(rte_tls_key_t key, const void *value)
> +{
> +	if (!key)
> +		return -EINVAL;
> +	/* discard const qualifier */
> +	char *p = (char *) (uintptr_t) value;

Please declare all variables at the top of a block.

> +
> +	if (!TlsSetValue(key->thread_index, p)) {
> +		RTE_LOG_WIN32_ERR("TlsSetValue()");
> +		return -ENOEXEC;
> +	}
> +	return 0;
> +}
> +
> +void *
> +rte_tls_get_thread_value(rte_tls_key_t key)
> +{
> +	if (!key)
> +		rte_errno = EINVAL;
> +	void *output = TlsGetValue(key->thread_index);

Same as above.

> +	if (GetLastError() != ERROR_SUCCESS) {
> +		RTE_LOG_WIN32_ERR("TlsGetValue()");
> +		rte_errno = ENOEXEC;
> +		return NULL;
> +	}
> +	return output;
> +}
  
Tal Shnaiderman Dec. 18, 2020, 7:37 p.m. UTC | #2
> Subject: Re: [PATCH v3] eal: add generic thread-local-storage functions
> 
> External email: Use caution opening links or attachments
> 
> 
> On Thu, 17 Dec 2020 19:49:13 +0200, Tal Shnaiderman wrote:
> > Add support for tls functionality in EAL.
> >
> > The following functions are added:
> > rte_tls_create_key - function to create a tls data key.
> > rte_tls_delete_key - function to delete a tls data key.
> > rte_tls_set_thread_value - function to set value bound to the tls key
> > rte_tls_get_thread_value - function to get value bound to the tls key
> >
> > tls key will be defied by the new type rte_tls_key_t
> >
> > Signed-off-by: Tal Shnaiderman <talshn@nvidia.com>
> > ---
> > v3: switch from pthread shim to generic eal implementation [DmitryK]
> 
> Hi Tal,
> 
> Unix code can be placed in common/ directory, so that it can be eventually
> used on Windows with external pthread implementation.
> 
> See more comments inline.
> 
> > +++ b/lib/librte_eal/include/rte_tls.h
> > @@ -0,0 +1,88 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause
> > + * Copyright(c) 2020 Mellanox Technologies, Ltd  */
> > +
> > +#include <rte_compat.h>
> > +
> > +#ifndef _RTE_TLS_H_
> > +#define _RTE_TLS_H_
> > +
> > +/**
> > + * @file
> > + *
> > + * TLS functions
> > + *
> > + * Simple TLS functionality supplied by eal.
> > + */
> 
> These functions are supposed to be the first part of an API that will be used
> instead of naked pthread in DPDK. Maybe more generic names are in order,
> like rte_thread.h and rte_thread_tls_create/destroy/get/set(). In particular,
> rte_tls_*() is confusing compared to rte_lcore_*().
> 
> > +
> > +/**
> > + * Opaque pointer for tls key.
> > + */
> > +typedef struct eal_tls_key *rte_tls_key_t;
> 
> "_t" suffix is reserved by POSIX, "rte_" prefix is sufficient.
> 
> > +
> > +/**
> > + * Function to create a tls data key visible to all threads in the
> > +process
> 
> Typos: "TLS", "EAL".
> 

Didn't find the EAL typo.

> > + * function need to be called once to create a key usable by all threads.
> > + * rte_tls_key_t is an opaque pointer used to store the allocated key.
> > + * and optional destructor can be set to be called when a thread expires.
> > + *
> > + * @param key
> > + *   The rte_tls_key_t will cantain the allocated key
> 
> Typo: "cantain". I'd say: "Pointer to store the allocated rte_tls_key".
> 
> > + * @param destructor
> > + *   The function to be called when the thread expires
> 
> Typo: no period (line-break will be removed by Doxygen).
> 
> > + *   Not supported on Windows OS.
> 
> Some drivers (net/mlx5, bus/dpaa, bus/fsmlc) rely on this feature.
> Admittedly, it would be hard to implement. You know net/mlx5 well, how
> will it be affected? Do you plan to stop relying on this feature or implement it
> in the future? Anyway, LGTM for now.

In net/mlx5 we will not relay on the destructor for the Windows flow and plan to do the housekeeping in the PMD, to summarize we save HANDLE for each thread using rte_tls_set_thread_value in the PMD destruction flow we call the needed release function for each terminated thread.

I used the opaque pointer in case future development might want to implement It without breaking the API, I assumed additions to the TLS key struct on Windows side will be needed.

Thanks for the review, I'll send a v4 after fixing your comments.

> 
> > + *
> > + * @return
> > + *   On success, zero.
> > + *   On failure, a negative error number -ENOMEM or -ENOEXEC
> 
> Let's not make POSIX codes part of the API.
> 
> > + */
> > +__rte_experimental
> > +int
> > +rte_tls_create_key(rte_tls_key_t *key, void (*destructor)(void *));
> 
> [...]
> > +int
> > +rte_tls_create_key(rte_tls_key_t *key, void (*destructor)(void *)) {
> > +     int err;
> > +
> > +     *key = malloc(sizeof(struct eal_tls_key));
> > +     if ((*key) == NULL) {
> > +             RTE_LOG(DEBUG, EAL, "Cannot allocate tls key.");
> > +             return -ENOMEM;
> > +     }
> > +     err = pthread_key_create(&((*key)->thread_index), destructor);
> > +     if (err) {
> > +             RTE_LOG(DEBUG, EAL, "pthread_key_create failed: %s\n",
> > +                      rte_strerror(err));
> 
> Use strerror() for OS error codes here and below.
> 
> > +             free(*key);
> > +             return -ENOEXEC;
> > +     }
> > +     return 0;
> > +}
> > +
> 
> [...]
> > +int
> > +rte_tls_set_thread_value(rte_tls_key_t key, const void *value) {
> > +     if (!key)
> > +             return -EINVAL;
> > +     /* discard const qualifier */
> > +     char *p = (char *) (uintptr_t) value;
> 
> Please declare all variables at the top of a block.
> 
> > +
> > +     if (!TlsSetValue(key->thread_index, p)) {
> > +             RTE_LOG_WIN32_ERR("TlsSetValue()");
> > +             return -ENOEXEC;
> > +     }
> > +     return 0;
> > +}
> > +
> > +void *
> > +rte_tls_get_thread_value(rte_tls_key_t key) {
> > +     if (!key)
> > +             rte_errno = EINVAL;
> > +     void *output = TlsGetValue(key->thread_index);
> 
> Same as above.
> 
> > +     if (GetLastError() != ERROR_SUCCESS) {
> > +             RTE_LOG_WIN32_ERR("TlsGetValue()");
> > +             rte_errno = ENOEXEC;
> > +             return NULL;
> > +     }
> > +     return output;
> > +}
  

Patch

diff --git a/lib/librte_eal/include/meson.build b/lib/librte_eal/include/meson.build
index dc007084ff..a307137242 100644
--- a/lib/librte_eal/include/meson.build
+++ b/lib/librte_eal/include/meson.build
@@ -41,6 +41,7 @@  headers += files(
 	'rte_string_fns.h',
 	'rte_tailq.h',
 	'rte_time.h',
+	'rte_tls.h',
 	'rte_trace.h',
 	'rte_trace_point.h',
 	'rte_trace_point_register.h',
diff --git a/lib/librte_eal/include/rte_tls.h b/lib/librte_eal/include/rte_tls.h
new file mode 100644
index 0000000000..3f84c683bc
--- /dev/null
+++ b/lib/librte_eal/include/rte_tls.h
@@ -0,0 +1,88 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Mellanox Technologies, Ltd
+ */
+
+#include <rte_compat.h>
+
+#ifndef _RTE_TLS_H_
+#define _RTE_TLS_H_
+
+/**
+ * @file
+ *
+ * TLS functions
+ *
+ * Simple TLS functionality supplied by eal.
+ */
+
+/**
+ * Opaque pointer for tls key.
+ */
+typedef struct eal_tls_key *rte_tls_key_t;
+
+/**
+ * Function to create a tls data key visible to all threads in the process
+ * function need to be called once to create a key usable by all threads.
+ * rte_tls_key_t is an opaque pointer used to store the allocated key.
+ * and optional destructor can be set to be called when a thread expires.
+ *
+ * @param key
+ *   The rte_tls_key_t will cantain the allocated key
+ * @param destructor
+ *   The function to be called when the thread expires
+ *   Not supported on Windows OS.
+ *
+ * @return
+ *   On success, zero.
+ *   On failure, a negative error number -ENOMEM or -ENOEXEC
+ */
+__rte_experimental
+int
+rte_tls_create_key(rte_tls_key_t *key, void (*destructor)(void *));
+
+/**
+ * Function to delete a tls data key visible to all threads in the process
+ * rte_tls_key_t is an opaque pointer used to allocated the key.
+ *
+ * @param key
+ *   The rte_tls_key_t will cantain the allocated key
+ *
+ * @return
+ *   On success, zero.
+ *   On failure, a negative error number -ENOMEM or -ENOEXEC
+ */
+__rte_experimental
+int
+rte_tls_delete_key(rte_tls_key_t key);
+
+/**
+ * Function to set value bound to the tls key on behalf of the calling thread
+ *
+ * @param key
+ *   The rte_tls_key_t key
+ * @param value
+ *   The value bound to the rte_tls_key_t key for the calling thread.
+ *
+ * @return
+ *   On success, zero.
+ *   On failure, a negative error number -ENOMEM or -ENOEXEC
+ */
+__rte_experimental
+int
+rte_tls_set_thread_value(rte_tls_key_t key, const void *value);
+
+/**
+ * Function to get value bound to the tls key on behalf of the calling thread
+ *
+ * @param key
+ *   The rte_tls_key_t key
+ *
+ * @return
+ *   On success, value data pointer.
+ *   On failure, a negative error number is set in rte_errno.
+ */
+__rte_experimental
+void *
+rte_tls_get_thread_value(rte_tls_key_t key);
+
+#endif /* _RTE_TLS_H_ */
diff --git a/lib/librte_eal/rte_eal_exports.def b/lib/librte_eal/rte_eal_exports.def
index 6a6be1cfa6..2d5078eb76 100644
--- a/lib/librte_eal/rte_eal_exports.def
+++ b/lib/librte_eal/rte_eal_exports.def
@@ -306,6 +306,11 @@  EXPORTS
 	rte_vect_get_max_simd_bitwidth
 	rte_vect_set_max_simd_bitwidth
 
+	rte_tls_create_key
+	rte_tls_delete_key
+	rte_tls_set_thread_value
+	rte_tls_get_thread_value
+
 	rte_mem_lock
 	rte_mem_map
 	rte_mem_page_size
diff --git a/lib/librte_eal/unix/eal_unix_tls.c b/lib/librte_eal/unix/eal_unix_tls.c
new file mode 100644
index 0000000000..8b99f8d701
--- /dev/null
+++ b/lib/librte_eal/unix/eal_unix_tls.c
@@ -0,0 +1,79 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2020 Mellanox Technologies, Ltd
+ */
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+#include <rte_common.h>
+#include <rte_errno.h>
+#include <rte_log.h>
+#include <rte_tls.h>
+
+struct eal_tls_key {
+	pthread_key_t thread_index;
+};
+
+int
+rte_tls_create_key(rte_tls_key_t *key, void (*destructor)(void *))
+{
+	int err;
+
+	*key = malloc(sizeof(struct eal_tls_key));
+	if ((*key) == NULL) {
+		RTE_LOG(DEBUG, EAL, "Cannot allocate tls key.");
+		return -ENOMEM;
+	}
+	err = pthread_key_create(&((*key)->thread_index), destructor);
+	if (err) {
+		RTE_LOG(DEBUG, EAL, "pthread_key_create failed: %s\n",
+			 rte_strerror(err));
+		free(*key);
+		return -ENOEXEC;
+	}
+	return 0;
+}
+
+int
+rte_tls_delete_key(rte_tls_key_t key)
+{
+	int err;
+
+	if (!key)
+		return -EINVAL;
+	err = pthread_key_delete(key->thread_index);
+	if (err) {
+		RTE_LOG(DEBUG, EAL, "pthread_key_delete failed: %s\n",
+			 rte_strerror(err));
+		free(key);
+		return -ENOEXEC;
+	}
+	free(key);
+	return 0;
+}
+
+int
+rte_tls_set_thread_value(rte_tls_key_t key, const void *value)
+{
+	int err;
+
+	if (!key)
+		return -EINVAL;
+	err = pthread_setspecific(key->thread_index, value);
+	if (err) {
+		RTE_LOG(DEBUG, EAL, "pthread_setspecific failed: %s\n",
+			 rte_strerror(err));
+		free(key);
+		return -ENOEXEC;
+	}
+	return 0;
+}
+
+void *
+rte_tls_get_thread_value(rte_tls_key_t key)
+{
+	if (!key)
+		rte_errno = EINVAL;
+	return pthread_getspecific(key->thread_index);
+}
diff --git a/lib/librte_eal/unix/meson.build b/lib/librte_eal/unix/meson.build
index d3af6b6fe2..74596a8817 100644
--- a/lib/librte_eal/unix/meson.build
+++ b/lib/librte_eal/unix/meson.build
@@ -5,4 +5,5 @@  sources += files(
 	'eal_file.c',
 	'eal_unix_memory.c',
 	'eal_unix_timer.c',
+	'eal_unix_tls.c',
 )
diff --git a/lib/librte_eal/version.map b/lib/librte_eal/version.map
index 354c068f31..3ec95d4cbb 100644
--- a/lib/librte_eal/version.map
+++ b/lib/librte_eal/version.map
@@ -403,6 +403,12 @@  EXPERIMENTAL {
 	rte_service_lcore_may_be_active;
 	rte_vect_get_max_simd_bitwidth;
 	rte_vect_set_max_simd_bitwidth;
+
+	# added in 21.02
+	rte_tls_create_key;
+	rte_tls_delete_key;
+	rte_tls_set_thread_value;
+	rte_tls_get_thread_value;
 };
 
 INTERNAL {
diff --git a/lib/librte_eal/windows/eal_tls.c b/lib/librte_eal/windows/eal_tls.c
new file mode 100644
index 0000000000..73fb17ef6d
--- /dev/null
+++ b/lib/librte_eal/windows/eal_tls.c
@@ -0,0 +1,72 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2020 Mellanox Technologies, Ltd
+ */
+#include <rte_common.h>
+#include <rte_errno.h>
+#include <rte_tls.h>
+#include <rte_windows.h>
+
+struct eal_tls_key {
+	DWORD thread_index;
+};
+
+int
+rte_tls_create_key(rte_tls_key_t *key,
+		__rte_unused void (*destructor)(void *))
+{
+	*key = malloc(sizeof(struct eal_tls_key));
+	if ((*key) == NULL) {
+		RTE_LOG(DEBUG, EAL, "Cannot allocate tls key.");
+		return -ENOMEM;
+	}
+	(*key)->thread_index = TlsAlloc();
+	if ((*key)->thread_index == TLS_OUT_OF_INDEXES) {
+		RTE_LOG_WIN32_ERR("TlsAlloc()");
+		free(*key);
+		return -ENOEXEC;
+	}
+	return 0;
+}
+
+int
+rte_tls_delete_key(rte_tls_key_t key)
+{
+	if (!key)
+		return -EINVAL;
+	if (!TlsFree(key->thread_index)) {
+		RTE_LOG_WIN32_ERR("TlsFree()");
+		free(key);
+		return -ENOEXEC;
+	}
+	free(key);
+	return 0;
+}
+
+int
+rte_tls_set_thread_value(rte_tls_key_t key, const void *value)
+{
+	if (!key)
+		return -EINVAL;
+	/* discard const qualifier */
+	char *p = (char *) (uintptr_t) value;
+
+	if (!TlsSetValue(key->thread_index, p)) {
+		RTE_LOG_WIN32_ERR("TlsSetValue()");
+		return -ENOEXEC;
+	}
+	return 0;
+}
+
+void *
+rte_tls_get_thread_value(rte_tls_key_t key)
+{
+	if (!key)
+		rte_errno = EINVAL;
+	void *output = TlsGetValue(key->thread_index);
+	if (GetLastError() != ERROR_SUCCESS) {
+		RTE_LOG_WIN32_ERR("TlsGetValue()");
+		rte_errno = ENOEXEC;
+		return NULL;
+	}
+	return output;
+}
diff --git a/lib/librte_eal/windows/meson.build b/lib/librte_eal/windows/meson.build
index 3b2faf29eb..38f51f8967 100644
--- a/lib/librte_eal/windows/meson.build
+++ b/lib/librte_eal/windows/meson.build
@@ -17,6 +17,7 @@  sources += files(
 	'eal_mp.c',
 	'eal_thread.c',
 	'eal_timer.c',
+	'eal_tls.c',
 	'fnmatch.c',
 	'getopt.c',
 )