Skip to content

Commit

Permalink
Going to call this v0.0.1 cuz it works
Browse files Browse the repository at this point in the history
  • Loading branch information
Colum McGaley committed May 14, 2024
1 parent 5d7b65d commit e9f8b19
Show file tree
Hide file tree
Showing 15 changed files with 434 additions and 228 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "shmr"
version = "0.1.0"
version = "0.0.1"
edition = "2021"

[target.x86_64-unknown-linux-gnu]
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
A Purpose Built filesystem for use with SMR Hard Drives.

**Prototype Stage** If you use this for actual workloads, I'm going to laugh at you.


Unlike a traditional file system, Shmr acts as a Virtual Filesystem on top of a standard file system. Shmr stores files
as a series of fixed size blocks, and uses an internal key/value store to map the blocks to the underlying storage.

Expand Down
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Goal. Full FUSE implementation that only uses the workspace/blocks directory to
- [ ] Truncate

- [ ] Rebuild decode utility
- [X] Test moving file from a Single file to Erasure Format
- [ ] Test moving file from a Single file to Erasure Format
- [ ] Garbage Collect Filehandles
- [ ] Garbage Collect Cached database entries
- [ ] Copy on Write for Erasure Blocks
Expand Down
8 changes: 3 additions & 5 deletions src/bin/shmr_fuse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ use clap::Parser;
use fuser::MountOption;
use log::{error, LevelFilter};
use serde::{Deserialize, Serialize};
use shmr::fuse::ShmrFuse;
use shmr::{build_poolmap, PoolMap};
use shmr::build_poolmap;
use std::path::PathBuf;
use shmr::kernel::Kernel;
use shmr::fuse::ShmrFuse;

#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
Expand Down Expand Up @@ -55,9 +54,8 @@ fn main() {
let mount = config.mount_dir.clone();

let pools = build_poolmap(config.write_pool.clone(), config.pools.clone());
let kernel = Kernel::new(pools);

let fs = ShmrFuse::open(config.metadata_dir, kernel).unwrap();
let fs = ShmrFuse::open(pools, config.metadata_dir).unwrap();
let result = fuser::mount2(fs, mount, &options);
if let Err(e) = result {
// Return a special error code for permission denied, which usually indicates that
Expand Down
114 changes: 114 additions & 0 deletions src/fuse/cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use std::collections::HashMap;
use std::mem;
use std::sync::{Arc, Mutex, RwLock};
use std::sync::atomic::{AtomicUsize, Ordering};
use log::debug;
use crate::fuse::magics::FILE_CACHE_MANAGER_LOW_WATERMARK_RATIO;
use crate::vfs::VirtualFile;

#[derive(Debug, Clone)]
pub enum FileCacheStrategy {
/// Prioritize keeping Read Heavy VirtualFiles in memory
ReadCachePriority,
/// Prioritize keeping Write Heavy VirtualFiles in memory
WriteCachePriority,
/// Prioritize keeping I/O Heavy VirtualFiles in memory, ignoring the type of I/O
IOCountPriority
}

#[derive(Debug, Clone)]
pub struct FileCacheManager {
// VirtualFile, as of 2024-05-14, is not threadsafe?
// TODO Make VirtualFile threadsafe, so we don't need to have a Mutex here
entries: Arc<RwLock<HashMap<u64, Arc<Mutex<VirtualFile>>>>>,

strategy: Arc<Mutex<FileCacheStrategy>>,

/// The memory usage target
memory_limit: Arc<AtomicUsize>
}

/// Thread Logic:
/// - Get memory size of entries. If it is below the watermark, do nothing.
/// - Iterate over the BTreeMap and get a copy of all the VirtualFile entries
/// - For each entry, lock the handle and call .iostat() and store the entries in a Vec
/// - Sort the Vec according to FileCacheStrategy
/// - Unload each VirtualFile, in order of highest to lowest score
fn cache_worker(mgr: FileCacheManager) {
fn read_size(mgr: &FileCacheManager) -> usize {
{
let block_cache = mgr.entries.read().unwrap();
mem::size_of_val(&*block_cache)
}
}
debug!("block cache current size: {} bytes", read_size(&mgr));
let memory_limit = mgr.memory_limit.load(Ordering::Relaxed);
if read_size(&mgr) > memory_limit {
let mem_target = (memory_limit as f32 * FILE_CACHE_MANAGER_LOW_WATERMARK_RATIO) as usize;

let mut entries: Vec<(u64, Arc<Mutex<VirtualFile>>)> = {
let block_cache = mgr.entries.read().unwrap();
block_cache.iter().map(|(k, v)| (*k, Arc::clone(v))).collect()
};
entries.sort_by(|(_, a), (_, b)| {
let (a_instant, a_read_count, a_write_count) = a.lock().unwrap().iostat();
let (b_instant, b_read_count, b_write_count) = b.lock().unwrap().iostat();
match *mgr.strategy.lock().unwrap() {
FileCacheStrategy::ReadCachePriority => {
a_instant.cmp(&b_instant)
}
FileCacheStrategy::WriteCachePriority => {
(a_write_count + a_read_count).cmp(&(b_write_count + b_read_count))
}
FileCacheStrategy::IOCountPriority => {
(a_write_count + a_read_count).cmp(&(b_write_count + b_read_count))
}
}
});
// for (key, entry) in entries {
// let mut vf = entry.lock().unwrap();
// vf.unload().unwrap();
// let size = vf.size();
// debug!("Unloaded VirtualFile with key: {}, size: {} bytes", key, size);
// }
while read_size(&mgr) > mem_target {
let (key, entry) = entries.pop().unwrap();
let vf = entry.lock().unwrap();
let vf_mem_size = mem::size_of_val(&*vf);
vf.unload().unwrap();
let new_vf_mem_size = mem::size_of_val(&*vf);
debug!("unloaded VirtualFile cache entry {}. old size: {}. new size: {} ", key, vf_mem_size, new_vf_mem_size);
}

}
todo!()
}
// fn block_cache_worker(kernel: Kernel) {
// debug!("entering block cache worker");
// fn read_size(kernel: &Kernel) -> usize {
// {
// let block_cache = kernel.block_cache.read().unwrap();
// mem::size_of_val(&*block_cache)
// }
// }
// let (a, b) = crate::kernel::tasks::split_duration(Duration::from_millis(KERNEL_BLOCK_CACHE_EVICTION_SWEEP_INTERVAL));
// let mut rng = rand::thread_rng();
// loop {
// thread::sleep(a);
// debug!("block cache current size: {} bytes", read_size(&kernel));
// // if we're past the high watermark...
// if read_size(&kernel) > KERNEL_BLOCK_CACHE_EVICTION_HIGH_MARK {
// debug!("evicting blocks from cache");
// // evict until we're below the low watermark
// while read_size(&kernel) > KERNEL_BLOCK_CACHE_EVICTION_LOW_MARK {
// // MVP eviction strategy: randomly evict a block
// let mut block_cache = kernel.block_cache.write().unwrap();
// // pick a random entry and drop it
// let idx = rng.gen_range(0..block_cache.len());
// let key = *block_cache.keys().nth(idx).unwrap();
// block_cache.remove(&key);
// }
// }
// thread::sleep(b);
// }
// }
11 changes: 11 additions & 0 deletions src/fuse/glint.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use uuid::Uuid;
use crate::fsdb::FsDB2;
use crate::fuse::types::{Inode, InodeDescriptor};
use crate::vfs::VirtualFile;

pub struct Glint {
inode_db: FsDB2<u64, Inode>,
// TODO Fold this into inode_db
descriptor_db: FsDB2<u64, InodeDescriptor>,
vfdb: FsDB2<Uuid, VirtualFile>
}
6 changes: 6 additions & 0 deletions src/fuse/magics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pub const MAX_NAME_LENGTH: u32 = 255;
pub const DEFAULT_CHUNK_SIZE: usize = 4096;

/// Run Interval for the FileCacheManager Worker thread, which
pub const FILE_CACHE_MANAGER_SWEEP_INTERVAL: usize = 500; // ms
pub const FILE_CACHE_MANAGER_LOW_WATERMARK_RATIO: f32 = 0.69420; //
Loading

0 comments on commit e9f8b19

Please sign in to comment.