1/*
2 * Copyright (c) 2016  Intel Corporation.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef _SEV_IMPL_H_
17#define _SEV_IMPL_H_
18
19#include <rte_common.h>
20#include <rte_memory.h>
21#include <rte_spinlock.h>
22#include <rte_atomic.h>
23#include <sys/queue.h>
24
25#ifdef __cplusplus
26extern "C" {
27#endif
28
29struct tle_evq;
30
31/**
32 * Possible states of the event.
33 */
34enum tle_ev_state {
35	TLE_SEV_IDLE,
36	TLE_SEV_DOWN,
37	TLE_SEV_UP,
38	TLE_SEV_NUM
39};
40
41struct tle_event {
42	TAILQ_ENTRY(tle_event) ql;
43	struct tle_evq *head;
44	const void *data;
45	enum tle_ev_state state;
46} __rte_cache_aligned;
47
48struct tle_evq {
49	rte_spinlock_t lock;
50	uint32_t nb_events;
51	uint32_t nb_armed;
52	uint32_t nb_free;
53	TAILQ_HEAD(, tle_event) armed;
54	TAILQ_HEAD(, tle_event) free;
55	struct tle_event events[0];
56};
57
58/**
59 * event queue creation parameters.
60 */
61struct tle_evq_param {
62	int32_t socket_id;    /**< socket ID to allocate memory from. */
63	uint32_t max_events;  /**< max number of events in queue. */
64};
65
66/**
67 * create event queue.
68 * @param prm
69 *   Parameters used to create and initialise the queue.
70 * @return
71 *   Pointer to new event queue structure,
72 *   or NULL on error, with error code set in rte_errno.
73 *   Possible rte_errno errors include:
74 *   - EINVAL - invalid parameter passed to function
75 *   - ENOMEM - out of memory
76 */
77struct tle_evq *tle_evq_create(const struct tle_evq_param *prm);
78
79/**
80 * Destroy given event queue.
81 *
82 * @param evq
83 *   event queue to destroy
84 */
85void tle_evq_destroy(struct tle_evq *evq);
86
87/**
88 * allocate a new event within given event queue.
89 * @param evq
90 *    event queue to allocate a new stream within.
91 * @param data
92 *   User data to be associated with that event.
93 * @return
94 *   Pointer to event structure that can be used in future tle_event API calls,
95 *   or NULL on error, with error code set in rte_errno.
96 *   Possible rte_errno errors include:
97 *   - EINVAL - invalid parameter passed to function
98 *   - ENOMEM - max limit of allocated events reached for that context
99 */
100struct tle_event *tle_event_alloc(struct tle_evq *evq, const void *data);
101
102/**
103 * free an allocated event.
104 * @param ev
105 *   Pointer to the event to free.
106 */
107void tle_event_free(struct tle_event *ev);
108
109static inline enum tle_ev_state
110tle_event_state(const struct tle_event *ev)
111{
112	return ev->state;
113}
114
115/**
116 * move event from DOWN to UP state.
117 * @param ev
118 *   Pointer to the event.
119 */
120static inline void
121tle_event_raise(struct tle_event *ev)
122{
123	struct tle_evq *q;
124
125	if (ev->state != TLE_SEV_DOWN)
126		return;
127
128	q = ev->head;
129	rte_compiler_barrier();
130
131	rte_spinlock_lock(&q->lock);
132	if (ev->state == TLE_SEV_DOWN) {
133		ev->state = TLE_SEV_UP;
134		TAILQ_INSERT_TAIL(&q->armed, ev, ql);
135		q->nb_armed++;
136	}
137	rte_spinlock_unlock(&q->lock);
138}
139
140/**
141 * move event from UP to DOWN state.
142 * @param ev
143 *   Pointer to the event.
144 */
145static inline void
146tle_event_down(struct tle_event *ev)
147{
148	struct tle_evq *q;
149
150	if (ev->state != TLE_SEV_UP)
151		return;
152
153	q = ev->head;
154	rte_compiler_barrier();
155
156	rte_spinlock_lock(&q->lock);
157	if (ev->state == TLE_SEV_UP) {
158		ev->state = TLE_SEV_DOWN;
159		TAILQ_REMOVE(&q->armed, ev, ql);
160		q->nb_armed--;
161	}
162	rte_spinlock_unlock(&q->lock);
163}
164
165/**
166 * move from IDLE to DOWN/UP state.
167 * @param ev
168 *   Pointer to the event.
169 * @param st
170 *   new state for the event.
171 */
172static inline void
173tle_event_active(struct tle_event *ev, enum tle_ev_state st)
174{
175	struct tle_evq *q;
176
177	if (ev->state != TLE_SEV_IDLE)
178		return;
179
180	q = ev->head;
181	rte_compiler_barrier();
182
183	rte_spinlock_lock(&q->lock);
184	if (st > ev->state) {
185		if (st == TLE_SEV_UP) {
186			TAILQ_INSERT_TAIL(&q->armed, ev, ql);
187			q->nb_armed++;
188		}
189		ev->state = st;
190	}
191	rte_spinlock_unlock(&q->lock);
192}
193
194/**
195 * move event IDLE state.
196 * @param ev
197 *   Pointer to the event.
198 */
199static inline void
200tle_event_idle(struct tle_event *ev)
201{
202	struct tle_evq *q;
203
204	if (ev->state == TLE_SEV_IDLE)
205		return;
206
207	q = ev->head;
208	rte_compiler_barrier();
209
210	rte_spinlock_lock(&q->lock);
211	if (ev->state == TLE_SEV_UP) {
212		TAILQ_REMOVE(&q->armed, ev, ql);
213		q->nb_armed--;
214	}
215	ev->state = TLE_SEV_IDLE;
216	rte_spinlock_unlock(&q->lock);
217}
218
219static inline void
220tle_evq_idle(struct tle_evq *evq, struct tle_event *ev[], uint32_t num)
221{
222	uint32_t i, n;
223
224	rte_spinlock_lock(&evq->lock);
225
226	n = 0;
227	for (i = 0; i != num; i++) {
228		if (ev[i]->state == TLE_SEV_UP) {
229			TAILQ_REMOVE(&evq->armed, ev[i], ql);
230			n++;
231		}
232		ev[i]->state = TLE_SEV_IDLE;
233	}
234
235	evq->nb_armed -= n;
236	rte_spinlock_unlock(&evq->lock);
237}
238
239
240/*
241 * return up to *num* user data pointers associated with
242 * the events that were in the UP state.
243 * Each retrieved event is automatically moved into the DOWN state.
244 * @param evq
245 *   event queue to retrieve events from.
246 * @param evd
247 *   An array of user data pointers associated with the events retrieved.
248 *   It must be large enough to store up to *num* pointers in it.
249 * @param num
250 *   Number of elements in the *evd* array.
251 * @return
252 *   number of of entries filled inside *evd* array.
253 */
254static inline int32_t
255tle_evq_get(struct tle_evq *evq, const void *evd[], uint32_t num)
256{
257	uint32_t i, n;
258	struct tle_event *ev;
259
260	if (evq->nb_armed == 0)
261		return 0;
262
263	rte_compiler_barrier();
264
265	rte_spinlock_lock(&evq->lock);
266	n = RTE_MIN(num, evq->nb_armed);
267	for (i = 0; i != n; i++) {
268		ev = TAILQ_FIRST(&evq->armed);
269		ev->state = TLE_SEV_DOWN;
270		TAILQ_REMOVE(&evq->armed, ev, ql);
271		evd[i] = ev->data;
272	}
273	evq->nb_armed -= n;
274	rte_spinlock_unlock(&evq->lock);
275	return n;
276}
277
278
279#ifdef __cplusplus
280}
281#endif
282
283#endif /* _SEV_IMPL_H_ */
284