1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2001-2002 Sistina Software (UK) Limited. |
4 | * Copyright (C) 2006-2008 Red Hat GmbH |
5 | * |
6 | * This file is released under the GPL. |
7 | */ |
8 | |
9 | #include "dm-exception-store.h" |
10 | |
11 | #include <linux/ctype.h> |
12 | #include <linux/mm.h> |
13 | #include <linux/pagemap.h> |
14 | #include <linux/vmalloc.h> |
15 | #include <linux/module.h> |
16 | #include <linux/slab.h> |
17 | |
18 | #define DM_MSG_PREFIX "snapshot exception stores" |
19 | |
20 | static LIST_HEAD(_exception_store_types); |
21 | static DEFINE_SPINLOCK(_lock); |
22 | |
23 | static struct dm_exception_store_type *__find_exception_store_type(const char *name) |
24 | { |
25 | struct dm_exception_store_type *type; |
26 | |
27 | list_for_each_entry(type, &_exception_store_types, list) |
28 | if (!strcmp(name, type->name)) |
29 | return type; |
30 | |
31 | return NULL; |
32 | } |
33 | |
34 | static struct dm_exception_store_type *_get_exception_store_type(const char *name) |
35 | { |
36 | struct dm_exception_store_type *type; |
37 | |
38 | spin_lock(lock: &_lock); |
39 | |
40 | type = __find_exception_store_type(name); |
41 | |
42 | if (type && !try_module_get(module: type->module)) |
43 | type = NULL; |
44 | |
45 | spin_unlock(lock: &_lock); |
46 | |
47 | return type; |
48 | } |
49 | |
50 | /* |
51 | * get_type |
52 | * @type_name |
53 | * |
54 | * Attempt to retrieve the dm_exception_store_type by name. If not already |
55 | * available, attempt to load the appropriate module. |
56 | * |
57 | * Exstore modules are named "dm-exstore-" followed by the 'type_name'. |
58 | * Modules may contain multiple types. |
59 | * This function will first try the module "dm-exstore-<type_name>", |
60 | * then truncate 'type_name' on the last '-' and try again. |
61 | * |
62 | * For example, if type_name was "clustered-shared", it would search |
63 | * 'dm-exstore-clustered-shared' then 'dm-exstore-clustered'. |
64 | * |
65 | * 'dm-exception-store-<type_name>' is too long of a name in my |
66 | * opinion, which is why I've chosen to have the files |
67 | * containing exception store implementations be 'dm-exstore-<type_name>'. |
68 | * If you want your module to be autoloaded, you will follow this |
69 | * naming convention. |
70 | * |
71 | * Returns: dm_exception_store_type* on success, NULL on failure |
72 | */ |
73 | static struct dm_exception_store_type *get_type(const char *type_name) |
74 | { |
75 | char *p, *type_name_dup; |
76 | struct dm_exception_store_type *type; |
77 | |
78 | type = _get_exception_store_type(name: type_name); |
79 | if (type) |
80 | return type; |
81 | |
82 | type_name_dup = kstrdup(s: type_name, GFP_KERNEL); |
83 | if (!type_name_dup) { |
84 | DMERR("No memory left to attempt load for \"%s\"" , type_name); |
85 | return NULL; |
86 | } |
87 | |
88 | while (request_module("dm-exstore-%s" , type_name_dup) || |
89 | !(type = _get_exception_store_type(name: type_name))) { |
90 | p = strrchr(type_name_dup, '-'); |
91 | if (!p) |
92 | break; |
93 | p[0] = '\0'; |
94 | } |
95 | |
96 | if (!type) |
97 | DMWARN("Module for exstore type \"%s\" not found." , type_name); |
98 | |
99 | kfree(objp: type_name_dup); |
100 | |
101 | return type; |
102 | } |
103 | |
104 | static void put_type(struct dm_exception_store_type *type) |
105 | { |
106 | spin_lock(lock: &_lock); |
107 | module_put(module: type->module); |
108 | spin_unlock(lock: &_lock); |
109 | } |
110 | |
111 | int dm_exception_store_type_register(struct dm_exception_store_type *type) |
112 | { |
113 | int r = 0; |
114 | |
115 | spin_lock(lock: &_lock); |
116 | if (!__find_exception_store_type(name: type->name)) |
117 | list_add(new: &type->list, head: &_exception_store_types); |
118 | else |
119 | r = -EEXIST; |
120 | spin_unlock(lock: &_lock); |
121 | |
122 | return r; |
123 | } |
124 | EXPORT_SYMBOL(dm_exception_store_type_register); |
125 | |
126 | int dm_exception_store_type_unregister(struct dm_exception_store_type *type) |
127 | { |
128 | spin_lock(lock: &_lock); |
129 | |
130 | if (!__find_exception_store_type(name: type->name)) { |
131 | spin_unlock(lock: &_lock); |
132 | return -EINVAL; |
133 | } |
134 | |
135 | list_del(entry: &type->list); |
136 | |
137 | spin_unlock(lock: &_lock); |
138 | |
139 | return 0; |
140 | } |
141 | EXPORT_SYMBOL(dm_exception_store_type_unregister); |
142 | |
143 | static int set_chunk_size(struct dm_exception_store *store, |
144 | const char *chunk_size_arg, char **error) |
145 | { |
146 | unsigned int chunk_size; |
147 | |
148 | if (kstrtouint(s: chunk_size_arg, base: 10, res: &chunk_size)) { |
149 | *error = "Invalid chunk size" ; |
150 | return -EINVAL; |
151 | } |
152 | |
153 | if (!chunk_size) { |
154 | store->chunk_size = store->chunk_mask = store->chunk_shift = 0; |
155 | return 0; |
156 | } |
157 | |
158 | return dm_exception_store_set_chunk_size(store, chunk_size, error); |
159 | } |
160 | |
161 | int dm_exception_store_set_chunk_size(struct dm_exception_store *store, |
162 | unsigned int chunk_size, |
163 | char **error) |
164 | { |
165 | /* Check chunk_size is a power of 2 */ |
166 | if (!is_power_of_2(n: chunk_size)) { |
167 | *error = "Chunk size is not a power of 2" ; |
168 | return -EINVAL; |
169 | } |
170 | |
171 | /* Validate the chunk size against the device block size */ |
172 | if (chunk_size % |
173 | (bdev_logical_block_size(bdev: dm_snap_cow(snap: store->snap)->bdev) >> 9) || |
174 | chunk_size % |
175 | (bdev_logical_block_size(bdev: dm_snap_origin(snap: store->snap)->bdev) >> 9)) { |
176 | *error = "Chunk size is not a multiple of device blocksize" ; |
177 | return -EINVAL; |
178 | } |
179 | |
180 | if (chunk_size > INT_MAX >> SECTOR_SHIFT) { |
181 | *error = "Chunk size is too high" ; |
182 | return -EINVAL; |
183 | } |
184 | |
185 | store->chunk_size = chunk_size; |
186 | store->chunk_mask = chunk_size - 1; |
187 | store->chunk_shift = __ffs(chunk_size); |
188 | |
189 | return 0; |
190 | } |
191 | |
192 | int dm_exception_store_create(struct dm_target *ti, int argc, char **argv, |
193 | struct dm_snapshot *snap, |
194 | unsigned int *args_used, |
195 | struct dm_exception_store **store) |
196 | { |
197 | int r = 0; |
198 | struct dm_exception_store_type *type = NULL; |
199 | struct dm_exception_store *tmp_store; |
200 | char persistent; |
201 | |
202 | if (argc < 2) { |
203 | ti->error = "Insufficient exception store arguments" ; |
204 | return -EINVAL; |
205 | } |
206 | |
207 | tmp_store = kzalloc(size: 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[0]); |
214 | if (persistent == 'P') |
215 | type = get_type(type_name: "P" ); |
216 | else if (persistent == 'N') |
217 | type = get_type(type_name: "N" ); |
218 | else { |
219 | ti->error = "Exception store type is not P or N" ; |
220 | r = -EINVAL; |
221 | goto bad_type; |
222 | } |
223 | |
224 | if (!type) { |
225 | ti->error = "Exception store type not recognised" ; |
226 | r = -EINVAL; |
227 | goto bad_type; |
228 | } |
229 | |
230 | tmp_store->type = type; |
231 | tmp_store->snap = snap; |
232 | |
233 | r = set_chunk_size(store: tmp_store, chunk_size_arg: argv[1], error: &ti->error); |
234 | if (r) |
235 | goto bad; |
236 | |
237 | r = type->ctr(tmp_store, (strlen(argv[0]) > 1 ? &argv[0][1] : NULL)); |
238 | if (r) { |
239 | ti->error = "Exception store type constructor failed" ; |
240 | goto bad; |
241 | } |
242 | |
243 | *args_used = 2; |
244 | *store = tmp_store; |
245 | return 0; |
246 | |
247 | bad: |
248 | put_type(type); |
249 | bad_type: |
250 | kfree(objp: tmp_store); |
251 | return r; |
252 | } |
253 | EXPORT_SYMBOL(dm_exception_store_create); |
254 | |
255 | void dm_exception_store_destroy(struct dm_exception_store *store) |
256 | { |
257 | store->type->dtr(store); |
258 | put_type(type: store->type); |
259 | kfree(objp: store); |
260 | } |
261 | EXPORT_SYMBOL(dm_exception_store_destroy); |
262 | |
263 | int dm_exception_store_init(void) |
264 | { |
265 | int r; |
266 | |
267 | r = dm_transient_snapshot_init(); |
268 | if (r) { |
269 | DMERR("Unable to register transient exception store type." ); |
270 | goto transient_fail; |
271 | } |
272 | |
273 | r = dm_persistent_snapshot_init(); |
274 | if (r) { |
275 | DMERR("Unable to register persistent exception store type" ); |
276 | goto persistent_fail; |
277 | } |
278 | |
279 | return 0; |
280 | |
281 | persistent_fail: |
282 | dm_transient_snapshot_exit(); |
283 | transient_fail: |
284 | return r; |
285 | } |
286 | |
287 | void dm_exception_store_exit(void) |
288 | { |
289 | dm_persistent_snapshot_exit(); |
290 | dm_transient_snapshot_exit(); |
291 | } |
292 | |