mirror of
https://github.com/rust-vmm/vhost-device.git
synced 2026-01-02 05:53:33 +00:00
scsi: split into binary and library crate
Split implementation to binary crate under src/main.rs and library crate under src/lib.rs Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
This commit is contained in:
parent
893ae1d83d
commit
7d7dd57d6f
5
vhost-device-scsi/src/lib.rs
Normal file
5
vhost-device-scsi/src/lib.rs
Normal file
@ -0,0 +1,5 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
|
||||
|
||||
pub mod scsi;
|
||||
pub mod vhu_scsi;
|
||||
pub mod virtio;
|
||||
@ -1,8 +1,12 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
|
||||
|
||||
mod scsi;
|
||||
mod vhu_scsi;
|
||||
mod virtio;
|
||||
use vhost_device_scsi::{
|
||||
scsi::emulation::{
|
||||
block_device::{BlockDevice, FileBackend, MediumRotationRate},
|
||||
target::EmulatedTarget,
|
||||
},
|
||||
vhu_scsi::VhostUserScsiBackend,
|
||||
};
|
||||
|
||||
use std::{
|
||||
fs::File,
|
||||
@ -17,14 +21,6 @@ use thiserror::Error as ThisError;
|
||||
use vhost_user_backend::VhostUserDaemon;
|
||||
use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap};
|
||||
|
||||
use crate::{
|
||||
scsi::emulation::{
|
||||
block_device::{BlockDevice, FileBackend, MediumRotationRate},
|
||||
target::EmulatedTarget,
|
||||
},
|
||||
vhu_scsi::VhostUserScsiBackend,
|
||||
};
|
||||
|
||||
#[derive(Debug, ThisError)]
|
||||
enum Error {
|
||||
#[error("More than 256 LUNs aren't currently supported")]
|
||||
|
||||
@ -22,13 +22,13 @@ use super::{
|
||||
};
|
||||
use crate::scsi::{sense, CmdError, CmdOutput, TaskAttr};
|
||||
|
||||
pub(crate) enum MediumRotationRate {
|
||||
pub enum MediumRotationRate {
|
||||
Unreported,
|
||||
NonRotating,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
||||
pub(crate) struct ByteOffset(u64);
|
||||
pub struct ByteOffset(u64);
|
||||
impl From<u64> for ByteOffset {
|
||||
fn from(value: u64) -> Self {
|
||||
ByteOffset(value)
|
||||
@ -48,7 +48,7 @@ impl Div<BlockSize> for ByteOffset {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
||||
pub(crate) struct BlockSize(NonZeroU32);
|
||||
pub struct BlockSize(NonZeroU32);
|
||||
impl From<BlockSize> for u32 {
|
||||
fn from(value: BlockSize) -> Self {
|
||||
u32::from(value.0)
|
||||
@ -63,7 +63,7 @@ impl TryFrom<u32> for BlockSize {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
||||
pub(crate) struct BlockOffset(u64);
|
||||
pub struct BlockOffset(u64);
|
||||
impl From<BlockOffset> for u64 {
|
||||
fn from(value: BlockOffset) -> Self {
|
||||
value.0
|
||||
@ -96,7 +96,7 @@ impl Mul<BlockSize> for BlockOffset {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait BlockDeviceBackend: Send + Sync {
|
||||
pub trait BlockDeviceBackend: Send + Sync {
|
||||
fn read_exact_at(&mut self, buf: &mut [u8], offset: ByteOffset) -> io::Result<()>;
|
||||
fn write_exact_at(&mut self, buf: &[u8], offset: ByteOffset) -> io::Result<()>;
|
||||
fn size_in_blocks(&mut self) -> io::Result<BlockOffset>;
|
||||
@ -104,7 +104,7 @@ pub(crate) trait BlockDeviceBackend: Send + Sync {
|
||||
fn sync(&mut self) -> io::Result<()>;
|
||||
}
|
||||
|
||||
pub(crate) struct FileBackend {
|
||||
pub struct FileBackend {
|
||||
file: File,
|
||||
block_size: BlockSize,
|
||||
}
|
||||
@ -142,14 +142,14 @@ impl BlockDeviceBackend for FileBackend {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct BlockDevice<T: BlockDeviceBackend> {
|
||||
pub struct BlockDevice<T: BlockDeviceBackend> {
|
||||
backend: T,
|
||||
write_protected: bool,
|
||||
rotation_rate: MediumRotationRate,
|
||||
}
|
||||
|
||||
impl<T: BlockDeviceBackend> BlockDevice<T> {
|
||||
pub(crate) const fn new(backend: T) -> Self {
|
||||
pub const fn new(backend: T) -> Self {
|
||||
Self {
|
||||
backend,
|
||||
write_protected: false,
|
||||
|
||||
@ -19,7 +19,7 @@ use crate::scsi::emulation::mode_page::ModePage;
|
||||
/// One of the modes supported by SCSI's REPORT LUNS command.
|
||||
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
pub(crate) enum ReportLunsSelectReport {
|
||||
pub enum ReportLunsSelectReport {
|
||||
NoWellKnown = 0x0,
|
||||
WellKnownOnly = 0x1,
|
||||
All = 0x2,
|
||||
@ -46,7 +46,7 @@ impl TryFrom<u8> for ReportLunsSelectReport {
|
||||
|
||||
/// A type of "vital product data" page returned by SCSI's INQUIRY command.
|
||||
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||
pub(crate) enum VpdPage {
|
||||
pub enum VpdPage {
|
||||
Ascii(u8),
|
||||
Ata, // *
|
||||
BlockDeviceCharacteristics, // *
|
||||
@ -78,7 +78,7 @@ pub(crate) enum VpdPage {
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
pub(crate) enum ModeSensePageControl {
|
||||
pub enum ModeSensePageControl {
|
||||
Current = 0b00,
|
||||
Changeable = 0b01,
|
||||
Default = 0b10,
|
||||
@ -169,24 +169,24 @@ impl From<VpdPage> for u8 {
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub(crate) enum SenseFormat {
|
||||
pub enum SenseFormat {
|
||||
Fixed,
|
||||
Descriptor,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub(crate) enum ModePageSelection {
|
||||
pub enum ModePageSelection {
|
||||
AllPageZeros,
|
||||
Single(ModePage),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum LunIndependentCommand {
|
||||
pub enum LunIndependentCommand {
|
||||
ReportLuns(ReportLunsSelectReport),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum LunSpecificCommand {
|
||||
pub enum LunSpecificCommand {
|
||||
Inquiry(Option<VpdPage>),
|
||||
ModeSense6 {
|
||||
pc: ModeSensePageControl,
|
||||
@ -230,13 +230,13 @@ pub(crate) enum LunSpecificCommand {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum Command {
|
||||
pub enum Command {
|
||||
LunIndependentCommand(LunIndependentCommand),
|
||||
LunSpecificCommand(LunSpecificCommand),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) enum CommandType {
|
||||
pub enum CommandType {
|
||||
Inquiry,
|
||||
ModeSense6,
|
||||
Read10,
|
||||
@ -251,7 +251,7 @@ pub(crate) enum CommandType {
|
||||
SynchronizeCache10,
|
||||
}
|
||||
|
||||
pub(crate) const OPCODES: &[(CommandType, (u8, Option<u16>))] = &[
|
||||
pub const OPCODES: &[(CommandType, (u8, Option<u16>))] = &[
|
||||
(CommandType::TestUnitReady, (0x0, None)),
|
||||
(CommandType::RequestSense, (0x3, None)),
|
||||
(CommandType::Inquiry, (0x12, None)),
|
||||
@ -270,7 +270,7 @@ pub(crate) const OPCODES: &[(CommandType, (u8, Option<u16>))] = &[
|
||||
];
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) struct UnparsedServiceAction(u8);
|
||||
pub struct UnparsedServiceAction(u8);
|
||||
impl UnparsedServiceAction {
|
||||
pub fn parse(self, service_action: u16) -> Option<CommandType> {
|
||||
OPCODES
|
||||
@ -282,7 +282,7 @@ impl UnparsedServiceAction {
|
||||
|
||||
/// See `parse_opcode`
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) enum ParseOpcodeResult {
|
||||
pub enum ParseOpcodeResult {
|
||||
/// The opcode represents a single command.
|
||||
Command(CommandType),
|
||||
/// The opcode requires a service action.
|
||||
@ -309,7 +309,7 @@ pub(crate) enum ParseOpcodeResult {
|
||||
/// - `ServiceAction`: the opcode is the first byte of a service action; the
|
||||
/// caller needs to call .parse() on the `UnparsedServiceAction` we returned
|
||||
/// with the service action byte.
|
||||
pub(crate) fn parse_opcode(opcode: u8) -> ParseOpcodeResult {
|
||||
pub fn parse_opcode(opcode: u8) -> ParseOpcodeResult {
|
||||
let found = OPCODES.iter().find(|(_, (x, _))| *x == opcode);
|
||||
match found {
|
||||
Some(&(ty, (_, None))) => ParseOpcodeResult::Command(ty),
|
||||
@ -493,14 +493,14 @@ impl CommandType {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Cdb {
|
||||
pub struct Cdb {
|
||||
pub command: Command,
|
||||
pub allocation_length: Option<u32>,
|
||||
pub naca: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
pub(crate) enum ParseError {
|
||||
pub enum ParseError {
|
||||
/// The opcode (specifically the first byte of the CDB) is unknown, i.e. we
|
||||
/// should respond with INVALID COMMAND OPERATION CODE
|
||||
InvalidCommand,
|
||||
@ -512,7 +512,7 @@ pub(crate) enum ParseError {
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
pub(crate) enum ReportSupportedOpCodesMode {
|
||||
pub enum ReportSupportedOpCodesMode {
|
||||
All,
|
||||
OneCommand(u8),
|
||||
OneServiceAction(u8, u16),
|
||||
@ -522,7 +522,7 @@ pub(crate) enum ReportSupportedOpCodesMode {
|
||||
impl Cdb {
|
||||
// TODO: do we want to ensure reserved fields are 0? SCSI allows, but
|
||||
// doesn't require, us to do so.
|
||||
pub(crate) fn parse(cdb: &[u8]) -> Result<Self, ParseError> {
|
||||
pub fn parse(cdb: &[u8]) -> Result<Self, ParseError> {
|
||||
let ct = CommandType::from_cdb(cdb)?;
|
||||
if cdb.len() < ct.cdb_template().len() {
|
||||
return Err(ParseError::TooSmall);
|
||||
|
||||
@ -9,7 +9,7 @@ use super::{
|
||||
};
|
||||
use crate::scsi::{sense, CmdError, CmdError::DataIn, CmdOutput};
|
||||
|
||||
pub(crate) struct MissingLun;
|
||||
pub struct MissingLun;
|
||||
|
||||
impl LogicalUnit for MissingLun {
|
||||
fn execute_command(
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
|
||||
|
||||
pub(crate) mod block_device;
|
||||
pub mod block_device;
|
||||
mod command;
|
||||
pub(crate) mod missing_lun;
|
||||
pub(crate) mod mode_page;
|
||||
pub mod missing_lun;
|
||||
pub mod mode_page;
|
||||
mod response_data;
|
||||
pub(crate) mod target;
|
||||
pub mod target;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
@ -3,26 +3,26 @@
|
||||
use std::io::{self, Write};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub(crate) enum ModePage {
|
||||
pub enum ModePage {
|
||||
Caching,
|
||||
}
|
||||
|
||||
impl ModePage {
|
||||
pub(crate) const ALL_ZERO: &'static [Self] = &[Self::Caching];
|
||||
pub const ALL_ZERO: &'static [Self] = &[Self::Caching];
|
||||
|
||||
pub(crate) const fn page_code(self) -> (u8, u8) {
|
||||
pub const fn page_code(self) -> (u8, u8) {
|
||||
match self {
|
||||
Self::Caching => (0x8, 0),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const fn page_length(self) -> u8 {
|
||||
pub const fn page_length(self) -> u8 {
|
||||
match self {
|
||||
Self::Caching => 0x12,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn write(self, data_in: &mut impl Write) -> io::Result<()> {
|
||||
pub fn write(self, data_in: &mut impl Write) -> io::Result<()> {
|
||||
assert_eq!(self.page_code().1, 0, "Subpages aren't supported yet.");
|
||||
|
||||
data_in.write_all(&[
|
||||
|
||||
@ -16,7 +16,7 @@ use super::{
|
||||
};
|
||||
use crate::scsi::{sense, CmdError, CmdOutput, Request, Target, TaskAttr};
|
||||
|
||||
pub(crate) struct LunRequest {
|
||||
pub struct LunRequest {
|
||||
pub _id: u64,
|
||||
pub task_attr: TaskAttr,
|
||||
pub crn: u8,
|
||||
@ -26,7 +26,7 @@ pub(crate) struct LunRequest {
|
||||
}
|
||||
|
||||
/// A single logical unit of an emulated SCSI device.
|
||||
pub(crate) trait LogicalUnit: Send + Sync {
|
||||
pub trait LogicalUnit: Send + Sync {
|
||||
/// Process a SCSI command sent to this logical unit.
|
||||
///
|
||||
/// # Return value
|
||||
@ -47,20 +47,20 @@ pub(crate) trait LogicalUnit: Send + Sync {
|
||||
}
|
||||
|
||||
/// A SCSI target implemented by emulating a device within vhost-device-scsi.
|
||||
pub(crate) struct EmulatedTarget {
|
||||
pub struct EmulatedTarget {
|
||||
luns: Vec<Box<dyn LogicalUnit>>,
|
||||
}
|
||||
|
||||
impl EmulatedTarget {
|
||||
pub(crate) fn new() -> Self {
|
||||
pub fn new() -> Self {
|
||||
Self { luns: Vec::new() }
|
||||
}
|
||||
|
||||
pub(crate) fn add_lun(&mut self, logical_unit: Box<dyn LogicalUnit>) {
|
||||
pub fn add_lun(&mut self, logical_unit: Box<dyn LogicalUnit>) {
|
||||
self.luns.push(logical_unit);
|
||||
}
|
||||
|
||||
pub(crate) fn luns(&self) -> impl ExactSizeIterator<Item = u16> + '_ {
|
||||
pub fn luns(&self) -> impl ExactSizeIterator<Item = u16> + '_ {
|
||||
// unwrap is safe: we limit LUNs at 256
|
||||
self.luns
|
||||
.iter()
|
||||
|
||||
@ -35,15 +35,21 @@ const REQUEST_QUEUE: u16 = 2;
|
||||
type DescriptorChainWriter = virtio::DescriptorChainWriter<GuestMemoryLoadGuard<GuestMemoryMmap>>;
|
||||
type DescriptorChainReader = virtio::DescriptorChainReader<GuestMemoryLoadGuard<GuestMemoryMmap>>;
|
||||
|
||||
pub(crate) struct VhostUserScsiBackend {
|
||||
pub struct VhostUserScsiBackend {
|
||||
event_idx: bool,
|
||||
mem: Option<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
targets: Vec<Box<dyn Target>>,
|
||||
pub(crate) exit_event: EventFd,
|
||||
pub exit_event: EventFd,
|
||||
}
|
||||
|
||||
impl Default for VhostUserScsiBackend {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl VhostUserScsiBackend {
|
||||
pub(crate) fn new() -> Self {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
event_idx: false,
|
||||
mem: None,
|
||||
@ -193,7 +199,7 @@ impl VhostUserScsiBackend {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn add_target(&mut self, target: Box<dyn Target>) {
|
||||
pub fn add_target(&mut self, target: Box<dyn Target>) {
|
||||
self.targets.push(target);
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,18 +20,18 @@ use vm_memory::{Bytes, GuestAddress, GuestMemory};
|
||||
/// virtio-scsi has its own format for LUNs, documented in 5.6.6.1 of virtio
|
||||
/// v1.1. This represents a LUN parsed from that format.
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub(crate) enum VirtioScsiLun {
|
||||
pub enum VirtioScsiLun {
|
||||
ReportLuns,
|
||||
TargetLun(u8, u16),
|
||||
}
|
||||
|
||||
pub(crate) const REPORT_LUNS: [u8; 8] = [0xc1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
|
||||
pub const REPORT_LUNS: [u8; 8] = [0xc1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
|
||||
|
||||
impl VirtioScsiLun {
|
||||
pub(crate) const FLAT_SPACE_ADDRESSING_METHOD: u8 = 0b0100_0000;
|
||||
pub(crate) const ADDRESS_METHOD_PATTERN: u8 = 0b1100_0000;
|
||||
pub const FLAT_SPACE_ADDRESSING_METHOD: u8 = 0b0100_0000;
|
||||
pub const ADDRESS_METHOD_PATTERN: u8 = 0b1100_0000;
|
||||
|
||||
pub(crate) fn parse(bytes: [u8; 8]) -> Option<Self> {
|
||||
pub fn parse(bytes: [u8; 8]) -> Option<Self> {
|
||||
if bytes == REPORT_LUNS {
|
||||
Some(Self::ReportLuns)
|
||||
} else if bytes[0] == 0x1 {
|
||||
@ -55,7 +55,7 @@ impl VirtioScsiLun {
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum ResponseCode {
|
||||
pub enum ResponseCode {
|
||||
Ok = 0,
|
||||
Overrun = 1,
|
||||
BadTarget = 3,
|
||||
@ -64,10 +64,10 @@ pub(crate) enum ResponseCode {
|
||||
|
||||
// These are the defaults given in the virtio spec; QEMU doesn't let the driver
|
||||
// write to config space, so these will always be the correct values.
|
||||
pub(crate) const SENSE_SIZE: usize = 96;
|
||||
pub(crate) const CDB_SIZE: usize = 32;
|
||||
pub const SENSE_SIZE: usize = 96;
|
||||
pub const CDB_SIZE: usize = 32;
|
||||
|
||||
pub(crate) struct Request {
|
||||
pub struct Request {
|
||||
pub id: u64,
|
||||
pub lun: VirtioScsiLun,
|
||||
pub prio: u8,
|
||||
@ -77,7 +77,7 @@ pub(crate) struct Request {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum RequestParseError {
|
||||
pub enum RequestParseError {
|
||||
CouldNotReadGuestMemory(io::Error),
|
||||
FailedParsingLun([u8; 8]),
|
||||
}
|
||||
@ -107,7 +107,7 @@ impl Request {
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(crate) struct Response {
|
||||
pub struct Response {
|
||||
pub response: ResponseCode,
|
||||
pub status: u8,
|
||||
pub status_qualifier: u16,
|
||||
@ -316,7 +316,7 @@ where
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
pub mod tests {
|
||||
use virtio_bindings::virtio_scsi::{virtio_scsi_cmd_req, virtio_scsi_cmd_resp};
|
||||
use virtio_queue::{desc::RawDescriptor, mock::MockSplitQueue};
|
||||
use vm_memory::{ByteValued, GuestAddress, GuestMemoryMmap};
|
||||
@ -325,19 +325,19 @@ pub(crate) mod tests {
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct VirtioScsiCmdReq(pub virtio_scsi_cmd_req);
|
||||
pub struct VirtioScsiCmdReq(pub virtio_scsi_cmd_req);
|
||||
/// SAFETY: struct is a transparent wrapper around the request
|
||||
/// which can be read from a byte array
|
||||
unsafe impl ByteValued for VirtioScsiCmdReq {}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct VirtioScsiCmdResp(pub virtio_scsi_cmd_resp);
|
||||
pub struct VirtioScsiCmdResp(pub virtio_scsi_cmd_resp);
|
||||
/// SAFETY: struct is a transparent wrapper around the response
|
||||
/// which can be read from a byte array
|
||||
unsafe impl ByteValued for VirtioScsiCmdResp {}
|
||||
|
||||
pub(crate) fn report_luns_command() -> VirtioScsiCmdReq {
|
||||
pub fn report_luns_command() -> VirtioScsiCmdReq {
|
||||
VirtioScsiCmdReq(virtio_scsi_cmd_req {
|
||||
lun: REPORT_LUNS,
|
||||
tag: 0,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user