diff options
Diffstat (limited to 'drivers/md/bitmap.c')
-rw-r--r-- | drivers/md/bitmap.c | 449 |
1 files changed, 391 insertions, 58 deletions
diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index 60e2b322db11..26ac8aad0b19 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c | |||
@@ -212,7 +212,7 @@ static void bitmap_checkfree(struct bitmap *bitmap, unsigned long page) | |||
212 | */ | 212 | */ |
213 | 213 | ||
214 | /* IO operations when bitmap is stored near all superblocks */ | 214 | /* IO operations when bitmap is stored near all superblocks */ |
215 | static struct page *read_sb_page(mddev_t *mddev, long offset, | 215 | static struct page *read_sb_page(mddev_t *mddev, loff_t offset, |
216 | struct page *page, | 216 | struct page *page, |
217 | unsigned long index, int size) | 217 | unsigned long index, int size) |
218 | { | 218 | { |
@@ -287,27 +287,36 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) | |||
287 | 287 | ||
288 | while ((rdev = next_active_rdev(rdev, mddev)) != NULL) { | 288 | while ((rdev = next_active_rdev(rdev, mddev)) != NULL) { |
289 | int size = PAGE_SIZE; | 289 | int size = PAGE_SIZE; |
290 | loff_t offset = mddev->bitmap_info.offset; | ||
290 | if (page->index == bitmap->file_pages-1) | 291 | if (page->index == bitmap->file_pages-1) |
291 | size = roundup(bitmap->last_page_size, | 292 | size = roundup(bitmap->last_page_size, |
292 | bdev_logical_block_size(rdev->bdev)); | 293 | bdev_logical_block_size(rdev->bdev)); |
293 | /* Just make sure we aren't corrupting data or | 294 | /* Just make sure we aren't corrupting data or |
294 | * metadata | 295 | * metadata |
295 | */ | 296 | */ |
296 | if (bitmap->offset < 0) { | 297 | if (mddev->external) { |
298 | /* Bitmap could be anywhere. */ | ||
299 | if (rdev->sb_start + offset + (page->index *(PAGE_SIZE/512)) > | ||
300 | rdev->data_offset && | ||
301 | rdev->sb_start + offset < | ||
302 | rdev->data_offset + mddev->dev_sectors + | ||
303 | (PAGE_SIZE/512)) | ||
304 | goto bad_alignment; | ||
305 | } else if (offset < 0) { | ||
297 | /* DATA BITMAP METADATA */ | 306 | /* DATA BITMAP METADATA */ |
298 | if (bitmap->offset | 307 | if (offset |
299 | + (long)(page->index * (PAGE_SIZE/512)) | 308 | + (long)(page->index * (PAGE_SIZE/512)) |
300 | + size/512 > 0) | 309 | + size/512 > 0) |
301 | /* bitmap runs in to metadata */ | 310 | /* bitmap runs in to metadata */ |
302 | goto bad_alignment; | 311 | goto bad_alignment; |
303 | if (rdev->data_offset + mddev->dev_sectors | 312 | if (rdev->data_offset + mddev->dev_sectors |
304 | > rdev->sb_start + bitmap->offset) | 313 | > rdev->sb_start + offset) |
305 | /* data runs in to bitmap */ | 314 | /* data runs in to bitmap */ |
306 | goto bad_alignment; | 315 | goto bad_alignment; |
307 | } else if (rdev->sb_start < rdev->data_offset) { | 316 | } else if (rdev->sb_start < rdev->data_offset) { |
308 | /* METADATA BITMAP DATA */ | 317 | /* METADATA BITMAP DATA */ |
309 | if (rdev->sb_start | 318 | if (rdev->sb_start |
310 | + bitmap->offset | 319 | + offset |
311 | + page->index*(PAGE_SIZE/512) + size/512 | 320 | + page->index*(PAGE_SIZE/512) + size/512 |
312 | > rdev->data_offset) | 321 | > rdev->data_offset) |
313 | /* bitmap runs in to data */ | 322 | /* bitmap runs in to data */ |
@@ -316,7 +325,7 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) | |||
316 | /* DATA METADATA BITMAP - no problems */ | 325 | /* DATA METADATA BITMAP - no problems */ |
317 | } | 326 | } |
318 | md_super_write(mddev, rdev, | 327 | md_super_write(mddev, rdev, |
319 | rdev->sb_start + bitmap->offset | 328 | rdev->sb_start + offset |
320 | + page->index * (PAGE_SIZE/512), | 329 | + page->index * (PAGE_SIZE/512), |
321 | size, | 330 | size, |
322 | page); | 331 | page); |
@@ -488,6 +497,8 @@ void bitmap_update_sb(struct bitmap *bitmap) | |||
488 | 497 | ||
489 | if (!bitmap || !bitmap->mddev) /* no bitmap for this array */ | 498 | if (!bitmap || !bitmap->mddev) /* no bitmap for this array */ |
490 | return; | 499 | return; |
500 | if (bitmap->mddev->bitmap_info.external) | ||
501 | return; | ||
491 | spin_lock_irqsave(&bitmap->lock, flags); | 502 | spin_lock_irqsave(&bitmap->lock, flags); |
492 | if (!bitmap->sb_page) { /* no superblock */ | 503 | if (!bitmap->sb_page) { /* no superblock */ |
493 | spin_unlock_irqrestore(&bitmap->lock, flags); | 504 | spin_unlock_irqrestore(&bitmap->lock, flags); |
@@ -501,6 +512,9 @@ void bitmap_update_sb(struct bitmap *bitmap) | |||
501 | bitmap->events_cleared = bitmap->mddev->events; | 512 | bitmap->events_cleared = bitmap->mddev->events; |
502 | sb->events_cleared = cpu_to_le64(bitmap->events_cleared); | 513 | sb->events_cleared = cpu_to_le64(bitmap->events_cleared); |
503 | } | 514 | } |
515 | /* Just in case these have been changed via sysfs: */ | ||
516 | sb->daemon_sleep = cpu_to_le32(bitmap->mddev->bitmap_info.daemon_sleep/HZ); | ||
517 | sb->write_behind = cpu_to_le32(bitmap->mddev->bitmap_info.max_write_behind); | ||
504 | kunmap_atomic(sb, KM_USER0); | 518 | kunmap_atomic(sb, KM_USER0); |
505 | write_page(bitmap, bitmap->sb_page, 1); | 519 | write_page(bitmap, bitmap->sb_page, 1); |
506 | } | 520 | } |
@@ -550,7 +564,8 @@ static int bitmap_read_sb(struct bitmap *bitmap) | |||
550 | 564 | ||
551 | bitmap->sb_page = read_page(bitmap->file, 0, bitmap, bytes); | 565 | bitmap->sb_page = read_page(bitmap->file, 0, bitmap, bytes); |
552 | } else { | 566 | } else { |
553 | bitmap->sb_page = read_sb_page(bitmap->mddev, bitmap->offset, | 567 | bitmap->sb_page = read_sb_page(bitmap->mddev, |
568 | bitmap->mddev->bitmap_info.offset, | ||
554 | NULL, | 569 | NULL, |
555 | 0, sizeof(bitmap_super_t)); | 570 | 0, sizeof(bitmap_super_t)); |
556 | } | 571 | } |
@@ -563,7 +578,7 @@ static int bitmap_read_sb(struct bitmap *bitmap) | |||
563 | sb = (bitmap_super_t *)kmap_atomic(bitmap->sb_page, KM_USER0); | 578 | sb = (bitmap_super_t *)kmap_atomic(bitmap->sb_page, KM_USER0); |
564 | 579 | ||
565 | chunksize = le32_to_cpu(sb->chunksize); | 580 | chunksize = le32_to_cpu(sb->chunksize); |
566 | daemon_sleep = le32_to_cpu(sb->daemon_sleep); | 581 | daemon_sleep = le32_to_cpu(sb->daemon_sleep) * HZ; |
567 | write_behind = le32_to_cpu(sb->write_behind); | 582 | write_behind = le32_to_cpu(sb->write_behind); |
568 | 583 | ||
569 | /* verify that the bitmap-specific fields are valid */ | 584 | /* verify that the bitmap-specific fields are valid */ |
@@ -576,7 +591,7 @@ static int bitmap_read_sb(struct bitmap *bitmap) | |||
576 | reason = "bitmap chunksize too small"; | 591 | reason = "bitmap chunksize too small"; |
577 | else if ((1 << ffz(~chunksize)) != chunksize) | 592 | else if ((1 << ffz(~chunksize)) != chunksize) |
578 | reason = "bitmap chunksize not a power of 2"; | 593 | reason = "bitmap chunksize not a power of 2"; |
579 | else if (daemon_sleep < 1 || daemon_sleep > MAX_SCHEDULE_TIMEOUT / HZ) | 594 | else if (daemon_sleep < 1 || daemon_sleep > MAX_SCHEDULE_TIMEOUT) |
580 | reason = "daemon sleep period out of range"; | 595 | reason = "daemon sleep period out of range"; |
581 | else if (write_behind > COUNTER_MAX) | 596 | else if (write_behind > COUNTER_MAX) |
582 | reason = "write-behind limit out of range (0 - 16383)"; | 597 | reason = "write-behind limit out of range (0 - 16383)"; |
@@ -610,10 +625,9 @@ static int bitmap_read_sb(struct bitmap *bitmap) | |||
610 | } | 625 | } |
611 | success: | 626 | success: |
612 | /* assign fields using values from superblock */ | 627 | /* assign fields using values from superblock */ |
613 | bitmap->chunksize = chunksize; | 628 | bitmap->mddev->bitmap_info.chunksize = chunksize; |
614 | bitmap->daemon_sleep = daemon_sleep; | 629 | bitmap->mddev->bitmap_info.daemon_sleep = daemon_sleep; |
615 | bitmap->daemon_lastrun = jiffies; | 630 | bitmap->mddev->bitmap_info.max_write_behind = write_behind; |
616 | bitmap->max_write_behind = write_behind; | ||
617 | bitmap->flags |= le32_to_cpu(sb->state); | 631 | bitmap->flags |= le32_to_cpu(sb->state); |
618 | if (le32_to_cpu(sb->version) == BITMAP_MAJOR_HOSTENDIAN) | 632 | if (le32_to_cpu(sb->version) == BITMAP_MAJOR_HOSTENDIAN) |
619 | bitmap->flags |= BITMAP_HOSTENDIAN; | 633 | bitmap->flags |= BITMAP_HOSTENDIAN; |
@@ -664,16 +678,26 @@ static int bitmap_mask_state(struct bitmap *bitmap, enum bitmap_state bits, | |||
664 | * general bitmap file operations | 678 | * general bitmap file operations |
665 | */ | 679 | */ |
666 | 680 | ||
681 | /* | ||
682 | * on-disk bitmap: | ||
683 | * | ||
684 | * Use one bit per "chunk" (block set). We do the disk I/O on the bitmap | ||
685 | * file a page at a time. There's a superblock at the start of the file. | ||
686 | */ | ||
667 | /* calculate the index of the page that contains this bit */ | 687 | /* calculate the index of the page that contains this bit */ |
668 | static inline unsigned long file_page_index(unsigned long chunk) | 688 | static inline unsigned long file_page_index(struct bitmap *bitmap, unsigned long chunk) |
669 | { | 689 | { |
670 | return CHUNK_BIT_OFFSET(chunk) >> PAGE_BIT_SHIFT; | 690 | if (!bitmap->mddev->bitmap_info.external) |
691 | chunk += sizeof(bitmap_super_t) << 3; | ||
692 | return chunk >> PAGE_BIT_SHIFT; | ||
671 | } | 693 | } |
672 | 694 | ||
673 | /* calculate the (bit) offset of this bit within a page */ | 695 | /* calculate the (bit) offset of this bit within a page */ |
674 | static inline unsigned long file_page_offset(unsigned long chunk) | 696 | static inline unsigned long file_page_offset(struct bitmap *bitmap, unsigned long chunk) |
675 | { | 697 | { |
676 | return CHUNK_BIT_OFFSET(chunk) & (PAGE_BITS - 1); | 698 | if (!bitmap->mddev->bitmap_info.external) |
699 | chunk += sizeof(bitmap_super_t) << 3; | ||
700 | return chunk & (PAGE_BITS - 1); | ||
677 | } | 701 | } |
678 | 702 | ||
679 | /* | 703 | /* |
@@ -686,8 +710,9 @@ static inline unsigned long file_page_offset(unsigned long chunk) | |||
686 | static inline struct page *filemap_get_page(struct bitmap *bitmap, | 710 | static inline struct page *filemap_get_page(struct bitmap *bitmap, |
687 | unsigned long chunk) | 711 | unsigned long chunk) |
688 | { | 712 | { |
689 | if (file_page_index(chunk) >= bitmap->file_pages) return NULL; | 713 | if (file_page_index(bitmap, chunk) >= bitmap->file_pages) return NULL; |
690 | return bitmap->filemap[file_page_index(chunk) - file_page_index(0)]; | 714 | return bitmap->filemap[file_page_index(bitmap, chunk) |
715 | - file_page_index(bitmap, 0)]; | ||
691 | } | 716 | } |
692 | 717 | ||
693 | 718 | ||
@@ -710,7 +735,7 @@ static void bitmap_file_unmap(struct bitmap *bitmap) | |||
710 | spin_unlock_irqrestore(&bitmap->lock, flags); | 735 | spin_unlock_irqrestore(&bitmap->lock, flags); |
711 | 736 | ||
712 | while (pages--) | 737 | while (pages--) |
713 | if (map[pages]->index != 0) /* 0 is sb_page, release it below */ | 738 | if (map[pages] != sb_page) /* 0 is sb_page, release it below */ |
714 | free_buffers(map[pages]); | 739 | free_buffers(map[pages]); |
715 | kfree(map); | 740 | kfree(map); |
716 | kfree(attr); | 741 | kfree(attr); |
@@ -821,7 +846,7 @@ static void bitmap_file_set_bit(struct bitmap *bitmap, sector_t block) | |||
821 | 846 | ||
822 | page = filemap_get_page(bitmap, chunk); | 847 | page = filemap_get_page(bitmap, chunk); |
823 | if (!page) return; | 848 | if (!page) return; |
824 | bit = file_page_offset(chunk); | 849 | bit = file_page_offset(bitmap, chunk); |
825 | 850 | ||
826 | /* set the bit */ | 851 | /* set the bit */ |
827 | kaddr = kmap_atomic(page, KM_USER0); | 852 | kaddr = kmap_atomic(page, KM_USER0); |
@@ -907,7 +932,7 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start) | |||
907 | chunks = bitmap->chunks; | 932 | chunks = bitmap->chunks; |
908 | file = bitmap->file; | 933 | file = bitmap->file; |
909 | 934 | ||
910 | BUG_ON(!file && !bitmap->offset); | 935 | BUG_ON(!file && !bitmap->mddev->bitmap_info.offset); |
911 | 936 | ||
912 | #ifdef INJECT_FAULTS_3 | 937 | #ifdef INJECT_FAULTS_3 |
913 | outofdate = 1; | 938 | outofdate = 1; |
@@ -919,14 +944,17 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start) | |||
919 | "recovery\n", bmname(bitmap)); | 944 | "recovery\n", bmname(bitmap)); |
920 | 945 | ||
921 | bytes = (chunks + 7) / 8; | 946 | bytes = (chunks + 7) / 8; |
947 | if (!bitmap->mddev->bitmap_info.external) | ||
948 | bytes += sizeof(bitmap_super_t); | ||
922 | 949 | ||
923 | num_pages = (bytes + sizeof(bitmap_super_t) + PAGE_SIZE - 1) / PAGE_SIZE; | 950 | |
951 | num_pages = (bytes + PAGE_SIZE - 1) / PAGE_SIZE; | ||
924 | 952 | ||
925 | if (file && i_size_read(file->f_mapping->host) < bytes + sizeof(bitmap_super_t)) { | 953 | if (file && i_size_read(file->f_mapping->host) < bytes) { |
926 | printk(KERN_INFO "%s: bitmap file too short %lu < %lu\n", | 954 | printk(KERN_INFO "%s: bitmap file too short %lu < %lu\n", |
927 | bmname(bitmap), | 955 | bmname(bitmap), |
928 | (unsigned long) i_size_read(file->f_mapping->host), | 956 | (unsigned long) i_size_read(file->f_mapping->host), |
929 | bytes + sizeof(bitmap_super_t)); | 957 | bytes); |
930 | goto err; | 958 | goto err; |
931 | } | 959 | } |
932 | 960 | ||
@@ -947,17 +975,16 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start) | |||
947 | 975 | ||
948 | for (i = 0; i < chunks; i++) { | 976 | for (i = 0; i < chunks; i++) { |
949 | int b; | 977 | int b; |
950 | index = file_page_index(i); | 978 | index = file_page_index(bitmap, i); |
951 | bit = file_page_offset(i); | 979 | bit = file_page_offset(bitmap, i); |
952 | if (index != oldindex) { /* this is a new page, read it in */ | 980 | if (index != oldindex) { /* this is a new page, read it in */ |
953 | int count; | 981 | int count; |
954 | /* unmap the old page, we're done with it */ | 982 | /* unmap the old page, we're done with it */ |
955 | if (index == num_pages-1) | 983 | if (index == num_pages-1) |
956 | count = bytes + sizeof(bitmap_super_t) | 984 | count = bytes - index * PAGE_SIZE; |
957 | - index * PAGE_SIZE; | ||
958 | else | 985 | else |
959 | count = PAGE_SIZE; | 986 | count = PAGE_SIZE; |
960 | if (index == 0) { | 987 | if (index == 0 && bitmap->sb_page) { |
961 | /* | 988 | /* |
962 | * if we're here then the superblock page | 989 | * if we're here then the superblock page |
963 | * contains some bits (PAGE_SIZE != sizeof sb) | 990 | * contains some bits (PAGE_SIZE != sizeof sb) |
@@ -967,14 +994,15 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start) | |||
967 | offset = sizeof(bitmap_super_t); | 994 | offset = sizeof(bitmap_super_t); |
968 | if (!file) | 995 | if (!file) |
969 | read_sb_page(bitmap->mddev, | 996 | read_sb_page(bitmap->mddev, |
970 | bitmap->offset, | 997 | bitmap->mddev->bitmap_info.offset, |
971 | page, | 998 | page, |
972 | index, count); | 999 | index, count); |
973 | } else if (file) { | 1000 | } else if (file) { |
974 | page = read_page(file, index, bitmap, count); | 1001 | page = read_page(file, index, bitmap, count); |
975 | offset = 0; | 1002 | offset = 0; |
976 | } else { | 1003 | } else { |
977 | page = read_sb_page(bitmap->mddev, bitmap->offset, | 1004 | page = read_sb_page(bitmap->mddev, |
1005 | bitmap->mddev->bitmap_info.offset, | ||
978 | NULL, | 1006 | NULL, |
979 | index, count); | 1007 | index, count); |
980 | offset = 0; | 1008 | offset = 0; |
@@ -1078,23 +1106,32 @@ static bitmap_counter_t *bitmap_get_counter(struct bitmap *bitmap, | |||
1078 | * out to disk | 1106 | * out to disk |
1079 | */ | 1107 | */ |
1080 | 1108 | ||
1081 | void bitmap_daemon_work(struct bitmap *bitmap) | 1109 | void bitmap_daemon_work(mddev_t *mddev) |
1082 | { | 1110 | { |
1111 | struct bitmap *bitmap; | ||
1083 | unsigned long j; | 1112 | unsigned long j; |
1084 | unsigned long flags; | 1113 | unsigned long flags; |
1085 | struct page *page = NULL, *lastpage = NULL; | 1114 | struct page *page = NULL, *lastpage = NULL; |
1086 | int blocks; | 1115 | int blocks; |
1087 | void *paddr; | 1116 | void *paddr; |
1088 | 1117 | ||
1089 | if (bitmap == NULL) | 1118 | /* Use a mutex to guard daemon_work against |
1119 | * bitmap_destroy. | ||
1120 | */ | ||
1121 | mutex_lock(&mddev->bitmap_info.mutex); | ||
1122 | bitmap = mddev->bitmap; | ||
1123 | if (bitmap == NULL) { | ||
1124 | mutex_unlock(&mddev->bitmap_info.mutex); | ||
1090 | return; | 1125 | return; |
1091 | if (time_before(jiffies, bitmap->daemon_lastrun + bitmap->daemon_sleep*HZ)) | 1126 | } |
1127 | if (time_before(jiffies, bitmap->daemon_lastrun | ||
1128 | + bitmap->mddev->bitmap_info.daemon_sleep)) | ||
1092 | goto done; | 1129 | goto done; |
1093 | 1130 | ||
1094 | bitmap->daemon_lastrun = jiffies; | 1131 | bitmap->daemon_lastrun = jiffies; |
1095 | if (bitmap->allclean) { | 1132 | if (bitmap->allclean) { |
1096 | bitmap->mddev->thread->timeout = MAX_SCHEDULE_TIMEOUT; | 1133 | bitmap->mddev->thread->timeout = MAX_SCHEDULE_TIMEOUT; |
1097 | return; | 1134 | goto done; |
1098 | } | 1135 | } |
1099 | bitmap->allclean = 1; | 1136 | bitmap->allclean = 1; |
1100 | 1137 | ||
@@ -1142,7 +1179,8 @@ void bitmap_daemon_work(struct bitmap *bitmap) | |||
1142 | /* We are possibly going to clear some bits, so make | 1179 | /* We are possibly going to clear some bits, so make |
1143 | * sure that events_cleared is up-to-date. | 1180 | * sure that events_cleared is up-to-date. |
1144 | */ | 1181 | */ |
1145 | if (bitmap->need_sync) { | 1182 | if (bitmap->need_sync && |
1183 | bitmap->mddev->bitmap_info.external == 0) { | ||
1146 | bitmap_super_t *sb; | 1184 | bitmap_super_t *sb; |
1147 | bitmap->need_sync = 0; | 1185 | bitmap->need_sync = 0; |
1148 | sb = kmap_atomic(bitmap->sb_page, KM_USER0); | 1186 | sb = kmap_atomic(bitmap->sb_page, KM_USER0); |
@@ -1152,7 +1190,8 @@ void bitmap_daemon_work(struct bitmap *bitmap) | |||
1152 | write_page(bitmap, bitmap->sb_page, 1); | 1190 | write_page(bitmap, bitmap->sb_page, 1); |
1153 | } | 1191 | } |
1154 | spin_lock_irqsave(&bitmap->lock, flags); | 1192 | spin_lock_irqsave(&bitmap->lock, flags); |
1155 | clear_page_attr(bitmap, page, BITMAP_PAGE_CLEAN); | 1193 | if (!bitmap->need_sync) |
1194 | clear_page_attr(bitmap, page, BITMAP_PAGE_CLEAN); | ||
1156 | } | 1195 | } |
1157 | bmc = bitmap_get_counter(bitmap, | 1196 | bmc = bitmap_get_counter(bitmap, |
1158 | (sector_t)j << CHUNK_BLOCK_SHIFT(bitmap), | 1197 | (sector_t)j << CHUNK_BLOCK_SHIFT(bitmap), |
@@ -1167,7 +1206,7 @@ void bitmap_daemon_work(struct bitmap *bitmap) | |||
1167 | if (*bmc == 2) { | 1206 | if (*bmc == 2) { |
1168 | *bmc=1; /* maybe clear the bit next time */ | 1207 | *bmc=1; /* maybe clear the bit next time */ |
1169 | set_page_attr(bitmap, page, BITMAP_PAGE_CLEAN); | 1208 | set_page_attr(bitmap, page, BITMAP_PAGE_CLEAN); |
1170 | } else if (*bmc == 1) { | 1209 | } else if (*bmc == 1 && !bitmap->need_sync) { |
1171 | /* we can clear the bit */ | 1210 | /* we can clear the bit */ |
1172 | *bmc = 0; | 1211 | *bmc = 0; |
1173 | bitmap_count_page(bitmap, | 1212 | bitmap_count_page(bitmap, |
@@ -1177,9 +1216,11 @@ void bitmap_daemon_work(struct bitmap *bitmap) | |||
1177 | /* clear the bit */ | 1216 | /* clear the bit */ |
1178 | paddr = kmap_atomic(page, KM_USER0); | 1217 | paddr = kmap_atomic(page, KM_USER0); |
1179 | if (bitmap->flags & BITMAP_HOSTENDIAN) | 1218 | if (bitmap->flags & BITMAP_HOSTENDIAN) |
1180 | clear_bit(file_page_offset(j), paddr); | 1219 | clear_bit(file_page_offset(bitmap, j), |
1220 | paddr); | ||
1181 | else | 1221 | else |
1182 | ext2_clear_bit(file_page_offset(j), paddr); | 1222 | ext2_clear_bit(file_page_offset(bitmap, j), |
1223 | paddr); | ||
1183 | kunmap_atomic(paddr, KM_USER0); | 1224 | kunmap_atomic(paddr, KM_USER0); |
1184 | } | 1225 | } |
1185 | } else | 1226 | } else |
@@ -1202,7 +1243,9 @@ void bitmap_daemon_work(struct bitmap *bitmap) | |||
1202 | 1243 | ||
1203 | done: | 1244 | done: |
1204 | if (bitmap->allclean == 0) | 1245 | if (bitmap->allclean == 0) |
1205 | bitmap->mddev->thread->timeout = bitmap->daemon_sleep * HZ; | 1246 | bitmap->mddev->thread->timeout = |
1247 | bitmap->mddev->bitmap_info.daemon_sleep; | ||
1248 | mutex_unlock(&mddev->bitmap_info.mutex); | ||
1206 | } | 1249 | } |
1207 | 1250 | ||
1208 | static bitmap_counter_t *bitmap_get_counter(struct bitmap *bitmap, | 1251 | static bitmap_counter_t *bitmap_get_counter(struct bitmap *bitmap, |
@@ -1332,6 +1375,7 @@ void bitmap_endwrite(struct bitmap *bitmap, sector_t offset, unsigned long secto | |||
1332 | bitmap->events_cleared < bitmap->mddev->events) { | 1375 | bitmap->events_cleared < bitmap->mddev->events) { |
1333 | bitmap->events_cleared = bitmap->mddev->events; | 1376 | bitmap->events_cleared = bitmap->mddev->events; |
1334 | bitmap->need_sync = 1; | 1377 | bitmap->need_sync = 1; |
1378 | sysfs_notify_dirent(bitmap->sysfs_can_clear); | ||
1335 | } | 1379 | } |
1336 | 1380 | ||
1337 | if (!success && ! (*bmc & NEEDED_MASK)) | 1381 | if (!success && ! (*bmc & NEEDED_MASK)) |
@@ -1470,7 +1514,7 @@ void bitmap_cond_end_sync(struct bitmap *bitmap, sector_t sector) | |||
1470 | return; | 1514 | return; |
1471 | } | 1515 | } |
1472 | if (time_before(jiffies, (bitmap->last_end_sync | 1516 | if (time_before(jiffies, (bitmap->last_end_sync |
1473 | + bitmap->daemon_sleep * HZ))) | 1517 | + bitmap->mddev->bitmap_info.daemon_sleep))) |
1474 | return; | 1518 | return; |
1475 | wait_event(bitmap->mddev->recovery_wait, | 1519 | wait_event(bitmap->mddev->recovery_wait, |
1476 | atomic_read(&bitmap->mddev->recovery_active) == 0); | 1520 | atomic_read(&bitmap->mddev->recovery_active) == 0); |
@@ -1522,6 +1566,12 @@ void bitmap_dirty_bits(struct bitmap *bitmap, unsigned long s, unsigned long e) | |||
1522 | sector_t sec = (sector_t)chunk << CHUNK_BLOCK_SHIFT(bitmap); | 1566 | sector_t sec = (sector_t)chunk << CHUNK_BLOCK_SHIFT(bitmap); |
1523 | bitmap_set_memory_bits(bitmap, sec, 1); | 1567 | bitmap_set_memory_bits(bitmap, sec, 1); |
1524 | bitmap_file_set_bit(bitmap, sec); | 1568 | bitmap_file_set_bit(bitmap, sec); |
1569 | if (sec < bitmap->mddev->recovery_cp) | ||
1570 | /* We are asserting that the array is dirty, | ||
1571 | * so move the recovery_cp address back so | ||
1572 | * that it is obvious that it is dirty | ||
1573 | */ | ||
1574 | bitmap->mddev->recovery_cp = sec; | ||
1525 | } | 1575 | } |
1526 | } | 1576 | } |
1527 | 1577 | ||
@@ -1531,7 +1581,7 @@ void bitmap_dirty_bits(struct bitmap *bitmap, unsigned long s, unsigned long e) | |||
1531 | void bitmap_flush(mddev_t *mddev) | 1581 | void bitmap_flush(mddev_t *mddev) |
1532 | { | 1582 | { |
1533 | struct bitmap *bitmap = mddev->bitmap; | 1583 | struct bitmap *bitmap = mddev->bitmap; |
1534 | int sleep; | 1584 | long sleep; |
1535 | 1585 | ||
1536 | if (!bitmap) /* there was no bitmap */ | 1586 | if (!bitmap) /* there was no bitmap */ |
1537 | return; | 1587 | return; |
@@ -1539,12 +1589,13 @@ void bitmap_flush(mddev_t *mddev) | |||
1539 | /* run the daemon_work three time to ensure everything is flushed | 1589 | /* run the daemon_work three time to ensure everything is flushed |
1540 | * that can be | 1590 | * that can be |
1541 | */ | 1591 | */ |
1542 | sleep = bitmap->daemon_sleep; | 1592 | sleep = mddev->bitmap_info.daemon_sleep * 2; |
1543 | bitmap->daemon_sleep = 0; | 1593 | bitmap->daemon_lastrun -= sleep; |
1544 | bitmap_daemon_work(bitmap); | 1594 | bitmap_daemon_work(mddev); |
1545 | bitmap_daemon_work(bitmap); | 1595 | bitmap->daemon_lastrun -= sleep; |
1546 | bitmap_daemon_work(bitmap); | 1596 | bitmap_daemon_work(mddev); |
1547 | bitmap->daemon_sleep = sleep; | 1597 | bitmap->daemon_lastrun -= sleep; |
1598 | bitmap_daemon_work(mddev); | ||
1548 | bitmap_update_sb(bitmap); | 1599 | bitmap_update_sb(bitmap); |
1549 | } | 1600 | } |
1550 | 1601 | ||
@@ -1574,6 +1625,7 @@ static void bitmap_free(struct bitmap *bitmap) | |||
1574 | kfree(bp); | 1625 | kfree(bp); |
1575 | kfree(bitmap); | 1626 | kfree(bitmap); |
1576 | } | 1627 | } |
1628 | |||
1577 | void bitmap_destroy(mddev_t *mddev) | 1629 | void bitmap_destroy(mddev_t *mddev) |
1578 | { | 1630 | { |
1579 | struct bitmap *bitmap = mddev->bitmap; | 1631 | struct bitmap *bitmap = mddev->bitmap; |
@@ -1581,10 +1633,15 @@ void bitmap_destroy(mddev_t *mddev) | |||
1581 | if (!bitmap) /* there was no bitmap */ | 1633 | if (!bitmap) /* there was no bitmap */ |
1582 | return; | 1634 | return; |
1583 | 1635 | ||
1636 | mutex_lock(&mddev->bitmap_info.mutex); | ||
1584 | mddev->bitmap = NULL; /* disconnect from the md device */ | 1637 | mddev->bitmap = NULL; /* disconnect from the md device */ |
1638 | mutex_unlock(&mddev->bitmap_info.mutex); | ||
1585 | if (mddev->thread) | 1639 | if (mddev->thread) |
1586 | mddev->thread->timeout = MAX_SCHEDULE_TIMEOUT; | 1640 | mddev->thread->timeout = MAX_SCHEDULE_TIMEOUT; |
1587 | 1641 | ||
1642 | if (bitmap->sysfs_can_clear) | ||
1643 | sysfs_put(bitmap->sysfs_can_clear); | ||
1644 | |||
1588 | bitmap_free(bitmap); | 1645 | bitmap_free(bitmap); |
1589 | } | 1646 | } |
1590 | 1647 | ||
@@ -1598,16 +1655,17 @@ int bitmap_create(mddev_t *mddev) | |||
1598 | sector_t blocks = mddev->resync_max_sectors; | 1655 | sector_t blocks = mddev->resync_max_sectors; |
1599 | unsigned long chunks; | 1656 | unsigned long chunks; |
1600 | unsigned long pages; | 1657 | unsigned long pages; |
1601 | struct file *file = mddev->bitmap_file; | 1658 | struct file *file = mddev->bitmap_info.file; |
1602 | int err; | 1659 | int err; |
1603 | sector_t start; | 1660 | sector_t start; |
1661 | struct sysfs_dirent *bm; | ||
1604 | 1662 | ||
1605 | BUILD_BUG_ON(sizeof(bitmap_super_t) != 256); | 1663 | BUILD_BUG_ON(sizeof(bitmap_super_t) != 256); |
1606 | 1664 | ||
1607 | if (!file && !mddev->bitmap_offset) /* bitmap disabled, nothing to do */ | 1665 | if (!file && !mddev->bitmap_info.offset) /* bitmap disabled, nothing to do */ |
1608 | return 0; | 1666 | return 0; |
1609 | 1667 | ||
1610 | BUG_ON(file && mddev->bitmap_offset); | 1668 | BUG_ON(file && mddev->bitmap_info.offset); |
1611 | 1669 | ||
1612 | bitmap = kzalloc(sizeof(*bitmap), GFP_KERNEL); | 1670 | bitmap = kzalloc(sizeof(*bitmap), GFP_KERNEL); |
1613 | if (!bitmap) | 1671 | if (!bitmap) |
@@ -1620,8 +1678,14 @@ int bitmap_create(mddev_t *mddev) | |||
1620 | 1678 | ||
1621 | bitmap->mddev = mddev; | 1679 | bitmap->mddev = mddev; |
1622 | 1680 | ||
1681 | bm = sysfs_get_dirent(mddev->kobj.sd, "bitmap"); | ||
1682 | if (bm) { | ||
1683 | bitmap->sysfs_can_clear = sysfs_get_dirent(bm, "can_clear"); | ||
1684 | sysfs_put(bm); | ||
1685 | } else | ||
1686 | bitmap->sysfs_can_clear = NULL; | ||
1687 | |||
1623 | bitmap->file = file; | 1688 | bitmap->file = file; |
1624 | bitmap->offset = mddev->bitmap_offset; | ||
1625 | if (file) { | 1689 | if (file) { |
1626 | get_file(file); | 1690 | get_file(file); |
1627 | /* As future accesses to this file will use bmap, | 1691 | /* As future accesses to this file will use bmap, |
@@ -1630,12 +1694,22 @@ int bitmap_create(mddev_t *mddev) | |||
1630 | */ | 1694 | */ |
1631 | vfs_fsync(file, file->f_dentry, 1); | 1695 | vfs_fsync(file, file->f_dentry, 1); |
1632 | } | 1696 | } |
1633 | /* read superblock from bitmap file (this sets bitmap->chunksize) */ | 1697 | /* read superblock from bitmap file (this sets mddev->bitmap_info.chunksize) */ |
1634 | err = bitmap_read_sb(bitmap); | 1698 | if (!mddev->bitmap_info.external) |
1699 | err = bitmap_read_sb(bitmap); | ||
1700 | else { | ||
1701 | err = 0; | ||
1702 | if (mddev->bitmap_info.chunksize == 0 || | ||
1703 | mddev->bitmap_info.daemon_sleep == 0) | ||
1704 | /* chunksize and time_base need to be | ||
1705 | * set first. */ | ||
1706 | err = -EINVAL; | ||
1707 | } | ||
1635 | if (err) | 1708 | if (err) |
1636 | goto error; | 1709 | goto error; |
1637 | 1710 | ||
1638 | bitmap->chunkshift = ffz(~bitmap->chunksize); | 1711 | bitmap->daemon_lastrun = jiffies; |
1712 | bitmap->chunkshift = ffz(~mddev->bitmap_info.chunksize); | ||
1639 | 1713 | ||
1640 | /* now that chunksize and chunkshift are set, we can use these macros */ | 1714 | /* now that chunksize and chunkshift are set, we can use these macros */ |
1641 | chunks = (blocks + CHUNK_BLOCK_RATIO(bitmap) - 1) >> | 1715 | chunks = (blocks + CHUNK_BLOCK_RATIO(bitmap) - 1) >> |
@@ -1677,7 +1751,8 @@ int bitmap_create(mddev_t *mddev) | |||
1677 | 1751 | ||
1678 | mddev->bitmap = bitmap; | 1752 | mddev->bitmap = bitmap; |
1679 | 1753 | ||
1680 | mddev->thread->timeout = bitmap->daemon_sleep * HZ; | 1754 | mddev->thread->timeout = mddev->bitmap_info.daemon_sleep; |
1755 | md_wakeup_thread(mddev->thread); | ||
1681 | 1756 | ||
1682 | bitmap_update_sb(bitmap); | 1757 | bitmap_update_sb(bitmap); |
1683 | 1758 | ||
@@ -1688,6 +1763,264 @@ int bitmap_create(mddev_t *mddev) | |||
1688 | return err; | 1763 | return err; |
1689 | } | 1764 | } |
1690 | 1765 | ||
1766 | static ssize_t | ||
1767 | location_show(mddev_t *mddev, char *page) | ||
1768 | { | ||
1769 | ssize_t len; | ||
1770 | if (mddev->bitmap_info.file) { | ||
1771 | len = sprintf(page, "file"); | ||
1772 | } else if (mddev->bitmap_info.offset) { | ||
1773 | len = sprintf(page, "%+lld", (long long)mddev->bitmap_info.offset); | ||
1774 | } else | ||
1775 | len = sprintf(page, "none"); | ||
1776 | len += sprintf(page+len, "\n"); | ||
1777 | return len; | ||
1778 | } | ||
1779 | |||
1780 | static ssize_t | ||
1781 | location_store(mddev_t *mddev, const char *buf, size_t len) | ||
1782 | { | ||
1783 | |||
1784 | if (mddev->pers) { | ||
1785 | if (!mddev->pers->quiesce) | ||
1786 | return -EBUSY; | ||
1787 | if (mddev->recovery || mddev->sync_thread) | ||
1788 | return -EBUSY; | ||
1789 | } | ||
1790 | |||
1791 | if (mddev->bitmap || mddev->bitmap_info.file || | ||
1792 | mddev->bitmap_info.offset) { | ||
1793 | /* bitmap already configured. Only option is to clear it */ | ||
1794 | if (strncmp(buf, "none", 4) != 0) | ||
1795 | return -EBUSY; | ||
1796 | if (mddev->pers) { | ||
1797 | mddev->pers->quiesce(mddev, 1); | ||
1798 | bitmap_destroy(mddev); | ||
1799 | mddev->pers->quiesce(mddev, 0); | ||
1800 | } | ||
1801 | mddev->bitmap_info.offset = 0; | ||
1802 | if (mddev->bitmap_info.file) { | ||
1803 | struct file *f = mddev->bitmap_info.file; | ||
1804 | mddev->bitmap_info.file = NULL; | ||
1805 | restore_bitmap_write_access(f); | ||
1806 | fput(f); | ||
1807 | } | ||
1808 | } else { | ||
1809 | /* No bitmap, OK to set a location */ | ||
1810 | long long offset; | ||
1811 | if (strncmp(buf, "none", 4) == 0) | ||
1812 | /* nothing to be done */; | ||
1813 | else if (strncmp(buf, "file:", 5) == 0) { | ||
1814 | /* Not supported yet */ | ||
1815 | return -EINVAL; | ||
1816 | } else { | ||
1817 | int rv; | ||
1818 | if (buf[0] == '+') | ||
1819 | rv = strict_strtoll(buf+1, 10, &offset); | ||
1820 | else | ||
1821 | rv = strict_strtoll(buf, 10, &offset); | ||
1822 | if (rv) | ||
1823 | return rv; | ||
1824 | if (offset == 0) | ||
1825 | return -EINVAL; | ||
1826 | if (mddev->bitmap_info.external == 0 && | ||
1827 | mddev->major_version == 0 && | ||
1828 | offset != mddev->bitmap_info.default_offset) | ||
1829 | return -EINVAL; | ||
1830 | mddev->bitmap_info.offset = offset; | ||
1831 | if (mddev->pers) { | ||
1832 | mddev->pers->quiesce(mddev, 1); | ||
1833 | rv = bitmap_create(mddev); | ||
1834 | if (rv) { | ||
1835 | bitmap_destroy(mddev); | ||
1836 | mddev->bitmap_info.offset = 0; | ||
1837 | } | ||
1838 | mddev->pers->quiesce(mddev, 0); | ||
1839 | if (rv) | ||
1840 | return rv; | ||
1841 | } | ||
1842 | } | ||
1843 | } | ||
1844 | if (!mddev->external) { | ||
1845 | /* Ensure new bitmap info is stored in | ||
1846 | * metadata promptly. | ||
1847 | */ | ||
1848 | set_bit(MD_CHANGE_DEVS, &mddev->flags); | ||
1849 | md_wakeup_thread(mddev->thread); | ||
1850 | } | ||
1851 | return len; | ||
1852 | } | ||
1853 | |||
1854 | static struct md_sysfs_entry bitmap_location = | ||
1855 | __ATTR(location, S_IRUGO|S_IWUSR, location_show, location_store); | ||
1856 | |||
1857 | static ssize_t | ||
1858 | timeout_show(mddev_t *mddev, char *page) | ||
1859 | { | ||
1860 | ssize_t len; | ||
1861 | unsigned long secs = mddev->bitmap_info.daemon_sleep / HZ; | ||
1862 | unsigned long jifs = mddev->bitmap_info.daemon_sleep % HZ; | ||
1863 | |||
1864 | len = sprintf(page, "%lu", secs); | ||
1865 | if (jifs) | ||
1866 | len += sprintf(page+len, ".%03u", jiffies_to_msecs(jifs)); | ||
1867 | len += sprintf(page+len, "\n"); | ||
1868 | return len; | ||
1869 | } | ||
1870 | |||
1871 | static ssize_t | ||
1872 | timeout_store(mddev_t *mddev, const char *buf, size_t len) | ||
1873 | { | ||
1874 | /* timeout can be set at any time */ | ||
1875 | unsigned long timeout; | ||
1876 | int rv = strict_strtoul_scaled(buf, &timeout, 4); | ||
1877 | if (rv) | ||
1878 | return rv; | ||
1879 | |||
1880 | /* just to make sure we don't overflow... */ | ||
1881 | if (timeout >= LONG_MAX / HZ) | ||
1882 | return -EINVAL; | ||
1883 | |||
1884 | timeout = timeout * HZ / 10000; | ||
1885 | |||
1886 | if (timeout >= MAX_SCHEDULE_TIMEOUT) | ||
1887 | timeout = MAX_SCHEDULE_TIMEOUT-1; | ||
1888 | if (timeout < 1) | ||
1889 | timeout = 1; | ||
1890 | mddev->bitmap_info.daemon_sleep = timeout; | ||
1891 | if (mddev->thread) { | ||
1892 | /* if thread->timeout is MAX_SCHEDULE_TIMEOUT, then | ||
1893 | * the bitmap is all clean and we don't need to | ||
1894 | * adjust the timeout right now | ||
1895 | */ | ||
1896 | if (mddev->thread->timeout < MAX_SCHEDULE_TIMEOUT) { | ||
1897 | mddev->thread->timeout = timeout; | ||
1898 | md_wakeup_thread(mddev->thread); | ||
1899 | } | ||
1900 | } | ||
1901 | return len; | ||
1902 | } | ||
1903 | |||
1904 | static struct md_sysfs_entry bitmap_timeout = | ||
1905 | __ATTR(time_base, S_IRUGO|S_IWUSR, timeout_show, timeout_store); | ||
1906 | |||
1907 | static ssize_t | ||
1908 | backlog_show(mddev_t *mddev, char *page) | ||
1909 | { | ||
1910 | return sprintf(page, "%lu\n", mddev->bitmap_info.max_write_behind); | ||
1911 | } | ||
1912 | |||
1913 | static ssize_t | ||
1914 | backlog_store(mddev_t *mddev, const char *buf, size_t len) | ||
1915 | { | ||
1916 | unsigned long backlog; | ||
1917 | int rv = strict_strtoul(buf, 10, &backlog); | ||
1918 | if (rv) | ||
1919 | return rv; | ||
1920 | if (backlog > COUNTER_MAX) | ||
1921 | return -EINVAL; | ||
1922 | mddev->bitmap_info.max_write_behind = backlog; | ||
1923 | return len; | ||
1924 | } | ||
1925 | |||
1926 | static struct md_sysfs_entry bitmap_backlog = | ||
1927 | __ATTR(backlog, S_IRUGO|S_IWUSR, backlog_show, backlog_store); | ||
1928 | |||
1929 | static ssize_t | ||
1930 | chunksize_show(mddev_t *mddev, char *page) | ||
1931 | { | ||
1932 | return sprintf(page, "%lu\n", mddev->bitmap_info.chunksize); | ||
1933 | } | ||
1934 | |||
1935 | static ssize_t | ||
1936 | chunksize_store(mddev_t *mddev, const char *buf, size_t len) | ||
1937 | { | ||
1938 | /* Can only be changed when no bitmap is active */ | ||
1939 | int rv; | ||
1940 | unsigned long csize; | ||
1941 | if (mddev->bitmap) | ||
1942 | return -EBUSY; | ||
1943 | rv = strict_strtoul(buf, 10, &csize); | ||
1944 | if (rv) | ||
1945 | return rv; | ||
1946 | if (csize < 512 || | ||
1947 | !is_power_of_2(csize)) | ||
1948 | return -EINVAL; | ||
1949 | mddev->bitmap_info.chunksize = csize; | ||
1950 | return len; | ||
1951 | } | ||
1952 | |||
1953 | static struct md_sysfs_entry bitmap_chunksize = | ||
1954 | __ATTR(chunksize, S_IRUGO|S_IWUSR, chunksize_show, chunksize_store); | ||
1955 | |||
1956 | static ssize_t metadata_show(mddev_t *mddev, char *page) | ||
1957 | { | ||
1958 | return sprintf(page, "%s\n", (mddev->bitmap_info.external | ||
1959 | ? "external" : "internal")); | ||
1960 | } | ||
1961 | |||
1962 | static ssize_t metadata_store(mddev_t *mddev, const char *buf, size_t len) | ||
1963 | { | ||
1964 | if (mddev->bitmap || | ||
1965 | mddev->bitmap_info.file || | ||
1966 | mddev->bitmap_info.offset) | ||
1967 | return -EBUSY; | ||
1968 | if (strncmp(buf, "external", 8) == 0) | ||
1969 | mddev->bitmap_info.external = 1; | ||
1970 | else if (strncmp(buf, "internal", 8) == 0) | ||
1971 | mddev->bitmap_info.external = 0; | ||
1972 | else | ||
1973 | return -EINVAL; | ||
1974 | return len; | ||
1975 | } | ||
1976 | |||
1977 | static struct md_sysfs_entry bitmap_metadata = | ||
1978 | __ATTR(metadata, S_IRUGO|S_IWUSR, metadata_show, metadata_store); | ||
1979 | |||
1980 | static ssize_t can_clear_show(mddev_t *mddev, char *page) | ||
1981 | { | ||
1982 | int len; | ||
1983 | if (mddev->bitmap) | ||
1984 | len = sprintf(page, "%s\n", (mddev->bitmap->need_sync ? | ||
1985 | "false" : "true")); | ||
1986 | else | ||
1987 | len = sprintf(page, "\n"); | ||
1988 | return len; | ||
1989 | } | ||
1990 | |||
1991 | static ssize_t can_clear_store(mddev_t *mddev, const char *buf, size_t len) | ||
1992 | { | ||
1993 | if (mddev->bitmap == NULL) | ||
1994 | return -ENOENT; | ||
1995 | if (strncmp(buf, "false", 5) == 0) | ||
1996 | mddev->bitmap->need_sync = 1; | ||
1997 | else if (strncmp(buf, "true", 4) == 0) { | ||
1998 | if (mddev->degraded) | ||
1999 | return -EBUSY; | ||
2000 | mddev->bitmap->need_sync = 0; | ||
2001 | } else | ||
2002 | return -EINVAL; | ||
2003 | return len; | ||
2004 | } | ||
2005 | |||
2006 | static struct md_sysfs_entry bitmap_can_clear = | ||
2007 | __ATTR(can_clear, S_IRUGO|S_IWUSR, can_clear_show, can_clear_store); | ||
2008 | |||
2009 | static struct attribute *md_bitmap_attrs[] = { | ||
2010 | &bitmap_location.attr, | ||
2011 | &bitmap_timeout.attr, | ||
2012 | &bitmap_backlog.attr, | ||
2013 | &bitmap_chunksize.attr, | ||
2014 | &bitmap_metadata.attr, | ||
2015 | &bitmap_can_clear.attr, | ||
2016 | NULL | ||
2017 | }; | ||
2018 | struct attribute_group md_bitmap_group = { | ||
2019 | .name = "bitmap", | ||
2020 | .attrs = md_bitmap_attrs, | ||
2021 | }; | ||
2022 | |||
2023 | |||
1691 | /* the bitmap API -- for raid personalities */ | 2024 | /* the bitmap API -- for raid personalities */ |
1692 | EXPORT_SYMBOL(bitmap_startwrite); | 2025 | EXPORT_SYMBOL(bitmap_startwrite); |
1693 | EXPORT_SYMBOL(bitmap_endwrite); | 2026 | EXPORT_SYMBOL(bitmap_endwrite); |