root/src/add-ons/kernel/drivers/disk/nvme/libnvme/nvme.h
/*-
 *   BSD LICENSE
 *
 *   Copyright (c) Intel Corporation. All rights reserved.
 *   Copyright (c) 2017, Western Digital Corporation or its affiliates.
 *
 *   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.
 *     * Neither the name of Intel Corporation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
 *   OWNER 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
 * NVMe driver public API
 *
 * @mainpage
 *
 * libnvme is a user space utility to provide control over NVMe,
 * the host controller interface for drives based on PCI Express.
 *
 * \addtogroup libnvme
 *  @{
 */

#ifndef __LIBNVME_H__
#define __LIBNVME_H__

#ifdef __cplusplus
extern "C" {
#endif

#include <libnvme/nvme_spec.h>

#ifndef __HAIKU__
#include <pciaccess.h>
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>

/**
 * Log levels.
 */
enum nvme_log_level {

        /**
         * Disable all log messages.
         */
        NVME_LOG_NONE = 0,

        /**
         * System is unusable.
         */
        NVME_LOG_EMERG,

        /**
         * Action must be taken immediately.
         */
        NVME_LOG_ALERT,

        /**
         * Critical conditions.
         */
        NVME_LOG_CRIT,

        /**
         * Error conditions.
         */
        NVME_LOG_ERR,

        /**
         * Warning conditions.
         */
        NVME_LOG_WARNING,

        /**
         * Normal but significant condition.
         */
        NVME_LOG_NOTICE,

        /**
         * Informational messages.
         */
        NVME_LOG_INFO,

        /**
         * Debug-level messages.
         */
        NVME_LOG_DEBUG,

};

/**
 * Log facilities.
 */
enum nvme_log_facility {

        /**
         * Standard output log facility
         */
        NVME_LOG_STDOUT = 0x00000001,

        /**
         * Regular file output log facility
         */
        NVME_LOG_FILE = 0x00000002,

        /**
         * syslog service output log facility
         */
        NVME_LOG_SYSLOG = 0x00000004,

};

/**
 * @brief Initialize libnvme
 *
 * @param level Library log level
 * @param facility      Facility code
 * @param path          File name for the NVME_LOG_FILE facility
 *
 * This function must always be called first before any other
 * function provided by libnvme. The arguments allow setting the
 * initial log level and log facility so that any problem during
 * initialization can be caught.
 *
 * @return 0 on success and a negative error code on failure.
 */
extern int nvme_lib_init(enum nvme_log_level level,
                         enum nvme_log_facility facility, const char *path);

/**
 * @brief Set the library log level
 *
 * @param level Library log level
 */
extern void nvme_set_log_level(enum nvme_log_level level);

/**
 * @brief Get the current log level
 *
 * @return The current library log level.
 */
extern enum nvme_log_level nvme_get_log_level(void);

/**
 * @brief Change the library log facility
 *
 * @param facility      Facility code
 * @param path          File name for the NVME_LOG_FILE facility
 *
 * Set th library log facility. On failure, the facility is
 * always automatically set to stdout.
 *
 * @return 0 on success and a negative error code on failure.
 */
extern int nvme_set_log_facility(enum nvme_log_facility facility,
                                 const char *path);

/**
 * @brief Get the current library log facility.
 *
 * @return The current library log facility.
 */
extern enum nvme_log_facility nvme_get_log_facility(void);

/**
 * @brief Opaque handle to a controller returned by nvme_ctrlr_open().
 */
struct nvme_ctrlr;

/**
 * @brief Opaque handle to a namespace
 */
struct nvme_ns;

/**
 * @brief Opaque handle to an I/O queue pair
 */
struct nvme_qpair;

/**
 * @brief Capabilities register of a controller
 */
struct nvme_register_data {

        /**
         * Maximum Queue Entries Supported indicates the maximum individual
         * queue size that the controller supports. This is a 0’s based value,
         * so 1 has to be added.
         */
        unsigned int            mqes;

};

/**
 * Length of the string for the serial number
 */
#define NVME_SERIAL_NUMBER_LENGTH       NVME_SERIAL_NUMBER_CHARACTERS + 1

/**
 * Length of the string for the model number
 */
#define NVME_MODEL_NUMBER_LENGTH        NVME_MODEL_NUMBER_CHARACTERS + 1

/**
 * @brief Controller information
 */
struct nvme_ctrlr_stat {

        /**
         * PCI device vendor ID.
         */
        unsigned short          vendor_id;

        /**
         * PCI device ID.
         */
        unsigned short          device_id;

        /**
         * PCI device sub-vendor ID.
         */
        unsigned short          subvendor_id;

        /**
         * PCI sub-device ID.
         */
        unsigned short          subdevice_id;

        /**
         * PCI device class.
         */
        unsigned int            device_class;

        /**
         * PCI device revision.
         */
        unsigned char           revision;

        /**
         * PCI slot domain.
         */
        unsigned int            domain;

        /**
         * PCI slot bus.
         */
        unsigned int            bus;

        /**
         * PCI slot bus device number.
         */
        unsigned int            dev;

        /**
         * PCI slot device function.
         */
        unsigned int            func;

        /**
         * Serial number
         */
        char                    sn[NVME_SERIAL_NUMBER_LENGTH];

        /**
         * Model number
         */
        char                    mn[NVME_MODEL_NUMBER_LENGTH];

        /**
         * Maximum transfer size.
         */
        size_t                  max_xfer_size;

        /**
         * All the log pages supported.
         */
        bool                    log_pages[256];

        /**
         * Whether SGL is supported by the controller.
         *
         * Note that this does not mean all SGL requests will fail;
         * many are convertible into standard (PRP) requests by libnvme.
         */
        bool                    sgl_supported;

        /**
         * All the features supported.
         */
        bool                    features[256];

        /**
         * Number of valid namespaces in the array of namespace IDs.
         */
        unsigned int            nr_ns;

        /**
         * Array of valid namespace IDs of the controller.
         * Namspeace IDs are integers between 1 and NVME_MAX_NS
         */
        unsigned int            ns_ids[NVME_MAX_NS];

        /**
         * Maximum number of I/O queue pairs
         */
        unsigned int            max_io_qpairs;

        /**
         * Number of I/O queue pairs allocated
         */
        unsigned int            io_qpairs;

        /**
         * Number of I/O queue pairs enabled
         */
        unsigned int            enabled_io_qpairs;

        /**
         * IO qpairs maximum entries
         */
        unsigned int            max_qd;
};

/**
 * @brief NVMe controller options
 *
 * Allow the user to request non-default options.
 */
struct nvme_ctrlr_opts {

        /**
         * Number of I/O queues to initialize.
         * (default: all possible I/O queues)
         */
        unsigned int            io_queues;

        /**
         * Enable submission queue in controller memory buffer
         * (default: false)
         */
        bool                    use_cmb_sqs;

        /**
         * Type of arbitration mechanism.
         * (default: round-robin == NVME_CC_AMS_RR)
         */
        enum nvme_cc_ams        arb_mechanism;

};

/**
 * @brief Namespace command support flags
 */
enum nvme_ns_flags {

        /**
         * The deallocate command is supported.
         */
        NVME_NS_DEALLOCATE_SUPPORTED    = 0x1,

        /**
         * The flush command is supported.
         */
        NVME_NS_FLUSH_SUPPORTED         = 0x2,

        /**
         * The reservation command is supported.
         */
        NVME_NS_RESERVATION_SUPPORTED   = 0x4,

        /**
         * The write zeroes command is supported.
         */
        NVME_NS_WRITE_ZEROES_SUPPORTED  = 0x8,

        /**
         * The end-to-end data protection is supported.
         */
        NVME_NS_DPS_PI_SUPPORTED        = 0x10,

        /**
         * The extended lba format is supported, metadata is transferred as
         * a contiguous part of the logical block that it is associated with.
         */
        NVME_NS_EXTENDED_LBA_SUPPORTED  = 0x20,

};

/**
 * @brief Namespace information
 */
struct nvme_ns_stat {

        /**
         * Namespace ID.
         */
        unsigned int                    id;

        /**
         * Namespace command support flags.
         */
        enum nvme_ns_flags              flags;

        /**
         * Namespace sector size in bytes.
         */
        size_t                          sector_size;

        /**
         * Namespace number of sectors.
         */
        uint64_t                        sectors;

        /**
         * Namespace metadata size in bytes.
         */
        size_t                          md_size;

        /**
         * Namespace priority information type.
         */
        enum nvme_pi_type               pi_type;

};

/**
 * @brief Queue pair information
 */
struct nvme_qpair_stat {

        /**
         * Qpair ID
         */
        unsigned int            id;

        /**
         * Qpair number of entries
         */
        unsigned int            qd;

        /**
         * Qpair is enabled
         */
        bool                    enabled;

        /**
         * Qpair priority
         */
        unsigned int            qprio;
};

/**
 * @brief Command completion callback function signature
 *
 * @param cmd_cb_arg    Callback function input argument.
 * @param cpl_status    Contains the completion status.
 */
typedef void (*nvme_cmd_cb)(void *cmd_cb_arg,
                            const struct nvme_cpl *cpl_status);

/**
 * @brief Asynchronous error request completion callback
 *
 * @param aer_cb_arg    AER context set by nvme_register_aer_callback()
 * @param cpl_status    Completion status of the asynchronous event request
 */
typedef void (*nvme_aer_cb)(void *aer_cb_arg,
                            const struct nvme_cpl *cpl_status);

/**
 * @brief Restart SGL walk to the specified offset callback
 *
 * @param cb_arg        Value passed to nvme_readv/nvme_writev
 * @param offset        Offset in the SGL
 */
typedef void (*nvme_req_reset_sgl_cb)(void *cb_arg, uint32_t offset);

/**
 * @brief Get an SGL entry address and length and advance to the next entry
 *
 * @param cb_arg        Value passed to readv/writev
 * @param address       Physical address of this segment
 * @param length        Length of this physical segment
 *
 * Fill out address and length with the current SGL entry and advance
 * to the next entry for the next time the callback is invoked
 */
typedef int (*nvme_req_next_sge_cb)(void *cb_arg,
                                    uint64_t *address, uint32_t *length);

/**
 * @brief Open an NVMe controller
 *
 * @param url   PCI device URL
 * @param opts  controller options
 *
 * Obtain a handle for an NVMe controller specified as a PCI device URL,
 * e.g. pci://[DDDD:]BB:DD.F. If called more than once for the same
 * controller, NULL is returned.
 * To stop using the the controller and release its associated resources,
 * call nvme_ctrlr_close() with the handle returned by this function.
 *
 * @return A handle to the controller on success and NULL on failure.
 */
struct pci_device {
        uint16_t  vendor_id;
        uint16_t  device_id;
        uint16_t  subvendor_id;
        uint16_t  subdevice_id;

        uint16_t domain;
        uint16_t bus;
        uint16_t dev;
        uint16_t func;

        void* pci_info;
};

extern struct nvme_ctrlr * nvme_ctrlr_open(struct pci_device *pdev,
                                           struct nvme_ctrlr_opts *opts);

/**
 * @brief Close an open NVMe controller
 *
 * @param ctrlr Controller handle
 *
 * This function should be called while no other threads
 * are actively using the controller.
 *
 * @return 0 on success and a negative error code on failure.
 */
extern int nvme_ctrlr_close(struct nvme_ctrlr *ctrlr);

/**
 * @brief Get controller capabilities and features
 *
 * @param ctrlr Controller handle
 * @param cstat Controller information
 *
 * @return 0 on success and a negative error code on failure.
 */
extern int nvme_ctrlr_stat(struct nvme_ctrlr *ctrlr,
                           struct nvme_ctrlr_stat *cstat);

/**
 * @brief Get controller data and some data from the capabilities register
 *
 * @param ctrlr Controller handle
 * @param cdata Controller data to fill
 * @param rdata Capabilities register data to fill
 *
 * cdata and rdata are optional (NULL can be specified).
 *
 * @return 0 on success and a negative error code on failure.
 */
extern int nvme_ctrlr_data(struct nvme_ctrlr *ctrlr,
                           struct nvme_ctrlr_data *cdata,
                           struct nvme_register_data *rdata);

/**
 * @brief Get a specific feature of a controller
 *
 * @param ctrlr         Controller handle
 * @param sel           Feature selector
 * @param feature       Feature identifier
 * @param cdw11         Command word 11 (command dependent)
 * @param attributes    Features attributes
 *
 * This function is thread safe and can be called at any point while
 * the controller is attached.
 *
 * @return 0 on success and a negative error code on failure.
 *
 * See nvme_ctrlr_set_feature()
 */
extern int nvme_ctrlr_get_feature(struct nvme_ctrlr *ctrlr,
                                  enum nvme_feat_sel sel,
                                  enum nvme_feat feature,
                                  uint32_t cdw11, uint32_t *attributes);

/**
 * @brief Set a specific feature of a controller
 *
 * @param ctrlr         Controller handle
 * @param save          Save feature across power cycles
 * @param feature       Feature identifier
 * @param cdw11         Command word 11 (feature dependent)
 * @param cdw12         Command word 12 (feature dependent)
 * @param attributes    Features attributes
 *
 * This function is thread safe and can be called at any point while
 * the controller is attached to the NVMe driver.
 *
 * @return 0 on success and a negative error code on failure.
 *
 * See nvme_ctrlr_get_feature()
 */
extern int nvme_ctrlr_set_feature(struct nvme_ctrlr *ctrlr,
                                  bool save, enum nvme_feat feature,
                                  uint32_t cdw11, uint32_t cdw12,
                                  uint32_t cdw13, uint32_t cdw14, uint32_t cdw15,
                                  void *buf, uint32_t len,
                                  uint32_t *attributes);

/**
 * @brief Attach the specified namespace to controllers
 *
 * @param ctrlr Controller handle to use for command submission
 * @param nsid  Namespace ID of the namespaces to attach
 * @param clist List of controllers as defined in the NVMe specification
 *
 * @return 0 on success and a negative error code on failure.
 */
extern int nvme_ctrlr_attach_ns(struct nvme_ctrlr *ctrlr, unsigned int nsid,
                                struct nvme_ctrlr_list *clist);

/**
 * @brief Detach the specified namespace from controllers
 *
 * @param ctrlr Controller handle to use for command submission
 * @param nsid  Namespace ID of the namespaces to detach
 * @param clist List of controllers as defined in the NVMe specification
 *
 * @return 0 on success and a negative error code on failure.
 */
extern int nvme_ctrlr_detach_ns(struct nvme_ctrlr *ctrlr, unsigned int nsid,
                                struct nvme_ctrlr_list *clist);

/**
 * @brief Create a namespace
 *
 * @param ctrlr         Controller handle
 * @param nsdata        namespace data
 *
 * @return Namespace ID (>= 1) on success and 0 on failure.
 */
extern unsigned int nvme_ctrlr_create_ns(struct nvme_ctrlr *ctrlr,
                                         struct nvme_ns_data *nsdata);

/**
 * @brief Delete a namespace
 *
 * @param ctrlr Controller handle
 * @param nsid  ID of the namespace to delete
 *
 * @return 0 on success and a negative error code on failure.
 */
extern int nvme_ctrlr_delete_ns(struct nvme_ctrlr *ctrlr, unsigned int nsid);

/**
 * @brief Format media
 *
 * @param ctrlr         Controller handle
 * @param nsid          ID of the namespace to format
 * @param format        Format information
 *
 * This function requests a low-level format of the media.
 * If nsid is NVME_GLOBAL_NS_TAG, all namspaces attached to the contoller
 * are formatted.
 *
 * @return 0 on success and a negative error code on failure.
 */
extern int nvme_ctrlr_format_ns(struct nvme_ctrlr *ctrlr,
                                unsigned int nsid, struct nvme_format *format);

/**
 * @brief Download a new firmware image
 *
 * @param ctrlr Controller handle
 * @param fw    Firmware data buffer
 * @param size  Firmware buffer size
 * @param slot  Firmware image slot to use
 *
 * @return 0 on success and a negative error code on failure.
 */
extern int nvme_ctrlr_update_firmware(struct nvme_ctrlr *ctrlr,
                                      void *fw, size_t size, int slot);

/**
 * @brief Get an I/O queue pair
 *
 * @param ctrlr Controller handle
 * @param qprio I/O queue pair priority for weighted round robin arbitration
 * @param qd    I/O queue pair maximum submission queue depth
 *
 * A queue depth of 0 will result in the maximum hardware defined queue
 * depth being used. The use of a queue pair is not thread safe. Applications
 * must ensure mutual exclusion access to the queue pair during I/O processing.
 *
 * @return An I/O queue pair handle on success and NULL in case of failure.
 */
extern struct nvme_qpair * nvme_ioqp_get(struct nvme_ctrlr *ctrlr,
                                         enum nvme_qprio qprio,
                                         unsigned int qd);

/**
 * @brief Release an I/O queue pair
 *
 * @param qpair I/O queue pair handle
 *
 * @return 0 on success and a negative error code on failure.
 */
extern int nvme_ioqp_release(struct nvme_qpair *qpair);

/**
 * @brief Get information on an I/O queue pair
 *
 * @param qpair         I/O queue pair handle
 * @param qpstat        I/O queue pair information to fill
 *
 * @return 0 on success and a negative error code on failure.
 */
extern int nvme_qpair_stat(struct nvme_qpair *qpair,
                           struct nvme_qpair_stat *qpstat);

/**
 * @brief Submit an NVMe command
 *
 * @param qpair         I/O qpair handle
 * @param cmd           Command to submit
 * @param buf           Payload buffer
 * @param len           Payload buffer length
 * @param cb_fn         Callback function
 * @param cb_arg        Argument for the call back function
 *
 * This is a low level interface for submitting I/O commands directly.
 * The validity of the command will not be checked.
 *
 * When constructing the nvme_command it is not necessary to fill out the PRP
 * list/SGL or the CID. The driver will handle both of those for you.
 *
 * @return 0 on success and a negative error code on failure.
 */
extern int nvme_ioqp_submit_cmd(struct nvme_qpair *qpair,
                                struct nvme_cmd *cmd,
                                void *buf, size_t len,
                                nvme_cmd_cb cb_fn, void *cb_arg);

/**
 * @brief Process I/O command completions
 *
 * @param qpair                 I/O queue pair handle
 * @param max_completions       Maximum number of completions to check
 *
 * This call is non-blocking, i.e. it only processes completions that are
 * ready at the time of this function call. It does not wait for
 * outstanding commands to complete.
 * For each completed command, the request callback function will
 * be called if specified as non-NULL when the request was submitted.
 * This function may be called at any point after the command submission
 * while the controller is open
 *
 * @return The number of completions processed (may be 0).
 *
 * @sa nvme_cmd_cb
 */
extern unsigned int nvme_qpair_poll(struct nvme_qpair *qpair,
                                   unsigned int max_completions);

/**
 * @brief Open a name space
 *
 * @param ctrlr Controller handle
 * @param ns_id ID of the name space to open
 *
 * @return A namspace handle on success or NULL in case of failure.
 */
extern struct nvme_ns *nvme_ns_open(struct nvme_ctrlr *ctrlr,
                                    unsigned int ns_id);

/**
 * @brief Close an open name space
 *
 * @param ns    Namspace handle
 *
 * See nvme_ns_open()
 */
extern int nvme_ns_close(struct nvme_ns *ns);

/**
 * @brief Get information on a namespace
 *
 * @param ns            Namespace handle
 * @param ns_stat       Namespace information
 *
 * @return 0 on success and a negative error code in case of failure.
 */
extern int nvme_ns_stat(struct nvme_ns *ns,
                        struct nvme_ns_stat *ns_stat);

/**
 * @brief Get namespace data
 *
 * @param ns            Namespace handle
 * @param nsdata        Namespace data
 *
 * @return 0 on success and a negative error code in case of failure.
 */
extern int nvme_ns_data(struct nvme_ns *ns,
                        struct nvme_ns_data *nsdata);

/**
 * @brief Submit a write I/O
 *
 * @param ns            Namespace handle
 * @param qpair         I/O queue pair handle
 * @param buffer        Physically contiguous data buffer
 * @param lba           Starting LBA to read from
 * @param lba_count     Number of LBAs to read
 * @param cb_fn         Completion callback
 * @param cb_arg        Argument to pass to the completion callback
 * @param io_flags      I/O flags (NVME_IO_FLAGS_*)
 *
 * @return 0 on success and a negative error code in case of failure.
 */
extern int nvme_ns_write(struct nvme_ns *ns, struct nvme_qpair *qpair,
                         void *buffer,
                         uint64_t lba, uint32_t lba_count,
                         nvme_cmd_cb cb_fn, void *cb_arg,
                         unsigned int io_flags);

/**
 * @brief Submit a scattered write I/O
 *
 * @param ns            Namespace handle
 * @param qpair         I/O queue pair handle
 * @param lba           Starting LBA to write to
 * @param lba_count     Number of LBAs to write
 * @param cb_fn         Completion callback
 * @param cb_arg        Argument to pass to the completion callback
 * @param io_flags      I/O flags (NVME_IO_FLAGS_*)
 * @param reset_sgl_fn  Reset scattered payload callback
 * @param next_sge_fn   Scattered payload iteration callback
 *
 * @return 0 on success and a negative error code in case of failure.
 */
extern int nvme_ns_writev(struct nvme_ns *ns, struct nvme_qpair *qpair,
                          uint64_t lba, uint32_t lba_count,
                          nvme_cmd_cb cb_fn, void *cb_arg,
                          unsigned int io_flags,
                          nvme_req_reset_sgl_cb reset_sgl_fn,
                          nvme_req_next_sge_cb next_sge_fn);

/**
 * @brief Submits a write I/O with metadata
 *
 * @param ns            Namespace handle
 * @param qpair         I/O queue pair handle
 * @param payload       Data buffer
 * @param metadata      Metadata payload
 * @param lba           Starting LBA to write to
 * @param lba_count     Number of LBAs to write
 * @param cb_fn         Completion callback
 * @param cb_arg        Argument to pass to the completion callback
 * @param io_flags      I/O flags (NVME_IO_FLAGS_*)
 * @param apptag_mask   Application tag mask
 * @param apptag        Application tag to use end-to-end protection information
 *
 * @return 0 on success and a negative error code in case of failure.
 */
extern int nvme_ns_write_with_md(struct nvme_ns *ns, struct nvme_qpair *qpair,
                                 void *payload, void *metadata,
                                 uint64_t lba, uint32_t lba_count,
                                 nvme_cmd_cb cb_fn, void *cb_arg,
                                 unsigned int io_flags,
                                 uint16_t apptag_mask, uint16_t apptag);

/**
 * @brief Submit a write zeroes I/O
 *
 * @param ns            Namespace handle
 * @param qpair         I/O queue pair handle
 * @param lba           Starting LBA to write to
 * @param lba_count     Number of LBAs to write
 * @param cb_fn         Completion callback
 * @param cb_arg        Argument to pass to the completion callback
 * @param io_flags      I/O flags (NVME_IO_FLAGS_*)
 *
 * @return 0 on success and a negative error code in case of failure.
 */
extern int nvme_ns_write_zeroes(struct nvme_ns *ns, struct nvme_qpair *qpair,
                                uint64_t lba, uint32_t lba_count,
                                nvme_cmd_cb cb_fn, void *cb_arg,
                                unsigned int io_flags);

/**
 * @brief Submit a read I/O
 *
 * @param ns            Namespace handle
 * @param qpair         I/O queue pair handle
 * @param buffer        Physically contiguous data buffer
 * @param lba           Starting LBA to read from
 * @param lba_count     Number of LBAs to read
 * @param cb_fn         Completion callback
 * @param cb_arg        Argument to pass to the completion callback
 * @param io_flags      I/O flags (NVME_IO_FLAGS_*)
 *
 * @return 0 on success and a negative error code in case of failure.
 */
extern int nvme_ns_read(struct nvme_ns *ns, struct nvme_qpair *qpair,
                        void *buffer,
                        uint64_t lba, uint32_t lba_count,
                        nvme_cmd_cb cb_fn, void *cb_arg,
                        unsigned int io_flags);

/**
 * @brief Submit a scattered read I/O
 *
 * @param ns            Namespace handle
 * @param qpair         I/O queue pair handle
 * @param lba           Starting LBA to read from
 * @param lba_count     Number of LBAs to read
 * @param cb_fn         Completion callback
 * @param cb_arg        Argument to pass to the completion callback
 * @param io_flags      I/O flags (NVME_IO_FLAGS_*)
 * @param reset_sgl_fn  Reset scattered payload callback
 * @param next_sge_fn   Scattered payload iteration callback
 *
 * @return 0 on success and a negative error code in case of failure.
 */
extern int nvme_ns_readv(struct nvme_ns *ns, struct nvme_qpair *qpair,
                         uint64_t lba, uint32_t lba_count,
                         nvme_cmd_cb cb_fn, void *cb_arg,
                         unsigned int io_flags,
                         nvme_req_reset_sgl_cb reset_sgl_fn,
                         nvme_req_next_sge_cb next_sge_fn);

/**
 * @brief Submit a read I/O with metadata
 *
 * @param ns            Namespace handle
 * @param qpair         I/O queue pair handle
 * @param buffer        Data buffer
 * @param metadata      Metadata payload
 * @param lba           Starting LBA to read from
 * @param lba_count     Number of LBAs to read
 * @param cb_fn         Completion callback
 * @param cb_arg        Argument to pass to the completion callback
 * @param io_flags      I/O flags (NVME_IO_FLAGS_*)
 * @param apptag_mask   Application tag mask
 * @param apptag        Application tag to use end-to-end protection information
 *
 * @return 0 on success and a negative error code in case of failure.
 */
extern int nvme_ns_read_with_md(struct nvme_ns *ns, struct nvme_qpair *qpair,
                                void *buffer, void *metadata,
                                uint64_t lba, uint32_t lba_count,
                                nvme_cmd_cb cb_fn, void *cb_arg,
                                unsigned int io_flags,
                                uint16_t apptag_mask, uint16_t apptag);

/**
 * @brief Submit a deallocate command
 *
 * @param ns            Namespace handle
 * @param qpair         I/O queue pair handle
 * @param payload       List of LBA ranges to deallocate
 * @param num_ranges    Number of ranges in the list
 * @param cb_fn         Completion callback
 * @param cb_arg        Argument to pass to the completion callback
 *
 * The number of LBA ranges must be at least 1 and at most
 * NVME_DATASET_MANAGEMENT_MAX_RANGES.
 *
 * @return 0 on success and a negative error code in case of failure.
 */
extern int nvme_ns_deallocate(struct nvme_ns *ns, struct nvme_qpair *qpair,
                              void *payload, uint16_t num_ranges,
                              nvme_cmd_cb cb_fn, void *cb_arg);

/**
 * @brief Submit a flush command
 *
 * @param ns            Namespace handle
 * @param qpair         I/O queue pair handle
 * @param cb_fn         Completion callback
 * @param cb_arg        Argument to pass to the completion callback
 *
 * @return 0 on success and a negative error code in case of failure.
 */
extern int nvme_ns_flush(struct nvme_ns *ns, struct nvme_qpair *qpair,
                         nvme_cmd_cb cb_fn, void *cb_arg);

/**
 * @brief Submit a reservation register command
 *
 * @param ns            Namespace handle
 * @param qpair         I/O queue pair handle
 * @param payload       Reservation register data buffer
 * @param ignore_key    Enable or not the current reservation key check
 * @param action        Registration action
 * @param cptpl         Persist Through Power Loss state
 * @param cb_fn         Completion callback
 * @param cb_arg        Argument to pass to the completion callback
 *
 * @return 0 on success and a negative error code in case of failure.
 */
extern int nvme_ns_reservation_register(struct nvme_ns *ns,
                                struct nvme_qpair *qpair,
                                struct nvme_reservation_register_data *payload,
                                bool ignore_key,
                                enum nvme_reservation_register_action action,
                                enum nvme_reservation_register_cptpl cptpl,
                                nvme_cmd_cb cb_fn, void *cb_arg);

/**
 * @brief Submit a reservation release command
 *
 * @param ns            Namespace handle
 * @param qpair         I/O queue pair handle
 * @param payload       Current reservation key buffer
 * @param ignore_key    Enable or not the current reservation key check
 * @param action        Reservation release action
 * @param type          Reservation type
 * @param cb_fn         Completion callback
 * @param cb_arg        Argument to pass to the completion callback
 *
 * @return 0 on success and a negative error code in case of failure.
 */
extern int nvme_ns_reservation_release(struct nvme_ns *ns,
                               struct nvme_qpair *qpair,
                               struct nvme_reservation_key_data *payload,
                               bool ignore_key,
                               enum nvme_reservation_release_action action,
                               enum nvme_reservation_type type,
                               nvme_cmd_cb cb_fn, void *cb_arg);

/**
 * @brief Submit a reservation acquire command
 *
 * @param ns            Namespace handle
 * @param qpair         I/O queue pair handle
 * @param payload       Reservation acquire data buffer
 * @param ignore_key    Enable or not the current reservation key check
 * @param action        Reservation acquire action
 * @param type          Reservation type
 * @param cb_fn         Completion callback
 * @param cb_arg        Argument to pass to the completion callback
 *
 * @return 0 on success and a negative error code in case of failure.
 */
extern int nvme_ns_reservation_acquire(struct nvme_ns *ns,
                                struct nvme_qpair *qpair,
                                struct nvme_reservation_acquire_data *payload,
                                bool ignore_key,
                                enum nvme_reservation_acquire_action action,
                                enum nvme_reservation_type type,
                                nvme_cmd_cb cb_fn, void *cb_arg);

/**
 * @brief Submits a reservation report to a namespace
 *
 * @param ns            Namespace handle
 * @param qpair         I/O queue pair handle
 * @param payload       Reservation status data buffer
 * @param len           Length in bytes of the reservation status data
 * @param cb_fn         Completion callback
 * @param cb_arg        Argument to pass to the completion callback
 *
 * The command is submitted to a qpair allocated by nvme_ctrlr_alloc_io_qpair().
 * The user must ensure that only one thread submits I/O on
 * a given qpair at any given time.
 *
 * @return 0 on success and a negative error code in case of failure.
 */
extern int nvme_ns_reservation_report(struct nvme_ns *ns,
                                      struct nvme_qpair *qpair,
                                      void *payload, size_t len,
                                      nvme_cmd_cb cb_fn, void *cb_arg);

/**
 * Any NUMA node.
 */
#define NVME_NODE_ID_ANY        (~0U)

/**
 * @brief Allocate physically contiguous memory
 *
 * @param size          Size (in bytes) to be allocated
 * @param align         Memory alignment constraint
 * @param node_id       The NUMA node to get memory from or NVME_NODE_ID_ANY
 *
 * This function allocates memory from the hugepage area of memory. The
 * memory is not cleared. In NUMA systems, the memory allocated resides
 * on the requested NUMA node if node_id is not NVME_NODE_ID_ANY.
 * Otherwise, allocation will take preferrably on the node of the
 * function call context, or any other node if that fails.
 *
 * @return The address of the allocated memory on success and NULL on failure.
 */
extern void *nvme_malloc_node(size_t size, size_t align,
                              unsigned int node_id);

/**
 * @brief Allocate zero'ed memory
 *
 * @param size          Size (in bytes) to be allocated
 * @param align         Memory alignment constraint
 * @param node_id       The NUMA node to get memory from or NVME_NODE_ID_ANY
 *
 * See @nvme_malloc_node.
 */
static inline void *nvme_zmalloc_node(size_t size, size_t align,
                                      unsigned int node_id)
{
        void *buf;

        buf = nvme_malloc_node(size, align, node_id);
        if (buf)
                memset(buf, 0, size);

        return buf;
}

/**
 * @brief Allocate zero'ed array memory
 *
 * @param num           Size of the array
 * @param size          Size (in bytes) of the array elements
 * @param align         Memory alignment constraint
 * @param node_id       The NUMA node to get memory from or NVME_NODE_ID_ANY
 *
 * See @nvme_malloc_node.
 */
static inline void *nvme_calloc_node(size_t num, size_t size,
                                     size_t align, unsigned int node_id)
{
        return nvme_zmalloc_node(size * num, align, node_id);
}

/**
 * @brief Allocate physically contiguous memory
 *
 * @param size          Size (in bytes) to be allocated
 * @param align         Memory alignment constraint
 *
 * @return The address of the allocated memory on success and NULL on error
 *
 * See @nvme_malloc_node.
 */
static inline void *nvme_malloc(size_t size, size_t align)
{
        return nvme_malloc_node(size, align, NVME_NODE_ID_ANY);
}

/**
 * @brief Allocate zero'ed memory
 *
 * @param size          Size (in bytes) to be allocated
 * @param align         Memory alignment constraint
 *
 * @return The address of the allocated memory on success and NULL on error
 *
 * See @nvme_zmalloc_node.
 */
static inline void *nvme_zmalloc(size_t size, size_t align)
{
        return nvme_zmalloc_node(size, align, NVME_NODE_ID_ANY);
}

/**
 * @brief Allocate zero'ed array memory
 *
 * @param num           Size of the array
 * @param size          Size (in bytes) of the array elements
 * @param align         Memory alignment constraint
 *
 * See @nvme_calloc_node.
 */
static inline void *nvme_calloc(size_t num, size_t size, size_t align)
{
        return nvme_calloc_node(num, size, align, NVME_NODE_ID_ANY);
}

/**
 * @brief Free allocated memory
 *
 * @param addr  Address of the memory to free
 *
 * Free the memory at the specified address.
 * The address must be one that was returned by one of the
 * allocation function nvme_malloc_node(), nvme_zmalloc_node()
 * or nvme_calloc_node().
 *
 * If the pointer is NULL, the function does nothing.
 */
extern void nvme_free(void *addr);

/**
 * Structure to hold memory statistics.
 */
struct nvme_mem_stats {

        /**
         * Number of huge pages allocated.
         */
        size_t          nr_hugepages;

        /**
         * Total bytes in memory pools.
         */
        size_t          total_bytes;

        /**
         * Total free bytes in memory pools.
         */
        size_t          free_bytes;

};

/**
 * @brief Get memory usage information
 *
 * @param stats         Memory usage inforamtion structure to fill
 * @param node_id       NUMA node ID or NVME_NVME_NODE_ID_ANY
 *
 * Return memory usage statistics for the specified
 * NUMA node (CPU socket) or global memory usage if node_id
 * is NVME_NODE_ID_ANY.
 *
 * @return 0 on success and a negative error code on failure.
 */
extern int nvme_memstat(struct nvme_mem_stats *stats,
                        unsigned int node_id);

/**
 * @}
 */

#ifdef __cplusplus
}
#endif

#endif /* __LIBNVME_H__ */