cache.rs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. use crate::constants::*;
  2. use crate::prelude::*;
  3. use crate::Block;
  4. use crate::BlockDevice;
  5. /// Write-back cache slot.
  6. #[derive(Debug, Clone, Copy, Default)]
  7. struct CacheSlot {
  8. /// Valid flag.
  9. valid: bool,
  10. /// Dirty flag.
  11. dirty: bool,
  12. /// Previous slot in the LRU list.
  13. prev: u8,
  14. /// Next slot in the LRU list.
  15. next: u8,
  16. /// Block data.
  17. block: Block,
  18. }
  19. /// Associative cache set.
  20. #[derive(Debug, Clone, Copy)]
  21. struct CacheSet {
  22. /// `CACHE_ASSOC`-way-associative slots.
  23. slots: [CacheSlot; CACHE_ASSOC],
  24. /// Head of the LRU list.
  25. head: u8,
  26. }
  27. impl CacheSet {
  28. /// Initialize the cache set. Initialize in heap to avoid stack overflow.
  29. fn new() -> Box<Self> {
  30. let mut set = Box::new(CacheSet {
  31. slots: [CacheSlot::default(); CACHE_ASSOC],
  32. head: CACHE_ASSOC as u8 - 1,
  33. });
  34. for i in 1..CACHE_ASSOC as u8 {
  35. set.link(i - 1, i);
  36. }
  37. set.link(CACHE_ASSOC as u8 - 1, 0);
  38. set
  39. }
  40. /// Link LRU list.
  41. fn link(&mut self, prev: u8, cur: u8) {
  42. self.slots[prev as usize].next = cur;
  43. self.slots[cur as usize].prev = prev;
  44. }
  45. /// Access a block in the cache set
  46. fn access(&mut self, block_id: PBlockId) -> usize {
  47. // Check if there is a slot allocated for the block
  48. let slot = self
  49. .slots
  50. .iter()
  51. .position(|b| b.valid && b.block.id == block_id);
  52. if let Some(slot) = slot {
  53. // If yes, set head as slot_id
  54. if self.head != slot as u8 {
  55. self.link(self.slots[slot].prev, self.slots[slot].next);
  56. self.link(self.slots[self.head as usize].prev, slot as u8);
  57. self.link(slot as u8, self.head);
  58. self.head = slot as u8;
  59. }
  60. slot
  61. } else {
  62. // If not, head goes 1 step forward to reach the last slot
  63. self.head = self.slots[self.head as usize].prev;
  64. self.head as usize
  65. }
  66. }
  67. }
  68. /// LRU Write-back Block Cache.
  69. pub struct BlockCache {
  70. /// Block cache allocated on the heap.
  71. cache: Arc<Mutex<[CacheSet; CACHE_SIZE]>>,
  72. /// The underlying block device.
  73. block_dev: Arc<dyn BlockDevice>,
  74. }
  75. impl BlockCache {
  76. /// Create a new block cache on a block device.
  77. pub fn new(block_dev: Arc<dyn BlockDevice>) -> Self {
  78. // Initialize in heap to avoid stack overflow
  79. let cache = vec![*CacheSet::new(); CACHE_SIZE];
  80. Self {
  81. cache: Arc::new(Mutex::new(cache.try_into().unwrap())),
  82. block_dev,
  83. }
  84. }
  85. /// Read a block.
  86. pub fn read_block(&self, block_id: PBlockId) -> Block {
  87. debug!("Reading block {}", block_id);
  88. let set_id = block_id as usize % CACHE_SIZE;
  89. let mut cache = self.cache.lock();
  90. let slot_id = cache[set_id].access(block_id) as usize;
  91. let slot = &mut cache[set_id].slots[slot_id];
  92. // Check block id
  93. if slot.valid && slot.block.id == block_id {
  94. // Cache hit
  95. return slot.block.clone();
  96. } else {
  97. // Cache miss
  98. if slot.valid && slot.dirty {
  99. // Write back Dirty block
  100. self.block_dev.write_block(&slot.block);
  101. slot.dirty = false;
  102. }
  103. // Read block from disk
  104. debug!("Loading block {} from disk", block_id);
  105. let block = self.block_dev.read_block(block_id);
  106. slot.block = block.clone();
  107. slot.valid = true;
  108. return block;
  109. }
  110. }
  111. /// Write a block. (Write-Allocate)
  112. pub fn write_block(&self, block: &Block) {
  113. debug!("Writing block {}", block.id);
  114. let set_id = block.id as usize % CACHE_SIZE;
  115. let mut cache = self.cache.lock();
  116. let slot_id = cache[set_id].access(block.id) as usize;
  117. let slot = &mut cache[set_id].slots[slot_id];
  118. // Check block id
  119. if slot.valid && slot.block.id == block.id {
  120. // Cache hit
  121. slot.block = block.clone();
  122. slot.dirty = true;
  123. } else {
  124. // Cache miss
  125. if slot.valid && slot.dirty {
  126. // Write back Dirty block
  127. self.block_dev.write_block(&slot.block);
  128. slot.dirty = false;
  129. }
  130. // Write allocate
  131. let block = self.block_dev.read_block(block.id);
  132. slot.block = block.clone();
  133. slot.valid = true;
  134. slot.dirty = true;
  135. }
  136. }
  137. }