blob: 8e18f5832e395d6089fcbf18d70acea3ba056f6d [file] [log] [blame]
Dave Barach371e4e12016-07-08 09:38:52 -04001/*
Ed Warnickecb9cada2015-12-08 15:45:58 -07002 *------------------------------------------------------------------
Florin Corase86a8ed2018-01-05 03:20:25 -08003 * svm_queue.c - unidirectional shared-memory queues
Ed Warnickecb9cada2015-12-08 15:45:58 -07004 *
5 * Copyright (c) 2009 Cisco and/or its affiliates.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at:
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *------------------------------------------------------------------
18 */
19
Florin Corase86a8ed2018-01-05 03:20:25 -080020
Ed Warnickecb9cada2015-12-08 15:45:58 -070021#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <pthread.h>
25#include <vppinfra/mem.h>
26#include <vppinfra/format.h>
27#include <vppinfra/cache.h>
Florin Corase86a8ed2018-01-05 03:20:25 -080028#include <svm/queue.h>
Mohsin Kazmi3fca5672018-01-04 18:57:26 +010029#include <vppinfra/time.h>
Ed Warnickecb9cada2015-12-08 15:45:58 -070030#include <signal.h>
31
32/*
Florin Corase86a8ed2018-01-05 03:20:25 -080033 * svm_queue_init
Dave Barach371e4e12016-07-08 09:38:52 -040034 *
Ed Warnickecb9cada2015-12-08 15:45:58 -070035 * nels = number of elements on the queue
36 * elsize = element size, presumably 4 and cacheline-size will
37 * be popular choices.
Ed Warnickecb9cada2015-12-08 15:45:58 -070038 * pid = consumer pid
Ed Warnickecb9cada2015-12-08 15:45:58 -070039 *
40 * The idea is to call this function in the queue consumer,
41 * and e-mail the queue pointer to the producer(s).
42 *
Dave Barach68b0fb02017-02-28 15:15:56 -050043 * The vpp process / main thread allocates one of these
44 * at startup; its main input queue. The vpp main input queue
Ed Warnickecb9cada2015-12-08 15:45:58 -070045 * has a pointer to it in the shared memory segment header.
Dave Barach371e4e12016-07-08 09:38:52 -040046 *
47 * You probably want to be on an svm data heap before calling this
Ed Warnickecb9cada2015-12-08 15:45:58 -070048 * function.
49 */
Florin Corase86a8ed2018-01-05 03:20:25 -080050svm_queue_t *
51svm_queue_init (int nels,
52 int elsize, int consumer_pid, int signal_when_queue_non_empty)
Ed Warnickecb9cada2015-12-08 15:45:58 -070053{
Florin Corase86a8ed2018-01-05 03:20:25 -080054 svm_queue_t *q;
Dave Barach371e4e12016-07-08 09:38:52 -040055 pthread_mutexattr_t attr;
56 pthread_condattr_t cattr;
Ed Warnickecb9cada2015-12-08 15:45:58 -070057
Florin Corase86a8ed2018-01-05 03:20:25 -080058 q = clib_mem_alloc_aligned (sizeof (svm_queue_t)
Dave Barach371e4e12016-07-08 09:38:52 -040059 + nels * elsize, CLIB_CACHE_LINE_BYTES);
60 memset (q, 0, sizeof (*q));
Ed Warnickecb9cada2015-12-08 15:45:58 -070061
Dave Barach371e4e12016-07-08 09:38:52 -040062 q->elsize = elsize;
63 q->maxsize = nels;
64 q->consumer_pid = consumer_pid;
65 q->signal_when_queue_non_empty = signal_when_queue_non_empty;
Ed Warnickecb9cada2015-12-08 15:45:58 -070066
Dave Barach371e4e12016-07-08 09:38:52 -040067 memset (&attr, 0, sizeof (attr));
Dave Barach68b0fb02017-02-28 15:15:56 -050068 memset (&cattr, 0, sizeof (cattr));
Ed Warnickecb9cada2015-12-08 15:45:58 -070069
Dave Barach371e4e12016-07-08 09:38:52 -040070 if (pthread_mutexattr_init (&attr))
71 clib_unix_warning ("mutexattr_init");
72 if (pthread_mutexattr_setpshared (&attr, PTHREAD_PROCESS_SHARED))
73 clib_unix_warning ("pthread_mutexattr_setpshared");
74 if (pthread_mutex_init (&q->mutex, &attr))
75 clib_unix_warning ("mutex_init");
76 if (pthread_mutexattr_destroy (&attr))
77 clib_unix_warning ("mutexattr_destroy");
78 if (pthread_condattr_init (&cattr))
79 clib_unix_warning ("condattr_init");
80 /* prints funny-looking messages in the Linux target */
81 if (pthread_condattr_setpshared (&cattr, PTHREAD_PROCESS_SHARED))
82 clib_unix_warning ("condattr_setpshared");
83 if (pthread_cond_init (&q->condvar, &cattr))
84 clib_unix_warning ("cond_init1");
85 if (pthread_condattr_destroy (&cattr))
86 clib_unix_warning ("cond_init2");
87
88 return (q);
Ed Warnickecb9cada2015-12-08 15:45:58 -070089}
90
91/*
Florin Corase86a8ed2018-01-05 03:20:25 -080092 * svm_queue_free
Ed Warnickecb9cada2015-12-08 15:45:58 -070093 */
Dave Barach371e4e12016-07-08 09:38:52 -040094void
Florin Corase86a8ed2018-01-05 03:20:25 -080095svm_queue_free (svm_queue_t * q)
Ed Warnickecb9cada2015-12-08 15:45:58 -070096{
Dave Barach371e4e12016-07-08 09:38:52 -040097 (void) pthread_mutex_destroy (&q->mutex);
98 (void) pthread_cond_destroy (&q->condvar);
99 clib_mem_free (q);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700100}
101
Dave Barach371e4e12016-07-08 09:38:52 -0400102void
Florin Corase86a8ed2018-01-05 03:20:25 -0800103svm_queue_lock (svm_queue_t * q)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700104{
Dave Barach371e4e12016-07-08 09:38:52 -0400105 pthread_mutex_lock (&q->mutex);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700106}
107
Dave Barach371e4e12016-07-08 09:38:52 -0400108void
Florin Corase86a8ed2018-01-05 03:20:25 -0800109svm_queue_unlock (svm_queue_t * q)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700110{
Dave Barach371e4e12016-07-08 09:38:52 -0400111 pthread_mutex_unlock (&q->mutex);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700112}
113
Dave Barach371e4e12016-07-08 09:38:52 -0400114int
Florin Corase86a8ed2018-01-05 03:20:25 -0800115svm_queue_is_full (svm_queue_t * q)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700116{
Dave Barach371e4e12016-07-08 09:38:52 -0400117 return q->cursize == q->maxsize;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700118}
119
120/*
Florin Corase86a8ed2018-01-05 03:20:25 -0800121 * svm_queue_add_nolock
Ed Warnickecb9cada2015-12-08 15:45:58 -0700122 */
Dave Barach371e4e12016-07-08 09:38:52 -0400123int
Florin Corase86a8ed2018-01-05 03:20:25 -0800124svm_queue_add_nolock (svm_queue_t * q, u8 * elem)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700125{
Dave Barach371e4e12016-07-08 09:38:52 -0400126 i8 *tailp;
127 int need_broadcast = 0;
128
129 if (PREDICT_FALSE (q->cursize == q->maxsize))
130 {
131 while (q->cursize == q->maxsize)
132 {
133 (void) pthread_cond_wait (&q->condvar, &q->mutex);
134 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700135 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700136
Dave Barach371e4e12016-07-08 09:38:52 -0400137 tailp = (i8 *) (&q->data[0] + q->elsize * q->tail);
138 clib_memcpy (tailp, elem, q->elsize);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700139
Dave Barach371e4e12016-07-08 09:38:52 -0400140 q->tail++;
141 q->cursize++;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700142
Dave Barach371e4e12016-07-08 09:38:52 -0400143 need_broadcast = (q->cursize == 1);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700144
Dave Barach371e4e12016-07-08 09:38:52 -0400145 if (q->tail == q->maxsize)
146 q->tail = 0;
147
148 if (need_broadcast)
149 {
150 (void) pthread_cond_broadcast (&q->condvar);
151 if (q->signal_when_queue_non_empty)
152 kill (q->consumer_pid, q->signal_when_queue_non_empty);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700153 }
Dave Barach371e4e12016-07-08 09:38:52 -0400154 return 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700155}
156
Florin Coras3c2fed52018-07-04 04:15:05 -0700157void
Florin Corase86a8ed2018-01-05 03:20:25 -0800158svm_queue_add_raw (svm_queue_t * q, u8 * elem)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700159{
Dave Barach371e4e12016-07-08 09:38:52 -0400160 i8 *tailp;
161
Dave Barach371e4e12016-07-08 09:38:52 -0400162 tailp = (i8 *) (&q->data[0] + q->elsize * q->tail);
163 clib_memcpy (tailp, elem, q->elsize);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700164
Florin Coras3c2fed52018-07-04 04:15:05 -0700165 q->tail = (q->tail + 1) % q->maxsize;
Dave Barach371e4e12016-07-08 09:38:52 -0400166 q->cursize++;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700167}
168
169
170/*
Florin Corase86a8ed2018-01-05 03:20:25 -0800171 * svm_queue_add
Ed Warnickecb9cada2015-12-08 15:45:58 -0700172 */
Dave Barach371e4e12016-07-08 09:38:52 -0400173int
Florin Corase86a8ed2018-01-05 03:20:25 -0800174svm_queue_add (svm_queue_t * q, u8 * elem, int nowait)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700175{
Dave Barach371e4e12016-07-08 09:38:52 -0400176 i8 *tailp;
177 int need_broadcast = 0;
178
179 if (nowait)
180 {
181 /* zero on success */
182 if (pthread_mutex_trylock (&q->mutex))
183 {
184 return (-1);
185 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700186 }
Dave Barach371e4e12016-07-08 09:38:52 -0400187 else
188 pthread_mutex_lock (&q->mutex);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700189
Dave Barach371e4e12016-07-08 09:38:52 -0400190 if (PREDICT_FALSE (q->cursize == q->maxsize))
191 {
192 if (nowait)
193 {
194 pthread_mutex_unlock (&q->mutex);
195 return (-2);
196 }
197 while (q->cursize == q->maxsize)
198 {
199 (void) pthread_cond_wait (&q->condvar, &q->mutex);
200 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700201 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700202
Dave Barach371e4e12016-07-08 09:38:52 -0400203 tailp = (i8 *) (&q->data[0] + q->elsize * q->tail);
204 clib_memcpy (tailp, elem, q->elsize);
205
206 q->tail++;
207 q->cursize++;
208
209 need_broadcast = (q->cursize == 1);
210
211 if (q->tail == q->maxsize)
212 q->tail = 0;
213
214 if (need_broadcast)
215 {
216 (void) pthread_cond_broadcast (&q->condvar);
217 if (q->signal_when_queue_non_empty)
218 kill (q->consumer_pid, q->signal_when_queue_non_empty);
219 }
220 pthread_mutex_unlock (&q->mutex);
221
222 return 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700223}
224
225/*
Florin Corase86a8ed2018-01-05 03:20:25 -0800226 * svm_queue_add2
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200227 */
228int
Florin Corase86a8ed2018-01-05 03:20:25 -0800229svm_queue_add2 (svm_queue_t * q, u8 * elem, u8 * elem2, int nowait)
Klement Sekera8f2a4ea2017-05-04 06:15:18 +0200230{
231 i8 *tailp;
232 int need_broadcast = 0;
233
234 if (nowait)
235 {
236 /* zero on success */
237 if (pthread_mutex_trylock (&q->mutex))
238 {
239 return (-1);
240 }
241 }
242 else
243 pthread_mutex_lock (&q->mutex);
244
245 if (PREDICT_FALSE (q->cursize + 1 == q->maxsize))
246 {
247 if (nowait)
248 {
249 pthread_mutex_unlock (&q->mutex);
250 return (-2);
251 }
252 while (q->cursize + 1 == q->maxsize)
253 {
254 (void) pthread_cond_wait (&q->condvar, &q->mutex);
255 }
256 }
257
258 tailp = (i8 *) (&q->data[0] + q->elsize * q->tail);
259 clib_memcpy (tailp, elem, q->elsize);
260
261 q->tail++;
262 q->cursize++;
263
264 if (q->tail == q->maxsize)
265 q->tail = 0;
266
267 need_broadcast = (q->cursize == 1);
268
269 tailp = (i8 *) (&q->data[0] + q->elsize * q->tail);
270 clib_memcpy (tailp, elem2, q->elsize);
271
272 q->tail++;
273 q->cursize++;
274
275 if (q->tail == q->maxsize)
276 q->tail = 0;
277
278 if (need_broadcast)
279 {
280 (void) pthread_cond_broadcast (&q->condvar);
281 if (q->signal_when_queue_non_empty)
282 kill (q->consumer_pid, q->signal_when_queue_non_empty);
283 }
284 pthread_mutex_unlock (&q->mutex);
285
286 return 0;
287}
288
289/*
Florin Corase86a8ed2018-01-05 03:20:25 -0800290 * svm_queue_sub
Ed Warnickecb9cada2015-12-08 15:45:58 -0700291 */
Dave Barach371e4e12016-07-08 09:38:52 -0400292int
Mohsin Kazmi3fca5672018-01-04 18:57:26 +0100293svm_queue_sub (svm_queue_t * q, u8 * elem, svm_q_conditional_wait_t cond,
294 u32 time)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700295{
Dave Barach371e4e12016-07-08 09:38:52 -0400296 i8 *headp;
297 int need_broadcast = 0;
Mohsin Kazmi3fca5672018-01-04 18:57:26 +0100298 int rc = 0;
Dave Barach371e4e12016-07-08 09:38:52 -0400299
Mohsin Kazmi3fca5672018-01-04 18:57:26 +0100300 if (cond == SVM_Q_NOWAIT)
Dave Barach371e4e12016-07-08 09:38:52 -0400301 {
302 /* zero on success */
303 if (pthread_mutex_trylock (&q->mutex))
304 {
305 return (-1);
306 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700307 }
Dave Barach371e4e12016-07-08 09:38:52 -0400308 else
309 pthread_mutex_lock (&q->mutex);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700310
Dave Barach371e4e12016-07-08 09:38:52 -0400311 if (PREDICT_FALSE (q->cursize == 0))
312 {
Mohsin Kazmi3fca5672018-01-04 18:57:26 +0100313 if (cond == SVM_Q_NOWAIT)
Dave Barach371e4e12016-07-08 09:38:52 -0400314 {
315 pthread_mutex_unlock (&q->mutex);
316 return (-2);
317 }
Mohsin Kazmi3fca5672018-01-04 18:57:26 +0100318 else if (cond == SVM_Q_TIMEDWAIT)
Dave Barach371e4e12016-07-08 09:38:52 -0400319 {
Mohsin Kazmi3fca5672018-01-04 18:57:26 +0100320 struct timespec ts;
321 ts.tv_sec = unix_time_now () + time;
322 ts.tv_nsec = 0;
323 while (q->cursize == 0 && rc == 0)
324 {
325 rc = pthread_cond_timedwait (&q->condvar, &q->mutex, &ts);
326 }
327 if (rc == ETIMEDOUT)
328 {
329 pthread_mutex_unlock (&q->mutex);
330 return ETIMEDOUT;
331 }
332 }
333 else
334 {
335 while (q->cursize == 0)
336 {
337 (void) pthread_cond_wait (&q->condvar, &q->mutex);
338 }
Dave Barach371e4e12016-07-08 09:38:52 -0400339 }
340 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700341
Dave Barach371e4e12016-07-08 09:38:52 -0400342 headp = (i8 *) (&q->data[0] + q->elsize * q->head);
343 clib_memcpy (elem, headp, q->elsize);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700344
Dave Barach371e4e12016-07-08 09:38:52 -0400345 q->head++;
Dave Barach68b0fb02017-02-28 15:15:56 -0500346 /* $$$$ JFC shouldn't this be == 0? */
Dave Barach371e4e12016-07-08 09:38:52 -0400347 if (q->cursize == q->maxsize)
348 need_broadcast = 1;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700349
Dave Barach371e4e12016-07-08 09:38:52 -0400350 q->cursize--;
351
352 if (q->head == q->maxsize)
353 q->head = 0;
354
355 if (need_broadcast)
356 (void) pthread_cond_broadcast (&q->condvar);
357
358 pthread_mutex_unlock (&q->mutex);
359
360 return 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700361}
362
Dave Barach371e4e12016-07-08 09:38:52 -0400363int
Florin Corase86a8ed2018-01-05 03:20:25 -0800364svm_queue_sub2 (svm_queue_t * q, u8 * elem)
365{
366 int need_broadcast;
367 i8 *headp;
368
369 pthread_mutex_lock (&q->mutex);
370 if (q->cursize == 0)
371 {
372 pthread_mutex_unlock (&q->mutex);
373 return -1;
374 }
375
376 headp = (i8 *) (&q->data[0] + q->elsize * q->head);
377 clib_memcpy (elem, headp, q->elsize);
378
379 q->head++;
380 need_broadcast = (q->cursize == q->maxsize / 2);
381 q->cursize--;
382
383 if (PREDICT_FALSE (q->head == q->maxsize))
384 q->head = 0;
385 pthread_mutex_unlock (&q->mutex);
386
387 if (need_broadcast)
388 (void) pthread_cond_broadcast (&q->condvar);
389
390 return 0;
391}
392
393int
394svm_queue_sub_raw (svm_queue_t * q, u8 * elem)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700395{
Dave Barach371e4e12016-07-08 09:38:52 -0400396 i8 *headp;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700397
Dave Barach371e4e12016-07-08 09:38:52 -0400398 if (PREDICT_FALSE (q->cursize == 0))
399 {
400 while (q->cursize == 0)
401 ;
402 }
403
404 headp = (i8 *) (&q->data[0] + q->elsize * q->head);
405 clib_memcpy (elem, headp, q->elsize);
406
Florin Coras3c2fed52018-07-04 04:15:05 -0700407 q->head = (q->head + 1) % q->maxsize;
Dave Barach371e4e12016-07-08 09:38:52 -0400408 q->cursize--;
409
Dave Barach371e4e12016-07-08 09:38:52 -0400410 return 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700411}
Dave Barach371e4e12016-07-08 09:38:52 -0400412
413/*
414 * fd.io coding-style-patch-verification: ON
415 *
416 * Local Variables:
417 * eval: (c-set-style "gnu")
418 * End:
419 */