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