1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2003 Sistina Software |
4 | * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved. |
5 | * |
6 | * This file is released under the LGPL. |
7 | */ |
8 | |
9 | #include <linux/init.h> |
10 | #include <linux/slab.h> |
11 | #include <linux/module.h> |
12 | #include <linux/vmalloc.h> |
13 | #include <linux/dm-io.h> |
14 | #include <linux/dm-dirty-log.h> |
15 | |
16 | #include <linux/device-mapper.h> |
17 | |
18 | #define DM_MSG_PREFIX "dirty region log" |
19 | |
20 | static LIST_HEAD(_log_types); |
21 | static DEFINE_SPINLOCK(_lock); |
22 | |
23 | static struct dm_dirty_log_type *__find_dirty_log_type(const char *name) |
24 | { |
25 | struct dm_dirty_log_type *log_type; |
26 | |
27 | list_for_each_entry(log_type, &_log_types, list) |
28 | if (!strcmp(name, log_type->name)) |
29 | return log_type; |
30 | |
31 | return NULL; |
32 | } |
33 | |
34 | static struct dm_dirty_log_type *_get_dirty_log_type(const char *name) |
35 | { |
36 | struct dm_dirty_log_type *log_type; |
37 | |
38 | spin_lock(lock: &_lock); |
39 | |
40 | log_type = __find_dirty_log_type(name); |
41 | if (log_type && !try_module_get(module: log_type->module)) |
42 | log_type = NULL; |
43 | |
44 | spin_unlock(lock: &_lock); |
45 | |
46 | return log_type; |
47 | } |
48 | |
49 | /* |
50 | * get_type |
51 | * @type_name |
52 | * |
53 | * Attempt to retrieve the dm_dirty_log_type by name. If not already |
54 | * available, attempt to load the appropriate module. |
55 | * |
56 | * Log modules are named "dm-log-" followed by the 'type_name'. |
57 | * Modules may contain multiple types. |
58 | * This function will first try the module "dm-log-<type_name>", |
59 | * then truncate 'type_name' on the last '-' and try again. |
60 | * |
61 | * For example, if type_name was "clustered-disk", it would search |
62 | * 'dm-log-clustered-disk' then 'dm-log-clustered'. |
63 | * |
64 | * Returns: dirty_log_type* on success, NULL on failure |
65 | */ |
66 | static struct dm_dirty_log_type *get_type(const char *type_name) |
67 | { |
68 | char *p, *type_name_dup; |
69 | struct dm_dirty_log_type *log_type; |
70 | |
71 | if (!type_name) |
72 | return NULL; |
73 | |
74 | log_type = _get_dirty_log_type(name: type_name); |
75 | if (log_type) |
76 | return log_type; |
77 | |
78 | type_name_dup = kstrdup(s: type_name, GFP_KERNEL); |
79 | if (!type_name_dup) { |
80 | DMWARN("No memory left to attempt log module load for \"%s\"" , |
81 | type_name); |
82 | return NULL; |
83 | } |
84 | |
85 | while (request_module("dm-log-%s" , type_name_dup) || |
86 | !(log_type = _get_dirty_log_type(name: type_name))) { |
87 | p = strrchr(type_name_dup, '-'); |
88 | if (!p) |
89 | break; |
90 | p[0] = '\0'; |
91 | } |
92 | |
93 | if (!log_type) |
94 | DMWARN("Module for logging type \"%s\" not found." , type_name); |
95 | |
96 | kfree(objp: type_name_dup); |
97 | |
98 | return log_type; |
99 | } |
100 | |
101 | static void put_type(struct dm_dirty_log_type *type) |
102 | { |
103 | if (!type) |
104 | return; |
105 | |
106 | spin_lock(lock: &_lock); |
107 | if (!__find_dirty_log_type(name: type->name)) |
108 | goto out; |
109 | |
110 | module_put(module: type->module); |
111 | |
112 | out: |
113 | spin_unlock(lock: &_lock); |
114 | } |
115 | |
116 | int dm_dirty_log_type_register(struct dm_dirty_log_type *type) |
117 | { |
118 | int r = 0; |
119 | |
120 | spin_lock(lock: &_lock); |
121 | if (!__find_dirty_log_type(name: type->name)) |
122 | list_add(new: &type->list, head: &_log_types); |
123 | else |
124 | r = -EEXIST; |
125 | spin_unlock(lock: &_lock); |
126 | |
127 | return r; |
128 | } |
129 | EXPORT_SYMBOL(dm_dirty_log_type_register); |
130 | |
131 | int dm_dirty_log_type_unregister(struct dm_dirty_log_type *type) |
132 | { |
133 | spin_lock(lock: &_lock); |
134 | |
135 | if (!__find_dirty_log_type(name: type->name)) { |
136 | spin_unlock(lock: &_lock); |
137 | return -EINVAL; |
138 | } |
139 | |
140 | list_del(entry: &type->list); |
141 | |
142 | spin_unlock(lock: &_lock); |
143 | |
144 | return 0; |
145 | } |
146 | EXPORT_SYMBOL(dm_dirty_log_type_unregister); |
147 | |
148 | struct dm_dirty_log *dm_dirty_log_create(const char *type_name, |
149 | struct dm_target *ti, |
150 | int (*flush_callback_fn)(struct dm_target *ti), |
151 | unsigned int argc, char **argv) |
152 | { |
153 | struct dm_dirty_log_type *type; |
154 | struct dm_dirty_log *log; |
155 | |
156 | log = kmalloc(size: sizeof(*log), GFP_KERNEL); |
157 | if (!log) |
158 | return NULL; |
159 | |
160 | type = get_type(type_name); |
161 | if (!type) { |
162 | kfree(objp: log); |
163 | return NULL; |
164 | } |
165 | |
166 | log->flush_callback_fn = flush_callback_fn; |
167 | log->type = type; |
168 | if (type->ctr(log, ti, argc, argv)) { |
169 | kfree(objp: log); |
170 | put_type(type); |
171 | return NULL; |
172 | } |
173 | |
174 | return log; |
175 | } |
176 | EXPORT_SYMBOL(dm_dirty_log_create); |
177 | |
178 | void dm_dirty_log_destroy(struct dm_dirty_log *log) |
179 | { |
180 | log->type->dtr(log); |
181 | put_type(type: log->type); |
182 | kfree(objp: log); |
183 | } |
184 | EXPORT_SYMBOL(dm_dirty_log_destroy); |
185 | |
186 | /* |
187 | *--------------------------------------------------------------- |
188 | * Persistent and core logs share a lot of their implementation. |
189 | * FIXME: need a reload method to be called from a resume |
190 | *--------------------------------------------------------------- |
191 | */ |
192 | /* |
193 | * Magic for persistent mirrors: "MiRr" |
194 | */ |
195 | #define MIRROR_MAGIC 0x4D695272 |
196 | |
197 | /* |
198 | * The on-disk version of the metadata. |
199 | */ |
200 | #define MIRROR_DISK_VERSION 2 |
201 | #define LOG_OFFSET 2 |
202 | |
203 | struct { |
204 | __le32 ; |
205 | |
206 | /* |
207 | * Simple, incrementing version. no backward |
208 | * compatibility. |
209 | */ |
210 | __le32 ; |
211 | __le64 ; |
212 | } __packed; |
213 | |
214 | struct { |
215 | uint32_t ; |
216 | uint32_t ; |
217 | uint64_t ; |
218 | }; |
219 | |
220 | struct log_c { |
221 | struct dm_target *ti; |
222 | int touched_dirtied; |
223 | int touched_cleaned; |
224 | int flush_failed; |
225 | uint32_t region_size; |
226 | unsigned int region_count; |
227 | region_t sync_count; |
228 | |
229 | unsigned int bitset_uint32_count; |
230 | uint32_t *clean_bits; |
231 | uint32_t *sync_bits; |
232 | uint32_t *recovering_bits; /* FIXME: this seems excessive */ |
233 | |
234 | int sync_search; |
235 | |
236 | /* Resync flag */ |
237 | enum sync { |
238 | DEFAULTSYNC, /* Synchronize if necessary */ |
239 | NOSYNC, /* Devices known to be already in sync */ |
240 | FORCESYNC, /* Force a sync to happen */ |
241 | } sync; |
242 | |
243 | struct dm_io_request io_req; |
244 | |
245 | /* |
246 | * Disk log fields |
247 | */ |
248 | int log_dev_failed; |
249 | int log_dev_flush_failed; |
250 | struct dm_dev *log_dev; |
251 | struct log_header_core ; |
252 | |
253 | struct dm_io_region ; |
254 | struct log_header_disk *; |
255 | }; |
256 | |
257 | /* |
258 | * The touched member needs to be updated every time we access |
259 | * one of the bitsets. |
260 | */ |
261 | static inline int log_test_bit(uint32_t *bs, unsigned int bit) |
262 | { |
263 | return test_bit_le(nr: bit, addr: bs) ? 1 : 0; |
264 | } |
265 | |
266 | static inline void log_set_bit(struct log_c *l, |
267 | uint32_t *bs, unsigned int bit) |
268 | { |
269 | __set_bit_le(nr: bit, addr: bs); |
270 | l->touched_cleaned = 1; |
271 | } |
272 | |
273 | static inline void log_clear_bit(struct log_c *l, |
274 | uint32_t *bs, unsigned int bit) |
275 | { |
276 | __clear_bit_le(nr: bit, addr: bs); |
277 | l->touched_dirtied = 1; |
278 | } |
279 | |
280 | /* |
281 | *--------------------------------------------------------------- |
282 | * Header IO |
283 | *-------------------------------------------------------------- |
284 | */ |
285 | static void (struct log_header_core *core, struct log_header_disk *disk) |
286 | { |
287 | disk->magic = cpu_to_le32(core->magic); |
288 | disk->version = cpu_to_le32(core->version); |
289 | disk->nr_regions = cpu_to_le64(core->nr_regions); |
290 | } |
291 | |
292 | static void (struct log_header_core *core, struct log_header_disk *disk) |
293 | { |
294 | core->magic = le32_to_cpu(disk->magic); |
295 | core->version = le32_to_cpu(disk->version); |
296 | core->nr_regions = le64_to_cpu(disk->nr_regions); |
297 | } |
298 | |
299 | static int (struct log_c *lc, enum req_op op) |
300 | { |
301 | lc->io_req.bi_opf = op; |
302 | |
303 | return dm_io(io_req: &lc->io_req, num_regions: 1, region: &lc->header_location, NULL); |
304 | } |
305 | |
306 | static int (struct log_c *lc) |
307 | { |
308 | struct dm_io_region null_location = { |
309 | .bdev = lc->header_location.bdev, |
310 | .sector = 0, |
311 | .count = 0, |
312 | }; |
313 | |
314 | lc->io_req.bi_opf = REQ_OP_WRITE | REQ_PREFLUSH; |
315 | |
316 | return dm_io(io_req: &lc->io_req, num_regions: 1, region: &null_location, NULL); |
317 | } |
318 | |
319 | static int (struct log_c *log) |
320 | { |
321 | int r; |
322 | |
323 | r = rw_header(lc: log, op: REQ_OP_READ); |
324 | if (r) |
325 | return r; |
326 | |
327 | header_from_disk(core: &log->header, disk: log->disk_header); |
328 | |
329 | /* New log required? */ |
330 | if (log->sync != DEFAULTSYNC || log->header.magic != MIRROR_MAGIC) { |
331 | log->header.magic = MIRROR_MAGIC; |
332 | log->header.version = MIRROR_DISK_VERSION; |
333 | log->header.nr_regions = 0; |
334 | } |
335 | |
336 | #ifdef __LITTLE_ENDIAN |
337 | if (log->header.version == 1) |
338 | log->header.version = 2; |
339 | #endif |
340 | |
341 | if (log->header.version != MIRROR_DISK_VERSION) { |
342 | DMWARN("incompatible disk log version" ); |
343 | return -EINVAL; |
344 | } |
345 | |
346 | return 0; |
347 | } |
348 | |
349 | static int _check_region_size(struct dm_target *ti, uint32_t region_size) |
350 | { |
351 | if (region_size < 2 || region_size > ti->len) |
352 | return 0; |
353 | |
354 | if (!is_power_of_2(n: region_size)) |
355 | return 0; |
356 | |
357 | return 1; |
358 | } |
359 | |
360 | /* |
361 | *-------------------------------------------------------------- |
362 | * core log constructor/destructor |
363 | * |
364 | * argv contains region_size followed optionally by [no]sync |
365 | *-------------------------------------------------------------- |
366 | */ |
367 | #define BYTE_SHIFT 3 |
368 | static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, |
369 | unsigned int argc, char **argv, |
370 | struct dm_dev *dev) |
371 | { |
372 | enum sync sync = DEFAULTSYNC; |
373 | |
374 | struct log_c *lc; |
375 | uint32_t region_size; |
376 | unsigned int region_count; |
377 | size_t bitset_size, buf_size; |
378 | int r; |
379 | char dummy; |
380 | |
381 | if (argc < 1 || argc > 2) { |
382 | DMWARN("wrong number of arguments to dirty region log" ); |
383 | return -EINVAL; |
384 | } |
385 | |
386 | if (argc > 1) { |
387 | if (!strcmp(argv[1], "sync" )) |
388 | sync = FORCESYNC; |
389 | else if (!strcmp(argv[1], "nosync" )) |
390 | sync = NOSYNC; |
391 | else { |
392 | DMWARN("unrecognised sync argument to dirty region log: %s" , argv[1]); |
393 | return -EINVAL; |
394 | } |
395 | } |
396 | |
397 | if (sscanf(argv[0], "%u%c" , ®ion_size, &dummy) != 1 || |
398 | !_check_region_size(ti, region_size)) { |
399 | DMWARN("invalid region size %s" , argv[0]); |
400 | return -EINVAL; |
401 | } |
402 | |
403 | region_count = dm_sector_div_up(ti->len, region_size); |
404 | |
405 | lc = kmalloc(size: sizeof(*lc), GFP_KERNEL); |
406 | if (!lc) { |
407 | DMWARN("couldn't allocate core log" ); |
408 | return -ENOMEM; |
409 | } |
410 | |
411 | lc->ti = ti; |
412 | lc->touched_dirtied = 0; |
413 | lc->touched_cleaned = 0; |
414 | lc->flush_failed = 0; |
415 | lc->region_size = region_size; |
416 | lc->region_count = region_count; |
417 | lc->sync = sync; |
418 | |
419 | /* |
420 | * Work out how many "unsigned long"s we need to hold the bitset. |
421 | */ |
422 | bitset_size = dm_round_up(region_count, BITS_PER_LONG); |
423 | bitset_size >>= BYTE_SHIFT; |
424 | |
425 | lc->bitset_uint32_count = bitset_size / sizeof(*lc->clean_bits); |
426 | |
427 | /* |
428 | * Disk log? |
429 | */ |
430 | if (!dev) { |
431 | lc->clean_bits = vmalloc(size: bitset_size); |
432 | if (!lc->clean_bits) { |
433 | DMWARN("couldn't allocate clean bitset" ); |
434 | kfree(objp: lc); |
435 | return -ENOMEM; |
436 | } |
437 | lc->disk_header = NULL; |
438 | } else { |
439 | lc->log_dev = dev; |
440 | lc->log_dev_failed = 0; |
441 | lc->log_dev_flush_failed = 0; |
442 | lc->header_location.bdev = lc->log_dev->bdev; |
443 | lc->header_location.sector = 0; |
444 | |
445 | /* |
446 | * Buffer holds both header and bitset. |
447 | */ |
448 | buf_size = |
449 | dm_round_up((LOG_OFFSET << SECTOR_SHIFT) + bitset_size, |
450 | bdev_logical_block_size(lc->header_location.bdev)); |
451 | |
452 | if (buf_size > bdev_nr_bytes(bdev: dev->bdev)) { |
453 | DMWARN("log device %s too small: need %llu bytes" , |
454 | dev->name, (unsigned long long)buf_size); |
455 | kfree(objp: lc); |
456 | return -EINVAL; |
457 | } |
458 | |
459 | lc->header_location.count = buf_size >> SECTOR_SHIFT; |
460 | |
461 | lc->io_req.mem.type = DM_IO_VMA; |
462 | lc->io_req.notify.fn = NULL; |
463 | lc->io_req.client = dm_io_client_create(); |
464 | if (IS_ERR(ptr: lc->io_req.client)) { |
465 | r = PTR_ERR(ptr: lc->io_req.client); |
466 | DMWARN("couldn't allocate disk io client" ); |
467 | kfree(objp: lc); |
468 | return r; |
469 | } |
470 | |
471 | lc->disk_header = vmalloc(size: buf_size); |
472 | if (!lc->disk_header) { |
473 | DMWARN("couldn't allocate disk log buffer" ); |
474 | dm_io_client_destroy(client: lc->io_req.client); |
475 | kfree(objp: lc); |
476 | return -ENOMEM; |
477 | } |
478 | |
479 | lc->io_req.mem.ptr.vma = lc->disk_header; |
480 | lc->clean_bits = (void *)lc->disk_header + |
481 | (LOG_OFFSET << SECTOR_SHIFT); |
482 | } |
483 | |
484 | memset(lc->clean_bits, -1, bitset_size); |
485 | |
486 | lc->sync_bits = vmalloc(size: bitset_size); |
487 | if (!lc->sync_bits) { |
488 | DMWARN("couldn't allocate sync bitset" ); |
489 | if (!dev) |
490 | vfree(addr: lc->clean_bits); |
491 | else |
492 | dm_io_client_destroy(client: lc->io_req.client); |
493 | vfree(addr: lc->disk_header); |
494 | kfree(objp: lc); |
495 | return -ENOMEM; |
496 | } |
497 | memset(lc->sync_bits, (sync == NOSYNC) ? -1 : 0, bitset_size); |
498 | lc->sync_count = (sync == NOSYNC) ? region_count : 0; |
499 | |
500 | lc->recovering_bits = vzalloc(size: bitset_size); |
501 | if (!lc->recovering_bits) { |
502 | DMWARN("couldn't allocate sync bitset" ); |
503 | vfree(addr: lc->sync_bits); |
504 | if (!dev) |
505 | vfree(addr: lc->clean_bits); |
506 | else |
507 | dm_io_client_destroy(client: lc->io_req.client); |
508 | vfree(addr: lc->disk_header); |
509 | kfree(objp: lc); |
510 | return -ENOMEM; |
511 | } |
512 | lc->sync_search = 0; |
513 | log->context = lc; |
514 | |
515 | return 0; |
516 | } |
517 | |
518 | static int core_ctr(struct dm_dirty_log *log, struct dm_target *ti, |
519 | unsigned int argc, char **argv) |
520 | { |
521 | return create_log_context(log, ti, argc, argv, NULL); |
522 | } |
523 | |
524 | static void destroy_log_context(struct log_c *lc) |
525 | { |
526 | vfree(addr: lc->sync_bits); |
527 | vfree(addr: lc->recovering_bits); |
528 | kfree(objp: lc); |
529 | } |
530 | |
531 | static void core_dtr(struct dm_dirty_log *log) |
532 | { |
533 | struct log_c *lc = log->context; |
534 | |
535 | vfree(addr: lc->clean_bits); |
536 | destroy_log_context(lc); |
537 | } |
538 | |
539 | /* |
540 | *--------------------------------------------------------------------- |
541 | * disk log constructor/destructor |
542 | * |
543 | * argv contains log_device region_size followed optionally by [no]sync |
544 | *--------------------------------------------------------------------- |
545 | */ |
546 | static int disk_ctr(struct dm_dirty_log *log, struct dm_target *ti, |
547 | unsigned int argc, char **argv) |
548 | { |
549 | int r; |
550 | struct dm_dev *dev; |
551 | |
552 | if (argc < 2 || argc > 3) { |
553 | DMWARN("wrong number of arguments to disk dirty region log" ); |
554 | return -EINVAL; |
555 | } |
556 | |
557 | r = dm_get_device(ti, path: argv[0], mode: dm_table_get_mode(t: ti->table), result: &dev); |
558 | if (r) |
559 | return r; |
560 | |
561 | r = create_log_context(log, ti, argc: argc - 1, argv: argv + 1, dev); |
562 | if (r) { |
563 | dm_put_device(ti, d: dev); |
564 | return r; |
565 | } |
566 | |
567 | return 0; |
568 | } |
569 | |
570 | static void disk_dtr(struct dm_dirty_log *log) |
571 | { |
572 | struct log_c *lc = log->context; |
573 | |
574 | dm_put_device(ti: lc->ti, d: lc->log_dev); |
575 | vfree(addr: lc->disk_header); |
576 | dm_io_client_destroy(client: lc->io_req.client); |
577 | destroy_log_context(lc); |
578 | } |
579 | |
580 | static void fail_log_device(struct log_c *lc) |
581 | { |
582 | if (lc->log_dev_failed) |
583 | return; |
584 | |
585 | lc->log_dev_failed = 1; |
586 | dm_table_event(t: lc->ti->table); |
587 | } |
588 | |
589 | static int disk_resume(struct dm_dirty_log *log) |
590 | { |
591 | int r; |
592 | unsigned int i; |
593 | struct log_c *lc = log->context; |
594 | size_t size = lc->bitset_uint32_count * sizeof(uint32_t); |
595 | |
596 | /* read the disk header */ |
597 | r = read_header(log: lc); |
598 | if (r) { |
599 | DMWARN("%s: Failed to read header on dirty region log device" , |
600 | lc->log_dev->name); |
601 | fail_log_device(lc); |
602 | /* |
603 | * If the log device cannot be read, we must assume |
604 | * all regions are out-of-sync. If we simply return |
605 | * here, the state will be uninitialized and could |
606 | * lead us to return 'in-sync' status for regions |
607 | * that are actually 'out-of-sync'. |
608 | */ |
609 | lc->header.nr_regions = 0; |
610 | } |
611 | |
612 | /* set or clear any new bits -- device has grown */ |
613 | if (lc->sync == NOSYNC) |
614 | for (i = lc->header.nr_regions; i < lc->region_count; i++) |
615 | /* FIXME: amazingly inefficient */ |
616 | log_set_bit(l: lc, bs: lc->clean_bits, bit: i); |
617 | else |
618 | for (i = lc->header.nr_regions; i < lc->region_count; i++) |
619 | /* FIXME: amazingly inefficient */ |
620 | log_clear_bit(l: lc, bs: lc->clean_bits, bit: i); |
621 | |
622 | /* clear any old bits -- device has shrunk */ |
623 | for (i = lc->region_count; i % BITS_PER_LONG; i++) |
624 | log_clear_bit(l: lc, bs: lc->clean_bits, bit: i); |
625 | |
626 | /* copy clean across to sync */ |
627 | memcpy(lc->sync_bits, lc->clean_bits, size); |
628 | lc->sync_count = memweight(ptr: lc->clean_bits, |
629 | bytes: lc->bitset_uint32_count * sizeof(uint32_t)); |
630 | lc->sync_search = 0; |
631 | |
632 | /* set the correct number of regions in the header */ |
633 | lc->header.nr_regions = lc->region_count; |
634 | |
635 | header_to_disk(core: &lc->header, disk: lc->disk_header); |
636 | |
637 | /* write the new header */ |
638 | r = rw_header(lc, op: REQ_OP_WRITE); |
639 | if (!r) { |
640 | r = flush_header(lc); |
641 | if (r) |
642 | lc->log_dev_flush_failed = 1; |
643 | } |
644 | if (r) { |
645 | DMWARN("%s: Failed to write header on dirty region log device" , |
646 | lc->log_dev->name); |
647 | fail_log_device(lc); |
648 | } |
649 | |
650 | return r; |
651 | } |
652 | |
653 | static uint32_t core_get_region_size(struct dm_dirty_log *log) |
654 | { |
655 | struct log_c *lc = log->context; |
656 | |
657 | return lc->region_size; |
658 | } |
659 | |
660 | static int core_resume(struct dm_dirty_log *log) |
661 | { |
662 | struct log_c *lc = log->context; |
663 | |
664 | lc->sync_search = 0; |
665 | return 0; |
666 | } |
667 | |
668 | static int core_is_clean(struct dm_dirty_log *log, region_t region) |
669 | { |
670 | struct log_c *lc = log->context; |
671 | |
672 | return log_test_bit(bs: lc->clean_bits, bit: region); |
673 | } |
674 | |
675 | static int core_in_sync(struct dm_dirty_log *log, region_t region, int block) |
676 | { |
677 | struct log_c *lc = log->context; |
678 | |
679 | return log_test_bit(bs: lc->sync_bits, bit: region); |
680 | } |
681 | |
682 | static int core_flush(struct dm_dirty_log *log) |
683 | { |
684 | /* no op */ |
685 | return 0; |
686 | } |
687 | |
688 | static int disk_flush(struct dm_dirty_log *log) |
689 | { |
690 | int r, i; |
691 | struct log_c *lc = log->context; |
692 | |
693 | /* only write if the log has changed */ |
694 | if (!lc->touched_cleaned && !lc->touched_dirtied) |
695 | return 0; |
696 | |
697 | if (lc->touched_cleaned && log->flush_callback_fn && |
698 | log->flush_callback_fn(lc->ti)) { |
699 | /* |
700 | * At this point it is impossible to determine which |
701 | * regions are clean and which are dirty (without |
702 | * re-reading the log off disk). So mark all of them |
703 | * dirty. |
704 | */ |
705 | lc->flush_failed = 1; |
706 | for (i = 0; i < lc->region_count; i++) |
707 | log_clear_bit(l: lc, bs: lc->clean_bits, bit: i); |
708 | } |
709 | |
710 | r = rw_header(lc, op: REQ_OP_WRITE); |
711 | if (r) |
712 | fail_log_device(lc); |
713 | else { |
714 | if (lc->touched_dirtied) { |
715 | r = flush_header(lc); |
716 | if (r) { |
717 | lc->log_dev_flush_failed = 1; |
718 | fail_log_device(lc); |
719 | } else |
720 | lc->touched_dirtied = 0; |
721 | } |
722 | lc->touched_cleaned = 0; |
723 | } |
724 | |
725 | return r; |
726 | } |
727 | |
728 | static void core_mark_region(struct dm_dirty_log *log, region_t region) |
729 | { |
730 | struct log_c *lc = log->context; |
731 | |
732 | log_clear_bit(l: lc, bs: lc->clean_bits, bit: region); |
733 | } |
734 | |
735 | static void core_clear_region(struct dm_dirty_log *log, region_t region) |
736 | { |
737 | struct log_c *lc = log->context; |
738 | |
739 | if (likely(!lc->flush_failed)) |
740 | log_set_bit(l: lc, bs: lc->clean_bits, bit: region); |
741 | } |
742 | |
743 | static int core_get_resync_work(struct dm_dirty_log *log, region_t *region) |
744 | { |
745 | struct log_c *lc = log->context; |
746 | |
747 | if (lc->sync_search >= lc->region_count) |
748 | return 0; |
749 | |
750 | do { |
751 | *region = find_next_zero_bit_le(addr: lc->sync_bits, |
752 | size: lc->region_count, |
753 | offset: lc->sync_search); |
754 | lc->sync_search = *region + 1; |
755 | |
756 | if (*region >= lc->region_count) |
757 | return 0; |
758 | |
759 | } while (log_test_bit(bs: lc->recovering_bits, bit: *region)); |
760 | |
761 | log_set_bit(l: lc, bs: lc->recovering_bits, bit: *region); |
762 | return 1; |
763 | } |
764 | |
765 | static void core_set_region_sync(struct dm_dirty_log *log, region_t region, |
766 | int in_sync) |
767 | { |
768 | struct log_c *lc = log->context; |
769 | |
770 | log_clear_bit(l: lc, bs: lc->recovering_bits, bit: region); |
771 | if (in_sync) { |
772 | log_set_bit(l: lc, bs: lc->sync_bits, bit: region); |
773 | lc->sync_count++; |
774 | } else if (log_test_bit(bs: lc->sync_bits, bit: region)) { |
775 | lc->sync_count--; |
776 | log_clear_bit(l: lc, bs: lc->sync_bits, bit: region); |
777 | } |
778 | } |
779 | |
780 | static region_t core_get_sync_count(struct dm_dirty_log *log) |
781 | { |
782 | struct log_c *lc = log->context; |
783 | |
784 | return lc->sync_count; |
785 | } |
786 | |
787 | #define DMEMIT_SYNC \ |
788 | do { \ |
789 | if (lc->sync != DEFAULTSYNC) \ |
790 | DMEMIT("%ssync ", lc->sync == NOSYNC ? "no" : ""); \ |
791 | } while (0) |
792 | |
793 | static int core_status(struct dm_dirty_log *log, status_type_t status, |
794 | char *result, unsigned int maxlen) |
795 | { |
796 | int sz = 0; |
797 | struct log_c *lc = log->context; |
798 | |
799 | switch (status) { |
800 | case STATUSTYPE_INFO: |
801 | DMEMIT("1 %s" , log->type->name); |
802 | break; |
803 | |
804 | case STATUSTYPE_TABLE: |
805 | DMEMIT("%s %u %u " , log->type->name, |
806 | lc->sync == DEFAULTSYNC ? 1 : 2, lc->region_size); |
807 | DMEMIT_SYNC; |
808 | break; |
809 | |
810 | case STATUSTYPE_IMA: |
811 | *result = '\0'; |
812 | break; |
813 | } |
814 | |
815 | return sz; |
816 | } |
817 | |
818 | static int disk_status(struct dm_dirty_log *log, status_type_t status, |
819 | char *result, unsigned int maxlen) |
820 | { |
821 | int sz = 0; |
822 | struct log_c *lc = log->context; |
823 | |
824 | switch (status) { |
825 | case STATUSTYPE_INFO: |
826 | DMEMIT("3 %s %s %c" , log->type->name, lc->log_dev->name, |
827 | lc->log_dev_flush_failed ? 'F' : |
828 | lc->log_dev_failed ? 'D' : |
829 | 'A'); |
830 | break; |
831 | |
832 | case STATUSTYPE_TABLE: |
833 | DMEMIT("%s %u %s %u " , log->type->name, |
834 | lc->sync == DEFAULTSYNC ? 2 : 3, lc->log_dev->name, |
835 | lc->region_size); |
836 | DMEMIT_SYNC; |
837 | break; |
838 | |
839 | case STATUSTYPE_IMA: |
840 | *result = '\0'; |
841 | break; |
842 | } |
843 | |
844 | return sz; |
845 | } |
846 | |
847 | static struct dm_dirty_log_type _core_type = { |
848 | .name = "core" , |
849 | .module = THIS_MODULE, |
850 | .ctr = core_ctr, |
851 | .dtr = core_dtr, |
852 | .resume = core_resume, |
853 | .get_region_size = core_get_region_size, |
854 | .is_clean = core_is_clean, |
855 | .in_sync = core_in_sync, |
856 | .flush = core_flush, |
857 | .mark_region = core_mark_region, |
858 | .clear_region = core_clear_region, |
859 | .get_resync_work = core_get_resync_work, |
860 | .set_region_sync = core_set_region_sync, |
861 | .get_sync_count = core_get_sync_count, |
862 | .status = core_status, |
863 | }; |
864 | |
865 | static struct dm_dirty_log_type _disk_type = { |
866 | .name = "disk" , |
867 | .module = THIS_MODULE, |
868 | .ctr = disk_ctr, |
869 | .dtr = disk_dtr, |
870 | .postsuspend = disk_flush, |
871 | .resume = disk_resume, |
872 | .get_region_size = core_get_region_size, |
873 | .is_clean = core_is_clean, |
874 | .in_sync = core_in_sync, |
875 | .flush = disk_flush, |
876 | .mark_region = core_mark_region, |
877 | .clear_region = core_clear_region, |
878 | .get_resync_work = core_get_resync_work, |
879 | .set_region_sync = core_set_region_sync, |
880 | .get_sync_count = core_get_sync_count, |
881 | .status = disk_status, |
882 | }; |
883 | |
884 | static int __init dm_dirty_log_init(void) |
885 | { |
886 | int r; |
887 | |
888 | r = dm_dirty_log_type_register(&_core_type); |
889 | if (r) |
890 | DMWARN("couldn't register core log" ); |
891 | |
892 | r = dm_dirty_log_type_register(&_disk_type); |
893 | if (r) { |
894 | DMWARN("couldn't register disk type" ); |
895 | dm_dirty_log_type_unregister(&_core_type); |
896 | } |
897 | |
898 | return r; |
899 | } |
900 | |
901 | static void __exit dm_dirty_log_exit(void) |
902 | { |
903 | dm_dirty_log_type_unregister(&_disk_type); |
904 | dm_dirty_log_type_unregister(&_core_type); |
905 | } |
906 | |
907 | module_init(dm_dirty_log_init); |
908 | module_exit(dm_dirty_log_exit); |
909 | |
910 | MODULE_DESCRIPTION(DM_NAME " dirty region log" ); |
911 | MODULE_AUTHOR("Joe Thornber, Heinz Mauelshagen <dm-devel@redhat.com>" ); |
912 | MODULE_LICENSE("GPL" ); |
913 | |