xref: /linux/drivers/md/persistent-data/dm-transaction-manager.c (revision d8ce7263e1bc3b6b2b906fec0c5037bc27d21d6a)
1 /*
2  * Copyright (C) 2011 Red Hat, Inc.
3  *
4  * This file is released under the GPL.
5  */
6 #include "dm-transaction-manager.h"
7 #include "dm-space-map.h"
8 #include "dm-space-map-checker.h"
9 #include "dm-space-map-disk.h"
10 #include "dm-space-map-metadata.h"
11 #include "dm-persistent-data-internal.h"
12 
13 #include <linux/export.h>
14 #include <linux/slab.h>
15 #include <linux/device-mapper.h>
16 
17 #define DM_MSG_PREFIX "transaction manager"
18 
19 /*----------------------------------------------------------------*/
20 
21 struct shadow_info {
22 	struct hlist_node hlist;
23 	dm_block_t where;
24 };
25 
26 /*
27  * It would be nice if we scaled with the size of transaction.
28  */
29 #define HASH_SIZE 256
30 #define HASH_MASK (HASH_SIZE - 1)
31 
32 struct dm_transaction_manager {
33 	int is_clone;
34 	struct dm_transaction_manager *real;
35 
36 	struct dm_block_manager *bm;
37 	struct dm_space_map *sm;
38 
39 	spinlock_t lock;
40 	struct hlist_head buckets[HASH_SIZE];
41 };
42 
43 /*----------------------------------------------------------------*/
44 
45 static int is_shadow(struct dm_transaction_manager *tm, dm_block_t b)
46 {
47 	int r = 0;
48 	unsigned bucket = dm_hash_block(b, HASH_MASK);
49 	struct shadow_info *si;
50 	struct hlist_node *n;
51 
52 	spin_lock(&tm->lock);
53 	hlist_for_each_entry(si, n, tm->buckets + bucket, hlist)
54 		if (si->where == b) {
55 			r = 1;
56 			break;
57 		}
58 	spin_unlock(&tm->lock);
59 
60 	return r;
61 }
62 
63 /*
64  * This can silently fail if there's no memory.  We're ok with this since
65  * creating redundant shadows causes no harm.
66  */
67 static void insert_shadow(struct dm_transaction_manager *tm, dm_block_t b)
68 {
69 	unsigned bucket;
70 	struct shadow_info *si;
71 
72 	si = kmalloc(sizeof(*si), GFP_NOIO);
73 	if (si) {
74 		si->where = b;
75 		bucket = dm_hash_block(b, HASH_MASK);
76 		spin_lock(&tm->lock);
77 		hlist_add_head(&si->hlist, tm->buckets + bucket);
78 		spin_unlock(&tm->lock);
79 	}
80 }
81 
82 static void wipe_shadow_table(struct dm_transaction_manager *tm)
83 {
84 	struct shadow_info *si;
85 	struct hlist_node *n, *tmp;
86 	struct hlist_head *bucket;
87 	int i;
88 
89 	spin_lock(&tm->lock);
90 	for (i = 0; i < HASH_SIZE; i++) {
91 		bucket = tm->buckets + i;
92 		hlist_for_each_entry_safe(si, n, tmp, bucket, hlist)
93 			kfree(si);
94 
95 		INIT_HLIST_HEAD(bucket);
96 	}
97 
98 	spin_unlock(&tm->lock);
99 }
100 
101 /*----------------------------------------------------------------*/
102 
103 static struct dm_transaction_manager *dm_tm_create(struct dm_block_manager *bm,
104 						   struct dm_space_map *sm)
105 {
106 	int i;
107 	struct dm_transaction_manager *tm;
108 
109 	tm = kmalloc(sizeof(*tm), GFP_KERNEL);
110 	if (!tm)
111 		return ERR_PTR(-ENOMEM);
112 
113 	tm->is_clone = 0;
114 	tm->real = NULL;
115 	tm->bm = bm;
116 	tm->sm = sm;
117 
118 	spin_lock_init(&tm->lock);
119 	for (i = 0; i < HASH_SIZE; i++)
120 		INIT_HLIST_HEAD(tm->buckets + i);
121 
122 	return tm;
123 }
124 
125 struct dm_transaction_manager *dm_tm_create_non_blocking_clone(struct dm_transaction_manager *real)
126 {
127 	struct dm_transaction_manager *tm;
128 
129 	tm = kmalloc(sizeof(*tm), GFP_KERNEL);
130 	if (tm) {
131 		tm->is_clone = 1;
132 		tm->real = real;
133 	}
134 
135 	return tm;
136 }
137 EXPORT_SYMBOL_GPL(dm_tm_create_non_blocking_clone);
138 
139 void dm_tm_destroy(struct dm_transaction_manager *tm)
140 {
141 	kfree(tm);
142 }
143 EXPORT_SYMBOL_GPL(dm_tm_destroy);
144 
145 int dm_tm_pre_commit(struct dm_transaction_manager *tm)
146 {
147 	int r;
148 
149 	if (tm->is_clone)
150 		return -EWOULDBLOCK;
151 
152 	r = dm_sm_commit(tm->sm);
153 	if (r < 0)
154 		return r;
155 
156 	return 0;
157 }
158 EXPORT_SYMBOL_GPL(dm_tm_pre_commit);
159 
160 int dm_tm_commit(struct dm_transaction_manager *tm, struct dm_block *root)
161 {
162 	if (tm->is_clone)
163 		return -EWOULDBLOCK;
164 
165 	wipe_shadow_table(tm);
166 
167 	return dm_bm_flush_and_unlock(tm->bm, root);
168 }
169 EXPORT_SYMBOL_GPL(dm_tm_commit);
170 
171 int dm_tm_new_block(struct dm_transaction_manager *tm,
172 		    struct dm_block_validator *v,
173 		    struct dm_block **result)
174 {
175 	int r;
176 	dm_block_t new_block;
177 
178 	if (tm->is_clone)
179 		return -EWOULDBLOCK;
180 
181 	r = dm_sm_new_block(tm->sm, &new_block);
182 	if (r < 0)
183 		return r;
184 
185 	r = dm_bm_write_lock_zero(tm->bm, new_block, v, result);
186 	if (r < 0) {
187 		dm_sm_dec_block(tm->sm, new_block);
188 		return r;
189 	}
190 
191 	/*
192 	 * New blocks count as shadows in that they don't need to be
193 	 * shadowed again.
194 	 */
195 	insert_shadow(tm, new_block);
196 
197 	return 0;
198 }
199 
200 static int __shadow_block(struct dm_transaction_manager *tm, dm_block_t orig,
201 			  struct dm_block_validator *v,
202 			  struct dm_block **result)
203 {
204 	int r;
205 	dm_block_t new;
206 	struct dm_block *orig_block;
207 
208 	r = dm_sm_new_block(tm->sm, &new);
209 	if (r < 0)
210 		return r;
211 
212 	r = dm_sm_dec_block(tm->sm, orig);
213 	if (r < 0)
214 		return r;
215 
216 	r = dm_bm_read_lock(tm->bm, orig, v, &orig_block);
217 	if (r < 0)
218 		return r;
219 
220 	r = dm_bm_unlock_move(orig_block, new);
221 	if (r < 0) {
222 		dm_bm_unlock(orig_block);
223 		return r;
224 	}
225 
226 	return dm_bm_write_lock(tm->bm, new, v, result);
227 }
228 
229 int dm_tm_shadow_block(struct dm_transaction_manager *tm, dm_block_t orig,
230 		       struct dm_block_validator *v, struct dm_block **result,
231 		       int *inc_children)
232 {
233 	int r;
234 
235 	if (tm->is_clone)
236 		return -EWOULDBLOCK;
237 
238 	r = dm_sm_count_is_more_than_one(tm->sm, orig, inc_children);
239 	if (r < 0)
240 		return r;
241 
242 	if (is_shadow(tm, orig) && !*inc_children)
243 		return dm_bm_write_lock(tm->bm, orig, v, result);
244 
245 	r = __shadow_block(tm, orig, v, result);
246 	if (r < 0)
247 		return r;
248 	insert_shadow(tm, dm_block_location(*result));
249 
250 	return r;
251 }
252 EXPORT_SYMBOL_GPL(dm_tm_shadow_block);
253 
254 int dm_tm_read_lock(struct dm_transaction_manager *tm, dm_block_t b,
255 		    struct dm_block_validator *v,
256 		    struct dm_block **blk)
257 {
258 	if (tm->is_clone)
259 		return dm_bm_read_try_lock(tm->real->bm, b, v, blk);
260 
261 	return dm_bm_read_lock(tm->bm, b, v, blk);
262 }
263 EXPORT_SYMBOL_GPL(dm_tm_read_lock);
264 
265 int dm_tm_unlock(struct dm_transaction_manager *tm, struct dm_block *b)
266 {
267 	return dm_bm_unlock(b);
268 }
269 EXPORT_SYMBOL_GPL(dm_tm_unlock);
270 
271 void dm_tm_inc(struct dm_transaction_manager *tm, dm_block_t b)
272 {
273 	/*
274 	 * The non-blocking clone doesn't support this.
275 	 */
276 	BUG_ON(tm->is_clone);
277 
278 	dm_sm_inc_block(tm->sm, b);
279 }
280 EXPORT_SYMBOL_GPL(dm_tm_inc);
281 
282 void dm_tm_dec(struct dm_transaction_manager *tm, dm_block_t b)
283 {
284 	/*
285 	 * The non-blocking clone doesn't support this.
286 	 */
287 	BUG_ON(tm->is_clone);
288 
289 	dm_sm_dec_block(tm->sm, b);
290 }
291 EXPORT_SYMBOL_GPL(dm_tm_dec);
292 
293 int dm_tm_ref(struct dm_transaction_manager *tm, dm_block_t b,
294 	      uint32_t *result)
295 {
296 	if (tm->is_clone)
297 		return -EWOULDBLOCK;
298 
299 	return dm_sm_get_count(tm->sm, b, result);
300 }
301 
302 struct dm_block_manager *dm_tm_get_bm(struct dm_transaction_manager *tm)
303 {
304 	return tm->bm;
305 }
306 
307 /*----------------------------------------------------------------*/
308 
309 static int dm_tm_create_internal(struct dm_block_manager *bm,
310 				 dm_block_t sb_location,
311 				 struct dm_block_validator *sb_validator,
312 				 size_t root_offset, size_t root_max_len,
313 				 struct dm_transaction_manager **tm,
314 				 struct dm_space_map **sm,
315 				 struct dm_block **sblock,
316 				 int create)
317 {
318 	int r;
319 	struct dm_space_map *inner;
320 
321 	inner = dm_sm_metadata_init();
322 	if (IS_ERR(inner))
323 		return PTR_ERR(inner);
324 
325 	*tm = dm_tm_create(bm, inner);
326 	if (IS_ERR(*tm)) {
327 		dm_sm_destroy(inner);
328 		return PTR_ERR(*tm);
329 	}
330 
331 	if (create) {
332 		r = dm_bm_write_lock_zero(dm_tm_get_bm(*tm), sb_location,
333 					  sb_validator, sblock);
334 		if (r < 0) {
335 			DMERR("couldn't lock superblock");
336 			goto bad1;
337 		}
338 
339 		r = dm_sm_metadata_create(inner, *tm, dm_bm_nr_blocks(bm),
340 					  sb_location);
341 		if (r) {
342 			DMERR("couldn't create metadata space map");
343 			goto bad2;
344 		}
345 
346 		*sm = dm_sm_checker_create(inner);
347 		if (!*sm)
348 			goto bad2;
349 
350 	} else {
351 		r = dm_bm_write_lock(dm_tm_get_bm(*tm), sb_location,
352 				     sb_validator, sblock);
353 		if (r < 0) {
354 			DMERR("couldn't lock superblock");
355 			goto bad1;
356 		}
357 
358 		r = dm_sm_metadata_open(inner, *tm,
359 					dm_block_data(*sblock) + root_offset,
360 					root_max_len);
361 		if (r) {
362 			DMERR("couldn't open metadata space map");
363 			goto bad2;
364 		}
365 
366 		*sm = dm_sm_checker_create(inner);
367 		if (!*sm)
368 			goto bad2;
369 	}
370 
371 	return 0;
372 
373 bad2:
374 	dm_tm_unlock(*tm, *sblock);
375 bad1:
376 	dm_tm_destroy(*tm);
377 	dm_sm_destroy(inner);
378 	return r;
379 }
380 
381 int dm_tm_create_with_sm(struct dm_block_manager *bm, dm_block_t sb_location,
382 			 struct dm_block_validator *sb_validator,
383 			 struct dm_transaction_manager **tm,
384 			 struct dm_space_map **sm, struct dm_block **sblock)
385 {
386 	return dm_tm_create_internal(bm, sb_location, sb_validator,
387 				     0, 0, tm, sm, sblock, 1);
388 }
389 EXPORT_SYMBOL_GPL(dm_tm_create_with_sm);
390 
391 int dm_tm_open_with_sm(struct dm_block_manager *bm, dm_block_t sb_location,
392 		       struct dm_block_validator *sb_validator,
393 		       size_t root_offset, size_t root_max_len,
394 		       struct dm_transaction_manager **tm,
395 		       struct dm_space_map **sm, struct dm_block **sblock)
396 {
397 	return dm_tm_create_internal(bm, sb_location, sb_validator, root_offset,
398 				     root_max_len, tm, sm, sblock, 0);
399 }
400 EXPORT_SYMBOL_GPL(dm_tm_open_with_sm);
401 
402 /*----------------------------------------------------------------*/
403