UCT/EP: Enhance uct_ep_check#11495
Conversation
…acks - Modified uct_ep_check to accept a completion callback, allowing for asynchronous reporting of peer status. - Updated related functions across various transport implementations to handle the new completion parameter. - Added tests to verify the correct behavior of the asynchronous completion mechanism during endpoint checks. This change improves the flexibility and responsiveness of endpoint status checks in the UCP layer.
|
🤖 Starting review — findings will be posted here when done. |
|
🤖 Starting review — findings will be posted here when done. |
svc-nvidia-pr-review
left a comment
There was a problem hiding this comment.
Code Review
8 findings — 🚨 1 blocker,
7 findings posted as inline comments.
General Findings
src/uct/api/uct.h
API doc for uct_ep_check is not updated to describe the new comp semantics. Public header still implies comp must be NULL (matching the just-removed UCT_CHECK_PARAM). pls add a docstring update covering UCS_OK vs UCS_INPROGRESS returns and when comp is invoked.
| ucs_status_t status; | ||
|
|
||
| UCT_EP_KEEPALIVE_CHECK_PARAM(flags, comp); | ||
| UCT_EP_KEEPALIVE_CHECK_PARAM(flags); |
There was a problem hiding this comment.
Behavior change for legacy comp == NULL callers: in the wireup branch the function now returns UCS_INPROGRESS (or UCS_ERR_NO_RESOURCE) via uct_ud_ep_comp_skb_add(..., NULL) instead of the previous UCS_OK. The UCP worker is adjusted, but other NULL-comp callers (e.g. ucp_wireup_ep_check) need to be re-checked for tolerance of the new statuses.
| if (status == UCS_ERR_NO_RESOURCE) { | ||
| return 0; | ||
| } else if (status != UCS_OK) { | ||
| } else if (UCS_STATUS_IS_ERR(status)) { |
There was a problem hiding this comment.
Replacing status != UCS_OK with UCS_STATUS_IS_ERR(status) silently swallows UCS_INPROGRESS here. The call passes comp == NULL, so today no transport returns UCS_INPROGRESS, but the assertion that previously held (status == UCS_OK) is now lost. Worth an ucs_assert(status == UCS_OK) in the success arm to catch regressions.
| * pending req, and the first req's dispatch clears the flag before the | ||
| * rest run. With comp == NULL we have nothing to fire and the sibling's | ||
| * keepalive WQE already covers peer-liveness - skip the redundant post. */ | ||
| if ((comp == NULL) && !(ep->flags & UCT_RC_EP_FLAG_KEEPALIVE_PENDING)) { |
There was a problem hiding this comment.
The (comp == NULL) && !KEEPALIVE_PENDING skip is only for NULL-comp pending reqs, but with comp != NULL multiple pending reqs can pile up on the same EP and each posts its own keepalive WQE. If a sibling already cleared the flag (peer-liveness was just proven) maybe we should fire the comp with UCS_OK instead of posting a redundant probe?
| } | ||
|
|
||
|
|
||
| UCT_INSTANTIATE_NO_SELF_TEST_CASE(test_uct_ep_check_async) |
There was a problem hiding this comment.
The new tests only exercise the UCS_OK comp path. No test exercises the error path (peer dies / EP destroyed → comp fires UCS_ERR_CANCELED / UCS_ERR_CONNECTION_RESET) for any of the new pending-purge code in tcp_ep.c, rc_ep.c, ud_ep.c.
| tcp_pending_req = ucs_derived_of(self, uct_tcp_ep_pending_req_t); | ||
| status = (tcp_pending_req->ep->conn_state == | ||
| UCT_TCP_EP_CONN_STATE_CLOSED) ? | ||
| UCS_ERR_CONNECTION_RESET : UCS_ERR_CANCELED; |
There was a problem hiding this comment.
uct_tcp_ep_pending_purge_cb chooses CONNECTION_RESET for CLOSED state, but on normal EP destroy the destructor flips conn_state to CLOSED before pending_purge runs, so user keepalive comps on destroyed EPs will always report UCS_ERR_CONNECTION_RESET rather than UCS_ERR_CANCELED. Is that the intended status?
| ep->flags &= ~UCT_RC_EP_FLAG_KEEPALIVE_PENDING; | ||
| if (status == UCS_OK) { | ||
| ep->flags &= ~UCT_RC_EP_FLAG_KEEPALIVE_PENDING; | ||
| ucs_assert(comp == NULL); |
There was a problem hiding this comment.
ucs_assert(comp == NULL) here is true by construction (uct_rc_ep_check_internal returns UCS_INPROGRESS when comp != NULL), but the early if (status == UCS_ERR_NO_RESOURCE) + late if (status == UCS_INPROGRESS) + final error branch reads easier as a switch. Minor readability.
evgeny-leksikov
left a comment
There was a problem hiding this comment.
Need to fix commit comment UCP -> UCT
| } | ||
|
|
||
| return status; | ||
| if (status == UCS_INPROGRESS) { |
There was a problem hiding this comment.
When a queued ep_check posts, uct_rc_ep_check_progress() returns UCS_INPROGRESS. uct_rc_ep_process_pending() maps that to NEXT_GROUP -> the same pending request stays queued. Further progress can repost w same comp -> purge can cancel a comp that the CQE path will also invoke. Free the pending request and return UCS_OK once the async check is armed?
There was a problem hiding this comment.
Posted operations should be removed from pending queue and enqueued to transport level outstanding ops. So, pending purge won't take any effect on it.
There was a problem hiding this comment.
In uct_rc_ep_check after uct_rc_ep_check_internal, there's
ucs_assert((comp == NULL) ? (status == UCS_OK) :
(status == UCS_INPROGRESS));
| /* in case if no TX resources are available then there is at least | ||
| * one signaled operation which provides actual peer status, in this case | ||
| * just return without any actions */ | ||
| UCT_RC_CHECK_TXQP_RET(iface, ep, UCS_OK); |
There was a problem hiding this comment.
Returns UCS_OK even when comp != NULL. In debug -> new assertion expects UCS_INPROGRESS; in release -> ignores the user comp. The async path should either attach comp to the relevant outstanding signaled op or keep/retry the check until it can arm a real comp.
There was a problem hiding this comment.
In debug -> new assertion expects UCS_INPROGRESS
could you please point which one?
in release -> ignores the user comp
this is the common UCT/UCP design - don't invoke completion if the operation completed immediately.
There was a problem hiding this comment.
uct_rc_ep_process_pending() gets UCS_INPROGRESS from the callback -> returns UCS_ARBITER_CB_RESULT_NEXT_GROUP. Then, after uct_rc_ep_check_progress() returns UCS_INPROGRESS, the same uct_pending_req_t still remains on the arbiter, while its comp was already attached to the txqp outstanding op?
What?
Enhance uct_ep_check to support asynchronous completion callbacks.
Why?
How?