establishment: the mechanics — reply capabilities, helloExchange, lineage routing
P0 of docs/establishment-planes-plan.md; no behavior changes yet, nothing sets the new flag or sends a hello capability. - service.run gains a reply-capability out-slot (replyWithCapability), the registry idiom init already uses, lifted into the harness; null stays the untouched common path. Subscribers gains claimArrival() so a provider handler can keep a turn capability through the same flag the reserved subscribe uses. - Hello wire struct: the padding byte becomes wants_channel — old callers wire-compatibly say 0, and the manager nominates a reply capability ONLY when asked, because a capability sent to a caller that never reads one is a leaked slot in that caller's table. - driver.helloExchange: one handshake can hand a serving endpoint up and receive the device's provider channel down (usb-storage will need both at once). No channel in the reply is retryable, never a verdict. - The manager stores each instance's serving endpoint on its Driver entry, routes consumer hellos by lineage (child -> reporter -> endpoint), replaces on re-hello, and closes the stale handle on death - the 32-slot table is the bound that makes forgetting this boot-fatal.
This commit is contained in:
@@ -253,6 +253,29 @@ const lookup_pause_ms: u64 = 20;
|
||||
/// The device this driver was assigned is the packet's `Header.target` — the manager's
|
||||
/// object addressing, so `no_device` here is a driver that serves none.
|
||||
pub fn hello(role: Role, device_id: u64) ?ipc.Handle {
|
||||
const exchanged = helloExchange(role, device_id, null, false) orelse return null;
|
||||
return exchanged.manager;
|
||||
}
|
||||
|
||||
/// What one hello moved, besides the handshake itself: the manager's endpoint
|
||||
/// (every hello), and — when asked — the channel to the driver that provides
|
||||
/// this device, shared into our handle table by the reply.
|
||||
pub const Exchange = struct {
|
||||
manager: ipc.Handle,
|
||||
/// The provider's channel, when `want_channel` asked and the manager's
|
||||
/// lineage had one. Null with `want_channel` set means the provider is not
|
||||
/// there YET (its own hello has not landed, or it is mid-restart) — a
|
||||
/// retryable condition, never a verdict.
|
||||
channel: ?ipc.Handle,
|
||||
};
|
||||
|
||||
/// The full handshake (communication.md "Establishment: two planes, one
|
||||
/// namespace"): a provider hands `serving` — the endpoint its consumers will
|
||||
/// be routed to — up with the request; a consumer sets `want_channel` and
|
||||
/// receives its device's provider channel with the reply. One call can do
|
||||
/// both (usb-storage serves block and consumes usb-transfer). The kernel
|
||||
/// shares capabilities as refcounted copies, so `serving` stays ours too.
|
||||
pub fn helloExchange(role: Role, device_id: u64, serving: ?ipc.Handle, want_channel: bool) ?Exchange {
|
||||
var attempts: u32 = 0;
|
||||
const manager = while (attempts < lookup_attempts) : (attempts += 1) {
|
||||
if (channel.openEndpoint("device-manager")) |handle| break handle;
|
||||
@@ -266,24 +289,26 @@ pub fn hello(role: Role, device_id: u64) ?ipc.Handle {
|
||||
const framed = device_manager_protocol.Protocol.encodeRequest(
|
||||
.hello,
|
||||
device_id,
|
||||
.{ .role = @intFromEnum(role) },
|
||||
.{ .role = @intFromEnum(role), .wants_channel = @intFromBool(want_channel) },
|
||||
&.{},
|
||||
&packet,
|
||||
) orelse return null;
|
||||
|
||||
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
|
||||
const length = ipc.call(manager, framed, &reply) catch {
|
||||
const answered = ipc.callCap(manager, framed, &reply, serving) catch {
|
||||
std.log.info("hello call failed", .{});
|
||||
return null;
|
||||
};
|
||||
const status = envelope.statusOf(reply[0..length]) orelse {
|
||||
const status = envelope.statusOf(reply[0..answered.len]) orelse {
|
||||
if (answered.cap) |stray| _ = ipc.close(stray); // never keep what we cannot read
|
||||
std.log.info("hello answered nothing readable", .{});
|
||||
return null;
|
||||
};
|
||||
if (status.status != 0) {
|
||||
if (answered.cap) |stray| _ = ipc.close(stray);
|
||||
std.log.info("hello refused", .{});
|
||||
return null;
|
||||
}
|
||||
std.log.info("hello acknowledged", .{});
|
||||
return manager;
|
||||
return .{ .manager = manager, .channel = answered.cap };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user