Commit a1c69a8e authored by jD91mZM2's avatar jD91mZM2

WIP: fmap support

parent a345132e
#![crate_name="redoxfs"]
#![crate_type="lib"]
#![deny(warnings)]
//#![deny(warnings)]
extern crate syscall;
extern crate uuid;
......
......@@ -2,12 +2,13 @@ use std::cmp::{min, max};
use std::time::{SystemTime, UNIX_EPOCH};
use syscall::data::TimeSpec;
use syscall::error::{Error, Result, EBADF, EINVAL, EISDIR, EPERM};
use syscall::error::{Error, Result, EBADF, EBUSY, EINVAL, EISDIR, EPERM};
use syscall::flag::{O_ACCMODE, O_RDONLY, O_WRONLY, O_RDWR, F_GETFL, F_SETFL, MODE_PERM};
use syscall::{Stat, SEEK_SET, SEEK_CUR, SEEK_END};
use disk::Disk;
use filesystem::FileSystem;
use super::scheme::{Fmaps, FmapKey, FmapValue};
pub trait Resource<D: Disk> {
fn block(&self) -> u64;
......@@ -15,12 +16,14 @@ pub trait Resource<D: Disk> {
fn read(&mut self, buf: &mut [u8], fs: &mut FileSystem<D>) -> Result<usize>;
fn write(&mut self, buf: &[u8], fs: &mut FileSystem<D>) -> Result<usize>;
fn seek(&mut self, offset: usize, whence: usize, fs: &mut FileSystem<D>) -> Result<usize>;
fn fmap(&mut self, offset: usize, size: usize, maps: &mut Fmaps, fs: &mut FileSystem<D>) -> Result<usize>;
fn funmap(&mut self, maps: &mut Fmaps, fs: &mut FileSystem<D>) -> Result<usize>;
fn fchmod(&mut self, mode: u16, fs: &mut FileSystem<D>) -> Result<usize>;
fn fchown(&mut self, uid: u32, gid: u32, fs: &mut FileSystem<D>) -> Result<usize>;
fn fcntl(&mut self, cmd: usize, arg: usize) -> Result<usize>;
fn path(&self, buf: &mut [u8]) -> Result<usize>;
fn stat(&self, _stat: &mut Stat, fs: &mut FileSystem<D>) -> Result<usize>;
fn sync(&mut self) -> Result<usize>;
fn sync(&mut self, maps: &mut Fmaps, fs: &mut FileSystem<D>) -> Result<usize>;
fn truncate(&mut self, len: usize, fs: &mut FileSystem<D>) -> Result<usize>;
fn utimens(&mut self, times: &[TimeSpec], fs: &mut FileSystem<D>) -> Result<usize>;
}
......@@ -87,6 +90,13 @@ impl<D: Disk> Resource<D> for DirResource {
Ok(self.seek)
}
fn fmap(&mut self, _offset: usize, _size: usize, _maps: &mut Fmaps, _fs: &mut FileSystem<D>) -> Result<usize> {
Err(Error::new(EBADF))
}
fn funmap(&mut self, _maps: &mut Fmaps, _fs: &mut FileSystem<D>) -> Result<usize> {
Err(Error::new(EBADF))
}
fn fchmod(&mut self, mode: u16, fs: &mut FileSystem<D>) -> Result<usize> {
let mut node = fs.node(self.block)?;
......@@ -158,7 +168,7 @@ impl<D: Disk> Resource<D> for DirResource {
Ok(0)
}
fn sync(&mut self) -> Result<usize> {
fn sync(&mut self, _maps: &mut Fmaps, _fs: &mut FileSystem<D>) -> Result<usize> {
Err(Error::new(EBADF))
}
......@@ -177,6 +187,7 @@ pub struct FileResource {
flags: usize,
seek: u64,
uid: u32,
fmap: Option<(usize, FmapKey)>
}
impl FileResource {
......@@ -187,8 +198,31 @@ impl FileResource {
flags: flags,
seek: seek,
uid: uid,
fmap: None
}
}
fn sync_fmap<D: Disk>(&mut self, maps: &mut Fmaps, fs: &mut FileSystem<D>) -> Result<()> {
if let Some((i, key_exact)) = self.fmap.as_ref() {
let (_, value) = maps.index(*i).as_mut().expect("mapping dropped while still referenced");
// Minimum out of our size and the original file size
let actual_size = value.actual_size.min(key_exact.size);
let mut count = 0;
while count < actual_size {
let mtime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
match fs.write_node(self.block, key_exact.offset as u64 + count as u64, &value.buffer[count..actual_size],
mtime.as_secs(), mtime.subsec_nanos())? {
0 => {
eprintln!("Fmap failed to write whole buffer, encountered EOF early.");
break;
}
n => count += n,
}
}
}
Ok(())
}
}
impl<D: Disk> Resource<D> for FileResource {
......@@ -203,6 +237,7 @@ impl<D: Disk> Resource<D> for FileResource {
flags: self.flags,
seek: self.seek,
uid: self.uid,
fmap: None
}))
}
......@@ -240,6 +275,71 @@ impl<D: Disk> Resource<D> for FileResource {
Ok(self.seek as usize)
}
fn fmap(&mut self, offset: usize, size: usize, maps: &mut Fmaps, fs: &mut FileSystem<D>) -> Result<usize> {
if self.flags & O_ACCMODE == O_RDWR {
let key_exact = FmapKey {
block: self.block,
offset,
size
};
let i = match maps.find_compatible(&key_exact) {
Ok((i, (key_existing, value))) => {
value.refcount += 1;
self.fmap = Some((i, key_exact));
return Ok(value.buffer.as_ptr() as usize + (key_exact.offset - key_existing.offset))
},
Err(None) => {
// This is bad!
// We reached the limit of maps, and we can't reallocate
// because that would invalidate stuff.
// Sorry, nothing personal :(
return Err(Error::new(EBUSY))
},
Err(Some(i)) => {
// Can't do stuff in here because lifetime issues
i
}
};
let key_round = key_exact.round();
let mut content = vec![0; key_round.size];
let mut count = 0;
while count < key_round.size {
match fs.read_node(self.block, key_round.offset as u64 + count as u64, &mut content[count..])? {
0 => break,
n => count += n
}
}
let value = maps.insert(i, key_round, FmapValue {
buffer: content,
actual_size: count,
refcount: 1
});
self.fmap = Some((i, key_exact));
Ok(value.buffer.as_ptr() as usize + (key_exact.offset - key_round.offset))
} else {
Err(Error::new(EBADF))
}
}
fn funmap(&mut self, maps: &mut Fmaps, fs: &mut FileSystem<D>) -> Result<usize> {
self.sync_fmap(maps, fs)?;
if let Some((i, _)) = self.fmap.as_ref() {
let value = maps.index(*i);
let clear = {
let (_, value) = value.as_mut().expect("mapping dropped while still referenced");
value.refcount -= 1;
value.refcount == 0
};
if clear {
*value = None;
}
}
Ok(0)
}
fn fchmod(&mut self, mode: u16, fs: &mut FileSystem<D>) -> Result<usize> {
let mut node = fs.node(self.block)?;
......@@ -318,7 +418,8 @@ impl<D: Disk> Resource<D> for FileResource {
Ok(0)
}
fn sync(&mut self) -> Result<usize> {
fn sync(&mut self, maps: &mut Fmaps, fs: &mut FileSystem<D>) -> Result<usize> {
self.sync_fmap(maps, fs)?;
Ok(0)
}
......
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::result::Result as StdResult;
use std::str;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
......@@ -17,11 +18,81 @@ use node::Node;
use super::resource::{Resource, DirResource, FileResource};
use super::spin::Mutex;
/// The size to round offset/len up to.
/// This ensures more fmaps can share the same memory even with different parameters.
const PAGE_SIZE: usize = 8;
/// The max amount of fmaps that can be held simultaneously.
/// This restriction is here because we can under no circumstances reallocate,
/// that would invalidate previous mappings.
const FMAP_AMOUNT: usize = 1024;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct FmapKey {
pub block: u64,
pub offset: usize,
pub size: usize
}
impl FmapKey {
pub fn round(&self) -> FmapKey {
let remainder = self.size % PAGE_SIZE;
FmapKey {
block: self.block,
offset: self.offset - self.offset % PAGE_SIZE,
size: if remainder == 0 { self.size } else { self.size - remainder + self.size }
}
}
pub fn is_compatible(&self, other: &FmapKey) -> bool {
self.block == other.block
&& self.offset <= other.offset
&& self.offset + self.size >= other.offset + other.size
}
}
#[derive(Clone)]
pub struct FmapValue {
pub buffer: Vec<u8>,
/// The actual file length. Syncing only writes &buffer[..actual_size].
pub actual_size: usize,
pub refcount: usize
}
// NOTE: This can NOT reallocate. That would invalidate previous mappings.
pub struct Fmaps(Vec<Option<(FmapKey, FmapValue)>>);
impl Default for Fmaps {
fn default() -> Fmaps {
Fmaps(vec![None; FMAP_AMOUNT])
}
}
impl Fmaps {
pub fn find_compatible(&mut self, key: &FmapKey) -> StdResult<(usize, &mut (FmapKey, FmapValue)), Option<usize>> {
let mut first_empty = None;
for (i, entry) in self.0.iter_mut().enumerate() {
match entry {
None => first_empty = Some(i),
Some(entry) if entry.0.is_compatible(key) => return Ok((i, entry)),
_ => ()
}
}
Err(first_empty)
}
pub fn index(&mut self, index: usize) -> &mut Option<(FmapKey, FmapValue)> {
&mut self.0[index]
}
pub fn insert(&mut self, index: usize, key: FmapKey, value: FmapValue) -> &mut FmapValue {
let elem = &mut self.0[index];
assert!(elem.is_none());
*elem = Some((key, value));
&mut elem.as_mut().unwrap().1
}
}
pub struct FileScheme<D: Disk> {
name: String,
fs: RefCell<FileSystem<D>>,
next_id: AtomicUsize,
files: Mutex<BTreeMap<usize, Box<Resource<D>>>>
files: Mutex<BTreeMap<usize, Box<Resource<D>>>>,
fmaps: Mutex<Fmaps>
}
impl<D: Disk> FileScheme<D> {
......@@ -30,7 +101,8 @@ impl<D: Disk> FileScheme<D> {
name: name,
fs: RefCell::new(fs),
next_id: AtomicUsize::new(1),
files: Mutex::new(BTreeMap::new())
files: Mutex::new(BTreeMap::new()),
fmaps: Mutex::new(Fmaps::default())
}
}
......@@ -651,7 +723,7 @@ impl<D: Disk> Scheme for FileScheme<D> {
// println!("Fsync {}", id);
let mut files = self.files.lock();
if let Some(file) = files.get_mut(&id) {
file.sync()
file.sync(&mut self.fmaps.lock(), &mut self.fs.borrow_mut())
} else {
Err(Error::new(EBADF))
}
......@@ -677,10 +749,21 @@ impl<D: Disk> Scheme for FileScheme<D> {
}
}
fn fmap(&self, id: usize, offset: usize, size: usize) -> Result<usize> {
// println!("Fmap {}, {}, {}", id, offset, size);
let mut files = self.files.lock();
if let Some(file) = files.get_mut(&id) {
file.fmap(offset, size, &mut self.fmaps.lock(), &mut self.fs.borrow_mut())
} else {
Err(Error::new(EBADF))
}
}
fn close(&self, id: usize) -> Result<usize> {
// println!("Close {}", id);
let mut files = self.files.lock();
if files.remove(&id).is_some() {
if let Some(mut file) = files.remove(&id) {
let _ = file.funmap(&mut self.fmaps.lock(), &mut self.fs.borrow_mut());
Ok(0)
} else {
Err(Error::new(EBADF))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment