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,
/// Target path for symbolic links
pub target: Option<PathBuf>,
/// Start offset of the payload if present.
pub payload_offset: Option<u64>,
}
/// Trait to create ReadSeek Decoder trait objects.
@ -68,9 +70,9 @@ impl Decoder {
check_ca_header::<PxarEntry>(&header, PXAR_ENTRY)?;
let entry: PxarEntry = self.inner.read_item()?;
let (header, xattr) = self.inner.read_attributes()?;
let size = match header.htype {
PXAR_PAYLOAD => header.size - HEADER_SIZE,
_ => 0,
let (size, payload_offset) = match header.htype {
PXAR_PAYLOAD => (header.size - HEADER_SIZE, Some(self.seek(SeekFrom::Current(0))?)),
_ => (0, None),
};
Ok(DirectoryEntry {
@ -81,6 +83,7 @@ impl Decoder {
xattr,
size,
target: None,
payload_offset,
})
}
@ -134,9 +137,9 @@ impl Decoder {
check_ca_header::<PxarEntry>(&head, PXAR_ENTRY)?;
let entry: PxarEntry = self.inner.read_item()?;
let (header, xattr) = self.inner.read_attributes()?;
let size = match header.htype {
PXAR_PAYLOAD => header.size - HEADER_SIZE,
_ => 0,
let (size, payload_offset) = match header.htype {
PXAR_PAYLOAD => (header.size - HEADER_SIZE, Some(self.seek(SeekFrom::Current(0))?)),
_ => (0, None),
};
let target = match header.htype {
PXAR_SYMLINK => Some(self.inner.read_link(header.size)?),
@ -151,6 +154,7 @@ impl Decoder {
xattr,
size,
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,
/// check if there is indeed a valid item with payload and then read `size`
/// bytes of content starting from `data_offset`.
/// If EOF is reached before reading `size` bytes, the reduced buffer is
/// returned.
pub fn read(&mut self, offset: u64, size: usize, data_offset: u64) -> Result<Vec<u8>, Error> {
self.seek(SeekFrom::Start(offset))?;
let head: PxarHeader = self.inner.read_item()?;
if head.htype != PXAR_FILENAME {
bail!("Expected PXAR_FILENAME, encountered 0x{:x?}", head.htype);
}
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 {
/// This will read a files payload as raw bytes starting from `offset` after
/// the payload marker, reading `size` bytes.
/// If the payload from `offset` to EOF is smaller than `size` bytes, the
/// buffer with reduced size is returned.
/// If `offset` is larger than the payload size of the `DirectoryEntry`, an
/// empty buffer is returned.
pub fn read(&mut self, entry: &DirectoryEntry, size: usize, offset: u64) -> Result<Vec<u8>, Error> {
let start_offset = entry.payload_offset
.ok_or_else(|| format_err!("entry has no payload offset"))?;
if offset >= entry.size {
return Ok(Vec::new());
}
let len = if data_offset + u64::try_from(size)? > payload_size {
usize::try_from(payload_size - data_offset)?
let len = if u64::try_from(size)? > entry.size {
usize::try_from(entry.size)?
} else {
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)?;
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) {
Self::run_in_context(req, inode, |ctx| {
let mut data = ctx.decoder.read(ctx.ino_offset, size, offset as u64).map_err(|_| libc::EIO)?;
Self::run_with_context_refs(req, inode, |decoder, map, _gbt_cache, entry_cache, ino_offset| {
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 len = data.len();