Skip to content

Commit ab0abb2

Browse files
mctpd: add bridged endpoint polling mechanism
Implement endpoint periodic polling mechanism to validate bridged endpoint accessiblity. Begin polling as soon as gateway routes are created. Stop polling once it's established that endpoint path is accessible. Publish peer path once downstream endpoint responds to send poll command. Since downstream endpoints are behind the bridge, available query_get_endpoint_id() won't be able to redirect the GET_ENDPOINT_ID command packet behind the bridge physical address of downstream endpoint would be same as bridge's own. But we can do direct query to endpoint since routes have already been layed out when bridge was allocated the eid pool space. Remove all downstream endpoints as well if bridge is being removed. Also stop endpoint periodic polling. Signed-off-by: Faizan Ali <faizana@nvidia.com>
1 parent 7d84d9b commit ab0abb2

File tree

1 file changed

+252
-1
lines changed

1 file changed

+252
-1
lines changed

src/mctpd.c

Lines changed: 252 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ struct role {
101101
const char *dbus_val;
102102
};
103103

104+
// Endpoint poll context for bridged endpoint polling
105+
struct ep_poll_ctx {
106+
struct peer *bridge;
107+
mctp_eid_t poll_eid;
108+
};
109+
104110
static const struct role roles[] = {
105111
[ENDPOINT_ROLE_UNKNOWN] = {
106112
.role = ENDPOINT_ROLE_UNKNOWN,
@@ -199,6 +205,10 @@ struct peer {
199205
// Pool size
200206
uint8_t pool_size;
201207
uint8_t pool_start;
208+
209+
struct {
210+
sd_event_source **sources;
211+
} bridge_ep_poll;
202212
};
203213

204214
struct msg_type_support {
@@ -284,6 +294,7 @@ static int add_peer_from_addr(struct ctx *ctx,
284294
const struct sockaddr_mctp_ext *addr,
285295
struct peer **ret_peer);
286296
static int remove_peer(struct peer *peer);
297+
static int remove_bridged_peers(struct peer *bridge);
287298
static int query_peer_properties(struct peer *peer);
288299
static int setup_added_peer(struct peer *peer);
289300
static void add_peer_route(struct peer *peer);
@@ -1963,6 +1974,57 @@ static int check_peer_struct(const struct peer *peer, const struct net *n)
19631974
return 0;
19641975
}
19651976

1977+
/* Stops downstream endpoint polling and removes
1978+
* peer structure when bridge endpoint is being removed.
1979+
*/
1980+
static int remove_bridged_peers(struct peer *bridge)
1981+
{
1982+
mctp_eid_t ep, pool_start, pool_end;
1983+
struct ep_poll_ctx *pctx = NULL;
1984+
struct peer *peer = NULL;
1985+
struct net *n = NULL;
1986+
int rc = 0;
1987+
1988+
pool_end = bridge->pool_start + bridge->pool_size - 1;
1989+
n = lookup_net(bridge->ctx, bridge->net);
1990+
pool_start = bridge->pool_start;
1991+
for (ep = pool_start; ep <= pool_end; ep++) {
1992+
// stop endpoint polling before removing peer
1993+
// else next trigger will create peer again.
1994+
int idx = ep - pool_start;
1995+
1996+
if (bridge->bridge_ep_poll.sources &&
1997+
bridge->bridge_ep_poll.sources[idx]) {
1998+
pctx = sd_event_source_get_userdata(
1999+
bridge->bridge_ep_poll.sources[idx]);
2000+
rc = sd_event_source_set_enabled(
2001+
bridge->bridge_ep_poll.sources[idx],
2002+
SD_EVENT_OFF);
2003+
if (rc < 0) {
2004+
warnx("Failed to stop polling timer while removing peer %d: %s",
2005+
ep, strerror(-rc));
2006+
}
2007+
2008+
sd_event_source_unref(
2009+
bridge->bridge_ep_poll.sources[idx]);
2010+
bridge->bridge_ep_poll.sources[idx] = NULL;
2011+
free(pctx);
2012+
}
2013+
peer = n->peers[ep];
2014+
if (!peer)
2015+
continue;
2016+
2017+
rc = remove_peer(peer);
2018+
if (rc < 0) {
2019+
warnx("Failed to remove peer %d from bridge eid %d pool [%d - %d]: %s",
2020+
ep, bridge->eid, pool_start, pool_end,
2021+
strerror(-rc));
2022+
}
2023+
}
2024+
2025+
return 0;
2026+
}
2027+
19662028
static int remove_peer(struct peer *peer)
19672029
{
19682030
struct ctx *ctx = peer->ctx;
@@ -1997,6 +2059,12 @@ static int remove_peer(struct peer *peer)
19972059
sd_event_source_unref(peer->recovery.source);
19982060
}
19992061

2062+
if (peer->pool_size) {
2063+
remove_bridged_peers(peer);
2064+
free(peer->bridge_ep_poll.sources);
2065+
peer->bridge_ep_poll.sources = NULL;
2066+
}
2067+
20002068
n->peers[peer->eid] = NULL;
20012069
free(peer->message_types);
20022070
free(peer->uuid);
@@ -2043,6 +2111,7 @@ static void free_peers(struct ctx *ctx)
20432111
free(peer->message_types);
20442112
free(peer->uuid);
20452113
free(peer->path);
2114+
free(peer->bridge_ep_poll.sources);
20462115
sd_bus_slot_unref(peer->slot_obmc_endpoint);
20472116
sd_bus_slot_unref(peer->slot_cc_endpoint);
20482117
sd_bus_slot_unref(peer->slot_bridge);
@@ -2446,6 +2515,52 @@ static int get_endpoint_peer(struct ctx *ctx, sd_bus_error *berr,
24462515
return 0;
24472516
}
24482517

2518+
/* DSP0236 section 8.17.6 Reclaiming EIDs from hot-plug devices
2519+
*
2520+
* The bus owner/bridge can detect a removed device or devices by
2521+
* validating the EIDs that are presently allocated to endpoints that
2522+
* are directly on the bus and identifying which EIDs are missing.
2523+
* It can do this by attempting to access each endpoint that the bridge
2524+
* has listed in its routing table as being a device that is directly on
2525+
* the particular bus. Attempting to access each endpoint can be accomplished
2526+
* by issuing the Get Endpoint ID command...
2527+
2528+
2529+
* since bridged endpoints are routed from bridge, direct query
2530+
* to eid should work if gateway routes are in place.
2531+
*/
2532+
static int query_endpoint_poll_commmand(struct peer *peer, mctp_eid_t *resp_eid)
2533+
{
2534+
struct sockaddr_mctp_ext addr = { 0 };
2535+
struct mctp_ctrl_cmd_get_eid req = { 0 };
2536+
struct mctp_ctrl_resp_get_eid *resp = NULL;
2537+
2538+
uint8_t *buf = NULL;
2539+
size_t buf_size;
2540+
uint8_t iid;
2541+
int rc;
2542+
2543+
iid = mctp_next_iid(peer->ctx);
2544+
mctp_ctrl_msg_hdr_init_req(&req.ctrl_hdr, iid,
2545+
MCTP_CTRL_CMD_GET_ENDPOINT_ID);
2546+
rc = endpoint_query_peer(peer, MCTP_CTRL_HDR_MSG_TYPE, &req,
2547+
sizeof(req), &buf, &buf_size, &addr);
2548+
if (rc < 0)
2549+
goto out;
2550+
2551+
rc = mctp_ctrl_validate_response(buf, buf_size, sizeof(*resp),
2552+
peer_tostr_short(peer), iid,
2553+
MCTP_CTRL_CMD_GET_ENDPOINT_ID);
2554+
if (!rc) {
2555+
resp = (void *)buf;
2556+
*resp_eid = resp->eid;
2557+
}
2558+
2559+
out:
2560+
free(buf);
2561+
return rc;
2562+
}
2563+
24492564
static int query_get_peer_msgtypes(struct peer *peer)
24502565
{
24512566
struct sockaddr_mctp_ext addr;
@@ -5213,6 +5328,139 @@ static int endpoint_send_allocate_endpoint_ids(
52135328
return rc;
52145329
}
52155330

5331+
static int peer_endpoint_poll(sd_event_source *s, uint64_t usec, void *userdata)
5332+
{
5333+
struct ep_poll_ctx *pctx = userdata;
5334+
struct peer *bridge = pctx->bridge;
5335+
mctp_eid_t ep = pctx->poll_eid;
5336+
mctp_eid_t pool_start, idx;
5337+
struct peer *peer = NULL;
5338+
mctp_eid_t ret_eid = 0;
5339+
struct net *n;
5340+
int rc = 0;
5341+
5342+
if (!bridge) {
5343+
free(pctx);
5344+
return 0;
5345+
}
5346+
5347+
pool_start = bridge->pool_start;
5348+
idx = ep - pool_start;
5349+
5350+
/* Polling policy :
5351+
*
5352+
* Once bridge eid pool space is allocated and gateway
5353+
* routes for downstream endpoints are in place, busowner
5354+
* would initiate periodic GET_ENDPOINT_ID command at an
5355+
* interval of atleast 1/2 * TRECLAIM.
5356+
5357+
1. The downstream endpoint if present behind the bridge,
5358+
responds to send poll command, that endpoint path is
5359+
considered accessible.
5360+
The endpoint path would be published as reachable to d-bus and
5361+
polling will no longer continue.
5362+
5363+
2. If endpoint is not present or doesn't responds to send poll
5364+
commmand, then it has not been establed yet that endpoint
5365+
path from the bridge is accessible or not, thus continue
5366+
to poll.
5367+
*/
5368+
5369+
n = lookup_net(bridge->ctx, bridge->net);
5370+
peer = n->peers[ep];
5371+
if (!peer) {
5372+
rc = add_peer(bridge->ctx, &(bridge->phys), ep, bridge->net,
5373+
&peer, true);
5374+
if (rc < 0)
5375+
goto exit;
5376+
}
5377+
5378+
rc = query_endpoint_poll_commmand(peer, &ret_eid);
5379+
if (rc < 0) {
5380+
goto reschedule;
5381+
}
5382+
5383+
if (ret_eid != ep) {
5384+
warnx("Unexpected eid %d abort polling for eid %d", ret_eid,
5385+
ep);
5386+
goto exit;
5387+
}
5388+
5389+
if (bridge->ctx->verbose) {
5390+
fprintf(stderr, "Endpoint %d is accessible\n", ep);
5391+
}
5392+
5393+
rc = setup_added_peer(peer);
5394+
if (rc < 0)
5395+
goto reschedule;
5396+
5397+
exit:
5398+
if (bridge) {
5399+
assert(sd_event_source_get_enabled(
5400+
bridge->bridge_ep_poll.sources[idx], NULL) == 0);
5401+
sd_event_source_unref(bridge->bridge_ep_poll.sources[idx]);
5402+
bridge->bridge_ep_poll.sources[idx] = NULL;
5403+
}
5404+
free(pctx);
5405+
return rc < 0 ? rc : 0;
5406+
5407+
reschedule:
5408+
rc = mctp_ops.sd_event.source_set_time_relative(
5409+
bridge->bridge_ep_poll.sources[idx],
5410+
bridge->ctx->endpoint_poll);
5411+
if (rc >= 0) {
5412+
rc = sd_event_source_set_enabled(
5413+
bridge->bridge_ep_poll.sources[idx], SD_EVENT_ONESHOT);
5414+
}
5415+
return 0;
5416+
}
5417+
5418+
static int bridge_poll_start(struct peer *bridge)
5419+
{
5420+
mctp_eid_t pool_start = bridge->pool_start;
5421+
mctp_eid_t pool_size = bridge->pool_size;
5422+
sd_event_source **sources = NULL;
5423+
struct ctx *ctx;
5424+
int rc;
5425+
int i;
5426+
5427+
sources = calloc(pool_size, sizeof(sd_event_source *));
5428+
ctx = bridge->ctx;
5429+
5430+
if (!sources) {
5431+
rc = -ENOMEM;
5432+
warnx("Failed to setup periodic polling for bridge (eid %d)",
5433+
bridge->eid);
5434+
return rc;
5435+
}
5436+
5437+
bridge->bridge_ep_poll.sources = sources;
5438+
for (i = 0; i < pool_size; i++) {
5439+
struct ep_poll_ctx *pctx =
5440+
calloc(1, sizeof(struct ep_poll_ctx));
5441+
if (!pctx) {
5442+
warnx("Failed to memory, skip polling for eid %d",
5443+
pool_start + i);
5444+
continue;
5445+
}
5446+
5447+
pctx->bridge = bridge;
5448+
pctx->poll_eid = pool_start + i;
5449+
rc = mctp_ops.sd_event.add_time_relative(
5450+
ctx->event, &bridge->bridge_ep_poll.sources[i],
5451+
CLOCK_MONOTONIC, ctx->endpoint_poll, 0,
5452+
peer_endpoint_poll, pctx);
5453+
if (rc < 0) {
5454+
warnx("Failed to setup poll event source for eid %d",
5455+
(pool_start + i));
5456+
free(pctx);
5457+
continue;
5458+
}
5459+
}
5460+
5461+
return 0;
5462+
}
5463+
52165464
static int endpoint_allocate_eids(struct peer *peer)
52175465
{
52185466
uint8_t allocated_pool_size = 0;
@@ -5281,7 +5529,10 @@ static int endpoint_allocate_eids(struct peer *peer)
52815529
peer->pool_size);
52825530
}
52835531

5284-
// TODO: Polling logic for downstream EID
5532+
// Poll for downstream endpoint accessibility
5533+
if (peer->ctx->endpoint_poll) {
5534+
bridge_poll_start(peer);
5535+
}
52855536

52865537
return 0;
52875538
}

0 commit comments

Comments
 (0)