From 042e05814a1f16eee8f782d1c3b19793f2106af3 Mon Sep 17 00:00:00 2001
From: NeilBrown <neilb@suse.de>
Date: Wed, 28 Mar 2012 17:29:55 +1100
Subject: [PATCH] Add data_offset arg to ->init_super and use it in super1.c

So if ->data_offset is already set, use that rather than
computing one.

Signed-off-by: NeilBrown <neilb@suse.de>
---
 Create.c      |    2 -
 Kill.c        |    2 -
 mdadm.h       |    2 -
 super-ddf.c   |   14 +++++++---
 super-intel.c |   13 +++++++--
 super0.c      |    7 ++++-
 super1.c      |   81 ++++++++++++++++++++++++++++++++++------------------------
 7 files changed, 77 insertions(+), 44 deletions(-)

--- mdadm-3.2.6.orig/Create.c
+++ mdadm-3.2.6/Create.c
@@ -697,7 +697,7 @@ int Create(struct supertype *st, char *m
 				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, -1LL))
 		goto abort_locked;
 
 	total_slots = info.array.nr_disks;
--- mdadm-3.2.6.orig/Kill.c
+++ mdadm-3.2.6/Kill.c
@@ -63,7 +63,7 @@ int Kill(char *dev, struct supertype *st
 	rv = st->ss->load_super(st, fd, dev);
 	if (rv == 0 || (force && rv >= 2)) {
 		st->ss->free_super(st);
-		st->ss->init_super(st, NULL, 0, "", NULL, NULL);
+		st->ss->init_super(st, NULL, 0, "", NULL, NULL, -1LL);
 		if (st->ss->store_super(st, fd)) {
 			if (!quiet)
 				fprintf(stderr, Name ": Could not zero superblock on %s\n",
--- mdadm-3.2.6.orig/mdadm.h
+++ mdadm-3.2.6/mdadm.h
@@ -686,7 +686,7 @@ extern struct superswitch {
 	 */
 	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, long long data_offset);
 
 	/* update the metadata to include new device, either at create or
 	 * when hot-adding a spare.
--- mdadm-3.2.6.orig/super-ddf.c
+++ mdadm-3.2.6/super-ddf.c
@@ -1584,12 +1584,12 @@ static int init_super_ddf_bvd(struct sup
 			      mdu_array_info_t *info,
 			      unsigned long long size,
 			      char *name, char *homehost,
-			      int *uuid);
+			      int *uuid, long long data_offset);
 
 static int init_super_ddf(struct supertype *st,
 			  mdu_array_info_t *info,
 			  unsigned long long size, char *name, char *homehost,
-			  int *uuid)
+			  int *uuid, long long data_offset)
 {
 	/* This is primarily called by Create when creating a new array.
 	 * We will then get add_to_super called for each component, and then
@@ -1625,8 +1625,14 @@ static int init_super_ddf(struct superty
 	struct phys_disk *pd;
 	struct virtual_disk *vd;
 
+	if (data_offset >= 0) {
+		fprintf(stderr, Name ": data-offset not supported by DDF\n");
+		return 0;
+	}
+
 	if (st->sb)
-		return init_super_ddf_bvd(st, info, size, name, homehost, uuid);
+		return init_super_ddf_bvd(st, info, size, name, homehost, uuid,
+					  data_offset);
 
 	if (posix_memalign((void**)&ddf, 512, sizeof(*ddf)) != 0) {
 		fprintf(stderr, Name ": %s could not allocate superblock\n", __func__);
@@ -1956,7 +1962,7 @@ static int init_super_ddf_bvd(struct sup
 			      mdu_array_info_t *info,
 			      unsigned long long size,
 			      char *name, char *homehost,
-			      int *uuid)
+			      int *uuid, long long data_offset)
 {
 	/* We are creating a BVD inside a pre-existing container.
 	 * so st->sb is already set.
--- mdadm-3.2.6.orig/super-intel.c
+++ mdadm-3.2.6/super-intel.c
@@ -4525,7 +4525,8 @@ static int check_name(struct intel_super
 
 static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
 				  unsigned long long size, char *name,
-				  char *homehost, int *uuid)
+				  char *homehost, int *uuid,
+				  long long data_offset)
 {
 	/* We are creating a volume inside a pre-existing container.
 	 * so st->sb is already set.
@@ -4695,7 +4696,7 @@ static int init_super_imsm_volume(struct
 
 static int init_super_imsm(struct supertype *st, mdu_array_info_t *info,
 			   unsigned long long size, char *name,
-			   char *homehost, int *uuid)
+			   char *homehost, int *uuid, long long data_offset)
 {
 	/* This is primarily called by Create when creating a new array.
 	 * We will then get add_to_super called for each component, and then
@@ -4710,8 +4711,14 @@ static int init_super_imsm(struct supert
 	size_t mpb_size;
 	char *version;
 
+	if (data_offset >= 0) {
+		fprintf(stderr, Name ": data-offset not supported by imsm\n");
+		return 0;
+	}
+
 	if (st->sb)
-		return init_super_imsm_volume(st, info, size, name, homehost, uuid);
+		return init_super_imsm_volume(st, info, size, name, homehost, uuid,
+					      data_offset);
 
 	if (info)
 		mpb_size = disks_to_mpb_size(info->nr_disks);
--- mdadm-3.2.6.orig/super0.c
+++ mdadm-3.2.6/super0.c
@@ -604,11 +604,16 @@ static int update_super0(struct supertyp
 
 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, long long data_offset)
 {
 	mdp_super_t *sb;
 	int spares;
 
+	if (data_offset >= 0) {
+		fprintf(stderr, Name ": data-offset not support for 0.90\n");
+		return 0;
+	}
+
 	if (posix_memalign((void**)&sb, 4096,
 			   MD_SB_BYTES + ROUND_UP(sizeof(bitmap_super_t), 4096)) != 0) {
 		fprintf(stderr, Name ": %s could not allocate superblock\n", __func__);
--- mdadm-3.2.6.orig/super1.c
+++ mdadm-3.2.6/super1.c
@@ -880,7 +880,8 @@ static int update_super1(struct supertyp
 }
 
 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, long long data_offset)
 {
 	struct mdp_superblock_1 *sb;
 	int spares;
@@ -944,7 +945,7 @@ static int init_super1(struct supertype
 	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_offset = __cpu_to_le64(data_offset);
 	sb->data_size = __cpu_to_le64(0);
 	sb->super_offset = __cpu_to_le64(0);
 	sb->recovery_offset = __cpu_to_le64(0);
@@ -1113,6 +1114,7 @@ static int write_init_super1(struct supe
 	struct devinfo *di;
 	unsigned long long dsize, array_size;
 	unsigned long long sb_offset, headroom;
+	long long data_offset;
 
 	for (di = st->info; di; di = di->next) {
 		if (di->disk.state == 1)
@@ -1203,49 +1205,62 @@ static int write_init_super1(struct supe
 			sb_offset -= 8*2;
 			sb_offset &= ~(4*2-1);
 			sb->super_offset = __cpu_to_le64(sb_offset);
-			sb->data_offset = __cpu_to_le64(0);
+			data_offset = (long long)(int64_t)__le64_to_cpu(sb->data_offset);
+			if (data_offset < 0)
+				sb->data_offset = 0;
+			/* data_offset already set */
 			if (sb_offset < array_size + bm_space)
 				bm_space = sb_offset - array_size;
 			sb->data_size = __cpu_to_le64(sb_offset - bm_space);
 			break;
 		case 1:
-			sb->super_offset = __cpu_to_le64(0);
-			reserved = bm_space + 4*2;
-			if (reserved < headroom)
-				reserved = headroom;
-			if (reserved + array_size > dsize)
-				reserved = dsize - array_size;
-			/* Try for multiple of 1Meg so it is nicely aligned */
-			#define ONE_MEG (2*1024)
-			if (reserved > ONE_MEG)
-				reserved = (reserved/ONE_MEG) * ONE_MEG;
+			data_offset = __le64_to_cpu(sb->data_offset);
+			if (data_offset < 0) {
+				sb->super_offset = __cpu_to_le64(0);
+				reserved = bm_space + 4*2;
+				if (reserved < headroom)
+					reserved = headroom;
+				if (reserved + array_size > dsize)
+					reserved = dsize - array_size;
+				/* Try for multiple of 1Meg so it is nicely aligned */
+				#define ONE_MEG (2*1024)
+				if (reserved > ONE_MEG)
+					reserved = (reserved/ONE_MEG) * ONE_MEG;
 
-			/* force 4K alignment */
-			reserved &= ~7ULL;
+				/* force 4K alignment */
+				reserved &= ~7ULL;
+
+			} else
+				reserved = data_offset;
 
 			sb->data_offset = __cpu_to_le64(reserved);
 			sb->data_size = __cpu_to_le64(dsize - reserved);
 			break;
 		case 2:
-			sb_offset = 4*2;
-			sb->super_offset = __cpu_to_le64(4*2);
-			if (4*2 + 4*2 + bm_space + array_size
-			    > dsize)
-				bm_space = dsize - array_size
-					- 4*2 - 4*2;
-
-			reserved = bm_space + 4*2 + 4*2;
-			if (reserved < headroom)
-				reserved = headroom;
-			if (reserved + array_size > dsize)
-				reserved = dsize - array_size;
-			/* Try for multiple of 1Meg so it is nicely aligned */
-			#define ONE_MEG (2*1024)
-			if (reserved > ONE_MEG)
-				reserved = (reserved/ONE_MEG) * ONE_MEG;
+			data_offset = __le64_to_cpu(sb->data_offset);
+			if (data_offset < 0) {
+				sb_offset = 4*2;
+				sb->super_offset = __cpu_to_le64(4*2);
+				if (4*2 + 4*2 + bm_space + array_size
+					> dsize)
+					bm_space = dsize - array_size
+						- 4*2 - 4*2;
+
+				reserved = bm_space + 4*2 + 4*2;
+				if (reserved < headroom)
+					reserved = headroom;
+				if (reserved + array_size > dsize)
+					reserved = dsize - array_size;
+				/* Try for multiple of 1Meg so it is nicely aligned */
+				#define ONE_MEG (2*1024)
+				if (reserved > ONE_MEG)
+					reserved = (reserved/ONE_MEG) * ONE_MEG;
+
+				/* force 4K alignment */
+				reserved &= ~7ULL;
 
-			/* force 4K alignment */
-			reserved &= ~7ULL;
+			} else
+				reserved = data_offset;
 
 			sb->data_offset = __cpu_to_le64(reserved);
 			sb->data_size = __cpu_to_le64(dsize - reserved);
