blob: 405df6006b580ce78414c8b14103e10d857e118a [file] [log] [blame] [edit]
// 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 <stdbool.h>
#include <assert.h>
#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