-
Notifications
You must be signed in to change notification settings - Fork 595
UCT/IB/MLX5: support in progress error handler #11829
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 16 commits
b27def8
0a8d5c3
38e997a
4107de3
6e05659
e428402
41e17cd
9c4afd0
a34966e
b01ffa0
a80d584
529fceb
220b9e0
95d6161
04896be
1232be4
1ba4778
7ae7ad8
55b6fd9
c4be7ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,6 +52,7 @@ typedef struct uct_rc_mlx5_base_ep { | |
| struct { | ||
| uct_ib_mlx5_txwq_t wq; | ||
| } tx; | ||
| uint8_t err_handler_inprogress; | ||
| } uct_rc_mlx5_base_ep_t; | ||
|
|
||
| typedef __be32 uct_rc_mlx5_tx_token_t; | ||
|
|
@@ -208,6 +209,9 @@ ucs_status_t | |
| uct_rc_mlx5_base_ep_invalidate(uct_ep_h tl_ep, | ||
| const uct_ep_invalidate_params_t *params); | ||
|
|
||
| ucs_status_t uct_rc_mlx5_ep_outstanding_purge( | ||
| uct_ep_h tl_ep, const uct_ep_outstanding_purge_params_t *params); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should probably remove this, will be added in am_short purge PR |
||
|
|
||
| ucs_status_t uct_rc_mlx5_base_ep_fc_ctrl(uct_ep_t *tl_ep, unsigned op, | ||
| uct_rc_pending_req_t *req); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1931,31 +1931,36 @@ uct_rc_mlx5_iface_common_atomic_data(unsigned opcode, unsigned size, uint64_t va | |
| } | ||
|
|
||
| static UCS_F_ALWAYS_INLINE void | ||
| uct_rc_mlx5_iface_update_tx_res(uct_rc_iface_t *rc_iface, | ||
| uct_rc_mlx5_base_ep_t *rc_mlx5_base_ep, | ||
| uint16_t hw_ci) | ||
| uct_rc_mlx5_iface_update_tx_cq_res(uct_rc_iface_t *rc_iface, | ||
| uct_rc_mlx5_base_ep_t *ep, uint16_t hw_ci) | ||
| { | ||
| uct_ib_mlx5_txwq_t *txwq = &rc_mlx5_base_ep->tx.wq; | ||
| uct_rc_txqp_t *txqp = &rc_mlx5_base_ep->super.txqp; | ||
| uint16_t bb_num; | ||
|
|
||
| bb_num = uct_ib_mlx5_txwq_update_bb(txwq, hw_ci) - | ||
| uct_rc_txqp_available(txqp); | ||
|
|
||
| /* Must always have positive number of released resources. The first | ||
| * completion will report bb_num=1 (because prev_sw_pi is initialized to -1) | ||
| * and all the rest report the amount of BBs the previous WQE has consumed. | ||
| */ | ||
| ucs_assertv(bb_num > 0, "hw_ci=%d prev_sw_pi=%d available=%d bb_num=%d", | ||
| hw_ci, txwq->prev_sw_pi, txqp->available, bb_num); | ||
| uct_ib_mlx5_txwq_t *txwq = &ep->tx.wq; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Switch from the uint16_t bb_num = hw_ci - prev_hw_ci;
ucs_assertv(bb_num > 0, ...);
txwq->hw_ci = hw_ci;The previous
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's strict monotonicity. HW should not generates 2 cqes for 1 wqe. |
||
| uint16_t prev_hw_ci = txwq->hw_ci; | ||
| uint16_t bb_num = hw_ci - prev_hw_ci; | ||
|
|
||
| uct_rc_txqp_available_add(txqp, bb_num); | ||
| ucs_assert(uct_rc_txqp_available(txqp) <= txwq->bb_max); | ||
| ucs_assertv(bb_num > 0, "hw_ci=%d prev_hw_ci=%d ft_ci=%d bb_num=%d", hw_ci, | ||
| prev_hw_ci, txwq->ft_ci, bb_num); | ||
|
|
||
| txwq->hw_ci = hw_ci; | ||
| uct_rc_iface_update_reads(rc_iface); | ||
| uct_rc_iface_add_cq_credits(rc_iface, bb_num); | ||
| } | ||
|
|
||
| static UCS_F_ALWAYS_INLINE void | ||
| uct_rc_mlx5_ep_update_tx_qp_res(uct_rc_mlx5_base_ep_t *ep, uint16_t sw_ci) | ||
| { | ||
| uct_ib_mlx5_txwq_t *txwq = &ep->tx.wq; | ||
| uct_rc_txqp_t *txqp = &ep->super.txqp; | ||
| int16_t prev_available = uct_rc_txqp_available(txqp); | ||
| uint16_t available = txwq->bb_max - | ||
| (txwq->prev_sw_pi - sw_ci); | ||
|
|
||
| ucs_assert(available >= prev_available); | ||
| uct_rc_txqp_available_add(txqp, available - prev_available); | ||
|
|
||
| ucs_assert(uct_rc_txqp_available(txqp) <= txwq->bb_max); | ||
| } | ||
|
|
||
| static UCS_F_ALWAYS_INLINE unsigned | ||
| uct_rc_mlx5_iface_poll_tx(uct_rc_mlx5_iface_common_t *iface, int poll_flags) | ||
| { | ||
|
|
@@ -1986,7 +1991,8 @@ uct_rc_mlx5_iface_poll_tx(uct_rc_mlx5_iface_common_t *iface, int poll_flags) | |
|
|
||
| uct_rc_mlx5_txqp_process_tx_cqe(&ep->super.txqp, cqe, hw_ci); | ||
| ucs_arbiter_group_schedule(&iface->super.tx.arbiter, &ep->super.arb_group); | ||
| uct_rc_mlx5_iface_update_tx_res(&iface->super, ep, hw_ci); | ||
| uct_rc_mlx5_ep_update_tx_qp_res(ep, hw_ci); | ||
| uct_rc_mlx5_iface_update_tx_cq_res(&iface->super, ep, hw_ci); | ||
| uct_rc_iface_arbiter_dispatch(&iface->super); | ||
| uct_ib_mlx5_update_db_cq_ci(&iface->cq[UCT_IB_DIR_TX]); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,7 @@ | |
| #endif | ||
|
|
||
| #include <uct/ib/mlx5/ib_mlx5_log.h> | ||
| #include <uct/ib/mlx5/ib_mlx5_ext.h> | ||
| #include <ucs/vfs/base/vfs_cb.h> | ||
| #include <ucs/vfs/base/vfs_obj.h> | ||
| #include <ucs/arch/cpu.h> | ||
|
|
@@ -817,6 +818,23 @@ ucs_status_t uct_rc_mlx5_base_ep_invalidate(uct_ep_h tl_ep, | |
| IBV_QPS_ERR); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. uct_rc_mlx5_ep_outstanding_purge asserts ep->err_handler_inprogress, but the public uct_ep_outstanding_purge API routes through it and is reachable outside error handling (fresh ep → err_handler_inprogress == 0 → debug abort). |
||
| } | ||
|
|
||
| ucs_status_t uct_rc_mlx5_ep_outstanding_purge( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. blocker: this replaces the plugin dispatch (
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will impl uct_rc_mlx5_ep_outstanding_purge in later PRs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. uct_rc_mlx5_ep_outstanding_purge begins with ucs_assert(ep->err_handler_inprogress) then reads ep->tx.wq.ft_ci; when the ep is not mid error-handling this aborts in debug builds and feeds a stale/uninitialized ft_ci into uct_rc_mlx5_ep_update_tx_qp_res in release builds. It should tolerate err_handler_inprogress == 0 instead of requiring callers to bypass the public API.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Purge should be called only after error callback return UCS_INPROGRESS. |
||
| uct_ep_h tl_ep, const uct_ep_outstanding_purge_params_t *params) | ||
| { | ||
| uct_rc_mlx5_base_ep_t *ep = ucs_derived_of(tl_ep, uct_rc_mlx5_base_ep_t); | ||
| ucs_status_t status; | ||
|
|
||
| ucs_assert(ep->err_handler_inprogress); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. blocker: |
||
|
|
||
| /* TODO: Implement purge and replace the external purge call. */ | ||
| status = uct_ib_mlx5_ext_ep_outstanding_purge(tl_ep, params); | ||
|
|
||
| /* TODO: Advance ft_ci as each WQE is processed to reclaim TX resources. */ | ||
| uct_rc_mlx5_ep_update_tx_qp_res(ep, ep->tx.wq.ft_ci); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| return status; | ||
| } | ||
|
|
||
| ucs_status_t uct_rc_mlx5_base_ep_fc_ctrl(uct_ep_t *tl_ep, unsigned op, | ||
| uct_rc_pending_req_t *req) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Purge will be implemented later PR. |
||
| { | ||
|
|
@@ -1242,6 +1260,7 @@ UCS_CLASS_INIT_FUNC(uct_rc_mlx5_base_ep_t, const uct_ep_params_t *params) | |
|
|
||
| UCS_CLASS_CALL_SUPER_INIT(uct_rc_ep_t, &iface->super, | ||
| self->tx.wq.super.qp_num, params); | ||
| self->err_handler_inprogress = 0; | ||
|
|
||
| if (self->tx.wq.super.type == UCT_IB_MLX5_OBJ_TYPE_VERBS) { | ||
| status = uct_rc_iface_qp_init(&iface->super, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -194,6 +194,10 @@ void uct_rc_mlx5_iface_handle_failure(uct_ib_iface_t *ib_iface, void *arg, | |
| qp_num), | ||
| uct_rc_mlx5_base_ep_t); | ||
| uint16_t pi = ntohs(cqe->wqe_counter); | ||
| #if UCS_ENABLE_ASSERT | ||
| uct_iface_attr_v2_t iface_attr; | ||
| ucs_status_t query_status; | ||
| #endif | ||
| ucs_log_level_t log_lvl; | ||
| ucs_status_t status; | ||
|
|
||
|
|
@@ -203,27 +207,58 @@ void uct_rc_mlx5_iface_handle_failure(uct_ib_iface_t *ib_iface, void *arg, | |
| goto out; | ||
| } | ||
|
|
||
| uct_rc_txqp_purge_outstanding(iface, &ep->super.txqp, ep_status, pi, 0); | ||
| ucs_arbiter_group_purge(&iface->tx.arbiter, &ep->super.arb_group, | ||
| uct_rc_ep_arbiter_purge_internal_cb, NULL); | ||
| uct_rc_mlx5_iface_update_tx_res(iface, ep, pi); | ||
| uct_ib_mlx5_txwq_update_flags(&ep->tx.wq, UCT_IB_MLX5_TXWQ_FLAG_FAILED, 0); | ||
|
|
||
| if (ep->super.flags & (UCT_RC_EP_FLAG_ERR_HANDLER_INVOKED | | ||
| UCT_RC_EP_FLAG_FLUSH_CANCEL)) { | ||
| goto out; | ||
| goto out_update_tx_res; | ||
| } | ||
|
|
||
| ep->super.flags |= UCT_RC_EP_FLAG_ERR_HANDLER_INVOKED; | ||
| uct_rc_fc_restore_wnd(iface, &ep->super.fc); | ||
|
|
||
| status = uct_iface_handle_ep_err(&iface->super.super.super, | ||
| &ep->super.super.super, ep_status); | ||
| log_lvl = uct_base_iface_failure_log_level(&ib_iface->super, status, | ||
| ep_status); | ||
|
|
||
| if (status == UCS_INPROGRESS) { | ||
| #if UCS_ENABLE_ASSERT | ||
| iface_attr.field_mask = UCT_IFACE_ATTR_FIELD_CAP_FLAGS; | ||
|
|
||
| query_status = uct_iface_query_v2(&iface->super.super.super, | ||
| &iface_attr); | ||
| ucs_assert(query_status == UCS_OK); | ||
| ucs_assert(iface_attr.cap.flags & UCT_IFACE_FLAG_V2_QUERY_TOKEN); | ||
| #endif | ||
|
|
||
| /* Save last completed WQE. TX QP resources are reserved until purge. */ | ||
| ep->tx.wq.ft_ci = ep->tx.wq.prev_sw_pi - | ||
| (ep->tx.wq.bb_max - | ||
| uct_rc_txqp_available(&ep->super.txqp)); | ||
| ep->err_handler_inprogress = 1; | ||
|
|
||
| ucs_debug("ep %p outstanding WQE range (%u, %u)", ep, ep->tx.wq.ft_ci, | ||
|
jeynmann marked this conversation as resolved.
|
||
| ep->tx.wq.sw_pi); | ||
|
|
||
| log_lvl = uct_base_iface_failure_log_level(&ib_iface->super, UCS_OK, | ||
| ep_status); | ||
| } else { | ||
| log_lvl = uct_base_iface_failure_log_level(&ib_iface->super, status, | ||
| ep_status); | ||
| } | ||
|
|
||
|
jeynmann marked this conversation as resolved.
|
||
|
|
||
| uct_ib_mlx5_completion_with_err(ib_iface, arg, &ep->tx.wq, log_lvl); | ||
|
|
||
| out_update_tx_res: | ||
| if (!(ep->err_handler_inprogress)) { | ||
| uct_rc_txqp_purge_outstanding(iface, &ep->super.txqp, ep_status, pi, 0); | ||
| uct_rc_mlx5_ep_update_tx_qp_res(ep, pi); | ||
| } | ||
|
|
||
| uct_rc_mlx5_iface_update_tx_cq_res(iface, ep, pi); | ||
|
|
||
| out: | ||
| uct_rc_iface_arbiter_dispatch(iface); | ||
| } | ||
|
|
@@ -1204,7 +1239,7 @@ static uct_rc_iface_ops_t uct_rc_mlx5_iface_ops = { | |
| .ep_is_connected = uct_rc_mlx5_base_ep_is_connected, | ||
| .ep_get_device_ep = (uct_ep_get_device_ep_func_t)ucs_empty_function_return_unsupported, | ||
| .ep_put_sgl_zcopy = uct_rc_mlx5_ep_put_sgl_zcopy, | ||
| .ep_outstanding_purge = uct_ib_mlx5_ext_ep_outstanding_purge | ||
| .ep_outstanding_purge = uct_rc_mlx5_ep_outstanding_purge | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rewiring .ep_outstanding_purge to uct_rc_mlx5_ep_outstanding_purge, which begins with ucs_assert(ep->err_handler_inprogress) and then reads ep->tx.wq.ft_ci, makes the public uct_ep_outstanding_purge() API path unsafe whenever the ep is not mid error-handling: it aborts in debug builds and, in release, feeds a stale/uninitialized ft_ci into uct_rc_mlx5_ep_update_tx_qp_res, corrupting QP resource accounting. The purge dispatch should tolerate err_handler_inprogress == 0 (early-return / delegate to the external purge without touching ft_ci) rather than requiring callers to route around it.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Purge should be called only after error callback return |
||
| }, | ||
| .create_cq = uct_rc_mlx5_iface_common_create_cq, | ||
| .destroy_cq = uct_rc_mlx5_iface_common_destroy_cq, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -104,7 +104,7 @@ UCS_TEST_P(test_uct_ib_mlx5_ext_rc, ep_outstanding_purge) | |
| { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The gtest was changed from uct_ep_outstanding_purge to uct_ib_mlx5_ext_ep_outstanding_purge, sidestepping the new wrapper and its assert rather than covering the public-API path — which is exactly the reachable case in Finding 1. |
||
| scoped_log_handler wrap_err(wrap_errors_logger); | ||
| EXPECT_EQ(UCS_ERR_INVALID_PARAM, | ||
| uct_ep_outstanding_purge(m_e1->ep(0), ¶ms)); | ||
| uct_ib_mlx5_ext_ep_outstanding_purge(m_e1->ep(0), ¶ms)); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test now calls uct_ib_mlx5_ext_ep_outstanding_purge() directly instead of the public uct_ep_outstanding_purge(). That change is a workaround for the newly-broken public entry point — it hides the regression and removes public-path coverage.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will be removed in later PRs. |
||
| } | ||
|
|
||
| register_plugin("stub"); | ||
|
|
@@ -118,7 +118,7 @@ UCS_TEST_P(test_uct_ib_mlx5_ext_rc, ep_outstanding_purge) | |
| params.cb = purge_cb; | ||
| params.arg = &callback_invoked; | ||
|
|
||
| ASSERT_UCS_OK(uct_ep_outstanding_purge(m_e1->ep(0), ¶ms)); | ||
| ASSERT_UCS_OK(uct_ib_mlx5_ext_ep_outstanding_purge(m_e1->ep(0), ¶ms)); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test now calls uct_ib_mlx5_ext_ep_outstanding_purge() directly instead of the public uct_ep_outstanding_purge(), bypassing the newly-broken public entry point and removing public-path coverage.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will be removed in later PRs. |
||
| EXPECT_TRUE(callback_invoked); | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using
uint8_t err_handler_inprogress;instead ofbool.boolis not used elsewhere in the IB ep/iface structs, and the initializerself->err_handler_inprogress = 0;assigns an integer rather thanfalse;uint8_tmatches the existing convention (e.g.uct_rc_ep_t::flags). Not blocking.