RTE OSAdapter POSIX 0.1.0 build 41b3eb3
POSIX/Linux OAL OSAdapter for Platform_RTE
Loading...
Searching...
No Matches
rte_posix_osadapter_ipc.c
Go to the documentation of this file.
1/* _GNU_SOURCE (implies _POSIX_C_SOURCE) rather than _POSIX_C_SOURCE alone:
2 * F_SETPIPE_SZ / F_GETPIPE_SZ below are a Linux/glibc extension, not
3 * standard POSIX, and are guarded with #if so this file still builds on a
4 * non-glibc POSIX target, where the pipe capacity is the compile-time
5 * RTE_POSIX_IPC_PIPE_CAPACITY instead. */
6#define _GNU_SOURCE
7
42
43#include <errno.h>
44#include <fcntl.h>
45#include <limits.h>
46#include <poll.h>
47#include <stdbool.h>
48#include <stdint.h>
49#include <sys/ioctl.h>
50#include <time.h>
51#include <unistd.h>
52
55#if defined(PIPE_BUF)
56#define POSIX_IPC_ATOMIC_MAX ((size_t)PIPE_BUF)
57#else
58#define POSIX_IPC_ATOMIC_MAX ((size_t)_POSIX_PIPE_BUF)
59#endif
61#define POSIX_IPC_POLL_NS 1000000L
62#define POSIX_IPC_NS_PER_MS 1000000L
63#define POSIX_IPC_NS_PER_S 1000000000L
64
65#if defined(F_SETPIPE_SZ) && defined(F_GETPIPE_SZ)
67#define POSIX_IPC_EXTRA_SLOTS 3U
68#else
76#ifndef RTE_POSIX_IPC_PIPE_CAPACITY
77#define RTE_POSIX_IPC_PIPE_CAPACITY 16384U
78#endif
79#endif
80
82typedef struct
83{
84 int read_fd;
86 size_t message_size;
87 size_t queue_depth;
89
90typedef char posix_ipc_storage_fits_[(sizeof(posix_ipc_state_t) <= sizeof(rte_ipc_storage_t)) ? 1 : -1];
91
93static bool compute_deadline(rte_duration_ms_t timeout_ms, struct timespec *out_deadline);
94static rte_status_t read_message(int fd, uint8_t *dst, size_t size, rte_duration_ms_t timeout_ms);
95static rte_status_t set_nonblocking(const int fds[2]);
96#if defined(F_SETPIPE_SZ) && defined(F_GETPIPE_SZ)
97static bool required_pipe_bytes(size_t message_size, size_t queue_depth, size_t *out_bytes);
98#endif
99static rte_status_t ensure_pipe_capacity(int write_fd, size_t message_size, size_t queue_depth);
100static rte_status_t try_send_once(const posix_ipc_state_t *state, const void *message);
101static rte_status_t sleep_before_retry(const struct timespec *deadline);
102static rte_status_t send_until_deadline(const posix_ipc_state_t *state, const void *message,
103 rte_duration_ms_t timeout_ms);
104
106
108static bool compute_deadline(rte_duration_ms_t timeout_ms, struct timespec *out_deadline)
109{
110 bool ok = false;
111
112 if (clock_gettime(CLOCK_MONOTONIC, out_deadline) == 0)
113 {
114 rte_duration_ms_t whole_s = timeout_ms / 1000U;
115 rte_duration_ms_t rest_ms = timeout_ms % 1000U;
116
117 out_deadline->tv_sec += (time_t)whole_s;
118 out_deadline->tv_nsec += (long)rest_ms * POSIX_IPC_NS_PER_MS;
119 if (out_deadline->tv_nsec >= POSIX_IPC_NS_PER_S)
120 {
121 out_deadline->tv_sec += 1;
122 out_deadline->tv_nsec -= POSIX_IPC_NS_PER_S;
123 }
124 ok = true;
125 }
126 return ok;
127}
128
132static rte_status_t read_message(int fd, uint8_t *dst, size_t size, rte_duration_ms_t timeout_ms)
133{
134 rte_status_t status = RTE_STATUS_OK;
135 struct timespec deadline;
136 size_t done = 0U;
137
138 if (!compute_deadline(timeout_ms, &deadline))
139 {
140 status = RTE_STATUS_INTERNAL_ERROR; /* no deadline - never wait unbounded */
141 }
142
143 while ((status == RTE_STATUS_OK) && (done < size))
144 {
145 int wait_ms = posix_remaining_ms(&deadline); /* capped at INT_MAX, never negative from a large timeout */
146
147 /* wait_ms == 0 still polls once without blocking, so timeout_ms == 0
148 * is a non-blocking try ("at most timeout_ms"), not an immediate TIMEOUT. */
149 if (wait_ms < 0)
150 {
151 status = RTE_STATUS_TIMEOUT;
152 }
153 else
154 {
155 struct pollfd pfd;
156 int rc;
157
158 pfd.fd = fd;
159 pfd.events = POLLIN;
160 pfd.revents = 0;
161 rc = poll(&pfd, 1, wait_ms);
162 if (rc == 0)
163 {
164 status = RTE_STATUS_TIMEOUT;
165 }
166 else if (rc < 0)
167 {
168 if (errno != EINTR)
169 {
170 status = RTE_STATUS_INTERNAL_ERROR;
171 }
172 }
173 else
174 {
175 ssize_t n = read(fd, &dst[done], size - done);
176
177 if (n < 0)
178 {
179 if ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINTR))
180 {
181 status = RTE_STATUS_INTERNAL_ERROR;
182 }
183 }
184 else if (n == 0)
185 {
186 /* read() returning 0 means the write end was closed. */
187 status = RTE_STATUS_INTERNAL_ERROR;
188 }
189 else
190 {
191 done += (size_t)n;
192 }
193 }
194 }
195 }
196
197 return status;
198}
199
201static rte_status_t set_nonblocking(const int fds[2])
202{
203 rte_status_t status = RTE_STATUS_INTERNAL_ERROR;
204
205 if (fcntl(fds[0], F_SETFL, O_NONBLOCK) != -1)
206 {
207 if (fcntl(fds[1], F_SETFL, O_NONBLOCK) != -1)
208 {
209 status = RTE_STATUS_OK;
210 }
211 }
212 return status;
213}
214
215#if defined(F_SETPIPE_SZ) && defined(F_GETPIPE_SZ)
225static bool required_pipe_bytes(size_t message_size, size_t queue_depth, size_t *out_bytes)
226{
227 bool ok = false;
228 long page = sysconf(_SC_PAGESIZE);
229
230 if ((page > 0) && ((size_t)page >= message_size))
231 {
232 size_t page_bytes = (size_t)page;
233 size_t full_slots = queue_depth / (page_bytes / message_size);
234
235 if (full_slots <= ((SIZE_MAX / page_bytes) - POSIX_IPC_EXTRA_SLOTS))
236 {
237 *out_bytes = (full_slots + POSIX_IPC_EXTRA_SLOTS) * page_bytes;
238 ok = true;
239 }
240 }
241 return ok;
242}
243
248static rte_status_t ensure_pipe_capacity(int write_fd, size_t message_size, size_t queue_depth)
249{
250 rte_status_t status = RTE_STATUS_RESOURCE_EXHAUSTED;
251 size_t needed = 0U;
252
253 if (required_pipe_bytes(message_size, queue_depth, &needed) && (needed <= (size_t)INT_MAX))
254 {
255 if (fcntl(write_fd, F_SETPIPE_SZ, (int)needed) >= 0)
256 {
257 int actual = fcntl(write_fd, F_GETPIPE_SZ);
258
259 if ((actual >= 0) && ((size_t)actual >= needed))
260 {
261 status = RTE_STATUS_OK;
262 }
263 }
264 }
265 return status;
266}
267#else
271static rte_status_t ensure_pipe_capacity(int write_fd, size_t message_size, size_t queue_depth)
272{
273 rte_status_t status = RTE_STATUS_RESOURCE_EXHAUSTED;
274
275 (void)write_fd;
276 if ((message_size * queue_depth) <= (size_t)RTE_POSIX_IPC_PIPE_CAPACITY)
277 {
278 status = RTE_STATUS_OK;
279 }
280 return status;
281}
282#endif
283
289static rte_status_t try_send_once(const posix_ipc_state_t *state, const void *message)
290{
291 rte_status_t status;
292 int queued_bytes = 0;
293
294 if (ioctl(state->read_fd, FIONREAD, &queued_bytes) != 0)
295 {
296 status = RTE_STATUS_INTERNAL_ERROR;
297 }
298 else if (queued_bytes < 0)
299 {
300 status = RTE_STATUS_INTERNAL_ERROR;
301 }
302 else if (((size_t)queued_bytes / state->message_size) >= state->queue_depth)
303 {
304 status = RTE_STATUS_TIMEOUT; /* queue full */
305 }
306 else
307 {
308 ssize_t written = write(state->write_fd, message, state->message_size);
309
310 if (written < 0)
311 {
312 if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR))
313 {
314 status = RTE_STATUS_TIMEOUT;
315 }
316 else
317 {
318 status = RTE_STATUS_INTERNAL_ERROR;
319 }
320 }
321 else if ((size_t)written == state->message_size)
322 {
323 status = RTE_STATUS_OK;
324 }
325 else
326 {
327 status = RTE_STATUS_INTERNAL_ERROR;
328 }
329 }
330 return status;
331}
332
337static rte_status_t sleep_before_retry(const struct timespec *deadline)
338{
339 rte_status_t status = RTE_STATUS_INTERNAL_ERROR;
340 struct timespec now;
341
342 if (clock_gettime(CLOCK_MONOTONIC, &now) == 0)
343 {
344 int64_t remaining_ns = (((int64_t)deadline->tv_sec - (int64_t)now.tv_sec) * POSIX_IPC_NS_PER_S) +
345 ((int64_t)deadline->tv_nsec - (int64_t)now.tv_nsec);
346
347 if (remaining_ns <= 0)
348 {
349 status = RTE_STATUS_TIMEOUT;
350 }
351 else
352 {
353 struct timespec nap;
354
355 nap.tv_sec = 0;
356 nap.tv_nsec = (long)((remaining_ns < POSIX_IPC_POLL_NS) ? remaining_ns : POSIX_IPC_POLL_NS);
357 /* An EINTR only ends the sleep early; the deadline still bounds the loop. */
358 (void)nanosleep(&nap, NULL);
359 status = RTE_STATUS_OK;
360 }
361 }
362 return status;
363}
364
367static rte_status_t send_until_deadline(const posix_ipc_state_t *state, const void *message,
368 rte_duration_ms_t timeout_ms)
369{
370 rte_status_t status;
371 struct timespec deadline;
372
373 if (!compute_deadline(timeout_ms, &deadline))
374 {
375 status = RTE_STATUS_INTERNAL_ERROR; /* no deadline - never wait unbounded */
376 }
377 else
378 {
379 bool trying = true;
380
381 status = RTE_STATUS_TIMEOUT;
382 while (trying)
383 {
384 status = try_send_once(state, message);
385 if (status == RTE_STATUS_TIMEOUT)
386 {
387 rte_status_t wait_status = sleep_before_retry(&deadline);
388
389 if (wait_status != RTE_STATUS_OK)
390 {
391 status = wait_status; /* TIMEOUT at the deadline, INTERNAL_ERROR without a clock */
392 trying = false;
393 }
394 }
395 else
396 {
397 trying = false;
398 }
399 }
400 }
401 return status;
402}
403
405
407static rte_status_t osadapter_create(rte_ipc_storage_t *storage, const rte_ipc_config_t *config,
408 rte_ipc_handle_t *out_handle)
409{
410 rte_status_t status;
411 int fds[2];
412
413 if ((storage == NULL) || (config == NULL) || (out_handle == NULL) || (config->message_size == 0U)
414 || (config->queue_depth == 0U))
415 {
416 status = RTE_STATUS_INVALID_PARAM;
417 }
418 else if (config->message_size > POSIX_IPC_ATOMIC_MAX)
419 {
420 status = RTE_STATUS_NOT_SUPPORTED; /* one write() could not queue the message whole */
421 }
422 else if (config->queue_depth > (SIZE_MAX / config->message_size))
423 {
424 status = RTE_STATUS_INVALID_PARAM; /* message_size * queue_depth overflows */
425 }
426 else if (pipe(fds) != 0)
427 {
428 status = RTE_STATUS_INTERNAL_ERROR;
429 }
430 else
431 {
432 status = set_nonblocking(fds);
433 if (status == RTE_STATUS_OK)
434 {
435 status = ensure_pipe_capacity(fds[1], config->message_size, config->queue_depth);
436 }
437
438 if (status == RTE_STATUS_OK)
439 {
440 posix_ipc_state_t *state = (posix_ipc_state_t *)(void *)storage;
441
442 state->read_fd = fds[0];
443 state->write_fd = fds[1];
444 state->message_size = config->message_size;
445 state->queue_depth = config->queue_depth;
446 *out_handle = (rte_ipc_handle_t)(void *)storage;
447 }
448 else
449 {
450 (void)close(fds[0]);
451 (void)close(fds[1]);
452 }
453 }
454 return status;
455}
456
458static rte_status_t backend_send(rte_ipc_handle_t handle, const void *message, size_t message_size,
459 rte_duration_ms_t timeout_ms)
460{
461 const posix_ipc_state_t *state = (const posix_ipc_state_t *)(const void *)handle;
462 rte_status_t status;
463
464 if ((state == NULL) || (message == NULL) || (message_size == 0U))
465 {
466 status = RTE_STATUS_INVALID_PARAM;
467 }
468 else if (message_size != state->message_size)
469 {
470 status = RTE_STATUS_INVALID_PARAM; /* every message has the size fixed at create */
471 }
472 else
473 {
474 status = send_until_deadline(state, message, timeout_ms);
475 }
476 return status;
477}
478
480static rte_status_t backend_receive(rte_ipc_handle_t handle, void *out_message, size_t buffer_size,
481 rte_duration_ms_t timeout_ms)
482{
483 const posix_ipc_state_t *state = (const posix_ipc_state_t *)(const void *)handle;
484 rte_status_t status;
485
486 if ((state == NULL) || (out_message == NULL) || (buffer_size == 0U))
487 {
488 status = RTE_STATUS_INVALID_PARAM;
489 }
490 else if (buffer_size < state->message_size)
491 {
492 status = RTE_STATUS_INVALID_PARAM; /* a message would not fit */
493 }
494 else
495 {
496 status = read_message(state->read_fd, (uint8_t *)out_message, state->message_size, timeout_ms);
497 }
498 return status;
499}
500
501static rte_status_t osadapter_destroy(rte_ipc_handle_t handle)
502{
503 posix_ipc_state_t *state = (posix_ipc_state_t *)(void *)handle;
504
505 if (state == NULL)
506 {
507 return RTE_STATUS_INVALID_PARAM;
508 }
509 (void)close(state->read_fd);
510 (void)close(state->write_fd);
511 return RTE_STATUS_OK;
512}
513
515static const rte_osadapter_ipc_t s_posix_ipc_osadapter = { osadapter_create, backend_send, backend_receive,
516 osadapter_destroy };
517
519const rte_osadapter_ipc_t *rte_posix_osadapter_ipc(void)
520{
521 return &s_posix_ipc_osadapter;
522}
Real POSIX/Linux OSAdapter for every OAL service (ADR-018).
const rte_osadapter_ipc_t * rte_posix_osadapter_ipc(void)
The POSIX rte_ipc OSAdapter (pipe() per channel).
static rte_status_t read_message(int fd, uint8_t *dst, size_t size, rte_duration_ms_t timeout_ms)
static bool compute_deadline(rte_duration_ms_t timeout_ms, struct timespec *out_deadline)
static rte_status_t send_until_deadline(const posix_ipc_state_t *state, const void *message, rte_duration_ms_t timeout_ms)
static rte_status_t try_send_once(const posix_ipc_state_t *state, const void *message)
#define POSIX_IPC_NS_PER_S
static rte_status_t sleep_before_retry(const struct timespec *deadline)
static rte_status_t backend_receive(rte_ipc_handle_t handle, void *out_message, size_t buffer_size, rte_duration_ms_t timeout_ms)
static const rte_osadapter_ipc_t s_posix_ipc_osadapter
static rte_status_t ensure_pipe_capacity(int write_fd, size_t message_size, size_t queue_depth)
#define RTE_POSIX_IPC_PIPE_CAPACITY
#define POSIX_IPC_POLL_NS
#define POSIX_IPC_NS_PER_MS
static rte_status_t osadapter_create(rte_ipc_storage_t *storage, const rte_ipc_config_t *config, rte_ipc_handle_t *out_handle)
#define POSIX_IPC_ATOMIC_MAX
Private helpers: millisecond waits for poll()'s int timeout argument.