Skip to content

Commit 47a4791

Browse files
mctpd: Send Discovery Notify on Endpoint role set
This commit adds support for Discovery Notify messages, specified in DSP0236 section 12.14. In our implementation, a Discovery Notify message is sent when mctpd sets an interface role from Unknown to Endpoint. To avoid notify discovery messages getting lost, retry the messages for a few time with delays. Signed-off-by: Khang D Nguyen <[email protected]>
1 parent 4692189 commit 47a4791

File tree

2 files changed

+231
-14
lines changed

2 files changed

+231
-14
lines changed

src/mctpd.c

Lines changed: 136 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,6 @@ enum discovery_state {
126126
};
127127

128128
struct link {
129-
enum discovery_state discovered;
130129
bool published;
131130
int ifindex;
132131
enum endpoint_role role;
@@ -135,6 +134,14 @@ struct link {
135134
sd_bus_slot *slot_iface;
136135
sd_bus_slot *slot_busowner;
137136

137+
struct {
138+
enum discovery_state flag;
139+
sd_event_source *notify_source;
140+
dest_phys notify_dest;
141+
uint64_t notify_retry_delay;
142+
uint8_t notify_tries_left;
143+
} discovery;
144+
138145
struct ctx *ctx;
139146
};
140147

@@ -805,8 +812,8 @@ static int handle_control_set_endpoint_id(struct ctx *ctx, int sd,
805812
warnx("ERR: cannot add bus owner to object lists");
806813
}
807814

808-
if (link_data->discovered != DISCOVERY_UNSUPPORTED) {
809-
link_data->discovered = DISCOVERY_DISCOVERED;
815+
if (link_data->discovery.flag != DISCOVERY_UNSUPPORTED) {
816+
link_data->discovery.flag = DISCOVERY_DISCOVERED;
810817
}
811818
resp->status =
812819
SET_MCTP_EID_ASSIGNMENT_STATUS(MCTP_SET_EID_ACCEPTED) |
@@ -817,13 +824,13 @@ static int handle_control_set_endpoint_id(struct ctx *ctx, int sd,
817824
return reply_message(ctx, sd, resp, resp_len, addr);
818825

819826
case MCTP_SET_EID_DISCOVERED:
820-
if (link_data->discovered == DISCOVERY_UNSUPPORTED) {
827+
if (link_data->discovery.flag == DISCOVERY_UNSUPPORTED) {
821828
resp->completion_code = MCTP_CTRL_CC_ERROR_INVALID_DATA;
822829
resp_len = sizeof(struct mctp_ctrl_resp);
823830
return reply_message(ctx, sd, resp, resp_len, addr);
824831
}
825832

826-
link_data->discovered = DISCOVERY_DISCOVERED;
833+
link_data->discovery.flag = DISCOVERY_DISCOVERED;
827834
resp->status =
828835
SET_MCTP_EID_ASSIGNMENT_STATUS(MCTP_SET_EID_REJECTED) |
829836
SET_MCTP_EID_ALLOCATION_STATUS(MCTP_SET_EID_POOL_NONE);
@@ -1061,16 +1068,16 @@ static int handle_control_prepare_endpoint_discovery(
10611068
resp = (void *)resp;
10621069
mctp_ctrl_msg_hdr_init_resp(&resp->ctrl_hdr, *req);
10631070

1064-
if (link_data->discovered == DISCOVERY_UNSUPPORTED) {
1071+
if (link_data->discovery.flag == DISCOVERY_UNSUPPORTED) {
10651072
warnx("received prepare for discovery request to unsupported interface %d",
10661073
addr->smctp_ifindex);
10671074
resp->completion_code = MCTP_CTRL_CC_ERROR_UNSUPPORTED_CMD;
10681075
return reply_message_phys(ctx, sd, resp,
10691076
sizeof(struct mctp_ctrl_resp), addr);
10701077
}
10711078

1072-
if (link_data->discovered == DISCOVERY_DISCOVERED) {
1073-
link_data->discovered = DISCOVERY_UNDISCOVERED;
1079+
if (link_data->discovery.flag == DISCOVERY_DISCOVERED) {
1080+
link_data->discovery.flag = DISCOVERY_UNDISCOVERED;
10741081
warnx("clear discovered flag of interface %d",
10751082
addr->smctp_ifindex);
10761083
}
@@ -1105,13 +1112,13 @@ handle_control_endpoint_discovery(struct ctx *ctx, int sd,
11051112
return 0;
11061113
}
11071114

1108-
if (link_data->discovered == DISCOVERY_UNSUPPORTED) {
1115+
if (link_data->discovery.flag == DISCOVERY_UNSUPPORTED) {
11091116
resp->completion_code = MCTP_CTRL_CC_ERROR_INVALID_DATA;
11101117
return reply_message(ctx, sd, resp,
11111118
sizeof(struct mctp_ctrl_resp), addr);
11121119
}
11131120

1114-
if (link_data->discovered == DISCOVERY_DISCOVERED) {
1121+
if (link_data->discovery.flag == DISCOVERY_DISCOVERED) {
11151122
// if we are already discovered (i.e, assigned an EID), then no reply
11161123
return 0;
11171124
}
@@ -3659,6 +3666,88 @@ static int bus_link_get_prop(sd_bus *bus, const char *path,
36593666
return rc;
36603667
}
36613668

3669+
static int query_discovery_notify(struct link *link)
3670+
{
3671+
struct mctp_ctrl_cmd_discovery_notify req = { 0 };
3672+
struct mctp_ctrl_resp_discovery_notify *resp;
3673+
struct sockaddr_mctp_ext resp_addr;
3674+
size_t buf_size;
3675+
uint8_t *buf;
3676+
int rc;
3677+
3678+
mctp_ctrl_msg_hdr_init_req(&req.ctrl_hdr, mctp_next_iid(link->ctx),
3679+
MCTP_CTRL_CMD_DISCOVERY_NOTIFY);
3680+
3681+
rc = endpoint_query_phys(link->ctx, &link->discovery.notify_dest,
3682+
MCTP_CTRL_HDR_MSG_TYPE, &req, sizeof(req),
3683+
&buf, &buf_size, &resp_addr);
3684+
if (rc < 0)
3685+
goto free_buf;
3686+
3687+
if (buf_size != sizeof(*resp)) {
3688+
warnx("%s: wrong reply length %zu bytes. dest %s", __func__,
3689+
buf_size, dest_phys_tostr(&link->discovery.notify_dest));
3690+
rc = -ENOMSG;
3691+
goto free_buf;
3692+
}
3693+
3694+
resp = (void *)buf;
3695+
if (resp->completion_code != 0) {
3696+
warnx("Failure completion code 0x%02x from %s",
3697+
resp->completion_code,
3698+
dest_phys_tostr(&link->discovery.notify_dest));
3699+
rc = -ECONNREFUSED;
3700+
goto free_buf;
3701+
}
3702+
3703+
free_buf:
3704+
free(buf);
3705+
return rc;
3706+
}
3707+
3708+
static int link_discovery_notify_callback(sd_event_source *source,
3709+
uint64_t time, void *userdata)
3710+
{
3711+
struct link *link = userdata;
3712+
struct ctx *ctx = link->ctx;
3713+
int rc;
3714+
3715+
// sanity check
3716+
assert(link->discovery.notify_source == source);
3717+
3718+
// Discovery notify succeeded
3719+
if (link->discovery.flag == DISCOVERY_DISCOVERED)
3720+
goto disarm;
3721+
3722+
rc = query_discovery_notify(link);
3723+
if (rc < 0) {
3724+
if (ctx->verbose) {
3725+
warnx("failed to send discovery notify at retry %d: %s",
3726+
link->discovery.notify_tries_left, strerror(-rc));
3727+
}
3728+
}
3729+
3730+
link->discovery.notify_tries_left -= 1;
3731+
if (link->discovery.notify_tries_left == 0) {
3732+
warnx("failed to send discovery notify after all retries");
3733+
goto disarm;
3734+
}
3735+
3736+
rc = mctp_ops.sd_event.source_set_time_relative(
3737+
source, link->discovery.notify_retry_delay);
3738+
if (rc < 0) {
3739+
warnx("failed to rearm discovery notify timer");
3740+
goto disarm;
3741+
}
3742+
3743+
return 0;
3744+
3745+
disarm:
3746+
sd_event_source_disable_unref(source);
3747+
link->discovery.notify_source = NULL;
3748+
return 0;
3749+
}
3750+
36623751
static int bus_link_set_prop(sd_bus *bus, const char *path,
36633752
const char *interface, const char *property,
36643753
sd_bus_message *value, void *userdata,
@@ -4496,7 +4585,7 @@ static int add_interface(struct ctx *ctx, int ifindex)
44964585
if (!link)
44974586
return -ENOMEM;
44984587

4499-
link->discovered = DISCOVERY_UNSUPPORTED;
4588+
link->discovery.flag = DISCOVERY_UNSUPPORTED;
45004589
link->published = false;
45014590
link->ifindex = ifindex;
45024591
link->ctx = ctx;
@@ -4526,7 +4615,42 @@ static int add_interface(struct ctx *ctx, int ifindex)
45264615
}
45274616

45284617
if (phys_binding == MCTP_PHYS_BINDING_PCIE_VDM) {
4529-
link->discovered = DISCOVERY_UNDISCOVERED;
4618+
link->discovery.flag = DISCOVERY_UNDISCOVERED;
4619+
// TODO: These numbers are respectively MN1 and MT4, specified in DSP0239
4620+
// control message timing.
4621+
//
4622+
// Might need to extract these to macros like MCTP_I2C_TSYM_* in this file,
4623+
// or a commit to actually centralize those timing at one place, now that
4624+
// we have support for detecting link binding type.
4625+
link->discovery.notify_tries_left = 3;
4626+
link->discovery.notify_retry_delay = 5000000;
4627+
4628+
// For PCIe-VDM, we want an all zeroes address for Route-to-Root-Complex.
4629+
rc = mctp_nl_hwaddr_len_byindex(
4630+
ctx->nl, ifindex,
4631+
&link->discovery.notify_dest.hwaddr_len);
4632+
if (rc < 0) {
4633+
warnx("Can't find hwaddr_len by index %d", ifindex);
4634+
return -ENOENT;
4635+
}
4636+
4637+
memset(link->discovery.notify_dest.hwaddr, 0,
4638+
link->discovery.notify_dest.hwaddr_len);
4639+
link->discovery.notify_dest.ifindex = ifindex;
4640+
4641+
rc = mctp_ops.sd_event.add_time_relative(
4642+
ctx->event, &link->discovery.notify_source,
4643+
CLOCK_MONOTONIC, 0, 0, link_discovery_notify_callback,
4644+
link);
4645+
if (rc >= 0) {
4646+
rc = sd_event_source_set_enabled(
4647+
link->discovery.notify_source, SD_EVENT_ON);
4648+
}
4649+
if (rc < 0) {
4650+
warnx("Failed to arm discovery notify timer");
4651+
sd_event_source_disable_unref(
4652+
link->discovery.notify_source);
4653+
}
45304654
}
45314655

45324656
link->published = true;

tests/test_mctpd_endpoint.py

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,16 @@ async def iface():
2222

2323

2424
@pytest.fixture
25-
async def sysnet(iface):
25+
async def bo(iface):
26+
return Endpoint(iface, bytes([0x10]), eid=8)
27+
28+
29+
@pytest.fixture
30+
async def sysnet(iface, bo):
2631
system = System()
2732
await system.add_interface(iface)
2833
network = Network()
29-
network.add_endpoint(Endpoint(iface, bytes([0x10]), eid=8))
34+
network.add_endpoint(bo)
3035
return Sysnet(system, network)
3136

3237

@@ -113,12 +118,100 @@ class TestDiscovery:
113118
async def iface(self):
114119
return System.Interface("mctp0", 1, 1, bytes([0x1D]), 68, 254, True, PhysicalBinding.PCIE_VDM)
115120

121+
@pytest.fixture
122+
async def bo(self, iface):
123+
return TestDiscovery.BusOwnerEndpoint(iface, bytes([0x00]), eid=8)
124+
125+
126+
class BusOwnerEndpoint(Endpoint):
127+
def __init__(self, *args, **kwargs):
128+
super().__init__(*args, **kwargs)
129+
self.sem = trio.Semaphore(initial_value=0)
130+
131+
async def handle_mctp_control(self, sock, addr, data):
132+
print(addr, data)
133+
flags, opcode = data[0:2]
134+
if opcode != 0x0D:
135+
return await super().handle_mctp_control(sock, addr, data)
136+
dst_addr = MCTPSockAddr.for_ep_resp(self, addr, sock.addr_ext)
137+
await sock.send(dst_addr, bytes([flags & 0x1F, opcode, 0x00]))
138+
self.sem.release()
139+
140+
116141
""" Test simple Discovery sequence """
117142
async def test_simple_discovery_sequence(self, dbus, mctpd):
118143
bo = mctpd.network.endpoints[0]
119144

120145
assert len(mctpd.system.addresses) == 0
121146

147+
# BMC should send a Discovery Notify message
148+
with trio.move_on_after(5) as expected:
149+
await bo.sem.acquire()
150+
assert not expected.cancelled_caught
151+
152+
# no EID yet
153+
rsp = await bo.send_control(mctpd.network.mctp_socket, MCTPControlCommand(True, 0, 0x02))
154+
assert rsp.hex(' ') == '00 02 00 00 02 00'
155+
156+
# BMC response to Prepare for Discovery
157+
rsp = await bo.send_control(mctpd.network.mctp_socket, MCTPControlCommand(True, 0, 0x0B))
158+
assert rsp.hex(' ') == '00 0b 00'
159+
160+
# BMC response to Endpoint Discovery
161+
rsp = await bo.send_control(mctpd.network.mctp_socket, MCTPControlCommand(True, 0, 0x0C))
162+
assert rsp.hex(' ') == '00 0c 00'
163+
164+
# set EID = 42
165+
eid = 42
166+
rsp = await bo.send_control(mctpd.network.mctp_socket, MCTPControlCommand(True, 0, 0x01, bytes([0x00, eid])))
167+
assert rsp.hex(' ') == f'00 01 00 00 {eid:02x} 00'
168+
169+
# BMC should contains two object paths: bus owner and itself
170+
assert await mctpd_mctp_endpoint_control_obj(dbus, f"/au/com/codeconstruct/mctp1/networks/1/endpoints/{bo.eid}")
171+
assert await mctpd_mctp_endpoint_control_obj(dbus, f"/au/com/codeconstruct/mctp1/networks/1/endpoints/{eid}")
172+
173+
174+
class TestDiscoveryRetry:
175+
@pytest.fixture
176+
async def iface(self):
177+
return System.Interface("mctp0", 1, 1, bytes([0x1D]), 68, 254, True, PhysicalBinding.PCIE_VDM)
178+
179+
@pytest.fixture
180+
async def bo(self, iface):
181+
return TestDiscoveryRetry.BusOwnerEndpoint(iface, bytes([0x00]), eid=8)
182+
183+
184+
class BusOwnerEndpoint(Endpoint):
185+
def __init__(self, *args, **kwargs):
186+
super().__init__(*args, **kwargs)
187+
self.sem = trio.Semaphore(initial_value=0)
188+
self.retry_left = 1
189+
190+
async def handle_mctp_control(self, sock, src_addr, msg):
191+
flags, opcode = msg[0:2]
192+
if opcode != 0x0D:
193+
return await super().handle_mctp_control(sock, src_addr, msg)
194+
195+
# only reply after 2 retries
196+
if self.retry_left == 0:
197+
dst_addr = MCTPSockAddr.for_ep_resp(self, src_addr, sock.addr_ext)
198+
await sock.send(dst_addr, bytes([flags & 0x1F, opcode, 0x00]))
199+
self.sem.release()
200+
else:
201+
self.retry_left -= 1
202+
203+
204+
""" Test simple Discovery sequence """
205+
async def test_discovery_after_one_retry(self, dbus, mctpd, autojump_clock):
206+
bo = mctpd.network.endpoints[0]
207+
208+
assert len(mctpd.system.addresses) == 0
209+
210+
# BMC should send a Discovery Notify message
211+
with trio.move_on_after(10) as expected:
212+
await bo.sem.acquire()
213+
assert not expected.cancelled_caught
214+
122215
# no EID yet
123216
rsp = await bo.send_control(mctpd.network.mctp_socket, MCTPControlCommand(True, 0, 0x02))
124217
assert rsp.hex(' ') == '00 02 00 00 02 00'

0 commit comments

Comments
 (0)