Skip to content

Commit 06bef87

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 b8d3227 commit 06bef87

1 file changed

Lines changed: 230 additions & 3 deletions

File tree

src/mctpd.c

Lines changed: 230 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,12 @@ struct role {
103103
const char *dbus_val;
104104
};
105105

106+
// Endpoint poll context for bridged endpoint polling
107+
struct ep_poll_ctx {
108+
struct peer *bridge;
109+
mctp_eid_t poll_eid;
110+
};
111+
106112
static const struct role roles[] = {
107113
[ENDPOINT_ROLE_UNKNOWN] = {
108114
.role = ENDPOINT_ROLE_UNKNOWN,
@@ -201,6 +207,10 @@ struct peer {
201207
// Pool size
202208
uint8_t pool_size;
203209
uint8_t pool_start;
210+
211+
struct {
212+
sd_event_source **sources;
213+
} bridge_ep_poll;
204214
};
205215

206216
struct msg_type_support {
@@ -286,6 +296,7 @@ static int add_peer_from_addr(struct ctx *ctx,
286296
const struct sockaddr_mctp_ext *addr,
287297
struct peer **ret_peer);
288298
static int remove_peer(struct peer *peer);
299+
static int remove_bridged_peers(struct peer *bridge);
289300
static int query_peer_properties(struct peer *peer);
290301
static int setup_added_peer(struct peer *peer);
291302
static void add_peer_route(struct peer *peer);
@@ -1965,6 +1976,55 @@ static int check_peer_struct(const struct peer *peer, const struct net *n)
19651976
return 0;
19661977
}
19671978

1979+
static int remove_bridged_peers(struct peer *bridge)
1980+
{
1981+
mctp_eid_t ep, pool_start, pool_end;
1982+
struct ep_poll_ctx *pctx = NULL;
1983+
struct peer *peer = NULL;
1984+
struct net *n = NULL;
1985+
int rc = 0;
1986+
1987+
sd_event_source **sources = bridge->bridge_ep_poll.sources;
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+
1992+
if (!sources)
1993+
return 0;
1994+
1995+
for (ep = pool_start; ep <= pool_end; ep++) {
1996+
// stop endpoint polling before removing peer
1997+
// else next trigger will create peer again.
1998+
int idx = ep - pool_start;
1999+
2000+
if (sources[idx]) {
2001+
pctx = sd_event_source_get_userdata(sources[idx]);
2002+
rc = sd_event_source_set_enabled(sources[idx],
2003+
SD_EVENT_OFF);
2004+
if (rc < 0) {
2005+
warnx("Failed to stop polling timer while removing peer %d: %s",
2006+
ep, strerror(-rc));
2007+
}
2008+
2009+
sd_event_source_unref(sources[idx]);
2010+
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+
19682028
static int remove_peer(struct peer *peer)
19692029
{
19702030
struct ctx *ctx = peer->ctx;
@@ -1999,6 +2059,12 @@ static int remove_peer(struct peer *peer)
19992059
sd_event_source_unref(peer->recovery.source);
20002060
}
20012061

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+
20022068
n->peers[peer->eid] = NULL;
20032069
free(peer->message_types);
20042070
free(peer->uuid);
@@ -2045,6 +2111,7 @@ static void free_peers(struct ctx *ctx)
20452111
free(peer->message_types);
20462112
free(peer->uuid);
20472113
free(peer->path);
2114+
free(peer->bridge_ep_poll.sources);
20482115
sd_bus_slot_unref(peer->slot_obmc_endpoint);
20492116
sd_bus_slot_unref(peer->slot_cc_endpoint);
20502117
sd_bus_slot_unref(peer->slot_bridge);
@@ -2374,8 +2441,10 @@ static int query_get_endpoint_id(struct ctx *ctx, const dest_phys *dest,
23742441
resp = (void *)buf;
23752442

23762443
*ret_eid = resp->eid;
2377-
*ret_ep_type = resp->eid_type;
2378-
*ret_media_spec = resp->medium_data;
2444+
if (ret_ep_type)
2445+
*ret_ep_type = resp->eid_type;
2446+
if (ret_media_spec)
2447+
*ret_media_spec = resp->medium_data;
23792448
out:
23802449
free(buf);
23812450
return rc;
@@ -5205,6 +5274,161 @@ static int endpoint_send_allocate_endpoint_ids(
52055274
return rc;
52065275
}
52075276

5277+
/* DSP0236 section 8.17.6 Reclaiming EIDs from hot-plug devices
5278+
*
5279+
* The bus owner/bridge can detect a removed device or devices by
5280+
* validating the EIDs that are presently allocated to endpoints that
5281+
* are directly on the bus and identifying which EIDs are missing.
5282+
* It can do this by attempting to access each endpoint that the bridge
5283+
* has listed in its routing table as being a device that is directly on
5284+
* the particular bus. Attempting to access each endpoint can be accomplished
5285+
* by issuing the Get Endpoint ID command...
5286+
5287+
5288+
* since bridged endpoints are routed from bridge, direct query
5289+
* to eid should work if gateway routes are in place.
5290+
*/
5291+
5292+
static int peer_reschedule_poll(sd_event_source *source, uint64_t usec)
5293+
{
5294+
int rc = 0;
5295+
rc = mctp_ops.sd_event.source_set_time_relative(source, usec);
5296+
if (rc >= 0) {
5297+
rc = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
5298+
}
5299+
5300+
return 0;
5301+
}
5302+
5303+
static int peer_endpoint_poll(sd_event_source *s, uint64_t usec, void *userdata)
5304+
{
5305+
struct ep_poll_ctx *pctx = userdata;
5306+
struct peer *bridge = pctx->bridge;
5307+
sd_event_source *source = NULL;
5308+
mctp_eid_t ep = pctx->poll_eid;
5309+
mctp_eid_t pool_start, idx;
5310+
struct peer *peer = NULL;
5311+
mctp_eid_t ret_eid = 0;
5312+
struct net *n;
5313+
int rc = 0;
5314+
5315+
if (!bridge) {
5316+
free(pctx);
5317+
return 0;
5318+
}
5319+
5320+
pool_start = bridge->pool_start;
5321+
idx = ep - pool_start;
5322+
source = bridge->bridge_ep_poll.sources[idx];
5323+
5324+
/* Polling policy :
5325+
*
5326+
* Once bridge eid pool space is allocated and gateway
5327+
* routes for downstream endpoints are in place, busowner
5328+
* would initiate periodic GET_ENDPOINT_ID command at an
5329+
* interval of atleast 1/2 * TRECLAIM.
5330+
5331+
1. The downstream endpoint if present behind the bridge,
5332+
responds to send poll command, that endpoint path is
5333+
considered accessible.
5334+
The endpoint path would be published as reachable to d-bus and
5335+
polling will no longer continue.
5336+
5337+
2. If endpoint is not present or doesn't responds to send poll
5338+
commmand, then it has not been establed yet that endpoint
5339+
path from the bridge is accessible or not, thus continue
5340+
to poll.
5341+
*/
5342+
5343+
n = lookup_net(bridge->ctx, bridge->net);
5344+
peer = n->peers[ep];
5345+
if (!peer) {
5346+
rc = add_peer(bridge->ctx, &(bridge->phys), ep, bridge->net,
5347+
&peer, true);
5348+
if (rc < 0)
5349+
goto exit;
5350+
}
5351+
5352+
rc = query_get_endpoint_id(bridge->ctx, &(bridge->phys), &ret_eid, NULL,
5353+
NULL, peer);
5354+
if (rc < 0) {
5355+
if (rc == -ETIMEDOUT) {
5356+
peer_reschedule_poll(source,
5357+
bridge->ctx->endpoint_poll);
5358+
return 0;
5359+
}
5360+
goto exit;
5361+
}
5362+
5363+
if (ret_eid != ep) {
5364+
warnx("Unexpected eid %d abort polling for eid %d", ret_eid,
5365+
ep);
5366+
goto exit;
5367+
}
5368+
5369+
if (bridge->ctx->verbose) {
5370+
fprintf(stderr, "Endpoint %d is accessible\n", ep);
5371+
}
5372+
5373+
rc = setup_added_peer(peer);
5374+
if (rc < 0) {
5375+
peer_reschedule_poll(source, bridge->ctx->endpoint_poll);
5376+
return 0;
5377+
}
5378+
5379+
exit:
5380+
assert(sd_event_source_get_enabled(source, NULL) == 0);
5381+
sd_event_source_unref(source);
5382+
bridge->bridge_ep_poll.sources[idx] = NULL;
5383+
free(pctx);
5384+
return rc;
5385+
}
5386+
5387+
static int bridge_poll_start(struct peer *bridge)
5388+
{
5389+
mctp_eid_t pool_start = bridge->pool_start;
5390+
mctp_eid_t pool_size = bridge->pool_size;
5391+
sd_event_source **sources = NULL;
5392+
struct ctx *ctx;
5393+
int rc;
5394+
int i;
5395+
5396+
sources = calloc(pool_size, sizeof(*sources));
5397+
bridge->bridge_ep_poll.sources = sources;
5398+
ctx = bridge->ctx;
5399+
5400+
if (!sources) {
5401+
rc = -ENOMEM;
5402+
warn("Failed to setup periodic polling for bridge (eid %d)",
5403+
bridge->eid);
5404+
return rc;
5405+
}
5406+
5407+
for (i = 0; i < pool_size; i++) {
5408+
struct ep_poll_ctx *pctx = calloc(1, sizeof(*pctx));
5409+
if (!pctx) {
5410+
warnx("Failed to allocate memory, skip polling for eid %d",
5411+
pool_start + i);
5412+
continue;
5413+
}
5414+
5415+
pctx->bridge = bridge;
5416+
pctx->poll_eid = pool_start + i;
5417+
rc = mctp_ops.sd_event.add_time_relative(
5418+
ctx->event, &bridge->bridge_ep_poll.sources[i],
5419+
CLOCK_MONOTONIC, ctx->endpoint_poll, 0,
5420+
peer_endpoint_poll, pctx);
5421+
if (rc < 0) {
5422+
warnx("Failed to setup poll event source for eid %d",
5423+
(pool_start + i));
5424+
free(pctx);
5425+
continue;
5426+
}
5427+
}
5428+
5429+
return 0;
5430+
}
5431+
52085432
static int endpoint_allocate_eids(struct peer *peer)
52095433
{
52105434
uint8_t allocated_pool_size = 0;
@@ -5273,7 +5497,10 @@ static int endpoint_allocate_eids(struct peer *peer)
52735497
peer->pool_size);
52745498
}
52755499

5276-
// TODO: Polling logic for downstream EID
5500+
// Poll for downstream endpoint accessibility
5501+
if (peer->ctx->endpoint_poll) {
5502+
bridge_poll_start(peer);
5503+
}
52775504

52785505
return 0;
52795506
}

0 commit comments

Comments
 (0)