Nothing to see here, move along meow
0

Configure Feed

Select the types of activity you want to include in your feed.

at main 9.8 kB View raw
1use crate::block_io::BlockIo; 2use crate::cache::BlockCache; 3use crate::commit::{CommitRoots, SuperblockPair, atomic_commit}; 4use crate::cow::FreemapReserved; 5use crate::ditto::DittoRegion; 6use crate::error::FsError; 7use crate::freemap::FreemapAllocator; 8use crate::integrity; 9use crate::pool::{NodeIdx, NodePool, addr_to_pool_idx, is_pool_ref}; 10use lancer_core::fs::{BLOCKREF_SIZE, BlockRef, DEDUP_SHARDS, INODE_DIRECT_REFS, Inode}; 11 12const MAX_DIRTY: usize = 8192; 13const MAX_FREEMAP_LEAVES: usize = 64; 14 15#[derive(Clone, Copy)] 16struct FlushEntry { 17 pool_idx: NodeIdx, 18 block_num: u64, 19 content_hash: u128, 20 integrity_crc: u32, 21} 22 23struct FlushMap { 24 entries: [FlushEntry; MAX_DIRTY], 25 count: usize, 26} 27 28impl FlushMap { 29 const fn new() -> Self { 30 Self { 31 entries: [FlushEntry { 32 pool_idx: 0, 33 block_num: 0, 34 content_hash: 0, 35 integrity_crc: 0, 36 }; MAX_DIRTY], 37 count: 0, 38 } 39 } 40 41 fn insert( 42 &mut self, 43 idx: NodeIdx, 44 block: u64, 45 content_hash: u128, 46 integrity_crc: u32, 47 ) -> Result<(), FsError> { 48 match self.count < MAX_DIRTY { 49 true => { 50 self.entries[self.count] = FlushEntry { 51 pool_idx: idx, 52 block_num: block, 53 content_hash, 54 integrity_crc, 55 }; 56 self.count += 1; 57 Ok(()) 58 } 59 false => Err(FsError::PoolExhausted), 60 } 61 } 62 63 fn lookup(&self, idx: NodeIdx) -> Option<&FlushEntry> { 64 self.entries[..self.count] 65 .iter() 66 .find(|e| e.pool_idx == idx) 67 } 68} 69 70#[allow(clippy::too_many_arguments)] 71pub fn transaction_commit( 72 pool: &mut NodePool, 73 cache: &mut BlockCache, 74 bio: &mut BlockIo, 75 freemap: &mut FreemapAllocator, 76 reserved: &mut FreemapReserved, 77 sb_pair: &mut SuperblockPair, 78 tree_root: &BlockRef, 79 dedup_roots: &[BlockRef; DEDUP_SHARDS], 80 snapshot_root: &BlockRef, 81 ditto: Option<&DittoRegion>, 82 scrub_cursor: u64, 83 next_object_id: u64, 84) -> Result<(), FsError> { 85 transaction_commit_full( 86 pool, 87 cache, 88 bio, 89 freemap, 90 reserved, 91 sb_pair, 92 tree_root, 93 dedup_roots, 94 snapshot_root, 95 ditto, 96 scrub_cursor, 97 next_object_id, 98 ) 99} 100 101#[allow(clippy::too_many_arguments)] 102pub fn transaction_commit_full( 103 pool: &mut NodePool, 104 cache: &mut BlockCache, 105 bio: &mut BlockIo, 106 freemap: &mut FreemapAllocator, 107 reserved: &mut FreemapReserved, 108 sb_pair: &mut SuperblockPair, 109 tree_root: &BlockRef, 110 dedup_roots: &[BlockRef; DEDUP_SHARDS], 111 snapshot_root: &BlockRef, 112 ditto: Option<&DittoRegion>, 113 scrub_cursor: u64, 114 next_object_id: u64, 115) -> Result<(), FsError> { 116 let (dirty_indices, dirty_count) = pool.dirty_indices_by_level(); 117 let mut flush_map = FlushMap::new(); 118 119 (0..dirty_count).try_for_each(|i| { 120 let idx = dirty_indices[i]; 121 let new_block = freemap.alloc_blocks(cache, bio, 1)?; 122 123 resolve_pool_refs_in_node(pool, idx, &flush_map); 124 125 let node_data = pool.node_bytes(idx); 126 let hashes = integrity::compute_block_hashes(node_data); 127 cache.cache_write(bio, new_block, node_data)?; 128 129 if let Some(ditto_region) = ditto { 130 let _ = ditto_region.write_ditto(bio, new_block, node_data); 131 } 132 133 flush_map.insert(idx, new_block, hashes.content_hash, hashes.integrity_crc)?; 134 135 Ok::<(), FsError>(()) 136 })?; 137 138 let resolved_tree = resolve_root(tree_root, &flush_map); 139 let resolved_dedups: [BlockRef; DEDUP_SHARDS] = 140 core::array::from_fn(|i| resolve_root(&dedup_roots[i], &flush_map)); 141 let resolved_snapshot = resolve_root(snapshot_root, &flush_map); 142 143 let modified_leaves = collect_modified_freemap_leaves(cache, freemap)?; 144 let new_freemap_root = modified_leaves 145 .iter() 146 .take_while(|&&k| k != u64::MAX) 147 .try_fold(freemap.root(), |current_root, &leaf_key| { 148 crate::cow::cow_freemap_path(cache, bio, &current_root, leaf_key, reserved) 149 })?; 150 freemap.set_root(new_freemap_root); 151 152 resolve_inode_pool_refs_in_cache(cache, bio, &flush_map)?; 153 154 let roots = CommitRoots { 155 tree_root: resolved_tree, 156 freemap_root: new_freemap_root, 157 dedup_roots: resolved_dedups, 158 snapshot_root: resolved_snapshot, 159 scrub_cursor, 160 next_object_id, 161 }; 162 163 atomic_commit(bio, cache, sb_pair, &roots)?; 164 reserved.flip_after_commit(); 165 pool.clear_all(); 166 167 Ok(()) 168} 169 170fn resolve_root(root: &BlockRef, flush_map: &FlushMap) -> BlockRef { 171 let addr = root.physical_block_addr(); 172 match is_pool_ref(addr) { 173 true => { 174 let pidx = crate::pool::addr_to_pool_idx(addr); 175 flush_map.lookup(pidx).map_or(*root, |entry| { 176 let mut resolved = *root; 177 resolved.physical_addr = 178 crate::block_num_to_phys(entry.block_num) | (root.physical_addr & 0xF); 179 resolved.content_hash = entry.content_hash.to_le_bytes(); 180 resolved.integrity_crc = entry.integrity_crc; 181 resolved 182 }) 183 } 184 false => *root, 185 } 186} 187 188fn resolve_pool_refs_in_node(pool: &mut NodePool, idx: NodeIdx, flush_map: &FlushMap) { 189 let node = pool.get_mut(idx); 190 let count = node.entry_count(); 191 (0..count).for_each(|i| { 192 let addr = node.entries[i].physical_block_addr(); 193 if is_pool_ref(addr) { 194 let pidx = crate::pool::addr_to_pool_idx(addr); 195 if let Some(entry) = flush_map.lookup(pidx) { 196 let old_shift_bits = node.entries[i].physical_addr & 0xF; 197 node.entries[i].physical_addr = 198 crate::block_num_to_phys(entry.block_num) | old_shift_bits; 199 node.entries[i].content_hash = entry.content_hash.to_le_bytes(); 200 node.entries[i].integrity_crc = entry.integrity_crc; 201 } 202 } 203 }); 204} 205 206fn collect_modified_freemap_leaves( 207 cache: &BlockCache, 208 freemap: &FreemapAllocator, 209) -> Result<[u64; MAX_FREEMAP_LEAVES], FsError> { 210 let mut result = [u64::MAX; MAX_FREEMAP_LEAVES]; 211 let mut count = 0usize; 212 let mut overflow = false; 213 let data_start = freemap.data_region_start(); 214 let bits_per_leaf = lancer_core::fs::FREEMAP_BITS_PER_LEAF as u64; 215 216 cache.dirty_block_nums(|block_num| { 217 if block_num >= data_start { 218 let relative = block_num - data_start; 219 let leaf_key = relative / bits_per_leaf; 220 let already_present = (0..count).any(|i| result[i] == leaf_key); 221 if !already_present { 222 match count < MAX_FREEMAP_LEAVES { 223 true => { 224 result[count] = leaf_key; 225 count += 1; 226 } 227 false => { 228 overflow = true; 229 } 230 } 231 } 232 } 233 }); 234 235 match overflow { 236 true => Err(FsError::PoolExhausted), 237 false => { 238 insertion_sort_leaves(&mut result, count); 239 Ok(result) 240 } 241 } 242} 243 244fn insertion_sort_leaves(arr: &mut [u64; MAX_FREEMAP_LEAVES], count: usize) { 245 (1..count).for_each(|i| { 246 let val = arr[i]; 247 let insert_pos = arr[..i] 248 .iter() 249 .rposition(|&v| v <= val) 250 .map_or(0, |p| p + 1); 251 if insert_pos < i { 252 arr.copy_within(insert_pos..i, insert_pos + 1); 253 arr[insert_pos] = val; 254 } 255 }); 256} 257 258const MAX_CACHE_DIRTY: usize = 512; 259 260fn resolve_inode_pool_refs_in_cache( 261 cache: &mut BlockCache, 262 bio: &mut BlockIo, 263 flush_map: &FlushMap, 264) -> Result<(), FsError> { 265 let mut dirty_blocks = [0u64; MAX_CACHE_DIRTY]; 266 let mut count = 0usize; 267 cache.dirty_inode_block_nums(|block_num| { 268 dirty_blocks.get_mut(count).into_iter().for_each(|slot| { 269 *slot = block_num; 270 count += 1; 271 }); 272 }); 273 274 (0..count).try_for_each(|i| { 275 let data = cache.cache_get_mut(bio, dirty_blocks[i])?; 276 resolve_blockref_pool_refs(data, flush_map); 277 Ok::<(), FsError>(()) 278 }) 279} 280 281fn resolve_blockref_pool_refs(block_data: &mut [u8; 4096], flush_map: &FlushMap) { 282 let direct_offset = core::mem::offset_of!(Inode, direct); 283 284 (0..INODE_DIRECT_REFS).for_each(|i| { 285 let ref_start = direct_offset + i * BLOCKREF_SIZE; 286 let addr = u64::from_le_bytes( 287 block_data[ref_start..ref_start + 8] 288 .try_into() 289 .unwrap_or([0u8; 8]), 290 ); 291 292 match is_pool_ref(addr) { 293 false => {} 294 true => { 295 let pidx = addr_to_pool_idx(addr); 296 if let Some(entry) = flush_map.lookup(pidx) { 297 let shift_bits = addr & 0xF; 298 let new_addr = crate::block_num_to_phys(entry.block_num) | shift_bits; 299 block_data[ref_start..ref_start + 8].copy_from_slice(&new_addr.to_le_bytes()); 300 301 let hash_start = ref_start + core::mem::offset_of!(BlockRef, content_hash); 302 block_data[hash_start..hash_start + 16] 303 .copy_from_slice(&entry.content_hash.to_le_bytes()); 304 305 let crc_start = ref_start + core::mem::offset_of!(BlockRef, integrity_crc); 306 block_data[crc_start..crc_start + 4] 307 .copy_from_slice(&entry.integrity_crc.to_le_bytes()); 308 } 309 } 310 } 311 }); 312}