1a9643ea8Slogwang /*
2*d30ea906Sjfb8856606  * SPDX-License-Identifier: BSD-3-Clause
3*d30ea906Sjfb8856606  * Copyright 2015 Intel Corporation.
4*d30ea906Sjfb8856606  * Copyright 2012 Hasan Alayli <[email protected]>
5a9643ea8Slogwang  */
6a9643ea8Slogwang 
7a9643ea8Slogwang #include <stdio.h>
8a9643ea8Slogwang #include <stdlib.h>
9a9643ea8Slogwang #include <string.h>
10a9643ea8Slogwang #include <stdint.h>
11a9643ea8Slogwang #include <stddef.h>
12a9643ea8Slogwang #include <limits.h>
13a9643ea8Slogwang #include <inttypes.h>
14a9643ea8Slogwang #include <unistd.h>
15a9643ea8Slogwang #include <pthread.h>
16a9643ea8Slogwang #include <fcntl.h>
17a9643ea8Slogwang #include <sys/time.h>
18a9643ea8Slogwang #include <sys/mman.h>
19a9643ea8Slogwang #include <errno.h>
20a9643ea8Slogwang 
21a9643ea8Slogwang #include <rte_log.h>
22a9643ea8Slogwang #include <rte_common.h>
23a9643ea8Slogwang 
24a9643ea8Slogwang #include "lthread_api.h"
25a9643ea8Slogwang #include "lthread_diag_api.h"
26a9643ea8Slogwang #include "lthread_diag.h"
27a9643ea8Slogwang #include "lthread_int.h"
28a9643ea8Slogwang #include "lthread_sched.h"
29a9643ea8Slogwang #include "lthread_queue.h"
30a9643ea8Slogwang #include "lthread_objcache.h"
31a9643ea8Slogwang #include "lthread_timer.h"
32a9643ea8Slogwang #include "lthread_mutex.h"
33a9643ea8Slogwang #include "lthread_cond.h"
34a9643ea8Slogwang 
35a9643ea8Slogwang /*
36a9643ea8Slogwang  * Create a condition variable
37a9643ea8Slogwang  */
38a9643ea8Slogwang int
lthread_cond_init(char * name,struct lthread_cond ** cond,__rte_unused const struct lthread_condattr * attr)39a9643ea8Slogwang lthread_cond_init(char *name, struct lthread_cond **cond,
40a9643ea8Slogwang 		  __rte_unused const struct lthread_condattr *attr)
41a9643ea8Slogwang {
42a9643ea8Slogwang 	struct lthread_cond *c;
43a9643ea8Slogwang 
44a9643ea8Slogwang 	if (cond == NULL)
45a9643ea8Slogwang 		return POSIX_ERRNO(EINVAL);
46a9643ea8Slogwang 
47a9643ea8Slogwang 	/* allocate a condition variable from cache */
48a9643ea8Slogwang 	c = _lthread_objcache_alloc((THIS_SCHED)->cond_cache);
49a9643ea8Slogwang 
50a9643ea8Slogwang 	if (c == NULL)
51a9643ea8Slogwang 		return POSIX_ERRNO(EAGAIN);
52a9643ea8Slogwang 
53a9643ea8Slogwang 	c->blocked = _lthread_queue_create("blocked");
54a9643ea8Slogwang 	if (c->blocked == NULL) {
55a9643ea8Slogwang 		_lthread_objcache_free((THIS_SCHED)->cond_cache, (void *)c);
56a9643ea8Slogwang 		return POSIX_ERRNO(EAGAIN);
57a9643ea8Slogwang 	}
58a9643ea8Slogwang 
59a9643ea8Slogwang 	if (name == NULL)
60a9643ea8Slogwang 		strncpy(c->name, "no name", sizeof(c->name));
61a9643ea8Slogwang 	else
62a9643ea8Slogwang 		strncpy(c->name, name, sizeof(c->name));
63a9643ea8Slogwang 	c->name[sizeof(c->name)-1] = 0;
64a9643ea8Slogwang 
65a9643ea8Slogwang 	c->root_sched = THIS_SCHED;
66a9643ea8Slogwang 
67a9643ea8Slogwang 	(*cond) = c;
68a9643ea8Slogwang 	DIAG_CREATE_EVENT((*cond), LT_DIAG_COND_CREATE);
69a9643ea8Slogwang 	return 0;
70a9643ea8Slogwang }
71a9643ea8Slogwang 
72a9643ea8Slogwang /*
73a9643ea8Slogwang  * Destroy a condition variable
74a9643ea8Slogwang  */
lthread_cond_destroy(struct lthread_cond * c)75a9643ea8Slogwang int lthread_cond_destroy(struct lthread_cond *c)
76a9643ea8Slogwang {
77a9643ea8Slogwang 	if (c == NULL) {
78a9643ea8Slogwang 		DIAG_EVENT(c, LT_DIAG_COND_DESTROY, c, POSIX_ERRNO(EINVAL));
79a9643ea8Slogwang 		return POSIX_ERRNO(EINVAL);
80a9643ea8Slogwang 	}
81a9643ea8Slogwang 
82a9643ea8Slogwang 	/* try to free it */
83a9643ea8Slogwang 	if (_lthread_queue_destroy(c->blocked) < 0) {
84a9643ea8Slogwang 		/* queue in use */
85a9643ea8Slogwang 		DIAG_EVENT(c, LT_DIAG_COND_DESTROY, c, POSIX_ERRNO(EBUSY));
86a9643ea8Slogwang 		return POSIX_ERRNO(EBUSY);
87a9643ea8Slogwang 	}
88a9643ea8Slogwang 
89a9643ea8Slogwang 	/* okay free it */
90a9643ea8Slogwang 	_lthread_objcache_free(c->root_sched->cond_cache, c);
91a9643ea8Slogwang 	DIAG_EVENT(c, LT_DIAG_COND_DESTROY, c, 0);
92a9643ea8Slogwang 	return 0;
93a9643ea8Slogwang }
94a9643ea8Slogwang 
95a9643ea8Slogwang /*
96a9643ea8Slogwang  * Wait on a condition variable
97a9643ea8Slogwang  */
lthread_cond_wait(struct lthread_cond * c,__rte_unused uint64_t reserved)98a9643ea8Slogwang int lthread_cond_wait(struct lthread_cond *c, __rte_unused uint64_t reserved)
99a9643ea8Slogwang {
100a9643ea8Slogwang 	struct lthread *lt = THIS_LTHREAD;
101a9643ea8Slogwang 
102a9643ea8Slogwang 	if (c == NULL) {
103a9643ea8Slogwang 		DIAG_EVENT(c, LT_DIAG_COND_WAIT, c, POSIX_ERRNO(EINVAL));
104a9643ea8Slogwang 		return POSIX_ERRNO(EINVAL);
105a9643ea8Slogwang 	}
106a9643ea8Slogwang 
107a9643ea8Slogwang 
108a9643ea8Slogwang 	DIAG_EVENT(c, LT_DIAG_COND_WAIT, c, 0);
109a9643ea8Slogwang 
110a9643ea8Slogwang 	/* queue the current thread in the blocked queue
111a9643ea8Slogwang 	 * this will be written when we return to the scheduler
112a9643ea8Slogwang 	 * to ensure that the current thread context is saved
113a9643ea8Slogwang 	 * before any signal could result in it being dequeued and
114a9643ea8Slogwang 	 * resumed
115a9643ea8Slogwang 	 */
116a9643ea8Slogwang 	lt->pending_wr_queue = c->blocked;
117a9643ea8Slogwang 	_suspend();
118a9643ea8Slogwang 
119a9643ea8Slogwang 	/* the condition happened */
120a9643ea8Slogwang 	return 0;
121a9643ea8Slogwang }
122a9643ea8Slogwang 
123a9643ea8Slogwang /*
124a9643ea8Slogwang  * Signal a condition variable
125a9643ea8Slogwang  * attempt to resume any blocked thread
126a9643ea8Slogwang  */
lthread_cond_signal(struct lthread_cond * c)127a9643ea8Slogwang int lthread_cond_signal(struct lthread_cond *c)
128a9643ea8Slogwang {
129a9643ea8Slogwang 	struct lthread *lt;
130a9643ea8Slogwang 
131a9643ea8Slogwang 	if (c == NULL) {
132a9643ea8Slogwang 		DIAG_EVENT(c, LT_DIAG_COND_SIGNAL, c, POSIX_ERRNO(EINVAL));
133a9643ea8Slogwang 		return POSIX_ERRNO(EINVAL);
134a9643ea8Slogwang 	}
135a9643ea8Slogwang 
136a9643ea8Slogwang 	lt = _lthread_queue_remove(c->blocked);
137a9643ea8Slogwang 
138a9643ea8Slogwang 	if (lt != NULL) {
139a9643ea8Slogwang 		/* okay wake up this thread */
140a9643ea8Slogwang 		DIAG_EVENT(c, LT_DIAG_COND_SIGNAL, c, lt);
141a9643ea8Slogwang 		_ready_queue_insert((struct lthread_sched *)lt->sched, lt);
142a9643ea8Slogwang 	}
143a9643ea8Slogwang 	return 0;
144a9643ea8Slogwang }
145a9643ea8Slogwang 
146a9643ea8Slogwang /*
147a9643ea8Slogwang  * Broadcast a condition variable
148a9643ea8Slogwang  */
lthread_cond_broadcast(struct lthread_cond * c)149a9643ea8Slogwang int lthread_cond_broadcast(struct lthread_cond *c)
150a9643ea8Slogwang {
151a9643ea8Slogwang 	struct lthread *lt;
152a9643ea8Slogwang 
153a9643ea8Slogwang 	if (c == NULL) {
154a9643ea8Slogwang 		DIAG_EVENT(c, LT_DIAG_COND_BROADCAST, c, POSIX_ERRNO(EINVAL));
155a9643ea8Slogwang 		return POSIX_ERRNO(EINVAL);
156a9643ea8Slogwang 	}
157a9643ea8Slogwang 
158a9643ea8Slogwang 	DIAG_EVENT(c, LT_DIAG_COND_BROADCAST, c, 0);
159a9643ea8Slogwang 	do {
160a9643ea8Slogwang 		/* drain the queue waking everybody */
161a9643ea8Slogwang 		lt = _lthread_queue_remove(c->blocked);
162a9643ea8Slogwang 
163a9643ea8Slogwang 		if (lt != NULL) {
164a9643ea8Slogwang 			DIAG_EVENT(c, LT_DIAG_COND_BROADCAST, c, lt);
165a9643ea8Slogwang 			/* wake up */
166a9643ea8Slogwang 			_ready_queue_insert((struct lthread_sched *)lt->sched,
167a9643ea8Slogwang 					    lt);
168a9643ea8Slogwang 		}
169a9643ea8Slogwang 	} while (!_lthread_queue_empty(c->blocked));
170a9643ea8Slogwang 	_reschedule();
171a9643ea8Slogwang 	DIAG_EVENT(c, LT_DIAG_COND_BROADCAST, c, 0);
172a9643ea8Slogwang 	return 0;
173a9643ea8Slogwang }
174a9643ea8Slogwang 
175a9643ea8Slogwang /*
176a9643ea8Slogwang  * return the diagnostic ref val stored in a condition var
177a9643ea8Slogwang  */
178a9643ea8Slogwang uint64_t
lthread_cond_diag_ref(struct lthread_cond * c)179a9643ea8Slogwang lthread_cond_diag_ref(struct lthread_cond *c)
180a9643ea8Slogwang {
181a9643ea8Slogwang 	if (c == NULL)
182a9643ea8Slogwang 		return 0;
183a9643ea8Slogwang 	return c->diag_ref;
184a9643ea8Slogwang }
185