tapdisk2 log
tapdisk2 的log 分为两部分 /var/log/messages , /tmp/tapdisk.log.${PID}
tapdisk syslog 以 tapdisk[1231] 开头,这里1231是进程号
tapdisk log 一般以 count.time.timeseconds 开头
tapdisk error log内容以 TAPDISK ERROR 开头,或者以 tap-err 开头
struct error {
int cnt;
int err;
char *func;
char msg[MAX_ENTRY_LEN];
};
struct ehandle {
int cnt;
int dropped;
struct error errors[MAX_ERROR_MESSAGES];
};
struct tlog {
char *p;
int size;
uint64_t cnt;
char *buf;
int level;
char *file;
int append;
};
int cnt;
int err;
char *func;
char msg[MAX_ENTRY_LEN];
};
struct ehandle {
int cnt;
int dropped;
struct error errors[MAX_ERROR_MESSAGES];
};
struct tlog {
char *p;
int size;
uint64_t cnt;
char *buf;
int level;
char *file;
int append;
};
具体的代码请参考 tapdisk-log.h / tapdisk-log.c
tapdisk2 image
tapdisk2 提供了 td_image_t 结构,用于保存 tapdisk2 读写的image盘信息
td_image_t - > driver 是不同类型的 disk_type 所提供的,保存为 td_driver_handle 类型,其中 td_driver_handle -> ops 为 struct tap_disk * 结构。
td_image_t -> flags ,其值可以为:
#define TD_OPEN_QUIET 0x00001
#define TD_OPEN_QUERY 0x00002
#define TD_OPEN_RDONLY 0x00004
#define TD_OPEN_STRICT 0x00008
#define TD_OPEN_SHAREABLE 0x00010
#define TD_OPEN_ADD_CACHE 0x00020
#define TD_OPEN_VHD_INDEX 0x00040
#define TD_OPEN_LOG_DIRTY 0x00080
#define TD_OPEN_QUERY 0x00002
#define TD_OPEN_RDONLY 0x00004
#define TD_OPEN_STRICT 0x00008
#define TD_OPEN_SHAREABLE 0x00010
#define TD_OPEN_ADD_CACHE 0x00020
#define TD_OPEN_VHD_INDEX 0x00040
#define TD_OPEN_LOG_DIRTY 0x00080
tapdisk_image_check_td_request 用于从 td_image_t 中检查 td_request_t 请求的合法性,e.g. td_request_t -> op 至少为TD_OP_READ或者TD_OP_WRITE,request 的sectors 不能越界。
tapdisk_image_check_ring_request 用于检查 blkif_request, blkif_response 的合法性。这两个函数唯一需要的是 image是否有TD_OPEN_RDONLY的flag
tapdisk2 queue
tlist 是 tiocb 的 list,其中 tiocb 是 tapdisk2 对 struct iocb 的结构的封装。 对于 struct iocb 的 IO 请求,有相应的 callback 函数 td_queue_callback_t cb
struct tqueue {
int size;
const struct tio *tio;
void *tio_data;
struct opioctx opioctx;
int queued;
struct iocb **iocbs;
/* number of iocbs pending in the aio layer */
int iocbs_pending;
/* number of tiocbs pending in the queue --
* this is likely to be larger than iocbs_pending
* due to request coalescing */
int tiocbs_pending;
/* iocbs may be deferred if the aio ring is full.
* tapdisk_queue_complete will ensure deferred
* iocbs are queued as slots become available. */
struct tlist deferred;
int tiocbs_deferred;
/* optional tapdisk filter */
struct tfilter *filter;
uint64_t deferrals;
};
int size;
const struct tio *tio;
void *tio_data;
struct opioctx opioctx;
int queued;
struct iocb **iocbs;
/* number of iocbs pending in the aio layer */
int iocbs_pending;
/* number of tiocbs pending in the queue --
* this is likely to be larger than iocbs_pending
* due to request coalescing */
int tiocbs_pending;
/* iocbs may be deferred if the aio ring is full.
* tapdisk_queue_complete will ensure deferred
* iocbs are queued as slots become available. */
struct tlist deferred;
int tiocbs_deferred;
/* optional tapdisk filter */
struct tfilter *filter;
uint64_t deferrals;
};
tapdisk 除了要通过各种 driver 来区分各种镜像的类型之外,还要区分不同的 IO类型,即 tapdisk 通过何种方式来读写镜像。目前支持的有两种: aio, rwio
Hide tapdisk support for different raw I/O interfaces behind a new struct tio. Libaio remains to dominate the interface, requiring everyone to dispatch iocb/ioevent structs. Backends: - lio: Kernel AIO via libaio. - rwio: Canonical read/write() mode.
struct tio {
const char *name;
size_t data_size;
int (*tio_setup) (struct tqueue *queue, int qlen);
void (*tio_destroy) (struct tqueue *queue);
int (*tio_submit) (struct tqueue *queue);
};
enum {
TIO_DRV_LIO = 1,
TIO_DRV_RWIO = 2,
};
size_t data_size;
int (*tio_setup) (struct tqueue *queue, int qlen);
void (*tio_destroy) (struct tqueue *queue);
int (*tio_submit) (struct tqueue *queue);
};
enum {
TIO_DRV_LIO = 1,
TIO_DRV_RWIO = 2,
};
tqueue -> tio 指向这个 struct tio 结构,通过 tio_submit 函数真正发出 IO 请求。
queue_tiocb :把 struct tiocb* 指向的结构体加入到队列中。注意 tiocb 是一个封装了 libaio 的 struct iocb 结构的一个结构体。同时 struct iocb -> data 所指向的 void 指针指向这个 struct tiocb。
defer_tiocb :把 struct tiocb* 指向的结构插入到 deferred list ( tqueue -> deferred ) 的末尾。
queue_deferred_tiocb : 取出 tqueue -> deferred 队列头的 struct tiocb 结构,调用 queue_tiocb 加入到 tqueue -> iocbs 队列中
complete_tiocb : 每一个 struct tiocb 结构体都会事先注册好一个回调函数 cb,对于libaio 返回的 struct iocb 结构,判断 libaio 的返回状态,之后调用 tiocb->cb(tiocb->arg, tiocb, err)
cancel_tiocbs : 对于 tqueue未完成的 tqueue->iocbs 队列里的所有 IO 请求,调用 complete_tiocb 取消这些请求。
对于两种IO模式,我们跳过 rwio, 直接去看 lio,下面是lio 对应的 struct tio 结构:
static const struct tio td_tio_lio = {
.name = "lio",
.data_size = sizeof(struct lio),
.tio_setup = tapdisk_lio_setup,
.tio_destroy = tapdisk_lio_destroy,
.tio_submit = tapdisk_lio_submit,
};
.name = "lio",
.data_size = sizeof(struct lio),
.tio_setup = tapdisk_lio_setup,
.tio_destroy = tapdisk_lio_destroy,
.tio_submit = tapdisk_lio_submit,
};
tapdisk_init_queue : 用于初始化 struct tqueue 结构,其中又调用了 tapdisk_queue_init_io , 该方法 会调用 tio->tio_setup 初始化 IO queue
tapdisk_free_queue : 用于释放 tqueue 占用的资源
tapdisk_debug_queue : 打印出当前 tqueue 的状态,对于 deferred queue 里的所有 IO 请求,同时打印详细信息
tapdisk_lio_setup_aio : 首先检查内核版本,从而调用 __lio_setup_aio_poll 或者 __lio_setup_aio_eventfd
tapdisk_lio_destroy_aio : 关闭 lio -> event_fd, 释放 lio -> aio_ctx
tapdisk_lio_setup : 调用 tapdisk_lio_setup_aio 获取一个 aio 的 event_fd,接着调用 tapdisk_server_register_event,把这个 event_fd 注册到事件 SCHEDULER_POLL_READ_FD 上面,对应的 callback 函数为 tapdisk_lio_event
tapdisk_lio_event : tapdisk_lio_event 是回调函数,首先调用 tapdisk_lio_ack_event 来读一个 uint64_t 大小的内容,之后调用 io_getevents, io_split, 得到对应的 event 个数, 影响的 tiocbs 的个数。最后对于所有的 tiocbs, 调用 complete_tiocb 完成IO
tapdisk_lio_submit : 调用 io_merge 对IO请求做合并优化,之后调用 io_submit 把请求通过 aio 提交上去。