This only works for 1.x metadata.
The idea is to reserve some space at the start of each device for
storing a boot loader.
As a boot loader will need sector 0, metadata 1.1 doesn't make sense.
The number given to reserve-space will be in kilobytes, though a
suffix of M can give Megabytes (or G gigabytes, but 2 is the limit).
When a spare is added to an array, we preserve the amount of reserved
space.
It is not yet possible to hot-add an internal bitmap to a 1.1 or 1.2
array with reserved space.
This option is not yet documented.
In future, we might copy the content of the reserved space from one
drive to any new spare that is added.
Signed-off-by: NeilBrown <neilb@suse.de>
char *name, char *homehost, int *uuid,
int subdevs, mddev_dev_t devlist,
int runstop, int verbose, int force, int assume_clean,
- char *bitmap_file, int bitmap_chunk, int write_behind, int delay)
+ char *bitmap_file, int bitmap_chunk, int write_behind, int delay,
+ unsigned long reserve_space)
{
/*
* Create a new raid array.
*/
int i;
char *name = "default";
- if (level >= 1 && ldsize > (0x7fffffffULL<<10))
+ if (reserve_space ||
+ (level >= 1 && ldsize > (0x7fffffffULL<<10)))
name = "default/large";
for(i=0; !st && superlist[i]; i++)
st = superlist[i]->match_metadata_desc(name);
st->ss->major,
st->minor_version);
}
- freesize = st->ss->avail_size(st, ldsize >> 9);
+ freesize = st->ss->avail_size(st, ldsize >> 9, reserve_space);
if (freesize == 0) {
fprintf(stderr, Name ": %s is too small: %luK\n",
dname, (unsigned long)(ldsize>>10));
name += 2;
}
}
- if (!st->ss->init_super(st, &info.array, size, name, homehost, uuid))
+ if (!st->ss->init_super(st, &info.array, size, name, homehost,
+ uuid, reserve_space))
return 1;
if (bitmap_file && vers < 9003) {
)
st->ss->write_bitmap(st, fd2);
else {
- fprintf(stderr, Name ": failed to create internal bitmap - chunksize problem.\n");
+ fprintf(stderr, Name ": failed to create internal bitmap.\n");
close(fd2);
return 1;
}
mdu_array_info_t info;
info.major_version = -1; /* zero superblock */
st->ss->free_super(st);
- st->ss->init_super(st, &info, 0, "", NULL, NULL);
+ st->ss->init_super(st, &info, 0, "", NULL, NULL, 0);
if (st->ss->store_super(st, fd)) {
if (!quiet)
fprintf(stderr, Name ": Could not zero superblock on %s\n",
}
/* Make sure device is large enough */
- if (tst->ss->avail_size(tst, ldsize/512) <
+ if (tst->ss->avail_size(tst, ldsize/512, 0) <
array_size) {
fprintf(stderr, Name ": %s not large enough to join array\n",
dv->devname);
{"homehost", 1, 0, HomeHost},
{"auto-update-homehost", 0, 0, AutoHomeHost},
{"symlinks", 1, 0, Symlinks},
+ {"reserve-space", 1, 0, ReserveSpace},
/* For assemble */
{"uuid", 1, 0, 'u'},
int chunk = 0;
long long size = -1;
+ unsigned long reserve_space = 0;
int level = UnSet;
int layout = UnSet;
int raiddisks = 0;
case O(INCREMENTAL, 'r'):
rebuild_map = 1;
continue;
+
+ case O(CREATE, ReserveSpace):
+ reserve_space = strtoul(optarg, &c, 10)*2;
+ if (reserve_space > 0) {
+ switch (*c++) {
+ default: c--; break;
+ case 'K': break;
+ case 'M':
+ reserve_space <<= 10;
+ break;
+ case 'G':
+ reserve_space <<= 20;
+ break;
+ }
+ }
+ if (reserve_space == 0 || *c) {
+ fprintf(stderr, Name ": Invalid value for reserve-space: %s\n",
+ optarg);
+ exit(2);
+ }
+ continue;
}
/* We have now processed all the valid options. Anything else is
* an error
raiddisks, sparedisks, ident.name, homehost,
ident.uuid_set ? ident.uuid : NULL,
devs_found-1, devlist->next, runstop, verbose-quiet, force, assume_clean,
- bitmap_file, bitmap_chunk, write_behind, delay);
+ bitmap_file, bitmap_chunk, write_behind, delay,
+ reserve_space);
break;
case MISC:
if (devmode == 'E') {
AutoHomeHost,
Symlinks,
AutoDetect,
+ ReserveSpace,
};
/* structures read from config file */
int uuid_set, char *homehost);
int (*init_super)(struct supertype *st, mdu_array_info_t *info,
unsigned long long size, char *name,
- char *homehost, int *uuid);
+ char *homehost, int *uuid,
+ unsigned long reserve_space);
void (*add_to_super)(struct supertype *st, mdu_disk_info_t *dinfo);
int (*store_super)(struct supertype *st, int fd);
int (*write_init_super)(struct supertype *st, mdu_disk_info_t *dinfo,
int (*compare_super)(struct supertype *st, struct supertype *tst);
int (*load_super)(struct supertype *st, int fd, char *devname);
struct supertype * (*match_metadata_desc)(char *arg);
- __u64 (*avail_size)(struct supertype *st, __u64 size);
+ __u64 (*avail_size)(struct supertype *st, __u64 size,
+ unsigned long reserve_space);
int (*add_internal_bitmap)(struct supertype *st, int *chunkp,
int delay, int write_behind,
unsigned long long size, int may_change, int major);
char *name, char *homehost, int *uuid,
int subdevs, mddev_dev_t devlist,
int runstop, int verbose, int force, int assume_clean,
- char *bitmap_file, int bitmap_chunk, int write_behind, int delay);
+ char *bitmap_file, int bitmap_chunk, int write_behind, int delay,
+ unsigned long reserve_space);
extern int Detail(char *dev, int brief, int export, int test, char *homehost);
extern int Query(char *dev);
static int init_super0(struct supertype *st, mdu_array_info_t *info,
unsigned long long size, char *ignored_name, char *homehost,
- int *uuid)
+ int *uuid, unsigned long reserve_space)
{
mdp_super_t *sb = malloc(MD_SB_BYTES + sizeof(bitmap_super_t));
int spares;
memset(sb, 0, MD_SB_BYTES + sizeof(bitmap_super_t));
st->sb = sb;
+ if (reserve_space) {
+ /* impossible - should already have been caught
+ * by avail_size
+ */
+ return 0;
+ }
+
if (info->major_version == -1) {
/* zeroing the superblock */
return 0;
return NULL;
}
-static __u64 avail_size0(struct supertype *st, __u64 devsize)
+static __u64 avail_size0(struct supertype *st, __u64 devsize,
+ unsigned long reserve_space)
{
+ if (reserve_space) {
+ fprintf(stderr, Name ": --reserve-space is not supported v0.90 metadata.\n"
+ " use e.g. --metadata=1.0\n");
+ return 0ULL;
+ }
if (devsize < MD_RESERVED_SECTORS)
return 0ULL;
return MD_NEW_SIZE_SECTORS(devsize);
return rv;
}
+static unsigned long choose_bm_space(unsigned long devsize)
+{
+ /* if the device is bigger than 8Gig, save 64k for bitmap usage,
+ * if bigger than 200Gig, save 128k
+ */
+ if (devsize < 64*2) return 0;
+ if (devsize - 64*2 >= 200*1024*1024*2)
+ return 128*2;
+ if (devsize - 4*2 > 8*1024*1024*2)
+ return 64*2;
+ return 4*2;
+}
+
static int init_super1(struct supertype *st, mdu_array_info_t *info,
- unsigned long long size, char *name, char *homehost, int *uuid)
+ unsigned long long size, char *name, char *homehost,
+ int *uuid, unsigned long reserve_space)
{
struct mdp_superblock_1 *sb = malloc(1024 + sizeof(bitmap_super_t) +
sizeof(struct misc_dev_info));
int spares;
int rfd;
+ int bm_space;
char defname[10];
memset(sb, 0, 1024);
sb->chunksize = __cpu_to_le32(info->chunk_size>>9);
sb->raid_disks = __cpu_to_le32(info->raid_disks);
- sb->data_offset = __cpu_to_le64(0);
- sb->data_size = __cpu_to_le64(0);
+ sb->data_size = __cpu_to_le64(0);
sb->super_offset = __cpu_to_le64(0);
sb->recovery_offset = __cpu_to_le64(0);
+ bm_space = choose_bm_space(size*2);
+
+ switch(st->minor_version) {
+ case 0:
+ sb->data_offset = __cpu_to_le64(reserve_space);
+ sb->bitmap_offset = __cpu_to_le32(0);
+ break;
+ case 1:
+ sb->super_offset = __cpu_to_le64(0);
+ sb->data_offset = __cpu_to_le64(4*2 +
+ reserve_space + bm_space);
+ sb->bitmap_offset = __cpu_to_le32(reserve_space + 4*2);
+ break;
+ case 2:
+ sb->super_offset = __cpu_to_le64(4*2);
+ sb->data_offset = __cpu_to_le64(4*2 + 4*2 +
+ reserve_space + bm_space);
+ sb->bitmap_offset = __cpu_to_le32(reserve_space + 4*2);
+ break;
+ }
+
sb->utime = sb->ctime;
sb->events = __cpu_to_le64(1);
if (info->state & (1<<MD_SB_CLEAN))
static int load_super1(struct supertype *st, int fd, char *devname);
-static unsigned long choose_bm_space(unsigned long devsize)
-{
- /* if the device is bigger than 8Gig, save 64k for bitmap usage,
- * if bigger than 200Gig, save 128k
- */
- if (devsize < 64*2) return 0;
- if (devsize - 64*2 >= 200*1024*1024*2)
- return 128*2;
- if (devsize - 4*2 > 8*1024*1024*2)
- return 64*2;
- return 4*2;
-}
-
static int write_init_super1(struct supertype *st,
mdu_disk_info_t *dinfo, char *devname)
{
* Depending on the array size, we might leave extra space
* for a bitmap.
*/
- array_size = __le64_to_cpu(sb->size);
- /* work out how much space we left for a bitmap */
- bm_space = choose_bm_space(array_size);
switch(st->minor_version) {
case 0:
sb_offset -= 8*2;
sb_offset &= ~(4*2-1);
sb->super_offset = __cpu_to_le64(sb_offset);
- sb->data_offset = __cpu_to_le64(0);
+
+ array_size = __le64_to_cpu(sb->size);
+ /* work out how much space we left for a bitmap */
+ bm_space = choose_bm_space(array_size);
if (sb_offset - bm_space < array_size)
bm_space = sb_offset - array_size;
- sb->data_size = __cpu_to_le64(sb_offset - bm_space);
+ sb->data_size = __cpu_to_le64(sb_offset - bm_space -
+ __le64_to_cpu(sb->data_offset));
break;
case 1:
- sb->super_offset = __cpu_to_le64(0);
- if (4*2 + bm_space + __le64_to_cpu(sb->size) > dsize)
- bm_space = dsize - __le64_to_cpu(sb->size) - 4*2;
- sb->data_offset = __cpu_to_le64(bm_space + 4*2);
- sb->data_size = __cpu_to_le64(dsize - bm_space - 4*2);
+ sb_offset = 0;
+ sb->data_size = __cpu_to_le64(dsize -
+ __le64_to_cpu(sb->data_offset));
break;
case 2:
sb_offset = 4*2;
- sb->super_offset = __cpu_to_le64(4*2);
- if (4*2 + 4*2 + bm_space + __le64_to_cpu(sb->size) > dsize)
- bm_space = dsize - __le64_to_cpu(sb->size) - 4*2 - 4*2;
- sb->data_offset = __cpu_to_le64(4*2 + 4*2 + bm_space);
- sb->data_size = __cpu_to_le64(dsize - 4*2 - 4*2 - bm_space );
+ sb->data_size = __cpu_to_le64(dsize -
+ __le64_to_cpu(sb->data_offset));
break;
default:
return -EINVAL;
* superblock type st, and reserving 'reserve' sectors for
* a possible bitmap
*/
-static __u64 avail_size1(struct supertype *st, __u64 devsize)
+static __u64 avail_size1(struct supertype *st, __u64 devsize,
+ unsigned long reserve_space)
{
struct mdp_superblock_1 *super = st->sb;
if (devsize < 24)
/* FALL THROUGH */
case 0:
/* at end */
- return ((devsize - 8*2 ) & ~(4*2-1));
+ if (reserve_space == 0 && super)
+ reserve_space = __le64_to_cpu(super->data_offset);
+ return ((devsize - 8*2 ) & ~(4*2-1)) - reserve_space;
case 1:
/* at start, 4K for superblock and possible bitmap */
- return devsize - 4*2;
+ if (reserve_space == 0 && super)
+ return devsize - __le64_to_cpu(super->data_offset);
+ return devsize - 4*2 - reserve_space;
case 2:
/* 4k from start, 4K for superblock and possible bitmap */
- return devsize - (4+4)*2;
+ if (reserve_space == 0 && super)
+ return devsize - __le64_to_cpu(super->data_offset);
+ return devsize - (4+4)*2 - reserve_space;
}
return 0;
}
case 1:
case 2: /* between superblock and data */
if (may_change) {
- offset = 4*2;
+ int room2;
+ /* init_super already set this up */
+ offset = __le32_to_cpu(sb->bitmap_offset);
+ if (offset == 0)
+ offset = 4*2;
room = choose_bm_space(__le64_to_cpu(sb->size));
+ room2 = __le64_to_cpu(sb->data_offset) - offset;
+ if (room > room2)
+ room = room2;
+
} 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;
+ if (__le32_to_cpu(sb->bitmap_offset) < 4*2) {
+ 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;
+ }
} else {
- room -= 4*2;
- offset = 4*2;
+ room = __le64_to_cpu(sb->data_offset) -
+ __le32_to_cpu(sb->bitmap_offset);
+ offset = __le32_to_cpu(sb->bitmap_offset);
+ fprintf(stderr, Name ": adding internal bitmaps "
+ "to array with a reserved offset is not "
+ "yet supported, sorry.\n");
+ return 0;
}
}
break;