Introduce copy_jobs to use copy-offload if it is natively
supported (by underlying device) or else fall back to original method.

run_copy_jobs() calls block layer copy offload api with
BLKDEV_COPY_NOEMULATION. On successful completion, if only one destination
device was present, then jobs is queued for completion. If multiple
destinations were present, the completed destination is zeroed and
pushed to pages_jobs to process copy offload for other destinations. In
case of copy_offload failure, remaining destinations are processed via
regular copying mechanism.

Signed-off-by: SelvaKumar S <selvakuma...@samsung.com>
---
 drivers/md/dm-kcopyd.c | 49 ++++++++++++++++++++++++++++++++++++------
 1 file changed, 43 insertions(+), 6 deletions(-)

diff --git a/drivers/md/dm-kcopyd.c b/drivers/md/dm-kcopyd.c
index 1bbe4a34ef4c..2442c4870e97 100644
--- a/drivers/md/dm-kcopyd.c
+++ b/drivers/md/dm-kcopyd.c
@@ -74,18 +74,20 @@ struct dm_kcopyd_client {
        atomic_t nr_jobs;
 
 /*
- * We maintain four lists of jobs:
+ * We maintain five 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 don't need to do any IO and just run a callback
- * iv) jobs that have completed.
+ * i)  jobs waiting to try copy offload
+ * ii)   jobs waiting for pages
+ * iii)  jobs that have pages, and are waiting for the io to be issued.
+ * iv) jobs that don't need to do any IO and just run a callback
+ * v) jobs that have completed.
  *
- * All four of these are protected by job_lock.
+ * All five of these are protected by job_lock.
  */
        spinlock_t job_lock;
        struct list_head callback_jobs;
        struct list_head complete_jobs;
+       struct list_head copy_jobs;
        struct list_head io_jobs;
        struct list_head pages_jobs;
 };
@@ -581,6 +583,36 @@ static int run_io_job(struct kcopyd_job *job)
        return r;
 }
 
+static int run_copy_job(struct kcopyd_job *job)
+{
+       int r, i, count = 0;
+       unsigned long flags = 0;
+       struct range_entry srange;
+
+       flags |= BLKDEV_COPY_NOEMULATION;
+       for (i = 0; i < job->num_dests; i++) {
+               srange.src = job->source.sector;
+               srange.len = job->source.count;
+
+               r = blkdev_issue_copy(job->source.bdev, 1, &srange,
+                       job->dests[i].bdev, job->dests[i].sector, GFP_KERNEL, 
flags);
+               if (r)
+                       break;
+
+               job->dests[i].count = 0;
+               count++;
+       }
+
+       if (count == job->num_dests) {
+               push(&job->kc->complete_jobs, job);
+       } else {
+               push(&job->kc->pages_jobs, job);
+               r = 0;
+       }
+
+       return r;
+}
+
 static int run_pages_job(struct kcopyd_job *job)
 {
        int r;
@@ -662,6 +694,7 @@ static void do_work(struct work_struct *work)
        spin_unlock_irqrestore(&kc->job_lock, flags);
 
        blk_start_plug(&plug);
+       process_jobs(&kc->copy_jobs, kc, run_copy_job);
        process_jobs(&kc->complete_jobs, kc, run_complete_job);
        process_jobs(&kc->pages_jobs, kc, run_pages_job);
        process_jobs(&kc->io_jobs, kc, run_io_job);
@@ -679,6 +712,8 @@ static void dispatch_job(struct kcopyd_job *job)
        atomic_inc(&kc->nr_jobs);
        if (unlikely(!job->source.count))
                push(&kc->callback_jobs, job);
+       else if (job->source.bdev->bd_disk == job->dests[0].bdev->bd_disk)
+               push(&kc->copy_jobs, job);
        else if (job->pages == &zero_page_list)
                push(&kc->io_jobs, job);
        else
@@ -919,6 +954,7 @@ struct dm_kcopyd_client *dm_kcopyd_client_create(struct 
dm_kcopyd_throttle *thro
        spin_lock_init(&kc->job_lock);
        INIT_LIST_HEAD(&kc->callback_jobs);
        INIT_LIST_HEAD(&kc->complete_jobs);
+       INIT_LIST_HEAD(&kc->copy_jobs);
        INIT_LIST_HEAD(&kc->io_jobs);
        INIT_LIST_HEAD(&kc->pages_jobs);
        kc->throttle = throttle;
@@ -974,6 +1010,7 @@ void dm_kcopyd_client_destroy(struct dm_kcopyd_client *kc)
 
        BUG_ON(!list_empty(&kc->callback_jobs));
        BUG_ON(!list_empty(&kc->complete_jobs));
+       BUG_ON(!list_empty(&kc->copy_jobs));
        BUG_ON(!list_empty(&kc->io_jobs));
        BUG_ON(!list_empty(&kc->pages_jobs));
        destroy_workqueue(kc->kcopyd_wq);
-- 
2.25.1

Reply via email to