如何实现一个FIO插件

FIO是一个开源的压力测试工具,主要用来测试磁盘的io性能。它支持13种不同类型的io引擎(libaio sync mmap libiscsi nbd nfs等等),它支持用户自定义各种不同的io pattern。

当我们测试自己开发的IO路径时,也可以添加一个自定义的ioengine。

ioengine接口

fio ioengine的接口在ioengines.h,要实现一个自定义的async engine,就需要实现这一套接口,然后fio的框架会调用engine的queue接口把io放进来,ioengine负责执行IO,然后ioengine会通过getevents接口来获取events,得到io执行结果。

struct ioengine_ops {
        struct flist_head list;
        const char *name;
        int version;
        int flags;
        void *dlhandle;
        int (*setup)(struct thread_data *);
        int (*init)(struct thread_data *);
        int (*post_init)(struct thread_data *);
        int (*prep)(struct thread_data *, struct io_u *);
        enum fio_q_status (*queue)(struct thread_data *, struct io_u *);
        int (*commit)(struct thread_data *);
        int (*getevents)(struct thread_data *, unsigned int, unsigned int, const struct timespec *);
        struct io_u *(*event)(struct thread_data *, int);
        char *(*errdetails)(struct io_u *);
        int (*cancel)(struct thread_data *, struct io_u *);
        void (*cleanup)(struct thread_data *);
        int (*open_file)(struct thread_data *, struct fio_file *);
        int (*close_file)(struct thread_data *, struct fio_file *);
        int (*invalidate)(struct thread_data *, struct fio_file *);
        int (*unlink_file)(struct thread_data *, struct fio_file *);
        int (*get_file_size)(struct thread_data *, struct fio_file *);
        int (*prepopulate_file)(struct thread_data *, struct fio_file *);
        void (*terminate)(struct thread_data *);
        int (*iomem_alloc)(struct thread_data *, size_t);
        void (*iomem_free)(struct thread_data *);
        int (*io_u_init)(struct thread_data *, struct io_u *);
        void (*io_u_free)(struct thread_data *, struct io_u *);
        int (*get_zoned_model)(struct thread_data *td,
                               struct fio_file *f, enum zbd_zoned_model *);
        int (*report_zones)(struct thread_data *, struct fio_file *,
                            uint64_t, struct zbd_zone *, unsigned int);
        int (*reset_wp)(struct thread_data *, struct fio_file *,
                        uint64_t, uint64_t);
        int (*get_max_open_zones)(struct thread_data *, struct fio_file *,
                                  unsigned int *);
        int (*get_max_active_zones)(struct thread_data *, struct fio_file *,
                                    unsigned int *);
        int (*finish_zone)(struct thread_data *, struct fio_file *,
                           uint64_t, uint64_t);
        int (*fdp_fetch_ruhs)(struct thread_data *, struct fio_file *,
                              struct fio_ruhs_info *);
        int option_struct_size;
        struct fio_option *options;
};

实现ioengine接口

1 每个线程都有一个thread data

struct libfs_data {
        struct io_u **io_us;
        struct io_u **io_u_index;
        struct libfs_io_result* results;
        unsigned int entries;
        unsigned int queued;
        unsigned int head;
        unsigned int tail;
};

2 定义一组ioengine_ops

static struct ioengine_ops ioengine = {
        .name           = "libengine",
        .version        = FIO_IOOPS_VERSION,
        .flags          = FIO_ASYNCIO_SYNC_TRIM | FIO_MEMALIGN | FIO_ASYNCIO_SETS_ISSUE_TIME,
        .init           = fio_libengine_init,
        .cleanup        = fio_libengine_cleanup,
        .open_file      = libengine_open_file,
        .close_file     = libengine_close_file,
        .get_file_size  = libengine_get_file_size,
        .setup          = libengine_setup,
        .queue          = fio_libengine_queue,
        .commit         = fio_libengine_commit,
        .getevents      = fio_libengine_getevents,
        .event          = fio_libengine_event,
        .invalidate     = libengine_invalidate,
        .option_struct_size = sizeof(struct libengine_options),
};

static void fio_init fio_libengine_register()
{
        register_ioengine(&ioengine);
}

static void fio_exit fio_libengine_unregister()
{
        unregister_ioengine(&ioengine);
}

3 IO入队列函数

void fio_libengine_queued(struct thread_data *td, struct io_u **io_us,
                              unsigned int nr)

4 提交队列中的请求

int fio_libengine_commit(struct thread_data *td)

5 POLL获取事件

int fio_libengine_getevents(struct thread_data *td, unsigned int min,
                                unsigned int max, const struct timespec *t)

6 解析每一个事件,释放IO_U

struct io_u *fio_libengine_event(struct thread_data *td, int event)

7 最后修改Makefile,把自定义engine添加进去

libengine_SRCS = engines/libengine.c
libengine_LIBS = -lengine
ENGINES += libengine

最后更新于