Reimplement fmap/funmap using improvements to kernel

This commit is contained in:
Jeremy Soller
2019-04-06 20:11:51 -06:00
parent cb2bcf9f59
commit ff17fed199
6 changed files with 200 additions and 252 deletions
+1
View File
@@ -81,6 +81,7 @@ impl<T: Disk> Disk for DiskCache<T> {
}
fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize> {
//TODO: Write only blocks that have changed
// println!("Cache write at {}", block);
self.inner.write_at(block, buffer)?;
-2
View File
@@ -1,5 +1,3 @@
extern crate spin;
use syscall;
use syscall::{Packet, Scheme};
use std::fs::File;
+102 -97
View File
@@ -1,13 +1,14 @@
use std::cmp::{min, max};
use std::collections::BTreeMap;
use std::slice;
use std::time::{SystemTime, UNIX_EPOCH};
use syscall::data::{Map, Stat, TimeSpec};
use syscall::error::{Error, Result, EBADF, EBUSY, EINVAL, EISDIR, EPERM};
use syscall::error::{Error, Result, EBADF, EINVAL, EISDIR, ENOMEM, EPERM};
use syscall::flag::{O_ACCMODE, O_RDONLY, O_WRONLY, O_RDWR, F_GETFL, F_SETFL, MODE_PERM, PROT_READ, PROT_WRITE, 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,14 +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, map: &Map, maps: &mut Fmaps, fs: &mut FileSystem<D>) -> Result<usize>;
fn funmap(&mut self, maps: &mut Fmaps, fs: &mut FileSystem<D>) -> Result<usize>;
fn fmap(&mut self, map: &Map, fs: &mut FileSystem<D>) -> Result<usize>;
fn funmap(&mut self, address: usize, 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, maps: &mut Fmaps, fs: &mut FileSystem<D>) -> Result<usize>;
fn sync(&mut self, 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>;
}
@@ -89,10 +90,10 @@ impl<D: Disk> Resource<D> for DirResource {
Ok(self.seek)
}
fn fmap(&mut self, _map: &Map, _maps: &mut Fmaps, _fs: &mut FileSystem<D>) -> Result<usize> {
fn fmap(&mut self, _map: &Map, _fs: &mut FileSystem<D>) -> Result<usize> {
Err(Error::new(EBADF))
}
fn funmap(&mut self, _maps: &mut Fmaps, _fs: &mut FileSystem<D>) -> Result<usize> {
fn funmap(&mut self, _address: usize, _fs: &mut FileSystem<D>) -> Result<usize> {
Err(Error::new(EBADF))
}
@@ -167,7 +168,7 @@ impl<D: Disk> Resource<D> for DirResource {
Ok(0)
}
fn sync(&mut self, _maps: &mut Fmaps, _fs: &mut FileSystem<D>) -> Result<usize> {
fn sync(&mut self, _fs: &mut FileSystem<D>) -> Result<usize> {
Err(Error::new(EBADF))
}
@@ -180,13 +181,78 @@ impl<D: Disk> Resource<D> for DirResource {
}
}
pub struct Fmap {
block: u64,
offset: usize,
flags: usize,
data: &'static mut [u8],
}
impl Fmap {
pub unsafe fn new<D: Disk>(block: u64, map: &Map, fs: &mut FileSystem<D>) -> Result<Self> {
extern "C" {
fn memalign(align: usize, size: usize) -> *mut u8;
fn free(ptr: *mut u8);
}
// Memory provided to fmap must be page aligned and sized
let align = 4096;
let address = memalign(align, ((map.size + align - 1) / align) * align);
if address.is_null() {
return Err(Error::new(ENOMEM));
}
// Read buffer from disk
let buf = slice::from_raw_parts_mut(address, map.size);
let count = match fs.read_node(block, map.offset as u64, buf) {
Ok(ok) => ok,
Err(err) => {
free(address);
return Err(err);
}
};
// Make sure remaining data is zeroed
for i in count..buf.len() {
buf[i] = 0;
}
Ok(Self {
block,
offset: map.offset,
flags: map.flags,
data: &mut buf[..count],
})
}
pub fn sync<D: Disk>(&mut self, fs: &mut FileSystem<D>) -> Result<()> {
if self.flags & PROT_WRITE == PROT_WRITE {
let mtime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
fs.write_node(self.block, self.offset as u64, &self.data, mtime.as_secs(), mtime.subsec_nanos())?;
}
Ok(())
}
}
impl Drop for Fmap {
fn drop(&mut self) {
unsafe {
extern "C" {
fn free(ptr: *mut u8);
}
free(self.data.as_mut_ptr());
}
}
}
pub struct FileResource {
path: String,
block: u64,
flags: usize,
seek: u64,
uid: u32,
fmap: Option<(usize, FmapKey)>
fmaps: BTreeMap<usize, Fmap>,
}
impl FileResource {
@@ -197,35 +263,9 @@ impl FileResource {
flags: flags,
seek: seek,
uid: uid,
fmap: None
fmaps: BTreeMap::new(),
}
}
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 (key_round, value) = maps.index(*i).as_mut().expect("mapping dropped while still referenced");
let rel_offset = key_exact.offset - key_round.offset;
// Minimum out of our size and the original file size
let actual_size = (value.actual_size - rel_offset).min(key_exact.size);
let mtime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let mut count = 0;
while count < actual_size {
match fs.write_node(self.block, key_exact.offset as u64 + count as u64,
&value.buffer[rel_offset..][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 {
@@ -240,7 +280,7 @@ impl<D: Disk> Resource<D> for FileResource {
flags: self.flags,
seek: self.seek,
uid: self.uid,
fmap: None
fmaps: BTreeMap::new(),
}))
}
@@ -278,7 +318,7 @@ impl<D: Disk> Resource<D> for FileResource {
Ok(self.seek as usize)
}
fn fmap(&mut self, map: &Map, maps: &mut Fmaps, fs: &mut FileSystem<D>) -> Result<usize> {
fn fmap(&mut self, map: &Map, fs: &mut FileSystem<D>) -> Result<usize> {
let accmode = self.flags & O_ACCMODE;
if map.flags & PROT_READ > 0 && ! (accmode == O_RDWR || accmode == O_RDONLY) {
return Err(Error::new(EBADF));
@@ -288,66 +328,20 @@ impl<D: Disk> Resource<D> for FileResource {
}
//TODO: PROT_EXEC?
let key_exact = FmapKey {
block: self.block,
offset: map.offset,
size: map.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..key_round.size])? {
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))
let map = unsafe { Fmap::new(self.block, map, fs)? };
let address = map.data.as_ptr() as usize;
self.fmaps.insert(address, map);
Ok(address)
}
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;
}
fn funmap(&mut self, address: usize, fs: &mut FileSystem<D>) -> Result<usize> {
if let Some(mut fmap) = self.fmaps.remove(&address) {
fmap.sync(fs)?;
Ok(0)
} else {
Err(Error::new(EINVAL))
}
Ok(0)
}
fn fchmod(&mut self, mode: u16, fs: &mut FileSystem<D>) -> Result<usize> {
@@ -428,8 +422,11 @@ impl<D: Disk> Resource<D> for FileResource {
Ok(0)
}
fn sync(&mut self, maps: &mut Fmaps, fs: &mut FileSystem<D>) -> Result<usize> {
self.sync_fmap(maps, fs)?;
fn sync(&mut self, fs: &mut FileSystem<D>) -> Result<usize> {
for fmap in self.fmaps.values_mut() {
fmap.sync(fs)?;
}
Ok(0)
}
@@ -462,3 +459,11 @@ impl<D: Disk> Resource<D> for FileResource {
}
}
}
impl Drop for FileResource {
fn drop(&mut self) {
if ! self.fmaps.is_empty() {
eprintln!("redoxfs: file {} still has {} fmaps!", self.path, self.fmaps.len());
}
}
}
+39 -96
View File
@@ -1,6 +1,5 @@
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};
@@ -16,83 +15,13 @@ use filesystem::FileSystem;
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>>>>,
fmaps: Mutex<Fmaps>
files: RefCell<BTreeMap<usize, Box<Resource<D>>>>,
fmap: RefCell<BTreeMap<usize, usize>>,
}
impl<D: Disk> FileScheme<D> {
@@ -101,8 +30,8 @@ impl<D: Disk> FileScheme<D> {
name: name,
fs: RefCell::new(fs),
next_id: AtomicUsize::new(1),
files: Mutex::new(BTreeMap::new()),
fmaps: Mutex::new(Fmaps::default())
files: RefCell::new(BTreeMap::new()),
fmap: RefCell::new(BTreeMap::new()),
}
}
@@ -381,7 +310,7 @@ impl<D: Disk> Scheme for FileScheme<D> {
};
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
self.files.lock().insert(id, resource);
self.files.borrow_mut().insert(id, resource);
Ok(id)
}
@@ -494,7 +423,7 @@ impl<D: Disk> Scheme for FileScheme<D> {
return Err(Error::new(EINVAL));
}
let mut files = self.files.lock();
let mut files = self.files.borrow_mut();
let resource = if let Some(old_resource) = files.get(&old_id) {
old_resource.dup()?
} else {
@@ -510,7 +439,7 @@ impl<D: Disk> Scheme for FileScheme<D> {
#[allow(unused_variables)]
fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
// println!("Read {}, {:X} {}", id, buf.as_ptr() as usize, buf.len());
let mut files = self.files.lock();
let mut files = self.files.borrow_mut();
if let Some(file) = files.get_mut(&id) {
file.read(buf, &mut self.fs.borrow_mut())
} else {
@@ -520,7 +449,7 @@ impl<D: Disk> Scheme for FileScheme<D> {
fn write(&self, id: usize, buf: &[u8]) -> Result<usize> {
// println!("Write {}, {:X} {}", id, buf.as_ptr() as usize, buf.len());
let mut files = self.files.lock();
let mut files = self.files.borrow_mut();
if let Some(file) = files.get_mut(&id) {
file.write(buf, &mut self.fs.borrow_mut())
} else {
@@ -530,7 +459,7 @@ impl<D: Disk> Scheme for FileScheme<D> {
fn seek(&self, id: usize, pos: usize, whence: usize) -> Result<usize> {
// println!("Seek {}, {} {}", id, pos, whence);
let mut files = self.files.lock();
let mut files = self.files.borrow_mut();
if let Some(file) = files.get_mut(&id) {
file.seek(pos, whence, &mut self.fs.borrow_mut())
} else {
@@ -539,7 +468,7 @@ impl<D: Disk> Scheme for FileScheme<D> {
}
fn fchmod(&self, id: usize, mode: u16) -> Result<usize> {
let mut files = self.files.lock();
let mut files = self.files.borrow_mut();
if let Some(file) = files.get_mut(&id) {
file.fchmod(mode, &mut self.fs.borrow_mut())
} else {
@@ -548,7 +477,7 @@ impl<D: Disk> Scheme for FileScheme<D> {
}
fn fchown(&self, id: usize, uid: u32, gid: u32) -> Result<usize> {
let mut files = self.files.lock();
let mut files = self.files.borrow_mut();
if let Some(file) = files.get_mut(&id) {
file.fchown(uid, gid, &mut self.fs.borrow_mut())
} else {
@@ -557,7 +486,7 @@ impl<D: Disk> Scheme for FileScheme<D> {
}
fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result<usize> {
let mut files = self.files.lock();
let mut files = self.files.borrow_mut();
if let Some(file) = files.get_mut(&id) {
file.fcntl(cmd, arg)
} else {
@@ -567,7 +496,7 @@ impl<D: Disk> Scheme for FileScheme<D> {
fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
// println!("Fpath {}, {:X} {}", id, buf.as_ptr() as usize, buf.len());
let files = self.files.lock();
let files = self.files.borrow_mut();
if let Some(file) = files.get(&id) {
let name = self.name.as_bytes();
@@ -596,7 +525,7 @@ impl<D: Disk> Scheme for FileScheme<D> {
// println!("Frename {}, {} from {}, {}", id, path, uid, gid);
let files = self.files.lock();
let files = self.files.borrow_mut();
if let Some(file) = files.get(&id) {
//TODO: Check for EINVAL
// The new pathname contained a path prefix of the old, or, more generally,
@@ -692,7 +621,7 @@ impl<D: Disk> Scheme for FileScheme<D> {
fn fstat(&self, id: usize, stat: &mut Stat) -> Result<usize> {
// println!("Fstat {}, {:X}", id, stat as *mut Stat as usize);
let files = self.files.lock();
let files = self.files.borrow_mut();
if let Some(file) = files.get(&id) {
file.stat(stat, &mut self.fs.borrow_mut())
} else {
@@ -701,7 +630,7 @@ impl<D: Disk> Scheme for FileScheme<D> {
}
fn fstatvfs(&self, id: usize, stat: &mut StatVfs) -> Result<usize> {
let files = self.files.lock();
let files = self.files.borrow_mut();
if let Some(_file) = files.get(&id) {
let mut fs = self.fs.borrow_mut();
@@ -721,9 +650,9 @@ impl<D: Disk> Scheme for FileScheme<D> {
fn fsync(&self, id: usize) -> Result<usize> {
// println!("Fsync {}", id);
let mut files = self.files.lock();
let mut files = self.files.borrow_mut();
if let Some(file) = files.get_mut(&id) {
file.sync(&mut self.fmaps.lock(), &mut self.fs.borrow_mut())
file.sync(&mut self.fs.borrow_mut())
} else {
Err(Error::new(EBADF))
}
@@ -731,7 +660,7 @@ impl<D: Disk> Scheme for FileScheme<D> {
fn ftruncate(&self, id: usize, len: usize) -> Result<usize> {
// println!("Ftruncate {}, {}", id, len);
let mut files = self.files.lock();
let mut files = self.files.borrow_mut();
if let Some(file) = files.get_mut(&id) {
file.truncate(len, &mut self.fs.borrow_mut())
} else {
@@ -741,7 +670,7 @@ impl<D: Disk> Scheme for FileScheme<D> {
fn futimens(&self, id: usize, times: &[TimeSpec]) -> Result<usize> {
// println!("Futimens {}, {}", id, times.len());
let mut files = self.files.lock();
let mut files = self.files.borrow_mut();
if let Some(file) = files.get_mut(&id) {
file.utimens(times, &mut self.fs.borrow_mut())
} else {
@@ -751,19 +680,33 @@ impl<D: Disk> Scheme for FileScheme<D> {
fn fmap(&self, id: usize, map: &Map) -> Result<usize> {
// println!("Fmap {}, {:?}", id, map);
let mut files = self.files.lock();
let mut files = self.files.borrow_mut();
if let Some(file) = files.get_mut(&id) {
file.fmap(map, &mut self.fmaps.lock(), &mut self.fs.borrow_mut())
let address = file.fmap(map, &mut self.fs.borrow_mut())?;
self.fmap.borrow_mut().insert(address, id);
Ok(address)
} else {
Err(Error::new(EBADF))
}
}
fn funmap(&self, address: usize) -> Result<usize> {
if let Some(id) = self.fmap.borrow_mut().remove(&address) {
let mut files = self.files.borrow_mut();
if let Some(file) = files.get_mut(&id) {
file.funmap(address, &mut self.fs.borrow_mut())
} else {
Err(Error::new(EINVAL))
}
} else {
Err(Error::new(EINVAL))
}
}
fn close(&self, id: usize) -> Result<usize> {
// println!("Close {}", id);
let mut files = self.files.lock();
if let Some(mut file) = files.remove(&id) {
let _ = file.funmap(&mut self.fmaps.lock(), &mut self.fs.borrow_mut());
let mut files = self.files.borrow_mut();
if files.remove(&id).is_some() {
Ok(0)
} else {
Err(Error::new(EBADF))