Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 141 additions & 42 deletions src/mctpd.c
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,22 @@ static struct peer *find_peer_by_addr(struct ctx *ctx, mctp_eid_t eid,
return NULL;
}

static struct peer *find_bridge_for_peer(struct ctx *ctx,
const struct peer *peer)
{
for (size_t i = 0; i < ctx->num_peers; i++) {
struct peer *p = ctx->peers[i];
if (p->pool_size == 0)
continue;
if (p->net != peer->net)
continue;
if (peer->eid >= p->pool_start &&
peer->eid < (mctp_eid_t)(p->pool_start + p->pool_size))
return p;
}
return NULL;
}

static int find_local_eids_by_net(struct net *net, size_t *local_eid_cnt,
mctp_eid_t *ret_eids)
{
Expand Down Expand Up @@ -2948,7 +2964,7 @@ static int query_get_peer_uuid_by_phys(struct ctx *ctx, const dest_phys *dest,
return rc;
}

static int query_get_peer_uuid(struct peer *peer)
static int query_get_peer_uuid(struct peer *peer, uint8_t uuid[16])
{
struct mctp_ctrl_resp_get_uuid *resp = NULL;
struct mctp_ctrl_cmd_get_uuid req;
Expand Down Expand Up @@ -2979,10 +2995,7 @@ static int query_get_peer_uuid(struct peer *peer)
goto out;

resp = cmd.resp;
rc = peer_set_uuid(peer, resp->uuid);
if (rc < 0)
goto out;
rc = 0;
memcpy(uuid, resp->uuid, 16);

out:
mctp_ctrl_cmd_free(&cmd);
Expand Down Expand Up @@ -3452,10 +3465,19 @@ static int query_peer_properties(struct peer *peer)
}
}

rc = query_get_peer_uuid(peer);
if (rc < 0 && peer->ctx->verbose) {
errno = -rc;
warn("Error getting UUID for %s", peer_tostr(peer));
{
uint8_t uuid[16];
memset(uuid, 0, sizeof(uuid));
rc = query_get_peer_uuid(peer, uuid);
if (rc < 0) {
if (peer->ctx->verbose) {
errno = -rc;
warn("Error getting UUID for %s",
peer_tostr(peer));
}
} else {
rc = peer_set_uuid(peer, uuid);
}
}

// TODO: emit property changed? Though currently they are all const.
Expand Down Expand Up @@ -3747,8 +3769,13 @@ static int peer_endpoint_recover(sd_event_source *s, uint64_t usec,
* Test if we still have connectivity to the endpoint. If we do, we will get a
* response reporting the current EID. This is the test recommended by 8.17.6
* of DSP0236 v1.3.1.

* Determine whether this is a downstream (bridged) endpoint before
* probing, so we can check bridge's connectivity first.
*/
rc = query_get_endpoint_id(ctx, &peer->phys, &peer->recovery.eid,
struct peer *bridge = find_bridge_for_peer(ctx, peer);
rc = query_get_endpoint_id(ctx, bridge ? &bridge->phys : &peer->phys,
&peer->recovery.eid,
&peer->recovery.endpoint_type,
&peer->recovery.medium_spec, /*peer=*/NULL,
/*retry=*/false);
Expand All @@ -3757,26 +3784,53 @@ static int peer_endpoint_recover(sd_event_source *s, uint64_t usec,
}

/*
* If we've got a response there are two scenarios:
*
* 1. The device responds with the EID that we expect it to have
* 2. The device responds with an unexpected EID, e.g. 0
*
* For scenario 1 we're done as the device is responsive and has the expected
* address. For scenario 2, we may not yet consider the EID assignment as
* expired, so check the UUID for a match. If the UUID matches we reassign the
* expected EID to the device. If the UUID does not match we allocate a new
* EID for the exchanged device, given it is responsive.
*/
if (peer->recovery.eid != peer->eid) {
* Directly-connected endpoint: If we've got a response there
* are two scenarios:
*
* 1. The device responds with the EID that we expect it to have
* 2. The device responds with an unexpected EID, e.g. 0
*
* For scenario 1 we're done as the device is responsive and has
* the expected address. For scenario 2, we may not yet consider
* the EID assignment as expired, so check the UUID for a match.
* If the UUID matches we reassign the expected EID to the
* device. If the UUID does not match we allocate a new EID for
* the exchanged device, given it is responsive.
*
* Bridged endpoint: If bridge connectivity is intact and we get a response,
* from downstream device, we can confirm the EID assignment is still valid
* since EID for downstreams endpoints are managed by birdge itself.
* If UUID doesn't match, bridge has re-assigned the EID to the new device,
* we discover the new device while recovering previously assigned EID.
*/

if (bridge) {
static const uint8_t nil_uuid[16] = { 0 };
bool uuid_matches_peer = false;
bool uuid_matches_nil = false;
uint8_t uuid[16] = { 0 };
mctp_eid_t new_eid;

rc = query_get_peer_uuid_by_phys(ctx, &peer->phys, uuid);
if (!rc && peer->uuid) {
/*
* Re-probe via EID routing so recovery.eid reflects the
* downstream device's response, not the bridge's EID.
*/
rc = query_get_endpoint_id(ctx, &peer->phys,
&peer->recovery.eid,
&peer->recovery.endpoint_type,
&peer->recovery.medium_spec, peer,
/*retry=*/false);
if (rc < 0)
goto reschedule;

/*
* UUID via EID routing to identify the downstream device
* specifically, not the bridge.
*/
rc = query_get_peer_uuid(peer, uuid);
if (rc < 0)
goto reclaim;

if (peer->uuid) {
static_assert(sizeof(uuid) == sizeof(nil_uuid),
"Unsynchronized UUID sizes");
uuid_matches_peer =
Expand All @@ -3785,33 +3839,78 @@ static int peer_endpoint_recover(sd_event_source *s, uint64_t usec,
memcmp(uuid, nil_uuid, sizeof(uuid)) == 0;
}

if (rc || !uuid_matches_peer ||
if (!uuid_matches_peer ||
(uuid_matches_nil && !MCTPD_RECOVER_NIL_UUID)) {
/* It's not known to be the same device, allocate a new EID */
/*
* The bridge has re-assigned this EID to a new device.
* Remove the old peer and register the new one at the
* same EID.
*/
dest_phys phys = peer->phys;
mctp_eid_t eid = peer->eid;
uint32_t net = peer->net;
struct peer *new_peer;

assert(sd_event_source_get_enabled(
peer->recovery.source, NULL) == 0);
remove_peer(peer);
/*
* The representation of the old peer is now gone. Set up the new peer,
* after which we immediately return as there's no old peer state left to
* maintain.
*/
return endpoint_assign_eid(ctx, NULL, &phys, &peer, 0,
false);
rc = add_peer(ctx, &phys, eid, net, &new_peer,
/*allow_bridged=*/true);
if (rc < 0)
return rc;
return setup_added_peer(new_peer);
}

/* Confirmation of the same device, apply its already allocated EID */
rc = endpoint_send_set_endpoint_id(peer, &new_eid, NULL);
if (rc < 0) {
goto reschedule;
}
} else {
if (peer->recovery.eid != peer->eid) {
static const uint8_t nil_uuid[16] = { 0 };
bool uuid_matches_peer = false;
bool uuid_matches_nil = false;
uint8_t uuid[16] = { 0 };
mctp_eid_t new_eid;

rc = query_get_peer_uuid_by_phys(ctx, &peer->phys,
uuid);
if (!rc && peer->uuid) {
static_assert(sizeof(uuid) == sizeof(nil_uuid),
"Unsynchronized UUID sizes");
uuid_matches_peer = memcmp(uuid, peer->uuid,
sizeof(uuid)) == 0;
uuid_matches_nil = memcmp(uuid, nil_uuid,
sizeof(uuid)) == 0;
}

if (rc || !uuid_matches_peer ||
(uuid_matches_nil && !MCTPD_RECOVER_NIL_UUID)) {
/* It's not known to be the same device, allocate a new EID */
dest_phys phys = peer->phys;

if (new_eid != peer->eid) {
rc = change_peer_eid(peer, new_eid);
assert(sd_event_source_get_enabled(
peer->recovery.source, NULL) ==
0);
remove_peer(peer);
/*
* The representation of the old peer is now
* gone. Set up the new peer, after which we
* immediately return as there's no old peer
* state left to maintain.
*/
return endpoint_assign_eid(ctx, NULL, &phys,
&peer, 0, false);
}

/* Confirmation of the same device, apply its already allocated EID */
rc = endpoint_send_set_endpoint_id(peer, &new_eid,
NULL);
if (rc < 0) {
goto reclaim;
goto reschedule;
}

if (new_eid != peer->eid) {
rc = change_peer_eid(peer, new_eid);
if (rc < 0) {
goto reclaim;
}
}
}
}
Expand Down
Loading
Loading