X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fmd%2Fkcopyd.c;h=f1db6eff48574c80d9340960ac3deb0d25831ca0;hb=16c70f8c1b54b61c3b951b6fb220df250fe09b32;hp=40e46944ecb0c0091eec0c5e23aa924abf9d36cb;hpb=5fc42a6ed0ec81088c37caadb45898ae6cd0ad2c;p=linux-2.6.git diff --git a/drivers/md/kcopyd.c b/drivers/md/kcopyd.c index 40e46944e..f1db6eff4 100644 --- a/drivers/md/kcopyd.c +++ b/drivers/md/kcopyd.c @@ -8,10 +8,10 @@ * completion notification. */ +#include #include #include -#include #include #include #include @@ -21,12 +21,10 @@ #include #include #include +#include #include "kcopyd.h" -/* FIXME: this is only needed for the DMERR macros */ -#include "dm.h" - static struct workqueue_struct *_kcopyd_wq; static struct work_struct _kcopyd_work; @@ -46,6 +44,9 @@ struct kcopyd_client { struct page_list *pages; unsigned int nr_pages; unsigned int nr_free_pages; + + wait_queue_head_t destroyq; + atomic_t nr_jobs; }; static struct page_list *alloc_pl(void) @@ -87,7 +88,7 @@ static int kcopyd_get_pages(struct kcopyd_client *kc, ; kc->pages = pl->next; - pl->next = 0; + pl->next = NULL; spin_unlock(&kc->lock); @@ -214,7 +215,7 @@ static mempool_t *_job_pool; * * All three of these are protected by job_lock. */ -static spinlock_t _job_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(_job_lock); static LIST_HEAD(_complete_jobs); static LIST_HEAD(_io_jobs); @@ -229,8 +230,7 @@ static int jobs_init(void) if (!_job_cache) return -ENOMEM; - _job_pool = mempool_create(MIN_JOBS, mempool_alloc_slab, - mempool_free_slab, _job_cache); + _job_pool = mempool_create_slab_pool(MIN_JOBS, _job_cache); if (!_job_pool) { kmem_cache_destroy(_job_cache); return -ENOMEM; @@ -295,10 +295,15 @@ static int run_complete_job(struct kcopyd_job *job) int read_err = job->read_err; unsigned int write_err = job->write_err; kcopyd_notify_fn fn = job->fn; + struct kcopyd_client *kc = job->kc; - kcopyd_put_pages(job->kc, job->pages); + kcopyd_put_pages(kc, job->pages); mempool_free(job, _job_pool); fn(read_err, write_err, context); + + if (atomic_dec_and_test(&kc->nr_jobs)) + wake_up(&kc->destroyq); + return 0; } @@ -308,7 +313,7 @@ static void complete_io(unsigned long error, void *context) if (error) { if (job->rw == WRITE) - job->write_err &= error; + job->write_err |= error; else job->read_err = 1; @@ -433,6 +438,7 @@ static void do_work(void *ignored) */ static void dispatch_job(struct kcopyd_job *job) { + atomic_inc(&job->kc->nr_jobs); push(&_pages_jobs, job); wake(); } @@ -453,7 +459,7 @@ static void segment_complete(int read_err, job->read_err = 1; if (write_err) - job->write_err &= write_err; + job->write_err |= write_err; /* * Only dispatch more work if there hasn't been an error. @@ -564,78 +570,79 @@ int kcopyd_copy(struct kcopyd_client *kc, struct io_region *from, * Cancels a kcopyd job, eg. someone might be deactivating a * mirror. */ +#if 0 int kcopyd_cancel(struct kcopyd_job *job, int block) { /* FIXME: finish */ return -1; } +#endif /* 0 */ /*----------------------------------------------------------------- * Unit setup *---------------------------------------------------------------*/ -static DECLARE_MUTEX(_client_lock); +static DEFINE_MUTEX(_client_lock); static LIST_HEAD(_clients); -static int client_add(struct kcopyd_client *kc) +static void client_add(struct kcopyd_client *kc) { - down(&_client_lock); + mutex_lock(&_client_lock); list_add(&kc->list, &_clients); - up(&_client_lock); - return 0; + mutex_unlock(&_client_lock); } static void client_del(struct kcopyd_client *kc) { - down(&_client_lock); + mutex_lock(&_client_lock); list_del(&kc->list); - up(&_client_lock); + mutex_unlock(&_client_lock); } -static DECLARE_MUTEX(kcopyd_init_lock); +static DEFINE_MUTEX(kcopyd_init_lock); static int kcopyd_clients = 0; static int kcopyd_init(void) { int r; - down(&kcopyd_init_lock); + mutex_lock(&kcopyd_init_lock); if (kcopyd_clients) { /* Already initialized. */ kcopyd_clients++; - up(&kcopyd_init_lock); + mutex_unlock(&kcopyd_init_lock); return 0; } r = jobs_init(); if (r) { - up(&kcopyd_init_lock); + mutex_unlock(&kcopyd_init_lock); return r; } _kcopyd_wq = create_singlethread_workqueue("kcopyd"); if (!_kcopyd_wq) { jobs_exit(); - up(&kcopyd_init_lock); + mutex_unlock(&kcopyd_init_lock); return -ENOMEM; } kcopyd_clients++; INIT_WORK(&_kcopyd_work, do_work, NULL); - up(&kcopyd_init_lock); + mutex_unlock(&kcopyd_init_lock); return 0; } static void kcopyd_exit(void) { - down(&kcopyd_init_lock); + mutex_lock(&kcopyd_init_lock); kcopyd_clients--; if (!kcopyd_clients) { jobs_exit(); destroy_workqueue(_kcopyd_wq); _kcopyd_wq = NULL; } - up(&kcopyd_init_lock); + mutex_unlock(&kcopyd_init_lock); } int kcopyd_client_create(unsigned int nr_pages, struct kcopyd_client **result) @@ -653,7 +660,7 @@ int kcopyd_client_create(unsigned int nr_pages, struct kcopyd_client **result) return -ENOMEM; } - kc->lock = SPIN_LOCK_UNLOCKED; + spin_lock_init(&kc->lock); kc->pages = NULL; kc->nr_pages = kc->nr_free_pages = 0; r = client_alloc_pages(kc, nr_pages); @@ -671,21 +678,19 @@ int kcopyd_client_create(unsigned int nr_pages, struct kcopyd_client **result) return r; } - r = client_add(kc); - if (r) { - dm_io_put(nr_pages); - client_free_pages(kc); - kfree(kc); - kcopyd_exit(); - return r; - } + init_waitqueue_head(&kc->destroyq); + atomic_set(&kc->nr_jobs, 0); + client_add(kc); *result = kc; return 0; } void kcopyd_client_destroy(struct kcopyd_client *kc) { + /* Wait for completion of all jobs submitted by this client. */ + wait_event(kc->destroyq, !atomic_read(&kc->nr_jobs)); + dm_io_put(kc->nr_pages); client_free_pages(kc); client_del(kc); @@ -696,4 +701,3 @@ void kcopyd_client_destroy(struct kcopyd_client *kc) EXPORT_SYMBOL(kcopyd_client_create); EXPORT_SYMBOL(kcopyd_client_destroy); EXPORT_SYMBOL(kcopyd_copy); -EXPORT_SYMBOL(kcopyd_cancel);