Skip to content

Commit fa94d66

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. 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 eea40ad commit fa94d66

1 file changed

Lines changed: 252 additions & 1 deletion

File tree

src/mctpd.c

Lines changed: 252 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ struct role {
105105
const char *dbus_val;
106106
};
107107

108+
// Endpoint poll context for bridged endpoint polling
109+
struct ep_poll_ctx {
110+
struct peer *bridge;
111+
mctp_eid_t poll_eid;
112+
};
113+
108114
static const struct role roles[] = {
109115
[ENDPOINT_ROLE_UNKNOWN] = {
110116
.role = ENDPOINT_ROLE_UNKNOWN,
@@ -203,6 +209,10 @@ struct peer {
203209
// Pool size
204210
uint8_t pool_size;
205211
uint8_t pool_start;
212+
213+
struct {
214+
sd_event_source **sources;
215+
} bridge_ep_poll;
206216
};
207217

208218
struct msg_type_support {
@@ -288,6 +298,7 @@ static int add_peer_from_addr(struct ctx *ctx,
288298
const struct sockaddr_mctp_ext *addr,
289299
struct peer **ret_peer);
290300
static int remove_peer(struct peer *peer);
301+
static int remove_bridged_peers(struct peer *bridge);
291302
static int query_peer_properties(struct peer *peer);
292303
static int setup_added_peer(struct peer *peer);
293304
static void add_peer_route(struct peer *peer);
@@ -1983,6 +1994,55 @@ static int check_peer_struct(const struct peer *peer, const struct net *n)
19831994
return 0;
19841995
}
19851996

1997+
static int remove_bridged_peers(struct peer *bridge)
1998+
{
1999+
mctp_eid_t ep, pool_start, pool_end;
2000+
struct ep_poll_ctx *pctx = NULL;
2001+
struct peer *peer = NULL;
2002+
struct net *n = NULL;
2003+
int rc = 0;
2004+
2005+
sd_event_source **sources = bridge->bridge_ep_poll.sources;
2006+
pool_end = bridge->pool_start + bridge->pool_size - 1;
2007+
n = lookup_net(bridge->ctx, bridge->net);
2008+
pool_start = bridge->pool_start;
2009+
2010+
if (!sources)
2011+
return 0;
2012+
2013+
for (ep = pool_start; ep <= pool_end; ep++) {
2014+
// stop endpoint polling before removing peer
2015+
// else next trigger will create peer again.
2016+
int idx = ep - pool_start;
2017+
2018+
if (sources[idx]) {
2019+
pctx = sd_event_source_get_userdata(sources[idx]);
2020+
rc = sd_event_source_set_enabled(sources[idx],
2021+
SD_EVENT_OFF);
2022+
if (rc < 0) {
2023+
warnx("Failed to stop polling timer while removing peer %d: %s",
2024+
ep, strerror(-rc));
2025+
}
2026+
2027+
sd_event_source_unref(sources[idx]);
2028+
sources[idx] = NULL;
2029+
free(pctx);
2030+
}
2031+
peer = n->peers[ep];
2032+
if (!peer)
2033+
continue;
2034+
2035+
rc = remove_peer(peer);
2036+
if (rc < 0) {
2037+
warnx("Failed to remove peer %d from bridge eid %d pool [%d - %d]: %s",
2038+
ep, bridge->eid, pool_start, pool_end,
2039+
strerror(-rc));
2040+
}
2041+
}
2042+
2043+
return 0;
2044+
}
2045+
19862046
static int remove_peer(struct peer *peer)
19872047
{
19882048
struct ctx *ctx = peer->ctx;
@@ -2017,6 +2077,12 @@ static int remove_peer(struct peer *peer)
20172077
sd_event_source_unref(peer->recovery.source);
20182078
}
20192079

2080+
if (peer->pool_size) {
2081+
remove_bridged_peers(peer);
2082+
free(peer->bridge_ep_poll.sources);
2083+
peer->bridge_ep_poll.sources = NULL;
2084+
}
2085+
20202086
n->peers[peer->eid] = NULL;
20212087
free(peer->message_types);
20222088
free(peer->uuid);
@@ -2063,6 +2129,7 @@ static void free_peers(struct ctx *ctx)
20632129
free(peer->message_types);
20642130
free(peer->uuid);
20652131
free(peer->path);
2132+
free(peer->bridge_ep_poll.sources);
20662133
sd_bus_slot_unref(peer->slot_obmc_endpoint);
20672134
sd_bus_slot_unref(peer->slot_cc_endpoint);
20682135
sd_bus_slot_unref(peer->slot_bridge);
@@ -5263,6 +5330,187 @@ static int endpoint_send_allocate_endpoint_ids(
52635330
return rc;
52645331
}
52655332

5333+
/* DSP0236 section 8.17.6 Reclaiming EIDs from hot-plug devices
5334+
*
5335+
* The bus owner/bridge can detect a removed device or devices by
5336+
* validating the EIDs that are presently allocated to endpoints that
5337+
* are directly on the bus and identifying which EIDs are missing.
5338+
* It can do this by attempting to access each endpoint that the bridge
5339+
* has listed in its routing table as being a device that is directly on
5340+
* the particular bus. Attempting to access each endpoint can be accomplished
5341+
* by issuing the Get Endpoint ID command...
5342+
5343+
5344+
* since bridged endpoints are routed from bridge, direct query
5345+
* to eid should work if gateway routes are in place.
5346+
*/
5347+
5348+
static int peer_reschedule_poll(sd_event_source *source, uint64_t usec)
5349+
{
5350+
int rc = 0;
5351+
rc = mctp_ops.sd_event.source_set_time_relative(source, usec);
5352+
if (rc >= 0) {
5353+
rc = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
5354+
}
5355+
5356+
return 0;
5357+
}
5358+
5359+
static int peer_endpoint_poll(sd_event_source *s, uint64_t usec, void *userdata)
5360+
{
5361+
struct sockaddr_mctp_ext resp_addr = { 0 };
5362+
struct mctp_ctrl_resp_get_eid *resp = NULL;
5363+
struct sockaddr_mctp_ext req_addr = { 0 };
5364+
struct mctp_ctrl_cmd_get_eid req = { 0 };
5365+
mctp_eid_t pool_start, idx, ret_eid = 0;
5366+
struct ep_poll_ctx *pctx = userdata;
5367+
struct peer *bridge = pctx->bridge;
5368+
sd_event_source *source = NULL;
5369+
struct peer *peer = NULL;
5370+
uint8_t *buf = NULL;
5371+
size_t buf_size;
5372+
struct net *n;
5373+
uint8_t iid;
5374+
int rc = 0;
5375+
5376+
if (!bridge) {
5377+
free(pctx);
5378+
return 0;
5379+
}
5380+
5381+
pool_start = bridge->pool_start;
5382+
mctp_eid_t ep = pctx->poll_eid;
5383+
idx = ep - pool_start;
5384+
source = bridge->bridge_ep_poll.sources[idx];
5385+
5386+
/* Polling policy :
5387+
*
5388+
* Once bridge eid pool space is allocated and gateway
5389+
* routes for downstream endpoints are in place, busowner
5390+
* would initiate periodic GET_ENDPOINT_ID command at an
5391+
* interval of atleast 1/2 * TRECLAIM.
5392+
5393+
1. The downstream endpoint if present behind the bridge,
5394+
responds to send poll command, that endpoint path is
5395+
considered accessible.
5396+
The endpoint path would be published as reachable to d-bus and
5397+
polling will no longer continue.
5398+
5399+
2. If endpoint is not present or doesn't responds to send poll
5400+
commmand, then it has not been establed yet that endpoint
5401+
path from the bridge is accessible or not, thus continue
5402+
to poll.
5403+
*/
5404+
5405+
req_addr.smctp_base.smctp_type = MCTP_CTRL_HDR_MSG_TYPE;
5406+
req_addr.smctp_base.smctp_network = bridge->net;
5407+
req_addr.smctp_base.smctp_tag = MCTP_TAG_OWNER;
5408+
req_addr.smctp_base.smctp_family = AF_MCTP;
5409+
req_addr.smctp_base.smctp_addr.s_addr = ep;
5410+
iid = mctp_next_iid(bridge->ctx);
5411+
5412+
mctp_ctrl_msg_hdr_init_req(&req.ctrl_hdr, iid,
5413+
MCTP_CTRL_CMD_GET_ENDPOINT_ID);
5414+
5415+
rc = endpoint_query_addr(bridge->ctx, &req_addr, false, &req,
5416+
sizeof(req), &buf, &buf_size, &resp_addr);
5417+
if (rc < 0) {
5418+
if (rc == -ETIMEDOUT) {
5419+
free(buf);
5420+
peer_reschedule_poll(source,
5421+
bridge->ctx->endpoint_poll);
5422+
return 0;
5423+
}
5424+
goto exit;
5425+
}
5426+
5427+
resp = (void *)buf;
5428+
if (!resp) {
5429+
warnx("Invalid response buffer");
5430+
return -ENOMEM;
5431+
}
5432+
5433+
ret_eid = resp->eid;
5434+
if (ret_eid != ep) {
5435+
warnx("Unexpected eid %d abort polling for eid %d", ret_eid,
5436+
ep);
5437+
goto exit;
5438+
}
5439+
5440+
if (bridge->ctx->verbose) {
5441+
fprintf(stderr, "Endpoint %d is accessible\n", ep);
5442+
}
5443+
5444+
n = lookup_net(bridge->ctx, bridge->net);
5445+
peer = n->peers[ep];
5446+
if (!peer) {
5447+
rc = add_peer(bridge->ctx, &(bridge->phys), ep, bridge->net,
5448+
&peer, true);
5449+
if (rc < 0)
5450+
goto exit;
5451+
}
5452+
5453+
rc = setup_added_peer(peer);
5454+
if (rc < 0) {
5455+
free(buf);
5456+
peer_reschedule_poll(source, bridge->ctx->endpoint_poll);
5457+
return 0;
5458+
}
5459+
5460+
exit:
5461+
assert(sd_event_source_get_enabled(source, NULL) == 0);
5462+
sd_event_source_unref(source);
5463+
bridge->bridge_ep_poll.sources[idx] = NULL;
5464+
free(pctx);
5465+
free(buf);
5466+
return rc;
5467+
}
5468+
5469+
static int bridge_poll_start(struct peer *bridge)
5470+
{
5471+
mctp_eid_t pool_start = bridge->pool_start;
5472+
mctp_eid_t pool_size = bridge->pool_size;
5473+
sd_event_source **sources = NULL;
5474+
struct ctx *ctx;
5475+
int rc;
5476+
int i;
5477+
5478+
sources = calloc(pool_size, sizeof(*sources));
5479+
bridge->bridge_ep_poll.sources = sources;
5480+
ctx = bridge->ctx;
5481+
5482+
if (!sources) {
5483+
rc = -ENOMEM;
5484+
warn("Failed to setup periodic polling for bridge (eid %d)",
5485+
bridge->eid);
5486+
return rc;
5487+
}
5488+
5489+
for (i = 0; i < pool_size; i++) {
5490+
struct ep_poll_ctx *pctx = calloc(1, sizeof(*pctx));
5491+
if (!pctx) {
5492+
warnx("Failed to allocate memory, skip polling for eid %d",
5493+
pool_start + i);
5494+
continue;
5495+
}
5496+
5497+
pctx->bridge = bridge;
5498+
pctx->poll_eid = pool_start + i;
5499+
rc = mctp_ops.sd_event.add_time_relative(
5500+
ctx->event, &bridge->bridge_ep_poll.sources[i],
5501+
CLOCK_MONOTONIC, ctx->endpoint_poll, 0,
5502+
peer_endpoint_poll, pctx);
5503+
if (rc < 0) {
5504+
warnx("Failed to setup poll event source for eid %d",
5505+
(pool_start + i));
5506+
free(pctx);
5507+
continue;
5508+
}
5509+
}
5510+
5511+
return 0;
5512+
}
5513+
52665514
static int endpoint_allocate_eids(struct peer *peer)
52675515
{
52685516
uint8_t allocated_pool_size = 0;
@@ -5331,7 +5579,10 @@ static int endpoint_allocate_eids(struct peer *peer)
53315579
peer->pool_size);
53325580
}
53335581

5334-
// TODO: Polling logic for downstream EID
5582+
// Poll for downstream endpoint accessibility
5583+
if (peer->ctx->endpoint_poll) {
5584+
bridge_poll_start(peer);
5585+
}
53355586

53365587
return 0;
53375588
}

0 commit comments

Comments
 (0)