Skip to content

Commit

Permalink
nexus: flush should always succeed
Browse files Browse the repository at this point in the history
During testing and performance benchmarking, it showed that when we
create an lvol, it does not support flush. The bdev then is exported as
a target -- over nvmf and consumed as replica.

By virtue of it being a nvme bdev, it implicitly supports flush, which
our lvol does not handle.

We must support flush and thus, simply complete the IO.

While here, i've removed some of the raw pointer deferences.
  • Loading branch information
gila committed Sep 17, 2020
1 parent 4d7d20b commit df43b77
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 165 deletions.
72 changes: 23 additions & 49 deletions mayastor/src/bdev/nexus/nexus_bdev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ use tonic::{Code, Status};
use spdk_sys::{
spdk_bdev,
spdk_bdev_desc,
spdk_bdev_flush_blocks,
spdk_bdev_io,
spdk_bdev_io_get_buf,
spdk_bdev_nvme_admin_passthru,
Expand Down Expand Up @@ -640,22 +639,23 @@ impl Nexus {
success: bool,
parent_io: *mut c_void,
) {
let mut pio = Bio(parent_io as *mut _);
let mut pio = Bio::from(parent_io);
let mut chio = Bio::from(child_io);

// if any child IO has failed record this within the io context
if !success {
trace!(
"child IO {:?} ({}) of parent {:?} failed",
Bio(child_io),
(*child_io).type_,
chio,
chio.io_type(),
pio
);

pio.ctx_as_mut_ref().status = io_status::FAILED;
}
pio.assess(child_io, success);
pio.assess(&mut chio, success);
// always free the child IO
Bio::io_free(child_io);
chio.free();
}

/// callback when the IO has buffer associated with itself
Expand All @@ -665,7 +665,7 @@ impl Nexus {
success: bool,
) {
if !success {
let bio = Bio(io);
let bio = Bio::from(io);
let nexus = bio.nexus_as_ref();
warn!("{}: Failed to get io buffer for io {:?}", nexus.name, bio);
}
Expand All @@ -674,7 +674,7 @@ impl Nexus {
let (desc, ch) = ch.ch[ch.previous].io_tuple();
let ret = Self::readv_impl(io, desc, ch);
if ret != 0 {
let bio = Bio(io);
let bio = Bio::from(io);
let nexus = bio.nexus_as_ref();
error!("{}: Failed to submit IO {:?}", nexus.name, bio);
}
Expand All @@ -691,7 +691,7 @@ impl Nexus {
if io.need_buf() {
unsafe {
spdk_bdev_io_get_buf(
io.0,
io.as_ptr(),
Some(Self::nexus_get_buf_cb),
io.num_blocks() * io.block_len(),
)
Expand All @@ -701,13 +701,13 @@ impl Nexus {

let (desc, ch) = channels.ch[child].io_tuple();

let ret = Self::readv_impl(io.0, desc, ch);
let ret = Self::readv_impl(io.as_ptr(), desc, ch);

if ret != 0 {
error!(
"{}: Failed to submit dispatched IO {:p}",
io.nexus_as_ref().name,
io.0
io.as_ptr()
);

io.fail();
Expand All @@ -721,7 +721,7 @@ impl Nexus {
desc: *mut spdk_bdev_desc,
ch: *mut spdk_io_channel,
) -> i32 {
let io = Bio(pio);
let io = Bio::from(pio);
let nexus = io.nexus_as_ref();
unsafe {
spdk_bdev_readv_blocks(
Expand All @@ -732,7 +732,7 @@ impl Nexus {
io.offset() + nexus.data_ent_offset,
io.num_blocks(),
Some(Self::io_completion),
io.0 as *mut _,
io.as_ptr() as *mut _,
)
}
}
Expand All @@ -750,7 +750,7 @@ impl Nexus {
bdev,
chan,
Some(Self::io_completion),
io.0 as *mut _,
io.as_ptr() as *mut _,
)
})
.collect::<Vec<_>>();
Expand All @@ -760,7 +760,7 @@ impl Nexus {
error!(
"{}: Failed to submit dispatched IO {:?}",
io.nexus_as_ref().name,
io.0
io.as_ptr(),
);
}
}
Expand All @@ -781,7 +781,7 @@ impl Nexus {
io.offset() + io.nexus_as_ref().data_ent_offset,
io.num_blocks(),
Some(Self::io_completion),
io.0 as *mut _,
io.as_ptr() as *mut _,
)
})
.collect::<Vec<_>>();
Expand All @@ -791,7 +791,7 @@ impl Nexus {
error!(
"{}: Failed to submit dispatched IO {:?}",
io.nexus_as_ref().name,
io.0
io.as_ptr()
);
}
}
Expand All @@ -808,7 +808,7 @@ impl Nexus {
io.offset() + io.nexus_as_ref().data_ent_offset,
io.num_blocks(),
Some(Self::io_completion),
io.0 as *mut _,
io.as_ptr() as *mut _,
)
})
.collect::<Vec<_>>();
Expand All @@ -817,33 +817,7 @@ impl Nexus {
error!(
"{}: Failed to submit dispatched IO {:?}",
io.nexus_as_ref().name,
io.0
);
}
}

pub(crate) fn flush(&self, io: &Bio, channels: &NexusChannelInner) {
let results = channels
.ch
.iter()
.map(|c| unsafe {
let (b, c) = c.io_tuple();
spdk_bdev_flush_blocks(
b,
c,
io.offset() + io.nexus_as_ref().data_ent_offset,
io.num_blocks(),
Some(Self::io_completion),
io.0 as *mut _,
)
})
.collect::<Vec<_>>();

if results.iter().any(|r| *r != 0) {
error!(
"{}: Failed to submit dispatched IO {:?}",
io.nexus_as_ref().name,
io.0
io.as_ptr()
);
}
}
Expand All @@ -860,7 +834,7 @@ impl Nexus {
io.offset() + io.nexus_as_ref().data_ent_offset,
io.num_blocks(),
Some(Self::io_completion),
io.0 as *mut _,
io.as_ptr() as *mut _,
)
})
.collect::<Vec<_>>();
Expand All @@ -869,7 +843,7 @@ impl Nexus {
error!(
"{}: Failed to submit dispatched IO {:?}",
io.nexus_as_ref().name,
io.0
io.as_ptr()
);
}
}
Expand All @@ -893,7 +867,7 @@ impl Nexus {
io.nvme_buf(),
io.nvme_nbytes(),
Some(Self::io_completion),
io.0 as *mut _,
io.as_ptr() as *mut _,
)
})
.collect::<Vec<_>>();
Expand All @@ -902,7 +876,7 @@ impl Nexus {
error!(
"{}: Failed to submit dispatched IO {:?}",
io.nexus_as_ref().name,
io.0
io.as_ptr()
);
}
}
Expand Down
110 changes: 53 additions & 57 deletions mayastor/src/bdev/nexus/nexus_fn_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,71 +96,67 @@ impl NexusFnTable {
io: *mut spdk_bdev_io,
) {
// only set the number of IO attempts before the first attempt
Bio::init(io, channel, Bio(io).nexus_as_ref().max_io_attempts);
Self::io_submit_or_resubmit(channel, io);
let mut bio = Bio::from(io);
bio.init();
Self::io_submit_or_resubmit(channel, &mut bio);
}

/// Submit an IO to the children at the first or subsequent attempts.
pub fn io_submit_or_resubmit(
pub(crate) fn io_submit_or_resubmit(
channel: *mut spdk_io_channel,
io: *mut spdk_bdev_io,
nio: &mut Bio,
) {
if let Some(io_type) = Bio::io_type(io) {
let mut ch = NexusChannel::inner_from_channel(channel);

// set the fields that need to be (re)set per-attempt
match io_type {
io_type::READ => Bio::reset(io, 1),
_ => Bio::reset(io, ch.ch.len() as i8),
};

let nio = Bio(io);
let nexus = nio.nexus_as_ref();

match io_type {
io_type::READ => nexus.readv(&nio, &mut ch),
io_type::WRITE => nexus.writev(&nio, &ch),
io_type::RESET => {
trace!("{}: Dispatching RESET", nexus.bdev.name());
nexus.reset(&nio, &ch)
}
io_type::UNMAP => {
if nexus.io_is_supported(io_type) {
nexus.unmap(&nio, &ch)
} else {
nio.fail();
}
}
io_type::FLUSH => {
if nexus.io_is_supported(io_type) {
nexus.flush(&nio, &ch)
} else {
nio.fail()
}
let mut ch = NexusChannel::inner_from_channel(channel);

// set the fields that need to be (re)set per-attempt
if nio.io_type() == io_type::READ {
nio.reset(1);
} else {
nio.reset(ch.ch.len())
}

let nexus = nio.nexus_as_ref();
let io_type = nio.io_type();
match io_type {
io_type::READ => nexus.readv(&nio, &mut ch),
io_type::WRITE => nexus.writev(&nio, &ch),
io_type::RESET => {
trace!("{}: Dispatching RESET", nexus.bdev.name());
nexus.reset(&nio, &ch)
}
io_type::UNMAP => {
if nexus.io_is_supported(io_type) {
nexus.unmap(&nio, &ch)
} else {
nio.fail();
}
io_type::WRITE_ZEROES => {
if nexus.io_is_supported(io_type) {
nexus.write_zeroes(&nio, &ch)
} else {
nio.fail()
}
}
io_type::FLUSH => {
// our replica's are attached to as nvme controllers
// who always support flush. This can be troublesome
// so we complete the IO directly.
nio.reset(0);
nio.ok();
}
io_type::WRITE_ZEROES => {
if nexus.io_is_supported(io_type) {
nexus.write_zeroes(&nio, &ch)
} else {
nio.fail()
}
io_type::NVME_ADMIN => {
if nexus.io_is_supported(io_type) {
nexus.nvme_admin(&nio, &ch)
} else {
nio.fail()
}
}
io_type::NVME_ADMIN => {
if nexus.io_is_supported(io_type) {
nexus.nvme_admin(&nio, &ch)
} else {
nio.fail()
}
_ => panic!(
"{} Received unsupported IO! type {}",
nexus.name, io_type
),
};
} else {
// something is very wrong ...
error!("Received unknown IO type {}", unsafe { (*io).type_ });
}
}
_ => panic!(
"{} Received unsupported IO! type {}",
nexus.name, io_type
),
};
}

/// called per core to create IO channels per Nexus instance
Expand Down
Loading

0 comments on commit df43b77

Please sign in to comment.