1*d30ea906Sjfb8856606 /* SPDX-License-Identifier: BSD-3-Clause
2*d30ea906Sjfb8856606  * Copyright(c) 2015 Intel Corporation
3a9643ea8Slogwang  */
4a9643ea8Slogwang 
5a9643ea8Slogwang #include <stdio.h>
6a9643ea8Slogwang #include <stdlib.h>
7a9643ea8Slogwang #include <string.h>
8a9643ea8Slogwang #include <stdint.h>
9a9643ea8Slogwang #include <limits.h>
10a9643ea8Slogwang #include <inttypes.h>
11a9643ea8Slogwang #include <unistd.h>
12a9643ea8Slogwang #include <pthread.h>
13a9643ea8Slogwang #include <fcntl.h>
14a9643ea8Slogwang #include <sys/time.h>
15a9643ea8Slogwang #include <sys/mman.h>
16a9643ea8Slogwang #include <sched.h>
17a9643ea8Slogwang 
18a9643ea8Slogwang #include <rte_malloc.h>
19a9643ea8Slogwang #include <rte_log.h>
20a9643ea8Slogwang #include <rte_ring.h>
21a9643ea8Slogwang #include <rte_atomic_64.h>
22a9643ea8Slogwang 
23a9643ea8Slogwang #include "lthread_tls.h"
24a9643ea8Slogwang #include "lthread_queue.h"
25a9643ea8Slogwang #include "lthread_objcache.h"
26a9643ea8Slogwang #include "lthread_sched.h"
27a9643ea8Slogwang 
28a9643ea8Slogwang static struct rte_ring *key_pool;
29a9643ea8Slogwang static uint64_t key_pool_init;
30a9643ea8Slogwang 
31a9643ea8Slogwang /* needed to cause section start and end to be defined */
32a9643ea8Slogwang RTE_DEFINE_PER_LTHREAD(void *, dummy);
33a9643ea8Slogwang 
34a9643ea8Slogwang static struct lthread_key key_table[LTHREAD_MAX_KEYS];
35a9643ea8Slogwang 
RTE_INIT(thread_tls_ctor)362bfe3f2eSlogwang RTE_INIT(thread_tls_ctor)
37a9643ea8Slogwang {
38a9643ea8Slogwang 	key_pool = NULL;
39a9643ea8Slogwang 	key_pool_init = 0;
40a9643ea8Slogwang }
41a9643ea8Slogwang 
42a9643ea8Slogwang /*
43a9643ea8Slogwang  * Initialize a pool of keys
44a9643ea8Slogwang  * These are unique tokens that can be obtained by threads
45a9643ea8Slogwang  * calling lthread_key_create()
46a9643ea8Slogwang  */
_lthread_key_pool_init(void)47a9643ea8Slogwang void _lthread_key_pool_init(void)
48a9643ea8Slogwang {
49a9643ea8Slogwang 	static struct rte_ring *pool;
50a9643ea8Slogwang 	struct lthread_key *new_key;
51a9643ea8Slogwang 	char name[MAX_LTHREAD_NAME_SIZE];
52a9643ea8Slogwang 
53a9643ea8Slogwang 	bzero(key_table, sizeof(key_table));
54a9643ea8Slogwang 
55a9643ea8Slogwang 	/* only one lcore should do this */
56a9643ea8Slogwang 	if (rte_atomic64_cmpset(&key_pool_init, 0, 1)) {
57a9643ea8Slogwang 
58a9643ea8Slogwang 		snprintf(name,
59a9643ea8Slogwang 			MAX_LTHREAD_NAME_SIZE,
60a9643ea8Slogwang 			"lthread_key_pool_%d",
61a9643ea8Slogwang 			getpid());
62a9643ea8Slogwang 
63a9643ea8Slogwang 		pool = rte_ring_create(name,
64a9643ea8Slogwang 					LTHREAD_MAX_KEYS, 0, 0);
65a9643ea8Slogwang 		RTE_ASSERT(pool);
66a9643ea8Slogwang 
67a9643ea8Slogwang 		int i;
68a9643ea8Slogwang 
69a9643ea8Slogwang 		for (i = 1; i < LTHREAD_MAX_KEYS; i++) {
70a9643ea8Slogwang 			new_key = &key_table[i];
71a9643ea8Slogwang 			rte_ring_mp_enqueue((struct rte_ring *)pool,
72a9643ea8Slogwang 						(void *)new_key);
73a9643ea8Slogwang 		}
74a9643ea8Slogwang 		key_pool = pool;
75a9643ea8Slogwang 	}
76a9643ea8Slogwang 	/* other lcores wait here till done */
77a9643ea8Slogwang 	while (key_pool == NULL) {
78a9643ea8Slogwang 		rte_compiler_barrier();
79a9643ea8Slogwang 		sched_yield();
80a9643ea8Slogwang 	};
81a9643ea8Slogwang }
82a9643ea8Slogwang 
83a9643ea8Slogwang /*
84a9643ea8Slogwang  * Create a key
85*d30ea906Sjfb8856606  * this means getting a key from the pool
86a9643ea8Slogwang  */
lthread_key_create(unsigned int * key,tls_destructor_func destructor)87a9643ea8Slogwang int lthread_key_create(unsigned int *key, tls_destructor_func destructor)
88a9643ea8Slogwang {
89a9643ea8Slogwang 	if (key == NULL)
90a9643ea8Slogwang 		return POSIX_ERRNO(EINVAL);
91a9643ea8Slogwang 
92a9643ea8Slogwang 	struct lthread_key *new_key;
93a9643ea8Slogwang 
94a9643ea8Slogwang 	if (rte_ring_mc_dequeue((struct rte_ring *)key_pool, (void **)&new_key)
95a9643ea8Slogwang 	    == 0) {
96a9643ea8Slogwang 		new_key->destructor = destructor;
97a9643ea8Slogwang 		*key = (new_key - key_table);
98a9643ea8Slogwang 
99a9643ea8Slogwang 		return 0;
100a9643ea8Slogwang 	}
101a9643ea8Slogwang 	return POSIX_ERRNO(EAGAIN);
102a9643ea8Slogwang }
103a9643ea8Slogwang 
104a9643ea8Slogwang 
105a9643ea8Slogwang /*
106a9643ea8Slogwang  * Delete a key
107a9643ea8Slogwang  */
lthread_key_delete(unsigned int k)108a9643ea8Slogwang int lthread_key_delete(unsigned int k)
109a9643ea8Slogwang {
110a9643ea8Slogwang 	struct lthread_key *key;
111a9643ea8Slogwang 
112a9643ea8Slogwang 	key = (struct lthread_key *) &key_table[k];
113a9643ea8Slogwang 
114a9643ea8Slogwang 	if (k > LTHREAD_MAX_KEYS)
115a9643ea8Slogwang 		return POSIX_ERRNO(EINVAL);
116a9643ea8Slogwang 
117a9643ea8Slogwang 	key->destructor = NULL;
118a9643ea8Slogwang 	rte_ring_mp_enqueue((struct rte_ring *)key_pool,
119a9643ea8Slogwang 					(void *)key);
120a9643ea8Slogwang 	return 0;
121a9643ea8Slogwang }
122a9643ea8Slogwang 
123a9643ea8Slogwang 
124a9643ea8Slogwang 
125a9643ea8Slogwang /*
126a9643ea8Slogwang  * Break association for all keys in use by this thread
127a9643ea8Slogwang  * invoke the destructor if available.
128a9643ea8Slogwang  * Since a destructor can create keys we could enter an infinite loop
129a9643ea8Slogwang  * therefore we give up after LTHREAD_DESTRUCTOR_ITERATIONS
130a9643ea8Slogwang  * the behavior is modelled on pthread
131a9643ea8Slogwang  */
_lthread_tls_destroy(struct lthread * lt)132a9643ea8Slogwang void _lthread_tls_destroy(struct lthread *lt)
133a9643ea8Slogwang {
134a9643ea8Slogwang 	int i, k;
135a9643ea8Slogwang 	int nb_keys;
136a9643ea8Slogwang 	void *data;
137a9643ea8Slogwang 
138a9643ea8Slogwang 	for (i = 0; i < LTHREAD_DESTRUCTOR_ITERATIONS; i++) {
139a9643ea8Slogwang 
140a9643ea8Slogwang 		for (k = 1; k < LTHREAD_MAX_KEYS; k++) {
141a9643ea8Slogwang 
142a9643ea8Slogwang 			/* no keys in use ? */
143a9643ea8Slogwang 			nb_keys = lt->tls->nb_keys_inuse;
144a9643ea8Slogwang 			if (nb_keys == 0)
145a9643ea8Slogwang 				return;
146a9643ea8Slogwang 
147a9643ea8Slogwang 			/* this key not in use ? */
148a9643ea8Slogwang 			if (lt->tls->data[k] == NULL)
149a9643ea8Slogwang 				continue;
150a9643ea8Slogwang 
151a9643ea8Slogwang 			/* remove this key */
152a9643ea8Slogwang 			data = lt->tls->data[k];
153a9643ea8Slogwang 			lt->tls->data[k] = NULL;
154a9643ea8Slogwang 			lt->tls->nb_keys_inuse = nb_keys-1;
155a9643ea8Slogwang 
156a9643ea8Slogwang 			/* invoke destructor */
157a9643ea8Slogwang 			if (key_table[k].destructor != NULL)
158a9643ea8Slogwang 				key_table[k].destructor(data);
159a9643ea8Slogwang 		}
160a9643ea8Slogwang 	}
161a9643ea8Slogwang }
162a9643ea8Slogwang 
163a9643ea8Slogwang /*
164a9643ea8Slogwang  * Return the pointer associated with a key
165a9643ea8Slogwang  * If the key is no longer valid return NULL
166a9643ea8Slogwang  */
167a9643ea8Slogwang void
lthread_getspecific(unsigned int k)168a9643ea8Slogwang *lthread_getspecific(unsigned int k)
169a9643ea8Slogwang {
1702bfe3f2eSlogwang 	void *res = NULL;
171a9643ea8Slogwang 
1722bfe3f2eSlogwang 	if (k < LTHREAD_MAX_KEYS)
1732bfe3f2eSlogwang 		res = THIS_LTHREAD->tls->data[k];
174a9643ea8Slogwang 
1752bfe3f2eSlogwang 	return res;
176a9643ea8Slogwang }
177a9643ea8Slogwang 
178a9643ea8Slogwang /*
179a9643ea8Slogwang  * Set a value against a key
180a9643ea8Slogwang  * If the key is no longer valid return an error
181a9643ea8Slogwang  * when storing value
182a9643ea8Slogwang  */
lthread_setspecific(unsigned int k,const void * data)183a9643ea8Slogwang int lthread_setspecific(unsigned int k, const void *data)
184a9643ea8Slogwang {
1852bfe3f2eSlogwang 	if (k >= LTHREAD_MAX_KEYS)
186a9643ea8Slogwang 		return POSIX_ERRNO(EINVAL);
187a9643ea8Slogwang 
188a9643ea8Slogwang 	int n = THIS_LTHREAD->tls->nb_keys_inuse;
189a9643ea8Slogwang 
190a9643ea8Slogwang 	/* discard const qualifier */
191a9643ea8Slogwang 	char *p = (char *) (uintptr_t) data;
192a9643ea8Slogwang 
193a9643ea8Slogwang 
194a9643ea8Slogwang 	if (data != NULL) {
195a9643ea8Slogwang 		if (THIS_LTHREAD->tls->data[k] == NULL)
196a9643ea8Slogwang 			THIS_LTHREAD->tls->nb_keys_inuse = n+1;
197a9643ea8Slogwang 	}
198a9643ea8Slogwang 
199a9643ea8Slogwang 	THIS_LTHREAD->tls->data[k] = (void *) p;
200a9643ea8Slogwang 	return 0;
201a9643ea8Slogwang }
202a9643ea8Slogwang 
203a9643ea8Slogwang /*
204a9643ea8Slogwang  * Allocate data for TLS cache
205a9643ea8Slogwang */
_lthread_tls_alloc(struct lthread * lt)206a9643ea8Slogwang void _lthread_tls_alloc(struct lthread *lt)
207a9643ea8Slogwang {
208a9643ea8Slogwang 	struct lthread_tls *tls;
209a9643ea8Slogwang 
210a9643ea8Slogwang 	tls = _lthread_objcache_alloc((THIS_SCHED)->tls_cache);
211a9643ea8Slogwang 
212a9643ea8Slogwang 	RTE_ASSERT(tls != NULL);
213a9643ea8Slogwang 
214a9643ea8Slogwang 	tls->root_sched = (THIS_SCHED);
215a9643ea8Slogwang 	lt->tls = tls;
216a9643ea8Slogwang 
217a9643ea8Slogwang 	/* allocate data for TLS varaiables using RTE_PER_LTHREAD macros */
218a9643ea8Slogwang 	if (sizeof(void *) < (uint64_t)RTE_PER_LTHREAD_SECTION_SIZE) {
219a9643ea8Slogwang 		lt->per_lthread_data =
220a9643ea8Slogwang 		    _lthread_objcache_alloc((THIS_SCHED)->per_lthread_cache);
221a9643ea8Slogwang 	}
222a9643ea8Slogwang }
223