Skip to content

mctpd: Support MCTP Discovery Notify command - #165

Open
chajasmine-bit wants to merge 1 commit into
CodeConstruct:mainfrom
chajasmine-bit:discovery-notify
Open

mctpd: Support MCTP Discovery Notify command#165
chajasmine-bit wants to merge 1 commit into
CodeConstruct:mainfrom
chajasmine-bit:discovery-notify

Conversation

@chajasmine-bit

Copy link
Copy Markdown

Implement support for the MCTP Discovery Notify control command in mctpd. When an MCTP endpoint issues a Discovery Notify control request to the Bus Owner, mctpd immediately acknowledges the request over the physical socket and defers EID assignment to the main systemd event loop.

This avoids blocking the event thread during control message processing and safely handles EID re-assignments via change_peer_eid(), keeping D-Bus object paths and netlink kernel routing tables synchronized. Also include unit test coverage for Discovery Notify in the test suite.

Assisted-by: Antigravity:Gemini-Next

@chajasmine-bit
chajasmine-bit force-pushed the discovery-notify branch 4 times, most recently from 4bee8eb to 9274ea2 Compare August 3, 2026 05:23
In MCTP networks, endpoints broadcast or send a Discovery Notify control
request (0x0D) to inform the Bus Owner when they boot up, reset, or are
hot-plugged. Currently, mctpd relies on active bus scanning or static
configurations, leaving newly online endpoints undiscovered until the next
poll cycle.

This change adds support for handling Discovery Notify requests when mctpd
is in the Bus Owner role:
 - Immediately acknowledges Discovery Notify requests over the physical
   socket with MCTP_CTRL_CC_SUCCESS inside handle_control_discovery_notify()
   to prevent hardware endpoints (e.g. PCIe VDMs / SMBus sequencers) from
   dropping subsequent Set Endpoint ID requests while waiting for an ACK.
 - Asynchronously defers EID assignment and route programming (RTM_NEWROUTE)
   to the systemd event loop (sd_event_add_defer) to prevent thread blocking.
 - Safely handles event deferral failures by dropping the request and
   returning -ENOMEM rather than executing synchronously in receive context,
   eliminating re-entrance and event loop corruption risks.
 - Scopes request deduplication to (ifindex, dest_phys) composite keys to
   prevent cross-bus address collisions on multi-bus platforms.
 - Enforces per-interface rate-limiting (maximum 5 discovery requests per
   second) to protect against discovery request floods.
 - Correctly supports Bridge Endpoints by reserving EID pools and
   invoking endpoint_allocate_eids() for downstream pool allocation and
   gateway routing.
 - Includes comprehensive unit test coverage in test_mctpd.py and
   test_mctpd_endpoint.py.

Assisted-by: Antigravity:Gemini-Next
Signed-off-by: Jasmine Cha <chajasmine@google.com>
@jk-ozlabs

Copy link
Copy Markdown
Member

I see there's been a few updates; let me know when this is stable and you'd like a review.

@chajasmine-bit

Copy link
Copy Markdown
Author

I see there's been a few updates; let me know when this is stable and you'd like a review.

The code is ready for review, please take a look at this. Thanks.

@jk-ozlabs jk-ozlabs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution. I have a few comments.

On the commit message: it reads like a marketing pitch rather than an explanation of the design, or rationale for the implementation. While you don't need to describe the individual changes, I would appreciate some background on the approach, and non-obvious parts of the implementation (like, why the new NLM_F flags?)

In MCTP networks, endpoints broadcast or send a Discovery Notify control
request (0x0D) to inform the Bus Owner when they boot up, reset, or are
hot-plugged. Currently, mctpd relies on active bus scanning or static
configurations,

or primarily: hot-plug events, where the transport provides them

leaving newly online endpoints undiscovered until the next poll cycle.

What poll cycle?

Comment thread src/mctp-netlink.c
return -1;
}
msg.nh.nlmsg_type = RTM_NEWROUTE;
msg.nh.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't see why we need different flags for the route creation for the discovery notify path. If this is needed, could you explain this in the commit message, or in a comment here?

Comment thread src/mctpd.c
struct {
sd_event_source **sources;
} bridge_ep_poll;
uint32_t flags;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why a new flag set for just one flag?

Comment thread src/mctpd.c
Comment on lines +1356 to +1361
/*
* Rate limiting is evaluated per-link (interface) rather than per-physical-MAC address
* for performance and simplicity. On shared multi-drop buses (e.g. I2C/SMBus), a noisy
* endpoint could temporarily consume the 5 req/sec quota; healthy adjacent endpoints will
* naturally retransmit Discovery Notify upon timeout.
*/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is more of an argument against a per-link rate-limit. Can you detail why per-link instead of per-endpoint? If it's just for simplicity of the implementation, then say so.

But is it more difficult to track per physaddr anyway?

Comment thread src/mctpd.c

if (link_data->discovery_count >= 5) {
return false;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, but please try to be consistent with your coding style (comparing to the if block on line 1367)

I have a slight preference for no-braces on single-statement blocks.

Comment thread src/mctpd.c
bool assign_bridge;
int rc;

(void)s; /* Floating event source unref is managed automatically by sd_event */

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems unnecessary?

Comment thread src/mctpd.c
Comment on lines +1430 to +1431
rc = endpoint_assign_eid(dctx->ctx, NULL, &dctx->phys, &peer,
target_eid, assign_bridge);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this sufficient? Are we sure that we should be re-assigning the old EID here?

As above: if we receive a Discovery Notify for a pre-existing peer, it seems that we have lost state synchronisation with that peer, which may be more like a recovery operation rather than a simple EID assignment. Your change doesn't really explain what we intend to do in that case.

Comment thread src/mctpd.c
dest_phys_tostr(&dctx->phys), strerror(-rc));
}

/* Keep pending context linked until execution completes to prevent deduplication races */

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wording of comment doesn't make sense here. Perhaps something like:

/* We have kept the context linked until this point, to prevent deduplication races, but
 * we are safe to unlink now.
 */

Comment thread src/mctpd.c
dctx->next = ctx->pending_discoveries;
ctx->pending_discoveries = dctx;

if (sd_event_add_defer(ctx->event, NULL, deferred_assign_eid_cb, dctx) <

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your implementation keeps a list of deferred discovery operations, but then schedules a new callback event for each. Would it make more sense just to have one callback queued?

Comment thread src/mctpd.c
if (!allow_bridged && is_eid_in_bridge_pool(n, ctx, eid))
return -EEXIST;

/* Re-use existing physical peer mapping safely via change_peer_eid */

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this related to the discovery implementation?

Comment thread tests/test_mctpd.py
assert rsp.hex(' ') == '01 0d 00'

# Allow deferred EID assignment event to execute
await trio.sleep(0.1)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a source of future unreliability, and will be difficult to debug. Is there a better way to synchronise rather than relying on delays?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants