diff options
Diffstat (limited to 'drivers/md/dm-exception-store.c')
-rw-r--r-- | drivers/md/dm-exception-store.c | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/drivers/md/dm-exception-store.c b/drivers/md/dm-exception-store.c index dccbfb0e010f..a2e26c242141 100644 --- a/drivers/md/dm-exception-store.c +++ b/drivers/md/dm-exception-store.c | |||
@@ -7,6 +7,7 @@ | |||
7 | 7 | ||
8 | #include "dm-exception-store.h" | 8 | #include "dm-exception-store.h" |
9 | 9 | ||
10 | #include <linux/ctype.h> | ||
10 | #include <linux/mm.h> | 11 | #include <linux/mm.h> |
11 | #include <linux/pagemap.h> | 12 | #include <linux/pagemap.h> |
12 | #include <linux/vmalloc.h> | 13 | #include <linux/vmalloc.h> |
@@ -14,6 +15,257 @@ | |||
14 | 15 | ||
15 | #define DM_MSG_PREFIX "snapshot exception stores" | 16 | #define DM_MSG_PREFIX "snapshot exception stores" |
16 | 17 | ||
18 | static LIST_HEAD(_exception_store_types); | ||
19 | static DEFINE_SPINLOCK(_lock); | ||
20 | |||
21 | static struct dm_exception_store_type *__find_exception_store_type(const char *name) | ||
22 | { | ||
23 | struct dm_exception_store_type *type; | ||
24 | |||
25 | list_for_each_entry(type, &_exception_store_types, list) | ||
26 | if (!strcmp(name, type->name)) | ||
27 | return type; | ||
28 | |||
29 | return NULL; | ||
30 | } | ||
31 | |||
32 | static struct dm_exception_store_type *_get_exception_store_type(const char *name) | ||
33 | { | ||
34 | struct dm_exception_store_type *type; | ||
35 | |||
36 | spin_lock(&_lock); | ||
37 | |||
38 | type = __find_exception_store_type(name); | ||
39 | |||
40 | if (type && !try_module_get(type->module)) | ||
41 | type = NULL; | ||
42 | |||
43 | spin_unlock(&_lock); | ||
44 | |||
45 | return type; | ||
46 | } | ||
47 | |||
48 | /* | ||
49 | * get_type | ||
50 | * @type_name | ||
51 | * | ||
52 | * Attempt to retrieve the dm_exception_store_type by name. If not already | ||
53 | * available, attempt to load the appropriate module. | ||
54 | * | ||
55 | * Exstore modules are named "dm-exstore-" followed by the 'type_name'. | ||
56 | * Modules may contain multiple types. | ||
57 | * This function will first try the module "dm-exstore-<type_name>", | ||
58 | * then truncate 'type_name' on the last '-' and try again. | ||
59 | * | ||
60 | * For example, if type_name was "clustered-shared", it would search | ||
61 | * 'dm-exstore-clustered-shared' then 'dm-exstore-clustered'. | ||
62 | * | ||
63 | * 'dm-exception-store-<type_name>' is too long of a name in my | ||
64 | * opinion, which is why I've chosen to have the files | ||
65 | * containing exception store implementations be 'dm-exstore-<type_name>'. | ||
66 | * If you want your module to be autoloaded, you will follow this | ||
67 | * naming convention. | ||
68 | * | ||
69 | * Returns: dm_exception_store_type* on success, NULL on failure | ||
70 | */ | ||
71 | static struct dm_exception_store_type *get_type(const char *type_name) | ||
72 | { | ||
73 | char *p, *type_name_dup; | ||
74 | struct dm_exception_store_type *type; | ||
75 | |||
76 | type = _get_exception_store_type(type_name); | ||
77 | if (type) | ||
78 | return type; | ||
79 | |||
80 | type_name_dup = kstrdup(type_name, GFP_KERNEL); | ||
81 | if (!type_name_dup) { | ||
82 | DMERR("No memory left to attempt load for \"%s\"", type_name); | ||
83 | return NULL; | ||
84 | } | ||
85 | |||
86 | while (request_module("dm-exstore-%s", type_name_dup) || | ||
87 | !(type = _get_exception_store_type(type_name))) { | ||
88 | p = strrchr(type_name_dup, '-'); | ||
89 | if (!p) | ||
90 | break; | ||
91 | p[0] = '\0'; | ||
92 | } | ||
93 | |||
94 | if (!type) | ||
95 | DMWARN("Module for exstore type \"%s\" not found.", type_name); | ||
96 | |||
97 | kfree(type_name_dup); | ||
98 | |||
99 | return type; | ||
100 | } | ||
101 | |||
102 | static void put_type(struct dm_exception_store_type *type) | ||
103 | { | ||
104 | spin_lock(&_lock); | ||
105 | module_put(type->module); | ||
106 | spin_unlock(&_lock); | ||
107 | } | ||
108 | |||
109 | int dm_exception_store_type_register(struct dm_exception_store_type *type) | ||
110 | { | ||
111 | int r = 0; | ||
112 | |||
113 | spin_lock(&_lock); | ||
114 | if (!__find_exception_store_type(type->name)) | ||
115 | list_add(&type->list, &_exception_store_types); | ||
116 | else | ||
117 | r = -EEXIST; | ||
118 | spin_unlock(&_lock); | ||
119 | |||
120 | return r; | ||
121 | } | ||
122 | EXPORT_SYMBOL(dm_exception_store_type_register); | ||
123 | |||
124 | int dm_exception_store_type_unregister(struct dm_exception_store_type *type) | ||
125 | { | ||
126 | spin_lock(&_lock); | ||
127 | |||
128 | if (!__find_exception_store_type(type->name)) { | ||
129 | spin_unlock(&_lock); | ||
130 | return -EINVAL; | ||
131 | } | ||
132 | |||
133 | list_del(&type->list); | ||
134 | |||
135 | spin_unlock(&_lock); | ||
136 | |||
137 | return 0; | ||
138 | } | ||
139 | EXPORT_SYMBOL(dm_exception_store_type_unregister); | ||
140 | |||
141 | /* | ||
142 | * Round a number up to the nearest 'size' boundary. size must | ||
143 | * be a power of 2. | ||
144 | */ | ||
145 | static ulong round_up(ulong n, ulong size) | ||
146 | { | ||
147 | size--; | ||
148 | return (n + size) & ~size; | ||
149 | } | ||
150 | |||
151 | static int set_chunk_size(struct dm_exception_store *store, | ||
152 | const char *chunk_size_arg, char **error) | ||
153 | { | ||
154 | unsigned long chunk_size_ulong; | ||
155 | char *value; | ||
156 | |||
157 | chunk_size_ulong = simple_strtoul(chunk_size_arg, &value, 10); | ||
158 | if (*chunk_size_arg == '\0' || *value != '\0') { | ||
159 | *error = "Invalid chunk size"; | ||
160 | return -EINVAL; | ||
161 | } | ||
162 | |||
163 | if (!chunk_size_ulong) { | ||
164 | store->chunk_size = store->chunk_mask = store->chunk_shift = 0; | ||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | /* | ||
169 | * Chunk size must be multiple of page size. Silently | ||
170 | * round up if it's not. | ||
171 | */ | ||
172 | chunk_size_ulong = round_up(chunk_size_ulong, PAGE_SIZE >> 9); | ||
173 | |||
174 | /* Check chunk_size is a power of 2 */ | ||
175 | if (!is_power_of_2(chunk_size_ulong)) { | ||
176 | *error = "Chunk size is not a power of 2"; | ||
177 | return -EINVAL; | ||
178 | } | ||
179 | |||
180 | /* Validate the chunk size against the device block size */ | ||
181 | if (chunk_size_ulong % (bdev_hardsect_size(store->cow->bdev) >> 9)) { | ||
182 | *error = "Chunk size is not a multiple of device blocksize"; | ||
183 | return -EINVAL; | ||
184 | } | ||
185 | |||
186 | store->chunk_size = chunk_size_ulong; | ||
187 | store->chunk_mask = chunk_size_ulong - 1; | ||
188 | store->chunk_shift = ffs(chunk_size_ulong) - 1; | ||
189 | |||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | int dm_exception_store_create(struct dm_target *ti, int argc, char **argv, | ||
194 | unsigned *args_used, | ||
195 | struct dm_exception_store **store) | ||
196 | { | ||
197 | int r = 0; | ||
198 | struct dm_exception_store_type *type; | ||
199 | struct dm_exception_store *tmp_store; | ||
200 | char persistent; | ||
201 | |||
202 | if (argc < 3) { | ||
203 | ti->error = "Insufficient exception store arguments"; | ||
204 | return -EINVAL; | ||
205 | } | ||
206 | |||
207 | tmp_store = kmalloc(sizeof(*tmp_store), GFP_KERNEL); | ||
208 | if (!tmp_store) { | ||
209 | ti->error = "Exception store allocation failed"; | ||
210 | return -ENOMEM; | ||
211 | } | ||
212 | |||
213 | persistent = toupper(*argv[1]); | ||
214 | if (persistent != 'P' && persistent != 'N') { | ||
215 | ti->error = "Persistent flag is not P or N"; | ||
216 | return -EINVAL; | ||
217 | } | ||
218 | |||
219 | type = get_type(argv[1]); | ||
220 | if (!type) { | ||
221 | ti->error = "Exception store type not recognised"; | ||
222 | r = -EINVAL; | ||
223 | goto bad_type; | ||
224 | } | ||
225 | |||
226 | tmp_store->type = type; | ||
227 | tmp_store->ti = ti; | ||
228 | |||
229 | r = dm_get_device(ti, argv[0], 0, 0, | ||
230 | FMODE_READ | FMODE_WRITE, &tmp_store->cow); | ||
231 | if (r) { | ||
232 | ti->error = "Cannot get COW device"; | ||
233 | goto bad_cow; | ||
234 | } | ||
235 | |||
236 | r = set_chunk_size(tmp_store, argv[2], &ti->error); | ||
237 | if (r) | ||
238 | goto bad_cow; | ||
239 | |||
240 | r = type->ctr(tmp_store, 0, NULL); | ||
241 | if (r) { | ||
242 | ti->error = "Exception store type constructor failed"; | ||
243 | goto bad_ctr; | ||
244 | } | ||
245 | |||
246 | *args_used = 3; | ||
247 | *store = tmp_store; | ||
248 | return 0; | ||
249 | |||
250 | bad_ctr: | ||
251 | dm_put_device(ti, tmp_store->cow); | ||
252 | bad_cow: | ||
253 | put_type(type); | ||
254 | bad_type: | ||
255 | kfree(tmp_store); | ||
256 | return r; | ||
257 | } | ||
258 | EXPORT_SYMBOL(dm_exception_store_create); | ||
259 | |||
260 | void dm_exception_store_destroy(struct dm_exception_store *store) | ||
261 | { | ||
262 | store->type->dtr(store); | ||
263 | dm_put_device(store->ti, store->cow); | ||
264 | put_type(store->type); | ||
265 | kfree(store); | ||
266 | } | ||
267 | EXPORT_SYMBOL(dm_exception_store_destroy); | ||
268 | |||
17 | int dm_exception_store_init(void) | 269 | int dm_exception_store_init(void) |
18 | { | 270 | { |
19 | int r; | 271 | int r; |