Git Product home page Git Product logo

scsi2nvme's Introduction

CI Status

scsi2nvme

Tools for implementing SCSI on top of NVMe

This repo contains the following:

  1. SCSI mock device
  2. NVMe device driver
  3. Translation Library
  4. Engine to orchestrate execution flow

Translation Library

The Translation Library offers a two-way command agnostic translation for SCSI commands to NVMe commands and NVMe responses to SCSI responses. It can be used in the user space or the kernel space.

The Library encapsulates away non-caller-actionable issues from the caller. For instance, since the caller is likely to be passing along a pre-constructed SCSI command to the Library, any issues with malformed SCSI commands is not actionable for the caller. In that case, the Library directly writes an error status to the SCSI buffer and no action is needed by the caller.

In cases where the caller is using the Translation Library incorrectly, e.g. calling Translation::Complete before calling Translation::Begin or calling Translation::Begin twice in a row, the library returns an ApiStatus which currently is either kSuccess or kFailure.

Translation Interface

The Translation Interface provides 4 functions:

BeginResponse Begin(Span<const uint8_t> scsi_cmd, Span<const uint8_t> buffer, scsi::LunAddress lun);

Span<const NvmeCmdWrappers> GetNvmeWrappers();

CompleteReponse Complete(Span<const nvme::GenericQueueEntryCpl> cpl_data, Span<const uint8_t> buffer_in, Span<const uint8_t> sense_buffer);

void AbortPipeline(); 

Translation::Begin()

BeginReponse Begin(Span<const uint8_t> scsi_cmd, 
                   Span<const uint8_t> buffer, 
                   scsi::LunAddress lun)

This function takes in:

  • Raw SCSI command in bytes
  • Buffer which can either be an input buffer or an output buffer. As an input buffer, it will be where the data from the device is read into. As an output buffer, it stores variable-length command data or the data to be written to the device
  • Lun Address which is the NSID in NVMe

From the parameters, the function then delegates to the appropriate command translator and then builds 1 or more NVMe commands. For some commands such as TestUnitReady or Report Supported Op Codes, the Translation Library does not build an NVMe command and instead writes data directly to the SCSI buffer.

The functions returns:

BeginReponse {
  ApiStatus status;   // Indicates correct usage of Translation Library 
  uint32_t alloc_len; // Defines size of buffer passed to Translation::Complete
}

Translation::GetNvmeWrappers()

Span<NvmeCmdWrapper> GetNvmeWrappers()

This function returns the constructed NVMe commands along with other useful data.

NvmeCmdWrapper {
  nvme::GenericQueueEntryCmd cmd; // NVMe command
  uint32_t buffer_len; // length of buffer pointed to by the NVMe PRP pointer
  bool is_admin; // whether command is an admin command. Needed for sending to correct NVMe queue
}

Translation::Complete()

CompleteReponse Complete(Span<const nvme::GenericQueueEntryCpl> cpl_data, 
                         Span<const uint8_t> buffer_in,
                         Span<const uint8_t> sense_buffer);

The function takes in:

  • NVMe Completion Queue Entry, which contains the NVMe response data
  • SCSI data in buffer where response data is written to
  • SCSI sense buffer where status codes are written to

This function translates the NVMe response data and status codes and writes them to the appropriate buffer.

Translation::AbortPipeline

void AbortPipeline()

Finally, in the case that the Translation pipeline needs to be aborted, this function handles all the necesssary memory cleanup.

Intended Usage

  1. Get the Raw SCSI command and other data from the SCSI subsystem
  2. Pass data to Translation::Begin()
  3. Get constructed NVMe commands and other data with Translation::GetNvmeWrappers()
  4. Send the commands to the NVMe device for execution
  5. Pass the NVMe completion queue entries to Translation::Complete()

End-to-end translation

Setup

  1. Ensure the following:

    • an NVMe device is attached to the machine at /dev/nvme0n1
    • kernel version is >= 4.19
  2. Call $ make in the root directory. Ensure this results in a scsi2nvme.ko

  3. Call $ insmod scsi2nvme.ko to insert the kernel module.

    Upon inserting, the SCSI mock device will be automatically attached. Once the device is attached, the SCSI subsystem will issue a set of commands.

    $ lsscsi should show the device with the name "NVMe    " (NVMe followed by 4 spaces)

Issuing commands

Issue commands to the SCSI device as such:

Read:

$ sudo dd if=$DEVICE bs=$BLOCK_SIZE count=$NUM_BLOCKS skip=$STARTING_BLOCK_ADDRESS

Write:

$ sudo dd of=$DEVICE bs=$BLOCK_SIZE count=$NUM_BLOCKS seek=$STARTING_BLOCK_ADDRESS

Then you will be prompted to enter the data to be written in stdin. Data can simply be a string of alphanumerical characters such as "The quick brown fox jumps over the lazy dog"

Issue commands to the NVMe device with the nvme-cli Some examples are:

$ sudo nvme read /dev/nvme0n1/-z $BLOCK_SIZE -c $NUM_BLOCKS -s $STARTING_BLOCK_ADDRESS

$ sudo nvme write /dev/nvme0n1/-z $BLOCK_SIZE -c $NUM_BLOCKS -s $STARTING_BLOCK_ADDRESS

See logs

See logs with $ sudo dmesg

Disclaimer

This is not an officially supported Google product.

scsi2nvme's People

Contributors

basimsahaf avatar eyhhuang avatar harloff-g avatar justinawei avatar ralith avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

poppro basimsahaf

scsi2nvme's Issues

Translate Status Codes

Translate NVMe status codes to SCSI status code, sense key, and additional sense code

TOC Commands

Read TOC (table of contents) commands with support of separate session info.

Investigate possible locations in IO kernel stack

Reference - Linux storage stack diagram

General idea of the module “location”:
Linux-storage-stack-diagram_v4 10
To investigate:

  • At what layer of SCSI we want to intercept? We probably want to either mimic a “low level driver” and expose a ‘fake’ SCSI driver .
  • How can we easily communicate with NVMe drivers?
  • Do we have to communicate with the block layer? My guess is “no” but we have to double check.

Note: There is a chance that some SCSI definitions are already in Linux source - the reason why we have our own redefined is that our module should be reusable in different places, not only kernel itself.

Check code style in CI

clang-format is a great tool for enforcing consistent style, both run manually to apply a style and in automated testing to ensure the style has been respected. Let's add a github actions job for the latter.

Investigate importing NVMe definitions

This is an alternative to implementing NVMe definitions by hand.

There are already libraries (like SPDK) and headers that define all of that. Alternative approach here is to simply copy the structure definitions and modify them slightly to be more consistent with C++ style. Please research if open source google projects allow that and what other license types we can work with before proceeding.

Read, Write, Verify and Sync Cache Commands

Read, Write, Verify and synchronize cache commands structures. Please note that some commands exist in a few variants like read6, read10 and read16. (They all act essentially the same, but *10 and *16 include more options)

Continuous integration

Set up automated build and test of every pull request and commit to master. Helps prevent inadvertent breakage from slipping in.

There are a number of free CI services available for open source projects, but GitHub Actions predictably have the best integration. Testing on Linux only should be fine.

Fix Allocation Length Verification

A proper solution for validating allocation length in the translation engine should be implemented in the future.

Per Michal, "A proper solution would be to have temporary allocation with predicted returned size, and copy the min of provided allocation length and our prediction from."

Import Identify namespace

Identify namespace with extra sub-structures: “LBA Format”, “Reservation capabilities bits”, “Deallocate block features”, “namespace features”, namespace GUID, “deallocate block features”, Data protection type settings

LUN Structures

Structures related to LUN addressing (Lun Data, lun addressing) and report luns command structure.

Initial setup of project structure and build environment.

This should be just a basic setup of build system, folders and build units.

What we need to have:

  • A folder structure as follows (seems like a common pattern?):
    • lib/(empty library)
    • test/(empty test library that uses gtest)
    • test/console (for quick and dirty prototyping? optional)
  • We need some default .gitignore file setup (one that will handle c++ and bazel build artifacts).
  • Default bazel build. After cloning and typing something like bazel build ...:all everything should get compiled.
  • Basic clang lint setup (that should work with bazel). This setup should follow Google style https://google.github.io/styleguide/cppguide.html - there should off-the-shelf config for that.

Nvme Transfer mode

“Transfer mode” (prp vs SGL with some extra options, part of read or write command)

Make clang format preserve include blocks

Currently clang format does not differentiate between other projects' .h files and our own project's .h files. To fix this, we could either add third_party as a category or we could set clang to preserve line breaks between blocks of includes

Sense Codes

  1. Sense response
  2. key
  3. key code
  4. key code qualifier (Enums expressing the errors)

SCSI basic structure definitions.

We need to define basic SCSI structures in C++. We can either write this from scratch or reference some library that already contains and exposes those structures. In general, we need definitions of:

  • SCSI statuses, device types, operation codes, version descriptors (Basic enums), control byte structure (that is shared across many commands).
  • Sense response, key, key code, and key code qualifier (Enums expressing the errors)
  • Structures related to LUN addressing (Lun Data, lun addressing) and report luns command structure.
  • Inquiry command structures (with supporting structures and enums) we want to support inquiring standard data, Device ID, Unit serial number, Block Limits and device characteristics, and logical block provisioning pages.
  • "Unit ready" command structures.
  • Read TOC (table of contents) commands with support of separate session info. Not implementing
  • Read, Write, Verify and synchronize cache commands structures. Please note that some commands exist in a few variants like read6, read10 and read16. (They all act essentially the same, but *10 and *16 include more options)
  • Read capacity command structures.
  • "Mode sense" command structures.
  • Persistent reservation command structures. (reserve in, read reservation, reserve out)
  • Unmap command structures.
  • Maintenance In command structures. Especially the part about support of op-codes.

They should all exist in single h file and have right sizes according to the documentation.

Please refer to SCSI documentation for more details.

Persistent Reservation

Persistent reservation command structures. (reserve in, read reservation, reserve out)

Translate Mode Sense Command

Devise a plan for translating SCSI Mode Sense (6 and 10) to NVMe equivalent. Implement solution in SCSI2NVMe translation library.

Write an optional template that works in the kernel space

As @harloff-g pointed out in #71, the std::optional results in some linking issues when used in the kernel space. As the optional class offers some useful features, notably to clearly indicate when a value has been initialized, we will create our own template class that implements the necessary features

Build and load library as a kernel module

This is something that is going to be needed in the future and might require some extra research. I do not expect that this is going to be available out of the box with the current bazel build. AFAIK loading modules written in C++ should be possible (I have seen rust modules), but obviously we need to use kernel C ABI.

As this is just a first step - we expect to get simple "hello world" from our module (that links our code) when triggered (via chardev) from user-space.

Import Identify controller

Some of subfields in these structures could be its own struct, like “SGL support Field”, “Async Event Support”, “Volatile write cache”, “Support for flush all”, “Optional NVM command support”, “Log Page attributes”, “Controller attributes”, “Power state descriptors”, Enum that specifies type of identify (namespace/controller/active Napesaces ID list etc).

Inquiry Command Structures

Inquiry command structures (with supporting structures and enums) we want to support inquiring:

  1. standard data
  2. Device ID
  3. Unit serial number
  4. Block Limits and device characteristics
  5. logical block provisioning pages.

Implement general translation structs

As per the design proposed by @harloff-g we will need some structs to facilitate translation such as

  • TranslateBundle
  • ScsiData
  • ResponseData

Once the design/names are finalized, this should be prioritized first so the other translation PRs can use the structs

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.