+ /* if the device is bigger than 8Gig, save 64k for bitmap usage,
+ * if bigger than 200Gig, save 128k
+ */
+ if (devsize-64*2 >= 200*1024*1024*2)
+ devsize -= 128*2;
+ else if (devsize >= 8*1024*1024*2)
+ devsize -= 64*2;
+
+ switch(st->minor_version) {
+ case 0:
+ /* at end */
+ return ((devsize - 8*2 ) & ~(4*2-1));
+ case 1:
+ /* at start, 4K for superblock and possible bitmap */
+ return devsize - 4*2;
+ case 2:
+ /* 4k from start, 4K for superblock and possible bitmap */
+ return devsize - (4+4)*2;
+ }
+ return 0;
+}
+
+static int
+add_internal_bitmap1(struct supertype *st, void *sbv,
+ int *chunkp, int delay, int write_behind,
+ unsigned long long size,
+ int may_change, int major)
+{
+ /*
+ * If not may_change, then this is a 'Grow', and the bitmap
+ * must fit after the superblock.
+ * If may_change, then this is create, and we can put the bitmap
+ * before the superblock if we like, or may move the start.
+ * If !may_change, the bitmap MUST live at offset of 1K, until
+ * we get a sysfs interface.
+ *
+ * size is in sectors, chunk is in bytes !!!
+ */
+
+ unsigned long long bits;
+ unsigned long long max_bits;
+ unsigned long long min_chunk;
+ long offset;
+ int chunk = *chunkp;
+ int room = 0;
+ struct mdp_superblock_1 *sb = sbv;
+ bitmap_super_t *bms = (bitmap_super_t*)(((char*)sb) + 1024);
+
+ switch(st->minor_version) {
+ case 0:
+ /* either 3K after the superblock, or some amount of space
+ * before.
+ */
+ if (may_change) {
+ /* We are creating array, so we *know* how much room has
+ * been left.
+ */
+ offset = 0;
+ if (__le64_to_cpu(sb->size) >= 200*1024*1024*2)
+ room = 128*2;
+ else if (__le64_to_cpu(sb->size) > 8*1024*1024*2)
+ room = 64*2;
+ else {
+ room = 3*2;
+ offset = 2;
+ }
+ } else {
+ room = __le64_to_cpu(sb->super_offset)
+ - __le64_to_cpu(sb->data_offset)
+ - __le64_to_cpu(sb->data_size);
+ /* remove '1 ||' when we can set offset via sysfs */
+ if (1 || (room < 3*2 &&
+ __le32_to_cpu(sb->max_dev) <= 384)) {
+ room = 3*2;
+ offset = 1*2;
+ } else {
+ offset = 0; /* means movable offset */
+ }
+ }
+ break;
+ case 1:
+ case 2: /* between superblock and data */
+ if (may_change) {
+ offset = 4*2;
+ if (__le64_to_cpu(sb->size) >= 200*1024*1024*2)
+ room = 128*2;
+ else if (__le64_to_cpu(sb->size) > 8*1024*1024*2)
+ room = 64*2;
+ else
+ room = 3*2;
+ } else {
+ room = __le64_to_cpu(sb->data_offset)
+ - __le64_to_cpu(sb->super_offset);
+ if (1 || __le32_to_cpu(sb->max_dev) <= 384) {
+ room -= 2;
+ offset = 2;
+ } else {
+ room -= 4*2;
+ offset = 4*2;
+ }
+ }
+ break;
+ }
+
+ if (chunk == UnSet && room > 128*2)
+ /* Limit to 128K of bitmap when chunk size not requested */
+ room = 128*2;
+
+ max_bits = (room * 512 - sizeof(bitmap_super_t)) * 8;
+
+ min_chunk = 4096; /* sub-page chunks don't work yet.. */
+ bits = (size*512)/min_chunk +1;
+ while (bits > max_bits) {
+ min_chunk *= 2;
+ bits = (bits+1)/2;
+ }
+ if (chunk == UnSet)
+ chunk = min_chunk;
+ else if (chunk < min_chunk)
+ return 0; /* chunk size too small */
+ if (chunk == 0) /* rounding problem */
+ return 0;
+
+ if (offset == 0) {
+ bits = (size*512) / chunk + 1;
+ room = ((bits+7)/8 + sizeof(bitmap_super_t) +511)/512;
+ offset = -room;
+ }
+
+ sb->bitmap_offset = __cpu_to_le32(offset);
+
+ sb->feature_map = __cpu_to_le32(__le32_to_cpu(sb->feature_map) | 1);
+ memset(bms, 0, sizeof(*bms));
+ bms->magic = __cpu_to_le32(BITMAP_MAGIC);
+ bms->version = __cpu_to_le32(major);
+ uuid_from_super1((int*)bms->uuid, sb);
+ bms->chunksize = __cpu_to_le32(chunk);
+ bms->daemon_sleep = __cpu_to_le32(delay);
+ bms->sync_size = __cpu_to_le64(size);
+ bms->write_behind = __cpu_to_le32(write_behind);
+
+ *chunkp = chunk;
+ return 1;
+}
+
+
+static void locate_bitmap1(struct supertype *st, int fd, void *sbv)
+{
+ unsigned long long offset;
+ struct mdp_superblock_1 *sb;
+ int mustfree = 0;
+
+ if (!sbv) {
+ if (st->ss->load_super(st, fd, &sbv, NULL))
+ return; /* no error I hope... */
+ mustfree = 1;
+ }
+ sb = sbv;
+
+ offset = __le64_to_cpu(sb->super_offset);
+ offset += (long) __le32_to_cpu(sb->bitmap_offset);
+ if (mustfree)
+ free(sb);
+ lseek64(fd, offset<<9, 0);
+}
+
+static int write_bitmap1(struct supertype *st, int fd, void *sbv)
+{
+ struct mdp_superblock_1 *sb = sbv;
+ bitmap_super_t *bms = (bitmap_super_t*)(((char*)sb)+1024);
+ int rv = 0;
+
+ int towrite, n;
+ char buf[4096];
+
+ locate_bitmap1(st, fd, sbv);
+
+ if (write(fd, ((char*)sb)+1024, sizeof(bitmap_super_t)) !=
+ sizeof(bitmap_super_t))
+ return -2;
+ towrite = __le64_to_cpu(bms->sync_size) / (__le32_to_cpu(bms->chunksize)>>9);
+ towrite = (towrite+7) >> 3; /* bits to bytes */
+ memset(buf, 0xff, sizeof(buf));
+ while (towrite > 0) {
+ n = towrite;
+ if (n > sizeof(buf))
+ n = sizeof(buf);
+ n = write(fd, buf, n);
+ if (n > 0)
+ towrite -= n;
+ else
+ break;
+ }
+ fsync(fd);
+ if (towrite)
+ rv = -2;
+
+ return rv;