Code Assumptions#
These are notes on contracts and assumptions in the xNVMe codebase. These are written down separate from the code for more convenient reading. Thus, whenever code-changes are made that alter assumptions and contracts described herein, then this document must be updated as well as part of the change-set.
The device handle contract#
Device handles are opaque pointers of type xnvme_dev
.
Device handles are obtained via the public xNVMe API
call xnvme_dev_open()
, and released via a call
to xnvme_dev_close()
.
The entities involved in the implementation of this and their individual responsibility in setting up the data-structure is described in this section here.
These functions are implemented in the library-private device module
(xnvme_dev.c
). The device module is responsible for allocating
and initializing the data-structure associated with and encapsulating the
device handle upon xnvme_dev_open()
and freeing these resources
upon xnvme_dev_close()
.
Device handles are system interface specific; file-descriptors on UNIX-like
systems, opaque pointers on Windows, typed pointers in user-space drivers
such as SPDK, etc. Thus, the device module is only responsible for generic
initialization, it delagates these system specifics to the backend module
(xnvme_be.c
).
The library-private backend module in turn, defines a backend
interface (xnvme_be.h
) and implements a backend factory
(xnvme_be_factory()
). The backend factory knows of all
backends implementing the backend interface.
Thus, to obtain a system specific handle it delegates to each backend,
in turn, the implementation of obtaining a system interface specific device
handle, for a given device identifier, is handled by the first backend
implementation (xnvme_be_*_dev.c
) capable of producing a handle.
In other words, the backend factory chooses the first backend capable of returning a system interface specific device handle and the system interface specific handle produced by the backend.
The entities described above are those involved in the setup of a xNVMe device handle. The xNVMe device handle is opaque, that is the details of its representation is hidden from the user. Thus, functions are provided to retrieve data about a given device handle.
However, described here are the details on how the representation is
constructed by the library-private: device module, backend module,
and instances of the backend interface implementation aka backends
(xnvme_be_{spdk,vfio,linux,fbsd,windows}_*.c`
).
Identification and Geometry#
The structure encapsulating the device handle is library-private, however, for contributions to backends, device module, and related, then a bit of info on how it is setup is useful, it is provided below.
# Assigned by xnvme_be_factory()
dev.be
# The backend-factory copies the opts and adjusts them to the options
# describing the backend, thus showing which "configuration" it ended up with
dev.opts
# Initialized by xnvme_dev_open() using helper xnvme_ident_from_uri(), then
# backends further manipulate the structure
dev.ident
# These are setup by the backend as it is "opening" the device, here it might
# do simple things such as fstat() on Linux to determine whether the device
# handle is a block-device or a char-device to indicate the ident.dtype
dev.ident.dtype
# The device module assigns this in the identification-helper _dev_idfy()
dev.ident.subnqn
# Each backend-implementation sets these up after they have created a
# system-interface-specific device handle
dev.ident.dtype
dev.ident.nsid
dev.ident.csi
dev.is_derived # setup by xnvme_dev_get_*() access after calling xnvme_dev_derive_geometry()
# These are assigned by the device module at "identification-time"
dev.id.ctrlr
dev.id.ns
dev.idcss.ctrlr
dev.idcss.ns
dev.attempted_dev_idfy
# Populated by the device module using the helper xnvme_dev_derive_geometry()
dev.geo
# Set by xnvme_dev_derive_geometry()
dev.attempted_derive_geo