1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2009-2010 The FreeBSD Foundation
5 * All rights reserved.
6 *
7 * This software was developed by Pawel Jakub Dawidek under sponsorship from
8 * the FreeBSD Foundation.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/param.h> /* powerof2() */
36 #include <sys/queue.h>
37
38 #include <bitstring.h>
39 #include <errno.h>
40 #include <stdint.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44
45 #include <pjdlog.h>
46
47 #include "activemap.h"
48
49 #ifndef PJDLOG_ASSERT
50 #include <assert.h>
51 #define PJDLOG_ASSERT(...) assert(__VA_ARGS__)
52 #endif
53
54 #define ACTIVEMAP_MAGIC 0xac71e4
55 struct activemap {
56 int am_magic; /* Magic value. */
57 off_t am_mediasize; /* Media size in bytes. */
58 uint32_t am_extentsize; /* Extent size in bytes,
59 must be power of 2. */
60 uint8_t am_extentshift;/* 2 ^ extentbits == extentsize */
61 int am_nextents; /* Number of extents. */
62 size_t am_mapsize; /* Bitmap size in bytes. */
63 uint16_t *am_memtab; /* An array that holds number of pending
64 writes per extent. */
65 bitstr_t *am_diskmap; /* On-disk bitmap of dirty extents. */
66 bitstr_t *am_memmap; /* In-memory bitmap of dirty extents. */
67 size_t am_diskmapsize; /* Map size rounded up to sector size. */
68 uint64_t am_ndirty; /* Number of dirty regions. */
69 bitstr_t *am_syncmap; /* Bitmap of extents to sync. */
70 off_t am_syncoff; /* Next synchronization offset. */
71 TAILQ_HEAD(skeepdirty, keepdirty) am_keepdirty; /* List of extents that
72 we keep dirty to reduce bitmap
73 updates. */
74 int am_nkeepdirty; /* Number of am_keepdirty elements. */
75 int am_nkeepdirty_limit; /* Maximum number of am_keepdirty
76 elements. */
77 };
78
79 struct keepdirty {
80 int kd_extent;
81 TAILQ_ENTRY(keepdirty) kd_next;
82 };
83
84 /*
85 * Helper function taken from sys/systm.h to calculate extentshift.
86 */
87 static uint32_t
bitcount32(uint32_t x)88 bitcount32(uint32_t x)
89 {
90
91 x = (x & 0x55555555) + ((x & 0xaaaaaaaa) >> 1);
92 x = (x & 0x33333333) + ((x & 0xcccccccc) >> 2);
93 x = (x + (x >> 4)) & 0x0f0f0f0f;
94 x = (x + (x >> 8));
95 x = (x + (x >> 16)) & 0x000000ff;
96 return (x);
97 }
98
99 static __inline int
off2ext(const struct activemap * amp,off_t offset)100 off2ext(const struct activemap *amp, off_t offset)
101 {
102 int extent;
103
104 PJDLOG_ASSERT(offset >= 0 && offset < amp->am_mediasize);
105 extent = (offset >> amp->am_extentshift);
106 PJDLOG_ASSERT(extent >= 0 && extent < amp->am_nextents);
107 return (extent);
108 }
109
110 static __inline off_t
ext2off(const struct activemap * amp,int extent)111 ext2off(const struct activemap *amp, int extent)
112 {
113 off_t offset;
114
115 PJDLOG_ASSERT(extent >= 0 && extent < amp->am_nextents);
116 offset = ((off_t)extent << amp->am_extentshift);
117 PJDLOG_ASSERT(offset >= 0 && offset < amp->am_mediasize);
118 return (offset);
119 }
120
121 /*
122 * Function calculates number of requests needed to synchronize the given
123 * extent.
124 */
125 static __inline int
ext2reqs(const struct activemap * amp,int ext)126 ext2reqs(const struct activemap *amp, int ext)
127 {
128 off_t left;
129
130 if (ext < amp->am_nextents - 1)
131 return (((amp->am_extentsize - 1) / MAXPHYS) + 1);
132
133 PJDLOG_ASSERT(ext == amp->am_nextents - 1);
134 left = amp->am_mediasize % amp->am_extentsize;
135 if (left == 0)
136 left = amp->am_extentsize;
137 return (((left - 1) / MAXPHYS) + 1);
138 }
139
140 /*
141 * Initialize activemap structure and allocate memory for internal needs.
142 * Function returns 0 on success and -1 if any of the allocations failed.
143 */
144 int
activemap_init(struct activemap ** ampp,uint64_t mediasize,uint32_t extentsize,uint32_t sectorsize,uint32_t keepdirty)145 activemap_init(struct activemap **ampp, uint64_t mediasize, uint32_t extentsize,
146 uint32_t sectorsize, uint32_t keepdirty)
147 {
148 struct activemap *amp;
149
150 PJDLOG_ASSERT(ampp != NULL);
151 PJDLOG_ASSERT(mediasize > 0);
152 PJDLOG_ASSERT(extentsize > 0);
153 PJDLOG_ASSERT(powerof2(extentsize));
154 PJDLOG_ASSERT(sectorsize > 0);
155 PJDLOG_ASSERT(powerof2(sectorsize));
156 PJDLOG_ASSERT(keepdirty > 0);
157
158 amp = malloc(sizeof(*amp));
159 if (amp == NULL)
160 return (-1);
161
162 amp->am_mediasize = mediasize;
163 amp->am_nkeepdirty_limit = keepdirty;
164 amp->am_extentsize = extentsize;
165 amp->am_extentshift = bitcount32(extentsize - 1);
166 amp->am_nextents = ((mediasize - 1) / extentsize) + 1;
167 amp->am_mapsize = bitstr_size(amp->am_nextents);
168 amp->am_diskmapsize = roundup2(amp->am_mapsize, sectorsize);
169 amp->am_ndirty = 0;
170 amp->am_syncoff = -2;
171 TAILQ_INIT(&->am_keepdirty);
172 amp->am_nkeepdirty = 0;
173
174 amp->am_memtab = calloc(amp->am_nextents, sizeof(amp->am_memtab[0]));
175 amp->am_diskmap = calloc(1, amp->am_diskmapsize);
176 amp->am_memmap = bit_alloc(amp->am_nextents);
177 amp->am_syncmap = bit_alloc(amp->am_nextents);
178
179 /*
180 * Check to see if any of the allocations above failed.
181 */
182 if (amp->am_memtab == NULL || amp->am_diskmap == NULL ||
183 amp->am_memmap == NULL || amp->am_syncmap == NULL) {
184 if (amp->am_memtab != NULL)
185 free(amp->am_memtab);
186 if (amp->am_diskmap != NULL)
187 free(amp->am_diskmap);
188 if (amp->am_memmap != NULL)
189 free(amp->am_memmap);
190 if (amp->am_syncmap != NULL)
191 free(amp->am_syncmap);
192 amp->am_magic = 0;
193 free(amp);
194 errno = ENOMEM;
195 return (-1);
196 }
197
198 amp->am_magic = ACTIVEMAP_MAGIC;
199 *ampp = amp;
200
201 return (0);
202 }
203
204 static struct keepdirty *
keepdirty_find(struct activemap * amp,int extent)205 keepdirty_find(struct activemap *amp, int extent)
206 {
207 struct keepdirty *kd;
208
209 TAILQ_FOREACH(kd, &->am_keepdirty, kd_next) {
210 if (kd->kd_extent == extent)
211 break;
212 }
213 return (kd);
214 }
215
216 static bool
keepdirty_add(struct activemap * amp,int extent)217 keepdirty_add(struct activemap *amp, int extent)
218 {
219 struct keepdirty *kd;
220
221 kd = keepdirty_find(amp, extent);
222 if (kd != NULL) {
223 /*
224 * Only move element at the beginning.
225 */
226 TAILQ_REMOVE(&->am_keepdirty, kd, kd_next);
227 TAILQ_INSERT_HEAD(&->am_keepdirty, kd, kd_next);
228 return (false);
229 }
230 /*
231 * Add new element, but first remove the most unused one if
232 * we have too many.
233 */
234 if (amp->am_nkeepdirty >= amp->am_nkeepdirty_limit) {
235 kd = TAILQ_LAST(&->am_keepdirty, skeepdirty);
236 PJDLOG_ASSERT(kd != NULL);
237 TAILQ_REMOVE(&->am_keepdirty, kd, kd_next);
238 amp->am_nkeepdirty--;
239 PJDLOG_ASSERT(amp->am_nkeepdirty > 0);
240 }
241 if (kd == NULL)
242 kd = malloc(sizeof(*kd));
243 /* We can ignore allocation failure. */
244 if (kd != NULL) {
245 kd->kd_extent = extent;
246 amp->am_nkeepdirty++;
247 TAILQ_INSERT_HEAD(&->am_keepdirty, kd, kd_next);
248 }
249
250 return (true);
251 }
252
253 static void
keepdirty_fill(struct activemap * amp)254 keepdirty_fill(struct activemap *amp)
255 {
256 struct keepdirty *kd;
257
258 TAILQ_FOREACH(kd, &->am_keepdirty, kd_next)
259 bit_set(amp->am_diskmap, kd->kd_extent);
260 }
261
262 static void
keepdirty_free(struct activemap * amp)263 keepdirty_free(struct activemap *amp)
264 {
265 struct keepdirty *kd;
266
267 while ((kd = TAILQ_FIRST(&->am_keepdirty)) != NULL) {
268 TAILQ_REMOVE(&->am_keepdirty, kd, kd_next);
269 amp->am_nkeepdirty--;
270 free(kd);
271 }
272 PJDLOG_ASSERT(amp->am_nkeepdirty == 0);
273 }
274
275 /*
276 * Function frees resources allocated by activemap_init() function.
277 */
278 void
activemap_free(struct activemap * amp)279 activemap_free(struct activemap *amp)
280 {
281
282 PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC);
283
284 amp->am_magic = 0;
285
286 keepdirty_free(amp);
287 free(amp->am_memtab);
288 free(amp->am_diskmap);
289 free(amp->am_memmap);
290 free(amp->am_syncmap);
291 }
292
293 /*
294 * Function should be called before we handle write requests. It updates
295 * internal structures and returns true if on-disk metadata should be updated.
296 */
297 bool
activemap_write_start(struct activemap * amp,off_t offset,off_t length)298 activemap_write_start(struct activemap *amp, off_t offset, off_t length)
299 {
300 bool modified;
301 off_t end;
302 int ext;
303
304 PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC);
305 PJDLOG_ASSERT(length > 0);
306
307 modified = false;
308 end = offset + length - 1;
309
310 for (ext = off2ext(amp, offset); ext <= off2ext(amp, end); ext++) {
311 /*
312 * If the number of pending writes is increased from 0,
313 * we have to mark the extent as dirty also in on-disk bitmap.
314 * By returning true we inform the caller that on-disk bitmap
315 * was modified and has to be flushed to disk.
316 */
317 if (amp->am_memtab[ext]++ == 0) {
318 PJDLOG_ASSERT(!bit_test(amp->am_memmap, ext));
319 bit_set(amp->am_memmap, ext);
320 amp->am_ndirty++;
321 }
322 if (keepdirty_add(amp, ext))
323 modified = true;
324 }
325
326 return (modified);
327 }
328
329 /*
330 * Function should be called after receiving write confirmation. It updates
331 * internal structures and returns true if on-disk metadata should be updated.
332 */
333 bool
activemap_write_complete(struct activemap * amp,off_t offset,off_t length)334 activemap_write_complete(struct activemap *amp, off_t offset, off_t length)
335 {
336 bool modified;
337 off_t end;
338 int ext;
339
340 PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC);
341 PJDLOG_ASSERT(length > 0);
342
343 modified = false;
344 end = offset + length - 1;
345
346 for (ext = off2ext(amp, offset); ext <= off2ext(amp, end); ext++) {
347 /*
348 * If the number of pending writes goes down to 0, we have to
349 * mark the extent as clean also in on-disk bitmap.
350 * By returning true we inform the caller that on-disk bitmap
351 * was modified and has to be flushed to disk.
352 */
353 PJDLOG_ASSERT(amp->am_memtab[ext] > 0);
354 PJDLOG_ASSERT(bit_test(amp->am_memmap, ext));
355 if (--amp->am_memtab[ext] == 0) {
356 bit_clear(amp->am_memmap, ext);
357 amp->am_ndirty--;
358 if (keepdirty_find(amp, ext) == NULL)
359 modified = true;
360 }
361 }
362
363 return (modified);
364 }
365
366 /*
367 * Function should be called after finishing synchronization of one extent.
368 * It returns true if on-disk metadata should be updated.
369 */
370 bool
activemap_extent_complete(struct activemap * amp,int extent)371 activemap_extent_complete(struct activemap *amp, int extent)
372 {
373 bool modified;
374 int reqs;
375
376 PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC);
377 PJDLOG_ASSERT(extent >= 0 && extent < amp->am_nextents);
378
379 modified = false;
380
381 reqs = ext2reqs(amp, extent);
382 PJDLOG_ASSERT(amp->am_memtab[extent] >= reqs);
383 amp->am_memtab[extent] -= reqs;
384 PJDLOG_ASSERT(bit_test(amp->am_memmap, extent));
385 if (amp->am_memtab[extent] == 0) {
386 bit_clear(amp->am_memmap, extent);
387 amp->am_ndirty--;
388 modified = true;
389 }
390
391 return (modified);
392 }
393
394 /*
395 * Function returns number of dirty regions.
396 */
397 uint64_t
activemap_ndirty(const struct activemap * amp)398 activemap_ndirty(const struct activemap *amp)
399 {
400
401 PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC);
402
403 return (amp->am_ndirty);
404 }
405
406 /*
407 * Function compare on-disk bitmap and in-memory bitmap and returns true if
408 * they differ and should be flushed to the disk.
409 */
410 bool
activemap_differ(const struct activemap * amp)411 activemap_differ(const struct activemap *amp)
412 {
413
414 PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC);
415
416 return (memcmp(amp->am_diskmap, amp->am_memmap,
417 amp->am_mapsize) != 0);
418 }
419
420 /*
421 * Function returns number of bytes used by bitmap.
422 */
423 size_t
activemap_size(const struct activemap * amp)424 activemap_size(const struct activemap *amp)
425 {
426
427 PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC);
428
429 return (amp->am_mapsize);
430 }
431
432 /*
433 * Function returns number of bytes needed for storing on-disk bitmap.
434 * This is the same as activemap_size(), but rounded up to sector size.
435 */
436 size_t
activemap_ondisk_size(const struct activemap * amp)437 activemap_ondisk_size(const struct activemap *amp)
438 {
439
440 PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC);
441
442 return (amp->am_diskmapsize);
443 }
444
445 /*
446 * Function copies the given buffer read from disk to the internal bitmap.
447 */
448 void
activemap_copyin(struct activemap * amp,const unsigned char * buf,size_t size)449 activemap_copyin(struct activemap *amp, const unsigned char *buf, size_t size)
450 {
451 int ext;
452
453 PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC);
454 PJDLOG_ASSERT(size >= amp->am_mapsize);
455
456 memcpy(amp->am_diskmap, buf, amp->am_mapsize);
457 memcpy(amp->am_memmap, buf, amp->am_mapsize);
458 memcpy(amp->am_syncmap, buf, amp->am_mapsize);
459
460 bit_ffs(amp->am_memmap, amp->am_nextents, &ext);
461 if (ext == -1) {
462 /* There are no dirty extents, so we can leave now. */
463 return;
464 }
465 /*
466 * Set synchronization offset to the first dirty extent.
467 */
468 activemap_sync_rewind(amp);
469 /*
470 * We have dirty extents and we want them to stay that way until
471 * we synchronize, so we set number of pending writes to number
472 * of requests needed to synchronize one extent.
473 */
474 amp->am_ndirty = 0;
475 for (; ext < amp->am_nextents; ext++) {
476 if (bit_test(amp->am_memmap, ext)) {
477 amp->am_memtab[ext] = ext2reqs(amp, ext);
478 amp->am_ndirty++;
479 }
480 }
481 }
482
483 /*
484 * Function merges the given bitmap with existing one.
485 */
486 void
activemap_merge(struct activemap * amp,const unsigned char * buf,size_t size)487 activemap_merge(struct activemap *amp, const unsigned char *buf, size_t size)
488 {
489 bitstr_t *remmap = __DECONST(bitstr_t *, buf);
490 int ext;
491
492 PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC);
493 PJDLOG_ASSERT(size >= amp->am_mapsize);
494
495 bit_ffs(remmap, amp->am_nextents, &ext);
496 if (ext == -1) {
497 /* There are no dirty extents, so we can leave now. */
498 return;
499 }
500 /*
501 * We have dirty extents and we want them to stay that way until
502 * we synchronize, so we set number of pending writes to number
503 * of requests needed to synchronize one extent.
504 */
505 for (; ext < amp->am_nextents; ext++) {
506 /* Local extent already dirty. */
507 if (bit_test(amp->am_syncmap, ext))
508 continue;
509 /* Remote extent isn't dirty. */
510 if (!bit_test(remmap, ext))
511 continue;
512 bit_set(amp->am_syncmap, ext);
513 bit_set(amp->am_memmap, ext);
514 bit_set(amp->am_diskmap, ext);
515 if (amp->am_memtab[ext] == 0)
516 amp->am_ndirty++;
517 amp->am_memtab[ext] = ext2reqs(amp, ext);
518 }
519 /*
520 * Set synchronization offset to the first dirty extent.
521 */
522 activemap_sync_rewind(amp);
523 }
524
525 /*
526 * Function returns pointer to internal bitmap that should be written to disk.
527 */
528 const unsigned char *
activemap_bitmap(struct activemap * amp,size_t * sizep)529 activemap_bitmap(struct activemap *amp, size_t *sizep)
530 {
531
532 PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC);
533
534 if (sizep != NULL)
535 *sizep = amp->am_diskmapsize;
536 memcpy(amp->am_diskmap, amp->am_memmap, amp->am_mapsize);
537 keepdirty_fill(amp);
538 return ((const unsigned char *)amp->am_diskmap);
539 }
540
541 /*
542 * Function calculates size needed to store bitmap on disk.
543 */
544 size_t
activemap_calc_ondisk_size(uint64_t mediasize,uint32_t extentsize,uint32_t sectorsize)545 activemap_calc_ondisk_size(uint64_t mediasize, uint32_t extentsize,
546 uint32_t sectorsize)
547 {
548 uint64_t nextents, mapsize;
549
550 PJDLOG_ASSERT(mediasize > 0);
551 PJDLOG_ASSERT(extentsize > 0);
552 PJDLOG_ASSERT(powerof2(extentsize));
553 PJDLOG_ASSERT(sectorsize > 0);
554 PJDLOG_ASSERT(powerof2(sectorsize));
555
556 nextents = ((mediasize - 1) / extentsize) + 1;
557 mapsize = bitstr_size(nextents);
558 return (roundup2(mapsize, sectorsize));
559 }
560
561 /*
562 * Set synchronization offset to the first dirty extent.
563 */
564 void
activemap_sync_rewind(struct activemap * amp)565 activemap_sync_rewind(struct activemap *amp)
566 {
567 int ext;
568
569 PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC);
570
571 bit_ffs(amp->am_syncmap, amp->am_nextents, &ext);
572 if (ext == -1) {
573 /* There are no extents to synchronize. */
574 amp->am_syncoff = -2;
575 return;
576 }
577 /*
578 * Mark that we want to start synchronization from the beginning.
579 */
580 amp->am_syncoff = -1;
581 }
582
583 /*
584 * Return next offset of where we should synchronize.
585 */
586 off_t
activemap_sync_offset(struct activemap * amp,off_t * lengthp,int * syncextp)587 activemap_sync_offset(struct activemap *amp, off_t *lengthp, int *syncextp)
588 {
589 off_t syncoff, left;
590 int ext;
591
592 PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC);
593 PJDLOG_ASSERT(lengthp != NULL);
594 PJDLOG_ASSERT(syncextp != NULL);
595
596 *syncextp = -1;
597
598 if (amp->am_syncoff == -2)
599 return (-1);
600
601 if (amp->am_syncoff >= 0 &&
602 (amp->am_syncoff + MAXPHYS >= amp->am_mediasize ||
603 off2ext(amp, amp->am_syncoff) !=
604 off2ext(amp, amp->am_syncoff + MAXPHYS))) {
605 /*
606 * We are about to change extent, so mark previous one as clean.
607 */
608 ext = off2ext(amp, amp->am_syncoff);
609 bit_clear(amp->am_syncmap, ext);
610 *syncextp = ext;
611 amp->am_syncoff = -1;
612 }
613
614 if (amp->am_syncoff == -1) {
615 /*
616 * Let's find first extent to synchronize.
617 */
618 bit_ffs(amp->am_syncmap, amp->am_nextents, &ext);
619 if (ext == -1) {
620 amp->am_syncoff = -2;
621 return (-1);
622 }
623 amp->am_syncoff = ext2off(amp, ext);
624 } else {
625 /*
626 * We don't change extent, so just increase offset.
627 */
628 amp->am_syncoff += MAXPHYS;
629 if (amp->am_syncoff >= amp->am_mediasize) {
630 amp->am_syncoff = -2;
631 return (-1);
632 }
633 }
634
635 syncoff = amp->am_syncoff;
636 left = ext2off(amp, off2ext(amp, syncoff)) +
637 amp->am_extentsize - syncoff;
638 if (syncoff + left > amp->am_mediasize)
639 left = amp->am_mediasize - syncoff;
640 if (left > MAXPHYS)
641 left = MAXPHYS;
642
643 PJDLOG_ASSERT(left >= 0 && left <= MAXPHYS);
644 PJDLOG_ASSERT(syncoff >= 0 && syncoff < amp->am_mediasize);
645 PJDLOG_ASSERT(syncoff + left >= 0 &&
646 syncoff + left <= amp->am_mediasize);
647
648 *lengthp = left;
649 return (syncoff);
650 }
651
652 /*
653 * Mark extent(s) containing the given region for synchronization.
654 * Most likely one of the components is unavailable.
655 */
656 bool
activemap_need_sync(struct activemap * amp,off_t offset,off_t length)657 activemap_need_sync(struct activemap *amp, off_t offset, off_t length)
658 {
659 bool modified;
660 off_t end;
661 int ext;
662
663 PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC);
664
665 modified = false;
666 end = offset + length - 1;
667
668 for (ext = off2ext(amp, offset); ext <= off2ext(amp, end); ext++) {
669 if (bit_test(amp->am_syncmap, ext)) {
670 /* Already marked for synchronization. */
671 PJDLOG_ASSERT(bit_test(amp->am_memmap, ext));
672 continue;
673 }
674 bit_set(amp->am_syncmap, ext);
675 if (!bit_test(amp->am_memmap, ext)) {
676 bit_set(amp->am_memmap, ext);
677 amp->am_ndirty++;
678 }
679 amp->am_memtab[ext] += ext2reqs(amp, ext);
680 modified = true;
681 }
682
683 return (modified);
684 }
685
686 void
activemap_dump(const struct activemap * amp)687 activemap_dump(const struct activemap *amp)
688 {
689 int bit;
690
691 printf("M: ");
692 for (bit = 0; bit < amp->am_nextents; bit++)
693 printf("%d", bit_test(amp->am_memmap, bit) ? 1 : 0);
694 printf("\n");
695 printf("D: ");
696 for (bit = 0; bit < amp->am_nextents; bit++)
697 printf("%d", bit_test(amp->am_diskmap, bit) ? 1 : 0);
698 printf("\n");
699 printf("S: ");
700 for (bit = 0; bit < amp->am_nextents; bit++)
701 printf("%d", bit_test(amp->am_syncmap, bit) ? 1 : 0);
702 printf("\n");
703 }
704