Add support for the hiffy network backend#616
Conversation
ef6dfe1 to
f69ae67
Compare
lzrd
left a comment
There was a problem hiding this comment.
Having a more helpful error message for archives where hiffy net feature is not configured is my only suggestion.
| if !core.is_net() { | ||
| let hiffy = if core.is_net() { | ||
| if let Some(hiffy_task) = hubris.lookup_task("hiffy") | ||
| && hubris.does_task_have_feature(hiffy_task, "net").unwrap() |
There was a problem hiding this comment.
Does/Can this error case give useful guidance to the user? e.g. "hiffy_task is not configured with net feature".
(Looking for other unwraps() that cold be more helpful in common failure cases...)
There was a problem hiding this comment.
That unwrap can't fail once we've found hiffy_task. I've rewritten this to be more clear.
| let buf_size = self | ||
| .hubris | ||
| .manifest | ||
| .get_socket_by_task("hiffy") |
There was a problem hiding this comment.
nit: (symbolic constants vs. literals) HIFFY_TASK_NAME? I know we're not going to change this and the function is get_socket_by_task(), but if there are enough "hiffy" names and symbols in different contexts it helps to know what kind of hiffy we are talking about.
There was a problem hiding this comment.
I'm ambivalent – HIFFY_TASK_NAME is marginally more clear, but replacing "hiffy" with HIFFY_TASK_NAME everywhere in this file just makes a bunch of lines slightly longer (forcing one large block to reflow to the next indent level).
labbott
left a comment
There was a problem hiding this comment.
I think this looks okay, I'm going to give it another pass in a bit
b524b06 to
51eae64
Compare
jamesmunns
left a comment
There was a problem hiding this comment.
Mostly pedantic notes below, I didn't spot anything disagreeable!
One "bigger picture" note here: A lot of the code is somewhat verbose because we're matching on different HiffyImpls, or similar cases. If this was more abstract, I'd say it'd be worth trait-ify-ing, but since we're deciding at runtime, that doesn't make sense unless we want to go down the dyn Trait route, which again doesn't make sense because all the impls live in this crate anyway.
HOWEVER, it might be worth informally defining an impl API, so we can have top level dispatch functions like:
pub fn text_size(&self) -> usize {
match &self.hiffy {
HiffyImpl::Debugger(dbg) => dbg.text_size(),
HiffyImpl::NetHiffy(net) => net.text_size(),
HiffyImpl::NetUdpRpc(..) => HIFFY_NET_TEXT_SIZE,
}
}then have:
impl HiffyDebuggerImpl {
fn text_size(&self) -> usize {
self.vars.text.size
}
}This way, we can more easily extract the "backend specific" behavior, and make the high level flow more clear, as well as make some of the helper "backend" functions a little easier to refactor.
If NetUdpRpc is the Last Ever™️ impl, it might not be worth it to refactor, but I think making the "backend contract" even informally defined probably has some documentation value.
| @@ -126,6 +126,7 @@ pub trait Core { | |||
| pub enum NetAgent { | |||
There was a problem hiding this comment.
Should we make this non_exhaustive to make additions like this not a breaking change (after this one)? Do we care about breaking changes?
There was a problem hiding this comment.
I don't think so – this is only ever used as an input, so it would be weird for outside the net core to match on it.
In general, we (Hubris / Humility) don't care much about breaking changes to quasi-internal Rust APIs, where no one is consuming them besides us and blast radius is a single repository (no one is using NetAgent outside of the humility repo). This could change as Humility gets library-ified, but even then I suspect we'll err on the side of breaking things freely.
| pub nbytes: U16<LittleEndian>, | ||
| } | ||
|
|
||
| /// Double of the RPC types from `hiffy` (with the `net` feature enabled) |
There was a problem hiding this comment.
Is it worth it to extract this into a shared crate somehow to avoid the risk of unsync'd changes?
There was a problem hiding this comment.
In theory, that would be the most robust option – but in practice, we've got a few of these internal message types and they're primitive enough to never change. There's also the fact that if they did change, we'd want to support both versions in Humility, because Humility wants to talk to every Hubris image.
| // Constants when running with the `NetUdpRpc` impl, which runs the program on | ||
| // the host computer. These values are much larger than anything we'd see on | ||
| // the embedded system, so we can run any program. | ||
| const HIFFY_NET_TEXT_SIZE: usize = 65536; |
There was a problem hiding this comment.
maybe note the "HOST" part in the name here somewhere to make it clear we're doing so/don't misuse these consts?
| HiffyImpl::Debugger(vars) | HiffyImpl::NetHiffy { vars, .. } => { | ||
| vars.data.size | ||
| } | ||
| HiffyImpl::NetUdpRpc { .. } => 0, // not supported |
There was a problem hiding this comment.
Maybe add a function doc comment to note that 0 is a sentinel for "not supported" (or return an Option if more invasive changes are palatable?)
There was a problem hiding this comment.
I'll add a doc comment; 0 is both a sentinel for "not support" and technically a valid response (you can access 0 bytes of HIFFY_DATA), so I don't want to return an Option here.
There was a problem hiding this comment.
I'm okay with that either way, but wouldn't that be BETTER to use a Option then? To disambiguate between "does not support" and "does support and you can access zero bytes"?
Not too fussed about it though :)
There was a problem hiding this comment.
I was going to argue that there's not a user-visible difference between "data not supported" and "supports data of size 0", but then I managed to panic humility auxflash write:
humility/cmd/auxflash/src/lib.rs
Line 330 in 1e40779
I've now switched data_size to return a Result<usize, DataNotSupported> (slowly sneaking thiserror-based types into Humility as I updated things).
| timeout: u32, | ||
| ) -> Result<HiffyContext<'a>> { | ||
| if !core.is_net() { | ||
| let hiffy = if core.is_net() { |
There was a problem hiding this comment.
nit: Maybe worth breaking out into a helper function to simplify the control flow/reduce rightward drift (mostly to allow early returns)?
| } | ||
|
|
||
| /// Abstraction for writing hiffy values over multiple transports | ||
| enum HiffyWrite<'a, 'b> { |
There was a problem hiding this comment.
Nit: It might be worth making each of the variants a specific struct, e.g. Debugger(DebuggerWrite), to be able to implement methods on each subtype. That would make the top-level dispatch functions a little smaller.
a3023ab to
1262796
Compare
|
@jamesmunns |
24b66e4 to
a6f62df
Compare
a6f62df to
372ab5e
Compare
f5c0550 to
0d0069d
Compare
This is the Humility side of oxidecomputer/hubris#2466
It adds a third backend to execute
hiffyprograms over the network; the three backends are nowtask-hiffyon the SPudprpcudprpcover the networkhiffy(new!)hiffyarrays are done over the network using a new sockettask-hiffyon the SPWe abstract over the debugger and network-attached
hiffywith a newenum HiffyWriteto write tohiffyarrays, instead of callingHubrisCorefunctions directly. Network-attachedudprpcremains a special case, because it can only handle a subset of HIF programs (ones that only useSend-flavored functions).