Skip to content

Commit 1606f27

Browse files
committed
Skip programming erased pages
Flash memory is set to all `1` when erased. We have to erase pages before writing so writing pages full of `1` is not a good use of CPU time. Before real 2m0.817s user 0m4.380s sys 0m3.305s After real 1m44.543s user 0m3.586s sys 0m2.710s
1 parent c00158c commit 1606f27

File tree

3 files changed

+89
-91
lines changed

3 files changed

+89
-91
lines changed

drv/cosmo-hf/src/apob.rs

Lines changed: 14 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
//! to Rev. 2.0 February 2025.
99
1010
use crate::{
11-
hf::ServerImpl, FlashAddr, FlashDriver, PAGE_SIZE_BYTES, SECTOR_SIZE_BYTES,
11+
hf::HfBufs, hf::ServerImpl, FlashAddr, FlashDriver, PAGE_SIZE_BYTES,
12+
SECTOR_SIZE_BYTES,
1213
};
1314
use drv_hf_api::{
1415
ApobBeginError, ApobCommitError, ApobHash, ApobReadError, ApobWriteError,
@@ -237,34 +238,6 @@ impl ApobSlot {
237238
}
238239
}
239240

240-
pub(crate) struct ApobBufs {
241-
persistent_data: &'static mut [u8; APOB_PERSISTENT_DATA_STRIDE],
242-
page: &'static mut [u8; PAGE_SIZE_BYTES],
243-
scratch: &'static mut [u8; PAGE_SIZE_BYTES],
244-
}
245-
246-
/// Grabs references to the static buffers. Can only be called once.
247-
impl ApobBufs {
248-
pub fn claim_statics() -> Self {
249-
use static_cell::ClaimOnceCell;
250-
static BUFS: ClaimOnceCell<(
251-
[u8; APOB_PERSISTENT_DATA_STRIDE],
252-
[u8; PAGE_SIZE_BYTES],
253-
[u8; PAGE_SIZE_BYTES],
254-
)> = ClaimOnceCell::new((
255-
[0; APOB_PERSISTENT_DATA_STRIDE],
256-
[0; PAGE_SIZE_BYTES],
257-
[0; PAGE_SIZE_BYTES],
258-
));
259-
let (persistent_data, page, scratch) = BUFS.claim();
260-
Self {
261-
persistent_data,
262-
page,
263-
scratch,
264-
}
265-
}
266-
}
267-
268241
/// State machine data, which implements the logic from RFD 593
269242
///
270243
/// See rfd.shared.oxide.computer/rfd/593#_production_strength_implementation
@@ -397,7 +370,7 @@ impl ApobState {
397370
///
398371
/// Searches for an active slot in the metadata regions, updating the offset
399372
/// in the FPGA driver if found, and erases unused or invalid slots.
400-
pub(crate) fn init(drv: &mut FlashDriver, buf: &mut ApobBufs) -> Self {
373+
pub(crate) fn init(drv: &mut FlashDriver, buf: &mut HfBufs) -> Self {
401374
// Look up persistent data, which specifies an active slot
402375
let out = if let Some(s) = Self::get_slot(drv) {
403376
// Erase the inactive slot, in preparation for writing
@@ -448,7 +421,7 @@ impl ApobState {
448421
}
449422

450423
/// Erases the given APOB slot
451-
fn slot_erase(drv: &mut FlashDriver, buf: &mut ApobBufs, slot: ApobSlot) {
424+
fn slot_erase(drv: &mut FlashDriver, buf: &mut HfBufs, slot: ApobSlot) {
452425
static_assertions::const_assert!(
453426
APOB_SLOT_SIZE.is_multiple_of(SECTOR_SIZE_BYTES)
454427
);
@@ -463,7 +436,7 @@ impl ApobState {
463436
/// If `size > APOB_SLOT_SIZE`
464437
fn slot_erase_range(
465438
drv: &mut FlashDriver,
466-
buf: &mut ApobBufs,
439+
buf: &mut HfBufs,
467440
slot: ApobSlot,
468441
size: u32,
469442
) {
@@ -578,7 +551,7 @@ impl ApobState {
578551
pub(crate) fn write(
579552
&mut self,
580553
drv: &mut FlashDriver,
581-
buf: &mut ApobBufs,
554+
buf: &mut HfBufs,
582555
offset: u32,
583556
data: Leased<R, [u8]>,
584557
) -> Result<(), ApobWriteError> {
@@ -647,7 +620,7 @@ impl ApobState {
647620
// If any byte is not a match, then we have to do a flash write
648621
// (otherwise, it's an idempotent write and we can skip it)
649622
if needs_write {
650-
drv.flash_write(addr, &mut &buf.page[..n])
623+
drv.flash_write(addr, &buf.page[..n])
651624
.map_err(|_| ApobWriteError::WriteFailed)?;
652625
}
653626
}
@@ -657,7 +630,7 @@ impl ApobState {
657630
pub(crate) fn read(
658631
&mut self,
659632
drv: &mut FlashDriver,
660-
buf: &mut ApobBufs,
633+
buf: &mut HfBufs,
661634
offset: u32,
662635
data: Leased<W, [u8]>,
663636
) -> Result<usize, ApobReadError> {
@@ -716,7 +689,7 @@ impl ApobState {
716689
pub(crate) fn commit(
717690
&mut self,
718691
drv: &mut FlashDriver,
719-
buf: &mut ApobBufs,
692+
buf: &mut HfBufs,
720693
) -> Result<(), ApobCommitError> {
721694
drv.check_flash_mux_state()
722695
.map_err(|_| ApobCommitError::InvalidState)?;
@@ -776,7 +749,7 @@ impl ApobState {
776749

777750
fn apob_validate(
778751
drv: &mut FlashDriver,
779-
buf: &mut ApobBufs,
752+
buf: &mut HfBufs,
780753
expected_length: u32,
781754
expected_hash: ApobHash,
782755
write_slot: ApobSlot,
@@ -852,17 +825,17 @@ impl ApobState {
852825

853826
fn write_raw_persistent_data(
854827
drv: &mut FlashDriver,
855-
buf: &mut ApobBufs,
828+
buf: &mut HfBufs,
856829
data: ApobRawPersistentData,
857830
meta: Meta,
858831
) {
859832
let mut found: Option<FlashAddr> = None;
860833
for offset in (0..APOB_META_SIZE).step_by(APOB_PERSISTENT_DATA_STRIDE) {
861834
let addr = meta.flash_addr(offset).unwrap_lite();
862835
// Infallible when using a slice
863-
drv.flash_read(addr, &mut buf.persistent_data.as_mut_slice())
836+
drv.flash_read(addr, &mut buf.apob_persistent_data.as_mut_slice())
864837
.unwrap_lite();
865-
if buf.persistent_data.iter().all(|c| *c == 0xFF) {
838+
if buf.apob_persistent_data.iter().all(|c| *c == 0xFF) {
866839
found = Some(addr);
867840
break;
868841
}
@@ -873,6 +846,6 @@ impl ApobState {
873846
addr
874847
});
875848
// Infallible when using a slice
876-
drv.flash_write(addr, &mut data.as_bytes()).unwrap_lite();
849+
drv.flash_write(addr, data.as_bytes()).unwrap_lite();
877850
}
878851
}

drv/cosmo-hf/src/hf.rs

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,16 @@ use drv_hf_api::{
99
HF_PERSISTENT_DATA_STRIDE,
1010
};
1111
use idol_runtime::{
12-
LeaseBufReader, LeaseBufWriter, Leased, LenLimit, NotificationHandler,
12+
ClientError, LeaseBufWriter, Leased, LenLimit, NotificationHandler,
1313
RequestError, R, W,
1414
};
1515
use ringbuf::ringbuf_entry_root as ringbuf_entry;
1616
use userlib::{set_timer_relative, task_slot, RecvMessage, UnwrapLite};
1717
use zerocopy::{FromZeros, IntoBytes};
1818

1919
use crate::{
20-
apob, FlashAddr, FlashDriver, Trace, PAGE_SIZE_BYTES, SECTOR_SIZE_BYTES,
20+
apob, apob::APOB_PERSISTENT_DATA_STRIDE, FlashAddr, FlashDriver, Trace,
21+
PAGE_SIZE_BYTES, SECTOR_SIZE_BYTES,
2122
};
2223

2324
task_slot!(HASH, hash_driver);
@@ -33,7 +34,36 @@ pub struct ServerImpl {
3334
hash: HashData,
3435

3536
pub(crate) apob_state: apob::ApobState,
36-
pub(crate) apob_buf: apob::ApobBufs,
37+
pub(crate) buf: HfBufs,
38+
}
39+
40+
pub(crate) struct HfBufs {
41+
pub(crate) apob_persistent_data:
42+
&'static mut [u8; APOB_PERSISTENT_DATA_STRIDE],
43+
pub(crate) page: &'static mut [u8; PAGE_SIZE_BYTES],
44+
pub(crate) scratch: &'static mut [u8; PAGE_SIZE_BYTES],
45+
}
46+
47+
/// Grabs references to the static buffers. Can only be called once.
48+
impl HfBufs {
49+
pub fn claim_statics() -> Self {
50+
use static_cell::ClaimOnceCell;
51+
static BUFS: ClaimOnceCell<(
52+
[u8; APOB_PERSISTENT_DATA_STRIDE],
53+
[u8; PAGE_SIZE_BYTES],
54+
[u8; PAGE_SIZE_BYTES],
55+
)> = ClaimOnceCell::new((
56+
[0; APOB_PERSISTENT_DATA_STRIDE],
57+
[0; PAGE_SIZE_BYTES],
58+
[0; PAGE_SIZE_BYTES],
59+
));
60+
let (apob_persistent_data, page, scratch) = BUFS.claim();
61+
Self {
62+
apob_persistent_data,
63+
page,
64+
scratch,
65+
}
66+
}
3767
}
3868

3969
/// This tunes how many bytes we hash in a single async timer notification
@@ -49,15 +79,15 @@ impl ServerImpl {
4979
/// Persistent data is loaded from the flash chip and used to select `dev`;
5080
/// in addition, it is made redundant (written to both virtual devices).
5181
pub fn new(mut drv: FlashDriver) -> Self {
52-
let mut apob_buf = apob::ApobBufs::claim_statics();
53-
let apob_state = apob::ApobState::init(&mut drv, &mut apob_buf);
82+
let mut buf = HfBufs::claim_statics();
83+
let apob_state = apob::ApobState::init(&mut drv, &mut buf);
5484

5585
let mut out = Self {
5686
dev: drv_hf_api::HfDevSelect::Flash0,
5787
drv,
5888
hash: HashData::new(HASH.get_task_id()),
5989
apob_state,
60-
apob_buf,
90+
buf,
6191
};
6292
out.drv.set_flash_mux_state(HfMuxState::SP);
6393
out.ensure_persistent_data_is_redundant();
@@ -205,7 +235,7 @@ impl ServerImpl {
205235
};
206236
// flash_write is infallible when given a slice
207237
self.drv
208-
.flash_write(addr, &mut raw_data.as_bytes())
238+
.flash_write(addr, raw_data.as_bytes())
209239
.unwrap_lite();
210240
}
211241

@@ -421,11 +451,12 @@ impl idl::InOrderHostFlashImpl for ServerImpl {
421451
self.drv.check_flash_mux_state()?;
422452
let addr = self.flash_addr(addr, data.len() as u32)?;
423453
self.invalidate_write();
454+
// Read the entire data block into our address space.
455+
data.read_range(0..data.len(), &mut self.buf.scratch[..data.len()])
456+
.map_err(|_| RequestError::Fail(ClientError::WentAway))?;
457+
424458
self.drv
425-
.flash_write(
426-
addr,
427-
&mut LeaseBufReader::<_, 32>::from(data.into_inner()),
428-
)
459+
.flash_write(addr, &self.buf.scratch[..data.len()])
429460
.map_err(|()| RequestError::went_away())
430461
}
431462

@@ -527,7 +558,7 @@ impl idl::InOrderHostFlashImpl for ServerImpl {
527558
data: Leased<R, [u8]>,
528559
) -> Result<(), RequestError<drv_hf_api::ApobWriteError>> {
529560
self.apob_state
530-
.write(&mut self.drv, &mut self.apob_buf, offset, data)
561+
.write(&mut self.drv, &mut self.buf, offset, data)
531562
.map_err(RequestError::from)
532563
}
533564

@@ -536,7 +567,7 @@ impl idl::InOrderHostFlashImpl for ServerImpl {
536567
_: &RecvMessage,
537568
) -> Result<(), RequestError<drv_hf_api::ApobCommitError>> {
538569
self.apob_state
539-
.commit(&mut self.drv, &mut self.apob_buf)
570+
.commit(&mut self.drv, &mut self.buf)
540571
.map_err(RequestError::from)
541572
}
542573

@@ -555,7 +586,7 @@ impl idl::InOrderHostFlashImpl for ServerImpl {
555586
data: Leased<W, [u8]>,
556587
) -> Result<usize, RequestError<drv_hf_api::ApobReadError>> {
557588
self.apob_state
558-
.read(&mut self.drv, &mut self.apob_buf, offset, data)
589+
.read(&mut self.drv, &mut self.buf, offset, data)
559590
.map_err(RequestError::from)
560591
}
561592

@@ -589,7 +620,7 @@ impl idl::InOrderHostFlashImpl for ServerImpl {
589620
// This also unlocks the APOB so it can be written (once muxed back
590621
// to the SP).
591622
self.apob_state =
592-
apob::ApobState::init(&mut self.drv, &mut self.apob_buf);
623+
apob::ApobState::init(&mut self.drv, &mut self.buf);
593624
}
594625
self.drv.set_flash_mux_state(state);
595626
self.invalidate_mux_switch();

drv/cosmo-hf/src/main.rs

Lines changed: 29 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -345,44 +345,38 @@ impl FlashDriver {
345345
Ok(())
346346
}
347347

348-
/// Writes data from a `BufReader` into the flash
348+
/// Writes data from a slice into the flash
349349
///
350-
/// This function will only return an error if it fails to write to a
351-
/// provided lease; when given a slice, it is infallible.
352-
fn flash_write(
353-
&mut self,
354-
addr: FlashAddr,
355-
data: &mut dyn idol_runtime::BufReader<'_>,
356-
) -> Result<(), ()> {
357-
loop {
358-
let len = data.remaining_size().min(PAGE_SIZE_BYTES);
359-
if len == 0 {
360-
break;
361-
}
362-
self.flash_write_enable();
363-
self.drv.data_bytes.set_count(len as u16);
364-
self.drv.addr.set_addr(addr.0);
365-
self.drv.dummy_cycles.set_count(0);
366-
for i in 0..len.div_ceil(4) {
367-
let mut v = [0u8; 4];
368-
for (j, byte) in v.iter_mut().enumerate() {
369-
let k = i * 4 + j;
370-
if k < len {
371-
let Some(d) = data.read() else {
372-
return Err(());
373-
};
374-
*byte = d;
375-
}
376-
}
377-
let v = u32::from_le_bytes(v);
378-
self.drv.tx_fifo_wdata.set_fifo_data(v);
379-
}
380-
self.drv.instr.set_opcode(instr::QUAD_INPUT_PAGE_PROGRAM_4B);
381-
self.wait_fpga_busy();
350+
/// This function will only return an error if the slice len is greater than
351+
/// a page
352+
fn flash_write(&mut self, addr: FlashAddr, data: &[u8]) -> Result<(), ()> {
353+
// Callers are already doing everything at a page granularity
354+
if data.len() > PAGE_SIZE_BYTES {
355+
return Err(());
356+
}
382357

383-
// Wait for the busy flag to be unset
384-
self.wait_flash_busy(Trace::WriteBusy);
358+
let len = data.len();
359+
360+
// Don't bother writing erased pages
361+
if data.iter().all(|x| *x == 0xff) {
362+
return Ok(());
385363
}
364+
365+
self.flash_write_enable();
366+
self.drv.data_bytes.set_count(len as u16);
367+
self.drv.addr.set_addr(addr.0);
368+
self.drv.dummy_cycles.set_count(0);
369+
370+
for chunk in data.chunks(4) {
371+
let v = u32::from_le_bytes(chunk.try_into().unwrap_lite());
372+
self.drv.tx_fifo_wdata.set_fifo_data(v);
373+
}
374+
375+
self.drv.instr.set_opcode(instr::QUAD_INPUT_PAGE_PROGRAM_4B);
376+
self.wait_fpga_busy();
377+
378+
// Wait for the busy flag to be unset
379+
self.wait_flash_busy(Trace::WriteBusy);
386380
Ok(())
387381
}
388382

0 commit comments

Comments
 (0)