Skip to content

Commit 18b1754

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 a025330 commit 18b1754

File tree

3 files changed

+82
-86
lines changed

3 files changed

+82
-86
lines changed

drv/cosmo-hf/src/apob.rs

Lines changed: 14 additions & 43 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,8 +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])
651-
.map_err(|_| ApobWriteError::WriteFailed)?;
623+
drv.flash_write(addr, &buf.page[..n]);
652624
}
653625
}
654626
Ok(())
@@ -657,7 +629,7 @@ impl ApobState {
657629
pub(crate) fn read(
658630
&mut self,
659631
drv: &mut FlashDriver,
660-
buf: &mut ApobBufs,
632+
buf: &mut HfBufs,
661633
offset: u32,
662634
data: Leased<W, [u8]>,
663635
) -> Result<usize, ApobReadError> {
@@ -716,7 +688,7 @@ impl ApobState {
716688
pub(crate) fn commit(
717689
&mut self,
718690
drv: &mut FlashDriver,
719-
buf: &mut ApobBufs,
691+
buf: &mut HfBufs,
720692
) -> Result<(), ApobCommitError> {
721693
drv.check_flash_mux_state()
722694
.map_err(|_| ApobCommitError::InvalidState)?;
@@ -776,7 +748,7 @@ impl ApobState {
776748

777749
fn apob_validate(
778750
drv: &mut FlashDriver,
779-
buf: &mut ApobBufs,
751+
buf: &mut HfBufs,
780752
expected_length: u32,
781753
expected_hash: ApobHash,
782754
write_slot: ApobSlot,
@@ -852,17 +824,17 @@ impl ApobState {
852824

853825
fn write_raw_persistent_data(
854826
drv: &mut FlashDriver,
855-
buf: &mut ApobBufs,
827+
buf: &mut HfBufs,
856828
data: ApobRawPersistentData,
857829
meta: Meta,
858830
) {
859831
let mut found: Option<FlashAddr> = None;
860832
for offset in (0..APOB_META_SIZE).step_by(APOB_PERSISTENT_DATA_STRIDE) {
861833
let addr = meta.flash_addr(offset).unwrap_lite();
862834
// Infallible when using a slice
863-
drv.flash_read(addr, &mut buf.persistent_data.as_mut_slice())
835+
drv.flash_read(addr, &mut buf.apob_persistent_data.as_mut_slice())
864836
.unwrap_lite();
865-
if buf.persistent_data.iter().all(|c| *c == 0xFF) {
837+
if buf.apob_persistent_data.iter().all(|c| *c == 0xFF) {
866838
found = Some(addr);
867839
break;
868840
}
@@ -872,7 +844,6 @@ impl ApobState {
872844
drv.flash_sector_erase(addr);
873845
addr
874846
});
875-
// Infallible when using a slice
876-
drv.flash_write(addr, &mut data.as_bytes()).unwrap_lite();
847+
drv.flash_write(addr, data.as_bytes());
877848
}
878849
}

drv/cosmo-hf/src/hf.rs

Lines changed: 47 additions & 18 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();
@@ -203,10 +233,8 @@ impl ServerImpl {
203233
addr
204234
}
205235
};
206-
// flash_write is infallible when given a slice
207236
self.drv
208-
.flash_write(addr, &mut raw_data.as_bytes())
209-
.unwrap_lite();
237+
.flash_write(addr, raw_data.as_bytes());
210238
}
211239

212240
/// Ensures that the persistent data is consistent between the virtual devs
@@ -421,12 +449,13 @@ impl idl::InOrderHostFlashImpl for ServerImpl {
421449
self.drv.check_flash_mux_state()?;
422450
let addr = self.flash_addr(addr, data.len() as u32)?;
423451
self.invalidate_write();
452+
// Read the entire data block into our address space.
453+
data.read_range(0..data.len(), &mut self.buf.scratch[..data.len()])
454+
.map_err(|_| RequestError::Fail(ClientError::WentAway))?;
455+
424456
self.drv
425-
.flash_write(
426-
addr,
427-
&mut LeaseBufReader::<_, 32>::from(data.into_inner()),
428-
)
429-
.map_err(|()| RequestError::went_away())
457+
.flash_write(addr, &self.buf.scratch[..data.len()]);
458+
Ok(())
430459
}
431460

432461
fn page_program_dev(
@@ -527,7 +556,7 @@ impl idl::InOrderHostFlashImpl for ServerImpl {
527556
data: Leased<R, [u8]>,
528557
) -> Result<(), RequestError<drv_hf_api::ApobWriteError>> {
529558
self.apob_state
530-
.write(&mut self.drv, &mut self.apob_buf, offset, data)
559+
.write(&mut self.drv, &mut self.buf, offset, data)
531560
.map_err(RequestError::from)
532561
}
533562

@@ -536,7 +565,7 @@ impl idl::InOrderHostFlashImpl for ServerImpl {
536565
_: &RecvMessage,
537566
) -> Result<(), RequestError<drv_hf_api::ApobCommitError>> {
538567
self.apob_state
539-
.commit(&mut self.drv, &mut self.apob_buf)
568+
.commit(&mut self.drv, &mut self.buf)
540569
.map_err(RequestError::from)
541570
}
542571

@@ -555,7 +584,7 @@ impl idl::InOrderHostFlashImpl for ServerImpl {
555584
data: Leased<W, [u8]>,
556585
) -> Result<usize, RequestError<drv_hf_api::ApobReadError>> {
557586
self.apob_state
558-
.read(&mut self.drv, &mut self.apob_buf, offset, data)
587+
.read(&mut self.drv, &mut self.buf, offset, data)
559588
.map_err(RequestError::from)
560589
}
561590

@@ -589,7 +618,7 @@ impl idl::InOrderHostFlashImpl for ServerImpl {
589618
// This also unlocks the APOB so it can be written (once muxed back
590619
// to the SP).
591620
self.apob_state =
592-
apob::ApobState::init(&mut self.drv, &mut self.apob_buf);
621+
apob::ApobState::init(&mut self.drv, &mut self.buf);
593622
}
594623
self.drv.set_flash_mux_state(state);
595624
self.invalidate_mux_switch();

drv/cosmo-hf/src/main.rs

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -348,45 +348,41 @@ impl FlashDriver {
348348
Ok(())
349349
}
350350

351-
/// Writes data from a `BufReader` into the flash
351+
/// Writes data from a slice into the flash
352352
///
353-
/// This function will only return an error if it fails to write to a
354-
/// provided lease; when given a slice, it is infallible.
355-
fn flash_write(
356-
&mut self,
357-
addr: FlashAddr,
358-
data: &mut dyn idol_runtime::BufReader<'_>,
359-
) -> Result<(), ()> {
360-
loop {
361-
let len = data.remaining_size().min(PAGE_SIZE_BYTES);
362-
if len == 0 {
363-
break;
364-
}
353+
/// This function will only return an error if the slice len is greater than
354+
/// a page
355+
fn flash_write(&mut self, addr: FlashAddr, data: &[u8]) {
356+
// Don't bother writing erased pages
357+
if data.iter().all(|x| *x == 0xff) {
358+
return;
359+
}
360+
361+
let mut addr = addr.0;
362+
for page_chunk in data.chunks(PAGE_SIZE_BYTES) {
365363
self.flash_write_enable();
366-
self.drv.data_bytes.set_count(len as u16);
367-
self.drv.addr.set_addr(addr.0);
364+
self.drv.data_bytes.set_count(page_chunk.len() as u16);
365+
self.drv.addr.set_addr(addr);
368366
self.drv.dummy_cycles.set_count(0);
369-
for i in 0..len.div_ceil(4) {
367+
368+
for chunk in page_chunk.chunks(4) {
369+
// Manually construct the u32 instad of just `try_into`
370+
// just in case this slice was an odd number of bytes
370371
let mut v = [0u8; 4];
371-
for (j, byte) in v.iter_mut().enumerate() {
372-
let k = i * 4 + j;
373-
if k < len {
374-
let Some(d) = data.read() else {
375-
return Err(());
376-
};
377-
*byte = d;
378-
}
372+
for (i, b) in chunk.iter().enumerate() {
373+
v[i] = *b;
379374
}
380375
let v = u32::from_le_bytes(v);
381376
self.drv.tx_fifo_wdata.set_fifo_data(v);
382377
}
378+
383379
self.drv.instr.set_opcode(instr::QUAD_INPUT_PAGE_PROGRAM_4B);
384380
self.wait_fpga_busy();
385381

386382
// Wait for the busy flag to be unset
387383
self.wait_flash_busy(Trace::WriteBusy);
384+
addr += page_chunk.len() as u32;
388385
}
389-
Ok(())
390386
}
391387

392388
/// Enable the quad enable bit in flash

0 commit comments

Comments
 (0)