diff --git a/README.md b/README.md index 16f8e90..9ba17fa 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ configuring remote endpoints on that bus: au.com.codeconstruct.MCTP.Interface1 interface - - - .AssignEndpoint method ay yisb - .AssignEndpointStatic method ayy yisb - + .AssignEndpointPreferred method ayy yisb - .LearnEndpoint method ay yisb - .SetupEndpoint method ay yisb - diff --git a/docs/endpoint-recovery.md b/docs/endpoint-recovery.md index 078a255..d2a2a09 100644 --- a/docs/endpoint-recovery.md +++ b/docs/endpoint-recovery.md @@ -180,6 +180,8 @@ root@cc-nvme-mi:~# busctl introspect au.com.codeconstruct.MCTP1 /au/com/codecons NAME TYPE SIGNATURE RESULT/VALUE FLAGS au.com.codeconstruct.Interface1 interface - - - .AssignEndpoint method ay yisb - +.AssignEndpointStatic method ayy yisb - +.AssignEndpointPreferred method ayy yisb - .LearnEndpoint method ay yisb - .SetupEndpoint method ay yisb - org.freedesktop.DBus.Introspectable interface - - - diff --git a/docs/mctpd.md b/docs/mctpd.md index bab76b6..a210ccf 100644 --- a/docs/mctpd.md +++ b/docs/mctpd.md @@ -138,6 +138,7 @@ au.com.codeconstruct.MCTP.Interface1 interface - - - au.com.codeconstruct.MCTP.BusOwner1 interface - - - .AssignEndpoint method ay yisb - .AssignEndpointStatic method ayy yisb - +.AssignEndpointPreferred method ayy yisb - .LearnEndpoint method ay yisb - .SetupEndpoint method ay yisb - ``` @@ -203,6 +204,20 @@ This call will fail if the endpoint already has an EID, and that EID is different from `static-EID`, or if `static-EID` is already assigned to another endpoint. +#### `.AssignEndpointPreferred`: `ayy` → `yisb` + +Similar to AssignEndpointStatic, but takes the additional EID argument as a +preferred EID rather than a strict static assignment: + +``` +AssignEndpointPreferred +``` + +If `` is available, it will be assigned to the endpoint. If that +EID is already assigned to another endpoint, `mctpd` falls back to dynamic EID +allocation. If the endpoint is already known to `mctpd`, the existing endpoint +record is returned. + #### `.LearnEndpoint`: `ay` → `yisb` Like SetupEndpoint but will not assign EIDs, will only query endpoints for a diff --git a/src/mctpd.c b/src/mctpd.c index 9cd16f3..588137a 100644 --- a/src/mctpd.c +++ b/src/mctpd.c @@ -3126,6 +3126,84 @@ static int method_assign_endpoint_static(sd_bus_message *call, void *data, return rc; } +static int method_assign_endpoint_preferred(sd_bus_message *call, void *data, + sd_bus_error *berr) +{ + dest_phys desti, *dest = &desti; + const char *peer_path = NULL; + struct peer *peer = NULL; + struct link *link = data; + struct ctx *ctx = link->ctx; + uint8_t eid; + int rc; + + dest->ifindex = link->ifindex; + if (dest->ifindex <= 0) + return sd_bus_error_setf(berr, SD_BUS_ERROR_INVALID_ARGS, + "Unknown MCTP interface"); + + rc = message_read_hwaddr(call, dest); + if (rc < 0) + goto err; + + rc = sd_bus_message_read(call, "y", &eid); + if (rc < 0) + goto err; + + rc = validate_dest_phys(ctx, dest); + if (rc < 0) + return sd_bus_error_setf(berr, SD_BUS_ERROR_INVALID_ARGS, + "Bad physaddr"); + + peer = find_peer_by_phys(ctx, dest); + if (peer) { + // Return existing record, even if it differs from the preferred EID. + peer_path = path_from_peer(peer); + if (!peer_path) + goto err; + + return sd_bus_reply_method_return(call, "yisb", peer->eid, + peer->net, peer_path, 0); + } else { + uint32_t netid; + struct net *net; + + netid = mctp_nl_net_byindex(ctx->nl, dest->ifindex); + net = lookup_net(ctx, netid); + peer = find_peer_by_addr(ctx, eid, netid); + if (peer || (net && is_eid_in_bridge_pool(net, ctx, eid))) { + if (peer) { + warnx("Preferred EID %d already in use by %s, using dynamic EID for %s", + eid, peer_tostr(peer), + dest_phys_tostr(dest)); + } else { + warnx("Preferred EID %d is in a bridge pool, using dynamic EID for %s", + eid, dest_phys_tostr(dest)); + } + rc = endpoint_assign_eid(ctx, berr, dest, &peer, 0, + true); + } else { + rc = endpoint_assign_eid(ctx, berr, dest, &peer, eid, + false); + } + if (rc < 0) + goto err; + } + + if (peer->pool_size > 0) + endpoint_allocate_eids(peer); + + peer_path = path_from_peer(peer); + if (!peer_path) + goto err; + + return sd_bus_reply_method_return(call, "yisb", peer->eid, peer->net, + peer_path, 1); +err: + set_berr(ctx, rc, berr); + return rc; +} + static int method_learn_endpoint(sd_bus_message *call, void *data, sd_bus_error *berr) { @@ -4055,6 +4133,18 @@ static const sd_bus_vtable bus_link_owner_vtable[] = { method_assign_endpoint_static, 0), + SD_BUS_METHOD_WITH_NAMES("AssignEndpointPreferred", + "ayy", + SD_BUS_PARAM(physaddr) + SD_BUS_PARAM(eid), + "yisb", + SD_BUS_PARAM(eid) + SD_BUS_PARAM(net) + SD_BUS_PARAM(path) + SD_BUS_PARAM(new), + method_assign_endpoint_preferred, + 0), + SD_BUS_METHOD_WITH_NAMES("LearnEndpoint", "ay", SD_BUS_PARAM(physaddr), diff --git a/tests/test_mctpd.py b/tests/test_mctpd.py index 178d4c3..4344708 100644 --- a/tests/test_mctpd.py +++ b/tests/test_mctpd.py @@ -409,6 +409,47 @@ async def test_assign_endpoint_static_conflict(dbus, mctpd): assert str(ex.value) == "Address in use" +async def test_assign_endpoint_preferred_static(dbus, mctpd): + """Test that preferred assignment uses the requested EID when available""" + iface = mctpd.system.interfaces[0] + mctp = await mctpd_mctp_iface_obj(dbus, iface) + dev = mctpd.network.endpoints[0] + preferred_eid = 12 + + (eid, _, _, new) = await mctp.call_assign_endpoint_preferred( + dev.lladdr, preferred_eid + ) + + assert eid == preferred_eid + assert dev.eid == preferred_eid + assert new + + +async def test_assign_endpoint_preferred_conflict_falls_back(dbus, mctpd): + """Test that preferred assignment falls back to dynamic EID on conflict""" + iface = mctpd.system.interfaces[0] + mctp = await mctpd_mctp_iface_obj(dbus, iface) + dev1 = mctpd.network.endpoints[0] + dev2 = Endpoint(iface, bytes([0x1E])) + preferred_eid = 12 + + mctpd.network.add_endpoint(dev2) + + (eid1, _, _, new1) = await mctp.call_assign_endpoint_static( + dev1.lladdr, preferred_eid + ) + assert eid1 == preferred_eid + assert new1 + + (eid2, _, _, new2) = await mctp.call_assign_endpoint_preferred( + dev2.lladdr, preferred_eid + ) + + assert eid2 != preferred_eid + assert dev2.eid == eid2 + assert new2 + + async def test_assign_endpoint_static_varies(dbus, mctpd): """Test that we cannot re-assign a static EID to an endpoint that already has a different EID allocated