#![allow(non_snake_case)]
use crate::allocator::Allocator;
use crate::backing_store::file::FileBackingStore;
#[cfg(target_os = "linux")]
use crate::backing_store::uring::UringBackingStore;
#[cfg(target_os = "linux")]
use crate::backing_store::uring::UringCfg;
use crate::backing_store::BackingStore;
use crate::object::LPAGE_SIZE_POW2;
use crate::object::SPAGE_SIZE_POW2_MIN;
use crate::pages::Pages;
use crate::util::ceil_pow2;
use crate::util::floor_pow2;
use backing_store::BoundedStore;
use ctx::Ctx;
use log_buffer::LogBuffer;
use metrics::BlobdMetrics;
use object::format_device_for_tuples;
use object::load_tuples_from_device;
use op::delete_object::op_delete_object;
use op::delete_object::OpDeleteObjectInput;
use op::delete_object::OpDeleteObjectOutput;
use op::read_object::op_read_object;
use op::read_object::OpReadObjectInput;
use op::read_object::OpReadObjectOutput;
use op::write_object::op_write_object;
use op::write_object::OpWriteObjectInput;
use op::write_object::OpWriteObjectOutput;
use op::OpResult;
use parking_lot::Mutex;
use std::fs::OpenOptions;
#[cfg(target_os = "linux")]
use std::os::unix::prelude::OpenOptionsExt;
use std::path::PathBuf;
use std::sync::Arc;
use tokio::join;
use tracing::info;
pub mod allocator;
pub mod backing_store;
pub mod ctx;
pub mod log_buffer;
pub mod metrics;
pub mod object;
pub mod op;
pub mod pages;
pub mod util;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum BlobdCfgBackingStore {
#[cfg(target_os = "linux")]
Uring,
File,
}
#[derive(Clone, Debug)]
pub struct BlobdCfg {
pub backing_store: BlobdCfgBackingStore,
pub device_path: PathBuf,
pub device_len: u64,
pub log_buffer_commit_threshold: u64,
pub log_buffer_size: u64,
pub object_tuples_area_reserved_space: u64,
pub spage_size_pow2: u8,
#[cfg(target_os = "linux")]
pub uring_coop_taskrun: bool,
#[cfg(target_os = "linux")]
pub uring_defer_taskrun: bool,
#[cfg(target_os = "linux")]
pub uring_iopoll: bool,
#[cfg(target_os = "linux")]
pub uring_sqpoll: Option<u32>,
}
impl BlobdCfg {
pub fn lpage_size(&self) -> u64 {
1 << LPAGE_SIZE_POW2
}
pub fn spage_size(&self) -> u64 {
1 << self.spage_size_pow2
}
}
pub struct BlobdLoader {
cfg: BlobdCfg,
dev: Arc<dyn BackingStore>,
pages: Pages,
metrics: BlobdMetrics,
heap_dev_offset: u64,
heap_size: u64,
log_commit_threshold: u64,
log_data_dev_offset: u64,
log_data_size: u64,
log_state_dev_offset: u64,
}
impl BlobdLoader {
pub fn new(cfg: BlobdCfg) -> Self {
assert!(cfg.spage_size_pow2 >= SPAGE_SIZE_POW2_MIN);
assert!(cfg.spage_size_pow2 <= LPAGE_SIZE_POW2);
let dev_end_aligned = floor_pow2(cfg.device_len, cfg.spage_size_pow2);
let log_data_size = cfg.log_buffer_size;
let log_commit_threshold = cfg.log_buffer_commit_threshold;
assert!(log_data_size > 1024 * 1024 * 64); assert!(log_commit_threshold < log_data_size);
let tuples_area_size = ceil_pow2(cfg.object_tuples_area_reserved_space, LPAGE_SIZE_POW2);
let heap_dev_offset = tuples_area_size;
let log_state_dev_offset = dev_end_aligned.checked_sub(cfg.spage_size()).unwrap();
let log_data_dev_offset = log_state_dev_offset.checked_sub(log_data_size).unwrap();
let heap_size = floor_pow2(
log_state_dev_offset.checked_sub(heap_dev_offset).unwrap(),
LPAGE_SIZE_POW2,
);
assert!(heap_size > 1024 * 1024 * 64);
let metrics = BlobdMetrics::default();
let pages = Pages::new(cfg.spage_size_pow2, LPAGE_SIZE_POW2);
let file = {
let mut opt = OpenOptions::new();
opt.read(true).write(true);
#[cfg(target_os = "linux")]
opt.custom_flags(libc::O_DIRECT);
opt.open(&cfg.device_path).unwrap()
};
let dev: Arc<dyn BackingStore> = match cfg.backing_store {
#[cfg(target_os = "linux")]
BlobdCfgBackingStore::Uring => Arc::new(UringBackingStore::new(
file,
pages.clone(),
metrics.clone(),
UringCfg {
coop_taskrun: cfg.uring_coop_taskrun,
defer_taskrun: cfg.uring_defer_taskrun,
iopoll: cfg.uring_iopoll,
sqpoll: cfg.uring_sqpoll,
},
)),
BlobdCfgBackingStore::File => Arc::new(FileBackingStore::new(file, pages.clone())),
};
Self {
cfg,
dev,
heap_dev_offset,
heap_size,
log_commit_threshold,
log_data_dev_offset,
log_data_size,
log_state_dev_offset,
metrics,
pages,
}
}
pub async fn format(&self) {
format_device_for_tuples(&self.dev, &self.pages, self.heap_dev_offset).await;
LogBuffer::format_device(
&BoundedStore::new(
self.dev.clone(),
self.log_state_dev_offset,
self.pages.spage_size(),
),
&self.pages,
)
.await;
self.dev.sync().await;
}
pub async fn load_and_start(self) -> Blobd {
info!(
heap_dev_offset = self.heap_dev_offset,
heap_size = self.heap_size,
log_data_dev_offset = self.log_data_dev_offset,
log_data_size = self.log_data_size,
log_state_dev_offset = self.log_state_dev_offset,
"loading blobd",
);
let heap_allocator = Arc::new(Mutex::new(Allocator::new(
self.heap_dev_offset,
self.heap_size,
self.pages.clone(),
self.metrics.clone(),
)));
let (_, log_buffer) = join! {
load_tuples_from_device(
&self.dev,
&self.pages,
&self.metrics,
heap_allocator.clone(),
self.heap_dev_offset,
),
LogBuffer::load_from_device(
self.dev.clone(),
BoundedStore::new(self.dev.clone(), 0, self.heap_dev_offset),
BoundedStore::new(
self.dev.clone(),
self.log_data_dev_offset,
self.log_data_size,
),
BoundedStore::new(
self.dev.clone(),
self.log_state_dev_offset,
self.pages.spage_size(),
),
heap_allocator.clone(),
self.pages.clone(),
self.metrics.clone(),
self.heap_dev_offset / self.pages.spage_size(),
self.log_commit_threshold,
),
};
log_buffer.start_background_threads().await;
let ctx = Arc::new(Ctx {
log_buffer,
device: self.dev,
heap_allocator,
metrics: self.metrics.clone(),
pages: self.pages,
});
Blobd {
cfg: self.cfg,
ctx,
metrics: self.metrics,
}
}
}
#[derive(Clone)]
pub struct Blobd {
cfg: BlobdCfg,
ctx: Arc<Ctx>,
metrics: BlobdMetrics,
}
impl Blobd {
pub fn cfg(&self) -> &BlobdCfg {
&self.cfg
}
pub fn metrics(&self) -> &BlobdMetrics {
&self.metrics
}
pub async fn wait_for_any_current_log_buffer_commit(&self) {
self.ctx.log_buffer.wait_for_any_current_commit().await;
}
pub async fn delete_object(&self, input: OpDeleteObjectInput) -> OpResult<OpDeleteObjectOutput> {
op_delete_object(self.ctx.clone(), input).await
}
pub async fn read_object(&self, input: OpReadObjectInput) -> OpResult<OpReadObjectOutput> {
op_read_object(self.ctx.clone(), input).await
}
pub async fn write_object(&self, input: OpWriteObjectInput) -> OpResult<OpWriteObjectOutput> {
op_write_object(self.ctx.clone(), input).await
}
}