[PATCH] 6/6: Add ASR reconfiguration support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]


Add basic support to ASR format handler to utilize reconfiguration
features in reconfig.c.

Signed-off-by: Adam DiCarlo <bikko@xxxxxxxxxx>
Signed-off-by: James Simshaw <simshawj@xxxxxxxxxx>
Signed-off-by: Darrick J. Wong <djwong@xxxxxxxxxx>




--- ORIGINAL/lib/format/ataraid/asr.c	2006-07-26 14:24:36.000000000 -0700
+++ PATCHED/lib/format/ataraid/asr.c	2006-08-29 13:55:42.000000000 -0700
@@ -2,7 +2,9 @@
  * Adaptec HostRAID ASR metadata format handler.
  *
  * Copyright (C) 2005-2006 IBM, All rights reserved.
- * Written by Darrick Wong <djwong@xxxxxxxxxx>
+ * Written by Darrick Wong <djwong@xxxxxxxxxx>,
+ * James Simshaw <simshawj@xxxxxxxxxx>, and
+ * Adam DiCarlo <bikko@xxxxxxxxxx>
  *
  * Copyright (C) 2006  Heinz Mauelshagen, Red Hat GmbH
  *		       All rights reserved.
@@ -12,6 +14,7 @@
 
 #include <errno.h>
 #include <netinet/in.h>
+#include <time.h>
 
 #define	HANDLER	"asr"
 
@@ -28,7 +31,7 @@ static const char *handler = HANDLER;
 
 #define SPARE_ARRAY	".asr_spares"
 
-static int asr_write(struct lib_context *lc,  struct raid_dev *rd, int erase);
+//static int asr_write(struct lib_context *lc,  struct raid_dev *rd, int erase);
 
 /* Map ASR disk status to dmraid status */
 static enum status disk_status(struct asr_raid_configline *disk) {
@@ -47,19 +50,25 @@ static enum status disk_status(struct as
 	return rd_status(states, disk->raidstate, EQUAL);
 }
 		
-/* Get this disk's configuration */
-static struct asr_raid_configline *this_disk(struct asr *asr)
+/* Extract config line from metadata */
+static struct asr_raid_configline *get_config(struct asr *asr, uint32_t magic)
 {
 	unsigned int i = asr->rt->elmcnt;
 	
 	while (i--) {
-		if (asr->rt->ent[i].raidmagic == asr->rb.drivemagic)
+		if (asr->rt->ent[i].raidmagic == magic)
 			return asr->rt->ent + i;
 	}
 
 	return NULL;
 }
 
+/* Get this disk's configuration */
+static struct asr_raid_configline *this_disk(struct asr *asr)
+{
+	return get_config(asr, asr->rb.drivemagic);
+}
+
 /* Make up RAID device name. */
 static size_t _name(struct lib_context *lc, struct asr *asr, char *str,
 		    size_t len)
@@ -235,7 +244,7 @@ static int read_extended(struct lib_cont
 			RVALID2);
 
 	/* Have we a valid element count? */
-	if (asr->rt->elmcnt >= asr->rt->maxelm)
+	if (asr->rt->elmcnt >= asr->rt->maxelm || asr->rt->elmcnt == 0)
 		LOG_ERR(lc, 0, "%s: Invalid RAID config table count.\n",
 			handler);
 
@@ -384,6 +393,25 @@ static void *read_metadata_areas(struct 
 	return (void*) asr;
 }
 
+/* Read the whole metadata chunk at once */
+static uint8_t *read_metadata_chunk(struct lib_context *lc, struct dev_info *di,
+				    uint64_t start)
+{
+	uint8_t *ret;
+	size_t size = (di->sectors - start) * ASR_DISK_BLOCK_SIZE;
+
+	if (!(ret = dbg_malloc(size)))
+		LOG_ERR(lc, ret, "%s: unable to allocate memory.", di->path);
+
+	if (!read_file(lc, handler, di->path, ret, size,
+		       start * ASR_DISK_BLOCK_SIZE)) {
+		dbg_free(ret);
+		LOG_ERR(lc, NULL, "%s: unable to read metadata.", di->path);
+	}
+
+	return ret;
+}
+
 /*
  * "File the metadata areas" -- I think this function is supposed to declare
  * which parts of the drive are metadata and thus off-limits to dmraid.
@@ -391,13 +419,20 @@ static void *read_metadata_areas(struct 
 static void file_metadata_areas(struct lib_context *lc, struct dev_info *di,
 				void *meta)
 {
+	uint8_t *buf;
 	struct asr *asr = meta;
+	uint64_t start = asr->rb.raidtbl;
+
+	if (!(buf = read_metadata_chunk(lc, di, start)))
+		return;
 
 	/* Register the raid tables. */
-	file_metadata(lc, handler, di->path, asr->rt,
+	file_metadata(lc, handler, di->path, buf,
 		      ASR_DISK_BLOCK_SIZE * 17,
-		      (uint64_t)asr->rb.raidtbl * ASR_DISK_BLOCK_SIZE);
-
+		      start * ASR_DISK_BLOCK_SIZE);
+	
+	dbg_free(buf);
+	
 	/* Record the device size if -D was specified. */
 	file_dev_size(lc, handler, di);
 }
@@ -482,6 +517,16 @@ static struct asr_raid_configline *find_
 	return NULL;
 }
 
+static struct raid_dev *find_spare(struct lib_context *lc) {
+	struct raid_dev *spare;
+	
+	list_for_each_entry(spare, LC_RD(lc), list) {
+		if (spare->type == t_spare)  
+			return spare;
+	}
+	return NULL;
+}
+
 /* Wrapper for name() */
 static char *js_name(struct lib_context *lc, struct raid_dev *rd,
 		     unsigned int subset)
@@ -581,6 +626,7 @@ static struct raid_set *asr_group(struct
 
 		/* Add the disk to the set. */
 		list_add_sorted(lc, &set->devs, &rd->devs, dev_sort);
+
 		return set;
 	}
 
@@ -626,12 +672,267 @@ static struct raid_set *asr_group(struct
 	LOG_ERR(lc, NULL, "Top level array config is not FWL/FWL2?\n");
 }
 
+/* deletes configline from metadata of given asr, by index. */
+static void delete_configline(struct asr *asr, int index)
+{
+	struct asr_raid_configline *cl, *end;
+
+	asr->rt->elmcnt--;
+	cl = asr->rt->ent + index;
+	end = asr->rt->ent + asr->rt->elmcnt;
+	while (cl < end) {
+		memcpy(cl, cl + 1, sizeof(*cl));
+		++cl;
+	}
+}
+
+/* Find the newest configline entry in raid set and return a pointer to it. */
+static struct raid_dev *find_newest_drive(struct raid_set *rs)
+{
+	struct asr *asr;
+	struct raid_dev *device, *newest = NULL;
+	uint16_t newest_raidseq = 0;
+        int i;
+	
+	list_for_each_entry(device, &rs->devs, devs) {
+		asr = META(device, asr);
+		// FIXME: We should be able to assume each configline
+		// in a single drive has the same raidseq as the rest
+		// in that drive. We're doing too much work here.
+		for (i = 0; i < asr->rt->elmcnt; ++i) {
+			if (asr->rt->ent[i].raidseq >= newest_raidseq) {
+				newest_raidseq = asr->rt->ent[i].raidseq;
+				newest = device;
+			}
+		}
+	}
+	
+	return newest;
+}
+
+/* Creates a random integer for a drive magic section */
+static uint32_t create_drivemagic() {
+
+	srand(time(NULL));
+
+	return rand() + rand();
+}
+
+static int spare(struct lib_context *lc, struct raid_dev *rd,
+		 struct asr *asr)
+{
+	struct asr_raid_configline *cl;
+
+	/* If the magic is already 0xFFFFFFFF, exit */
+	if (asr->rt->raidmagic == 0xFFFFFFFF)
+		return 1;
+
+	/* Otherwise, set the magic */
+	asr->rt->raidmagic = 0xFFFFFFFF;
+
+	/* Erase all the CLs, create the two defaults and exit */
+	/* FIXME: How to set blockstoragetid? */
+	asr->rt->elmcnt = 2;
+
+	/* Note the presence of an array of spares in first config
+	 * line entry. */
+	cl = asr->rt->ent;
+	cl->raidmagic = 0xFFFFFFFF;
+	cl->raidseq = 0;
+	cl->name[0] = 0;
+	cl->raidcnt = 1;
+	cl->raidtype = ASR_RAIDSPR;
+	cl->lcapcty = rd->di->sectors;
+	cl->raidlevel = FWL;
+	cl++;
+
+	/* Actually describe the disk: it's a spare. */
+	cl->raidmagic = asr->rb.drivemagic;
+	cl->raidseq = 0;
+	cl->name[0] = 0;
+	cl->raidcnt = 0;
+	cl->raidtype = ASR_RAIDSPR;
+	cl->lcapcty = rd->di->sectors;
+	cl->raidlevel = FWP;
+
+	return 1;
+}
+
+/* Returns (boolean) whether or not the drive described by the given configline
+ * is in the given raid_set. */
+static int in_raid_set(struct asr_raid_configline *cl, struct raid_set *rs)
+{
+	struct asr *asr;
+	struct raid_dev *d;
+
+	list_for_each_entry(d, &rs->devs, devs) {
+		asr = META(d, asr);
+		if (cl->raidmagic == asr->rb.drivemagic)
+			return 1;
+	}
+	return 0;
+}
+
+/* Delete extra configlines which would otherwise trip us up. */
+static int cleanup_configlines(struct raid_dev *rd, struct raid_set *rs)
+{
+	struct asr *a;
+	struct raid_dev *d;
+	struct asr_raid_configline *cl;
+	int clcnt;
+
+	list_for_each_entry(d, &rs->devs, devs) {
+		a = META(d, asr);
+	
+		cl = a->rt->ent;
+		for (clcnt = 0; clcnt < a->rt->elmcnt; /* done in loop */ ) {
+			/* If it's in the seen list, or is a logical drive, 
+			 * end iteration. The idea: get rid of configlines
+			 * which describe devices which are no longer in the
+			 * array.
+			 * FIXME: If our topmost level is FWL2, we could have
+			 * FWL entries which need to be removed, right? We need
+			 * to check for this condition, too. */
+			if (cl->raidlevel != FWP || in_raid_set(cl, rs)) {
+				cl++;
+				clcnt++;
+			} else {
+				/* Delete entry. After deleting, a new entry is
+				 * found at *cl (a->rt->ent[clcnt]), so don't
+				 * increment counter/pointer; otherwise we'd
+				 * skip an entry.
+				 */
+				delete_configline(a, clcnt);
+			}
+		}
+	}
+	return 1;
+}
+
+/* Add a CL entry */
+static int create_configline(struct raid_set *rs, struct asr *asr,
+		             struct asr *a, struct raid_dev* newest)
+{
+	if (asr->rt->elmcnt >= RCTBL_MAX_ENTRIES) {
+		return 0;
+	}
+
+	struct asr *newest_asr;
+	struct asr_raid_configline *cl;
+	
+	newest_asr = META(newest, asr);
+	
+	cl = asr->rt->ent + asr->rt->elmcnt;
+	asr->rt->elmcnt++;
+
+	/* Use first raidseq, below: FIXME - don't assume all CLS are
+	 * consistent */
+	cl->raidmagic = a->rb.drivemagic;
+	cl->raidseq = newest_asr->rt->ent[0].raidseq;
+	cl->strpsize = newest_asr->rt->ent[0].strpsize;
+	strcpy((char*) cl->name, &rs->name[4]); /* starts after "asr_" */
+	cl->raidcnt = 0;
+
+	/* Convert rs->type to an ASR_RAID type for the CL */
+	switch (rs->type) {
+	case t_raid0:
+		cl->raidtype = ASR_RAID0;
+		break;
+	case t_raid1:
+		cl->raidtype = ASR_RAID1;
+		break;
+	default:
+		return 0;
+	}
+	cl->lcapcty = newest_asr->rt->ent[0].lcapcty;
+	cl->raidlevel = FWP;
+	return 1;
+}
+
+/* Update metadata to reflect the current raid set configuration.
+ * Returns boolean success. */
+static int update_metadata(struct lib_context *lc,  struct raid_dev *rd,
+			   struct asr *asr)
+{
+	struct raid_set *rs;
+	struct asr_raid_configline *cl;
+	struct raid_dev *d, *newest;
+	struct asr *a;
+
+	/* Find the raid set */
+	rs = get_raid_set(lc, rd);
+	if (!rs) {
+		/* Array-less disks ... have no CLs ? */
+		asr->rt->elmcnt = 0;
+		return 1;
+	}
+	
+	/* If this is the spare array... */
+	if (!strcmp(SPARE_ARRAY, rs->name)) {
+		return spare(lc, rd, asr);
+	}
+
+	/* Find newest drive for use below */
+	if (!(newest = find_newest_drive(rs)))
+		return 0;
+
+	/* If the drive magic is 0xFFFFFFFF, assign a random one. */
+	if (asr->rb.drivemagic == 0xFFFFFFFF)
+		asr->rb.drivemagic = create_drivemagic();
+
+	/* Make sure the raid type agrees with the metadata */
+	if (type(this_disk(asr)) == t_spare) {
+		struct asr *newest_asr = META(newest, asr);
+	
+		/* copy entire table from newest drive */	
+		asr->rt->elmcnt = newest_asr->rt->elmcnt;
+		memcpy(asr->rt->ent, newest_asr->rt->ent,
+			asr->rt->elmcnt * sizeof(*asr->rt->ent));
+	}
+
+	/* Increment the top level CL's raid count */
+	/* Fixme: What about the the FWLs in a FWL2 setting? */
+	cl = asr->rt->ent + find_toplevel(lc, asr);
+	cl->raidseq++;
+
+	/* For each disk in the rs */
+	list_for_each_entry(d, &rs->devs, devs) {
+		a = META(d, asr);
+
+		/* If it's in the CL already... */
+		if ((cl = get_config(asr, a->rb.drivemagic))) {
+			/* Increment seq number */
+			cl->raidseq++;
+			continue;
+		}
+
+		/* If the magic is 0xFFFFFFFF, assign a random one */
+		if (a->rb.drivemagic == 0xFFFFFFFF) {
+			a->rb.drivemagic = create_drivemagic();
+		}
+		
+		if (!(newest = find_newest_drive(rs)))
+			return 0;
+				
+		create_configline(rs, asr, a, newest);
+	}
+
+	cleanup_configlines(rd, rs);
+
+	return 1;
+}
+
+
 /* Write metadata. */
 static int asr_write(struct lib_context *lc,  struct raid_dev *rd, int erase)
 {
         struct asr *asr = META(rd, asr);
 	int elmcnt = asr->rt->elmcnt, i, ret;
 
+	/* Update the metadata if we're not erasing it. */
+	if (!erase)
+		update_metadata(lc, rd, asr);
+
 	/* Untruncate trailing whitespace in the name. */
 	for (i = 0; i < elmcnt; i++)
 		handle_white_space(asr->rt->ent[i].name, UNTRUNCATE);



_______________________________________________
Ataraid-list mailing list
Ataraid-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/ataraid-list

[Linux RAID]     [Linux IDE]     [Linux SCSI]     [Kernel]     [Linux Books]     [Linux Admin]     [GFS]     [RPM]     [Photos]     [Yosemite Photos]     [Yosemite News]     [AMD 64]
  Powered by Linux