// SPDX-License-Identifier: MIT or LGPL-2.1-only /** * @file ublksrv.h * * libublksrv APIs * * This header define the interfaces of libublksrv */ #ifndef UBLKSRV_INC_H #define UBLKSRV_INC_H #include #include #include "liburing.h" #ifdef __cplusplus extern "C" { #endif #include "ublk_cmd.h" #define MAX_NR_HW_QUEUES 32 #define MAX_QD UBLK_MAX_QUEUE_DEPTH #define MAX_BUF_SIZE (32U << 20) #define DEF_NR_HW_QUEUES 1 #define DEF_QD 128 #define DEF_BUF_SIZE (512 << 10) /************ stored in ublksrv_ctrl_dev_info->ublksrv_flags *******/ /* * target may not use io_uring for handling io, so eventfd is required * for wakeup io command io_uring context */ #define UBLKSRV_F_NEED_EVENTFD (1UL << 1) struct io_uring; struct io_uring_cqe; struct ublksrv_aio_ctx; struct ublksrv_ctrl_dev; /** * Generic data for creating one ublk control device, which is used for * sending control commands to /dev/ublk-control. * * Control commands(UBLK_CMD_*) are defined in ublk_cmd.h. */ struct ublksrv_dev_data { int dev_id; unsigned max_io_buf_bytes; unsigned short nr_hw_queues; unsigned short queue_depth; const char *tgt_type; const struct ublksrv_tgt_type *tgt_ops; int tgt_argc; char **tgt_argv; const char *run_dir; unsigned long flags; unsigned long ublksrv_flags; unsigned long reserved[7]; }; /** * IO data passed to target io handling callbacks, such as * ->handle_io_async() and ->tgt_io_done(). */ struct ublk_io_data { /** tag of this io data, unique in queue wide */ int tag; unsigned int pad; /** io description from ublk driver */ const struct ublksrv_io_desc *iod; /** * IO private data, created in ublksrv_queue_init(), * data size is specified in ublksrv_tgt_info.io_data_size */ void *private_data; }; /* queue state is only retrieved via ublksrv_queue_state() API */ #define UBLKSRV_QUEUE_STOPPING (1U << 0) #define UBLKSRV_QUEUE_IDLE (1U << 1) #define UBLKSRV_QUEUE_IOCTL_OP (1U << 2) #define UBLKSRV_USER_COPY (1U << 3) /** * ublksrv_queue is 1:1 mapping with ublk driver's blk-mq queue, and * has same queue depth with ublk driver's blk-mq queue. */ struct ublksrv_queue { /** queue id */ int q_id; /** So far, all queues in same device has same depth */ int q_depth; /** io uring for handling io commands() from ublk driver */ struct io_uring *ring_ptr; /** which device this queue belongs to */ const struct ublksrv_dev *dev; /** queue's private data, passed from ublksrv_queue_init() */ void *private_data; }; struct ublksrv_tgt_type; #define UBLKSRV_TGT_MAX_FDS 32 /** * * ublksrv_tgt_info: target data * */ struct ublksrv_tgt_info { /** device size */ unsigned long long dev_size; /** * target ring depth, for handling target IOs */ unsigned int tgt_ring_depth; /** how many FDs regisgered */ unsigned int nr_fds; /** file descriptor table */ int fds[UBLKSRV_TGT_MAX_FDS]; /** target private data */ void *tgt_data; /** * Extra IO slots for each queue, target code can reserve some * slots for handling internal IO, such as meta data IO, then * ublk_io instances can be assigned for these extra IOs. * * IO slot is useful for storing coroutine data which is for * handling this (meta) IO. */ unsigned int extra_ios; /** size of io private data */ unsigned int io_data_size; /** * target io handling type, target main job is to implement * callbacks defined in this type */ const struct ublksrv_tgt_type *ops; /** * If target needs to override default max workers for io_uring, * initialize io_wq_max_workers with proper value, otherwise * keep them as zero */ unsigned int iowq_max_workers[2]; unsigned long reserved[4]; }; /** * ublksrv device */ struct ublksrv_dev { /** device data */ struct ublksrv_tgt_info tgt; }; /** * * ublksrv_tgt_type: target type * */ struct ublksrv_tgt_type { /** * One IO request comes from /dev/ublkbN, so notify target code * for handling the IO. Inside target code, the IO can be handled * with our io_uring too, if this is true, ->tgt_io_done callback * has to be implemented. Otherwise, target can implement * ->handle_event() for processing io completion there. * * Required. */ int (*handle_io_async)(const struct ublksrv_queue *, const struct ublk_io_data *io); /** * target io is handled by our io_uring, and once the target io * is completed, this callback is called. * * Optional, only required iff this target io is handled by ublksrv's * io_uring. */ void (*tgt_io_done)(const struct ublksrv_queue *, const struct ublk_io_data *io, const struct io_uring_cqe *); /** * Someone has written to our eventfd, so let target handle the * event, most of times, it is for handling io completion by * calling ublksrv_complete_io() which has to be run in ubq_daemon * context. * * Follows the typical scenario: * * 1) one target io is completed in target pthread context, so * target code calls ublksrv_queue_send_event for notifying ubq * daemon * * 2) ubq daemon gets notified, so wakeup from io_uring_enter(), * then found eventfd is completed, so call ->handle_event() * * 3) inside ->handle_event(), if any io represented by one io * command is completed, ublksrv_complete_io() is called for * this io. * * 4) after returning from ->handle_event(), ubq_daemon will * queue & submit the eventfd io immediately for getting * notification from future event. * * Optional. Only needed if target IO is handled by target its * own pthread context. */ void (*handle_event)(const struct ublksrv_queue *); /** * One typical use case is to flush meta data, which is usually done * in background. So there isn't any tag from libublksrv for this kind * of IOs, and the target code has to request for allocating extra ios * by passing tgt_type->extra_ios and let this callback consume & handle * these extra IOs. * * nr_queued_io: count of queued IOs in ublksrv_reap_events_uring of * this time * * Optional. */ void (*handle_io_background)(const struct ublksrv_queue *, int nr_queued_io); /** * show target specific command line for adding new device * * Be careful: this callback is the only one which is not run from * ublk device daemon task context. */ void (*usage_for_add)(void); /** * initialize this new target, argc/argv includes target specific * command line parameters * * Required. */ int (*init_tgt)(struct ublksrv_dev *, int type, int argc, char *argv[]); /** * Deinitialize this target * * Optional. */ void (*deinit_tgt)(const struct ublksrv_dev *); /** * callback for allocating io buffer * * Optional. */ void *(*alloc_io_buf)(const struct ublksrv_queue *q, int tag, int size); /** * callback for freeing io buffer * * Optional. */ void (*free_io_buf)(const struct ublksrv_queue *q, void *buf, int tag); /** * Called when the ublksrv io_uring is idle. * * Optional. */ void (*idle_fn)(const struct ublksrv_queue *q, bool enter); /** target type */ int type; /** flags required for ublk driver */ unsigned ublk_flags; /** flags required for ublksrv */ unsigned ublksrv_flags; unsigned pad; /** target name */ const char *name; /** * recovery callback for this target * * Required. */ int (*recovery_tgt)(struct ublksrv_dev *, int type); /** * queue_data_ptr points to address of q->priviate_data, so that * we still can pass 'const struct ublksrv_queue *', meantime * queue data can be stored to q->private_data via queue_data_ptr. * * ->init_queue provides one chance to override/init the passed * "queue_data" to ublksrv_queue_init(), "queue_data" is set to * q->private_data before calling ->init_queue() */ int (*init_queue)(const struct ublksrv_queue *, void **queue_data_ptr); /** deinit queue data, counter pair of ->init_queue */ void (*deinit_queue)(const struct ublksrv_queue *); unsigned long reserved[5]; }; /** * Build sqe->user_data. * * io_uring relies on ->user_data to map cqe to the submitted io represented by * sqe, encodes ublk interested info into ->user_data for handling IO * completion efficiently. * * @param tag ublk io tag * @param op operation code of submitted io * @param tgt_data target data for this io * @param is_taget_io is this one target io, and it should be true for target, * and false for ublksrv built uring command, which is for communicating * with ublk_drv */ static inline __u64 build_user_data(unsigned tag, unsigned op, unsigned tgt_data, unsigned is_target_io) { assert(!(tag >> 16) && !(op >> 8) && !(tgt_data >> 16)); return tag | (op << 16) | (tgt_data << 24) | (__u64)is_target_io << 63; } static inline unsigned int user_data_to_tag(__u64 user_data) { return user_data & 0xffff; } static inline unsigned int user_data_to_op(__u64 user_data) { return (user_data >> 16) & 0xff; } static inline unsigned int user_data_to_tgt_data(__u64 user_data) { return (user_data >> 24) & 0xffff; } static inline __u64 ublk_pos(__u16 q_id, __u16 tag, __u32 offset) { assert(!(offset & ~UBLK_IO_BUF_BITS_MASK)); return UBLKSRV_IO_BUF_OFFSET + ((((__u64)q_id) << UBLK_QID_OFF) | (((__u64)tag) << UBLK_TAG_OFF) | (__u64)offset); } /** * \defgroup ctrl_dev control device API * * Most of APIs are for sending command to ublk control device(/dev/ublk-control), * and some of them are just for device management purpose, such as, retrieving * device json buffer, run_dir, prepare for recovering, get cached device info ... * * Almost all these APIs can be called in random context by random io uring * context * * @{ */ /** * Deinit one control device * * @param dev the ublksrv control device instance * */ extern void ublksrv_ctrl_deinit(struct ublksrv_ctrl_dev *dev); /** * Allocate and init one control device * * @param data data for allocating & initializing this control device * */ extern struct ublksrv_ctrl_dev *ublksrv_ctrl_init(struct ublksrv_dev_data *data); /** * Retrieve and store each queue's cpu affinity info into private data of the * control device by sending commands to ublk control device * * @param ctrl_dev the ublksrv control device instance * */ extern int ublksrv_ctrl_get_affinity(struct ublksrv_ctrl_dev *ctrl_dev); /** * Add one ublk device by sending command to ublk driver * * @param dev the ublksrv control device instance */ extern int ublksrv_ctrl_add_dev(struct ublksrv_ctrl_dev *dev); /** * Delete this ublk device by sending command to ublk driver * * @param dev the ublksrv control device instance */ extern int ublksrv_ctrl_del_dev(struct ublksrv_ctrl_dev *dev); /** * Delete this ublk device asynchronously by sending command to ublk driver * * @param dev the ublksrv control device instance */ extern int ublksrv_ctrl_del_dev_async(struct ublksrv_ctrl_dev *dev); /** * Retrieve ublk device info by sending command to ublk control device * * @param dev the ublksrv control device instance */ extern int ublksrv_ctrl_get_info(struct ublksrv_ctrl_dev *dev); /** * Stop the specified ublk device by sending command to ublk control device * * @param dev the ublksrv control device instance */ extern int ublksrv_ctrl_stop_dev(struct ublksrv_ctrl_dev *dev); /** * Dump this ublk device * * @param dev the ublksrv control device instance * @param buf ublk device json buffer, optional */ extern void ublksrv_ctrl_dump(struct ublksrv_ctrl_dev *dev, const char *buf); /** * Start this ublk device by sending command to ublk control device * * @param ctrl_dev the ublksrv control device instance * @param daemon_pid pid of the ublksrv process */ extern int ublksrv_ctrl_start_dev(struct ublksrv_ctrl_dev *ctrl_dev, int daemon_pid); /** * Set specified device parameter by sending command to ublk control device * * @param dev the ublksrv control device instance * @param params the specified parameter for setting device */ extern int ublksrv_ctrl_set_params(struct ublksrv_ctrl_dev *dev, struct ublk_params *params); /** * Get specified device parameter by sending command to ublk control device * * @param dev the ublksrv control device instance * @param params the parameter buffer for storing the device parameter */ extern int ublksrv_ctrl_get_params(struct ublksrv_ctrl_dev *dev, struct ublk_params *params); /** * Start to recovery device by sending command to ublk control device * * @param dev the ublksrv control device instance */ extern int ublksrv_ctrl_start_recovery(struct ublksrv_ctrl_dev *dev); /** * End recovery device by sending command to ublk control device * * Once this command is successful, the device is recovered to normal state * * @param dev the ublksrv control device instance * @param daemon_pid pid of the new ublksrv process */ extern int ublksrv_ctrl_end_recovery(struct ublksrv_ctrl_dev *dev, int daemon_pid); /** * Return cached device info for this device * * @param dev the ublksrv control device instance */ extern const struct ublksrv_ctrl_dev_info *ublksrv_ctrl_get_dev_info( const struct ublksrv_ctrl_dev *dev); /** * Return feature set supported by ublk driver * * @features points to buffer for holding the returned features */ extern int ublksrv_ctrl_get_features(struct ublksrv_ctrl_dev *dev, __u64 *features); /** * Return run dir of ublk device * * Device pid file and json string stored under this dir * * @param dev the ublksrv control device instance */ extern const char *ublksrv_ctrl_get_run_dir(const struct ublksrv_ctrl_dev *dev); /** * Prepare for starting to recovery device * * Setup target type, run_dir and json buffer before starting to recovery device. * * @param dev the ublksrv control device instance * @param tgt_type target type name of this device * @param tgt_ops target type of this devie * @param recovery_jbuf points to device json buffer */ extern void ublksrv_ctrl_prep_recovery(struct ublksrv_ctrl_dev *dev, const char *tgt_type, const struct ublksrv_tgt_type *tgt_ops, const char *recovery_jbuf); /** * Return device's json buffer * * Setup target type, run_dir and json buffer before starting to recovery device. * * @param dev the ublksrv control device instance */ extern const char *ublksrv_ctrl_get_recovery_jbuf(const struct ublksrv_ctrl_dev *dev); /** * Return true if this control device is for recovering * * @param dev the ublksrv control device instance */ extern bool ublksrv_is_recovering(const struct ublksrv_ctrl_dev *ctrl_dev); /** @} */ // end of ctrl_dev group /** * \defgroup ublksrv_dev ublksrv device API * * ublksrv device ("/dev/ublkcN") level APIs, and ublksrv device focuses on * IO handling related function * * All APIs in this group should be called in ublksrv daemon process context * * @{ */ /** * Allocate and initialize ublksrv device * * @param ctrl_dev the ublksrv control device instance */ extern const struct ublksrv_dev *ublksrv_dev_init(const struct ublksrv_ctrl_dev * ctrl_dev); /** * Deinitialize and free ublksrv device * * @param dev the ublksrv device instance */ extern void ublksrv_dev_deinit(const struct ublksrv_dev *dev); /** * Return the associated ublksrv control device instance * * @param dev the ublksrv device instance */ extern const struct ublksrv_ctrl_dev *ublksrv_get_ctrl_dev( const struct ublksrv_dev *dev); /** * Return pid file FD of this ublksrv device * * @param dev the ublksrv device instance */ extern int ublksrv_get_pidfile_fd(const struct ublksrv_dev *dev); /** * Set completion queue depth of this ublksrv device * * @param dev the ublksrv device instance * @param cq_depth depth of the completion queue of io_uring */ extern void ublksrv_dev_set_cq_depth(struct ublksrv_dev *dev, int cq_depth); /** * Get completion queue depth of this ublksrv device * * @param dev the ublksrv device instance */ extern int ublksrv_dev_get_cq_depth(struct ublksrv_dev *dev); /** * * Apply OOM porotection */ extern void ublksrv_apply_oom_protection(void); /** @} */ // end of ublksrv_dev group /* target json has to include the following key/value */ #define UBLKSRV_TGT_NAME_MAX_LEN 32 struct ublksrv_tgt_base_json { char name[UBLKSRV_TGT_NAME_MAX_LEN]; int type; unsigned int pad; unsigned long long dev_size; unsigned long reserved[8]; }; /** * \defgroup ublksrv_json ublksrv json string API * * ublksrv json string APIs * * APIs for serializing/deserializing device data to/from json string * * @{ */ /** * Serialize json buffer from device's ublksrv_ctrl_dev_info data * * @param dev the ublksrv control device instance * @param buf json buffer * @param len length of json buffer */ extern int ublksrv_json_write_dev_info(const struct ublksrv_ctrl_dev *dev, char *buf, int len); /** * Deserialize json buffer to ublksrv_ctrl_dev_info instance * * @param json_buf json buffer * @param info device info for storing the parsed ublksrv_ctrl_dev_info */ extern int ublksrv_json_read_dev_info(const char *json_buf, struct ublksrv_ctrl_dev_info *info); /** * Serialize json buffer from ublksrv queue * * @param dev the ublksrv control device instance * @param jbuf json buffer * @param len length of json buffer * @param qid queue id * @param ubq_daemon_tid queue pthread tid */ extern int ublksrv_json_write_queue_info(const struct ublksrv_ctrl_dev *dev, char *jbuf, int len, int qid, int ubq_daemon_tid); /** * Deserialize json buffer to ublksrv queue * * @param jbuf json buffer * @param qid queue id * @param tid queue pthread tid * @param affinity_buf queue affinity buffer * @param len length of json buffer */ extern int ublksrv_json_read_queue_info(const char *jbuf, int qid, unsigned *tid, char *affinity_buf, int len); /** * Deserialize json buffer to target data * * @param jbuf json buffer * @param tgt_buf target buffer * @param len length of json buffer */ extern int ublksrv_json_read_target_info(const char *jbuf, char *tgt_buf, int len); /** * Deserialize json buffer to target string field * * @param jbuf json buffer * @param len length of json buffer * @param name string name * @param val string value */ extern int ublksrv_json_read_target_str_info(const char *jbuf, int len, const char *name, char *val); /** * Deserialize json buffer to target ulong field * * @param jbuf json buffer * @param name field name with ulong type * @param val field value with ulong type */ extern int ublksrv_json_read_target_ulong_info(const char *jbuf, const char *name, long *val); /** * Serialize json buffer from target field with string type * * @param jbuf json buffer * @param len length of json buffer * @param name field name with string type * @param val field value with string type */ extern int ublksrv_json_write_target_str_info(char *jbuf, int len, const char *name, const char *val); extern int ublksrv_json_write_target_long_info(char *jbuf, int len, const char *name, long val); /** * Serialize json buffer from target field with ulong type * * @param jbuf json buffer * @param len length of json buffer * @param name field name with ulong type * @param val field value with ulong type */ extern int ublksrv_json_write_target_ulong_info(char *jbuf, int len, const char *name, unsigned long val); extern void ublksrv_json_dump(const char *jbuf); /** * Deserialize json buffer to ublksrv_tgt_base_json instance * * @param jbuf json buffer * @param tgt ublksrv_tgt_base_json instance */ extern int ublksrv_json_read_target_base_info(const char *jbuf, struct ublksrv_tgt_base_json *tgt); /** * Serialize json buffer from ublksrv_tgt_base_json * * @param jbuf json buffer * @param len length of json buffer * @param tgt ublksrv_tgt_base_json instance */ extern int ublksrv_json_write_target_base_info(char *jbuf, int len, const struct ublksrv_tgt_base_json *tgt); /** * Deserialize json buffer to ublk_params instance * * @param p ublk_params instance * @param jbuf json buffer */ extern int ublksrv_json_read_params(struct ublk_params *p, const char *jbuf); /** * Serialize json buffer from ublk_params instance * * @param p ublk_params instance * @param jbuf json buffer * @param len length of json buffer */ extern int ublksrv_json_write_params(const struct ublk_params *p, char *jbuf, int len); extern int ublksrv_json_dump_params(const char *jbuf); /** * Return actual length of the json buffer * * @param jbuf json buffer */ extern int ublksrv_json_get_length(const char *jbuf); /** @} */ // end of ublksrv_json group /** * \defgroup ublksrv_queue ublksrv queue API * * ublksrv queue level APIs * * All APIs in this group is supposed to be called in the queue context * * @{ */ /** * Return the specified io private data * * Each IO has unique tag, so we use tag to represent specified io. * * Inside ->init_tgt() callback, target code sets io private data * size via dev->tgt.io_data_size, then io private data will be allocated * in ublksrv_queue_init(). The allocated io private data is very useful * to store target specific io data, then runtime memory allocation in io * handling code path can be avoided. * * @param q the ublksrv queue instance * @param tag tag for this io */ extern void *ublksrv_io_private_data(const struct ublksrv_queue *q, int tag); /** * Return the specified io generic io data * * Each IO has unique tag, so we use tag to represent specified io. * * @param q the ublksrv queue instance * @param tag tag for this io * @return 'struct ublk_io_data' instance, which is for storing io descriptor, * tag, and private data */ extern const struct ublk_io_data *ublksrv_queue_get_io_data( const struct ublksrv_queue *q, int tag); /** * Return pre-allocated io buffer * * Each IO has unique tag, so we use tag to represent specified io. * * @param q the ublksrv queue instance * @param tag tag for this io * @return pre-allocated io buffer for this io */ extern void *ublksrv_queue_get_io_buf(const struct ublksrv_queue *q, int tag); /** * Return current queue state * * queue state is usually for debug purpose * * @param q the ublksrv queue instance * @return queue current state */ extern unsigned int ublksrv_queue_state(const struct ublksrv_queue *q); /** * Allocate and initialize ublksrv queue instance * * @param dev the ublksrv device instance * @param q_id queue id * @param queue_data queue private data */ extern const struct ublksrv_queue *ublksrv_queue_init(const struct ublksrv_dev *dev, unsigned short q_id, void *queue_data); /** * Deinit & free ublksrv queue instance * * @param q the ublksrv queue instance */ extern void ublksrv_queue_deinit(const struct ublksrv_queue *q); /** * Return how many unconsumed cqes in CQ of queue uring * * @param q the ublksrv queue instance * * Return -1 if uring isn't setup correctly */ extern int ublksrv_queue_unconsumed_cqes(const struct ublksrv_queue *q); extern int ublksrv_queue_handled_event(const struct ublksrv_queue *q); extern int ublksrv_queue_send_event(const struct ublksrv_queue *q); /** * Return the specified queue instance by ublksrv device and qid * * Retrieve queue instance by ublksrv device and queue id * * @param dev the ublksrv device instance * @param q_id queue id */ extern const struct ublksrv_queue *ublksrv_get_queue(const struct ublksrv_dev *dev, int q_id); /** * Process target IO & IO command from this queue's io_uring * * Handle incoming io command by calling target ->handle_io_async(), or * call ->tgt_io_done() if target IO is completed. * * It is the engine of libulksrv, almost everything is driven by this * API. * * @param q the ublksrv queue instance */ extern int ublksrv_process_io(const struct ublksrv_queue *q); /** * Complete specified io with result of 'res' * * This API will tell ublk driver via /dev/ublkcN that this IO is completed. * * @param q the ublksrv queue instance * @param tag the io to be completed * @param res io result */ extern int ublksrv_complete_io(const struct ublksrv_queue *q, unsigned tag, int res); /** @} */ // end of ublksrv_queue group #ifdef __cplusplus } #endif #endif