This source file includes following definitions.
- dm_rh_sector_to_region
- dm_rh_region_to_sector
- dm_rh_bio_to_region
- dm_rh_region_context
- dm_rh_get_region_key
- dm_rh_get_region_size
- dm_region_hash_create
- dm_region_hash_destroy
- dm_rh_dirty_log
- rh_hash
- __rh_lookup
- __rh_insert
- __rh_alloc
- __rh_find
- dm_rh_get_state
- complete_resync_work
- dm_rh_mark_nosync
- dm_rh_update_states
- rh_inc
- dm_rh_inc_pending
- dm_rh_dec
- __rh_recovery_prepare
- dm_rh_recovery_prepare
- dm_rh_recovery_start
- dm_rh_recovery_end
- dm_rh_recovery_in_flight
- dm_rh_flush
- dm_rh_delay
- dm_rh_stop_recovery
- dm_rh_start_recovery
1
2
3
4
5
6
7
8 #include <linux/dm-dirty-log.h>
9 #include <linux/dm-region-hash.h>
10
11 #include <linux/ctype.h>
12 #include <linux/init.h>
13 #include <linux/module.h>
14 #include <linux/slab.h>
15 #include <linux/vmalloc.h>
16
17 #include "dm.h"
18
19 #define DM_MSG_PREFIX "region hash"
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57 struct dm_region_hash {
58 uint32_t region_size;
59 unsigned region_shift;
60
61
62 struct dm_dirty_log *log;
63
64
65 rwlock_t hash_lock;
66 unsigned mask;
67 unsigned nr_buckets;
68 unsigned prime;
69 unsigned shift;
70 struct list_head *buckets;
71
72
73
74
75 int flush_failure;
76
77 unsigned max_recovery;
78
79 spinlock_t region_lock;
80 atomic_t recovery_in_flight;
81 struct list_head clean_regions;
82 struct list_head quiesced_regions;
83 struct list_head recovered_regions;
84 struct list_head failed_recovered_regions;
85 struct semaphore recovery_count;
86
87 mempool_t region_pool;
88
89 void *context;
90 sector_t target_begin;
91
92
93 void (*dispatch_bios)(void *context, struct bio_list *bios);
94
95
96 void (*wakeup_workers)(void *context);
97
98
99 void (*wakeup_all_recovery_waiters)(void *context);
100 };
101
102 struct dm_region {
103 struct dm_region_hash *rh;
104 region_t key;
105 int state;
106
107 struct list_head hash_list;
108 struct list_head list;
109
110 atomic_t pending;
111 struct bio_list delayed_bios;
112 };
113
114
115
116
117 static region_t dm_rh_sector_to_region(struct dm_region_hash *rh, sector_t sector)
118 {
119 return sector >> rh->region_shift;
120 }
121
122 sector_t dm_rh_region_to_sector(struct dm_region_hash *rh, region_t region)
123 {
124 return region << rh->region_shift;
125 }
126 EXPORT_SYMBOL_GPL(dm_rh_region_to_sector);
127
128 region_t dm_rh_bio_to_region(struct dm_region_hash *rh, struct bio *bio)
129 {
130 return dm_rh_sector_to_region(rh, bio->bi_iter.bi_sector -
131 rh->target_begin);
132 }
133 EXPORT_SYMBOL_GPL(dm_rh_bio_to_region);
134
135 void *dm_rh_region_context(struct dm_region *reg)
136 {
137 return reg->rh->context;
138 }
139 EXPORT_SYMBOL_GPL(dm_rh_region_context);
140
141 region_t dm_rh_get_region_key(struct dm_region *reg)
142 {
143 return reg->key;
144 }
145 EXPORT_SYMBOL_GPL(dm_rh_get_region_key);
146
147 sector_t dm_rh_get_region_size(struct dm_region_hash *rh)
148 {
149 return rh->region_size;
150 }
151 EXPORT_SYMBOL_GPL(dm_rh_get_region_size);
152
153
154
155
156
157 #define RH_HASH_MULT 2654435387U
158 #define RH_HASH_SHIFT 12
159
160 #define MIN_REGIONS 64
161 struct dm_region_hash *dm_region_hash_create(
162 void *context, void (*dispatch_bios)(void *context,
163 struct bio_list *bios),
164 void (*wakeup_workers)(void *context),
165 void (*wakeup_all_recovery_waiters)(void *context),
166 sector_t target_begin, unsigned max_recovery,
167 struct dm_dirty_log *log, uint32_t region_size,
168 region_t nr_regions)
169 {
170 struct dm_region_hash *rh;
171 unsigned nr_buckets, max_buckets;
172 size_t i;
173 int ret;
174
175
176
177
178
179 max_buckets = nr_regions >> 6;
180 for (nr_buckets = 128u; nr_buckets < max_buckets; nr_buckets <<= 1)
181 ;
182 nr_buckets >>= 1;
183
184 rh = kzalloc(sizeof(*rh), GFP_KERNEL);
185 if (!rh) {
186 DMERR("unable to allocate region hash memory");
187 return ERR_PTR(-ENOMEM);
188 }
189
190 rh->context = context;
191 rh->dispatch_bios = dispatch_bios;
192 rh->wakeup_workers = wakeup_workers;
193 rh->wakeup_all_recovery_waiters = wakeup_all_recovery_waiters;
194 rh->target_begin = target_begin;
195 rh->max_recovery = max_recovery;
196 rh->log = log;
197 rh->region_size = region_size;
198 rh->region_shift = __ffs(region_size);
199 rwlock_init(&rh->hash_lock);
200 rh->mask = nr_buckets - 1;
201 rh->nr_buckets = nr_buckets;
202
203 rh->shift = RH_HASH_SHIFT;
204 rh->prime = RH_HASH_MULT;
205
206 rh->buckets = vmalloc(array_size(nr_buckets, sizeof(*rh->buckets)));
207 if (!rh->buckets) {
208 DMERR("unable to allocate region hash bucket memory");
209 kfree(rh);
210 return ERR_PTR(-ENOMEM);
211 }
212
213 for (i = 0; i < nr_buckets; i++)
214 INIT_LIST_HEAD(rh->buckets + i);
215
216 spin_lock_init(&rh->region_lock);
217 sema_init(&rh->recovery_count, 0);
218 atomic_set(&rh->recovery_in_flight, 0);
219 INIT_LIST_HEAD(&rh->clean_regions);
220 INIT_LIST_HEAD(&rh->quiesced_regions);
221 INIT_LIST_HEAD(&rh->recovered_regions);
222 INIT_LIST_HEAD(&rh->failed_recovered_regions);
223 rh->flush_failure = 0;
224
225 ret = mempool_init_kmalloc_pool(&rh->region_pool, MIN_REGIONS,
226 sizeof(struct dm_region));
227 if (ret) {
228 vfree(rh->buckets);
229 kfree(rh);
230 rh = ERR_PTR(-ENOMEM);
231 }
232
233 return rh;
234 }
235 EXPORT_SYMBOL_GPL(dm_region_hash_create);
236
237 void dm_region_hash_destroy(struct dm_region_hash *rh)
238 {
239 unsigned h;
240 struct dm_region *reg, *nreg;
241
242 BUG_ON(!list_empty(&rh->quiesced_regions));
243 for (h = 0; h < rh->nr_buckets; h++) {
244 list_for_each_entry_safe(reg, nreg, rh->buckets + h,
245 hash_list) {
246 BUG_ON(atomic_read(®->pending));
247 mempool_free(reg, &rh->region_pool);
248 }
249 }
250
251 if (rh->log)
252 dm_dirty_log_destroy(rh->log);
253
254 mempool_exit(&rh->region_pool);
255 vfree(rh->buckets);
256 kfree(rh);
257 }
258 EXPORT_SYMBOL_GPL(dm_region_hash_destroy);
259
260 struct dm_dirty_log *dm_rh_dirty_log(struct dm_region_hash *rh)
261 {
262 return rh->log;
263 }
264 EXPORT_SYMBOL_GPL(dm_rh_dirty_log);
265
266 static unsigned rh_hash(struct dm_region_hash *rh, region_t region)
267 {
268 return (unsigned) ((region * rh->prime) >> rh->shift) & rh->mask;
269 }
270
271 static struct dm_region *__rh_lookup(struct dm_region_hash *rh, region_t region)
272 {
273 struct dm_region *reg;
274 struct list_head *bucket = rh->buckets + rh_hash(rh, region);
275
276 list_for_each_entry(reg, bucket, hash_list)
277 if (reg->key == region)
278 return reg;
279
280 return NULL;
281 }
282
283 static void __rh_insert(struct dm_region_hash *rh, struct dm_region *reg)
284 {
285 list_add(®->hash_list, rh->buckets + rh_hash(rh, reg->key));
286 }
287
288 static struct dm_region *__rh_alloc(struct dm_region_hash *rh, region_t region)
289 {
290 struct dm_region *reg, *nreg;
291
292 nreg = mempool_alloc(&rh->region_pool, GFP_ATOMIC);
293 if (unlikely(!nreg))
294 nreg = kmalloc(sizeof(*nreg), GFP_NOIO | __GFP_NOFAIL);
295
296 nreg->state = rh->log->type->in_sync(rh->log, region, 1) ?
297 DM_RH_CLEAN : DM_RH_NOSYNC;
298 nreg->rh = rh;
299 nreg->key = region;
300 INIT_LIST_HEAD(&nreg->list);
301 atomic_set(&nreg->pending, 0);
302 bio_list_init(&nreg->delayed_bios);
303
304 write_lock_irq(&rh->hash_lock);
305 reg = __rh_lookup(rh, region);
306 if (reg)
307
308 mempool_free(nreg, &rh->region_pool);
309 else {
310 __rh_insert(rh, nreg);
311 if (nreg->state == DM_RH_CLEAN) {
312 spin_lock(&rh->region_lock);
313 list_add(&nreg->list, &rh->clean_regions);
314 spin_unlock(&rh->region_lock);
315 }
316
317 reg = nreg;
318 }
319 write_unlock_irq(&rh->hash_lock);
320
321 return reg;
322 }
323
324 static struct dm_region *__rh_find(struct dm_region_hash *rh, region_t region)
325 {
326 struct dm_region *reg;
327
328 reg = __rh_lookup(rh, region);
329 if (!reg) {
330 read_unlock(&rh->hash_lock);
331 reg = __rh_alloc(rh, region);
332 read_lock(&rh->hash_lock);
333 }
334
335 return reg;
336 }
337
338 int dm_rh_get_state(struct dm_region_hash *rh, region_t region, int may_block)
339 {
340 int r;
341 struct dm_region *reg;
342
343 read_lock(&rh->hash_lock);
344 reg = __rh_lookup(rh, region);
345 read_unlock(&rh->hash_lock);
346
347 if (reg)
348 return reg->state;
349
350
351
352
353
354 r = rh->log->type->in_sync(rh->log, region, may_block);
355
356
357
358
359
360 return r == 1 ? DM_RH_CLEAN : DM_RH_NOSYNC;
361 }
362 EXPORT_SYMBOL_GPL(dm_rh_get_state);
363
364 static void complete_resync_work(struct dm_region *reg, int success)
365 {
366 struct dm_region_hash *rh = reg->rh;
367
368 rh->log->type->set_region_sync(rh->log, reg->key, success);
369
370
371
372
373
374
375
376
377
378
379 rh->dispatch_bios(rh->context, ®->delayed_bios);
380 if (atomic_dec_and_test(&rh->recovery_in_flight))
381 rh->wakeup_all_recovery_waiters(rh->context);
382 up(&rh->recovery_count);
383 }
384
385
386
387
388
389
390
391
392
393
394
395 void dm_rh_mark_nosync(struct dm_region_hash *rh, struct bio *bio)
396 {
397 unsigned long flags;
398 struct dm_dirty_log *log = rh->log;
399 struct dm_region *reg;
400 region_t region = dm_rh_bio_to_region(rh, bio);
401 int recovering = 0;
402
403 if (bio->bi_opf & REQ_PREFLUSH) {
404 rh->flush_failure = 1;
405 return;
406 }
407
408 if (bio_op(bio) == REQ_OP_DISCARD)
409 return;
410
411
412 log->type->set_region_sync(log, region, 0);
413
414 read_lock(&rh->hash_lock);
415 reg = __rh_find(rh, region);
416 read_unlock(&rh->hash_lock);
417
418
419 BUG_ON(!reg);
420 BUG_ON(!list_empty(®->list));
421
422 spin_lock_irqsave(&rh->region_lock, flags);
423
424
425
426
427
428
429
430 recovering = (reg->state == DM_RH_RECOVERING);
431 reg->state = DM_RH_NOSYNC;
432 BUG_ON(!list_empty(®->list));
433 spin_unlock_irqrestore(&rh->region_lock, flags);
434
435 if (recovering)
436 complete_resync_work(reg, 0);
437 }
438 EXPORT_SYMBOL_GPL(dm_rh_mark_nosync);
439
440 void dm_rh_update_states(struct dm_region_hash *rh, int errors_handled)
441 {
442 struct dm_region *reg, *next;
443
444 LIST_HEAD(clean);
445 LIST_HEAD(recovered);
446 LIST_HEAD(failed_recovered);
447
448
449
450
451 write_lock_irq(&rh->hash_lock);
452 spin_lock(&rh->region_lock);
453 if (!list_empty(&rh->clean_regions)) {
454 list_splice_init(&rh->clean_regions, &clean);
455
456 list_for_each_entry(reg, &clean, list)
457 list_del(®->hash_list);
458 }
459
460 if (!list_empty(&rh->recovered_regions)) {
461 list_splice_init(&rh->recovered_regions, &recovered);
462
463 list_for_each_entry(reg, &recovered, list)
464 list_del(®->hash_list);
465 }
466
467 if (!list_empty(&rh->failed_recovered_regions)) {
468 list_splice_init(&rh->failed_recovered_regions,
469 &failed_recovered);
470
471 list_for_each_entry(reg, &failed_recovered, list)
472 list_del(®->hash_list);
473 }
474
475 spin_unlock(&rh->region_lock);
476 write_unlock_irq(&rh->hash_lock);
477
478
479
480
481
482
483 list_for_each_entry_safe(reg, next, &recovered, list) {
484 rh->log->type->clear_region(rh->log, reg->key);
485 complete_resync_work(reg, 1);
486 mempool_free(reg, &rh->region_pool);
487 }
488
489 list_for_each_entry_safe(reg, next, &failed_recovered, list) {
490 complete_resync_work(reg, errors_handled ? 0 : 1);
491 mempool_free(reg, &rh->region_pool);
492 }
493
494 list_for_each_entry_safe(reg, next, &clean, list) {
495 rh->log->type->clear_region(rh->log, reg->key);
496 mempool_free(reg, &rh->region_pool);
497 }
498
499 rh->log->type->flush(rh->log);
500 }
501 EXPORT_SYMBOL_GPL(dm_rh_update_states);
502
503 static void rh_inc(struct dm_region_hash *rh, region_t region)
504 {
505 struct dm_region *reg;
506
507 read_lock(&rh->hash_lock);
508 reg = __rh_find(rh, region);
509
510 spin_lock_irq(&rh->region_lock);
511 atomic_inc(®->pending);
512
513 if (reg->state == DM_RH_CLEAN) {
514 reg->state = DM_RH_DIRTY;
515 list_del_init(®->list);
516 spin_unlock_irq(&rh->region_lock);
517
518 rh->log->type->mark_region(rh->log, reg->key);
519 } else
520 spin_unlock_irq(&rh->region_lock);
521
522
523 read_unlock(&rh->hash_lock);
524 }
525
526 void dm_rh_inc_pending(struct dm_region_hash *rh, struct bio_list *bios)
527 {
528 struct bio *bio;
529
530 for (bio = bios->head; bio; bio = bio->bi_next) {
531 if (bio->bi_opf & REQ_PREFLUSH || bio_op(bio) == REQ_OP_DISCARD)
532 continue;
533 rh_inc(rh, dm_rh_bio_to_region(rh, bio));
534 }
535 }
536 EXPORT_SYMBOL_GPL(dm_rh_inc_pending);
537
538 void dm_rh_dec(struct dm_region_hash *rh, region_t region)
539 {
540 unsigned long flags;
541 struct dm_region *reg;
542 int should_wake = 0;
543
544 read_lock(&rh->hash_lock);
545 reg = __rh_lookup(rh, region);
546 read_unlock(&rh->hash_lock);
547
548 spin_lock_irqsave(&rh->region_lock, flags);
549 if (atomic_dec_and_test(®->pending)) {
550
551
552
553
554
555
556
557
558
559
560
561
562 if (unlikely(rh->flush_failure)) {
563
564
565
566
567
568 reg->state = DM_RH_NOSYNC;
569 } else if (reg->state == DM_RH_RECOVERING) {
570 list_add_tail(®->list, &rh->quiesced_regions);
571 } else if (reg->state == DM_RH_DIRTY) {
572 reg->state = DM_RH_CLEAN;
573 list_add(®->list, &rh->clean_regions);
574 }
575 should_wake = 1;
576 }
577 spin_unlock_irqrestore(&rh->region_lock, flags);
578
579 if (should_wake)
580 rh->wakeup_workers(rh->context);
581 }
582 EXPORT_SYMBOL_GPL(dm_rh_dec);
583
584
585
586
587 static int __rh_recovery_prepare(struct dm_region_hash *rh)
588 {
589 int r;
590 region_t region;
591 struct dm_region *reg;
592
593
594
595
596 r = rh->log->type->get_resync_work(rh->log, ®ion);
597 if (r <= 0)
598 return r;
599
600
601
602
603
604 read_lock(&rh->hash_lock);
605 reg = __rh_find(rh, region);
606 read_unlock(&rh->hash_lock);
607
608 spin_lock_irq(&rh->region_lock);
609 reg->state = DM_RH_RECOVERING;
610
611
612 if (atomic_read(®->pending))
613 list_del_init(®->list);
614 else
615 list_move(®->list, &rh->quiesced_regions);
616
617 spin_unlock_irq(&rh->region_lock);
618
619 return 1;
620 }
621
622 void dm_rh_recovery_prepare(struct dm_region_hash *rh)
623 {
624
625 atomic_inc(&rh->recovery_in_flight);
626
627 while (!down_trylock(&rh->recovery_count)) {
628 atomic_inc(&rh->recovery_in_flight);
629 if (__rh_recovery_prepare(rh) <= 0) {
630 atomic_dec(&rh->recovery_in_flight);
631 up(&rh->recovery_count);
632 break;
633 }
634 }
635
636
637 if (atomic_dec_and_test(&rh->recovery_in_flight))
638 rh->wakeup_all_recovery_waiters(rh->context);
639 }
640 EXPORT_SYMBOL_GPL(dm_rh_recovery_prepare);
641
642
643
644
645 struct dm_region *dm_rh_recovery_start(struct dm_region_hash *rh)
646 {
647 struct dm_region *reg = NULL;
648
649 spin_lock_irq(&rh->region_lock);
650 if (!list_empty(&rh->quiesced_regions)) {
651 reg = list_entry(rh->quiesced_regions.next,
652 struct dm_region, list);
653 list_del_init(®->list);
654 }
655 spin_unlock_irq(&rh->region_lock);
656
657 return reg;
658 }
659 EXPORT_SYMBOL_GPL(dm_rh_recovery_start);
660
661 void dm_rh_recovery_end(struct dm_region *reg, int success)
662 {
663 struct dm_region_hash *rh = reg->rh;
664
665 spin_lock_irq(&rh->region_lock);
666 if (success)
667 list_add(®->list, ®->rh->recovered_regions);
668 else
669 list_add(®->list, ®->rh->failed_recovered_regions);
670
671 spin_unlock_irq(&rh->region_lock);
672
673 rh->wakeup_workers(rh->context);
674 }
675 EXPORT_SYMBOL_GPL(dm_rh_recovery_end);
676
677
678 int dm_rh_recovery_in_flight(struct dm_region_hash *rh)
679 {
680 return atomic_read(&rh->recovery_in_flight);
681 }
682 EXPORT_SYMBOL_GPL(dm_rh_recovery_in_flight);
683
684 int dm_rh_flush(struct dm_region_hash *rh)
685 {
686 return rh->log->type->flush(rh->log);
687 }
688 EXPORT_SYMBOL_GPL(dm_rh_flush);
689
690 void dm_rh_delay(struct dm_region_hash *rh, struct bio *bio)
691 {
692 struct dm_region *reg;
693
694 read_lock(&rh->hash_lock);
695 reg = __rh_find(rh, dm_rh_bio_to_region(rh, bio));
696 bio_list_add(®->delayed_bios, bio);
697 read_unlock(&rh->hash_lock);
698 }
699 EXPORT_SYMBOL_GPL(dm_rh_delay);
700
701 void dm_rh_stop_recovery(struct dm_region_hash *rh)
702 {
703 int i;
704
705
706 for (i = 0; i < rh->max_recovery; i++)
707 down(&rh->recovery_count);
708 }
709 EXPORT_SYMBOL_GPL(dm_rh_stop_recovery);
710
711 void dm_rh_start_recovery(struct dm_region_hash *rh)
712 {
713 int i;
714
715 for (i = 0; i < rh->max_recovery; i++)
716 up(&rh->recovery_count);
717
718 rh->wakeup_workers(rh->context);
719 }
720 EXPORT_SYMBOL_GPL(dm_rh_start_recovery);
721
722 MODULE_DESCRIPTION(DM_NAME " region hash");
723 MODULE_AUTHOR("Joe Thornber/Heinz Mauelshagen <dm-devel@redhat.com>");
724 MODULE_LICENSE("GPL");