blob: f1aacb0ad926d489c2d54bbc5874ee6a38e0225a [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
crate::{
log::*,
object_handle::{ObjectHandle, ReadObjectHandle},
range::RangeExt,
},
anyhow::Error,
async_trait::async_trait,
std::{
cmp::min,
ops::Range,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
},
storage_device::{
buffer::{Buffer, MutableBufferRef},
Device,
},
};
/// To read the super-block and journal, we use this handle since we cannot use StoreObjectHandle
/// until we've replayed the whole journal. Clients must supply the extents to be used.
pub struct BootstrapObjectHandle {
object_id: u64,
device: Arc<dyn Device>,
start_offset: u64,
// A list of extents we know of for the handle. The extents are all logically contiguous and
// start from |start_offset|, so we don't bother storing the logical offsets.
extents: Vec<Range<u64>>,
size: u64,
trace: AtomicBool,
}
impl BootstrapObjectHandle {
pub fn new(object_id: u64, device: Arc<dyn Device>) -> Self {
Self {
object_id,
device,
start_offset: 0,
extents: Vec::new(),
size: 0,
trace: AtomicBool::new(false),
}
}
pub fn new_with_start_offset(
object_id: u64,
device: Arc<dyn Device>,
start_offset: u64,
) -> Self {
Self {
object_id,
device,
start_offset,
extents: Vec::new(),
size: 0,
trace: AtomicBool::new(false),
}
}
pub fn push_extent(&mut self, r: Range<u64>) {
self.size += r.length().unwrap();
self.extents.push(r);
}
// Discard any extents whose logical offset succeeds |offset|.
pub fn discard_extents(&mut self, discard_offset: u64) {
let mut offset = self.start_offset + self.size;
let mut num = 0;
while let Some(extent) = self.extents.last() {
let length = extent.length().unwrap();
offset = offset.checked_sub(length).unwrap();
if offset < discard_offset {
break;
}
self.size -= length;
self.extents.pop();
num += 1;
}
if self.trace.load(Ordering::Relaxed) {
info!(count = num, offset = discard_offset, "JH: Discarded extents");
}
}
pub fn start_offset(&self) -> u64 {
self.start_offset
}
}
impl ObjectHandle for BootstrapObjectHandle {
fn object_id(&self) -> u64 {
self.object_id
}
fn allocate_buffer(&self, size: usize) -> Buffer<'_> {
self.device.allocate_buffer(size)
}
fn block_size(&self) -> u64 {
self.device.block_size().into()
}
fn get_size(&self) -> u64 {
self.size
}
fn set_trace(&self, trace: bool) {
let old_value = self.trace.swap(trace, Ordering::Relaxed);
if trace != old_value {
info!(oid = self.object_id, trace, "JH: trace");
}
}
}
#[async_trait]
impl ReadObjectHandle for BootstrapObjectHandle {
async fn read(&self, mut offset: u64, mut buf: MutableBufferRef<'_>) -> Result<usize, Error> {
assert!(offset >= self.start_offset);
let trace = self.trace.load(Ordering::Relaxed);
if trace {
info!(len = buf.len(), offset, "JH: read");
}
let len = buf.len();
let mut buf_offset = 0;
let mut file_offset = self.start_offset;
for extent in &self.extents {
let extent_len = extent.end - extent.start;
if offset < file_offset + extent_len {
if trace {
info!(?extent, "JH: matching extent");
}
let device_offset = extent.start + offset - file_offset;
let to_read = min(extent.end - device_offset, (len - buf_offset) as u64) as usize;
assert!(buf_offset % self.device.block_size() as usize == 0);
self.device
.read(
device_offset,
buf.reborrow().subslice_mut(buf_offset..buf_offset + to_read),
)
.await?;
buf_offset += to_read;
if buf_offset == len {
break;
}
offset += to_read as u64;
}
file_offset += extent_len;
}
Ok(len)
}
}