Store number of blocks actually used

This commit is contained in:
Jeremy Soller
2025-08-01 11:51:37 -06:00
parent f30b363ce1
commit a1c94311db
8 changed files with 110 additions and 52 deletions
+11 -11
View File
@@ -191,11 +191,11 @@ impl AllocEntry {
}
pub fn allocate(addr: BlockAddr) -> Self {
Self::new(addr.index(), -addr.level().blocks())
Self::new(addr.index(), -addr.level().blocks::<i64>())
}
pub fn deallocate(addr: BlockAddr) -> Self {
Self::new(addr.index(), addr.level().blocks())
Self::new(addr.index(), addr.level().blocks::<i64>())
}
pub fn index(&self) -> u64 {
@@ -282,17 +282,17 @@ fn alloc_node_size_test() {
fn allocator_test() {
let mut alloc = Allocator::default();
assert_eq!(alloc.allocate(BlockLevel::default()), None);
assert_eq!(alloc.allocate(BlockMeta::default()), None);
alloc.deallocate(unsafe { BlockAddr::new(1, BlockLevel::default()) });
alloc.deallocate(unsafe { BlockAddr::new(1, BlockMeta::default()) });
assert_eq!(
alloc.allocate(BlockLevel::default()),
Some(unsafe { BlockAddr::new(1, BlockLevel::default()) })
alloc.allocate(BlockMeta::default()),
Some(unsafe { BlockAddr::new(1, BlockMeta::default()) })
);
assert_eq!(alloc.allocate(BlockLevel::default()), None);
assert_eq!(alloc.allocate(BlockMeta::default()), None);
for addr in 1023..2048 {
alloc.deallocate(unsafe { BlockAddr::new(addr, BlockLevel::default()) });
alloc.deallocate(unsafe { BlockAddr::new(addr, BlockMeta::default()) });
}
assert_eq!(alloc.levels.len(), 11);
@@ -308,11 +308,11 @@ fn allocator_test() {
for addr in 1023..2048 {
assert_eq!(
alloc.allocate(BlockLevel::default()),
Some(unsafe { BlockAddr::new(addr, BlockLevel::default()) })
alloc.allocate(BlockMeta::default()),
Some(unsafe { BlockAddr::new(addr, BlockMeta::default()) })
);
}
assert_eq!(alloc.allocate(BlockLevel::default()), None);
assert_eq!(alloc.allocate(BlockMeta::default()), None);
assert_eq!(alloc.levels.len(), 11);
for level in 0..alloc.levels.len() {
+2 -2
View File
@@ -125,8 +125,8 @@ impl BlockLevel {
/// The number of [`BLOCK_SIZE`] blocks (i.e, level 0 blocks)
/// in a block of this level
pub fn blocks(self) -> i64 {
1 << self.0
pub fn blocks<T: From<u32>>(self) -> T {
T::from(1u32 << self.0)
}
/// The number of bytes in a block of this level
+1 -1
View File
@@ -77,7 +77,7 @@ fn node_attr(node: &TreeData<Node>) -> FileAttr {
ino: node.id() as u64,
size: node.data().size(),
// Blocks is in 512 byte blocks, not in our block size
blocks: node.data().size().div_ceil(BLOCK_SIZE) * (BLOCK_SIZE / 512),
blocks: node.data().blocks() * (BLOCK_SIZE / 512),
blksize: 512,
atime: SystemTime::UNIX_EPOCH + Duration::new(node.data().atime().0, node.data().atime().1),
mtime: SystemTime::UNIX_EPOCH + Duration::new(node.data().mtime().0, node.data().mtime().1),
+4 -2
View File
@@ -14,7 +14,7 @@ use syscall::flag::{
};
use syscall::{EBADFD, PAGE_SIZE};
use crate::{Disk, Node, Transaction, TreePtr};
use crate::{Disk, Node, Transaction, TreePtr, BLOCK_SIZE};
pub type Fmaps = BTreeMap<u32, FileMmapInfo>;
@@ -117,13 +117,15 @@ pub trait Resource<D: Disk> {
st_uid: node.data().uid(),
st_gid: node.data().gid(),
st_size: node.data().size(),
st_blksize: 512,
// Blocks is in 512 byte blocks, not in our block size
st_blocks: node.data().blocks() * (BLOCK_SIZE / 512),
st_mtime: mtime.0,
st_mtime_nsec: mtime.1,
st_atime: atime.0,
st_atime_nsec: atime.1,
st_ctime: ctime.0,
st_ctime_nsec: ctime.1,
..Default::default()
};
Ok(())
+27 -7
View File
@@ -107,17 +107,25 @@ pub struct Node {
/// The length of this file, in bytes
pub size: Le<u64>,
/// The disk usage of this file, in blocks
pub blocks: Le<u64>,
/// Creation time
pub ctime: Le<u64>,
pub ctime_nsec: Le<u32>,
/// Modification time
pub mtime: Le<u64>,
pub mtime_nsec: Le<u32>,
/// Access time
pub atime: Le<u64>,
pub atime_nsec: Le<u32>,
/// Record level
pub record_level: Le<u32>,
pub padding: [u8; BLOCK_SIZE as usize - 4094],
pub padding: [u8; BLOCK_SIZE as usize - 4038],
/// The first 128 blocks of this file.
///
@@ -143,11 +151,11 @@ pub struct Node {
/// Total size: 16 * 256 * 256 * 256 * RECORD_SIZE (32 TiB, 2 TiB each)
pub level3: [BlockPtr<BlockListL3>; 16],
/// The next 12 * 256 * 256 * 256 * 256 blocks of this file,
/// stored behind 12 level four tables.
/// The next 8 * 256 * 256 * 256 * 256 blocks of this file,
/// stored behind 8 level four tables.
///
/// Total size: 12 * 256 * 256 * 256 * 256 * RECORD_SIZE (6 PiB, 512 TiB each)
pub level4: [BlockPtr<BlockListL4>; 12],
/// Total size: 8 * 256 * 256 * 256 * 256 * RECORD_SIZE (4 PiB, 512 TiB each)
pub level4: [BlockPtr<BlockListL4>; 8],
}
unsafe impl BlockTrait for Node {
@@ -168,6 +176,9 @@ impl Default for Node {
gid: 0.into(),
links: 0.into(),
size: 0.into(),
// Node counts as a block.
//TODO: track all the blocks in indirect levels
blocks: 1.into(),
ctime: 0.into(),
ctime_nsec: 0.into(),
mtime: 0.into(),
@@ -175,12 +186,12 @@ impl Default for Node {
atime: 0.into(),
atime_nsec: 0.into(),
record_level: 0.into(),
padding: [0; BLOCK_SIZE as usize - 4094],
padding: [0; BLOCK_SIZE as usize - 4038],
level0: [BlockPtr::default(); 128],
level1: [BlockPtr::default(); 64],
level2: [BlockPtr::default(); 32],
level3: [BlockPtr::default(); 16],
level4: [BlockPtr::default(); 12],
level4: [BlockPtr::default(); 8],
}
}
}
@@ -253,6 +264,11 @@ impl Node {
self.size.to_ne()
}
/// The disk usage of this file, in blocks.
pub fn blocks(&self) -> u64 {
self.blocks.to_ne()
}
pub fn ctime(&self) -> (u64, u32) {
(self.ctime.to_ne(), self.ctime_nsec.to_ne())
}
@@ -289,6 +305,10 @@ impl Node {
self.size = size.into();
}
pub fn set_blocks(&mut self, blocks: u64) {
self.blocks = blocks.into();
}
pub fn set_mtime(&mut self, mtime: u64, mtime_nsec: u32) {
self.mtime = mtime.into();
self.mtime_nsec = mtime_nsec.into();
+2 -2
View File
@@ -1,6 +1,6 @@
use crate::htree::{HTreeHash, HTreeNode, HTreePtr, HTREE_IDX_ENTRIES};
use crate::{
unmount_path, BlockAddr, BlockData, BlockLevel, BlockPtr, DirEntry, DirList, DiskMemory,
unmount_path, BlockAddr, BlockData, BlockMeta, BlockPtr, DirEntry, DirList, DiskMemory,
DiskSparse, FileSystem, Node, TreePtr, ALLOC_GC_THRESHOLD, BLOCK_SIZE,
};
use core::panic::AssertUnwindSafe;
@@ -515,7 +515,7 @@ fn create_minimal_l2_htree(
let mut parent = tx.read_tree(parent_ptr).unwrap();
let child1_block_data = BlockData::new(
unsafe { tx.allocate(BlockLevel::default()) }.unwrap(),
unsafe { tx.allocate(BlockMeta::default()) }.unwrap(),
Node::new(
Node::MODE_FILE,
parent.data().uid(),
+61 -25
View File
@@ -84,7 +84,7 @@ impl<'a, D: Disk> Transaction<'a, D> {
let mut found = false;
for i in (0..self.allocator_log.len()).rev() {
let entry = self.allocator_log[i];
if entry.index() == addr.index() && entry.count() == -addr.level().blocks() {
if entry.index() == addr.index() && entry.count() == -addr.level().blocks::<i64>() {
found = true;
self.allocator_log.remove(i);
break;
@@ -100,14 +100,44 @@ impl<'a, D: Disk> Transaction<'a, D> {
}
}
fn deallocate_block<T: BlockTrait>(&mut self, ptr: BlockPtr<T>) {
if !ptr.is_null() {
unsafe {
self.deallocate(ptr.addr());
}
unsafe fn node_allocate(
&mut self,
node: &mut TreeData<Node>,
meta: BlockMeta,
) -> Result<BlockAddr> {
let addr = self.allocate(meta)?;
let blocks = node.data().blocks();
node.data_mut().set_blocks(
blocks
.checked_add(addr.level().blocks())
.expect("node allocated too many blocks"),
);
Ok(addr)
}
unsafe fn node_deallocate(&mut self, node: &mut TreeData<Node>, addr: BlockAddr) -> bool {
if !addr.is_null() {
self.deallocate(addr);
let blocks = node.data().blocks();
node.data_mut().set_blocks(
blocks
.checked_sub(addr.level().blocks())
.expect("node deallocated too many blocks"),
);
true
} else {
false
}
}
unsafe fn node_deallocate_block<T: BlockTrait>(
&mut self,
node: &mut TreeData<Node>,
ptr: BlockPtr<T>,
) -> bool {
self.node_deallocate(node, ptr.addr())
}
/// Drain `self.allocator_log` and `self.deallocate`,
/// updating the [`AllocList`] with the resulting state.
///
@@ -1343,13 +1373,15 @@ impl<'a, D: Disk> Transaction<'a, D> {
unsafe {
match NodeLevel::new(record_offset).ok_or(Error::new(ERANGE))? {
NodeLevel::L0(i0) => {
self.deallocate_block(node.data_mut().level0[i0].clear());
let ptr = node.data_mut().level0[i0].clear();
self.node_deallocate_block(node, ptr);
}
NodeLevel::L1(i1, i0) => {
let mut l0 = self.read_block_or_empty(node.data().level1[i1])?;
self.deallocate_block(l0.data_mut().ptrs[i0].clear());
self.node_deallocate_block(node, l0.data_mut().ptrs[i0].clear());
if l0.data().is_empty() {
self.deallocate_block(node.data_mut().level1[i1].clear());
let ptr = node.data_mut().level1[i1].clear();
self.node_deallocate_block(node, ptr);
} else {
node.data_mut().level1[i1] = self.sync_block(l0)?;
}
@@ -1357,14 +1389,15 @@ impl<'a, D: Disk> Transaction<'a, D> {
NodeLevel::L2(i2, i1, i0) => {
let mut l1 = self.read_block_or_empty(node.data().level2[i2])?;
let mut l0 = self.read_block_or_empty(l1.data().ptrs[i1])?;
self.deallocate_block(l0.data_mut().ptrs[i0].clear());
self.node_deallocate_block(node, l0.data_mut().ptrs[i0].clear());
if l0.data().is_empty() {
self.deallocate_block(l1.data_mut().ptrs[i1].clear());
self.node_deallocate_block(node, l1.data_mut().ptrs[i1].clear());
} else {
l1.data_mut().ptrs[i1] = self.sync_block(l0)?;
}
if l1.data().is_empty() {
self.deallocate_block(node.data_mut().level2[i2].clear());
let ptr = node.data_mut().level2[i2].clear();
self.node_deallocate_block(node, ptr);
} else {
node.data_mut().level2[i2] = self.sync_block(l1)?;
}
@@ -1373,19 +1406,20 @@ impl<'a, D: Disk> Transaction<'a, D> {
let mut l2 = self.read_block_or_empty(node.data().level3[i3])?;
let mut l1 = self.read_block_or_empty(l2.data().ptrs[i2])?;
let mut l0 = self.read_block_or_empty(l1.data().ptrs[i1])?;
self.deallocate_block(l0.data_mut().ptrs[i0].clear());
self.node_deallocate_block(node, l0.data_mut().ptrs[i0].clear());
if l0.data().is_empty() {
self.deallocate_block(l1.data_mut().ptrs[i1].clear());
self.node_deallocate_block(node, l1.data_mut().ptrs[i1].clear());
} else {
l1.data_mut().ptrs[i1] = self.sync_block(l0)?;
}
if l1.data().is_empty() {
self.deallocate_block(l2.data_mut().ptrs[i2].clear());
self.node_deallocate_block(node, l2.data_mut().ptrs[i2].clear());
} else {
l2.data_mut().ptrs[i2] = self.sync_block(l1)?;
}
if l2.data().is_empty() {
self.deallocate_block(node.data_mut().level3[i3].clear());
let ptr = node.data_mut().level3[i3].clear();
self.node_deallocate_block(node, ptr);
} else {
node.data_mut().level3[i3] = self.sync_block(l2)?;
}
@@ -1395,24 +1429,25 @@ impl<'a, D: Disk> Transaction<'a, D> {
let mut l2 = self.read_block_or_empty(l3.data().ptrs[i3])?;
let mut l1 = self.read_block_or_empty(l2.data().ptrs[i2])?;
let mut l0 = self.read_block_or_empty(l1.data().ptrs[i1])?;
self.deallocate_block(l0.data_mut().ptrs[i0].clear());
self.node_deallocate_block(node, l0.data_mut().ptrs[i0].clear());
if l0.data().is_empty() {
self.deallocate_block(l1.data_mut().ptrs[i1].clear());
self.node_deallocate_block(node, l1.data_mut().ptrs[i1].clear());
} else {
l1.data_mut().ptrs[i1] = self.sync_block(l0)?;
}
if l1.data().is_empty() {
self.deallocate_block(l2.data_mut().ptrs[i2].clear());
self.node_deallocate_block(node, l2.data_mut().ptrs[i2].clear());
} else {
l2.data_mut().ptrs[i2] = self.sync_block(l1)?;
}
if l2.data().is_empty() {
self.deallocate_block(l3.data_mut().ptrs[i3].clear());
self.node_deallocate_block(node, l3.data_mut().ptrs[i3].clear());
} else {
l3.data_mut().ptrs[i3] = self.sync_block(l2)?;
}
if l3.data().is_empty() {
self.deallocate_block(node.data_mut().level4[i4].clear());
let ptr = node.data_mut().level4[i4].clear();
self.node_deallocate_block(node, ptr);
} else {
node.data_mut().level4[i4] = self.sync_block(l3)?;
}
@@ -1676,8 +1711,8 @@ impl<'a, D: Disk> Transaction<'a, D> {
}
// CoW record using its current level
let mut old_addr =
unsafe { record.swap_addr(self.allocate(record.addr().meta())?) };
let new_addr = unsafe { self.node_allocate(node, record.addr().meta())? };
let mut old_addr = record.swap_addr(new_addr);
// If the record was resized we need to dealloc the original ptr
if old_addr.is_null() {
@@ -1685,6 +1720,7 @@ impl<'a, D: Disk> Transaction<'a, D> {
}
// Write record to disk
//TODO: deallocate new_addr on failure?
record_ptr = unsafe { self.write_block(record)? };
// Update record pointer
@@ -1692,8 +1728,8 @@ impl<'a, D: Disk> Transaction<'a, D> {
node_changed = true;
// Deallocate old record
if !old_addr.is_null() {
unsafe { self.deallocate(old_addr) };
unsafe {
self.node_deallocate(node, old_addr);
}
}
+2 -2
View File
@@ -224,7 +224,7 @@ impl<T> Default for TreePtr<T> {
#[cfg(test)]
mod tests {
use crate::{BlockAddr, BlockData};
use crate::{BlockAddr, BlockData, BlockMeta};
use super::*;
@@ -257,7 +257,7 @@ mod tests {
}
fn mock_block(addr: u64) -> BlockPtr<BlockRaw> {
let block_addr = unsafe { BlockAddr::new(addr, BlockLevel::default()) };
let block_addr = unsafe { BlockAddr::new(addr, BlockMeta::default()) };
BlockData::empty(block_addr).unwrap().create_ptr()
}