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 eal_memalloc_mem_event_notify(RTE_MEM_EVENT_ALLOC, map_addr, alloc_sz); 213 214 /* we have succeeded in allocating memory, but we still need to sync 215 * with other processes. however, since DPDK IPC is single-threaded, we 216 * send an asynchronous request and exit this callback. 217 */ 218 219 req->alloc_state.ms = ms; 220 req->alloc_state.ms_len = n_segs; 221 req->alloc_state.map_addr = map_addr; 222 req->alloc_state.map_len = alloc_sz; 223 req->alloc_state.elem = elem; 224 req->alloc_state.heap = heap; 225 226 return 0; 227 fail: 228 free(ms); 229 return -1; 230 } 231 232 /* first stage of primary handling requests from secondary */ 233 static int 234 handle_request(const struct rte_mp_msg *msg, const void *peer __rte_unused) 235 { 236 const struct malloc_mp_req *m = 237 (const struct malloc_mp_req *)msg->param; 238 struct mp_request *entry; 239 int ret; 240 241 /* lock access to request */ 242 pthread_mutex_lock(&mp_request_list.lock); 243 244 /* make sure it's not a dupe */ 245 entry = find_request_by_id(m->id); 246 if (entry != NULL) { 247 RTE_LOG(ERR, EAL, "Duplicate request id\n"); 248 goto fail; 249 } 250 251 entry = malloc(sizeof(*entry)); 252 if (entry == NULL) { 253 RTE_LOG(ERR, EAL, "Unable to allocate memory for request\n"); 254 goto fail; 255 } 256 257 /* erase all data */ 258 memset(entry, 0, sizeof(*entry)); 259 260 if (m->t == REQ_TYPE_ALLOC) { 261 ret = handle_alloc_request(m, entry); 262 } else if (m->t == REQ_TYPE_FREE) { 263 eal_memalloc_mem_event_notify(RTE_MEM_EVENT_FREE, 264 m->free_req.addr, m->free_req.len); 265 266 ret = malloc_heap_free_pages(m->free_req.addr, 267 m->free_req.len); 268 } else { 269 RTE_LOG(ERR, EAL, "Unexpected request from secondary\n"); 270 goto fail; 271 } 272 273 if (ret != 0) { 274 struct rte_mp_msg resp_msg; 275 struct malloc_mp_req *resp = 276 (struct malloc_mp_req *)resp_msg.param; 277 278 /* send failure message straight away */ 279 resp_msg.num_fds = 0; 280 resp_msg.len_param = sizeof(*resp); 281 strlcpy(resp_msg.name, MP_ACTION_RESPONSE, 282 sizeof(resp_msg.name)); 283 284 resp->t = m->t; 285 resp->result = REQ_RESULT_FAIL; 286 resp->id = m->id; 287 288 if (rte_mp_sendmsg(&resp_msg)) { 289 RTE_LOG(ERR, EAL, "Couldn't send response\n"); 290 goto fail; 291 } 292 /* we did not modify the request */ 293 free(entry); 294 } else { 295 struct rte_mp_msg sr_msg; 296 struct malloc_mp_req *sr = 297 (struct malloc_mp_req *)sr_msg.param; 298 struct timespec ts; 299 300 memset(&sr_msg, 0, sizeof(sr_msg)); 301 302 /* we can do something, so send sync request asynchronously */ 303 sr_msg.num_fds = 0; 304 sr_msg.len_param = sizeof(*sr); 305 strlcpy(sr_msg.name, MP_ACTION_SYNC, sizeof(sr_msg.name)); 306 307 ts.tv_nsec = 0; 308 ts.tv_sec = MP_TIMEOUT_S; 309 310 /* sync requests carry no data */ 311 sr->t = REQ_TYPE_SYNC; 312 sr->id = m->id; 313 314 /* there may be stray timeout still waiting */ 315 do { 316 ret = rte_mp_request_async(&sr_msg, &ts, 317 handle_sync_response); 318 } while (ret != 0 && rte_errno == EEXIST); 319 if (ret != 0) { 320 RTE_LOG(ERR, EAL, "Couldn't send sync request\n"); 321 if (m->t == REQ_TYPE_ALLOC) 322 free(entry->alloc_state.ms); 323 goto fail; 324 } 325 326 /* mark request as in progress */ 327 memcpy(&entry->user_req, m, sizeof(*m)); 328 entry->state = REQ_STATE_ACTIVE; 329 330 TAILQ_INSERT_TAIL(&mp_request_list.list, entry, next); 331 } 332 pthread_mutex_unlock(&mp_request_list.lock); 333 return 0; 334 fail: 335 pthread_mutex_unlock(&mp_request_list.lock); 336 free(entry); 337 return -1; 338 } 339 340 /* callback for asynchronous sync requests for primary. this will either do a 341 * sendmsg with results, or trigger rollback request. 342 */ 343 static int 344 handle_sync_response(const struct rte_mp_msg *request, 345 const struct rte_mp_reply *reply) 346 { 347 enum malloc_req_result result; 348 struct mp_request *entry; 349 const struct malloc_mp_req *mpreq = 350 (const struct malloc_mp_req *)request->param; 351 int i; 352 353 /* lock the request */ 354 pthread_mutex_lock(&mp_request_list.lock); 355 356 entry = find_request_by_id(mpreq->id); 357 if (entry == NULL) { 358 RTE_LOG(ERR, EAL, "Wrong request ID\n"); 359 goto fail; 360 } 361 362 result = REQ_RESULT_SUCCESS; 363 364 if (reply->nb_received != reply->nb_sent) 365 result = REQ_RESULT_FAIL; 366 367 for (i = 0; i < reply->nb_received; i++) { 368 struct malloc_mp_req *resp = 369 (struct malloc_mp_req *)reply->msgs[i].param; 370 371 if (resp->t != REQ_TYPE_SYNC) { 372 RTE_LOG(ERR, EAL, "Unexpected response to sync request\n"); 373 result = REQ_RESULT_FAIL; 374 break; 375 } 376 if (resp->id != entry->user_req.id) { 377 RTE_LOG(ERR, EAL, "Response to wrong sync request\n"); 378 result = REQ_RESULT_FAIL; 379 break; 380 } 381 if (resp->result == REQ_RESULT_FAIL) { 382 result = REQ_RESULT_FAIL; 383 break; 384 } 385 } 386 387 if (entry->user_req.t == REQ_TYPE_FREE) { 388 struct rte_mp_msg msg; 389 struct malloc_mp_req *resp = (struct malloc_mp_req *)msg.param; 390 391 memset(&msg, 0, sizeof(msg)); 392 393 /* this is a free request, just sendmsg result */ 394 resp->t = REQ_TYPE_FREE; 395 resp->result = result; 396 resp->id = entry->user_req.id; 397 msg.num_fds = 0; 398 msg.len_param = sizeof(*resp); 399 strlcpy(msg.name, MP_ACTION_RESPONSE, sizeof(msg.name)); 400 401 if (rte_mp_sendmsg(&msg)) 402 RTE_LOG(ERR, EAL, "Could not send message to secondary process\n"); 403 404 TAILQ_REMOVE(&mp_request_list.list, entry, next); 405 free(entry); 406 } else if (entry->user_req.t == REQ_TYPE_ALLOC && 407 result == REQ_RESULT_SUCCESS) { 408 struct malloc_heap *heap = entry->alloc_state.heap; 409 struct rte_mp_msg msg; 410 struct malloc_mp_req *resp = 411 (struct malloc_mp_req *)msg.param; 412 413 memset(&msg, 0, sizeof(msg)); 414 415 heap->total_size += entry->alloc_state.map_len; 416 417 /* result is success, so just notify secondary about this */ 418 resp->t = REQ_TYPE_ALLOC; 419 resp->result = result; 420 resp->id = entry->user_req.id; 421 msg.num_fds = 0; 422 msg.len_param = sizeof(*resp); 423 strlcpy(msg.name, MP_ACTION_RESPONSE, sizeof(msg.name)); 424 425 if (rte_mp_sendmsg(&msg)) 426 RTE_LOG(ERR, EAL, "Could not send message to secondary process\n"); 427 428 TAILQ_REMOVE(&mp_request_list.list, entry, next); 429 free(entry->alloc_state.ms); 430 free(entry); 431 } else if (entry->user_req.t == REQ_TYPE_ALLOC && 432 result == REQ_RESULT_FAIL) { 433 struct rte_mp_msg rb_msg; 434 struct malloc_mp_req *rb = 435 (struct malloc_mp_req *)rb_msg.param; 436 struct timespec ts; 437 struct primary_alloc_req_state *state = 438 &entry->alloc_state; 439 int ret; 440 441 memset(&rb_msg, 0, sizeof(rb_msg)); 442 443 /* we've failed to sync, so do a rollback */ 444 eal_memalloc_mem_event_notify(RTE_MEM_EVENT_FREE, 445 state->map_addr, state->map_len); 446 447 rollback_expand_heap(state->ms, state->ms_len, state->elem, 448 state->map_addr, state->map_len); 449 450 /* send rollback request */ 451 rb_msg.num_fds = 0; 452 rb_msg.len_param = sizeof(*rb); 453 strlcpy(rb_msg.name, MP_ACTION_ROLLBACK, sizeof(rb_msg.name)); 454 455 ts.tv_nsec = 0; 456 ts.tv_sec = MP_TIMEOUT_S; 457 458 /* sync requests carry no data */ 459 rb->t = REQ_TYPE_SYNC; 460 rb->id = entry->user_req.id; 461 462 /* there may be stray timeout still waiting */ 463 do { 464 ret = rte_mp_request_async(&rb_msg, &ts, 465 handle_rollback_response); 466 } while (ret != 0 && rte_errno == EEXIST); 467 if (ret != 0) { 468 RTE_LOG(ERR, EAL, "Could not send rollback request to secondary process\n"); 469 470 /* we couldn't send rollback request, but that's OK - 471 * secondary will time out, and memory has been removed 472 * from heap anyway. 473 */ 474 TAILQ_REMOVE(&mp_request_list.list, entry, next); 475 free(state->ms); 476 free(entry); 477 goto fail; 478 } 479 } else { 480 RTE_LOG(ERR, EAL, " to sync request of unknown type\n"); 481 goto fail; 482 } 483 484 pthread_mutex_unlock(&mp_request_list.lock); 485 return 0; 486 fail: 487 pthread_mutex_unlock(&mp_request_list.lock); 488 return -1; 489 } 490 491 static int 492 handle_rollback_response(const struct rte_mp_msg *request, 493 const struct rte_mp_reply *reply __rte_unused) 494 { 495 struct rte_mp_msg msg; 496 struct malloc_mp_req *resp = (struct malloc_mp_req *)msg.param; 497 const struct malloc_mp_req *mpreq = 498 (const struct malloc_mp_req *)request->param; 499 struct mp_request *entry; 500 501 /* lock the request */ 502 pthread_mutex_lock(&mp_request_list.lock); 503 504 memset(&msg, 0, sizeof(msg)); 505 506 entry = find_request_by_id(mpreq->id); 507 if (entry == NULL) { 508 RTE_LOG(ERR, EAL, "Wrong request ID\n"); 509 goto fail; 510 } 511 512 if (entry->user_req.t != REQ_TYPE_ALLOC) { 513 RTE_LOG(ERR, EAL, "Unexpected active request\n"); 514 goto fail; 515 } 516 517 /* we don't care if rollback succeeded, request still failed */ 518 resp->t = REQ_TYPE_ALLOC; 519 resp->result = REQ_RESULT_FAIL; 520 resp->id = mpreq->id; 521 msg.num_fds = 0; 522 msg.len_param = sizeof(*resp); 523 strlcpy(msg.name, MP_ACTION_RESPONSE, sizeof(msg.name)); 524 525 if (rte_mp_sendmsg(&msg)) 526 RTE_LOG(ERR, EAL, "Could not send message to secondary process\n"); 527 528 /* clean up */ 529 TAILQ_REMOVE(&mp_request_list.list, entry, next); 530 free(entry->alloc_state.ms); 531 free(entry); 532 533 pthread_mutex_unlock(&mp_request_list.lock); 534 return 0; 535 fail: 536 pthread_mutex_unlock(&mp_request_list.lock); 537 return -1; 538 } 539 540 /* final stage of the request from secondary */ 541 static int 542 handle_response(const struct rte_mp_msg *msg, const void *peer __rte_unused) 543 { 544 const struct malloc_mp_req *m = 545 (const struct malloc_mp_req *)msg->param; 546 struct mp_request *entry; 547 548 pthread_mutex_lock(&mp_request_list.lock); 549 550 entry = find_request_by_id(m->id); 551 if (entry != NULL) { 552 /* update request status */ 553 entry->user_req.result = m->result; 554 555 entry->state = REQ_STATE_COMPLETE; 556 557 /* trigger thread wakeup */ 558 pthread_cond_signal(&entry->cond); 559 } 560 561 pthread_mutex_unlock(&mp_request_list.lock); 562 563 return 0; 564 } 565 566 /* synchronously request memory map sync, this is only called whenever primary 567 * process initiates the allocation. 568 */ 569 int 570 request_sync(void) 571 { 572 struct rte_mp_msg msg; 573 struct rte_mp_reply reply; 574 struct malloc_mp_req *req = (struct malloc_mp_req *)msg.param; 575 struct timespec ts; 576 int i, ret; 577 578 memset(&msg, 0, sizeof(msg)); 579 memset(&reply, 0, sizeof(reply)); 580 581 /* no need to create tailq entries as this is entirely synchronous */ 582 583 msg.num_fds = 0; 584 msg.len_param = sizeof(*req); 585 strlcpy(msg.name, MP_ACTION_SYNC, sizeof(msg.name)); 586 587 /* sync request carries no data */ 588 req->t = REQ_TYPE_SYNC; 589 req->id = get_unique_id(); 590 591 ts.tv_nsec = 0; 592 ts.tv_sec = MP_TIMEOUT_S; 593 594 /* there may be stray timeout still waiting */ 595 do { 596 ret = rte_mp_request_sync(&msg, &reply, &ts); 597 } while (ret != 0 && rte_errno == EEXIST); 598 if (ret != 0) { 599 RTE_LOG(ERR, EAL, "Could not send sync request to secondary process\n"); 600 ret = -1; 601 goto out; 602 } 603 604 if (reply.nb_received != reply.nb_sent) { 605 RTE_LOG(ERR, EAL, "Not all secondaries have responded\n"); 606 ret = -1; 607 goto out; 608 } 609 610 for (i = 0; i < reply.nb_received; i++) { 611 struct malloc_mp_req *resp = 612 (struct malloc_mp_req *)reply.msgs[i].param; 613 if (resp->t != REQ_TYPE_SYNC) { 614 RTE_LOG(ERR, EAL, "Unexpected response from secondary\n"); 615 ret = -1; 616 goto out; 617 } 618 if (resp->id != req->id) { 619 RTE_LOG(ERR, EAL, "Wrong request ID\n"); 620 ret = -1; 621 goto out; 622 } 623 if (resp->result != REQ_RESULT_SUCCESS) { 624 RTE_LOG(ERR, EAL, "Secondary process failed to synchronize\n"); 625 ret = -1; 626 goto out; 627 } 628 } 629 630 ret = 0; 631 out: 632 free(reply.msgs); 633 return ret; 634 } 635 636 /* this is a synchronous wrapper around a bunch of asynchronous requests to 637 * primary process. this will initiate a request and wait until responses come. 638 */ 639 int 640 request_to_primary(struct malloc_mp_req *user_req) 641 { 642 struct rte_mp_msg msg; 643 struct malloc_mp_req *msg_req = (struct malloc_mp_req *)msg.param; 644 struct mp_request *entry; 645 struct timespec ts; 646 struct timeval now; 647 int ret; 648 649 memset(&msg, 0, sizeof(msg)); 650 memset(&ts, 0, sizeof(ts)); 651 652 pthread_mutex_lock(&mp_request_list.lock); 653 654 entry = malloc(sizeof(*entry)); 655 if (entry == NULL) { 656 RTE_LOG(ERR, EAL, "Cannot allocate memory for request\n"); 657 goto fail; 658 } 659 660 memset(entry, 0, sizeof(*entry)); 661 662 if (gettimeofday(&now, NULL) < 0) { 663 RTE_LOG(ERR, EAL, "Cannot get current time\n"); 664 goto fail; 665 } 666 667 ts.tv_nsec = (now.tv_usec * 1000) % 1000000000; 668 ts.tv_sec = now.tv_sec + MP_TIMEOUT_S + 669 (now.tv_usec * 1000) / 1000000000; 670 671 /* initialize the request */ 672 pthread_cond_init(&entry->cond, NULL); 673 674 msg.num_fds = 0; 675 msg.len_param = sizeof(*msg_req); 676 strlcpy(msg.name, MP_ACTION_REQUEST, sizeof(msg.name)); 677 678 /* (attempt to) get a unique id */ 679 user_req->id = get_unique_id(); 680 681 /* copy contents of user request into the message */ 682 memcpy(msg_req, user_req, sizeof(*msg_req)); 683 684 if (rte_mp_sendmsg(&msg)) { 685 RTE_LOG(ERR, EAL, "Cannot send message to primary\n"); 686 goto fail; 687 } 688 689 /* copy contents of user request into active request */ 690 memcpy(&entry->user_req, user_req, sizeof(*user_req)); 691 692 /* mark request as in progress */ 693 entry->state = REQ_STATE_ACTIVE; 694 695 TAILQ_INSERT_TAIL(&mp_request_list.list, entry, next); 696 697 /* finally, wait on timeout */ 698 do { 699 ret = pthread_cond_timedwait(&entry->cond, 700 &mp_request_list.lock, &ts); 701 } while (ret != 0 && ret != ETIMEDOUT); 702 703 if (entry->state != REQ_STATE_COMPLETE) { 704 RTE_LOG(ERR, EAL, "Request timed out\n"); 705 ret = -1; 706 } else { 707 ret = 0; 708 user_req->result = entry->user_req.result; 709 } 710 TAILQ_REMOVE(&mp_request_list.list, entry, next); 711 free(entry); 712 713 pthread_mutex_unlock(&mp_request_list.lock); 714 return ret; 715 fail: 716 pthread_mutex_unlock(&mp_request_list.lock); 717 free(entry); 718 return -1; 719 } 720 721 int 722 register_mp_requests(void) 723 { 724 if (rte_eal_process_type() == RTE_PROC_PRIMARY) { 725 if (rte_mp_action_register(MP_ACTION_REQUEST, handle_request)) { 726 RTE_LOG(ERR, EAL, "Couldn't register '%s' action\n", 727 MP_ACTION_REQUEST); 728 return -1; 729 } 730 } else { 731 if (rte_mp_action_register(MP_ACTION_SYNC, handle_sync)) { 732 RTE_LOG(ERR, EAL, "Couldn't register '%s' action\n", 733 MP_ACTION_SYNC); 734 return -1; 735 } 736 if (rte_mp_action_register(MP_ACTION_ROLLBACK, handle_sync)) { 737 RTE_LOG(ERR, EAL, "Couldn't register '%s' action\n", 738 MP_ACTION_SYNC); 739 return -1; 740 } 741 if (rte_mp_action_register(MP_ACTION_RESPONSE, 742 handle_response)) { 743 RTE_LOG(ERR, EAL, "Couldn't register '%s' action\n", 744 MP_ACTION_RESPONSE); 745 return -1; 746 } 747 } 748 return 0; 749 } 750