Skip to content

Commit f4be456

Browse files
authored
ereports: add declare_ereporter! macro (#2427)
Depends on #2399. While working on #2399, I noticed that there was an emerging boilerplate pattern for tasks which report ereports, where I had declared a struct that bundles together a buffer of a size calculated using `microcbor::max_cbor_len_for!` with the `Packrat` API client and some methods for reporting ereports. For example, in the Cosmo sequencer, I wrote this: https://github.com/oxidecomputer/hubris/blob/9e18d0bd9cb69f76534ab0539f9114aeb398ac8c/drv/cosmo-seq-server/src/main.rs#L1237-L1263 And then an identical copy of the same code in the Gimlet sequencer: https://github.com/oxidecomputer/hubris/blob/9e18d0bd9cb69f76534ab0539f9114aeb398ac8c/drv/gimlet-seq-server/src/main.rs#L1620-L1647 I started to wonder whether it would be possible to factor out this boilerplate. While we could easily just have a ```rust pub struct Ereporter<const BUF_LEN: usize> { // ... } ``` and some methods in the `ereports` crate, and let the user provide the buffer size from a separate invocation of the `microcbor` macro, it occurred to me that there was a way to ... feed ... two birds with one...loaf of bread?[^1] and also solve some of the long running issues with the present ereport-recording APIs. In particular, a long-standing thorn in my side is the fact that, while we presently have a way to _calculate_ the required buffer length for a list of ereports, we **don't** currently have anything that actually **stops** you from attempting to to actually use that buffer to encode an ereport that **wasn't** involved in the length calculation. I had hoped that there would be a generic way to do this using const generics, but unfortunately, Rust's const generics remain only sort of half finished, as I discussed in detail in #2246 (comment). So, this means that it's possible to add a new ereport type, forget to add it to the list of ereports used for the length calculation, and end up with an ereport that never fits in the buffer without really noticing. So that's not great. Another less important but similarly bothersome thing is that I have generally tried to add ringbuf entries to record when an ereport is submitted, and more importantly, when it _isn't_, either because the ereport was too big for the encoding buffer due to issues like the one I described above, or due to the `packrat` ereport aggregation buffer being full. Ideally, these ringbuf entries/counters would also record _which_ ereport class was or was not reported, but doing this requires even *more* boilerplate which must be specific to the individual task that records ereports, as the list of ereports depends on the task. So, I've come up with a way to feed all of the aforementioned birds, by introducing a new `declare_ereporter!` macro in the `ereports` crate. This macro is invoked with the name of a struct, the name of a _trait_, and a list of ereports, and generates an ereporter struct, a trait implemented by all of the ereports in the provided list, and a method on the ereporter struct to record an ereport *where the ereport type implements the generated trait*. This way, we can have a way to ensure that only ereports whose lengths were used in the encoding buffer size calculation are reported using that buffer. The macro also generates ringbuf entries/counters for each ereport that's recorded using the generated ereporter type. For more details on how the new thing is used, see [the RustDoc I added for the macro][1]. [1]: https://github.com/oxidecomputer/hubris/blob/d4444b38458ae4d6fb72df7cec8fb38519b7f040/lib/ereports/src/lib.rs#L14-L104 [^1]: Attempting to use non-violent language here... :)
1 parent dd6beb1 commit f4be456

14 files changed

Lines changed: 478 additions & 330 deletions

File tree

Cargo.lock

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

drv/cosmo-seq-server/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ drv-packrat-vpd-loader = { path = "../packrat-vpd-loader" }
1616
drv-spartan7-loader-api = { path = "../spartan7-loader-api" }
1717
drv-spi-api = { path = "../spi-api" }
1818
drv-stm32xx-sys-api = { path = "../stm32xx-sys-api" }
19-
ereports = { path = "../../lib/ereports" }
19+
ereports = { path = "../../lib/ereports", features = ["ereporter-macro"] }
2020
gnarle = { path = "../../lib/gnarle" }
2121
ringbuf = { path = "../../lib/ringbuf" }
2222
userlib = { path = "../../sys/userlib", features = ["panic-messages"] }

drv/cosmo-seq-server/src/main.rs

Lines changed: 18 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,6 @@ enum Trace {
118118
},
119119
UnexpectedInterrupt,
120120
CPUNotPresent,
121-
EreportSent(usize),
122-
EreportLost(usize, task_packrat_api::EreportWriteError),
123-
EreportTooBig,
124121
}
125122
counted_ringbuf!(Trace, 128, Trace::None);
126123

@@ -183,17 +180,7 @@ fn main() -> ! {
183180
let packrat = Packrat::from(PACKRAT.get_task_id());
184181
read_vpd_and_load_packrat(&packrat, I2C.get_task_id());
185182

186-
let ereport_buf = {
187-
use static_cell::ClaimOnceCell;
188-
static EREPORT_BUF: ClaimOnceCell<[u8; EREPORT_BUF_LEN]> =
189-
ClaimOnceCell::new([0; EREPORT_BUF_LEN]);
190-
EREPORT_BUF.claim()
191-
};
192-
193-
let mut ereporter = Ereporter {
194-
packrat,
195-
buf: ereport_buf,
196-
};
183+
let mut ereporter = Ereporter::claim_static_resources(packrat);
197184

198185
//
199186
// Apply the configuration mitigation on the BMR491, if required. This
@@ -227,7 +214,7 @@ fn main() -> ! {
227214
last_cause,
228215
succeeded,
229216
};
230-
ereporter.try_send_ereport(&ereport);
217+
ereporter.deliver_ereport(&ereport);
231218
}
232219
}
233220

@@ -568,7 +555,7 @@ impl ServerImpl {
568555

569556
if !present {
570557
ringbuf_entry!(Trace::CPUNotPresent);
571-
self.ereporter.try_send_ereport(
558+
self.ereporter.deliver_ereport(
572559
&ereports::cpu::CpuMissing {
573560
cpu: &HOST_CPU_REFDES,
574561
},
@@ -637,7 +624,7 @@ impl ServerImpl {
637624
ok: sp5rx_ok,
638625
},
639626
};
640-
self.ereporter.try_send_ereport(&ereport);
627+
self.ereporter.deliver_ereport(&ereport);
641628
return Err(CpuSeqError::UnrecognizedCPU);
642629
}
643630

@@ -936,7 +923,7 @@ impl ServerImpl {
936923
self.seq.ifr.modify(|h| h.set_thermtrip(true));
937924
ringbuf_entry!(Trace::Thermtrip);
938925
action = InternalAction::ThermTrip;
939-
self.ereporter.try_send_ereport(&ereports::cpu::Thermtrip {
926+
self.ereporter.deliver_ereport(&ereports::cpu::Thermtrip {
940927
cpu: &HOST_CPU_REFDES,
941928
state: self.ereport_current_state(),
942929
});
@@ -954,7 +941,7 @@ impl ServerImpl {
954941
self.seq.ifr.modify(|h| h.set_smerr_assert(true));
955942
ringbuf_entry!(Trace::SmerrInterrupt);
956943
action = InternalAction::Smerr;
957-
self.ereporter.try_send_ereport(&ereports::cpu::Smerr {
944+
self.ereporter.deliver_ereport(&ereports::cpu::Smerr {
958945
cpu: &HOST_CPU_REFDES,
959946
state: self.ereport_current_state(),
960947
});
@@ -1258,49 +1245,25 @@ impl NotificationHandler for ServerImpl {
12581245

12591246
////////////////////////////////////////////////////////////////////////////////
12601247

1261-
const EREPORT_BUF_LEN: usize = microcbor::max_cbor_len_for![
1262-
ereports::pwr::PmbusAlert<vcore::Rail, { REFDES_LEN }>,
1263-
ereports::pwr::Bmr491MitigationFailure<{ REFDES_LEN }>,
1264-
ereports::cpu::Thermtrip,
1265-
ereports::cpu::Smerr,
1266-
ereports::cpu::UnsupportedCpu<3, 4>,
1267-
ereports::cpu::CpuMissing,
1268-
];
1248+
ereports::declare_ereporter! {
1249+
pub(crate) struct Ereporter<SeqEreport> {
1250+
PmbusAlert(ereports::pwr::PmbusAlert<vcore::Rail, { REFDES_LEN }>),
1251+
Bmr491MitigationFailure(
1252+
ereports::pwr::Bmr491MitigationFailure<{ REFDES_LEN }>
1253+
),
1254+
Thermtrip(ereports::cpu::Thermtrip),
1255+
Smerr(ereports::cpu::Smerr),
1256+
UnsupportedCpu(ereports::cpu::UnsupportedCpu<3, 4>),
1257+
CpuMissing(ereports::cpu::CpuMissing),
1258+
}
1259+
}
12691260

12701261
static HOST_CPU_REFDES: ereports::cpu::HostCpuRefdes =
12711262
ereports::cpu::HostCpuRefdes {
12721263
refdes: fixedstr::FixedString::from_str("P0"),
12731264
dev_id: fixedstr::FixedString::from_str("sp5-host-cpu"),
12741265
};
12751266

1276-
/// This is just the Packrat API handle and the ereport buffer bundled together
1277-
/// in one thing so that it can be passed into various places as a single
1278-
/// argument.
1279-
pub(crate) struct Ereporter {
1280-
packrat: task_packrat_api::Packrat,
1281-
buf: &'static mut [u8; EREPORT_BUF_LEN],
1282-
}
1283-
1284-
impl Ereporter {
1285-
pub(crate) fn try_send_ereport(
1286-
&mut self,
1287-
ereport: &impl microcbor::StaticCborLen,
1288-
) {
1289-
let eresult = self
1290-
.packrat
1291-
.deliver_microcbor_ereport(&ereport, &mut self.buf[..]);
1292-
match eresult {
1293-
Ok(len) => ringbuf_entry!(Trace::EreportSent(len)),
1294-
Err(task_packrat_api::EreportEncodeError::Packrat { len, err }) => {
1295-
ringbuf_entry!(Trace::EreportLost(len, err))
1296-
}
1297-
Err(task_packrat_api::EreportEncodeError::Encoder(_)) => {
1298-
ringbuf_entry!(Trace::EreportTooBig)
1299-
}
1300-
}
1301-
}
1302-
}
1303-
13041267
////////////////////////////////////////////////////////////////////////////////
13051268

13061269
mod idl {

drv/cosmo-seq-server/src/vcore.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ impl VCore {
477477
pmbus_status,
478478
pwr_good: power_good,
479479
};
480-
ereporter.try_send_ereport(&ereport);
480+
ereporter.deliver_ereport(&ereport);
481481
// TODO(eliza): if POWER_GOOD has been deasserted, we should produce a
482482
// subsequent ereport for that.
483483

drv/gimlet-seq-server/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ drv-spi-api = { path = "../spi-api" }
1515
drv-stm32h7-spi = { path = "../stm32h7-spi" }
1616
drv-stm32xx-sys-api = { path = "../stm32xx-sys-api" }
1717
counters = { path = "../../lib/counters" }
18-
ereports = { path = "../../lib/ereports" }
18+
ereports = { path = "../../lib/ereports", features = ["ereporter-macro"] }
1919
gnarle = { path = "../../lib/gnarle" }
2020
ringbuf = { path = "../../lib/ringbuf" }
2121
task-jefe-api = { path = "../../task/jefe-api" }

drv/gimlet-seq-server/src/main.rs

Lines changed: 21 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ use idol_runtime::{NotificationHandler, RequestError};
3131
use seq_spi::{Addr, Reg};
3232
use static_assertions::const_assert;
3333
use task_jefe_api::Jefe;
34-
use task_packrat_api as packrat_api;
3534

3635
task_slot!(SYS, sys);
3736
task_slot!(SPI, spi_driver);
@@ -158,9 +157,6 @@ enum Trace {
158157
retries_remaining: u8,
159158
},
160159
StartFailed(#[count(children)] SeqError),
161-
EreportSent(usize),
162-
EreportLost(usize, packrat_api::EreportWriteError),
163-
EreportTooBig,
164160
}
165161

166162
counted_ringbuf!(Trace, 128, Trace::None);
@@ -452,15 +448,7 @@ impl<S: SpiServer + Clone> ServerImpl<S> {
452448
}
453449

454450
let packrat = Packrat::from(PACKRAT.get_task_id());
455-
let mut ereporter = {
456-
use static_cell::ClaimOnceCell;
457-
static EREPORT_BUF: ClaimOnceCell<[u8; EREPORT_BUF_LEN]> =
458-
ClaimOnceCell::new([0; EREPORT_BUF_LEN]);
459-
Ereporter {
460-
buf: EREPORT_BUF.claim(),
461-
packrat: packrat.clone(),
462-
}
463-
};
451+
let mut ereporter = Ereporter::claim_static_resources(packrat.clone());
464452

465453
//
466454
// Apply the configuration mitigation on the BMR491, if required. This
@@ -495,7 +483,7 @@ impl<S: SpiServer + Clone> ServerImpl<S> {
495483
last_cause,
496484
succeeded,
497485
};
498-
ereporter.try_send_ereport(&ereport);
486+
ereporter.deliver_ereport(&ereport);
499487
}
500488
}
501489

@@ -859,7 +847,7 @@ impl<S: SpiServer> ServerImpl<S> {
859847
ringbuf_entry!(Trace::CPUPresent(present));
860848

861849
if !present {
862-
self.ereporter.try_send_ereport(
850+
self.ereporter.deliver_ereport(
863851
&ereports::cpu::CpuMissing {
864852
cpu: &HOST_CPU_REFDES,
865853
},
@@ -885,7 +873,7 @@ impl<S: SpiServer> ServerImpl<S> {
885873
//
886874
let rev_ok = sp3r1 && !sp3r2;
887875
if !coretype || !rev_ok {
888-
self.ereporter.try_send_ereport(
876+
self.ereporter.deliver_ereport(
889877
&ereports::cpu::UnsupportedCpu {
890878
cpu: &HOST_CPU_REFDES,
891879
coretype: ereports::cpu::CpuTypeBits {
@@ -1108,7 +1096,7 @@ impl<S: SpiServer> ServerImpl<S> {
11081096

11091097
if ifr & thermtrip != 0 {
11101098
self.seq.clear_bytes(Addr::IFR, &[thermtrip]).unwrap_lite();
1111-
self.ereporter.try_send_ereport(&ereports::cpu::Thermtrip {
1099+
self.ereporter.deliver_ereport(&ereports::cpu::Thermtrip {
11121100
cpu: &HOST_CPU_REFDES,
11131101
state: self.ereport_current_state(),
11141102
});
@@ -1645,48 +1633,29 @@ cfg_if::cfg_if! {
16451633

16461634
////////////////////////////////////////////////////////////////////////////////
16471635

1648-
const EREPORT_BUF_LEN: usize = microcbor::max_cbor_len_for![
1649-
ereports::pwr::PmbusAlert<FixedStr<'static, 9>, { REFDES_LEN }>,
1650-
ereports::pwr::Bmr491MitigationFailure<{ REFDES_LEN }>,
1651-
ereports::cpu::Thermtrip,
1652-
ereports::cpu::UnsupportedCpu<1, 2>,
1653-
ereports::cpu::CpuMissing,
1654-
];
1636+
ereports::declare_ereporter! {
1637+
pub(crate) struct Ereporter<SeqEreport> {
1638+
PmbusAlert(
1639+
ereports::pwr::PmbusAlert<
1640+
FixedStr<'static, 9>,
1641+
{ REFDES_LEN },
1642+
>,
1643+
),
1644+
Bmr491MitigationFailure(
1645+
ereports::pwr::Bmr491MitigationFailure<{ REFDES_LEN }>
1646+
),
1647+
Thermtrip(ereports::cpu::Thermtrip),
1648+
UnsupportedCpu(ereports::cpu::UnsupportedCpu<1, 2>),
1649+
CpuMissing(ereports::cpu::CpuMissing),
1650+
}
1651+
}
16551652

16561653
static HOST_CPU_REFDES: ereports::cpu::HostCpuRefdes =
16571654
ereports::cpu::HostCpuRefdes {
16581655
refdes: fixedstr::FixedString::from_str("P0"),
16591656
dev_id: fixedstr::FixedString::from_str("sp3-host-cpu"),
16601657
};
16611658

1662-
/// This is just the Packrat API handle and the ereport buffer bundled together
1663-
/// in one thing so that it can be passed into various places as a single
1664-
/// argument.
1665-
pub(crate) struct Ereporter {
1666-
packrat: task_packrat_api::Packrat,
1667-
buf: &'static mut [u8; EREPORT_BUF_LEN],
1668-
}
1669-
1670-
impl Ereporter {
1671-
pub(crate) fn try_send_ereport(
1672-
&mut self,
1673-
ereport: &impl microcbor::StaticCborLen,
1674-
) {
1675-
let eresult = self
1676-
.packrat
1677-
.deliver_microcbor_ereport(&ereport, &mut self.buf[..]);
1678-
match eresult {
1679-
Ok(len) => ringbuf_entry!(Trace::EreportSent(len)),
1680-
Err(task_packrat_api::EreportEncodeError::Packrat { len, err }) => {
1681-
ringbuf_entry!(Trace::EreportLost(len, err))
1682-
}
1683-
Err(task_packrat_api::EreportEncodeError::Encoder(_)) => {
1684-
ringbuf_entry!(Trace::EreportTooBig)
1685-
}
1686-
}
1687-
}
1688-
}
1689-
16901659
////////////////////////////////////////////////////////////////////////////////
16911660

16921661
mod idl {

drv/gimlet-seq-server/src/vcore.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ impl VCore {
338338
pwr_good,
339339
pmbus_status: status,
340340
};
341-
ereporter.try_send_ereport(&ereport);
341+
ereporter.deliver_ereport(&ereport);
342342
// TODO(eliza): if POWER_GOOD has been deasserted, we should produce a
343343
// subsequent ereport for that.
344344

drv/psc-seq-server/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ task-packrat-api = { path = "../../task/packrat-api", features = ["microcbor"] }
1414
userlib = { path = "../../sys/userlib", features = ["panic-messages"] }
1515
ringbuf = { path = "../../lib/ringbuf", features = ["counters"] }
1616
counters = { path = "../../lib/counters" }
17-
ereports = { path = "../../lib/ereports" }
17+
ereports = { path = "../../lib/ereports", features = ["ereporter-macro"] }
1818
static-cell = { path = "../../lib/static-cell" }
1919
microcbor.path = "../../lib/microcbor"
2020
fixedstr = { path = "../../lib/fixedstr", features = ["microcbor"] }

0 commit comments

Comments
 (0)