diff options
| author | Joe Thornber <thornber@redhat.com> | 2011-10-31 16:19:11 -0400 |
|---|---|---|
| committer | Alasdair G Kergon <agk@redhat.com> | 2011-10-31 16:19:11 -0400 |
| commit | 3241b1d3e0aaafbfcd320f4d71ade629728cc4f4 (patch) | |
| tree | 499461f724d4db3d7118641f4a20f5be23549edd /drivers/md/persistent-data | |
| parent | 95d402f057f2e208e4631893f6cd4a59c7c05e41 (diff) | |
dm: add persistent data library
The persistent-data library offers a re-usable framework for the storage
and management of on-disk metadata in device-mapper targets.
It's used by the thin-provisioning target in the next patch and in an
upcoming hierarchical storage target.
For further information, please read
Documentation/device-mapper/persistent-data.txt
Signed-off-by: Joe Thornber <thornber@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Diffstat (limited to 'drivers/md/persistent-data')
21 files changed, 5625 insertions, 0 deletions
diff --git a/drivers/md/persistent-data/Kconfig b/drivers/md/persistent-data/Kconfig new file mode 100644 index 000000000000..ceb359050a59 --- /dev/null +++ b/drivers/md/persistent-data/Kconfig | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | config DM_PERSISTENT_DATA | ||
| 2 | tristate | ||
| 3 | depends on BLK_DEV_DM && EXPERIMENTAL | ||
| 4 | select LIBCRC32C | ||
| 5 | select DM_BUFIO | ||
| 6 | ---help--- | ||
| 7 | Library providing immutable on-disk data structure support for | ||
| 8 | device-mapper targets such as the thin provisioning target. | ||
diff --git a/drivers/md/persistent-data/Makefile b/drivers/md/persistent-data/Makefile new file mode 100644 index 000000000000..cfa95f662230 --- /dev/null +++ b/drivers/md/persistent-data/Makefile | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | obj-$(CONFIG_DM_PERSISTENT_DATA) += dm-persistent-data.o | ||
| 2 | dm-persistent-data-objs := \ | ||
| 3 | dm-block-manager.o \ | ||
| 4 | dm-space-map-checker.o \ | ||
| 5 | dm-space-map-common.o \ | ||
| 6 | dm-space-map-disk.o \ | ||
| 7 | dm-space-map-metadata.o \ | ||
| 8 | dm-transaction-manager.o \ | ||
| 9 | dm-btree.o \ | ||
| 10 | dm-btree-remove.o \ | ||
| 11 | dm-btree-spine.o | ||
diff --git a/drivers/md/persistent-data/dm-block-manager.c b/drivers/md/persistent-data/dm-block-manager.c new file mode 100644 index 000000000000..0317ecdc6e53 --- /dev/null +++ b/drivers/md/persistent-data/dm-block-manager.c | |||
| @@ -0,0 +1,620 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2011 Red Hat, Inc. | ||
| 3 | * | ||
| 4 | * This file is released under the GPL. | ||
| 5 | */ | ||
| 6 | #include "dm-block-manager.h" | ||
| 7 | #include "dm-persistent-data-internal.h" | ||
| 8 | #include "../dm-bufio.h" | ||
| 9 | |||
| 10 | #include <linux/crc32c.h> | ||
| 11 | #include <linux/module.h> | ||
| 12 | #include <linux/slab.h> | ||
| 13 | #include <linux/rwsem.h> | ||
| 14 | #include <linux/device-mapper.h> | ||
| 15 | #include <linux/stacktrace.h> | ||
| 16 | |||
| 17 | #define DM_MSG_PREFIX "block manager" | ||
| 18 | |||
| 19 | /*----------------------------------------------------------------*/ | ||
| 20 | |||
| 21 | /* | ||
| 22 | * This is a read/write semaphore with a couple of differences. | ||
| 23 | * | ||
| 24 | * i) There is a restriction on the number of concurrent read locks that | ||
| 25 | * may be held at once. This is just an implementation detail. | ||
| 26 | * | ||
| 27 | * ii) Recursive locking attempts are detected and return EINVAL. A stack | ||
| 28 | * trace is also emitted for the previous lock aquisition. | ||
| 29 | * | ||
| 30 | * iii) Priority is given to write locks. | ||
| 31 | */ | ||
| 32 | #define MAX_HOLDERS 4 | ||
| 33 | #define MAX_STACK 10 | ||
| 34 | |||
| 35 | typedef unsigned long stack_entries[MAX_STACK]; | ||
| 36 | |||
| 37 | struct block_lock { | ||
| 38 | spinlock_t lock; | ||
| 39 | __s32 count; | ||
| 40 | struct list_head waiters; | ||
| 41 | struct task_struct *holders[MAX_HOLDERS]; | ||
| 42 | |||
| 43 | #ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING | ||
| 44 | struct stack_trace traces[MAX_HOLDERS]; | ||
| 45 | stack_entries entries[MAX_HOLDERS]; | ||
| 46 | #endif | ||
| 47 | }; | ||
| 48 | |||
| 49 | struct waiter { | ||
| 50 | struct list_head list; | ||
| 51 | struct task_struct *task; | ||
| 52 | int wants_write; | ||
| 53 | }; | ||
| 54 | |||
| 55 | static unsigned __find_holder(struct block_lock *lock, | ||
| 56 | struct task_struct *task) | ||
| 57 | { | ||
| 58 | unsigned i; | ||
| 59 | |||
| 60 | for (i = 0; i < MAX_HOLDERS; i++) | ||
| 61 | if (lock->holders[i] == task) | ||
| 62 | break; | ||
| 63 | |||
| 64 | BUG_ON(i == MAX_HOLDERS); | ||
| 65 | return i; | ||
| 66 | } | ||
| 67 | |||
| 68 | /* call this *after* you increment lock->count */ | ||
| 69 | static void __add_holder(struct block_lock *lock, struct task_struct *task) | ||
| 70 | { | ||
| 71 | unsigned h = __find_holder(lock, NULL); | ||
| 72 | #ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING | ||
| 73 | struct stack_trace *t; | ||
| 74 | #endif | ||
| 75 | |||
| 76 | get_task_struct(task); | ||
| 77 | lock->holders[h] = task; | ||
| 78 | |||
| 79 | #ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING | ||
| 80 | t = lock->traces + h; | ||
| 81 | t->nr_entries = 0; | ||
| 82 | t->max_entries = MAX_STACK; | ||
| 83 | t->entries = lock->entries[h]; | ||
| 84 | t->skip = 2; | ||
| 85 | save_stack_trace(t); | ||
| 86 | #endif | ||
| 87 | } | ||
| 88 | |||
| 89 | /* call this *before* you decrement lock->count */ | ||
| 90 | static void __del_holder(struct block_lock *lock, struct task_struct *task) | ||
| 91 | { | ||
| 92 | unsigned h = __find_holder(lock, task); | ||
| 93 | lock->holders[h] = NULL; | ||
| 94 | put_task_struct(task); | ||
| 95 | } | ||
| 96 | |||
| 97 | static int __check_holder(struct block_lock *lock) | ||
| 98 | { | ||
| 99 | unsigned i; | ||
| 100 | #ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING | ||
| 101 | static struct stack_trace t; | ||
| 102 | static stack_entries entries; | ||
| 103 | #endif | ||
| 104 | |||
| 105 | for (i = 0; i < MAX_HOLDERS; i++) { | ||
| 106 | if (lock->holders[i] == current) { | ||
| 107 | DMERR("recursive lock detected in pool metadata"); | ||
| 108 | #ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING | ||
| 109 | DMERR("previously held here:"); | ||
| 110 | print_stack_trace(lock->traces + i, 4); | ||
| 111 | |||
| 112 | DMERR("subsequent aquisition attempted here:"); | ||
| 113 | t.nr_entries = 0; | ||
| 114 | t.max_entries = MAX_STACK; | ||
| 115 | t.entries = entries; | ||
| 116 | t.skip = 3; | ||
| 117 | save_stack_trace(&t); | ||
| 118 | print_stack_trace(&t, 4); | ||
| 119 | #endif | ||
| 120 | return -EINVAL; | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | return 0; | ||
| 125 | } | ||
| 126 | |||
| 127 | static void __wait(struct waiter *w) | ||
| 128 | { | ||
| 129 | for (;;) { | ||
| 130 | set_task_state(current, TASK_UNINTERRUPTIBLE); | ||
| 131 | |||
| 132 | if (!w->task) | ||
| 133 | break; | ||
| 134 | |||
| 135 | schedule(); | ||
| 136 | } | ||
| 137 | |||
| 138 | set_task_state(current, TASK_RUNNING); | ||
| 139 | } | ||
| 140 | |||
| 141 | static void __wake_waiter(struct waiter *w) | ||
| 142 | { | ||
| 143 | struct task_struct *task; | ||
| 144 | |||
