pxar: Improve read performance for fuse.

By storing the payload start offset in the `DirectoryEntry` and passing this
information to `Decoder::read()`, the payload can be read directly and a repeated
re-reading of the entry information is avoided.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Christian Ebner 2020-02-03 11:21:28 +01:00 committed by Wolfgang Bumiller
parent 032d3ad80f
commit 63698e720c
2 changed files with 29 additions and 41 deletions

View File

@ -33,6 +33,8 @@ pub struct DirectoryEntry {
pub size: u64, pub size: u64,
/// Target path for symbolic links /// Target path for symbolic links
pub target: Option<PathBuf>, pub target: Option<PathBuf>,
/// Start offset of the payload if present.
pub payload_offset: Option<u64>,
} }
/// Trait to create ReadSeek Decoder trait objects. /// Trait to create ReadSeek Decoder trait objects.
@ -68,9 +70,9 @@ impl Decoder {
check_ca_header::<PxarEntry>(&header, PXAR_ENTRY)?; check_ca_header::<PxarEntry>(&header, PXAR_ENTRY)?;
let entry: PxarEntry = self.inner.read_item()?; let entry: PxarEntry = self.inner.read_item()?;
let (header, xattr) = self.inner.read_attributes()?; let (header, xattr) = self.inner.read_attributes()?;
let size = match header.htype { let (size, payload_offset) = match header.htype {
PXAR_PAYLOAD => header.size - HEADER_SIZE, PXAR_PAYLOAD => (header.size - HEADER_SIZE, Some(self.seek(SeekFrom::Current(0))?)),
_ => 0, _ => (0, None),
}; };
Ok(DirectoryEntry { Ok(DirectoryEntry {
@ -81,6 +83,7 @@ impl Decoder {
xattr, xattr,
size, size,
target: None, target: None,
payload_offset,
}) })
} }
@ -134,9 +137,9 @@ impl Decoder {
check_ca_header::<PxarEntry>(&head, PXAR_ENTRY)?; check_ca_header::<PxarEntry>(&head, PXAR_ENTRY)?;
let entry: PxarEntry = self.inner.read_item()?; let entry: PxarEntry = self.inner.read_item()?;
let (header, xattr) = self.inner.read_attributes()?; let (header, xattr) = self.inner.read_attributes()?;
let size = match header.htype { let (size, payload_offset) = match header.htype {
PXAR_PAYLOAD => header.size - HEADER_SIZE, PXAR_PAYLOAD => (header.size - HEADER_SIZE, Some(self.seek(SeekFrom::Current(0))?)),
_ => 0, _ => (0, None),
}; };
let target = match header.htype { let target = match header.htype {
PXAR_SYMLINK => Some(self.inner.read_link(header.size)?), PXAR_SYMLINK => Some(self.inner.read_link(header.size)?),
@ -151,6 +154,7 @@ impl Decoder {
xattr, xattr,
size, size,
target, target,
payload_offset,
}) })
} }
@ -335,45 +339,26 @@ impl Decoder {
} }
} }
/// Read the payload of the file given by `offset`. /// Read the payload of the file given by `entry`.
/// ///
/// This will read the file by first seeking to `offset` within the archive, /// This will read a files payload as raw bytes starting from `offset` after
/// check if there is indeed a valid item with payload and then read `size` /// the payload marker, reading `size` bytes.
/// bytes of content starting from `data_offset`. /// If the payload from `offset` to EOF is smaller than `size` bytes, the
/// If EOF is reached before reading `size` bytes, the reduced buffer is /// buffer with reduced size is returned.
/// returned. /// If `offset` is larger than the payload size of the `DirectoryEntry`, an
pub fn read(&mut self, offset: u64, size: usize, data_offset: u64) -> Result<Vec<u8>, Error> { /// empty buffer is returned.
self.seek(SeekFrom::Start(offset))?; pub fn read(&mut self, entry: &DirectoryEntry, size: usize, offset: u64) -> Result<Vec<u8>, Error> {
let head: PxarHeader = self.inner.read_item()?; let start_offset = entry.payload_offset
if head.htype != PXAR_FILENAME { .ok_or_else(|| format_err!("entry has no payload offset"))?;
bail!("Expected PXAR_FILENAME, encountered 0x{:x?}", head.htype); if offset >= entry.size {
}
let _filename = self.inner.read_filename(head.size)?;
let head: PxarHeader = self.inner.read_item()?;
if head.htype == PXAR_FORMAT_HARDLINK {
let (_, diff) = self.inner.read_hardlink(head.size)?;
return self.read(offset - diff, size, data_offset);
}
check_ca_header::<PxarEntry>(&head, PXAR_ENTRY)?;
let _: PxarEntry = self.inner.read_item()?;
let (header, _) = self.inner.read_attributes()?;
if header.htype != PXAR_PAYLOAD {
bail!("Expected PXAR_PAYLOAD, found 0x{:x?}", header.htype);
}
let payload_size = header.size - HEADER_SIZE;
if data_offset >= payload_size {
return Ok(Vec::new()); return Ok(Vec::new());
} }
let len = if u64::try_from(size)? > entry.size {
let len = if data_offset + u64::try_from(size)? > payload_size { usize::try_from(entry.size)?
usize::try_from(payload_size - data_offset)?
} else { } else {
size size
}; };
self.inner.skip_bytes(usize::try_from(data_offset)?)?; self.seek(SeekFrom::Start(start_offset + offset))?;
let data = self.inner.get_reader_mut().read_exact_allocated(len)?; let data = self.inner.get_reader_mut().read_exact_allocated(len)?;
Ok(data) Ok(data)

View File

@ -538,8 +538,11 @@ impl Session {
} }
extern "C" fn read(req: Request, inode: u64, size: size_t, offset: c_int, _fileinfo: MutPtr) { extern "C" fn read(req: Request, inode: u64, size: size_t, offset: c_int, _fileinfo: MutPtr) {
Self::run_in_context(req, inode, |ctx| { Self::run_with_context_refs(req, inode, |decoder, map, _gbt_cache, entry_cache, ino_offset| {
let mut data = ctx.decoder.read(ctx.ino_offset, size, offset as u64).map_err(|_| libc::EIO)?; let entry = entry_cache.access(ino_offset, &mut EntryCacher { decoder, map })
.map_err(|_| libc::EIO)?
.ok_or_else(|| libc::EIO)?;
let mut data = decoder.read(&entry, size, offset as u64).map_err(|_| libc::EIO)?;
let _res = unsafe { let _res = unsafe {
let len = data.len(); let len = data.len();