kcopyd

kcopyd 提供的是一个易于使用的接口,隐藏了异步:dm-kcopyd.c 重点在于 "d",即对 kcopyd_jobs 的异步管理,而 copy 相关的具体 async IO 细节在 dm-io.c 中实现

kcopyd provides the ability to copy a range of sectors from one block-device to one or more other block-devices, with an asynchronous completion notification (dm-io 提供了 async/sync IO service,kcopyd 使用了 async IO service).

使用者只需要提供自己的 dm_kcopyd_throttle (可以为 NULL)、src dm_io_region 和 dest dm_io_region(s)、dm_kcopyd_notify_fn异步由 kcopyd 负责,用户不需要关心

It is used by various dm target types, including mirror, thin, snapshot, cache, writeback, and zoned.

Users of kcopyd must first create a dm_kcopyd_client object. This is done with a call to dm_kcopyd_client_create(). The throttle arg can be NULL if users don't want any throttling.

    struct dm_kcopyd_client *dm_kcopyd_client_create(struct dm_kcopyd_throttle *throttle);

To start a copy job, the user must set up dm_io_region structures to describe the source and destinations of the copy. Each dm_io_region indicates a block-device along with the starting sector and size of the region. The source of the copy is given as one dm_io_region structure, and the destinations of the copy are given as an array of dm_io_region structures.

    struct dm_io_region {
        struct block_device *bdev;
        sector_t            sector;
        sector_t            count;
   };

To start the copy, the user calls dm_kcopyd_copy(), passing in the client pointer, pointers to the source and destination dm_io_regions, the name of a completion callback routine, and a pointer to some context data for the copy.

    int dm_kcopyd_copy(struct dm_kcopyd_client *kc, 
                       struct dm_io_region *from,
                       unsigned int num_dests, 
                       struct dm_io_region *dests,
                       unsigned int flags,
                       dm_kcopyd_notify_fn fn,
                       void *context);

    typedef void (*dm_kcopyd_notify_fn)(int read_err,
                                        unsigned long write_err,
                                        void *context);

When the copy completes, kcopyd will call the user's completion routine, passing back the user's context pointer. It will also indicate if a read or write error occurred during the copy.

When a user is done with all their copy jobs, they should call dm_kcopyd_client_destroy() to delete the kcopyd client, which will release the associated memory pages.

    void dm_kcopyd_client_destroy(struct dm_kcopyd_client *kc);

struct dm_kcopyd_throttle

commit df5d2e9089c7d5b8c46f767e4278610ea3e815b9
Author: Mikulas Patocka <mpatocka@redhat.com>
Date:   Fri Mar 1 22:45:49 2013 +0000

    dm kcopyd: introduce configurable throttling

    This patch allows the administrator to reduce the rate at which kcopyd
    issues I/O.

    Each module that uses kcopyd acquires a throttle parameter that can be
    set in /sys/module/*/parameters.

    We maintain a history of kcopyd usage by each module in the variables
    io_period and total_period in struct dm_kcopyd_throttle. The actual
    kcopyd activity is calculated as a percentage of time equal to
    "(100 * io_period / total_period)".  This is compared with the user-defined
    throttle percentage threshold and if it is exceeded, we sleep.

    Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
    Signed-off-by: Alasdair G Kergon <agk@redhat.com>
    struct dm_kcopyd_throttle {
        unsigned    throttle;
        unsigned    num_io_jobs;
        unsigned    io_period;
        unsigned    total_period;
        unsigned    last_jiffies;
    };

    /*
     * kcopyd clients that want to support throttling must pass an initialised
     * dm_kcopyd_throttle struct into dm_kcopyd_client_create().
     * Two or more clients may share the same instance of this struct between
     * them if they wish to be throttled as a group.
     *
     * This macro also creates a corresponding module parameter to configure
     * the amount of throttling.
     */
    #define DECLARE_DM_KCOPYD_THROTTLE_WITH_MODULE_PARM(name, description)  \
        static struct dm_kcopyd_throttle dm_kcopyd_throttle = { 100, 0, 0, 0, 0 }; \
        module_param_named(name, dm_kcopyd_throttle.throttle, uint, 0644); \
        MODULE_PARM_DESC(name, description)

struct dm_kcopyd_client

    #define SUB_JOB_SIZE    128
    #define SPLIT_COUNT     8
    #define MIN_JOBS        8
    #define RESERVE_PAGES   (DIV_ROUND_UP(SUB_JOB_SIZE << SECTOR_SHIFT, PAGE_SIZE))


    /*-----------------------------------------------------------------
     * Each kcopyd client has its own little pool of preallocated
     * pages for kcopyd io.
     *---------------------------------------------------------------*/
    struct dm_kcopyd_client {
        struct page_list            *pages;
                                        调用 dm_kcopyd_client_create 的时候会通过
                                        client_reserve_pages 预分配 RESERVE_PAGES
                                        个 pages
        unsigned                    nr_reserved_pages;  初始化为 RESERVE_PAGES
        unsigned                    nr_free_pages;      初始化为 RESERVE_PAGES

        struct dm_io_client         *io_client;

        wait_queue_head_t           destroyq;

        mempool_t                   job_pool;

        struct workqueue_struct     *kcopyd_wq;
                                        alloc_workqueue("kcopyd", WQ_MEM_RECLAIM, 0)
        struct work_struct          kcopyd_work;
                                        关联 dm-kcopyd.c/do_work
                                        kcopyd does this every time it's woken up

        struct dm_kcopyd_throttle   *throttle;

        atomic_t                    nr_jobs;            初始化为 0

    /*
     * We maintain three lists of jobs:
     *
     * i)   jobs waiting for pages
     * ii)  jobs that have pages, and are waiting for the io to be issued.
     * iii) jobs that have completed.
     *
     * All three of these are protected by job_lock.
     */
        spinlock_t                  job_lock;
        struct list_head            complete_jobs;
        struct list_head            io_jobs;
        struct list_head            pages_jobs;
    };


    struct dm_io_client {
        mempool_t       pool;
        struct bio_set  bios;
    };