|
@@ -66,6 +66,8 @@ struct dm_kcopyd_client {
|
|
|
struct list_head pages_jobs;
|
|
|
};
|
|
|
|
|
|
+static struct page_list zero_page_list;
|
|
|
+
|
|
|
static void wake(struct dm_kcopyd_client *kc)
|
|
|
{
|
|
|
queue_work(kc->kcopyd_wq, &kc->kcopyd_work);
|
|
@@ -254,6 +256,9 @@ int __init dm_kcopyd_init(void)
|
|
|
if (!_job_cache)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
+ zero_page_list.next = &zero_page_list;
|
|
|
+ zero_page_list.page = ZERO_PAGE(0);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -322,7 +327,7 @@ static int run_complete_job(struct kcopyd_job *job)
|
|
|
dm_kcopyd_notify_fn fn = job->fn;
|
|
|
struct dm_kcopyd_client *kc = job->kc;
|
|
|
|
|
|
- if (job->pages)
|
|
|
+ if (job->pages && job->pages != &zero_page_list)
|
|
|
kcopyd_put_pages(kc, job->pages);
|
|
|
/*
|
|
|
* If this is the master job, the sub jobs have already
|
|
@@ -484,6 +489,8 @@ static void dispatch_job(struct kcopyd_job *job)
|
|
|
atomic_inc(&kc->nr_jobs);
|
|
|
if (unlikely(!job->source.count))
|
|
|
push(&kc->complete_jobs, job);
|
|
|
+ else if (job->pages == &zero_page_list)
|
|
|
+ push(&kc->io_jobs, job);
|
|
|
else
|
|
|
push(&kc->pages_jobs, job);
|
|
|
wake(kc);
|
|
@@ -592,14 +599,20 @@ int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from,
|
|
|
job->flags = flags;
|
|
|
job->read_err = 0;
|
|
|
job->write_err = 0;
|
|
|
- job->rw = READ;
|
|
|
-
|
|
|
- job->source = *from;
|
|
|
|
|
|
job->num_dests = num_dests;
|
|
|
memcpy(&job->dests, dests, sizeof(*dests) * num_dests);
|
|
|
|
|
|
- job->pages = NULL;
|
|
|
+ if (from) {
|
|
|
+ job->source = *from;
|
|
|
+ job->pages = NULL;
|
|
|
+ job->rw = READ;
|
|
|
+ } else {
|
|
|
+ memset(&job->source, 0, sizeof job->source);
|
|
|
+ job->source.count = job->dests[0].count;
|
|
|
+ job->pages = &zero_page_list;
|
|
|
+ job->rw = WRITE;
|
|
|
+ }
|
|
|
|
|
|
job->fn = fn;
|
|
|
job->context = context;
|
|
@@ -617,6 +630,14 @@ int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from,
|
|
|
}
|
|
|
EXPORT_SYMBOL(dm_kcopyd_copy);
|
|
|
|
|
|
+int dm_kcopyd_zero(struct dm_kcopyd_client *kc,
|
|
|
+ unsigned num_dests, struct dm_io_region *dests,
|
|
|
+ unsigned flags, dm_kcopyd_notify_fn fn, void *context)
|
|
|
+{
|
|
|
+ return dm_kcopyd_copy(kc, NULL, num_dests, dests, flags, fn, context);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(dm_kcopyd_zero);
|
|
|
+
|
|
|
void *dm_kcopyd_prepare_callback(struct dm_kcopyd_client *kc,
|
|
|
dm_kcopyd_notify_fn fn, void *context)
|
|
|
{
|