Browse Source

Merge git://git.kernel.org/pub/scm/linux/kernel/git/agk/linux-2.6-dm

* git://git.kernel.org/pub/scm/linux/kernel/git/agk/linux-2.6-dm: (24 commits)
  dm crypt: add documentation
  dm: remove md argument from specific_minor
  dm table: remove unused dm_create_error_table
  dm table: drop void suspend_targets return
  dm: unplug queues in threads
  dm raid1: use timer
  dm: move include files
  dm kcopyd: rename
  dm: expose macros
  dm kcopyd: remove redundant client counting
  dm kcopyd: private mempool
  dm kcopyd: per device
  dm log: make module use tracking internal
  dm log: move register functions
  dm log: clean interface
  dm kcopyd: clean interface
  dm io: clean interface
  dm io: rename error to error_bits
  dm snapshot: store pointer to target instance
  dm log: move dirty region log code into separate module
  ...
Linus Torvalds 17 years ago
parent
commit
6f97b220f4

+ 52 - 0
Documentation/device-mapper/dm-crypt.txt

@@ -0,0 +1,52 @@
+dm-crypt
+=========
+
+Device-Mapper's "crypt" target provides transparent encryption of block devices
+using the kernel crypto API.
+
+Parameters: <cipher> <key> <iv_offset> <device path> <offset>
+
+<cipher>
+    Encryption cipher and an optional IV generation mode.
+    (In format cipher-chainmode-ivopts:ivmode).
+    Examples:
+       des
+       aes-cbc-essiv:sha256
+       twofish-ecb
+
+    /proc/crypto contains supported crypto modes
+
+<key>
+    Key used for encryption. It is encoded as a hexadecimal number.
+    You can only use key sizes that are valid for the selected cipher.
+
+<iv_offset>
+    The IV offset is a sector count that is added to the sector number
+    before creating the IV.
+
+<device path>
+    This is the device that is going to be used as backend and contains the
+    encrypted data.  You can specify it as a path like /dev/xxx or a device
+    number <major>:<minor>.
+
+<offset>
+    Starting sector within the device where the encrypted data begins.
+
+Example scripts
+===============
+LUKS (Linux Unified Key Setup) is now the preferred way to set up disk
+encryption with dm-crypt using the 'cryptsetup' utility, see
+http://luks.endorphin.org/
+
+[[
+#!/bin/sh
+# Create a crypt device using dmsetup
+dmsetup create crypt1 --table "0 `blockdev --getsize $1` crypt aes-cbc-essiv:sha256 babebabebabebabebabebabebabebabe 0 $1 0"
+]]
+
+[[
+#!/bin/sh
+# Create a crypt device using cryptsetup and LUKS header with default cipher
+cryptsetup luksFormat $1
+cryptsetup luksOpen $1 crypt1
+]]

+ 3 - 3
drivers/md/Makefile

@@ -3,10 +3,10 @@
 #
 #
 
 
 dm-mod-objs	:= dm.o dm-table.o dm-target.o dm-linear.o dm-stripe.o \
 dm-mod-objs	:= dm.o dm-table.o dm-target.o dm-linear.o dm-stripe.o \
-		   dm-ioctl.o dm-io.o kcopyd.o
+		   dm-ioctl.o dm-io.o dm-kcopyd.o
 dm-multipath-objs := dm-hw-handler.o dm-path-selector.o dm-mpath.o
 dm-multipath-objs := dm-hw-handler.o dm-path-selector.o dm-mpath.o
 dm-snapshot-objs := dm-snap.o dm-exception-store.o
 dm-snapshot-objs := dm-snap.o dm-exception-store.o
-dm-mirror-objs	:= dm-log.o dm-raid1.o
+dm-mirror-objs	:= dm-raid1.o
 dm-rdac-objs	:= dm-mpath-rdac.o
 dm-rdac-objs	:= dm-mpath-rdac.o
 dm-hp-sw-objs	:= dm-mpath-hp-sw.o
 dm-hp-sw-objs	:= dm-mpath-hp-sw.o
 md-mod-objs     := md.o bitmap.o
 md-mod-objs     := md.o bitmap.o
@@ -39,7 +39,7 @@ obj-$(CONFIG_DM_MULTIPATH_EMC)	+= dm-emc.o
 obj-$(CONFIG_DM_MULTIPATH_HP)	+= dm-hp-sw.o
 obj-$(CONFIG_DM_MULTIPATH_HP)	+= dm-hp-sw.o
 obj-$(CONFIG_DM_MULTIPATH_RDAC)	+= dm-rdac.o
 obj-$(CONFIG_DM_MULTIPATH_RDAC)	+= dm-rdac.o
 obj-$(CONFIG_DM_SNAPSHOT)	+= dm-snapshot.o
 obj-$(CONFIG_DM_SNAPSHOT)	+= dm-snapshot.o
-obj-$(CONFIG_DM_MIRROR)		+= dm-mirror.o
+obj-$(CONFIG_DM_MIRROR)		+= dm-mirror.o dm-log.o
 obj-$(CONFIG_DM_ZERO)		+= dm-zero.o
 obj-$(CONFIG_DM_ZERO)		+= dm-zero.o
 
 
 quiet_cmd_unroll = UNROLL  $@
 quiet_cmd_unroll = UNROLL  $@

+ 5 - 5
drivers/md/dm-exception-store.c

@@ -9,13 +9,13 @@
 
 
 #include "dm.h"
 #include "dm.h"
 #include "dm-snap.h"
 #include "dm-snap.h"
-#include "dm-io.h"
-#include "kcopyd.h"
 
 
 #include <linux/mm.h>
 #include <linux/mm.h>
 #include <linux/pagemap.h>
 #include <linux/pagemap.h>
 #include <linux/vmalloc.h>
 #include <linux/vmalloc.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
+#include <linux/dm-io.h>
+#include <linux/dm-kcopyd.h>
 
 
 #define DM_MSG_PREFIX "snapshots"
 #define DM_MSG_PREFIX "snapshots"
 #define DM_CHUNK_SIZE_DEFAULT_SECTORS 32	/* 16KB */
 #define DM_CHUNK_SIZE_DEFAULT_SECTORS 32	/* 16KB */
@@ -131,7 +131,7 @@ struct pstore {
 
 
 static unsigned sectors_to_pages(unsigned sectors)
 static unsigned sectors_to_pages(unsigned sectors)
 {
 {
-	return sectors / (PAGE_SIZE >> 9);
+	return DIV_ROUND_UP(sectors, PAGE_SIZE >> 9);
 }
 }
 
 
 static int alloc_area(struct pstore *ps)
 static int alloc_area(struct pstore *ps)
@@ -159,7 +159,7 @@ static void free_area(struct pstore *ps)
 }
 }
 
 
 struct mdata_req {
 struct mdata_req {
-	struct io_region *where;
+	struct dm_io_region *where;
 	struct dm_io_request *io_req;
 	struct dm_io_request *io_req;
 	struct work_struct work;
 	struct work_struct work;
 	int result;
 	int result;
@@ -177,7 +177,7 @@ static void do_metadata(struct work_struct *work)
  */
  */
 static int chunk_io(struct pstore *ps, uint32_t chunk, int rw, int metadata)
 static int chunk_io(struct pstore *ps, uint32_t chunk, int rw, int metadata)
 {
 {
-	struct io_region where = {
+	struct dm_io_region where = {
 		.bdev = ps->snap->cow->bdev,
 		.bdev = ps->snap->cow->bdev,
 		.sector = ps->snap->chunk_size * chunk,
 		.sector = ps->snap->chunk_size * chunk,
 		.count = ps->snap->chunk_size,
 		.count = ps->snap->chunk_size,

+ 22 - 16
drivers/md/dm-io.c

@@ -5,13 +5,14 @@
  * This file is released under the GPL.
  * This file is released under the GPL.
  */
  */
 
 
-#include "dm-io.h"
+#include "dm.h"
 
 
 #include <linux/bio.h>
 #include <linux/bio.h>
 #include <linux/mempool.h>
 #include <linux/mempool.h>
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
+#include <linux/dm-io.h>
 
 
 struct dm_io_client {
 struct dm_io_client {
 	mempool_t *pool;
 	mempool_t *pool;
@@ -20,7 +21,7 @@ struct dm_io_client {
 
 
 /* FIXME: can we shrink this ? */
 /* FIXME: can we shrink this ? */
 struct io {
 struct io {
-	unsigned long error;
+	unsigned long error_bits;
 	atomic_t count;
 	atomic_t count;
 	struct task_struct *sleeper;
 	struct task_struct *sleeper;
 	struct dm_io_client *client;
 	struct dm_io_client *client;
@@ -107,14 +108,14 @@ static inline unsigned bio_get_region(struct bio *bio)
 static void dec_count(struct io *io, unsigned int region, int error)
 static void dec_count(struct io *io, unsigned int region, int error)
 {
 {
 	if (error)
 	if (error)
-		set_bit(region, &io->error);
+		set_bit(region, &io->error_bits);
 
 
 	if (atomic_dec_and_test(&io->count)) {
 	if (atomic_dec_and_test(&io->count)) {
 		if (io->sleeper)
 		if (io->sleeper)
 			wake_up_process(io->sleeper);
 			wake_up_process(io->sleeper);
 
 
 		else {
 		else {
-			unsigned long r = io->error;
+			unsigned long r = io->error_bits;
 			io_notify_fn fn = io->callback;
 			io_notify_fn fn = io->callback;
 			void *context = io->context;
 			void *context = io->context;
 
 
@@ -271,7 +272,7 @@ static void km_dp_init(struct dpages *dp, void *data)
 /*-----------------------------------------------------------------
 /*-----------------------------------------------------------------
  * IO routines that accept a list of pages.
  * IO routines that accept a list of pages.
  *---------------------------------------------------------------*/
  *---------------------------------------------------------------*/
-static void do_region(int rw, unsigned int region, struct io_region *where,
+static void do_region(int rw, unsigned region, struct dm_io_region *where,
 		      struct dpages *dp, struct io *io)
 		      struct dpages *dp, struct io *io)
 {
 {
 	struct bio *bio;
 	struct bio *bio;
@@ -320,7 +321,7 @@ static void do_region(int rw, unsigned int region, struct io_region *where,
 }
 }
 
 
 static void dispatch_io(int rw, unsigned int num_regions,
 static void dispatch_io(int rw, unsigned int num_regions,
-			struct io_region *where, struct dpages *dp,
+			struct dm_io_region *where, struct dpages *dp,
 			struct io *io, int sync)
 			struct io *io, int sync)
 {
 {
 	int i;
 	int i;
@@ -347,17 +348,17 @@ static void dispatch_io(int rw, unsigned int num_regions,
 }
 }
 
 
 static int sync_io(struct dm_io_client *client, unsigned int num_regions,
 static int sync_io(struct dm_io_client *client, unsigned int num_regions,
-		   struct io_region *where, int rw, struct dpages *dp,
+		   struct dm_io_region *where, int rw, struct dpages *dp,
 		   unsigned long *error_bits)
 		   unsigned long *error_bits)
 {
 {
 	struct io io;
 	struct io io;
 
 
-	if (num_regions > 1 && rw != WRITE) {
+	if (num_regions > 1 && (rw & RW_MASK) != WRITE) {
 		WARN_ON(1);
 		WARN_ON(1);
 		return -EIO;
 		return -EIO;
 	}
 	}
 
 
-	io.error = 0;
+	io.error_bits = 0;
 	atomic_set(&io.count, 1); /* see dispatch_io() */
 	atomic_set(&io.count, 1); /* see dispatch_io() */
 	io.sleeper = current;
 	io.sleeper = current;
 	io.client = client;
 	io.client = client;
@@ -378,25 +379,25 @@ static int sync_io(struct dm_io_client *client, unsigned int num_regions,
 		return -EINTR;
 		return -EINTR;
 
 
 	if (error_bits)
 	if (error_bits)
-		*error_bits = io.error;
+		*error_bits = io.error_bits;
 
 
-	return io.error ? -EIO : 0;
+	return io.error_bits ? -EIO : 0;
 }
 }
 
 
 static int async_io(struct dm_io_client *client, unsigned int num_regions,
 static int async_io(struct dm_io_client *client, unsigned int num_regions,
-		    struct io_region *where, int rw, struct dpages *dp,
+		    struct dm_io_region *where, int rw, struct dpages *dp,
 		    io_notify_fn fn, void *context)
 		    io_notify_fn fn, void *context)
 {
 {
 	struct io *io;
 	struct io *io;
 
 
-	if (num_regions > 1 && rw != WRITE) {
+	if (num_regions > 1 && (rw & RW_MASK) != WRITE) {
 		WARN_ON(1);
 		WARN_ON(1);
 		fn(1, context);
 		fn(1, context);
 		return -EIO;
 		return -EIO;
 	}
 	}
 
 
 	io = mempool_alloc(client->pool, GFP_NOIO);
 	io = mempool_alloc(client->pool, GFP_NOIO);
-	io->error = 0;
+	io->error_bits = 0;
 	atomic_set(&io->count, 1); /* see dispatch_io() */
 	atomic_set(&io->count, 1); /* see dispatch_io() */
 	io->sleeper = NULL;
 	io->sleeper = NULL;
 	io->client = client;
 	io->client = client;
@@ -435,10 +436,15 @@ static int dp_init(struct dm_io_request *io_req, struct dpages *dp)
 }
 }
 
 
 /*
 /*
- * New collapsed (a)synchronous interface
+ * New collapsed (a)synchronous interface.
+ *
+ * If the IO is asynchronous (i.e. it has notify.fn), you must either unplug
+ * the queue with blk_unplug() some time later or set the BIO_RW_SYNC bit in
+ * io_req->bi_rw. If you fail to do one of these, the IO will be submitted to
+ * the disk after q->unplug_delay, which defaults to 3ms in blk-settings.c.
  */
  */
 int dm_io(struct dm_io_request *io_req, unsigned num_regions,
 int dm_io(struct dm_io_request *io_req, unsigned num_regions,
-	  struct io_region *where, unsigned long *sync_error_bits)
+	  struct dm_io_region *where, unsigned long *sync_error_bits)
 {
 {
 	int r;
 	int r;
 	struct dpages dp;
 	struct dpages dp;

+ 122 - 176
drivers/md/kcopyd.c → drivers/md/dm-kcopyd.c

@@ -9,9 +9,8 @@
  * completion notification.
  * completion notification.
  */
  */
 
 
-#include <asm/types.h>
+#include <linux/types.h>
 #include <asm/atomic.h>
 #include <asm/atomic.h>
-
 #include <linux/blkdev.h>
 #include <linux/blkdev.h>
 #include <linux/fs.h>
 #include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/init.h>
@@ -23,24 +22,15 @@
 #include <linux/vmalloc.h>
 #include <linux/vmalloc.h>
 #include <linux/workqueue.h>
 #include <linux/workqueue.h>
 #include <linux/mutex.h>
 #include <linux/mutex.h>
+#include <linux/dm-kcopyd.h>
 
 
-#include "kcopyd.h"
-
-static struct workqueue_struct *_kcopyd_wq;
-static struct work_struct _kcopyd_work;
-
-static void wake(void)
-{
-	queue_work(_kcopyd_wq, &_kcopyd_work);
-}
+#include "dm.h"
 
 
 /*-----------------------------------------------------------------
 /*-----------------------------------------------------------------
  * Each kcopyd client has its own little pool of preallocated
  * Each kcopyd client has its own little pool of preallocated
  * pages for kcopyd io.
  * pages for kcopyd io.
  *---------------------------------------------------------------*/
  *---------------------------------------------------------------*/
-struct kcopyd_client {
-	struct list_head list;
-
+struct dm_kcopyd_client {
 	spinlock_t lock;
 	spinlock_t lock;
 	struct page_list *pages;
 	struct page_list *pages;
 	unsigned int nr_pages;
 	unsigned int nr_pages;
@@ -50,8 +40,32 @@ struct kcopyd_client {
 
 
 	wait_queue_head_t destroyq;
 	wait_queue_head_t destroyq;
 	atomic_t nr_jobs;
 	atomic_t nr_jobs;
+
+	mempool_t *job_pool;
+
+	struct workqueue_struct *kcopyd_wq;
+	struct work_struct kcopyd_work;
+
+/*
+ * 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;
 };
 };
 
 
+static void wake(struct dm_kcopyd_client *kc)
+{
+	queue_work(kc->kcopyd_wq, &kc->kcopyd_work);
+}
+
 static struct page_list *alloc_pl(void)
 static struct page_list *alloc_pl(void)
 {
 {
 	struct page_list *pl;
 	struct page_list *pl;
@@ -75,7 +89,7 @@ static void free_pl(struct page_list *pl)
 	kfree(pl);
 	kfree(pl);
 }
 }
 
 
-static int kcopyd_get_pages(struct kcopyd_client *kc,
+static int kcopyd_get_pages(struct dm_kcopyd_client *kc,
 			    unsigned int nr, struct page_list **pages)
 			    unsigned int nr, struct page_list **pages)
 {
 {
 	struct page_list *pl;
 	struct page_list *pl;
@@ -98,7 +112,7 @@ static int kcopyd_get_pages(struct kcopyd_client *kc,
 	return 0;
 	return 0;
 }
 }
 
 
-static void kcopyd_put_pages(struct kcopyd_client *kc, struct page_list *pl)
+static void kcopyd_put_pages(struct dm_kcopyd_client *kc, struct page_list *pl)
 {
 {
 	struct page_list *cursor;
 	struct page_list *cursor;
 
 
@@ -126,7 +140,7 @@ static void drop_pages(struct page_list *pl)
 	}
 	}
 }
 }
 
 
-static int client_alloc_pages(struct kcopyd_client *kc, unsigned int nr)
+static int client_alloc_pages(struct dm_kcopyd_client *kc, unsigned int nr)
 {
 {
 	unsigned int i;
 	unsigned int i;
 	struct page_list *pl = NULL, *next;
 	struct page_list *pl = NULL, *next;
@@ -147,7 +161,7 @@ static int client_alloc_pages(struct kcopyd_client *kc, unsigned int nr)
 	return 0;
 	return 0;
 }
 }
 
 
-static void client_free_pages(struct kcopyd_client *kc)
+static void client_free_pages(struct dm_kcopyd_client *kc)
 {
 {
 	BUG_ON(kc->nr_free_pages != kc->nr_pages);
 	BUG_ON(kc->nr_free_pages != kc->nr_pages);
 	drop_pages(kc->pages);
 	drop_pages(kc->pages);
@@ -161,7 +175,7 @@ static void client_free_pages(struct kcopyd_client *kc)
  * ever having to do io (which could cause a deadlock).
  * ever having to do io (which could cause a deadlock).
  *---------------------------------------------------------------*/
  *---------------------------------------------------------------*/
 struct kcopyd_job {
 struct kcopyd_job {
-	struct kcopyd_client *kc;
+	struct dm_kcopyd_client *kc;
 	struct list_head list;
 	struct list_head list;
 	unsigned long flags;
 	unsigned long flags;
 
 
@@ -175,13 +189,13 @@ struct kcopyd_job {
 	 * Either READ or WRITE
 	 * Either READ or WRITE
 	 */
 	 */
 	int rw;
 	int rw;
-	struct io_region source;
+	struct dm_io_region source;
 
 
 	/*
 	/*
 	 * The destinations for the transfer.
 	 * The destinations for the transfer.
 	 */
 	 */
 	unsigned int num_dests;
 	unsigned int num_dests;
-	struct io_region dests[KCOPYD_MAX_REGIONS];
+	struct dm_io_region dests[DM_KCOPYD_MAX_REGIONS];
 
 
 	sector_t offset;
 	sector_t offset;
 	unsigned int nr_pages;
 	unsigned int nr_pages;
@@ -191,7 +205,7 @@ struct kcopyd_job {
 	 * Set this to ensure you are notified when the job has
 	 * Set this to ensure you are notified when the job has
 	 * completed.  'context' is for callback to use.
 	 * completed.  'context' is for callback to use.
 	 */
 	 */
-	kcopyd_notify_fn fn;
+	dm_kcopyd_notify_fn fn;
 	void *context;
 	void *context;
 
 
 	/*
 	/*
@@ -207,47 +221,19 @@ struct kcopyd_job {
 #define MIN_JOBS 512
 #define MIN_JOBS 512
 
 
 static struct kmem_cache *_job_cache;
 static struct kmem_cache *_job_cache;
-static mempool_t *_job_pool;
 
 
-/*
- * 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.
- */
-static DEFINE_SPINLOCK(_job_lock);
-
-static LIST_HEAD(_complete_jobs);
-static LIST_HEAD(_io_jobs);
-static LIST_HEAD(_pages_jobs);
-
-static int jobs_init(void)
+int __init dm_kcopyd_init(void)
 {
 {
 	_job_cache = KMEM_CACHE(kcopyd_job, 0);
 	_job_cache = KMEM_CACHE(kcopyd_job, 0);
 	if (!_job_cache)
 	if (!_job_cache)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	_job_pool = mempool_create_slab_pool(MIN_JOBS, _job_cache);
-	if (!_job_pool) {
-		kmem_cache_destroy(_job_cache);
-		return -ENOMEM;
-	}
-
 	return 0;
 	return 0;
 }
 }
 
 
-static void jobs_exit(void)
+void dm_kcopyd_exit(void)
 {
 {
-	BUG_ON(!list_empty(&_complete_jobs));
-	BUG_ON(!list_empty(&_io_jobs));
-	BUG_ON(!list_empty(&_pages_jobs));
-
-	mempool_destroy(_job_pool);
 	kmem_cache_destroy(_job_cache);
 	kmem_cache_destroy(_job_cache);
-	_job_pool = NULL;
 	_job_cache = NULL;
 	_job_cache = NULL;
 }
 }
 
 
@@ -255,18 +241,19 @@ static void jobs_exit(void)
  * Functions to push and pop a job onto the head of a given job
  * Functions to push and pop a job onto the head of a given job
  * list.
  * list.
  */
  */
-static struct kcopyd_job *pop(struct list_head *jobs)
+static struct kcopyd_job *pop(struct list_head *jobs,
+			      struct dm_kcopyd_client *kc)
 {
 {
 	struct kcopyd_job *job = NULL;
 	struct kcopyd_job *job = NULL;
 	unsigned long flags;
 	unsigned long flags;
 
 
-	spin_lock_irqsave(&_job_lock, flags);
+	spin_lock_irqsave(&kc->job_lock, flags);
 
 
 	if (!list_empty(jobs)) {
 	if (!list_empty(jobs)) {
 		job = list_entry(jobs->next, struct kcopyd_job, list);
 		job = list_entry(jobs->next, struct kcopyd_job, list);
 		list_del(&job->list);
 		list_del(&job->list);
 	}
 	}
-	spin_unlock_irqrestore(&_job_lock, flags);
+	spin_unlock_irqrestore(&kc->job_lock, flags);
 
 
 	return job;
 	return job;
 }
 }
@@ -274,10 +261,11 @@ static struct kcopyd_job *pop(struct list_head *jobs)
 static void push(struct list_head *jobs, struct kcopyd_job *job)
 static void push(struct list_head *jobs, struct kcopyd_job *job)
 {
 {
 	unsigned long flags;
 	unsigned long flags;
+	struct dm_kcopyd_client *kc = job->kc;
 
 
-	spin_lock_irqsave(&_job_lock, flags);
+	spin_lock_irqsave(&kc->job_lock, flags);
 	list_add_tail(&job->list, jobs);
 	list_add_tail(&job->list, jobs);
-	spin_unlock_irqrestore(&_job_lock, flags);
+	spin_unlock_irqrestore(&kc->job_lock, flags);
 }
 }
 
 
 /*
 /*
@@ -294,11 +282,11 @@ static int run_complete_job(struct kcopyd_job *job)
 	void *context = job->context;
 	void *context = job->context;
 	int read_err = job->read_err;
 	int read_err = job->read_err;
 	unsigned long write_err = job->write_err;
 	unsigned long write_err = job->write_err;
-	kcopyd_notify_fn fn = job->fn;
-	struct kcopyd_client *kc = job->kc;
+	dm_kcopyd_notify_fn fn = job->fn;
+	struct dm_kcopyd_client *kc = job->kc;
 
 
 	kcopyd_put_pages(kc, job->pages);
 	kcopyd_put_pages(kc, job->pages);
-	mempool_free(job, _job_pool);
+	mempool_free(job, kc->job_pool);
 	fn(read_err, write_err, context);
 	fn(read_err, write_err, context);
 
 
 	if (atomic_dec_and_test(&kc->nr_jobs))
 	if (atomic_dec_and_test(&kc->nr_jobs))
@@ -310,6 +298,7 @@ static int run_complete_job(struct kcopyd_job *job)
 static void complete_io(unsigned long error, void *context)
 static void complete_io(unsigned long error, void *context)
 {
 {
 	struct kcopyd_job *job = (struct kcopyd_job *) context;
 	struct kcopyd_job *job = (struct kcopyd_job *) context;
+	struct dm_kcopyd_client *kc = job->kc;
 
 
 	if (error) {
 	if (error) {
 		if (job->rw == WRITE)
 		if (job->rw == WRITE)
@@ -317,22 +306,22 @@ static void complete_io(unsigned long error, void *context)
 		else
 		else
 			job->read_err = 1;
 			job->read_err = 1;
 
 
-		if (!test_bit(KCOPYD_IGNORE_ERROR, &job->flags)) {
-			push(&_complete_jobs, job);
-			wake();
+		if (!test_bit(DM_KCOPYD_IGNORE_ERROR, &job->flags)) {
+			push(&kc->complete_jobs, job);
+			wake(kc);
 			return;
 			return;
 		}
 		}
 	}
 	}
 
 
 	if (job->rw == WRITE)
 	if (job->rw == WRITE)
-		push(&_complete_jobs, job);
+		push(&kc->complete_jobs, job);
 
 
 	else {
 	else {
 		job->rw = WRITE;
 		job->rw = WRITE;
-		push(&_io_jobs, job);
+		push(&kc->io_jobs, job);
 	}
 	}
 
 
-	wake();
+	wake(kc);
 }
 }
 
 
 /*
 /*
@@ -343,7 +332,7 @@ static int run_io_job(struct kcopyd_job *job)
 {
 {
 	int r;
 	int r;
 	struct dm_io_request io_req = {
 	struct dm_io_request io_req = {
-		.bi_rw = job->rw,
+		.bi_rw = job->rw | (1 << BIO_RW_SYNC),
 		.mem.type = DM_IO_PAGE_LIST,
 		.mem.type = DM_IO_PAGE_LIST,
 		.mem.ptr.pl = job->pages,
 		.mem.ptr.pl = job->pages,
 		.mem.offset = job->offset,
 		.mem.offset = job->offset,
@@ -369,7 +358,7 @@ static int run_pages_job(struct kcopyd_job *job)
 	r = kcopyd_get_pages(job->kc, job->nr_pages, &job->pages);
 	r = kcopyd_get_pages(job->kc, job->nr_pages, &job->pages);
 	if (!r) {
 	if (!r) {
 		/* this job is ready for io */
 		/* this job is ready for io */
-		push(&_io_jobs, job);
+		push(&job->kc->io_jobs, job);
 		return 0;
 		return 0;
 	}
 	}
 
 
@@ -384,12 +373,13 @@ static int run_pages_job(struct kcopyd_job *job)
  * Run through a list for as long as possible.  Returns the count
  * Run through a list for as long as possible.  Returns the count
  * of successful jobs.
  * of successful jobs.
  */
  */
-static int process_jobs(struct list_head *jobs, int (*fn) (struct kcopyd_job *))
+static int process_jobs(struct list_head *jobs, struct dm_kcopyd_client *kc,
+			int (*fn) (struct kcopyd_job *))
 {
 {
 	struct kcopyd_job *job;
 	struct kcopyd_job *job;
 	int r, count = 0;
 	int r, count = 0;
 
 
-	while ((job = pop(jobs))) {
+	while ((job = pop(jobs, kc))) {
 
 
 		r = fn(job);
 		r = fn(job);
 
 
@@ -399,7 +389,7 @@ static int process_jobs(struct list_head *jobs, int (*fn) (struct kcopyd_job *))
 				job->write_err = (unsigned long) -1L;
 				job->write_err = (unsigned long) -1L;
 			else
 			else
 				job->read_err = 1;
 				job->read_err = 1;
-			push(&_complete_jobs, job);
+			push(&kc->complete_jobs, job);
 			break;
 			break;
 		}
 		}
 
 
@@ -421,8 +411,11 @@ static int process_jobs(struct list_head *jobs, int (*fn) (struct kcopyd_job *))
 /*
 /*
  * kcopyd does this every time it's woken up.
  * kcopyd does this every time it's woken up.
  */
  */
-static void do_work(struct work_struct *ignored)
+static void do_work(struct work_struct *work)
 {
 {
+	struct dm_kcopyd_client *kc = container_of(work,
+					struct dm_kcopyd_client, kcopyd_work);
+
 	/*
 	/*
 	 * The order that these are called is *very* important.
 	 * The order that these are called is *very* important.
 	 * complete jobs can free some pages for pages jobs.
 	 * complete jobs can free some pages for pages jobs.
@@ -430,9 +423,9 @@ static void do_work(struct work_struct *ignored)
 	 * list.  io jobs call wake when they complete and it all
 	 * list.  io jobs call wake when they complete and it all
 	 * starts again.
 	 * starts again.
 	 */
 	 */
-	process_jobs(&_complete_jobs, run_complete_job);
-	process_jobs(&_pages_jobs, run_pages_job);
-	process_jobs(&_io_jobs, run_io_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);
 }
 }
 
 
 /*
 /*
@@ -442,9 +435,10 @@ static void do_work(struct work_struct *ignored)
  */
  */
 static void dispatch_job(struct kcopyd_job *job)
 static void dispatch_job(struct kcopyd_job *job)
 {
 {
-	atomic_inc(&job->kc->nr_jobs);
-	push(&_pages_jobs, job);
-	wake();
+	struct dm_kcopyd_client *kc = job->kc;
+	atomic_inc(&kc->nr_jobs);
+	push(&kc->pages_jobs, job);
+	wake(kc);
 }
 }
 
 
 #define SUB_JOB_SIZE 128
 #define SUB_JOB_SIZE 128
@@ -469,7 +463,7 @@ static void segment_complete(int read_err, unsigned long write_err,
 	 * Only dispatch more work if there hasn't been an error.
 	 * Only dispatch more work if there hasn't been an error.
 	 */
 	 */
 	if ((!job->read_err && !job->write_err) ||
 	if ((!job->read_err && !job->write_err) ||
-	    test_bit(KCOPYD_IGNORE_ERROR, &job->flags)) {
+	    test_bit(DM_KCOPYD_IGNORE_ERROR, &job->flags)) {
 		/* get the next chunk of work */
 		/* get the next chunk of work */
 		progress = job->progress;
 		progress = job->progress;
 		count = job->source.count - progress;
 		count = job->source.count - progress;
@@ -484,7 +478,8 @@ static void segment_complete(int read_err, unsigned long write_err,
 
 
 	if (count) {
 	if (count) {
 		int i;
 		int i;
-		struct kcopyd_job *sub_job = mempool_alloc(_job_pool, GFP_NOIO);
+		struct kcopyd_job *sub_job = mempool_alloc(job->kc->job_pool,
+							   GFP_NOIO);
 
 
 		*sub_job = *job;
 		*sub_job = *job;
 		sub_job->source.sector += progress;
 		sub_job->source.sector += progress;
@@ -508,7 +503,7 @@ static void segment_complete(int read_err, unsigned long write_err,
 		 * after we've completed.
 		 * after we've completed.
 		 */
 		 */
 		job->fn(read_err, write_err, job->context);
 		job->fn(read_err, write_err, job->context);
-		mempool_free(job, _job_pool);
+		mempool_free(job, job->kc->job_pool);
 	}
 	}
 }
 }
 
 
@@ -526,16 +521,16 @@ static void split_job(struct kcopyd_job *job)
 		segment_complete(0, 0u, job);
 		segment_complete(0, 0u, job);
 }
 }
 
 
-int kcopyd_copy(struct kcopyd_client *kc, struct io_region *from,
-		unsigned int num_dests, struct io_region *dests,
-		unsigned int flags, kcopyd_notify_fn fn, void *context)
+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)
 {
 {
 	struct kcopyd_job *job;
 	struct kcopyd_job *job;
 
 
 	/*
 	/*
 	 * Allocate a new job.
 	 * Allocate a new job.
 	 */
 	 */
-	job = mempool_alloc(_job_pool, GFP_NOIO);
+	job = mempool_alloc(kc->job_pool, GFP_NOIO);
 
 
 	/*
 	/*
 	 * set up for the read.
 	 * set up for the read.
@@ -569,6 +564,7 @@ int kcopyd_copy(struct kcopyd_client *kc, struct io_region *from,
 
 
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL(dm_kcopyd_copy);
 
 
 /*
 /*
  * Cancels a kcopyd job, eg. someone might be deactivating a
  * Cancels a kcopyd job, eg. someone might be deactivating a
@@ -583,126 +579,76 @@ int kcopyd_cancel(struct kcopyd_job *job, int block)
 #endif  /*  0  */
 #endif  /*  0  */
 
 
 /*-----------------------------------------------------------------
 /*-----------------------------------------------------------------
- * Unit setup
+ * Client setup
  *---------------------------------------------------------------*/
  *---------------------------------------------------------------*/
-static DEFINE_MUTEX(_client_lock);
-static LIST_HEAD(_clients);
-
-static void client_add(struct kcopyd_client *kc)
+int dm_kcopyd_client_create(unsigned int nr_pages,
+			    struct dm_kcopyd_client **result)
 {
 {
-	mutex_lock(&_client_lock);
-	list_add(&kc->list, &_clients);
-	mutex_unlock(&_client_lock);
-}
-
-static void client_del(struct kcopyd_client *kc)
-{
-	mutex_lock(&_client_lock);
-	list_del(&kc->list);
-	mutex_unlock(&_client_lock);
-}
-
-static DEFINE_MUTEX(kcopyd_init_lock);
-static int kcopyd_clients = 0;
+	int r = -ENOMEM;
+	struct dm_kcopyd_client *kc;
 
 
-static int kcopyd_init(void)
-{
-	int r;
-
-	mutex_lock(&kcopyd_init_lock);
-
-	if (kcopyd_clients) {
-		/* Already initialized. */
-		kcopyd_clients++;
-		mutex_unlock(&kcopyd_init_lock);
-		return 0;
-	}
-
-	r = jobs_init();
-	if (r) {
-		mutex_unlock(&kcopyd_init_lock);
-		return r;
-	}
-
-	_kcopyd_wq = create_singlethread_workqueue("kcopyd");
-	if (!_kcopyd_wq) {
-		jobs_exit();
-		mutex_unlock(&kcopyd_init_lock);
+	kc = kmalloc(sizeof(*kc), GFP_KERNEL);
+	if (!kc)
 		return -ENOMEM;
 		return -ENOMEM;
-	}
-
-	kcopyd_clients++;
-	INIT_WORK(&_kcopyd_work, do_work);
-	mutex_unlock(&kcopyd_init_lock);
-	return 0;
-}
 
 
-static void kcopyd_exit(void)
-{
-	mutex_lock(&kcopyd_init_lock);
-	kcopyd_clients--;
-	if (!kcopyd_clients) {
-		jobs_exit();
-		destroy_workqueue(_kcopyd_wq);
-		_kcopyd_wq = NULL;
-	}
-	mutex_unlock(&kcopyd_init_lock);
-}
-
-int kcopyd_client_create(unsigned int nr_pages, struct kcopyd_client **result)
-{
-	int r = 0;
-	struct kcopyd_client *kc;
+	spin_lock_init(&kc->lock);
+	spin_lock_init(&kc->job_lock);
+	INIT_LIST_HEAD(&kc->complete_jobs);
+	INIT_LIST_HEAD(&kc->io_jobs);
+	INIT_LIST_HEAD(&kc->pages_jobs);
 
 
-	r = kcopyd_init();
-	if (r)
-		return r;
+	kc->job_pool = mempool_create_slab_pool(MIN_JOBS, _job_cache);
+	if (!kc->job_pool)
+		goto bad_slab;
 
 
-	kc = kmalloc(sizeof(*kc), GFP_KERNEL);
-	if (!kc) {
-		kcopyd_exit();
-		return -ENOMEM;
-	}
+	INIT_WORK(&kc->kcopyd_work, do_work);
+	kc->kcopyd_wq = create_singlethread_workqueue("kcopyd");
+	if (!kc->kcopyd_wq)
+		goto bad_workqueue;
 
 
-	spin_lock_init(&kc->lock);
 	kc->pages = NULL;
 	kc->pages = NULL;
 	kc->nr_pages = kc->nr_free_pages = 0;
 	kc->nr_pages = kc->nr_free_pages = 0;
 	r = client_alloc_pages(kc, nr_pages);
 	r = client_alloc_pages(kc, nr_pages);
-	if (r) {
-		kfree(kc);
-		kcopyd_exit();
-		return r;
-	}
+	if (r)
+		goto bad_client_pages;
 
 
 	kc->io_client = dm_io_client_create(nr_pages);
 	kc->io_client = dm_io_client_create(nr_pages);
 	if (IS_ERR(kc->io_client)) {
 	if (IS_ERR(kc->io_client)) {
 		r = PTR_ERR(kc->io_client);
 		r = PTR_ERR(kc->io_client);
-		client_free_pages(kc);
-		kfree(kc);
-		kcopyd_exit();
-		return r;
+		goto bad_io_client;
 	}
 	}
 
 
 	init_waitqueue_head(&kc->destroyq);
 	init_waitqueue_head(&kc->destroyq);
 	atomic_set(&kc->nr_jobs, 0);
 	atomic_set(&kc->nr_jobs, 0);
 
 
-	client_add(kc);
 	*result = kc;
 	*result = kc;
 	return 0;
 	return 0;
+
+bad_io_client:
+	client_free_pages(kc);
+bad_client_pages:
+	destroy_workqueue(kc->kcopyd_wq);
+bad_workqueue:
+	mempool_destroy(kc->job_pool);
+bad_slab:
+	kfree(kc);
+
+	return r;
 }
 }
+EXPORT_SYMBOL(dm_kcopyd_client_create);
 
 
-void kcopyd_client_destroy(struct kcopyd_client *kc)
+void dm_kcopyd_client_destroy(struct dm_kcopyd_client *kc)
 {
 {
 	/* Wait for completion of all jobs submitted by this client. */
 	/* Wait for completion of all jobs submitted by this client. */
 	wait_event(kc->destroyq, !atomic_read(&kc->nr_jobs));
 	wait_event(kc->destroyq, !atomic_read(&kc->nr_jobs));
 
 
+	BUG_ON(!list_empty(&kc->complete_jobs));
+	BUG_ON(!list_empty(&kc->io_jobs));
+	BUG_ON(!list_empty(&kc->pages_jobs));
+	destroy_workqueue(kc->kcopyd_wq);
 	dm_io_client_destroy(kc->io_client);
 	dm_io_client_destroy(kc->io_client);
 	client_free_pages(kc);
 	client_free_pages(kc);
-	client_del(kc);
+	mempool_destroy(kc->job_pool);
 	kfree(kc);
 	kfree(kc);
-	kcopyd_exit();
 }
 }
-
-EXPORT_SYMBOL(kcopyd_client_create);
-EXPORT_SYMBOL(kcopyd_client_destroy);
-EXPORT_SYMBOL(kcopyd_copy);
+EXPORT_SYMBOL(dm_kcopyd_client_destroy);

+ 164 - 90
drivers/md/dm-log.c

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright (C) 2003 Sistina Software
  * Copyright (C) 2003 Sistina Software
+ * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
  *
  *
  * This file is released under the LGPL.
  * This file is released under the LGPL.
  */
  */
@@ -8,64 +9,58 @@
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/vmalloc.h>
 #include <linux/vmalloc.h>
+#include <linux/dm-io.h>
+#include <linux/dm-dirty-log.h>
 
 
-#include "dm-log.h"
-#include "dm-io.h"
+#include "dm.h"
 
 
-#define DM_MSG_PREFIX "mirror log"
+#define DM_MSG_PREFIX "dirty region log"
 
 
-static LIST_HEAD(_log_types);
-static DEFINE_SPINLOCK(_lock);
+struct dm_dirty_log_internal {
+	struct dm_dirty_log_type *type;
 
 
-int dm_register_dirty_log_type(struct dirty_log_type *type)
-{
-	spin_lock(&_lock);
-	type->use_count = 0;
-	list_add(&type->list, &_log_types);
-	spin_unlock(&_lock);
+	struct list_head list;
+	long use;
+};
 
 
-	return 0;
-}
+static LIST_HEAD(_log_types);
+static DEFINE_SPINLOCK(_lock);
 
 
-int dm_unregister_dirty_log_type(struct dirty_log_type *type)
+static struct dm_dirty_log_internal *__find_dirty_log_type(const char *name)
 {
 {
-	spin_lock(&_lock);
-
-	if (type->use_count)
-		DMWARN("Attempt to unregister a log type that is still in use");
-	else
-		list_del(&type->list);
+	struct dm_dirty_log_internal *log_type;
 
 
-	spin_unlock(&_lock);
+	list_for_each_entry(log_type, &_log_types, list)
+		if (!strcmp(name, log_type->type->name))
+			return log_type;
 
 
-	return 0;
+	return NULL;
 }
 }
 
 
-static struct dirty_log_type *_get_type(const char *type_name)
+static struct dm_dirty_log_internal *_get_dirty_log_type(const char *name)
 {
 {
-	struct dirty_log_type *type;
+	struct dm_dirty_log_internal *log_type;
 
 
 	spin_lock(&_lock);
 	spin_lock(&_lock);
-	list_for_each_entry (type, &_log_types, list)
-		if (!strcmp(type_name, type->name)) {
-			if (!type->use_count && !try_module_get(type->module)){
-				spin_unlock(&_lock);
-				return NULL;
-			}
-			type->use_count++;
-			spin_unlock(&_lock);
-			return type;
-		}
+
+	log_type = __find_dirty_log_type(name);
+	if (log_type) {
+		if (!log_type->use && !try_module_get(log_type->type->module))
+			log_type = NULL;
+		else
+			log_type->use++;
+	}
 
 
 	spin_unlock(&_lock);
 	spin_unlock(&_lock);
-	return NULL;
+
+	return log_type;
 }
 }
 
 
 /*
 /*
  * get_type
  * get_type
  * @type_name
  * @type_name
  *
  *
- * Attempt to retrieve the dirty_log_type by name.  If not already
+ * Attempt to retrieve the dm_dirty_log_type by name.  If not already
  * available, attempt to load the appropriate module.
  * available, attempt to load the appropriate module.
  *
  *
  * Log modules are named "dm-log-" followed by the 'type_name'.
  * Log modules are named "dm-log-" followed by the 'type_name'.
@@ -78,14 +73,17 @@ static struct dirty_log_type *_get_type(const char *type_name)
  *
  *
  * Returns: dirty_log_type* on success, NULL on failure
  * Returns: dirty_log_type* on success, NULL on failure
  */
  */
-static struct dirty_log_type *get_type(const char *type_name)
+static struct dm_dirty_log_type *get_type(const char *type_name)
 {
 {
 	char *p, *type_name_dup;
 	char *p, *type_name_dup;
-	struct dirty_log_type *type;
+	struct dm_dirty_log_internal *log_type;
+
+	if (!type_name)
+		return NULL;
 
 
-	type = _get_type(type_name);
-	if (type)
-		return type;
+	log_type = _get_dirty_log_type(type_name);
+	if (log_type)
+		return log_type->type;
 
 
 	type_name_dup = kstrdup(type_name, GFP_KERNEL);
 	type_name_dup = kstrdup(type_name, GFP_KERNEL);
 	if (!type_name_dup) {
 	if (!type_name_dup) {
@@ -95,34 +93,106 @@ static struct dirty_log_type *get_type(const char *type_name)
 	}
 	}
 
 
 	while (request_module("dm-log-%s", type_name_dup) ||
 	while (request_module("dm-log-%s", type_name_dup) ||
-	       !(type = _get_type(type_name))) {
+	       !(log_type = _get_dirty_log_type(type_name))) {
 		p = strrchr(type_name_dup, '-');
 		p = strrchr(type_name_dup, '-');
 		if (!p)
 		if (!p)
 			break;
 			break;
 		p[0] = '\0';
 		p[0] = '\0';
 	}
 	}
 
 
-	if (!type)
+	if (!log_type)
 		DMWARN("Module for logging type \"%s\" not found.", type_name);
 		DMWARN("Module for logging type \"%s\" not found.", type_name);
 
 
 	kfree(type_name_dup);
 	kfree(type_name_dup);
 
 
-	return type;
+	return log_type ? log_type->type : NULL;
 }
 }
 
 
-static void put_type(struct dirty_log_type *type)
+static void put_type(struct dm_dirty_log_type *type)
 {
 {
+	struct dm_dirty_log_internal *log_type;
+
+	if (!type)
+		return;
+
 	spin_lock(&_lock);
 	spin_lock(&_lock);
-	if (!--type->use_count)
+	log_type = __find_dirty_log_type(type->name);
+	if (!log_type)
+		goto out;
+
+	if (!--log_type->use)
 		module_put(type->module);
 		module_put(type->module);
+
+	BUG_ON(log_type->use < 0);
+
+out:
 	spin_unlock(&_lock);
 	spin_unlock(&_lock);
 }
 }
 
 
-struct dirty_log *dm_create_dirty_log(const char *type_name, struct dm_target *ti,
-				      unsigned int argc, char **argv)
+static struct dm_dirty_log_internal *_alloc_dirty_log_type(struct dm_dirty_log_type *type)
 {
 {
-	struct dirty_log_type *type;
-	struct dirty_log *log;
+	struct dm_dirty_log_internal *log_type = kzalloc(sizeof(*log_type),
+							 GFP_KERNEL);
+
+	if (log_type)
+		log_type->type = type;
+
+	return log_type;
+}
+
+int dm_dirty_log_type_register(struct dm_dirty_log_type *type)
+{
+	struct dm_dirty_log_internal *log_type = _alloc_dirty_log_type(type);
+	int r = 0;
+
+	if (!log_type)
+		return -ENOMEM;
+
+	spin_lock(&_lock);
+	if (!__find_dirty_log_type(type->name))
+		list_add(&log_type->list, &_log_types);
+	else {
+		kfree(log_type);
+		r = -EEXIST;
+	}
+	spin_unlock(&_lock);
+
+	return r;
+}
+EXPORT_SYMBOL(dm_dirty_log_type_register);
+
+int dm_dirty_log_type_unregister(struct dm_dirty_log_type *type)
+{
+	struct dm_dirty_log_internal *log_type;
+
+	spin_lock(&_lock);
+
+	log_type = __find_dirty_log_type(type->name);
+	if (!log_type) {
+		spin_unlock(&_lock);
+		return -EINVAL;
+	}
+
+	if (log_type->use) {
+		spin_unlock(&_lock);
+		return -ETXTBSY;
+	}
+
+	list_del(&log_type->list);
+
+	spin_unlock(&_lock);
+	kfree(log_type);
+
+	return 0;
+}
+EXPORT_SYMBOL(dm_dirty_log_type_unregister);
+
+struct dm_dirty_log *dm_dirty_log_create(const char *type_name,
+					 struct dm_target *ti,
+					 unsigned int argc, char **argv)
+{
+	struct dm_dirty_log_type *type;
+	struct dm_dirty_log *log;
 
 
 	log = kmalloc(sizeof(*log), GFP_KERNEL);
 	log = kmalloc(sizeof(*log), GFP_KERNEL);
 	if (!log)
 	if (!log)
@@ -143,13 +213,15 @@ struct dirty_log *dm_create_dirty_log(const char *type_name, struct dm_target *t
 
 
 	return log;
 	return log;
 }
 }
+EXPORT_SYMBOL(dm_dirty_log_create);
 
 
-void dm_destroy_dirty_log(struct dirty_log *log)
+void dm_dirty_log_destroy(struct dm_dirty_log *log)
 {
 {
 	log->type->dtr(log);
 	log->type->dtr(log);
 	put_type(log->type);
 	put_type(log->type);
 	kfree(log);
 	kfree(log);
 }
 }
+EXPORT_SYMBOL(dm_dirty_log_destroy);
 
 
 /*-----------------------------------------------------------------
 /*-----------------------------------------------------------------
  * Persistent and core logs share a lot of their implementation.
  * Persistent and core logs share a lot of their implementation.
@@ -207,7 +279,7 @@ struct log_c {
 	struct dm_dev *log_dev;
 	struct dm_dev *log_dev;
 	struct log_header header;
 	struct log_header header;
 
 
-	struct io_region header_location;
+	struct dm_io_region header_location;
 	struct log_header *disk_header;
 	struct log_header *disk_header;
 };
 };
 
 
@@ -215,7 +287,7 @@ struct log_c {
  * The touched member needs to be updated every time we access
  * The touched member needs to be updated every time we access
  * one of the bitsets.
  * one of the bitsets.
  */
  */
-static  inline int log_test_bit(uint32_t *bs, unsigned bit)
+static inline int log_test_bit(uint32_t *bs, unsigned bit)
 {
 {
 	return ext2_test_bit(bit, (unsigned long *) bs) ? 1 : 0;
 	return ext2_test_bit(bit, (unsigned long *) bs) ? 1 : 0;
 }
 }
@@ -302,7 +374,7 @@ static inline int write_header(struct log_c *log)
  * argv contains region_size followed optionally by [no]sync
  * argv contains region_size followed optionally by [no]sync
  *--------------------------------------------------------------*/
  *--------------------------------------------------------------*/
 #define BYTE_SHIFT 3
 #define BYTE_SHIFT 3
-static int create_log_context(struct dirty_log *log, struct dm_target *ti,
+static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,
 			      unsigned int argc, char **argv,
 			      unsigned int argc, char **argv,
 			      struct dm_dev *dev)
 			      struct dm_dev *dev)
 {
 {
@@ -315,7 +387,7 @@ static int create_log_context(struct dirty_log *log, struct dm_target *ti,
 	int r;
 	int r;
 
 
 	if (argc < 1 || argc > 2) {
 	if (argc < 1 || argc > 2) {
-		DMWARN("wrong number of arguments to mirror log");
+		DMWARN("wrong number of arguments to dirty region log");
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
@@ -325,8 +397,8 @@ static int create_log_context(struct dirty_log *log, struct dm_target *ti,
 		else if (!strcmp(argv[1], "nosync"))
 		else if (!strcmp(argv[1], "nosync"))
 			sync = NOSYNC;
 			sync = NOSYNC;
 		else {
 		else {
-			DMWARN("unrecognised sync argument to mirror log: %s",
-			       argv[1]);
+			DMWARN("unrecognised sync argument to "
+			       "dirty region log: %s", argv[1]);
 			return -EINVAL;
 			return -EINVAL;
 		}
 		}
 	}
 	}
@@ -434,7 +506,7 @@ static int create_log_context(struct dirty_log *log, struct dm_target *ti,
 	return 0;
 	return 0;
 }
 }
 
 
-static int core_ctr(struct dirty_log *log, struct dm_target *ti,
+static int core_ctr(struct dm_dirty_log *log, struct dm_target *ti,
 		    unsigned int argc, char **argv)
 		    unsigned int argc, char **argv)
 {
 {
 	return create_log_context(log, ti, argc, argv, NULL);
 	return create_log_context(log, ti, argc, argv, NULL);
@@ -447,7 +519,7 @@ static void destroy_log_context(struct log_c *lc)
 	kfree(lc);
 	kfree(lc);
 }
 }
 
 
-static void core_dtr(struct dirty_log *log)
+static void core_dtr(struct dm_dirty_log *log)
 {
 {
 	struct log_c *lc = (struct log_c *) log->context;
 	struct log_c *lc = (struct log_c *) log->context;
 
 
@@ -460,14 +532,14 @@ static void core_dtr(struct dirty_log *log)
  *
  *
  * argv contains log_device region_size followed optionally by [no]sync
  * argv contains log_device region_size followed optionally by [no]sync
  *--------------------------------------------------------------*/
  *--------------------------------------------------------------*/
-static int disk_ctr(struct dirty_log *log, struct dm_target *ti,
+static int disk_ctr(struct dm_dirty_log *log, struct dm_target *ti,
 		    unsigned int argc, char **argv)
 		    unsigned int argc, char **argv)
 {
 {
 	int r;
 	int r;
 	struct dm_dev *dev;
 	struct dm_dev *dev;
 
 
 	if (argc < 2 || argc > 3) {
 	if (argc < 2 || argc > 3) {
-		DMWARN("wrong number of arguments to disk mirror log");
+		DMWARN("wrong number of arguments to disk dirty region log");
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
@@ -485,7 +557,7 @@ static int disk_ctr(struct dirty_log *log, struct dm_target *ti,
 	return 0;
 	return 0;
 }
 }
 
 
-static void disk_dtr(struct dirty_log *log)
+static void disk_dtr(struct dm_dirty_log *log)
 {
 {
 	struct log_c *lc = (struct log_c *) log->context;
 	struct log_c *lc = (struct log_c *) log->context;
 
 
@@ -514,7 +586,7 @@ static void fail_log_device(struct log_c *lc)
 	dm_table_event(lc->ti->table);
 	dm_table_event(lc->ti->table);
 }
 }
 
 
-static int disk_resume(struct dirty_log *log)
+static int disk_resume(struct dm_dirty_log *log)
 {
 {
 	int r;
 	int r;
 	unsigned i;
 	unsigned i;
@@ -524,7 +596,7 @@ static int disk_resume(struct dirty_log *log)
 	/* read the disk header */
 	/* read the disk header */
 	r = read_header(lc);
 	r = read_header(lc);
 	if (r) {
 	if (r) {
-		DMWARN("%s: Failed to read header on mirror log device",
+		DMWARN("%s: Failed to read header on dirty region log device",
 		       lc->log_dev->name);
 		       lc->log_dev->name);
 		fail_log_device(lc);
 		fail_log_device(lc);
 		/*
 		/*
@@ -562,7 +634,7 @@ static int disk_resume(struct dirty_log *log)
 	/* write the new header */
 	/* write the new header */
 	r = write_header(lc);
 	r = write_header(lc);
 	if (r) {
 	if (r) {
-		DMWARN("%s: Failed to write header on mirror log device",
+		DMWARN("%s: Failed to write header on dirty region log device",
 		       lc->log_dev->name);
 		       lc->log_dev->name);
 		fail_log_device(lc);
 		fail_log_device(lc);
 	}
 	}
@@ -570,38 +642,38 @@ static int disk_resume(struct dirty_log *log)
 	return r;
 	return r;
 }
 }
 
 
-static uint32_t core_get_region_size(struct dirty_log *log)
+static uint32_t core_get_region_size(struct dm_dirty_log *log)
 {
 {
 	struct log_c *lc = (struct log_c *) log->context;
 	struct log_c *lc = (struct log_c *) log->context;
 	return lc->region_size;
 	return lc->region_size;
 }
 }
 
 
-static int core_resume(struct dirty_log *log)
+static int core_resume(struct dm_dirty_log *log)
 {
 {
 	struct log_c *lc = (struct log_c *) log->context;
 	struct log_c *lc = (struct log_c *) log->context;
 	lc->sync_search = 0;
 	lc->sync_search = 0;
 	return 0;
 	return 0;
 }
 }
 
 
-static int core_is_clean(struct dirty_log *log, region_t region)
+static int core_is_clean(struct dm_dirty_log *log, region_t region)
 {
 {
 	struct log_c *lc = (struct log_c *) log->context;
 	struct log_c *lc = (struct log_c *) log->context;
 	return log_test_bit(lc->clean_bits, region);
 	return log_test_bit(lc->clean_bits, region);
 }
 }
 
 
-static int core_in_sync(struct dirty_log *log, region_t region, int block)
+static int core_in_sync(struct dm_dirty_log *log, region_t region, int block)
 {
 {
 	struct log_c *lc = (struct log_c *) log->context;
 	struct log_c *lc = (struct log_c *) log->context;
 	return log_test_bit(lc->sync_bits, region);
 	return log_test_bit(lc->sync_bits, region);
 }
 }
 
 
-static int core_flush(struct dirty_log *log)
+static int core_flush(struct dm_dirty_log *log)
 {
 {
 	/* no op */
 	/* no op */
 	return 0;
 	return 0;
 }
 }
 
 
-static int disk_flush(struct dirty_log *log)
+static int disk_flush(struct dm_dirty_log *log)
 {
 {
 	int r;
 	int r;
 	struct log_c *lc = (struct log_c *) log->context;
 	struct log_c *lc = (struct log_c *) log->context;
@@ -619,19 +691,19 @@ static int disk_flush(struct dirty_log *log)
 	return r;
 	return r;
 }
 }
 
 
-static void core_mark_region(struct dirty_log *log, region_t region)
+static void core_mark_region(struct dm_dirty_log *log, region_t region)
 {
 {
 	struct log_c *lc = (struct log_c *) log->context;
 	struct log_c *lc = (struct log_c *) log->context;
 	log_clear_bit(lc, lc->clean_bits, region);
 	log_clear_bit(lc, lc->clean_bits, region);
 }
 }
 
 
-static void core_clear_region(struct dirty_log *log, region_t region)
+static void core_clear_region(struct dm_dirty_log *log, region_t region)
 {
 {
 	struct log_c *lc = (struct log_c *) log->context;
 	struct log_c *lc = (struct log_c *) log->context;
 	log_set_bit(lc, lc->clean_bits, region);
 	log_set_bit(lc, lc->clean_bits, region);
 }
 }
 
 
-static int core_get_resync_work(struct dirty_log *log, region_t *region)
+static int core_get_resync_work(struct dm_dirty_log *log, region_t *region)
 {
 {
 	struct log_c *lc = (struct log_c *) log->context;
 	struct log_c *lc = (struct log_c *) log->context;
 
 
@@ -654,7 +726,7 @@ static int core_get_resync_work(struct dirty_log *log, region_t *region)
 	return 1;
 	return 1;
 }
 }
 
 
-static void core_set_region_sync(struct dirty_log *log, region_t region,
+static void core_set_region_sync(struct dm_dirty_log *log, region_t region,
 				 int in_sync)
 				 int in_sync)
 {
 {
 	struct log_c *lc = (struct log_c *) log->context;
 	struct log_c *lc = (struct log_c *) log->context;
@@ -669,7 +741,7 @@ static void core_set_region_sync(struct dirty_log *log, region_t region,
 	}
 	}
 }
 }
 
 
-static region_t core_get_sync_count(struct dirty_log *log)
+static region_t core_get_sync_count(struct dm_dirty_log *log)
 {
 {
         struct log_c *lc = (struct log_c *) log->context;
         struct log_c *lc = (struct log_c *) log->context;
 
 
@@ -680,7 +752,7 @@ static region_t core_get_sync_count(struct dirty_log *log)
 	if (lc->sync != DEFAULTSYNC) \
 	if (lc->sync != DEFAULTSYNC) \
 		DMEMIT("%ssync ", lc->sync == NOSYNC ? "no" : "")
 		DMEMIT("%ssync ", lc->sync == NOSYNC ? "no" : "")
 
 
-static int core_status(struct dirty_log *log, status_type_t status,
+static int core_status(struct dm_dirty_log *log, status_type_t status,
 		       char *result, unsigned int maxlen)
 		       char *result, unsigned int maxlen)
 {
 {
 	int sz = 0;
 	int sz = 0;
@@ -700,7 +772,7 @@ static int core_status(struct dirty_log *log, status_type_t status,
 	return sz;
 	return sz;
 }
 }
 
 
-static int disk_status(struct dirty_log *log, status_type_t status,
+static int disk_status(struct dm_dirty_log *log, status_type_t status,
 		       char *result, unsigned int maxlen)
 		       char *result, unsigned int maxlen)
 {
 {
 	int sz = 0;
 	int sz = 0;
@@ -722,7 +794,7 @@ static int disk_status(struct dirty_log *log, status_type_t status,
 	return sz;
 	return sz;
 }
 }
 
 
-static struct dirty_log_type _core_type = {
+static struct dm_dirty_log_type _core_type = {
 	.name = "core",
 	.name = "core",
 	.module = THIS_MODULE,
 	.module = THIS_MODULE,
 	.ctr = core_ctr,
 	.ctr = core_ctr,
@@ -740,7 +812,7 @@ static struct dirty_log_type _core_type = {
 	.status = core_status,
 	.status = core_status,
 };
 };
 
 
-static struct dirty_log_type _disk_type = {
+static struct dm_dirty_log_type _disk_type = {
 	.name = "disk",
 	.name = "disk",
 	.module = THIS_MODULE,
 	.module = THIS_MODULE,
 	.ctr = disk_ctr,
 	.ctr = disk_ctr,
@@ -763,26 +835,28 @@ int __init dm_dirty_log_init(void)
 {
 {
 	int r;
 	int r;
 
 
-	r = dm_register_dirty_log_type(&_core_type);
+	r = dm_dirty_log_type_register(&_core_type);
 	if (r)
 	if (r)
 		DMWARN("couldn't register core log");
 		DMWARN("couldn't register core log");
 
 
-	r = dm_register_dirty_log_type(&_disk_type);
+	r = dm_dirty_log_type_register(&_disk_type);
 	if (r) {
 	if (r) {
 		DMWARN("couldn't register disk type");
 		DMWARN("couldn't register disk type");
-		dm_unregister_dirty_log_type(&_core_type);
+		dm_dirty_log_type_unregister(&_core_type);
 	}
 	}
 
 
 	return r;
 	return r;
 }
 }
 
 
-void dm_dirty_log_exit(void)
+void __exit dm_dirty_log_exit(void)
 {
 {
-	dm_unregister_dirty_log_type(&_disk_type);
-	dm_unregister_dirty_log_type(&_core_type);
+	dm_dirty_log_type_unregister(&_disk_type);
+	dm_dirty_log_type_unregister(&_core_type);
 }
 }
 
 
-EXPORT_SYMBOL(dm_register_dirty_log_type);
-EXPORT_SYMBOL(dm_unregister_dirty_log_type);
-EXPORT_SYMBOL(dm_create_dirty_log);
-EXPORT_SYMBOL(dm_destroy_dirty_log);
+module_init(dm_dirty_log_init);
+module_exit(dm_dirty_log_exit);
+
+MODULE_DESCRIPTION(DM_NAME " dirty region log");
+MODULE_AUTHOR("Joe Thornber, Heinz Mauelshagen <dm-devel@redhat.com>");
+MODULE_LICENSE("GPL");

+ 66 - 66
drivers/md/dm-raid1.c

@@ -7,9 +7,6 @@
 #include "dm.h"
 #include "dm.h"
 #include "dm-bio-list.h"
 #include "dm-bio-list.h"
 #include "dm-bio-record.h"
 #include "dm-bio-record.h"
-#include "dm-io.h"
-#include "dm-log.h"
-#include "kcopyd.h"
 
 
 #include <linux/ctype.h>
 #include <linux/ctype.h>
 #include <linux/init.h>
 #include <linux/init.h>
@@ -22,6 +19,9 @@
 #include <linux/workqueue.h>
 #include <linux/workqueue.h>
 #include <linux/log2.h>
 #include <linux/log2.h>
 #include <linux/hardirq.h>
 #include <linux/hardirq.h>
+#include <linux/dm-io.h>
+#include <linux/dm-dirty-log.h>
+#include <linux/dm-kcopyd.h>
 
 
 #define DM_MSG_PREFIX "raid1"
 #define DM_MSG_PREFIX "raid1"
 #define DM_IO_PAGES 64
 #define DM_IO_PAGES 64
@@ -74,7 +74,7 @@ struct region_hash {
 	unsigned region_shift;
 	unsigned region_shift;
 
 
 	/* holds persistent region state */
 	/* holds persistent region state */
-	struct dirty_log *log;
+	struct dm_dirty_log *log;
 
 
 	/* hash table */
 	/* hash table */
 	rwlock_t hash_lock;
 	rwlock_t hash_lock;
@@ -133,7 +133,7 @@ struct mirror_set {
 	struct dm_target *ti;
 	struct dm_target *ti;
 	struct list_head list;
 	struct list_head list;
 	struct region_hash rh;
 	struct region_hash rh;
-	struct kcopyd_client *kcopyd_client;
+	struct dm_kcopyd_client *kcopyd_client;
 	uint64_t features;
 	uint64_t features;
 
 
 	spinlock_t lock;	/* protects the lists */
 	spinlock_t lock;	/* protects the lists */
@@ -154,6 +154,9 @@ struct mirror_set {
 
 
 	struct workqueue_struct *kmirrord_wq;
 	struct workqueue_struct *kmirrord_wq;
 	struct work_struct kmirrord_work;
 	struct work_struct kmirrord_work;
+	struct timer_list timer;
+	unsigned long timer_pending;
+
 	struct work_struct trigger_event;
 	struct work_struct trigger_event;
 
 
 	unsigned int nr_mirrors;
 	unsigned int nr_mirrors;
@@ -178,13 +181,32 @@ static void wake(struct mirror_set *ms)
 	queue_work(ms->kmirrord_wq, &ms->kmirrord_work);
 	queue_work(ms->kmirrord_wq, &ms->kmirrord_work);
 }
 }
 
 
+static void delayed_wake_fn(unsigned long data)
+{
+	struct mirror_set *ms = (struct mirror_set *) data;
+
+	clear_bit(0, &ms->timer_pending);
+	wake(ms);
+}
+
+static void delayed_wake(struct mirror_set *ms)
+{
+	if (test_and_set_bit(0, &ms->timer_pending))
+		return;
+
+	ms->timer.expires = jiffies + HZ / 5;
+	ms->timer.data = (unsigned long) ms;
+	ms->timer.function = delayed_wake_fn;
+	add_timer(&ms->timer);
+}
+
 /* FIXME move this */
 /* FIXME move this */
 static void queue_bio(struct mirror_set *ms, struct bio *bio, int rw);
 static void queue_bio(struct mirror_set *ms, struct bio *bio, int rw);
 
 
 #define MIN_REGIONS 64
 #define MIN_REGIONS 64
 #define MAX_RECOVERY 1
 #define MAX_RECOVERY 1
 static int rh_init(struct region_hash *rh, struct mirror_set *ms,
 static int rh_init(struct region_hash *rh, struct mirror_set *ms,
-		   struct dirty_log *log, uint32_t region_size,
+		   struct dm_dirty_log *log, uint32_t region_size,
 		   region_t nr_regions)
 		   region_t nr_regions)
 {
 {
 	unsigned int nr_buckets, max_buckets;
 	unsigned int nr_buckets, max_buckets;
@@ -249,7 +271,7 @@ static void rh_exit(struct region_hash *rh)
 	}
 	}
 
 
 	if (rh->log)
 	if (rh->log)
-		dm_destroy_dirty_log(rh->log);
+		dm_dirty_log_destroy(rh->log);
 	if (rh->region_pool)
 	if (rh->region_pool)
 		mempool_destroy(rh->region_pool);
 		mempool_destroy(rh->region_pool);
 	vfree(rh->buckets);
 	vfree(rh->buckets);
@@ -405,24 +427,22 @@ static void rh_update_states(struct region_hash *rh)
 	write_lock_irq(&rh->hash_lock);
 	write_lock_irq(&rh->hash_lock);
 	spin_lock(&rh->region_lock);
 	spin_lock(&rh->region_lock);
 	if (!list_empty(&rh->clean_regions)) {
 	if (!list_empty(&rh->clean_regions)) {
-		list_splice(&rh->clean_regions, &clean);
-		INIT_LIST_HEAD(&rh->clean_regions);
+		list_splice_init(&rh->clean_regions, &clean);
 
 
 		list_for_each_entry(reg, &clean, list)
 		list_for_each_entry(reg, &clean, list)
 			list_del(&reg->hash_list);
 			list_del(&reg->hash_list);
 	}
 	}
 
 
 	if (!list_empty(&rh->recovered_regions)) {
 	if (!list_empty(&rh->recovered_regions)) {
-		list_splice(&rh->recovered_regions, &recovered);
-		INIT_LIST_HEAD(&rh->recovered_regions);
+		list_splice_init(&rh->recovered_regions, &recovered);
 
 
 		list_for_each_entry (reg, &recovered, list)
 		list_for_each_entry (reg, &recovered, list)
 			list_del(&reg->hash_list);
 			list_del(&reg->hash_list);
 	}
 	}
 
 
 	if (!list_empty(&rh->failed_recovered_regions)) {
 	if (!list_empty(&rh->failed_recovered_regions)) {
-		list_splice(&rh->failed_recovered_regions, &failed_recovered);
-		INIT_LIST_HEAD(&rh->failed_recovered_regions);
+		list_splice_init(&rh->failed_recovered_regions,
+				 &failed_recovered);
 
 
 		list_for_each_entry(reg, &failed_recovered, list)
 		list_for_each_entry(reg, &failed_recovered, list)
 			list_del(&reg->hash_list);
 			list_del(&reg->hash_list);
@@ -790,7 +810,7 @@ static int recover(struct mirror_set *ms, struct region *reg)
 {
 {
 	int r;
 	int r;
 	unsigned int i;
 	unsigned int i;
-	struct io_region from, to[KCOPYD_MAX_REGIONS], *dest;
+	struct dm_io_region from, to[DM_KCOPYD_MAX_REGIONS], *dest;
 	struct mirror *m;
 	struct mirror *m;
 	unsigned long flags = 0;
 	unsigned long flags = 0;
 
 
@@ -822,9 +842,9 @@ static int recover(struct mirror_set *ms, struct region *reg)
 	}
 	}
 
 
 	/* hand to kcopyd */
 	/* hand to kcopyd */
-	set_bit(KCOPYD_IGNORE_ERROR, &flags);
-	r = kcopyd_copy(ms->kcopyd_client, &from, ms->nr_mirrors - 1, to, flags,
-			recovery_complete, reg);
+	set_bit(DM_KCOPYD_IGNORE_ERROR, &flags);
+	r = dm_kcopyd_copy(ms->kcopyd_client, &from, ms->nr_mirrors - 1, to,
+			   flags, recovery_complete, reg);
 
 
 	return r;
 	return r;
 }
 }
@@ -833,7 +853,7 @@ static void do_recovery(struct mirror_set *ms)
 {
 {
 	int r;
 	int r;
 	struct region *reg;
 	struct region *reg;
-	struct dirty_log *log = ms->rh.log;
+	struct dm_dirty_log *log = ms->rh.log;
 
 
 	/*
 	/*
 	 * Start quiescing some regions.
 	 * Start quiescing some regions.
@@ -909,7 +929,7 @@ static void map_bio(struct mirror *m, struct bio *bio)
 	bio->bi_sector = map_sector(m, bio);
 	bio->bi_sector = map_sector(m, bio);
 }
 }
 
 
-static void map_region(struct io_region *io, struct mirror *m,
+static void map_region(struct dm_io_region *io, struct mirror *m,
 		       struct bio *bio)
 		       struct bio *bio)
 {
 {
 	io->bdev = m->dev->bdev;
 	io->bdev = m->dev->bdev;
@@ -951,7 +971,7 @@ static void read_callback(unsigned long error, void *context)
 /* Asynchronous read. */
 /* Asynchronous read. */
 static void read_async_bio(struct mirror *m, struct bio *bio)
 static void read_async_bio(struct mirror *m, struct bio *bio)
 {
 {
-	struct io_region io;
+	struct dm_io_region io;
 	struct dm_io_request io_req = {
 	struct dm_io_request io_req = {
 		.bi_rw = READ,
 		.bi_rw = READ,
 		.mem.type = DM_IO_BVEC,
 		.mem.type = DM_IO_BVEC,
@@ -1019,7 +1039,7 @@ static void __bio_mark_nosync(struct mirror_set *ms,
 {
 {
 	unsigned long flags;
 	unsigned long flags;
 	struct region_hash *rh = &ms->rh;
 	struct region_hash *rh = &ms->rh;
-	struct dirty_log *log = ms->rh.log;
+	struct dm_dirty_log *log = ms->rh.log;
 	struct region *reg;
 	struct region *reg;
 	region_t region = bio_to_region(rh, bio);
 	region_t region = bio_to_region(rh, bio);
 	int recovering = 0;
 	int recovering = 0;
@@ -1107,7 +1127,7 @@ out:
 static void do_write(struct mirror_set *ms, struct bio *bio)
 static void do_write(struct mirror_set *ms, struct bio *bio)
 {
 {
 	unsigned int i;
 	unsigned int i;
-	struct io_region io[ms->nr_mirrors], *dest = io;
+	struct dm_io_region io[ms->nr_mirrors], *dest = io;
 	struct mirror *m;
 	struct mirror *m;
 	struct dm_io_request io_req = {
 	struct dm_io_request io_req = {
 		.bi_rw = WRITE,
 		.bi_rw = WRITE,
@@ -1182,6 +1202,7 @@ static void do_writes(struct mirror_set *ms, struct bio_list *writes)
 		spin_lock_irq(&ms->lock);
 		spin_lock_irq(&ms->lock);
 		bio_list_merge(&ms->failures, &sync);
 		bio_list_merge(&ms->failures, &sync);
 		spin_unlock_irq(&ms->lock);
 		spin_unlock_irq(&ms->lock);
+		wake(ms);
 	} else
 	} else
 		while ((bio = bio_list_pop(&sync)))
 		while ((bio = bio_list_pop(&sync)))
 			do_write(ms, bio);
 			do_write(ms, bio);
@@ -1241,7 +1262,7 @@ static void do_failures(struct mirror_set *ms, struct bio_list *failures)
 	bio_list_merge(&ms->failures, failures);
 	bio_list_merge(&ms->failures, failures);
 	spin_unlock_irq(&ms->lock);
 	spin_unlock_irq(&ms->lock);
 
 
-	wake(ms);
+	delayed_wake(ms);
 }
 }
 
 
 static void trigger_event(struct work_struct *work)
 static void trigger_event(struct work_struct *work)
@@ -1255,7 +1276,7 @@ static void trigger_event(struct work_struct *work)
 /*-----------------------------------------------------------------
 /*-----------------------------------------------------------------
  * kmirrord
  * kmirrord
  *---------------------------------------------------------------*/
  *---------------------------------------------------------------*/
-static int _do_mirror(struct work_struct *work)
+static void do_mirror(struct work_struct *work)
 {
 {
 	struct mirror_set *ms =container_of(work, struct mirror_set,
 	struct mirror_set *ms =container_of(work, struct mirror_set,
 					    kmirrord_work);
 					    kmirrord_work);
@@ -1277,23 +1298,7 @@ static int _do_mirror(struct work_struct *work)
 	do_writes(ms, &writes);
 	do_writes(ms, &writes);
 	do_failures(ms, &failures);
 	do_failures(ms, &failures);
 
 
-	return (ms->failures.head) ? 1 : 0;
-}
-
-static void do_mirror(struct work_struct *work)
-{
-	/*
-	 * If _do_mirror returns 1, we give it
-	 * another shot.  This helps for cases like
-	 * 'suspend' where we call flush_workqueue
-	 * and expect all work to be finished.  If
-	 * a failure happens during a suspend, we
-	 * couldn't issue a 'wake' because it would
-	 * not be honored.  Therefore, we return '1'
-	 * from _do_mirror, and retry here.
-	 */
-	while (_do_mirror(work))
-		schedule();
+	dm_table_unplug_all(ms->ti->table);
 }
 }
 
 
 
 
@@ -1303,7 +1308,7 @@ static void do_mirror(struct work_struct *work)
 static struct mirror_set *alloc_context(unsigned int nr_mirrors,
 static struct mirror_set *alloc_context(unsigned int nr_mirrors,
 					uint32_t region_size,
 					uint32_t region_size,
 					struct dm_target *ti,
 					struct dm_target *ti,
-					struct dirty_log *dl)
+					struct dm_dirty_log *dl)
 {
 {
 	size_t len;
 	size_t len;
 	struct mirror_set *ms = NULL;
 	struct mirror_set *ms = NULL;
@@ -1403,12 +1408,12 @@ static int get_mirror(struct mirror_set *ms, struct dm_target *ti,
 /*
 /*
  * Create dirty log: log_type #log_params <log_params>
  * Create dirty log: log_type #log_params <log_params>
  */
  */
-static struct dirty_log *create_dirty_log(struct dm_target *ti,
+static struct dm_dirty_log *create_dirty_log(struct dm_target *ti,
 					  unsigned int argc, char **argv,
 					  unsigned int argc, char **argv,
 					  unsigned int *args_used)
 					  unsigned int *args_used)
 {
 {
 	unsigned int param_count;
 	unsigned int param_count;
-	struct dirty_log *dl;
+	struct dm_dirty_log *dl;
 
 
 	if (argc < 2) {
 	if (argc < 2) {
 		ti->error = "Insufficient mirror log arguments";
 		ti->error = "Insufficient mirror log arguments";
@@ -1427,7 +1432,7 @@ static struct dirty_log *create_dirty_log(struct dm_target *ti,
 		return NULL;
 		return NULL;
 	}
 	}
 
 
-	dl = dm_create_dirty_log(argv[0], ti, param_count, argv + 2);
+	dl = dm_dirty_log_create(argv[0], ti, param_count, argv + 2);
 	if (!dl) {
 	if (!dl) {
 		ti->error = "Error creating mirror dirty log";
 		ti->error = "Error creating mirror dirty log";
 		return NULL;
 		return NULL;
@@ -1435,7 +1440,7 @@ static struct dirty_log *create_dirty_log(struct dm_target *ti,
 
 
 	if (!_check_region_size(ti, dl->type->get_region_size(dl))) {
 	if (!_check_region_size(ti, dl->type->get_region_size(dl))) {
 		ti->error = "Invalid region size";
 		ti->error = "Invalid region size";
-		dm_destroy_dirty_log(dl);
+		dm_dirty_log_destroy(dl);
 		return NULL;
 		return NULL;
 	}
 	}
 
 
@@ -1496,7 +1501,7 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 	int r;
 	int r;
 	unsigned int nr_mirrors, m, args_used;
 	unsigned int nr_mirrors, m, args_used;
 	struct mirror_set *ms;
 	struct mirror_set *ms;
-	struct dirty_log *dl;
+	struct dm_dirty_log *dl;
 
 
 	dl = create_dirty_log(ti, argc, argv, &args_used);
 	dl = create_dirty_log(ti, argc, argv, &args_used);
 	if (!dl)
 	if (!dl)
@@ -1506,9 +1511,9 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 	argc -= args_used;
 	argc -= args_used;
 
 
 	if (!argc || sscanf(argv[0], "%u", &nr_mirrors) != 1 ||
 	if (!argc || sscanf(argv[0], "%u", &nr_mirrors) != 1 ||
-	    nr_mirrors < 2 || nr_mirrors > KCOPYD_MAX_REGIONS + 1) {
+	    nr_mirrors < 2 || nr_mirrors > DM_KCOPYD_MAX_REGIONS + 1) {
 		ti->error = "Invalid number of mirrors";
 		ti->error = "Invalid number of mirrors";
-		dm_destroy_dirty_log(dl);
+		dm_dirty_log_destroy(dl);
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
@@ -1516,13 +1521,13 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 
 
 	if (argc < nr_mirrors * 2) {
 	if (argc < nr_mirrors * 2) {
 		ti->error = "Too few mirror arguments";
 		ti->error = "Too few mirror arguments";
-		dm_destroy_dirty_log(dl);
+		dm_dirty_log_destroy(dl);
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
 	ms = alloc_context(nr_mirrors, dl->type->get_region_size(dl), ti, dl);
 	ms = alloc_context(nr_mirrors, dl->type->get_region_size(dl), ti, dl);
 	if (!ms) {
 	if (!ms) {
-		dm_destroy_dirty_log(dl);
+		dm_dirty_log_destroy(dl);
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
@@ -1547,6 +1552,8 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 		goto err_free_context;
 		goto err_free_context;
 	}
 	}
 	INIT_WORK(&ms->kmirrord_work, do_mirror);
 	INIT_WORK(&ms->kmirrord_work, do_mirror);
+	init_timer(&ms->timer);
+	ms->timer_pending = 0;
 	INIT_WORK(&ms->trigger_event, trigger_event);
 	INIT_WORK(&ms->trigger_event, trigger_event);
 
 
 	r = parse_features(ms, argc, argv, &args_used);
 	r = parse_features(ms, argc, argv, &args_used);
@@ -1571,7 +1578,7 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 		goto err_destroy_wq;
 		goto err_destroy_wq;
 	}
 	}
 
 
-	r = kcopyd_client_create(DM_IO_PAGES, &ms->kcopyd_client);
+	r = dm_kcopyd_client_create(DM_IO_PAGES, &ms->kcopyd_client);
 	if (r)
 	if (r)
 		goto err_destroy_wq;
 		goto err_destroy_wq;
 
 
@@ -1589,8 +1596,9 @@ static void mirror_dtr(struct dm_target *ti)
 {
 {
 	struct mirror_set *ms = (struct mirror_set *) ti->private;
 	struct mirror_set *ms = (struct mirror_set *) ti->private;
 
 
+	del_timer_sync(&ms->timer);
 	flush_workqueue(ms->kmirrord_wq);
 	flush_workqueue(ms->kmirrord_wq);
-	kcopyd_client_destroy(ms->kcopyd_client);
+	dm_kcopyd_client_destroy(ms->kcopyd_client);
 	destroy_workqueue(ms->kmirrord_wq);
 	destroy_workqueue(ms->kmirrord_wq);
 	free_context(ms, ti, ms->nr_mirrors);
 	free_context(ms, ti, ms->nr_mirrors);
 }
 }
@@ -1734,7 +1742,7 @@ out:
 static void mirror_presuspend(struct dm_target *ti)
 static void mirror_presuspend(struct dm_target *ti)
 {
 {
 	struct mirror_set *ms = (struct mirror_set *) ti->private;
 	struct mirror_set *ms = (struct mirror_set *) ti->private;
-	struct dirty_log *log = ms->rh.log;
+	struct dm_dirty_log *log = ms->rh.log;
 
 
 	atomic_set(&ms->suspend, 1);
 	atomic_set(&ms->suspend, 1);
 
 
@@ -1763,7 +1771,7 @@ static void mirror_presuspend(struct dm_target *ti)
 static void mirror_postsuspend(struct dm_target *ti)
 static void mirror_postsuspend(struct dm_target *ti)
 {
 {
 	struct mirror_set *ms = ti->private;
 	struct mirror_set *ms = ti->private;
-	struct dirty_log *log = ms->rh.log;
+	struct dm_dirty_log *log = ms->rh.log;
 
 
 	if (log->type->postsuspend && log->type->postsuspend(log))
 	if (log->type->postsuspend && log->type->postsuspend(log))
 		/* FIXME: need better error handling */
 		/* FIXME: need better error handling */
@@ -1773,7 +1781,7 @@ static void mirror_postsuspend(struct dm_target *ti)
 static void mirror_resume(struct dm_target *ti)
 static void mirror_resume(struct dm_target *ti)
 {
 {
 	struct mirror_set *ms = ti->private;
 	struct mirror_set *ms = ti->private;
-	struct dirty_log *log = ms->rh.log;
+	struct dm_dirty_log *log = ms->rh.log;
 
 
 	atomic_set(&ms->suspend, 0);
 	atomic_set(&ms->suspend, 0);
 	if (log->type->resume && log->type->resume(log))
 	if (log->type->resume && log->type->resume(log))
@@ -1811,7 +1819,7 @@ static int mirror_status(struct dm_target *ti, status_type_t type,
 {
 {
 	unsigned int m, sz = 0;
 	unsigned int m, sz = 0;
 	struct mirror_set *ms = (struct mirror_set *) ti->private;
 	struct mirror_set *ms = (struct mirror_set *) ti->private;
-	struct dirty_log *log = ms->rh.log;
+	struct dm_dirty_log *log = ms->rh.log;
 	char buffer[ms->nr_mirrors + 1];
 	char buffer[ms->nr_mirrors + 1];
 
 
 	switch (type) {
 	switch (type) {
@@ -1864,15 +1872,9 @@ static int __init dm_mirror_init(void)
 {
 {
 	int r;
 	int r;
 
 
-	r = dm_dirty_log_init();
-	if (r)
-		return r;
-
 	r = dm_register_target(&mirror_target);
 	r = dm_register_target(&mirror_target);
-	if (r < 0) {
+	if (r < 0)
 		DMERR("Failed to register mirror target");
 		DMERR("Failed to register mirror target");
-		dm_dirty_log_exit();
-	}
 
 
 	return r;
 	return r;
 }
 }
@@ -1884,8 +1886,6 @@ static void __exit dm_mirror_exit(void)
 	r = dm_unregister_target(&mirror_target);
 	r = dm_unregister_target(&mirror_target);
 	if (r < 0)
 	if (r < 0)
 		DMERR("unregister failed %d", r);
 		DMERR("unregister failed %d", r);
-
-	dm_dirty_log_exit();
 }
 }
 
 
 /* Module hooks */
 /* Module hooks */

+ 11 - 11
drivers/md/dm-snap.c

@@ -18,10 +18,10 @@
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <linux/vmalloc.h>
 #include <linux/log2.h>
 #include <linux/log2.h>
+#include <linux/dm-kcopyd.h>
 
 
 #include "dm-snap.h"
 #include "dm-snap.h"
 #include "dm-bio-list.h"
 #include "dm-bio-list.h"
-#include "kcopyd.h"
 
 
 #define DM_MSG_PREFIX "snapshots"
 #define DM_MSG_PREFIX "snapshots"
 
 
@@ -36,9 +36,9 @@
 #define SNAPSHOT_COPY_PRIORITY 2
 #define SNAPSHOT_COPY_PRIORITY 2
 
 
 /*
 /*
- * Each snapshot reserves this many pages for io
+ * Reserve 1MB for each snapshot initially (with minimum of 1 page).
  */
  */
-#define SNAPSHOT_PAGES 256
+#define SNAPSHOT_PAGES (((1UL << 20) >> PAGE_SHIFT) ? : 1)
 
 
 static struct workqueue_struct *ksnapd;
 static struct workqueue_struct *ksnapd;
 static void flush_queued_bios(struct work_struct *work);
 static void flush_queued_bios(struct work_struct *work);
@@ -536,7 +536,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 	s->last_percent = 0;
 	s->last_percent = 0;
 	init_rwsem(&s->lock);
 	init_rwsem(&s->lock);
 	spin_lock_init(&s->pe_lock);
 	spin_lock_init(&s->pe_lock);
-	s->table = ti->table;
+	s->ti = ti;
 
 
 	/* Allocate hash table for COW data */
 	/* Allocate hash table for COW data */
 	if (init_hash_tables(s)) {
 	if (init_hash_tables(s)) {
@@ -558,7 +558,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 		goto bad4;
 		goto bad4;
 	}
 	}
 
 
-	r = kcopyd_client_create(SNAPSHOT_PAGES, &s->kcopyd_client);
+	r = dm_kcopyd_client_create(SNAPSHOT_PAGES, &s->kcopyd_client);
 	if (r) {
 	if (r) {
 		ti->error = "Could not create kcopyd client";
 		ti->error = "Could not create kcopyd client";
 		goto bad5;
 		goto bad5;
@@ -591,7 +591,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 	return 0;
 	return 0;
 
 
  bad6:
  bad6:
-	kcopyd_client_destroy(s->kcopyd_client);
+	dm_kcopyd_client_destroy(s->kcopyd_client);
 
 
  bad5:
  bad5:
 	s->store.destroy(&s->store);
 	s->store.destroy(&s->store);
@@ -613,7 +613,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 
 
 static void __free_exceptions(struct dm_snapshot *s)
 static void __free_exceptions(struct dm_snapshot *s)
 {
 {
-	kcopyd_client_destroy(s->kcopyd_client);
+	dm_kcopyd_client_destroy(s->kcopyd_client);
 	s->kcopyd_client = NULL;
 	s->kcopyd_client = NULL;
 
 
 	exit_exception_table(&s->pending, pending_cache);
 	exit_exception_table(&s->pending, pending_cache);
@@ -699,7 +699,7 @@ static void __invalidate_snapshot(struct dm_snapshot *s, int err)
 
 
 	s->valid = 0;
 	s->valid = 0;
 
 
-	dm_table_event(s->table);
+	dm_table_event(s->ti->table);
 }
 }
 
 
 static void get_pending_exception(struct dm_snap_pending_exception *pe)
 static void get_pending_exception(struct dm_snap_pending_exception *pe)
@@ -824,7 +824,7 @@ static void copy_callback(int read_err, unsigned long write_err, void *context)
 static void start_copy(struct dm_snap_pending_exception *pe)
 static void start_copy(struct dm_snap_pending_exception *pe)
 {
 {
 	struct dm_snapshot *s = pe->snap;
 	struct dm_snapshot *s = pe->snap;
-	struct io_region src, dest;
+	struct dm_io_region src, dest;
 	struct block_device *bdev = s->origin->bdev;
 	struct block_device *bdev = s->origin->bdev;
 	sector_t dev_size;
 	sector_t dev_size;
 
 
@@ -839,7 +839,7 @@ static void start_copy(struct dm_snap_pending_exception *pe)
 	dest.count = src.count;
 	dest.count = src.count;
 
 
 	/* Hand over to kcopyd */
 	/* Hand over to kcopyd */
-	kcopyd_copy(s->kcopyd_client,
+	dm_kcopyd_copy(s->kcopyd_client,
 		    &src, 1, &dest, 0, copy_callback, pe);
 		    &src, 1, &dest, 0, copy_callback, pe);
 }
 }
 
 
@@ -1060,7 +1060,7 @@ static int __origin_write(struct list_head *snapshots, struct bio *bio)
 			goto next_snapshot;
 			goto next_snapshot;
 
 
 		/* Nothing to do if writing beyond end of snapshot */
 		/* Nothing to do if writing beyond end of snapshot */
-		if (bio->bi_sector >= dm_table_get_size(snap->table))
+		if (bio->bi_sector >= dm_table_get_size(snap->ti->table))
 			goto next_snapshot;
 			goto next_snapshot;
 
 
 		/*
 		/*

+ 2 - 2
drivers/md/dm-snap.h

@@ -132,7 +132,7 @@ struct exception_store {
 
 
 struct dm_snapshot {
 struct dm_snapshot {
 	struct rw_semaphore lock;
 	struct rw_semaphore lock;
-	struct dm_table *table;
+	struct dm_target *ti;
 
 
 	struct dm_dev *origin;
 	struct dm_dev *origin;
 	struct dm_dev *cow;
 	struct dm_dev *cow;
@@ -169,7 +169,7 @@ struct dm_snapshot {
 	/* The on disk metadata handler */
 	/* The on disk metadata handler */
 	struct exception_store store;
 	struct exception_store store;
 
 
-	struct kcopyd_client *kcopyd_client;
+	struct dm_kcopyd_client *kcopyd_client;
 
 
 	/* Queue of snapshot writes for ksnapd to flush */
 	/* Queue of snapshot writes for ksnapd to flush */
 	struct bio_list queued_bios;
 	struct bio_list queued_bios;

+ 2 - 40
drivers/md/dm-table.c

@@ -245,44 +245,6 @@ int dm_table_create(struct dm_table **result, int mode,
 	return 0;
 	return 0;
 }
 }
 
 
-int dm_create_error_table(struct dm_table **result, struct mapped_device *md)
-{
-	struct dm_table *t;
-	sector_t dev_size = 1;
-	int r;
-
-	/*
-	 * Find current size of device.
-	 * Default to 1 sector if inactive.
-	 */
-	t = dm_get_table(md);
-	if (t) {
-		dev_size = dm_table_get_size(t);
-		dm_table_put(t);
-	}
-
-	r = dm_table_create(&t, FMODE_READ, 1, md);
-	if (r)
-		return r;
-
-	r = dm_table_add_target(t, "error", 0, dev_size, NULL);
-	if (r)
-		goto out;
-
-	r = dm_table_complete(t);
-	if (r)
-		goto out;
-
-	*result = t;
-
-out:
-	if (r)
-		dm_table_put(t);
-
-	return r;
-}
-EXPORT_SYMBOL_GPL(dm_create_error_table);
-
 static void free_devices(struct list_head *devices)
 static void free_devices(struct list_head *devices)
 {
 {
 	struct list_head *tmp, *next;
 	struct list_head *tmp, *next;
@@ -954,7 +916,7 @@ void dm_table_presuspend_targets(struct dm_table *t)
 	if (!t)
 	if (!t)
 		return;
 		return;
 
 
-	return suspend_targets(t, 0);
+	suspend_targets(t, 0);
 }
 }
 
 
 void dm_table_postsuspend_targets(struct dm_table *t)
 void dm_table_postsuspend_targets(struct dm_table *t)
@@ -962,7 +924,7 @@ void dm_table_postsuspend_targets(struct dm_table *t)
 	if (!t)
 	if (!t)
 		return;
 		return;
 
 
-	return suspend_targets(t, 1);
+	suspend_targets(t, 1);
 }
 }
 
 
 int dm_table_resume_targets(struct dm_table *t)
 int dm_table_resume_targets(struct dm_table *t)

+ 8 - 8
drivers/md/dm.c

@@ -204,6 +204,7 @@ static int (*_inits[])(void) __initdata = {
 	dm_target_init,
 	dm_target_init,
 	dm_linear_init,
 	dm_linear_init,
 	dm_stripe_init,
 	dm_stripe_init,
+	dm_kcopyd_init,
 	dm_interface_init,
 	dm_interface_init,
 };
 };
 
 
@@ -212,6 +213,7 @@ static void (*_exits[])(void) = {
 	dm_target_exit,
 	dm_target_exit,
 	dm_linear_exit,
 	dm_linear_exit,
 	dm_stripe_exit,
 	dm_stripe_exit,
+	dm_kcopyd_exit,
 	dm_interface_exit,
 	dm_interface_exit,
 };
 };
 
 
@@ -922,7 +924,7 @@ static void free_minor(int minor)
 /*
 /*
  * See if the device with a specific minor # is free.
  * See if the device with a specific minor # is free.
  */
  */
-static int specific_minor(struct mapped_device *md, int minor)
+static int specific_minor(int minor)
 {
 {
 	int r, m;
 	int r, m;
 
 
@@ -955,7 +957,7 @@ out:
 	return r;
 	return r;
 }
 }
 
 
-static int next_free_minor(struct mapped_device *md, int *minor)
+static int next_free_minor(int *minor)
 {
 {
 	int r, m;
 	int r, m;
 
 
@@ -966,9 +968,8 @@ static int next_free_minor(struct mapped_device *md, int *minor)
 	spin_lock(&_minor_lock);
 	spin_lock(&_minor_lock);
 
 
 	r = idr_get_new(&_minor_idr, MINOR_ALLOCED, &m);
 	r = idr_get_new(&_minor_idr, MINOR_ALLOCED, &m);
-	if (r) {
+	if (r)
 		goto out;
 		goto out;
-	}
 
 
 	if (m >= (1 << MINORBITS)) {
 	if (m >= (1 << MINORBITS)) {
 		idr_remove(&_minor_idr, m);
 		idr_remove(&_minor_idr, m);
@@ -991,7 +992,7 @@ static struct block_device_operations dm_blk_dops;
 static struct mapped_device *alloc_dev(int minor)
 static struct mapped_device *alloc_dev(int minor)
 {
 {
 	int r;
 	int r;
-	struct mapped_device *md = kmalloc(sizeof(*md), GFP_KERNEL);
+	struct mapped_device *md = kzalloc(sizeof(*md), GFP_KERNEL);
 	void *old_md;
 	void *old_md;
 
 
 	if (!md) {
 	if (!md) {
@@ -1004,13 +1005,12 @@ static struct mapped_device *alloc_dev(int minor)
 
 
 	/* get a minor number for the dev */
 	/* get a minor number for the dev */
 	if (minor == DM_ANY_MINOR)
 	if (minor == DM_ANY_MINOR)
-		r = next_free_minor(md, &minor);
+		r = next_free_minor(&minor);
 	else
 	else
-		r = specific_minor(md, minor);
+		r = specific_minor(minor);
 	if (r < 0)
 	if (r < 0)
 		goto bad_minor;
 		goto bad_minor;
 
 
-	memset(md, 0, sizeof(*md));
 	init_rwsem(&md->io_lock);
 	init_rwsem(&md->io_lock);
 	mutex_init(&md->suspend_lock);
 	mutex_init(&md->suspend_lock);
 	spin_lock_init(&md->pushback_lock);
 	spin_lock_init(&md->pushback_lock);

+ 9 - 89
drivers/md/dm.h

@@ -16,67 +16,6 @@
 #include <linux/blkdev.h>
 #include <linux/blkdev.h>
 #include <linux/hdreg.h>
 #include <linux/hdreg.h>
 
 
-#define DM_NAME "device-mapper"
-
-#define DMERR(f, arg...) \
-	printk(KERN_ERR DM_NAME ": " DM_MSG_PREFIX ": " f "\n", ## arg)
-#define DMERR_LIMIT(f, arg...) \
-	do { \
-		if (printk_ratelimit())	\
-			printk(KERN_ERR DM_NAME ": " DM_MSG_PREFIX ": " \
-			       f "\n", ## arg); \
-	} while (0)
-
-#define DMWARN(f, arg...) \
-	printk(KERN_WARNING DM_NAME ": " DM_MSG_PREFIX ": " f "\n", ## arg)
-#define DMWARN_LIMIT(f, arg...) \
-	do { \
-		if (printk_ratelimit())	\
-			printk(KERN_WARNING DM_NAME ": " DM_MSG_PREFIX ": " \
-			       f "\n", ## arg); \
-	} while (0)
-
-#define DMINFO(f, arg...) \
-	printk(KERN_INFO DM_NAME ": " DM_MSG_PREFIX ": " f "\n", ## arg)
-#define DMINFO_LIMIT(f, arg...) \
-	do { \
-		if (printk_ratelimit())	\
-			printk(KERN_INFO DM_NAME ": " DM_MSG_PREFIX ": " f \
-			       "\n", ## arg); \
-	} while (0)
-
-#ifdef CONFIG_DM_DEBUG
-#  define DMDEBUG(f, arg...) \
-	printk(KERN_DEBUG DM_NAME ": " DM_MSG_PREFIX " DEBUG: " f "\n", ## arg)
-#  define DMDEBUG_LIMIT(f, arg...) \
-	do { \
-		if (printk_ratelimit())	\
-			printk(KERN_DEBUG DM_NAME ": " DM_MSG_PREFIX ": " f \
-			       "\n", ## arg); \
-	} while (0)
-#else
-#  define DMDEBUG(f, arg...) do {} while (0)
-#  define DMDEBUG_LIMIT(f, arg...) do {} while (0)
-#endif
-
-#define DMEMIT(x...) sz += ((sz >= maxlen) ? \
-			  0 : scnprintf(result + sz, maxlen - sz, x))
-
-#define SECTOR_SHIFT 9
-
-/*
- * Definitions of return values from target end_io function.
- */
-#define DM_ENDIO_INCOMPLETE	1
-#define DM_ENDIO_REQUEUE	2
-
-/*
- * Definitions of return values from target map function.
- */
-#define DM_MAPIO_SUBMITTED	0
-#define DM_MAPIO_REMAPPED	1
-#define DM_MAPIO_REQUEUE	DM_ENDIO_REQUEUE
-
 /*
 /*
  * Suspend feature flags
  * Suspend feature flags
  */
  */
@@ -136,34 +75,6 @@ static inline int array_too_big(unsigned long fixed, unsigned long obj,
 	return (num > (ULONG_MAX - fixed) / obj);
 	return (num > (ULONG_MAX - fixed) / obj);
 }
 }
 
 
-/*
- * Ceiling(n / sz)
- */
-#define dm_div_up(n, sz) (((n) + (sz) - 1) / (sz))
-
-#define dm_sector_div_up(n, sz) ( \
-{ \
-	sector_t _r = ((n) + (sz) - 1); \
-	sector_div(_r, (sz)); \
-	_r; \
-} \
-)
-
-/*
- * ceiling(n / size) * size
- */
-#define dm_round_up(n, sz) (dm_div_up((n), (sz)) * (sz))
-
-static inline sector_t to_sector(unsigned long n)
-{
-	return (n >> 9);
-}
-
-static inline unsigned long to_bytes(sector_t n)
-{
-	return (n << 9);
-}
-
 int dm_split_args(int *argc, char ***argvp, char *input);
 int dm_split_args(int *argc, char ***argvp, char *input);
 
 
 /*
 /*
@@ -189,4 +100,13 @@ int dm_lock_for_deletion(struct mapped_device *md);
 
 
 void dm_kobject_uevent(struct mapped_device *md);
 void dm_kobject_uevent(struct mapped_device *md);
 
 
+/*
+ * Dirty log
+ */
+int dm_dirty_log_init(void);
+void dm_dirty_log_exit(void);
+
+int dm_kcopyd_init(void);
+void dm_kcopyd_exit(void);
+
 #endif
 #endif

+ 0 - 42
drivers/md/kcopyd.h

@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2001 Sistina Software
- *
- * This file is released under the GPL.
- *
- * Kcopyd provides a simple interface for copying an area of one
- * block-device to one or more other block-devices, with an asynchronous
- * completion notification.
- */
-
-#ifndef DM_KCOPYD_H
-#define DM_KCOPYD_H
-
-#include "dm-io.h"
-
-/* FIXME: make this configurable */
-#define KCOPYD_MAX_REGIONS 8
-
-#define KCOPYD_IGNORE_ERROR 1
-
-/*
- * To use kcopyd you must first create a kcopyd client object.
- */
-struct kcopyd_client;
-int kcopyd_client_create(unsigned int num_pages, struct kcopyd_client **result);
-void kcopyd_client_destroy(struct kcopyd_client *kc);
-
-/*
- * Submit a copy job to kcopyd.  This is built on top of the
- * previous three fns.
- *
- * read_err is a boolean,
- * write_err is a bitset, with 1 bit for each destination region
- */
-typedef void (*kcopyd_notify_fn)(int read_err, unsigned long write_err,
-				 void *context);
-
-int kcopyd_copy(struct kcopyd_client *kc, struct io_region *from,
-		unsigned int num_dests, struct io_region *dests,
-		unsigned int flags, kcopyd_notify_fn fn, void *context);
-
-#endif

+ 92 - 4
include/linux/device-mapper.h

@@ -1,6 +1,6 @@
 /*
 /*
  * Copyright (C) 2001 Sistina Software (UK) Limited.
  * Copyright (C) 2001 Sistina Software (UK) Limited.
- * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
  *
  *
  * This file is released under the LGPL.
  * This file is released under the LGPL.
  */
  */
@@ -10,6 +10,8 @@
 
 
 #ifdef __KERNEL__
 #ifdef __KERNEL__
 
 
+#include <linux/bio.h>
+
 struct dm_target;
 struct dm_target;
 struct dm_table;
 struct dm_table;
 struct dm_dev;
 struct dm_dev;
@@ -250,11 +252,97 @@ void dm_table_event(struct dm_table *t);
  */
  */
 int dm_swap_table(struct mapped_device *md, struct dm_table *t);
 int dm_swap_table(struct mapped_device *md, struct dm_table *t);
 
 
+/*-----------------------------------------------------------------
+ * Macros.
+ *---------------------------------------------------------------*/
+#define DM_NAME "device-mapper"
+
+#define DMERR(f, arg...) \
+	printk(KERN_ERR DM_NAME ": " DM_MSG_PREFIX ": " f "\n", ## arg)
+#define DMERR_LIMIT(f, arg...) \
+	do { \
+		if (printk_ratelimit())	\
+			printk(KERN_ERR DM_NAME ": " DM_MSG_PREFIX ": " \
+			       f "\n", ## arg); \
+	} while (0)
+
+#define DMWARN(f, arg...) \
+	printk(KERN_WARNING DM_NAME ": " DM_MSG_PREFIX ": " f "\n", ## arg)
+#define DMWARN_LIMIT(f, arg...) \
+	do { \
+		if (printk_ratelimit())	\
+			printk(KERN_WARNING DM_NAME ": " DM_MSG_PREFIX ": " \
+			       f "\n", ## arg); \
+	} while (0)
+
+#define DMINFO(f, arg...) \
+	printk(KERN_INFO DM_NAME ": " DM_MSG_PREFIX ": " f "\n", ## arg)
+#define DMINFO_LIMIT(f, arg...) \
+	do { \
+		if (printk_ratelimit())	\
+			printk(KERN_INFO DM_NAME ": " DM_MSG_PREFIX ": " f \
+			       "\n", ## arg); \
+	} while (0)
+
+#ifdef CONFIG_DM_DEBUG
+#  define DMDEBUG(f, arg...) \
+	printk(KERN_DEBUG DM_NAME ": " DM_MSG_PREFIX " DEBUG: " f "\n", ## arg)
+#  define DMDEBUG_LIMIT(f, arg...) \
+	do { \
+		if (printk_ratelimit())	\
+			printk(KERN_DEBUG DM_NAME ": " DM_MSG_PREFIX ": " f \
+			       "\n", ## arg); \
+	} while (0)
+#else
+#  define DMDEBUG(f, arg...) do {} while (0)
+#  define DMDEBUG_LIMIT(f, arg...) do {} while (0)
+#endif
+
+#define DMEMIT(x...) sz += ((sz >= maxlen) ? \
+			  0 : scnprintf(result + sz, maxlen - sz, x))
+
+#define SECTOR_SHIFT 9
+
+/*
+ * Definitions of return values from target end_io function.
+ */
+#define DM_ENDIO_INCOMPLETE	1
+#define DM_ENDIO_REQUEUE	2
+
+/*
+ * Definitions of return values from target map function.
+ */
+#define DM_MAPIO_SUBMITTED	0
+#define DM_MAPIO_REMAPPED	1
+#define DM_MAPIO_REQUEUE	DM_ENDIO_REQUEUE
+
+/*
+ * Ceiling(n / sz)
+ */
+#define dm_div_up(n, sz) (((n) + (sz) - 1) / (sz))
+
+#define dm_sector_div_up(n, sz) ( \
+{ \
+	sector_t _r = ((n) + (sz) - 1); \
+	sector_div(_r, (sz)); \
+	_r; \
+} \
+)
+
 /*
 /*
- * Prepare a table for a device that will error all I/O.
- * To make it active, call dm_suspend(), dm_swap_table() then dm_resume().
+ * ceiling(n / size) * size
  */
  */
-int dm_create_error_table(struct dm_table **result, struct mapped_device *md);
+#define dm_round_up(n, sz) (dm_div_up((n), (sz)) * (sz))
+
+static inline sector_t to_sector(unsigned long n)
+{
+	return (n >> SECTOR_SHIFT);
+}
+
+static inline unsigned long to_bytes(sector_t n)
+{
+	return (n << SECTOR_SHIFT);
+}
 
 
 #endif	/* __KERNEL__ */
 #endif	/* __KERNEL__ */
 #endif	/* _LINUX_DEVICE_MAPPER_H */
 #endif	/* _LINUX_DEVICE_MAPPER_H */

+ 42 - 42
drivers/md/dm-log.h → include/linux/dm-dirty-log.h

@@ -1,52 +1,56 @@
 /*
 /*
  * Copyright (C) 2003 Sistina Software
  * Copyright (C) 2003 Sistina Software
+ * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
+ *
+ * Device-Mapper dirty region log.
  *
  *
  * This file is released under the LGPL.
  * This file is released under the LGPL.
  */
  */
 
 
-#ifndef DM_DIRTY_LOG
-#define DM_DIRTY_LOG
+#ifndef _LINUX_DM_DIRTY_LOG
+#define _LINUX_DM_DIRTY_LOG
+
+#ifdef __KERNEL__
 
 
-#include "dm.h"
+#include <linux/types.h>
+#include <linux/device-mapper.h>
 
 
 typedef sector_t region_t;
 typedef sector_t region_t;
 
 
-struct dirty_log_type;
+struct dm_dirty_log_type;
 
 
-struct dirty_log {
-	struct dirty_log_type *type;
+struct dm_dirty_log {
+	struct dm_dirty_log_type *type;
 	void *context;
 	void *context;
 };
 };
 
 
-struct dirty_log_type {
-	struct list_head list;
+struct dm_dirty_log_type {
 	const char *name;
 	const char *name;
 	struct module *module;
 	struct module *module;
-	unsigned int use_count;
 
 
-	int (*ctr)(struct dirty_log *log, struct dm_target *ti,
-		   unsigned int argc, char **argv);
-	void (*dtr)(struct dirty_log *log);
+	int (*ctr)(struct dm_dirty_log *log, struct dm_target *ti,
+		   unsigned argc, char **argv);
+	void (*dtr)(struct dm_dirty_log *log);
 
 
 	/*
 	/*
 	 * There are times when we don't want the log to touch
 	 * There are times when we don't want the log to touch
 	 * the disk.
 	 * the disk.
 	 */
 	 */
-	int (*presuspend)(struct dirty_log *log);
-	int (*postsuspend)(struct dirty_log *log);
-	int (*resume)(struct dirty_log *log);
+	int (*presuspend)(struct dm_dirty_log *log);
+	int (*postsuspend)(struct dm_dirty_log *log);
+	int (*resume)(struct dm_dirty_log *log);
 
 
 	/*
 	/*
 	 * Retrieves the smallest size of region that the log can
 	 * Retrieves the smallest size of region that the log can
 	 * deal with.
 	 * deal with.
 	 */
 	 */
-	uint32_t (*get_region_size)(struct dirty_log *log);
+	uint32_t (*get_region_size)(struct dm_dirty_log *log);
 
 
-        /*
+	/*
 	 * A predicate to say whether a region is clean or not.
 	 * A predicate to say whether a region is clean or not.
 	 * May block.
 	 * May block.
 	 */
 	 */
-	int (*is_clean)(struct dirty_log *log, region_t region);
+	int (*is_clean)(struct dm_dirty_log *log, region_t region);
 
 
 	/*
 	/*
 	 *  Returns: 0, 1, -EWOULDBLOCK, < 0
 	 *  Returns: 0, 1, -EWOULDBLOCK, < 0
@@ -59,13 +63,14 @@ struct dirty_log_type {
 	 * passed to a daemon to deal with, since a daemon is
 	 * passed to a daemon to deal with, since a daemon is
 	 * allowed to block.
 	 * allowed to block.
 	 */
 	 */
-	int (*in_sync)(struct dirty_log *log, region_t region, int can_block);
+	int (*in_sync)(struct dm_dirty_log *log, region_t region,
+		       int can_block);
 
 
 	/*
 	/*
 	 * Flush the current log state (eg, to disk).  This
 	 * Flush the current log state (eg, to disk).  This
 	 * function may block.
 	 * function may block.
 	 */
 	 */
-	int (*flush)(struct dirty_log *log);
+	int (*flush)(struct dm_dirty_log *log);
 
 
 	/*
 	/*
 	 * Mark an area as clean or dirty.  These functions may
 	 * Mark an area as clean or dirty.  These functions may
@@ -73,8 +78,8 @@ struct dirty_log_type {
 	 * be extremely rare (eg, allocating another chunk of
 	 * be extremely rare (eg, allocating another chunk of
 	 * memory for some reason).
 	 * memory for some reason).
 	 */
 	 */
-	void (*mark_region)(struct dirty_log *log, region_t region);
-	void (*clear_region)(struct dirty_log *log, region_t region);
+	void (*mark_region)(struct dm_dirty_log *log, region_t region);
+	void (*clear_region)(struct dm_dirty_log *log, region_t region);
 
 
 	/*
 	/*
 	 * Returns: <0 (error), 0 (no region), 1 (region)
 	 * Returns: <0 (error), 0 (no region), 1 (region)
@@ -88,44 +93,39 @@ struct dirty_log_type {
 	 * tells you if an area is synchronised, the other
 	 * tells you if an area is synchronised, the other
 	 * assigns recovery work.
 	 * assigns recovery work.
 	*/
 	*/
-	int (*get_resync_work)(struct dirty_log *log, region_t *region);
+	int (*get_resync_work)(struct dm_dirty_log *log, region_t *region);
 
 
 	/*
 	/*
 	 * This notifies the log that the resync status of a region
 	 * This notifies the log that the resync status of a region
 	 * has changed.  It also clears the region from the recovering
 	 * has changed.  It also clears the region from the recovering
 	 * list (if present).
 	 * list (if present).
 	 */
 	 */
-	void (*set_region_sync)(struct dirty_log *log,
+	void (*set_region_sync)(struct dm_dirty_log *log,
 				region_t region, int in_sync);
 				region_t region, int in_sync);
 
 
-        /*
+	/*
 	 * Returns the number of regions that are in sync.
 	 * Returns the number of regions that are in sync.
-         */
-        region_t (*get_sync_count)(struct dirty_log *log);
+	 */
+	region_t (*get_sync_count)(struct dm_dirty_log *log);
 
 
 	/*
 	/*
 	 * Support function for mirror status requests.
 	 * Support function for mirror status requests.
 	 */
 	 */
-	int (*status)(struct dirty_log *log, status_type_t status_type,
-		      char *result, unsigned int maxlen);
+	int (*status)(struct dm_dirty_log *log, status_type_t status_type,
+		      char *result, unsigned maxlen);
 };
 };
 
 
-int dm_register_dirty_log_type(struct dirty_log_type *type);
-int dm_unregister_dirty_log_type(struct dirty_log_type *type);
-
+int dm_dirty_log_type_register(struct dm_dirty_log_type *type);
+int dm_dirty_log_type_unregister(struct dm_dirty_log_type *type);
 
 
 /*
 /*
  * Make sure you use these two functions, rather than calling
  * Make sure you use these two functions, rather than calling
  * type->constructor/destructor() directly.
  * type->constructor/destructor() directly.
  */
  */
-struct dirty_log *dm_create_dirty_log(const char *type_name, struct dm_target *ti,
-				      unsigned int argc, char **argv);
-void dm_destroy_dirty_log(struct dirty_log *log);
-
-/*
- * init/exit functions.
- */
-int dm_dirty_log_init(void);
-void dm_dirty_log_exit(void);
+struct dm_dirty_log *dm_dirty_log_create(const char *type_name,
+					 struct dm_target *ti,
+					 unsigned argc, char **argv);
+void dm_dirty_log_destroy(struct dm_dirty_log *log);
 
 
-#endif
+#endif	/* __KERNEL__ */
+#endif	/* _LINUX_DM_DIRTY_LOG_H */

+ 12 - 6
drivers/md/dm-io.h → include/linux/dm-io.h

@@ -1,15 +1,20 @@
 /*
 /*
  * Copyright (C) 2003 Sistina Software
  * Copyright (C) 2003 Sistina Software
+ * Copyright (C) 2004 - 2008 Red Hat, Inc. All rights reserved.
+ *
+ * Device-Mapper low-level I/O.
  *
  *
  * This file is released under the GPL.
  * This file is released under the GPL.
  */
  */
 
 
-#ifndef _DM_IO_H
-#define _DM_IO_H
+#ifndef _LINUX_DM_IO_H
+#define _LINUX_DM_IO_H
+
+#ifdef __KERNEL__
 
 
-#include "dm.h"
+#include <linux/types.h>
 
 
-struct io_region {
+struct dm_io_region {
 	struct block_device *bdev;
 	struct block_device *bdev;
 	sector_t sector;
 	sector_t sector;
 	sector_t count;		/* If this is zero the region is ignored. */
 	sector_t count;		/* If this is zero the region is ignored. */
@@ -74,6 +79,7 @@ void dm_io_client_destroy(struct dm_io_client *client);
  * error occurred doing io to the corresponding region.
  * error occurred doing io to the corresponding region.
  */
  */
 int dm_io(struct dm_io_request *io_req, unsigned num_regions,
 int dm_io(struct dm_io_request *io_req, unsigned num_regions,
-	  struct io_region *region, unsigned long *sync_error_bits);
+	  struct dm_io_region *region, unsigned long *sync_error_bits);
 
 
-#endif
+#endif	/* __KERNEL__ */
+#endif	/* _LINUX_DM_IO_H */

+ 47 - 0
include/linux/dm-kcopyd.h

@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2001 - 2003 Sistina Software
+ * Copyright (C) 2004 - 2008 Red Hat, Inc. All rights reserved.
+ *
+ * kcopyd provides a simple interface for copying an area of one
+ * block-device to one or more other block-devices, either synchronous
+ * or with an asynchronous completion notification.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef _LINUX_DM_KCOPYD_H
+#define _LINUX_DM_KCOPYD_H
+
+#ifdef __KERNEL__
+
+#include <linux/dm-io.h>
+
+/* FIXME: make this configurable */
+#define DM_KCOPYD_MAX_REGIONS 8
+
+#define DM_KCOPYD_IGNORE_ERROR 1
+
+/*
+ * To use kcopyd you must first create a dm_kcopyd_client object.
+ */
+struct dm_kcopyd_client;
+int dm_kcopyd_client_create(unsigned num_pages,
+			    struct dm_kcopyd_client **result);
+void dm_kcopyd_client_destroy(struct dm_kcopyd_client *kc);
+
+/*
+ * Submit a copy job to kcopyd.  This is built on top of the
+ * previous three fns.
+ *
+ * read_err is a boolean,
+ * write_err is a bitset, with 1 bit for each destination region
+ */
+typedef void (*dm_kcopyd_notify_fn)(int read_err, unsigned long write_err,
+				    void *context);
+
+int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from,
+		   unsigned num_dests, struct dm_io_region *dests,
+		   unsigned flags, dm_kcopyd_notify_fn fn, void *context);
+
+#endif	/* __KERNEL__ */
+#endif	/* _LINUX_DM_KCOPYD_H */