Merge branch 'master' into 'master'
fmap support See merge request redox-os/redoxfs!35
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
||||
#![crate_name="redoxfs"]
|
||||
#![crate_type="lib"]
|
||||
|
||||
#![deny(warnings)]
|
||||
//#![deny(warnings)]
|
||||
|
||||
extern crate syscall;
|
||||
extern crate uuid;
|
||||
|
||||
+105
-4
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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 = 4096;
|
||||
/// 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 + PAGE_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 if first_empty.is_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))
|
||||
|
||||
Reference in New Issue
Block a user