use std::{cmp, mem};
use std::rc::Rc;
use super::dmu_objset::ObjectSet;
use super::from_bytes::FromBytes;
use super::metaslab::{Metaslab, MetaslabClass, MetaslabGroup};
use super::nvpair::{NvList, NvValue};
use super::uberblock;
use super::util;
use super::vdev_file::VdevFile;
use super::zfs;
#[repr(packed)]
pub struct VdevLabel {
pub blank: [u8; 8 * 1024],
pub boot_header: [u8; 8 * 1024],
pub nv_pairs: [u8; 112 * 1024],
pub uberblocks: [u8; 128 * 1024],
}
impl FromBytes for VdevLabel {}
pub trait IVdevOps {
fn open(&mut self, vdev: &mut Vdev) -> zfs::Result<(u64, u64, u64)>;
fn close(&mut self, vdev: &mut Vdev);
fn asize(&mut self, vdev: &mut Vdev, psize: u64) -> u64;
fn hold(&mut self, vdev: &mut Vdev);
fn release(&mut self, vdev: &mut Vdev);
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum VdevType {
Disk,
File,
Mirror,
Raidz,
Replacing,
Root,
}
impl VdevType {
fn to_str(self) -> &'static str {
match self {
VdevType::Disk => "disk",
VdevType::File => "file",
VdevType::Mirror => "mirror",
VdevType::Raidz => "raidz",
VdevType::Replacing => "replacing",
VdevType::Root => "root",
}
}
}
pub struct VdevOps {
pub ops: Box<IVdevOps>,
vdev_type: VdevType,
is_leaf: bool,
}
impl VdevOps {
pub fn vdev_type(&self) -> &str {
self.vdev_type.to_str()
}
pub fn is_leaf(&self) -> bool {
self.is_leaf
}
}
fn load_ops(vdev_type: &str, nv: &NvList) -> zfs::Result<VdevOps> {
match vdev_type {
"disk" => {
Ok(VdevOps {
ops: Box::new(try!(VdevFile::load(nv))),
vdev_type: VdevType::Disk,
is_leaf: true,
})
}
_ => Err(zfs::Error::Invalid),
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum AllocType {
Load = 0,
Add,
Spare,
L2Cache,
RootPool,
Split,
Attach,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum State {
Unknown, Closed, Offline, Removed, CannotOpen, Faulted, Degraded, Healthy, }
pub struct Top {
pub ms_array: u64, pub ms_shift: u64, pub ms_group: MetaslabGroup, pub metaslabs: Vec<Metaslab>, pub is_hole: bool,
pub removing: bool, }
impl Top {
pub fn new(ms_array: u64, ms_shift: u64, ms_group: MetaslabGroup) -> Self {
Top {
ms_array: ms_array,
ms_shift: ms_shift,
ms_group: ms_group,
metaslabs: vec![],
is_hole: false, removing: false,
}
}
}
pub struct Leaf {
whole_disk: u64,
}
impl Leaf {
pub fn new() -> Self {
Leaf { whole_disk: 0 }
}
}
pub struct Vdev {
id: u64, guid: u64, guid_sum: u64, orig_guid: u64, asize: u64, min_asize: u64, max_asize: u64, pub ashift: u64, state: State,
prev_state: State,
pub ops: VdevOps,
parent: Option<TreeIndex>,
top_vdev: Option<TreeIndex>,
children: Vec<TreeIndex>,
create_txg: u64,
pub top: Option<Top>,
pub leaf: Option<Leaf>,
}
impl Vdev {
pub fn new(id: u64,
guid: Option<u64>,
ashift: u64,
ops: VdevOps,
create_txg: u64,
vdev_top: Option<Top>)
-> Self {
let guid = guid.unwrap_or_else(|| {
0
});
Vdev {
id: id,
guid: guid,
guid_sum: guid, orig_guid: 0,
asize: 0,
min_asize: 0,
max_asize: 0,
ashift: ashift,
state: State::Closed,
prev_state: State::Unknown,
ops: ops,
parent: None,
top_vdev: None,
children: Vec::new(),
create_txg: create_txg,
top: vdev_top,
leaf: None,
}
}
pub fn load(normal_class: &Rc<MetaslabClass>,
nv: &NvList,
id: u64,
parent: Option<TreeIndex>,
vdev_tree: &Tree,
alloc_type: AllocType)
-> zfs::Result<Self> {
let vdev_type = try!(nv.get::<&String>("type").ok_or(zfs::Error::Invalid)).clone();
let ops = try!(load_ops(vdev_type.as_ref(), nv));
if alloc_type == AllocType::Load {
let label_id: u64 = try!(nv.get("id").ok_or(zfs::Error::Invalid));
if label_id != id {
return Err(zfs::Error::Invalid);
}
}
let guid = match alloc_type {
AllocType::Load | AllocType::Spare | AllocType::L2Cache | AllocType::RootPool => {
Some(try!(nv.get("guid").ok_or(zfs::Error::Invalid)))
}
_ => None,
};
let create_txg = try!(nv.get("create_txg").ok_or(zfs::Error::Invalid));
let ashift = try!(nv.get("ashift").ok_or(zfs::Error::Invalid));
let mut vdev_top = None;
if let Some(parent) = parent {
if parent.get(vdev_tree).parent.is_none() {
let mut ms_array = 0;
let mut ms_shift = 0;
if alloc_type == AllocType::Load || alloc_type == AllocType::Split {
ms_array = try!(nv.get("metaslab_array").ok_or(zfs::Error::Invalid));
ms_shift = try!(nv.get("metaslab_shift").ok_or(zfs::Error::Invalid));
}
if alloc_type != AllocType::Attach {
assert!(alloc_type == AllocType::Load || alloc_type == AllocType::Add ||
alloc_type == AllocType::Split ||
alloc_type == AllocType::RootPool);
let ms_group = MetaslabGroup::create(normal_class.clone());
vdev_top = Some(Top::new(ms_array, ms_shift, ms_group));
}
}
}
let mut vdev = Self::new(id, guid, ashift, ops, create_txg, vdev_top);
vdev.parent = parent;
Ok(vdev)
}
fn open(&mut self) -> zfs::Result<()> {
Ok(())
}
fn metaslab_init(&mut self, mos: &mut ObjectSet, txg: u64) -> zfs::Result<()> {
let ref mut top = try!(self.top.as_mut().ok_or(zfs::Error::Invalid));
let old_count = top.metaslabs.len();
let new_count = (self.asize >> top.ms_shift) as usize;
if top.ms_shift == 0 {
return Ok(());
}
assert!(!top.is_hole);
assert!(old_count <= new_count);
for m in old_count..new_count {
let object: u64 = 0;
if txg == 0 {
}
}
if old_count == 0 && !top.removing {
}
Ok(())
}
pub fn uberblock_shift(&self) -> u64 {
cmp::min(cmp::max(self.ashift, uberblock::UBERBLOCK_SHIFT),
MAX_UBERBLOCK_SHIFT)
}
pub fn uberblock_count(&self) -> u64 {
UBERBLOCK_RING >> self.uberblock_shift()
}
pub fn uberblock_size(&self) -> u64 {
1 << self.uberblock_shift()
}
}
#[derive(Copy, Clone, PartialEq)]
pub struct TreeIndex(usize);
impl TreeIndex {
pub fn get<'a>(&self, tree: &'a Tree) -> &'a Vdev {
tree.nodes[self.0].as_ref().unwrap()
}
pub fn get_mut<'a>(&self, tree: &'a mut Tree) -> &'a mut Vdev {
tree.nodes[self.0].as_mut().unwrap()
}
}
pub struct Tree {
nodes: Vec<Option<Vdev>>,
free: Vec<usize>,
}
impl Tree {
pub fn new() -> Self {
Tree {
nodes: Vec::new(),
free: Vec::new(),
}
}
pub fn add(&mut self, vdev: Vdev) -> TreeIndex {
let parent = vdev.parent;
let guid = vdev.guid;
let index = TreeIndex(match self.free.pop() {
Some(free_index) => {
self.nodes[free_index] = Some(vdev);
free_index
}
None => {
self.nodes.push(Some(vdev));
self.nodes.len() - 1
}
});
index.get_mut(self).top_vdev = parent.map(|parent| {
parent.get(self).top_vdev.unwrap_or(index)
});
if let Some(parent) = parent {
parent.get_mut(self).guid_sum += guid;
parent.get_mut(self).children.push(index);
}
index
}
pub fn parse(&mut self,
normal_class: &Rc<MetaslabClass>,
nv: &NvList,
parent: Option<TreeIndex>,
alloc_type: AllocType)
-> zfs::Result<TreeIndex> {
let vdev = try!(Vdev::load(normal_class, nv, 0, parent, self, alloc_type));
let index = self.add(vdev);
if index.get(self).ops.is_leaf() {
return Ok(index);
}
let children: &Vec<NvList> = try!(nv.get("children").ok_or(zfs::Error::Invalid));
for child in children {
self.parse(normal_class, child, Some(index), alloc_type);
}
Ok(index)
}
pub fn load(&mut self, mos: &mut ObjectSet, root: TreeIndex) {
let mut queue = vec![root];
while let Some(index) = queue.pop() {
let vdev = index.get_mut(self);
for child in &vdev.children {
queue.push(*child);
}
if vdev.top.is_some() {
if vdev.ashift == 0 || vdev.asize == 0 || vdev.metaslab_init(mos, 0).is_err() {
}
}
}
}
}
const DIRTY_METASLAB: u64 = 0x01;
const DIRTY_DTL: u64 = 0x02;
const RAIDZ_MAXPARITY: usize = 3;
const PAD_SIZE: u64 = 8 << 10;
const SKIP_SIZE: u64 = PAD_SIZE * 2;
const PHYS_SIZE: u64 = 112 << 10;
const UBERBLOCK_RING: u64 = 128 << 10;
const MAX_UBERBLOCK_SHIFT: u64 = 13;