Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / drivers / md / kcopyd.c
index 40e4694..f1db6ef 100644 (file)
@@ -8,10 +8,10 @@
  * completion notification.
  */
 
+#include <asm/types.h>
 #include <asm/atomic.h>
 
 #include <linux/blkdev.h>
-#include <linux/config.h>
 #include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/list.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <linux/workqueue.h>
+#include <linux/mutex.h>
 
 #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);