Skip to content

Commit

Permalink
Added the first real cache
Browse files Browse the repository at this point in the history
  • Loading branch information
diondokter committed Jan 12, 2024
1 parent f54488f commit 733cc49
Show file tree
Hide file tree
Showing 5 changed files with 223 additions and 79 deletions.
8 changes: 8 additions & 0 deletions fuzz/fuzz_targets/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ fn fuzz(ops: Input) {
block_on(sequential_storage::queue::try_repair(
&mut flash,
FLASH_RANGE,
&mut cache,
))
.unwrap();
corruption_repaired = true;
Expand Down Expand Up @@ -149,6 +150,7 @@ fn fuzz(ops: Input) {
block_on(sequential_storage::queue::try_repair(
&mut flash,
FLASH_RANGE,
&mut cache,
))
.unwrap();
corruption_repaired = true;
Expand Down Expand Up @@ -198,6 +200,7 @@ fn fuzz(ops: Input) {
block_on(sequential_storage::queue::try_repair(
&mut flash,
FLASH_RANGE,
&mut cache,
))
.unwrap();
corruption_repaired = true;
Expand All @@ -224,6 +227,7 @@ fn fuzz(ops: Input) {
block_on(sequential_storage::queue::try_repair(
&mut flash,
FLASH_RANGE,
&mut cache,
))
.unwrap();
corruption_repaired = true;
Expand Down Expand Up @@ -273,6 +277,7 @@ fn fuzz(ops: Input) {
block_on(sequential_storage::queue::try_repair(
&mut flash,
FLASH_RANGE,
&mut cache,
))
.unwrap();
corruption_repaired = true;
Expand Down Expand Up @@ -308,6 +313,7 @@ fn fuzz(ops: Input) {
block_on(sequential_storage::queue::try_repair(
&mut flash,
FLASH_RANGE,
&mut cache,
))
.unwrap();
corruption_repaired = true;
Expand All @@ -334,6 +340,7 @@ fn fuzz(ops: Input) {
block_on(sequential_storage::queue::try_repair(
&mut flash,
FLASH_RANGE,
&mut cache,
))
.unwrap();
corruption_repaired = true;
Expand Down Expand Up @@ -365,6 +372,7 @@ fn fuzz(ops: Input) {
block_on(sequential_storage::queue::try_repair(
&mut flash,
FLASH_RANGE,
&mut cache,
))
.unwrap();
corruption_repaired = true;
Expand Down
199 changes: 172 additions & 27 deletions src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,36 +15,11 @@ pub(crate) trait StateQuery {
fn unmark_dirty(&mut self);
fn is_dirty(&self) -> bool;

fn notice_page_state(&mut self, page_index: usize, new_state: PageState);
fn notice_page_state(&mut self, _page_index: usize, _new_state: PageState) {}

/// Get the state of the page located at the given index
async fn get_page_state<S: NorFlash>(
&self,
flash: &mut S,
flash_range: Range<u32>,
page_index: usize,
) -> Result<PageState, Error<S::Error>>;
}

pub struct NoCache;

impl StateQuery for NoCache {
fn invalidate_cache_state(&mut self) {}

fn mark_dirty(&mut self) {}

fn unmark_dirty(&mut self) {}

fn is_dirty(&self) -> bool {
false
}

fn notice_page_state(&mut self, _page_index: usize, _new_state: PageState) {
todo!()
}

async fn get_page_state<S: NorFlash>(
&self,
&mut self,
flash: &mut S,
flash_range: Range<u32>,
page_index: usize,
Expand Down Expand Up @@ -99,3 +74,173 @@ impl StateQuery for NoCache {
}
}
}

pub struct NoCache;

impl StateQuery for NoCache {
fn invalidate_cache_state(&mut self) {}

fn mark_dirty(&mut self) {}

fn unmark_dirty(&mut self) {}

fn is_dirty(&self) -> bool {
false
}
}

struct DirtyCache {
dirty: bool,
}

impl StateQuery for DirtyCache {
fn invalidate_cache_state(&mut self) {
self.dirty = false;
}

fn mark_dirty(&mut self) {
self.dirty = true;
}

fn unmark_dirty(&mut self) {
self.dirty = false;
}

fn is_dirty(&self) -> bool {
self.dirty
}
}

pub struct PageStateCache<const PAGE_COUNT: usize> {
dirty_cache: DirtyCache,
pages: [Option<PageState>; PAGE_COUNT],
}

impl<const PAGE_COUNT: usize> PageStateCache<PAGE_COUNT> {
pub const fn new() -> Self {
Self {
dirty_cache: DirtyCache { dirty: false },
pages: [None; PAGE_COUNT],
}
}
}

impl<const PAGE_COUNT: usize> Default for PageStateCache<PAGE_COUNT> {
fn default() -> Self {
Self::new()
}
}

impl<const PAGE_COUNT: usize> StateQuery for PageStateCache<PAGE_COUNT> {
fn invalidate_cache_state(&mut self) {
self.dirty_cache.invalidate_cache_state();
self.pages = [None; PAGE_COUNT];
}

fn mark_dirty(&mut self) {
self.dirty_cache.mark_dirty();
}

fn unmark_dirty(&mut self) {
self.dirty_cache.unmark_dirty();
}

fn is_dirty(&self) -> bool {
self.dirty_cache.is_dirty()
}

fn notice_page_state(&mut self, page_index: usize, new_state: PageState) {
self.mark_dirty();
self.pages[page_index] = Some(new_state);
}

async fn get_page_state<S: NorFlash>(
&mut self,
flash: &mut S,
flash_range: Range<u32>,
page_index: usize,
) -> Result<PageState, Error<S::Error>> {
match self.pages[page_index] {
Some(state) => Ok(state),
None => {
let state = NoCache
.get_page_state(flash, flash_range, page_index)
.await?;
self.pages[page_index] = Some(state);
Ok(state)
}
}
}
}

#[cfg(test)]
mod queue_tests {
use crate::{
mock_flash::{self, WriteCountCheck},
queue::{peek, pop, push},
};

use super::*;
use futures_test::test;

const NUM_PAGES: usize = 4;
const LOOP_COUNT: usize = 2000;

#[test]
async fn no_cache() {
assert_eq!(run_test(NoCache).await, (594934, 6299, 146));
}

#[test]
async fn page_state_cache() {
assert_eq!(
run_test(PageStateCache::<NUM_PAGES>::new()).await,
(308740, 6299, 146)
);
}

async fn run_test(mut cache: impl Cache) -> (u32, u32, u32) {
let mut flash =
mock_flash::MockFlashBase::<NUM_PAGES, 1, 256>::new(WriteCountCheck::Twice, None);
const FLASH_RANGE: Range<u32> = 0x00..0x400;
let mut data_buffer = [0; 1024];

for i in 0..LOOP_COUNT {
println!("{i}");
let data = vec![i as u8; i % 20 + 1];

println!("PUSH");
push(&mut flash, FLASH_RANGE, &mut cache, &data, true)
.await
.unwrap();
assert_eq!(
&peek(&mut flash, FLASH_RANGE, &mut cache, &mut data_buffer)
.await
.unwrap()
.unwrap()[..],
&data,
"At {i}"
);
println!("POP");
assert_eq!(
&pop(&mut flash, FLASH_RANGE, &mut cache, &mut data_buffer)
.await
.unwrap()
.unwrap()[..],
&data,
"At {i}"
);
println!("PEEK");
assert_eq!(
peek(&mut flash, FLASH_RANGE, &mut cache, &mut data_buffer)
.await
.unwrap(),
None,
"At {i}"
);
println!("DONE");
}

(flash.reads, flash.writes, flash.erases)
}
}
53 changes: 37 additions & 16 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ impl<const SIZE: usize> DerefMut for AlignedBuf<SIZE> {
async fn try_general_repair<S: NorFlash>(
flash: &mut S,
flash_range: Range<u32>,
query: &mut impl StateQuery,
) -> Result<(), Error<S::Error>> {
// Loop through the pages and get their state. If one returns the corrupted error,
// the page is likely half-erased. Fix for that is to re-erase again to hopefully finish the job.
Expand All @@ -59,17 +60,7 @@ async fn try_general_repair<S: NorFlash>(
.await,
Err(Error::Corrupted { .. })
) {
flash
.erase(
calculate_page_address::<S>(flash_range.clone(), page_index),
calculate_page_end_address::<S>(flash_range.clone(), page_index),
)
.await
.map_err(|e| Error::Storage {
value: e,
#[cfg(feature = "_test")]
backtrace: std::backtrace::Backtrace::capture(),
})?;
open_page(flash, flash_range.clone(), query, page_index).await?;
}
}

Expand Down Expand Up @@ -147,6 +138,30 @@ const fn calculate_page_index<S: NorFlash>(flash_range: Range<u32>, address: u32
/// The marker being used for page states
const MARKER: u8 = 0;

/// Erase the page to open it again
async fn open_page<S: NorFlash>(
flash: &mut S,
flash_range: Range<u32>,
query: &mut impl StateQuery,
page_index: usize,
) -> Result<(), Error<S::Error>> {
query.notice_page_state(page_index, PageState::Open);

flash
.erase(
calculate_page_address::<S>(flash_range.clone(), page_index),
calculate_page_end_address::<S>(flash_range.clone(), page_index),
)
.await
.map_err(|e| Error::Storage {
value: e,
#[cfg(feature = "_test")]
backtrace: std::backtrace::Backtrace::capture(),
})?;

Ok(())
}

/// Fully closes a page by writing both the start and end marker
async fn close_page<S: NorFlash>(
flash: &mut S,
Expand All @@ -161,6 +176,8 @@ async fn close_page<S: NorFlash>(
return Ok(());
}

query.notice_page_state(page_index, PageState::Closed);

let buffer = AlignedBuf([MARKER; MAX_WORD_SIZE]);
// Close the end marker
flash
Expand Down Expand Up @@ -193,6 +210,14 @@ async fn partial_close_page<S: NorFlash>(
return Ok(current_state);
}

let new_state = match current_state {
PageState::Closed => PageState::Closed,
PageState::PartialOpen => PageState::PartialOpen,
PageState::Open => PageState::PartialOpen,
};

query.notice_page_state(page_index, new_state);

let buffer = AlignedBuf([MARKER; MAX_WORD_SIZE]);
// Close the start marker
flash
Expand All @@ -207,11 +232,7 @@ async fn partial_close_page<S: NorFlash>(
backtrace: std::backtrace::Backtrace::capture(),
})?;

Ok(match current_state {
PageState::Closed => PageState::Closed,
PageState::PartialOpen => PageState::PartialOpen,
PageState::Open => PageState::PartialOpen,
})
Ok(new_state)
}

/// The state of a page
Expand Down
Loading

0 comments on commit 733cc49

Please sign in to comment.