New upstream version 18.11-rc1
[deb_dpdk.git] / lib / librte_eal / common / malloc_mp.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2018 Intel Corporation
3  */
4
5 #include <string.h>
6 #include <sys/time.h>
7
8 #include <rte_alarm.h>
9 #include <rte_errno.h>
10 #include <rte_string_fns.h>
11
12 #include "eal_memalloc.h"
13
14 #include "malloc_elem.h"
15 #include "malloc_mp.h"
16
17 #define MP_ACTION_SYNC "mp_malloc_sync"
18 /**< request sent by primary process to notify of changes in memory map */
19 #define MP_ACTION_ROLLBACK "mp_malloc_rollback"
20 /**< request sent by primary process to notify of changes in memory map. this is
21  * essentially a regular sync request, but we cannot send sync requests while
22  * another one is in progress, and we might have to - therefore, we do this as
23  * a separate callback.
24  */
25 #define MP_ACTION_REQUEST "mp_malloc_request"
26 /**< request sent by secondary process to ask for allocation/deallocation */
27 #define MP_ACTION_RESPONSE "mp_malloc_response"
28 /**< response sent to secondary process to indicate result of request */
29
30 /* forward declarations */
31 static int
32 handle_sync_response(const struct rte_mp_msg *request,
33                 const struct rte_mp_reply *reply);
34 static int
35 handle_rollback_response(const struct rte_mp_msg *request,
36                 const struct rte_mp_reply *reply);
37
38 #define MP_TIMEOUT_S 5 /**< 5 seconds timeouts */
39
40 /* when we're allocating, we need to store some state to ensure that we can
41  * roll back later
42  */
43 struct primary_alloc_req_state {
44         struct malloc_heap *heap;
45         struct rte_memseg **ms;
46         int ms_len;
47         struct malloc_elem *elem;
48         void *map_addr;
49         size_t map_len;
50 };
51
52 enum req_state {
53         REQ_STATE_INACTIVE = 0,
54         REQ_STATE_ACTIVE,
55         REQ_STATE_COMPLETE
56 };
57
58 struct mp_request {
59         TAILQ_ENTRY(mp_request) next;
60         struct malloc_mp_req user_req; /**< contents of request */
61         pthread_cond_t cond; /**< variable we use to time out on this request */
62         enum req_state state; /**< indicate status of this request */
63         struct primary_alloc_req_state alloc_state;
64 };
65
66 /*
67  * We could've used just a single request, but it may be possible for
68  * secondaries to timeout earlier than the primary, and send a new request while
69  * primary is still expecting replies to the old one. Therefore, each new
70  * request will get assigned a new ID, which is how we will distinguish between
71  * expected and unexpected messages.
72  */
73 TAILQ_HEAD(mp_request_list, mp_request);
74 static struct {
75         struct mp_request_list list;
76         pthread_mutex_t lock;
77 } mp_request_list = {
78         .list = TAILQ_HEAD_INITIALIZER(mp_request_list.list),
79         .lock = PTHREAD_MUTEX_INITIALIZER
80 };
81
82 /**
83  * General workflow is the following:
84  *
85  * Allocation:
86  * S: send request to primary
87  * P: attempt to allocate memory
88  *    if failed, sendmsg failure
89  *    if success, send sync request
90  * S: if received msg of failure, quit
91  *    if received sync request, synchronize memory map and reply with result
92  * P: if received sync request result
93  *    if success, sendmsg success
94  *    if failure, roll back allocation and send a rollback request
95  * S: if received msg of success, quit
96  *    if received rollback request, synchronize memory map and reply with result
97  * P: if received sync request result
98  *    sendmsg sync request result
99  * S: if received msg, quit
100  *
101  * Aside from timeouts, there are three points where we can quit:
102  *  - if allocation failed straight away
103  *  - if allocation and sync request succeeded
104  *  - if allocation succeeded, sync request failed, allocation rolled back and
105  *    rollback request received (irrespective of whether it succeeded or failed)
106  *
107  * Deallocation:
108  * S: send request to primary
109  * P: attempt to deallocate memory
110  *    if failed, sendmsg failure
111  *    if success, send sync request
112  * S: if received msg of failure, quit
113  *    if received sync request, synchronize memory map and reply with result
114  * P: if received sync request result
115  *    sendmsg sync request result
116  * S: if received msg, quit
117  *
118  * There is no "rollback" from deallocation, as it's safe to have some memory
119  * mapped in some processes - it's absent from the heap, so it won't get used.
120  */
121
122 static struct mp_request *
123 find_request_by_id(uint64_t id)
124 {
125         struct mp_request *req;
126         TAILQ_FOREACH(req, &mp_request_list.list, next) {
127                 if (req->user_req.id == id)
128                         break;
129         }
130         return req;
131 }
132
133 /* this ID is, like, totally guaranteed to be absolutely unique. pinky swear. */
134 static uint64_t
135 get_unique_id(void)
136 {
137         uint64_t id;
138         do {
139                 id = rte_rand();
140         } while (find_request_by_id(id) != NULL);
141         return id;
142 }
143
144 /* secondary will respond to sync requests thusly */
145 static int
146 handle_sync(const struct rte_mp_msg *msg, const void *peer)
147 {
148         struct rte_mp_msg reply;
149         const struct malloc_mp_req *req =
150                         (const struct malloc_mp_req *)msg->param;
151         struct malloc_mp_req *resp =
152                         (struct malloc_mp_req *)reply.param;
153         int ret;
154
155         if (req->t != REQ_TYPE_SYNC) {
156                 RTE_LOG(ERR, EAL, "Unexpected request from primary\n");
157                 return -1;
158         }
159
160         memset(&reply, 0, sizeof(reply));
161
162         reply.num_fds = 0;
163         strlcpy(reply.name, msg->name, sizeof(reply.name));
164         reply.len_param = sizeof(*resp);
165
166         ret = eal_memalloc_sync_with_primary();
167
168         resp->t = REQ_TYPE_SYNC;
169         resp->id = req->id;
170         resp->result = ret == 0 ? REQ_RESULT_SUCCESS : REQ_RESULT_FAIL;
171
172         rte_mp_reply(&reply, peer);
173
174         return 0;
175 }
176
177 static int
178 handle_alloc_request(const struct malloc_mp_req *m,
179                 struct mp_request *req)
180 {
181         const struct malloc_req_alloc *ar = &m->alloc_req;
182         struct malloc_heap *heap;
183         struct malloc_elem *elem;
184         struct rte_memseg **ms;
185         size_t alloc_sz;
186         int n_segs;
187         void *map_addr;
188
189         alloc_sz = RTE_ALIGN_CEIL(ar->align + ar->elt_size +
190                         MALLOC_ELEM_TRAILER_LEN, ar->page_sz);
191         n_segs = alloc_sz / ar->page_sz;
192
193         heap = ar->heap;
194
195         /* we can't know in advance how many pages we'll need, so we malloc */
196         ms = malloc(sizeof(*ms) * n_segs);
197         if (ms == NULL) {
198                 RTE_LOG(ERR, EAL, "Couldn't allocate memory for request state\n");
199                 goto fail;
200         }
201         memset(ms, 0, sizeof(*ms) * n_segs);
202
203         elem = alloc_pages_on_heap(heap, ar->page_sz, ar->elt_size, ar->socket,
204                         ar->flags, ar->align, ar->bound, ar->contig, ms,
205                         n_segs);
206
207         if (elem == NULL)
208                 goto fail;
209
210         map_addr = ms[0]->addr;
211
212         /* we have succeeded in allocating memory, but we still need to sync
213          * with other processes. however, since DPDK IPC is single-threaded, we
214          * send an asynchronous request and exit this callback.
215          */
216
217         req->alloc_state.ms = ms;
218         req->alloc_state.ms_len = n_segs;
219         req->alloc_state.map_addr = map_addr;
220         req->alloc_state.map_len = alloc_sz;
221         req->alloc_state.elem = elem;
222         req->alloc_state.heap = heap;
223
224         return 0;
225 fail:
226         free(ms);
227         return -1;
228 }
229
230 /* first stage of primary handling requests from secondary */
231 static int
232 handle_request(const struct rte_mp_msg *msg, const void *peer __rte_unused)
233 {
234         const struct malloc_mp_req *m =
235                         (const struct malloc_mp_req *)msg->param;
236         struct mp_request *entry;
237         int ret;
238
239         /* lock access to request */
240         pthread_mutex_lock(&mp_request_list.lock);
241
242         /* make sure it's not a dupe */
243         entry = find_request_by_id(m->id);
244         if (entry != NULL) {
245                 RTE_LOG(ERR, EAL, "Duplicate request id\n");
246                 goto fail;
247         }
248
249         entry = malloc(sizeof(*entry));
250         if (entry == NULL) {
251                 RTE_LOG(ERR, EAL, "Unable to allocate memory for request\n");
252                 goto fail;
253         }
254
255         /* erase all data */
256         memset(entry, 0, sizeof(*entry));
257
258         if (m->t == REQ_TYPE_ALLOC) {
259                 ret = handle_alloc_request(m, entry);
260         } else if (m->t == REQ_TYPE_FREE) {
261                 ret = malloc_heap_free_pages(m->free_req.addr,
262                                 m->free_req.len);
263         } else {
264                 RTE_LOG(ERR, EAL, "Unexpected request from secondary\n");
265                 goto fail;
266         }
267
268         if (ret != 0) {
269                 struct rte_mp_msg resp_msg;
270                 struct malloc_mp_req *resp =
271                                 (struct malloc_mp_req *)resp_msg.param;
272
273                 /* send failure message straight away */
274                 resp_msg.num_fds = 0;
275                 resp_msg.len_param = sizeof(*resp);
276                 strlcpy(resp_msg.name, MP_ACTION_RESPONSE,
277                                 sizeof(resp_msg.name));
278
279                 resp->t = m->t;
280                 resp->result = REQ_RESULT_FAIL;
281                 resp->id = m->id;
282
283                 if (rte_mp_sendmsg(&resp_msg)) {
284                         RTE_LOG(ERR, EAL, "Couldn't send response\n");
285                         goto fail;
286                 }
287                 /* we did not modify the request */
288                 free(entry);
289         } else {
290                 struct rte_mp_msg sr_msg;
291                 struct malloc_mp_req *sr =
292                                 (struct malloc_mp_req *)sr_msg.param;
293                 struct timespec ts;
294
295                 memset(&sr_msg, 0, sizeof(sr_msg));
296
297                 /* we can do something, so send sync request asynchronously */
298                 sr_msg.num_fds = 0;
299                 sr_msg.len_param = sizeof(*sr);
300                 strlcpy(sr_msg.name, MP_ACTION_SYNC, sizeof(sr_msg.name));
301
302                 ts.tv_nsec = 0;
303                 ts.tv_sec = MP_TIMEOUT_S;
304
305                 /* sync requests carry no data */
306                 sr->t = REQ_TYPE_SYNC;
307                 sr->id = m->id;
308
309                 /* there may be stray timeout still waiting */
310                 do {
311                         ret = rte_mp_request_async(&sr_msg, &ts,
312                                         handle_sync_response);
313                 } while (ret != 0 && rte_errno == EEXIST);
314                 if (ret != 0) {
315                         RTE_LOG(ERR, EAL, "Couldn't send sync request\n");
316                         if (m->t == REQ_TYPE_ALLOC)
317                                 free(entry->alloc_state.ms);
318                         goto fail;
319                 }
320
321                 /* mark request as in progress */
322                 memcpy(&entry->user_req, m, sizeof(*m));
323                 entry->state = REQ_STATE_ACTIVE;
324
325                 TAILQ_INSERT_TAIL(&mp_request_list.list, entry, next);
326         }
327         pthread_mutex_unlock(&mp_request_list.lock);
328         return 0;
329 fail:
330         pthread_mutex_unlock(&mp_request_list.lock);
331         free(entry);
332         return -1;
333 }
334
335 /* callback for asynchronous sync requests for primary. this will either do a
336  * sendmsg with results, or trigger rollback request.
337  */
338 static int
339 handle_sync_response(const struct rte_mp_msg *request,
340                 const struct rte_mp_reply *reply)
341 {
342         enum malloc_req_result result;
343         struct mp_request *entry;
344         const struct malloc_mp_req *mpreq =
345                         (const struct malloc_mp_req *)request->param;
346         int i;
347
348         /* lock the request */
349         pthread_mutex_lock(&mp_request_list.lock);
350
351         entry = find_request_by_id(mpreq->id);
352         if (entry == NULL) {
353                 RTE_LOG(ERR, EAL, "Wrong request ID\n");
354                 goto fail;
355         }
356
357         result = REQ_RESULT_SUCCESS;
358
359         if (reply->nb_received != reply->nb_sent)
360                 result = REQ_RESULT_FAIL;
361
362         for (i = 0; i < reply->nb_received; i++) {
363                 struct malloc_mp_req *resp =
364                                 (struct malloc_mp_req *)reply->msgs[i].param;
365
366                 if (resp->t != REQ_TYPE_SYNC) {
367                         RTE_LOG(ERR, EAL, "Unexpected response to sync request\n");
368                         result = REQ_RESULT_FAIL;
369                         break;
370                 }
371                 if (resp->id != entry->user_req.id) {
372                         RTE_LOG(ERR, EAL, "Response to wrong sync request\n");
373                         result = REQ_RESULT_FAIL;
374                         break;
375                 }
376                 if (resp->result == REQ_RESULT_FAIL) {
377                         result = REQ_RESULT_FAIL;
378                         break;
379                 }
380         }
381
382         if (entry->user_req.t == REQ_TYPE_FREE) {
383                 struct rte_mp_msg msg;
384                 struct malloc_mp_req *resp = (struct malloc_mp_req *)msg.param;
385
386                 memset(&msg, 0, sizeof(msg));
387
388                 /* this is a free request, just sendmsg result */
389                 resp->t = REQ_TYPE_FREE;
390                 resp->result = result;
391                 resp->id = entry->user_req.id;
392                 msg.num_fds = 0;
393                 msg.len_param = sizeof(*resp);
394                 strlcpy(msg.name, MP_ACTION_RESPONSE, sizeof(msg.name));
395
396                 if (rte_mp_sendmsg(&msg))
397                         RTE_LOG(ERR, EAL, "Could not send message to secondary process\n");
398
399                 TAILQ_REMOVE(&mp_request_list.list, entry, next);
400                 free(entry);
401         } else if (entry->user_req.t == REQ_TYPE_ALLOC &&
402                         result == REQ_RESULT_SUCCESS) {
403                 struct malloc_heap *heap = entry->alloc_state.heap;
404                 struct rte_mp_msg msg;
405                 struct malloc_mp_req *resp =
406                                 (struct malloc_mp_req *)msg.param;
407
408                 memset(&msg, 0, sizeof(msg));
409
410                 heap->total_size += entry->alloc_state.map_len;
411
412                 /* result is success, so just notify secondary about this */
413                 resp->t = REQ_TYPE_ALLOC;
414                 resp->result = result;
415                 resp->id = entry->user_req.id;
416                 msg.num_fds = 0;
417                 msg.len_param = sizeof(*resp);
418                 strlcpy(msg.name, MP_ACTION_RESPONSE, sizeof(msg.name));
419
420                 if (rte_mp_sendmsg(&msg))
421                         RTE_LOG(ERR, EAL, "Could not send message to secondary process\n");
422
423                 TAILQ_REMOVE(&mp_request_list.list, entry, next);
424                 free(entry->alloc_state.ms);
425                 free(entry);
426         } else if (entry->user_req.t == REQ_TYPE_ALLOC &&
427                         result == REQ_RESULT_FAIL) {
428                 struct rte_mp_msg rb_msg;
429                 struct malloc_mp_req *rb =
430                                 (struct malloc_mp_req *)rb_msg.param;
431                 struct timespec ts;
432                 struct primary_alloc_req_state *state =
433                                 &entry->alloc_state;
434                 int ret;
435
436                 memset(&rb_msg, 0, sizeof(rb_msg));
437
438                 /* we've failed to sync, so do a rollback */
439                 rollback_expand_heap(state->ms, state->ms_len, state->elem,
440                                 state->map_addr, state->map_len);
441
442                 /* send rollback request */
443                 rb_msg.num_fds = 0;
444                 rb_msg.len_param = sizeof(*rb);
445                 strlcpy(rb_msg.name, MP_ACTION_ROLLBACK, sizeof(rb_msg.name));
446
447                 ts.tv_nsec = 0;
448                 ts.tv_sec = MP_TIMEOUT_S;
449
450                 /* sync requests carry no data */
451                 rb->t = REQ_TYPE_SYNC;
452                 rb->id = entry->user_req.id;
453
454                 /* there may be stray timeout still waiting */
455                 do {
456                         ret = rte_mp_request_async(&rb_msg, &ts,
457                                         handle_rollback_response);
458                 } while (ret != 0 && rte_errno == EEXIST);
459                 if (ret != 0) {
460                         RTE_LOG(ERR, EAL, "Could not send rollback request to secondary process\n");
461
462                         /* we couldn't send rollback request, but that's OK -
463                          * secondary will time out, and memory has been removed
464                          * from heap anyway.
465                          */
466                         TAILQ_REMOVE(&mp_request_list.list, entry, next);
467                         free(state->ms);
468                         free(entry);
469                         goto fail;
470                 }
471         } else {
472                 RTE_LOG(ERR, EAL, " to sync request of unknown type\n");
473                 goto fail;
474         }
475
476         pthread_mutex_unlock(&mp_request_list.lock);
477         return 0;
478 fail:
479         pthread_mutex_unlock(&mp_request_list.lock);
480         return -1;
481 }
482
483 static int
484 handle_rollback_response(const struct rte_mp_msg *request,
485                 const struct rte_mp_reply *reply __rte_unused)
486 {
487         struct rte_mp_msg msg;
488         struct malloc_mp_req *resp = (struct malloc_mp_req *)msg.param;
489         const struct malloc_mp_req *mpreq =
490                         (const struct malloc_mp_req *)request->param;
491         struct mp_request *entry;
492
493         /* lock the request */
494         pthread_mutex_lock(&mp_request_list.lock);
495
496         memset(&msg, 0, sizeof(0));
497
498         entry = find_request_by_id(mpreq->id);
499         if (entry == NULL) {
500                 RTE_LOG(ERR, EAL, "Wrong request ID\n");
501                 goto fail;
502         }
503
504         if (entry->user_req.t != REQ_TYPE_ALLOC) {
505                 RTE_LOG(ERR, EAL, "Unexpected active request\n");
506                 goto fail;
507         }
508
509         /* we don't care if rollback succeeded, request still failed */
510         resp->t = REQ_TYPE_ALLOC;
511         resp->result = REQ_RESULT_FAIL;
512         resp->id = mpreq->id;
513         msg.num_fds = 0;
514         msg.len_param = sizeof(*resp);
515         strlcpy(msg.name, MP_ACTION_RESPONSE, sizeof(msg.name));
516
517         if (rte_mp_sendmsg(&msg))
518                 RTE_LOG(ERR, EAL, "Could not send message to secondary process\n");
519
520         /* clean up */
521         TAILQ_REMOVE(&mp_request_list.list, entry, next);
522         free(entry->alloc_state.ms);
523         free(entry);
524
525         pthread_mutex_unlock(&mp_request_list.lock);
526         return 0;
527 fail:
528         pthread_mutex_unlock(&mp_request_list.lock);
529         return -1;
530 }
531
532 /* final stage of the request from secondary */
533 static int
534 handle_response(const struct rte_mp_msg *msg, const void *peer  __rte_unused)
535 {
536         const struct malloc_mp_req *m =
537                         (const struct malloc_mp_req *)msg->param;
538         struct mp_request *entry;
539
540         pthread_mutex_lock(&mp_request_list.lock);
541
542         entry = find_request_by_id(m->id);
543         if (entry != NULL) {
544                 /* update request status */
545                 entry->user_req.result = m->result;
546
547                 entry->state = REQ_STATE_COMPLETE;
548
549                 /* trigger thread wakeup */
550                 pthread_cond_signal(&entry->cond);
551         }
552
553         pthread_mutex_unlock(&mp_request_list.lock);
554
555         return 0;
556 }
557
558 /* synchronously request memory map sync, this is only called whenever primary
559  * process initiates the allocation.
560  */
561 int
562 request_sync(void)
563 {
564         struct rte_mp_msg msg;
565         struct rte_mp_reply reply;
566         struct malloc_mp_req *req = (struct malloc_mp_req *)msg.param;
567         struct timespec ts;
568         int i, ret;
569
570         memset(&msg, 0, sizeof(msg));
571         memset(&reply, 0, sizeof(reply));
572
573         /* no need to create tailq entries as this is entirely synchronous */
574
575         msg.num_fds = 0;
576         msg.len_param = sizeof(*req);
577         strlcpy(msg.name, MP_ACTION_SYNC, sizeof(msg.name));
578
579         /* sync request carries no data */
580         req->t = REQ_TYPE_SYNC;
581         req->id = get_unique_id();
582
583         ts.tv_nsec = 0;
584         ts.tv_sec = MP_TIMEOUT_S;
585
586         /* there may be stray timeout still waiting */
587         do {
588                 ret = rte_mp_request_sync(&msg, &reply, &ts);
589         } while (ret != 0 && rte_errno == EEXIST);
590         if (ret != 0) {
591                 RTE_LOG(ERR, EAL, "Could not send sync request to secondary process\n");
592                 ret = -1;
593                 goto out;
594         }
595
596         if (reply.nb_received != reply.nb_sent) {
597                 RTE_LOG(ERR, EAL, "Not all secondaries have responded\n");
598                 ret = -1;
599                 goto out;
600         }
601
602         for (i = 0; i < reply.nb_received; i++) {
603                 struct malloc_mp_req *resp =
604                                 (struct malloc_mp_req *)reply.msgs[i].param;
605                 if (resp->t != REQ_TYPE_SYNC) {
606                         RTE_LOG(ERR, EAL, "Unexpected response from secondary\n");
607                         ret = -1;
608                         goto out;
609                 }
610                 if (resp->id != req->id) {
611                         RTE_LOG(ERR, EAL, "Wrong request ID\n");
612                         ret = -1;
613                         goto out;
614                 }
615                 if (resp->result != REQ_RESULT_SUCCESS) {
616                         RTE_LOG(ERR, EAL, "Secondary process failed to synchronize\n");
617                         ret = -1;
618                         goto out;
619                 }
620         }
621
622         ret = 0;
623 out:
624         free(reply.msgs);
625         return ret;
626 }
627
628 /* this is a synchronous wrapper around a bunch of asynchronous requests to
629  * primary process. this will initiate a request and wait until responses come.
630  */
631 int
632 request_to_primary(struct malloc_mp_req *user_req)
633 {
634         struct rte_mp_msg msg;
635         struct malloc_mp_req *msg_req = (struct malloc_mp_req *)msg.param;
636         struct mp_request *entry;
637         struct timespec ts;
638         struct timeval now;
639         int ret;
640
641         memset(&msg, 0, sizeof(msg));
642         memset(&ts, 0, sizeof(ts));
643
644         pthread_mutex_lock(&mp_request_list.lock);
645
646         entry = malloc(sizeof(*entry));
647         if (entry == NULL) {
648                 RTE_LOG(ERR, EAL, "Cannot allocate memory for request\n");
649                 goto fail;
650         }
651
652         memset(entry, 0, sizeof(*entry));
653
654         if (gettimeofday(&now, NULL) < 0) {
655                 RTE_LOG(ERR, EAL, "Cannot get current time\n");
656                 goto fail;
657         }
658
659         ts.tv_nsec = (now.tv_usec * 1000) % 1000000000;
660         ts.tv_sec = now.tv_sec + MP_TIMEOUT_S +
661                         (now.tv_usec * 1000) / 1000000000;
662
663         /* initialize the request */
664         pthread_cond_init(&entry->cond, NULL);
665
666         msg.num_fds = 0;
667         msg.len_param = sizeof(*msg_req);
668         strlcpy(msg.name, MP_ACTION_REQUEST, sizeof(msg.name));
669
670         /* (attempt to) get a unique id */
671         user_req->id = get_unique_id();
672
673         /* copy contents of user request into the message */
674         memcpy(msg_req, user_req, sizeof(*msg_req));
675
676         if (rte_mp_sendmsg(&msg)) {
677                 RTE_LOG(ERR, EAL, "Cannot send message to primary\n");
678                 goto fail;
679         }
680
681         /* copy contents of user request into active request */
682         memcpy(&entry->user_req, user_req, sizeof(*user_req));
683
684         /* mark request as in progress */
685         entry->state = REQ_STATE_ACTIVE;
686
687         TAILQ_INSERT_TAIL(&mp_request_list.list, entry, next);
688
689         /* finally, wait on timeout */
690         do {
691                 ret = pthread_cond_timedwait(&entry->cond,
692                                 &mp_request_list.lock, &ts);
693         } while (ret != 0 && ret != ETIMEDOUT);
694
695         if (entry->state != REQ_STATE_COMPLETE) {
696                 RTE_LOG(ERR, EAL, "Request timed out\n");
697                 ret = -1;
698         } else {
699                 ret = 0;
700                 user_req->result = entry->user_req.result;
701         }
702         TAILQ_REMOVE(&mp_request_list.list, entry, next);
703         free(entry);
704
705         pthread_mutex_unlock(&mp_request_list.lock);
706         return ret;
707 fail:
708         pthread_mutex_unlock(&mp_request_list.lock);
709         free(entry);
710         return -1;
711 }
712
713 int
714 register_mp_requests(void)
715 {
716         if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
717                 if (rte_mp_action_register(MP_ACTION_REQUEST, handle_request)) {
718                         RTE_LOG(ERR, EAL, "Couldn't register '%s' action\n",
719                                 MP_ACTION_REQUEST);
720                         return -1;
721                 }
722         } else {
723                 if (rte_mp_action_register(MP_ACTION_SYNC, handle_sync)) {
724                         RTE_LOG(ERR, EAL, "Couldn't register '%s' action\n",
725                                 MP_ACTION_SYNC);
726                         return -1;
727                 }
728                 if (rte_mp_action_register(MP_ACTION_ROLLBACK, handle_sync)) {
729                         RTE_LOG(ERR, EAL, "Couldn't register '%s' action\n",
730                                 MP_ACTION_SYNC);
731                         return -1;
732                 }
733                 if (rte_mp_action_register(MP_ACTION_RESPONSE,
734                                 handle_response)) {
735                         RTE_LOG(ERR, EAL, "Couldn't register '%s' action\n",
736                                 MP_ACTION_RESPONSE);
737                         return -1;
738                 }
739         }
740         return 0;
741 }