Public Header¶
/*
* User space I/O library for Open-Channel SSDs
*
* Copyright (C) 2015-2017 Javier Gonzáles <javier@cnexlabs.com>
* Copyright (C) 2015-2017 Matias Bjørling <matias@cnexlabs.com>
* Copyright (C) 2015-2017 Simon A. F. Lund <slund@cnexlabs.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file liblightnvm.h
*/
#ifndef __LIBLIGHTNVM_H
#define __LIBLIGHTNVM_H
#ifdef __cplusplus
extern "C" {
#endif
#include <inttypes.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <liblightnvm_util.h>
#include <liblightnvm_spec.h>
#define NVM_NADDR_MAX 64
#define NVM_DEV_NAME_LEN 32
#define NVM_DEV_PATH_LEN (NVM_DEV_NAME_LEN + 5)
#define NVM_FLAG_SCRBL 0x200 ///< Scrambler ON/OFF: Context sensitive
/**
* Enumeration of nvm_cmd backends
*/
enum nvm_be_id {
NVM_BE_ANY = 0x0, ///< ANY backend
NVM_BE_IOCTL = 0x1, ///< IOCTL backend
NVM_BE_LBD = 0x1 << 1, ///< IOCTL + LBD backend
NVM_BE_SPDK = 0x1 << 2, ///< SPDK backend
};
#define NVM_BE_ALL (NVM_BE_IOCTL | NVM_BE_LBD | NVM_BE_SPDK)
/**
* Enumeration of nvm_cmd options
*/
enum nvm_cmd_opts {
NVM_CMD_SYNC = 0x1 << 3,
NVM_CMD_ASYNC = 0x1 << 4,
NVM_CMD_SCALAR = 0x1 << 5,
NVM_CMD_VECTOR = 0x1 << 6,
NVM_CMD_PRP = 0x1 << 7,
NVM_CMD_SGL = 0x1 << 8,
};
#define NVM_CMD_MASK_IOMD (NVM_CMD_SYNC | NVM_CMD_ASYNC)
#define NVM_CMD_MASK_ADDR (NVM_CMD_SCALAR | NVM_CMD_VECTOR)
#define NVM_CMD_MASK_PLOD (NVM_CMD_PRP | NVM_CMD_SGL)
#define NVM_CMD_DEF_IOMD NVM_CMD_SYNC
#define NVM_CMD_DEF_ADDR NVM_CMD_VECTOR
#define NVM_CMD_DEF_PLOD NVM_CMD_PRP
/**
* Plane-mode access for IO
*/
enum nvm_pmode {
NVM_FLAG_PMODE_SNGL = 0x0, ///< Single-plane
NVM_FLAG_PMODE_DUAL = 0x1, ///< Dual-plane
NVM_FLAG_PMODE_QUAD = 0x1 << 1 ///< Quad-plane
};
#define NVM_FLAG_DEFAULT (NVM_FLAG_PMODE_SNGL | NVM_FLAG_SCRBL)
/**
* Flags for device quirks
*/
enum nvm_quirks {
NVM_QUIRK_PMODE_ERASE_RUNROLL = 0x1,
NVM_QUIRK_NSID_BY_NAMECONV = 0x1 << 1,
NVM_QUIRK_OOB_READ_1ST4BYTES_NULL = 0x1 << 2,
NVM_QUIRK_OOB_2LRG = 0x1 << 3,
};
/**
* Opaque handle for NVM devices
*
* @struct nvm_dev
*/
struct nvm_dev;
/**
* Opaque handle for a Scatter Gather List (SGL).
*
* @struct nvm_sgl
*/
struct nvm_sgl;
/**
* De-allocate an SGL
*
* @see nvm_sgl_alloc
*
* @param sgl Pointer to sgl as allocated by `nvm_sgl_alloc`
*/
void nvm_sgl_free(struct nvm_sgl *sgl);
/**
* Allocate a SGL
*
* @see nvm_sgl_add
* @see nvm_sgl_free
*
* @returns An initialized (empty) SGL.
*/
struct nvm_sgl *nvm_sgl_alloc(void);
/**
* Add an entry to the SGL
*
* @see nvm_sgl_alloc
* @see nvm_buf_alloc
*
* @param sgl Pointer to sgl as allocated by `nvm_sgl_alloc`
* @param buf Pointer to buffer as allocated with `nvm_buf_alloc`
* @param nbytes Size of the given buffer in bytes
*/
void nvm_sgl_add(struct nvm_sgl *sgl, void *buf, size_t nbytes);
/**
* Virtual block abstraction
*
* Facilitates a libc-like read/write and a system-like `pread`/`pwrite`
* interface to perform I/O on a virtual block spanning multiple blocks of
* physical NVM.
*
* Consult the `nvm_vblk_alloc`, `nvm_vblk_alloc_line` for the different spans
*
* @see nvm_vblk_alloc
* @see nvm_vblk_alloc_line
*
* @struct nvm_vblk
*/
struct nvm_vblk;
/**
* Enumeration of pseudo meta mode
*/
enum nvm_meta_mode {
NVM_META_MODE_NONE = 0x0,
NVM_META_MODE_ALPHA = 0x1,
NVM_META_MODE_CONST = 0x1 << 1
};
/**
* Enumeration of device bounds
*/
enum nvm_bounds {
NVM_BOUNDS_CHANNEL = 0x1,
NVM_BOUNDS_LUN = 0x1 << 1,
NVM_BOUNDS_PLANE = 0x1 << 2,
NVM_BOUNDS_BLOCK = 0x1 << 3,
NVM_BOUNDS_PAGE = 0x1 << 4,
NVM_BOUNDS_SECTOR = 0x1 << 5,
NVM_BOUNDS_PUGRP = 0x1 << 6,
NVM_BOUNDS_PUNIT = 0x1 << 7,
NVM_BOUNDS_CHUNK = 0x1 << 8,
NVM_BOUNDS_SECTR = 0x1 << 9
};
/**
* Opaque asynchronous context as returned by 'nvm_async_init'
*
* @see nvm_async_init
* @see nvm_async_term
*
* @struct nvm_async_ctx
*/
struct nvm_async_ctx;
/**
* Forward declaration, see nvm_ret further down
*/
struct nvm_ret;
/**
* Signature of function used with asynchronous callbacks.
*/
typedef void (*nvm_async_cb)(struct nvm_ret *ret, void *opaque);
/**
* IO ASYNC command context per IO, setup this struct inside nvm_ret per call to
* the nvm_cmd IO functions and set the CMD option NVM_CMD_ASYNC.
*
* @see nvm_async_cb
* @see nvm_async_init
*/
struct nvm_async_cmd_ctx {
struct nvm_async_ctx *ctx; ///< from nvm_async_init
nvm_async_cb cb; ///< User provided callback function
void *cb_arg; ///< User provided callback arguments
};
/**
* Allocate an asynchronous context for command submission of the given depth
* for submission of commands to the given device
*
* @param dev Associated device
* @param depth Maximum iodepth / qdepth, maximum number of outstanding commands
* of the returned context
* @param flags TBD
*/
struct nvm_async_ctx *nvm_async_init(struct nvm_dev *dev, uint32_t depth,
uint16_t flags);
/**
* Get the I/O depth of the context.
*
* @param ctx Asynchronous context
*/
uint32_t nvm_async_get_depth(struct nvm_async_ctx *ctx);
/**
* Get the number of outstanding I/O.
*
* @param ctx Asynchronous context
*/
uint32_t nvm_async_get_outstanding(struct nvm_async_ctx *ctx);
/**
* Tear down the given ASYNC context
*/
int nvm_async_term(struct nvm_dev *dev, struct nvm_async_ctx *ctx);
/**
* Process completions from the given ASYNC context.
*
* Set process 'max' to limit number of completions, 0 means no max.
*
* @return On success, number of completions processed, may be 0. On error, -1
* is returned and errno set to indicate the error.
*/
int nvm_async_poke(struct nvm_dev *dev, struct nvm_async_ctx *ctx, uint32_t max);
/**
* Wait for completion of all outstanding commands in the given 'ctx'
*
* @return On success, number of completions processed, may be 0. On error, -1
* is returned and errno set to indicate the error.
*/
int nvm_async_wait(struct nvm_dev *dev, struct nvm_async_ctx *ctx);
/**
* Encapsulation and representation of lower-level error conditions
*
* @struct nvm_ret
*/
struct nvm_ret {
union {
struct {
uint64_t cs; ///< NVM completion status (CS)
} vio; ///< Vector I/O result
uint32_t cdw0; ///< NVMe command specific result
} result;
uint16_t status; ///< NVMe command status
struct nvm_async_cmd_ctx async; ///< ASYNC command context
};
/**
* Obtain string representation of the given plane-mode
*
* @param pmode The plane-mode to obtain string representation of
*
* @returns On success, string representation of the given plane-mode. On error,
* "UNKN".
*/
const char *nvm_pmode_str(int pmode);
/**
* Encapsulation of physical/hierarchical/geometric addressing in generic format
*
* Although the user need not worry about device specific address formats the
* user has to know and respect addressing within device specific geometric
* boundaries.
*
* For that purpose one can use the `struct nvm_geo` of an `struct nvm_dev` to
* obtain device specific geometries.
*/
struct nvm_addr {
union {
/**
* Address packing and geometric accessors
*/
struct {
uint64_t sec : 8; ///< Sector address
uint64_t pg : 16; ///< Page address
uint64_t pl : 8; ///< Plane address
uint64_t blk : 16; ///< Block address
uint64_t lun : 8; ///< LUN address
uint64_t ch : 8; ///< Channel address
} g;
struct {
uint64_t sectr : 32; ///< Logical Sector in Chunk
uint64_t chunk : 16; ///< Chunk in PU
uint64_t punit : 8; ///< Parallel Unit (PU) in PUG
uint64_t pugrp : 8; ///< Parallel Unit Group (PUG)
} l;
uint64_t ppa; ///< Address as raw value
uint64_t val; ///< Address as raw value
};
};
/**
* Representation of device geometry
*
* @see nvm_dev_get_geo
*/
struct nvm_geo {
union {
/**
* Spec 2.0
*/
struct {
size_t npugrp; ///< # Parallel Unit Groups
size_t npunit; ///< # Parallel Units in PUG
size_t nchunk; ///< # Chunks in PU
size_t nsectr; ///< # Sectors per CNK
size_t nbytes; ///< # Bytes per SECTOR
size_t nbytes_oob; ///< # Bytes per SECTOR in OOB
} l;
/**
* Spec 1.2
*/
struct {
size_t nchannels; ///< # of channels on device
size_t nluns; ///< # of LUNs per channel
size_t nblocks; ///< # of blocks per plane
size_t nsectors; ///< # of sectors per page
size_t sector_nbytes; ///< # of bytes per sector
size_t meta_nbytes; ///< # of bytes for OOB
size_t nplanes; ///< # of planes per LUN
size_t npages; ///< # of pages per block
size_t page_nbytes; ///< # of bytes per page
} g;
struct {
size_t nchannels; ///< # of channels on device
size_t nluns; ///< # of LUNs per channel
size_t nblocks; ///< # of blocks per plane
size_t nsectors; ///< # of sectors per page
size_t sector_nbytes; ///< # of bytes per sector
size_t meta_nbytes; ///< # of bytes for OOB
size_t nplanes; ///< # of planes per LUN
size_t npages; ///< # of pages per block
size_t page_nbytes; ///< # of bytes per page
};
};
size_t tbytes; ///< Total # bytes in geometry
int verid; ///< Associated dev verid
};
/**
* Representation of valid values of bad-block-table states
*/
enum nvm_bbt_state {
NVM_BBT_FREE = 0x0, ///< Block is free AKA good
NVM_BBT_BAD = 0x1, ///< Block is bad
NVM_BBT_GBAD = 0x1 << 1, ///< Block marked as grown bad
NVM_BBT_DMRK = 0x1 << 2, ///< Block marked by device side
NVM_BBT_HMRK = 0x1 << 3 ///< Block marked by host side
};
/**
* Representation of bad-block-table
*
* The bad-block-table describes block-state of a given LUN
*
* @see nvm_bbt_get, nvm_bbt_set, nvm_bbt_mark, nvm_bbt_free, and nvm_bbt_pr
*/
struct nvm_bbt {
struct nvm_dev *dev; ///< Device on which the bbt resides
struct nvm_addr addr; ///< Address of the LUN described by the bbt
uint64_t nblks; ///< Total # of blocks in lun
uint32_t nbad; ///< # of manufacturer marked bad blocks
uint32_t ngbad; ///< # of grown bad blocks
uint32_t ndmrk; ///< # of device reserved/marked blocks
uint32_t nhmrk; ///< # of of host reserved/marked blocks
uint8_t blks[]; ///< Array of block status for each block in LUN
};
/**
* Execute an Open-Channel 1.2 identify / Open-Channel 2.0 geometry command
*
* NOTE: Caller is responsible for de-allocating the returned structure
*
* @return On success, pointer identify structure is returned. On error, NULL is
* returned and `errno` set to indicate the error and ret filled with
* lower-level result codes
*/
struct nvm_spec_idfy *nvm_cmd_idfy(struct nvm_dev *dev, struct nvm_ret *ret);
/**
* Executes one or multiple Open-Channel 2.0 get-log-page for chunk-information
*
* @note
* Caller is responsible for de-allocating the returned structure
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param addr Pointer to a `struct nvm_addr` containing the address of a chunk
* to report about
* @param opt Reporting options, see `enum nvm_spec_chunk_state`
* @param ret Pointer to structure in which to store lower-level status and
* result
*
* @return On success, pointer report chunk structure is returned. On error,
* NULL is returned and `errno` set to indicate the error and ret filled with
* lower-level result codes
*/
struct nvm_spec_rprt *nvm_cmd_rprt(struct nvm_dev *dev, struct nvm_addr *addr,
int opt, struct nvm_ret *ret);
/**
* Find an arbitrary set of 'naddrs' chunk-addresses on the given 'dev', in the
* given chunk state 'cs' and store them in the provided 'addrs' array
*
* @returns 0 on success, -1 on error and errno set to indicate the error.
*/
int nvm_cmd_rprt_arbs(struct nvm_dev *dev, int cs, int naddrs,
struct nvm_addr addrs[]);
/**
* Execute an Open-Channel 2.0 Get Feature command
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param id Feature identifier (see NVMe 1.3; Figure 84)
* @param feat Structure defining feature attributes
* @param ret Pointer to structure in which to store lower-level status and
* result
*
* @returns 0 on success, -1 on error and errno set to indicate the error.
*/
int nvm_cmd_gfeat(struct nvm_dev *dev, uint8_t id, union nvm_spec_feat *feat,
struct nvm_ret *ret);
/**
* Execute an Open-Channel 2.0 Set Feature command
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param id Feature identifier (see NVMe 1.3; Figure 84)
* @param feat Structure defining feature attributes
* @param ret Pointer to structure in which to store lower-level status and
* result
*
* @returns 0 on success, -1 on error and errno set to indicate the error.
*/
int nvm_cmd_sfeat(struct nvm_dev *dev, uint8_t id,
const union nvm_spec_feat *feat, struct nvm_ret *ret);
/**
* Execute an Open-Channel 1.2 get bad-block-table command
*
* @return On success, pointer to bad block table is returned. On error, NULL is
* returned and `errno` set to indicate the error and ret filled with
* lower-level result codes
*/
struct nvm_spec_bbt *nvm_cmd_gbbt(struct nvm_dev *dev, struct nvm_addr addr,
struct nvm_ret *ret);
/**
* Find an arbitrary set of 'naddrs' block-addresses on the given 'dev', in the
* given block state 'bs' and store them in the provided 'addrs' array
*
* @returns On success, 0 is returned. On error, -1 is returned and errno set to
* indicate the error.
*/
int nvm_cmd_gbbt_arbs(struct nvm_dev *dev, int bs, int naddrs,
struct nvm_addr addrs[]);
/**
* Execute an Open-Channel 1.2 set bad block table command
*
* @returns On success, 0 is returned. On error, -1 is returned and errno set to
* indicate the error.
*/
int nvm_cmd_sbbt(struct nvm_dev *dev, struct nvm_addr *addrs, int naddrs,
uint16_t flags, struct nvm_ret *ret);
/**
* Execute an Open-Channel 1.2 erase / Open-Channel 2.0 reset command
*
* @return On success, 0 is returned. On error, -1 is returned and `errno` set
* to indicate the error and ret filled with lower-level result codes
*/
int nvm_cmd_erase(struct nvm_dev *dev, struct nvm_addr addrs[], int naddrs,
void *meta, uint16_t flags, struct nvm_ret *ret);
/**
* Execute an Open-Channel 1.2 / 2.0 vector-write command
*
* @return On success, 0 is returned. On error, -1 is returned and `errno` set
* to indicate the error and ret filled with lower-level result codes
*/
int nvm_cmd_write(struct nvm_dev *dev, struct nvm_addr addrs[], int naddrs,
const void *data, const void *meta, uint16_t flags,
struct nvm_ret *ret);
/**
* Execute an Open-Channel 1.2 / 2.0 vector-read command
*
* @return On success, 0 is returned. On error, -1 is returned and `errno` set
* to indicate the error and ret filled with lower-level result codes
*/
int nvm_cmd_read(struct nvm_dev *dev, struct nvm_addr addrs[], int naddrs,
void *data, void *meta, uint16_t flags, struct nvm_ret *ret);
/**
* Execute an Open-Channel 2.0 vector-copy command
*
* @return On success, 0 is returned. On error, -1 is returned and `errno` set
* to indicate the error and ret filled with lower-level result codes
*/
int nvm_cmd_copy(struct nvm_dev *dev, struct nvm_addr src[],
struct nvm_addr dst[], int naddrs, uint16_t flags,
struct nvm_ret *ret);
/**
* @returns the "major" version of the library
*/
int nvm_ver_major(void);
/**
* @returns the "minor" version of the library
*/
int nvm_ver_minor(void);
/**
* @returns the "patch" version of the library
*/
int nvm_ver_patch(void);
/**
* Prints version information about the library
*/
void nvm_ver_pr(void);
/**
* Prints a humanly readable description of given boundary mask
*/
void nvm_bounds_pr(int mask);
/**
* Prints a humanly readable representation the given `struct nvm_ret`
*
* @param ret Pointer to the `struct nvm_ret` to print
*/
void nvm_ret_pr(const struct nvm_ret *ret);
/**
* Retrieves a bad block table from device
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param addr Address of the LUN to retrieve bad-block-table for
* @param ret Pointer to structure in which to store lower-level status and
* result
* @returns On success, a pointer to the bad-block-table is returned. On error,
* NULL is returned, `errno` set to indicate the error and ret filled with
* lower-level result codes
*/
const struct nvm_bbt *nvm_bbt_get(struct nvm_dev *dev, struct nvm_addr addr,
struct nvm_ret *ret);
/**
* Updates the bad-block-table on given device using the provided bbt
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param bbt The bbt to write to device
* @param ret Pointer to structure in which to store lower-level status and
* result
* @returns On success, 0 is returned. On error, -1 is returned, `errno` set to
* indicate the error and ret filled with lower-level result codes
*/
int nvm_bbt_set(struct nvm_dev *dev, const struct nvm_bbt *bbt,
struct nvm_ret *ret);
/**
* Mark addresses good, bad, or host-bad.
*
* @note
* The addresses given to this function are interpreted as block addresses, in
* contrast to `nvm_addr_write`, and `nvm_addr_read` which interpret addresses
* and sector addresses.
*
* @see `enum nvm_bbt_state`
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param addrs Array of memory address
* @param naddrs Length of memory address array
* @param flags 0x0 = GOOD, 0x1 = BAD, 0x2 = GROWN_BAD, as well as access mode
* @param ret Pointer to structure in which to store lower-level status and
* result.
* @returns On success, 0 is returned. On error, -1 is returned, `errno` set to
* indicate the error and ret filled with lower-level result codes
*/
int nvm_bbt_mark(struct nvm_dev *dev, struct nvm_addr addrs[], int naddrs,
uint16_t flags, struct nvm_ret *ret);
/**
* Persist the bad-block-table at `addr` on device and deallocate managed memory
* for the given bad-block-table describing the LUN at `addr`.
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param addr Address of the LUN to flush bad-block-table for
* @param ret Pointer to structure in which to store lower-level status and
* result
* @returns On success, 0 is returned. On error, -1 is returned, `errno` set to
* indicate the error and ret filled with lower-level result codes
*/
int nvm_bbt_flush(struct nvm_dev *dev, struct nvm_addr addr,
struct nvm_ret *ret);
/**
* Persist all bad-block-tables associated with the given `dev`
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param ret Pointer to structure in which to store lower-level status and
* result
* @returns On success, 0 is returned. On error, -1 is returned, `errno` set to
* indicate the error and ret filled with lower-level result codes
*/
int nvm_bbt_flush_all(struct nvm_dev *dev, struct nvm_ret *ret);
/**
* Allocate a copy of the given bad-block-table
*
* @param bbt Pointer to the bad-block-table to copy
* @returns On success, a pointer to a write-able copy of the given bbt is
* returned. On error, NULL is returned and errno set to indicate the error
*/
struct nvm_bbt *nvm_bbt_alloc_cp(const struct nvm_bbt *bbt);
/**
* Destroys a given bad-block-table
*
* @param bbt The bad-block-table to destroy
*/
void nvm_bbt_free(struct nvm_bbt *bbt);
/**
* Prints a humanly readable representation of the given bad-block-table
*
* @param bbt The bad-block-table to print
*/
void nvm_bbt_pr(const struct nvm_bbt *bbt);
/**
* Prints a humanly readable representation of the given bad-block-table state
*/
void nvm_bbt_state_pr(int state);
/**
* Prints human readable representation of the given geometry
*/
void nvm_geo_pr(const struct nvm_geo *geo);
/**
* Creates a handle to given device path
*
* @param dev_path Path of the device to open e.g. "/dev/nvme0n1"
*
* @returns A handle to the device
*/
struct nvm_dev *nvm_dev_open(const char *dev_path);
/**
* Creates a handle to given device path
*
* @param dev_path Path of the device to open e.g. "/dev/nvme0n1"
* @param flags Flags for opening device in different modes
*
* @returns A handle to the device
*/
struct nvm_dev *nvm_dev_openf(const char *dev_path, int flags);
/**
* Destroys device-handle
*
* @param dev Device handle obtained with `nvm_dev_open`
*/
void nvm_dev_close(struct nvm_dev *dev);
/**
* Prints misc. device attribute associated with the given handle
*
* @param dev Device handle obtained with `nvm_dev_open`
*/
void nvm_dev_attr_pr(const struct nvm_dev *dev);
/**
* Prints all information about the device associated with the given handle
*
* @param dev Device handle obtained with `nvm_dev_open`
*/
void nvm_dev_pr(const struct nvm_dev *dev);
/**
* Returns the file-descriptor associated with the given device
*
* @param dev Device handle obtained with `nvm_dev_open`
* @return On success, file descriptor is returned
*/
int nvm_dev_get_fd(const struct nvm_dev *dev);
/**
* Returns the name associated with the given device
*
* @param dev Device handle obtained with `nvm_dev_open`
* @return On success, string is returned. On error, NULL is returned.
*/
const char *nvm_dev_get_name(const struct nvm_dev *dev);
/**
* Returns the path associated with the given device
*
* @param dev Device handle obtained with `nvm_dev_open`
* @return On success, string is returned. On error, NULL is returned.
*/
const char *nvm_dev_get_path(const struct nvm_dev *dev);
/**
* Returns the NVME namespace identifier of the given device
*
* @param dev Device handle obtained with `nvm_dev_open`
*
* @return On success, NVME namespace identifier is returned.
*/
int nvm_dev_get_nsid(const struct nvm_dev *dev);
/**
* Returns the verid of the given device
*
* @param dev Device handle obtained with `nvm_dev_open`
* @return On success, verid is returned
*/
int nvm_dev_get_verid(const struct nvm_dev *dev);
/**
* Returns the media-controller capabilities mask of the given device
*
* @param dev Device handle obtained with `nvm_dev_open`
* @return On success, capabilities mask is returned
*/
uint32_t nvm_dev_get_mccap(const struct nvm_dev *dev);
/**
* Returns the default plane_mode of the given device
*
* @param dev Device handle obtained with `nvm_dev_open`
* @return On success, pmode flag is returned
*/
int nvm_dev_get_pmode(const struct nvm_dev *dev);
/**
* Set the default plane-mode for the given device
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param pmode Default plane-mode
*
* @returns 0 on success, -1 on error and errno set to indicate the error.
*/
int nvm_dev_set_pmode(struct nvm_dev *dev, int pmode);
/**
* Returns the mask of quirks for the given device
*
* @param dev Device handle obtained with `nvm_dev_open`
* @return On success, quirk mask is returned
*/
int nvm_dev_get_quirks(const struct nvm_dev *dev);
/**
* Set the default plane-mode for the given device
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param quirks Mask of `enum nvm_quirks`
*
* @returns 0 on success, -1 on error and errno set to indicate the error.
*/
int nvm_dev_set_quirks(struct nvm_dev *dev, int quirks);
/**
* Returns the ppa-format of the given device
*
* @param dev Device handle obtained with `nvm_dev_open`
* @return On success, ppa-format is returned
*
*/
const struct nvm_spec_ppaf_nand *nvm_dev_get_ppaf(const struct nvm_dev *dev);
/**
* Returns the ppa-format mask of the given device
*
* @param dev Device handle obtained with `nvm_dev_open`
* @return On success, ppa-format mask is returned
*
*/
const struct nvm_spec_ppaf_nand_mask *nvm_dev_get_ppaf_mask(const struct nvm_dev *dev);
/**
* Returns the LBA format of the given device
*
* @param dev Device handle obtained with `nvm_dev_open`
* @return On success, LBA format is returned
*/
const struct nvm_spec_lbaf *nvm_dev_get_lbaf(const struct nvm_dev *dev);
/**
* Returns the 'meta-mode' of the given device
*
* @param dev Device handle obtained with `nvm_dev_open`
* @return On success, meta-mode is returned
*/
int nvm_dev_get_meta_mode(const struct nvm_dev *dev);
/**
* Set the default 'meta-mode' of the given device
*
* The meta-mode is a setting used by the nvm_vblk interface to write
* pseudo-meta data to the out-of-bound area.
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param meta_mode One of: NVM_META_MODE_[NONE|ALPHA|CONST]
*
* @returns On success, 0 is returned. On error, -1 is returned and errno set to
* indicate the error.
*/
int nvm_dev_set_meta_mode(struct nvm_dev *dev, int meta_mode);
/**
* Returns the maximum number of addresses to use when sending erases to device.
* That is, when invoking nvm_addr_erase.
*
* @param dev Device handle obtained with `nvm_dev_open`
*/
int nvm_dev_get_erase_naddrs_max(const struct nvm_dev *dev);
/**
* Returns whether caching is enabled for bad-block-tables on the device.
*
* @note
* 0 = cache disabled
* 1 = cache enabled
*
* @param dev Device handle obtained with `nvm_dev_open`
*/
int nvm_dev_get_bbts_cached(const struct nvm_dev *dev);
/**
* Returns the backend identifier associated with the given device
*
* @param dev Device handle obtained with `nvm_dev_open`
*/
int nvm_dev_get_be_id(const struct nvm_dev *dev);
/**
* Set the maximum number of addresses to use for reads, that is, when invoking
* nvm_addr_read
*
* @param dev Device handle obtained with `nvm_dev_open`
*/
int nvm_dev_get_read_naddrs_max(const struct nvm_dev *dev);
/**
* Set the maximum number of addresses to use for writes, that is, when invoking
* nvm_addr_write
*
* @param dev Device handle obtained with `nvm_dev_open`
*/
int nvm_dev_get_write_naddrs_max(const struct nvm_dev *dev);
/**
* Set the maximum number of addresses to use for erases, that is, when invoking
* nvm_addr_erase
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param naddrs The maximum
*
* @returns 0 on success, -1 on error and errno set to indicate the error.
*/
int nvm_dev_set_erase_naddrs_max(struct nvm_dev *dev, int naddrs);
/**
* Sets whether retrieval and changes to bad-block-tables should be cached.
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param bbts_cached 1 = cache enabled, 0 = cache disabled
*
* @returns 0 on success, -1 on error and errno set to indicate the error.
*/
int nvm_dev_set_bbts_cached(struct nvm_dev *dev, int bbts_cached);
/**
* Set the maximum number of addresses to use for erases, that is, when invoking
* nvm_addr_erase.
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param naddrs The maximum
*
* @returns 0 on success, -1 on error and errno set to indicate the error.
*/
int nvm_dev_set_read_naddrs_max(struct nvm_dev *dev, int naddrs);
/**
* Set the maximum number of addresses to use for erases, that is, when invoking
* nvm_addr_erase.
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param naddrs The maximum
*
* @returns 0 on success, -1 on error and errno set to indicate the error.
*/
int nvm_dev_set_write_naddrs_max(struct nvm_dev *dev, int naddrs);
/**
* Returns the geometry of the given device
*
* @note
* See struct nvm_geo for the specifics of the returned geometry
*
* @param dev Device handle obtained with `nvm_dev_open`
*
* @returns The geometry (struct nvm_geo) of given device handle
*/
const struct nvm_geo *nvm_dev_get_geo(const struct nvm_dev *dev);
/**
* Returns the minimum write size, in number of sectors, for the given device.
*
* @param dev Device handle obtained with `nvm_dev_open`
*
* @returns The spec. 2.0 defined minimum write size, in sectors. An equivalent
* value for spec. 1.2
*/
int nvm_dev_get_ws_min(const struct nvm_dev *dev);
/**
* Returns the optimal write size, in number of sectors, for the given device.
*
* @param dev Device handle obtained with `nvm_dev_open`
*
* @returns The spec. 2.0 defined optimal write size, in sectors, an equivalent
* value for spec. 1.2
*/
int nvm_dev_get_ws_opt(const struct nvm_dev *dev);
/**
* Returns the minimal write cache units for the given device
*
* @param dev Device handle obtained with `nvm_dev_open`
*
* @returns The spec. 2.0 defined minimal write cache units, in sectors.
*/
int nvm_dev_get_mw_cunits(const struct nvm_dev *dev);
/**
* Allocate a buffer for IO with the given device
*
* The buffer will be aligned to device geometry and DMA allocated if required
* by the backend for IO
*
* @note
* nbytes must be greater than zero and a multiple of minimal granularity
* @note
* De-allocate the buffer using `nvm_buf_free`
*
* @see nvm_buf_free
*
* @param dev The device to allocate IO buffers for
* @param nbytes The size of the allocated buffer in bytes
* @param phys A pointer to the variable to hold the physical address of the
* allocated buffer. If NULL, the physical address is not returned.
*
* @returns On success, a pointer to the allocated memory is returned. On error,
* NULL is returned and `errno` set to indicate the error.
*/
void *nvm_buf_alloc(const struct nvm_dev *dev, size_t nbytes, uint64_t *phys);
/**
* Free the given IO buffer allocated with `nvm_buf_alloc`
*
* @see nvm_buf_alloc
*
* @param dev Pointer to the device which the IO buffer was allocated for
* @param buf Pointer to a buffer allocated with `nvm_buf_alloc`
*/
void nvm_buf_free(const struct nvm_dev *dev, void *buf);
/**
* Retrieve the physical address of the given buffer
*
* @param dev Pointer to the device which the IO buffer was allocated for
* @param buf Pointer to a buffer allocated with `nvm_buf_alloc`
* @param phys A pointer to the variable to hold the physical address of the
* given buffer.
*
* @return On success, 0 is returned. On error, -1 is returned and errno set to
* indicate the error.
*/
int nvm_buf_vtophys(const struct nvm_dev *dev, void *buf, uint64_t *phys);
/**
* Allocate a buffer of virtual memory of the given 'nbytes' and 'alignment'
*
* @note
* You must use `nvm_buf_virt_free` to de-allocate the buffer
*
* @param alignment The alignment in bytes
* @param nbytes The size of the buffer in bytes
*
* @returns A pointer to the allocated memory. On error: NULL is returned and
* `errno` set appropriatly
*/
void *nvm_buf_virt_alloc(size_t alignment, size_t nbytes);
/**
* Free the given virtual memory buffer
*
* @param buf Pointer to a buffer allocated with `nvm_buf_virt_alloc`
*/
void nvm_buf_virt_free(void *buf);
/**
* Fills `buf` with chars A-Z
*
* @param buf Pointer to the buffer to fill
* @param nbytes Amount of bytes to fill in buf
*/
void nvm_buf_fill(char *buf, size_t nbytes);
/**
* Prints `buf` to stdout
*
* @param buf Pointer to the buffer to print
* @param nbytes Amount of bytes of buf to print
*/
void nvm_buf_pr(char *buf, size_t nbytes);
/**
* Returns the number of bytes where expected is different from actual
*/
size_t nvm_buf_diff(char *expected, char *actual, size_t nbytes);
/**
* Prints the number and value of bytes where expected is different from actual
*/
void nvm_buf_diff_pr(char *expected, char *actual, size_t nbytes);
/**
* Write content of buffer into file
*
* @param buf Pointer to the buffer
* @param nbytes Size of buf
* @param path Destination where buffer will be dumped to
*
* @returns 0 on success, -1 on error and errno set to indicate the error.
*/
int nvm_buf_to_file(char *buf, size_t nbytes, const char *path);
/**
* Read content of file into buffer
*
* @param buf Pointer to the buffer
* @param nbytes Size of buf
* @param path Source to read from
*
* @returns 0 on success, -1 on error and errno set to indicate the error.
*/
int nvm_buf_from_file(char *buf, size_t nbytes, const char *path);
/**
* Encapsulation of a IO buffer-set for common-case setup
*
* This structure is allocated and initialized by `nvm_buf_set_alloc`, and
* de-allocated by `nvm_buf_set_free`
*
* @struct nvm_buf_set
*/
struct nvm_buf_set {
struct nvm_dev *dev; ///< Device for which IO buffers are allocated
char *write; ///< Data buffer to use for writes
char *write_meta; ///< Meta / OOB buffer to use for writes
char *read; ///< Data buffer to use for reads
char *read_meta; ///< Meta / OOB buffer to use for reads
size_t nbytes; ///< # of bytes per data buffer
size_t nbytes_meta; ///< # of bytes per meta buffer
};
/**
* Prints the given nvm_buf_set
*/
void nvm_buf_set_pr(struct nvm_buf_set *bufs);
struct nvm_buf_set *nvm_buf_set_alloc(struct nvm_dev *dev, size_t nbytes,
size_t nbytes_meta);
void nvm_buf_set_fill(struct nvm_buf_set *bufs);
void nvm_buf_set_free(struct nvm_buf_set *bufs);
/**
* Checks whether the given address exceeds bounds of the geometry of the given
* device
*
* @param addr The addr to check
* @param dev The device of which to check geometric bounds against
* @returns A mask of exceeded boundaries
*/
int nvm_addr_check(struct nvm_addr addr, const struct nvm_dev *dev);
/**
* Compute log-page-offset (lpo) in the NVMe chunk-information get-log-page
*
* That is, the location of the chunk-descriptor in the log-page, for the chunk
* at the given address in generic-format
*
* @note
* This is a helper for function for `nvm_cmd_rprt`, as a library user you will
* most likely not have a use for it
*
* @returns the log page offset (lpo) for the given addr
*/
uint64_t nvm_addr_gen2lpo(struct nvm_dev *dev, struct nvm_addr addr);
/**
* Inverse function of `nvm_addr_gen2lpo`
*
* @note
* This is a helper for function for `nvm_cmd_rprt`, as a library user you will
* most likely not have a use for it
*
* @returns the page offset (lpo) for the given addr
*/
struct nvm_addr nvm_addr_lpo2gen(struct nvm_dev *dev, uint64_t lpo);
/**
* Converts an address, in generic-format, to device-format
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param addr The address, in generic-format, to convert
* @return Address in device-format
*/
uint64_t nvm_addr_gen2dev(struct nvm_dev *dev, struct nvm_addr addr);
/**
* Converts an address, in device-format, to generic-format
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param addr The address, in device-format, to convert
* @return Address in generic-format
*/
struct nvm_addr nvm_addr_dev2gen(struct nvm_dev *dev, uint64_t addr);
/**
* Converts an address, in generic-format, to Linux Block Device offset
*
* @note
* This is a helper for function for the LBD backend, as a library user you will
* most likely not have a use for it
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param addr The address, in generic-format, to convert
* @return LBD offset
*/
uint64_t nvm_addr_gen2off(struct nvm_dev *dev, struct nvm_addr addr);
/**
* Converts a Linux Block Device offset to an address in generic-format
*
* @note
* This is a helper for function for the LBD backend, as a library user you will
* most likely not have a use for it
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param off LBD offset
* @return Address in generic-format
*/
struct nvm_addr nvm_addr_off2gen(struct nvm_dev *dev, uint64_t off);
/**
* Converts an address, in device-format, to Linux Block Device offset
*
* @note
* This is a helper for function for the LBD backend, as a library user you will
* most likely not have a use for it
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param addr The physical address on device-format to convert
* @return Physical address on lba-offset-format
*/
uint64_t nvm_addr_dev2off(struct nvm_dev *dev, uint64_t addr);
/**
* Converts a Linux Block Device offset to an address in device-format
*
* @note
* This is a helper for function for the LBD backend, as a library user you will
* most likely not have a use for it
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param addr The physical address on device-format to convert
* @return Physical address on lba-offset-format
*/
uint64_t nvm_addr_off2dev(struct nvm_dev *dev, uint64_t addr);
/**
* Prints a hexidecimal representation of the given address value
*/
void nvm_addr_pr(const struct nvm_addr addr);
/**
* Prints a humanly readable representation of the given list of addresses
* according to the geometry of the given device (spec 1.2 or 2.0)
*/
void nvm_addr_prn(const struct nvm_addr *addr, unsigned int naddrs,
const struct nvm_dev *dev);
/**
* Allocate a virtual block, spanning a given set of physical blocks
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param addrs Set of block-addresses forming the virtual block
* @param naddrs The number of addresses in the address-set
*
* @returns On success, an opaque pointer to the initialized virtual block is
* returned. On error, NULL and `errno` set to indicate the error.
*/
struct nvm_vblk *nvm_vblk_alloc(struct nvm_dev *dev, struct nvm_addr addrs[],
int naddrs);
/**
* Allocate a virtual block (spanning planes, channels, and LUNs)
*
* @param dev Device handle obtained with `nvm_dev_open`
* @param ch_bgn Beginning of the channel span, as inclusive index
* @param ch_end End of the channel span, as inclusive index
* @param lun_bgn Beginning of the LUN span, as inclusive index
* @param lun_end End of the LUN span, as inclusive index
* @param blk Block index
*
* @returns On success, an opaque pointer to the initialized virtual block is
* returned. On error, NULL and `errno` set to indicate the error.
*/
struct nvm_vblk *nvm_vblk_alloc_line(struct nvm_dev *dev, int ch_bgn,
int ch_end, int lun_bgn, int lun_end,
int blk);
/**
* Set the command mode for the virtual block to async.
*/
int nvm_vblk_set_async(struct nvm_vblk *vblk, uint32_t depth);
/**
* Set the command mode for the virtual block to scalar.
*/
int nvm_vblk_set_scalar(struct nvm_vblk *vblk);
/**
* Destroy a virtual block
*
* @param vblk The virtual block to destroy
*/
void nvm_vblk_free(struct nvm_vblk *vblk);
/**
* Erase a virtual block
*
* @note
* Erasing a vblk will reset internal position pointers
*
* @param vblk The virtual block to erase
* @returns On success, the number of bytes erased is returned. On error, -1 is
* returned and `errno` set to indicate the error.
*/
ssize_t nvm_vblk_erase(struct nvm_vblk *vblk);
/**
* Write to a virtual block
*
* @note
* buf must be aligned to device geometry, see struct nvm_geo and nvm_buf_alloc
* count must be a multiple of min-size, see struct nvm_geo
* do not mix use of nvm_vblk_pwrite with nvm_vblk_write on the same virtual
* block
*
* @param vblk The virtual block to write to
* @param buf Write content starting at buf
* @param count The number of bytes to write
* @returns On success, the number of bytes written is returned and vblk
* internal position is updated. On error, -1 is returned and `errno` set to
* indicate the error.
*/
ssize_t nvm_vblk_write(struct nvm_vblk *vblk, const void *buf, size_t count);
/**
* Write to a virtual block at a given offset
*
* @note
* buf must be aligned to device geometry, see struct nvm_geo and nvm_buf_alloc
* count must be a multiple of min-size, see struct nvm_geo
* offset must be a multiple of min-size, see struct nvm_geo
* do not mix use of nvm_vblk_pwrite with nvm_vblk_write on the same virtual
* block
*
* @param vblk The virtual block to write to
* @param buf Write content starting at buf
* @param count The number of bytes to write
* @param offset Start writing offset bytes within virtual block
* @returns On success, the number of bytes written is returned. On error, -1 is
* returned and `errno` set to indicate the error.
*/
ssize_t nvm_vblk_pwrite(struct nvm_vblk *vblk, const void *buf, size_t count,
size_t offset);
/**
* Pad the virtual block with synthetic data
*
* @note
* Assumes that you have used nvm_vblk_write and now want to fill the remaining
* virtual block in order to meet block write-before-read constraints
*
* @param vblk The virtual block to pad
* @returns On success, the number of bytes padded is returned and internal
* position is updated. On error, -1 is returned and `errno` set to indicate the
* error.
*/
ssize_t nvm_vblk_pad(struct nvm_vblk *vblk);
/**
* Read from a virtual block
*/
ssize_t nvm_vblk_read(struct nvm_vblk *vblk, void *buf, size_t count);
/**
* Read from a virtual block at given offset
*/
ssize_t nvm_vblk_pread(struct nvm_vblk *vblk, void *buf, size_t count,
size_t offset);
/**
* Copy the virtual block 'src' to the virtual block 'dst'
*
* @returns On success, the number of bytes copied is returned. On error, -1 is
* returned and `errno` set to indicate the error.
*/
ssize_t nvm_vblk_copy(struct nvm_vblk *src, struct nvm_vblk *dst, int flags);
/**
* Retrieve the device associated with the given virtual block
*
* @param vblk The entity to retrieve information from
*/
struct nvm_dev *nvm_vblk_get_dev(struct nvm_vblk *vblk);
/**
* Retrieve the set of addresses defining the virtual block
*
* @param vblk The entity to retrieve information from
*/
struct nvm_addr *nvm_vblk_get_addrs(struct nvm_vblk *vblk);
/**
* Retrieve the number of addresses in the address set of the virtual block
*
* @param vblk The entity to retrieve information from
*/
int nvm_vblk_get_naddrs(struct nvm_vblk *vblk);
/**
* Retrieve the size, in bytes, of a given virtual block
*
* @param vblk The entity to retrieve information from
*/
size_t nvm_vblk_get_nbytes(struct nvm_vblk *vblk);
/**
* Retrieve the read cursor position for the given virtual block
*
* @param vblk The entity to retrieve information from
*/
size_t nvm_vblk_get_pos_read(struct nvm_vblk *vblk);
/**
* Retrieve the write cursor position for the given virtual block
*
* @param vblk The entity to retrieve information from
*/
size_t nvm_vblk_get_pos_write(struct nvm_vblk *vblk);
/**
* Set the read cursor position for the given virtual block
*
* @param vblk The vblk to change
* @param pos The new read cursor
*
* @returns On success, 0 is returned. On error, -1 is returned and `errno` set
* to indicate the error
*/
int nvm_vblk_set_pos_read(struct nvm_vblk *vblk, size_t pos);
/**
* Set the write cursor position for the given virtual block
*
* @param vblk The vblk to change
* @param pos The new write cursor
*
* @returns On success, 0 is returned. On error, -1 is returned and `errno` set
* to indicate the error
*/
int nvm_vblk_set_pos_write(struct nvm_vblk *vblk, size_t pos);
/**
* Print the virtual block in a humanly readable form
*
* @param vblk The entity to print information about
*/
void nvm_vblk_pr(struct nvm_vblk *vblk);
/**
* Boilerplate for working with the API
*
* Encapsulated in a struct such that example code can focus on the interesting
* parts and the usual boiler-plate code needed to get things going.
*/
struct nvm_bp {
struct nvm_dev *dev;
const struct nvm_geo *geo;
struct nvm_buf_set *bufs;
struct nvm_vblk *vblk;
size_t ws_opt;
size_t naddrs;
struct nvm_addr addrs[];
};
void nvm_bp_pr(const struct nvm_bp *bp);
/**
* Use argv as 'nvm_bp_init(argv[1], argv[2], argv[3])'
*
* @returns On success, a initialized boiler-plate
*/
struct nvm_bp *nvm_bp_init_from_args(int argc, char **argv);
/**
* argv[3]: naddrs
* argv[2]: Backend Identifier as hex, eg. NVM_BE_IOCTL(0x1), NVM_BE_SPDK(0x4)
* argv[1]: Device identifier, eg. "/dev/nvme0n1" or "traddrs:0000:00:01.0"
*/
struct nvm_bp *nvm_bp_init(const char *dev_ident, int flags, int naddrs);
void nvm_bp_term(struct nvm_bp *bp);
#ifdef __cplusplus
}
#endif
#endif /* __LIBLIGHTNVM.H */