Skip to contentSkip to conversation

md multipathing extensions in 2.4

PATCH Lars Marowsky-Bree 1 participant 1 message Oct 2, 2002 MD RAID

lore
Share & tools
lore
#0 lore raw

Neil,

please find the revised version of the multipath extensions attached. They have undergone some testing as part of the United Linux stuff and seem stable.

They still implement the same features as last time, just fix a bug where they broke raid0 accidentially. The new changes should be even more contained and only change behaviour for multipathing in any way.

I've also attached the mdadm patches, so that it is obvious how they affect userspace. (Because it was so small I've also added a tiny fix to make mdadm work on 64bit archs in there)

Please consider submitting it to 2.4 and comment on whether you'd like the changes to go in 2.5 too.

Sincerely,
Lars Marowsky-Brée <[email protected]>

Signature · 5 lines
Principal Squirrel
Research and Development, SuSE Linux AG
 
``Immortality is an adequate definition of high availability for me.''
	--- Gregory F. Pfister
drivers/md/md.c +236 -166
diff -ur linux.orig/drivers/md/md.c linux/drivers/md/md.c
@@ -782,7 +782,7 @@
 
 static void print_rdev(mdk_rdev_t *rdev)
 {
-	printk(KERN_INFO "md: rdev %s: O:%s, SZ:%08ld F:%d DN:%d ",
+	printk(KERN_INFO "md: rdev %s: O:%s, SZ:%08ld F:%d DN:%d\n",
 		partition_name(rdev->dev), partition_name(rdev->old_dev),
 		rdev->size, rdev->faulty, rdev->desc_nr);
 	if (rdev->sb) {
@@ -984,7 +984,7 @@
 	struct md_list_head *tmp;
 
 	ITERATE_RDEV(mddev,rdev,tmp) {
-		if (rdev->faulty || rdev->alias_device)
+		if (rdev->faulty || rdev_is_alias(rdev))
 			continue;
 		sb = rdev->sb;
 		*sb = *mddev->sb;
@@ -1036,11 +1036,11 @@
 		printk(KERN_INFO "md: ");
 		if (rdev->faulty)
 			printk("(skipping faulty ");
-		if (rdev->alias_device)
+		if (rdev_is_alias(rdev))
 			printk("(skipping alias ");
 
 		printk("%s ", partition_name(rdev->dev));
-		if (!rdev->faulty && !rdev->alias_device) {
+		if (!rdev->faulty && !rdev_is_alias(rdev)) {
 			printk("[events: %08lx]",
 				(unsigned long)rdev->sb->events_lo);
 			err += write_disk_sb(rdev);
@@ -1220,8 +1220,10 @@
 		if (alloc_array_sb(mddev))
 			goto abort;
 	sb = mddev->sb;
+	
+	/* Find the freshest superblock */
 	freshest = NULL;
-
+	
 	ITERATE_RDEV(mddev,rdev,tmp) {
 		__u64 ev1, ev2;
 		/*
@@ -1258,154 +1260,170 @@
 		printk(KERN_INFO "md: freshest: %s\n", partition_name(freshest->dev));
 	}
 	memcpy (sb, freshest->sb, sizeof(*sb));
-
-	/*
-	 * at this point we have picked the 'best' superblock
-	 * from all available superblocks.
-	 * now we validate this superblock and kick out possibly
-	 * failed disks.
-	 */
-	ITERATE_RDEV(mddev,rdev,tmp) {
-		/*
-		 * Kick all non-fresh devices
-		 */
-		__u64 ev1, ev2;
-		ev1 = md_event(rdev->sb);
-		ev2 = md_event(sb);
-		++ev1;
-		if (ev1 < ev2) {
-			printk(KERN_WARNING "md: kicking non-fresh %s from array!\n",
-						partition_name(rdev->dev));
-			kick_rdev_from_array(rdev);
-			continue;
-		}
-	}
-
-	/*
-	 * Fix up changed device names ... but only if this disk has a
-	 * recent update time. Use faulty checksum ones too.
-	 */
-	if (mddev->sb->level != -4)
-	ITERATE_RDEV(mddev,rdev,tmp) {
-		__u64 ev1, ev2, ev3;
-		if (rdev->faulty || rdev->alias_device) {
-			MD_BUG();
-			goto abort;
-		}
-		ev1 = md_event(rdev->sb);
-		ev2 = md_event(sb);
-		ev3 = ev2;
-		--ev3;
-		if ((rdev->dev != rdev->old_dev) &&
-			((ev1 == ev2) || (ev1 == ev3))) {
+	
+	/* For multipathing, lots of things are different from "true"
+	 * RAIDs.
+	 * All rdev's could be read, so they are no longer faulty.
+	 * As there is just one sb, trying to find changed devices via the
+	 * this_disk pointer is useless too.
+	 * 
+	 * [email protected], 2002-09-12
+	 */
+	if (sb->level == -4) {
+		int desc_nr = 0;
+		
+		/* ... and initialize from the current rdevs instead */
+		ITERATE_RDEV(mddev,rdev,tmp) {
 			mdp_disk_t *desc;
-
-			printk(KERN_WARNING "md: device name has changed from %s to %s since last import!\n",
-			       partition_name(rdev->old_dev), partition_name(rdev->dev));
-			if (rdev->desc_nr == -1) {
-				MD_BUG();
-				goto abort;
-			}
+			
+			rdev->desc_nr=desc_nr;
+			
 			desc = &sb->disks[rdev->desc_nr];
-			if (rdev->old_dev != MKDEV(desc->major, desc->minor)) {
-				MD_BUG();
-				goto abort;
-			}
+			
+			desc->raid_disk = desc_nr;
+			desc->number = desc_nr;
 			desc->major = MAJOR(rdev->dev);
 			desc->minor = MINOR(rdev->dev);
 			desc = &rdev->sb->this_disk;
 			desc->major = MAJOR(rdev->dev);
 			desc->minor = MINOR(rdev->dev);
-		}
-	}
 
-	/*
-	 * Remove unavailable and faulty devices ...
-	 *
-	 * note that if an array becomes completely unrunnable due to
-	 * missing devices, we do not write the superblock back, so the
-	 * administrator has a chance to fix things up. The removal thus
-	 * only happens if it's nonfatal to the contents of the array.
-	 */
-	for (i = 0; i < MD_SB_DISKS; i++) {
-		int found;
-		mdp_disk_t *desc;
-		kdev_t dev;
+			/* We could read from it, so it isn't faulty
+			 * any longer */
+			if (disk_faulty(desc))
+				mark_disk_spare(desc);
+			
+			desc_nr++;
+		}
 
-		desc = sb->disks + i;
-		dev = MKDEV(desc->major, desc->minor);
+		/* Kick out all old info about disks we used to have,
+		 * if any */
+		for (i = desc_nr; i < MD_SB_DISKS; i++)
+			memset(&(sb->disks[i]),0,sizeof(mdp_disk_t));
 
+	} else {
 		/*
-		 * We kick faulty devices/descriptors immediately.
+		 * at this point we have picked the 'best' superblock
+		 * from all available superblocks.
 		 *
-		 * Note: multipath devices are a special case.  Since we
-		 * were able to read the superblock on the path, we don't
-		 * care if it was previously marked as faulty, it's up now
-		 * so enable it.
+		 * now we validate this superblock and kick out possibly
+		 * failed disks.
 		 */
-		if (disk_faulty(desc) && mddev->sb->level != -4) {
-			found = 0;
-			ITERATE_RDEV(mddev,rdev,tmp) {
-				if (rdev->desc_nr != desc->number)
-					continue;
-				printk(KERN_WARNING "md%d: kicking faulty %s!\n",
-					mdidx(mddev),partition_name(rdev->dev));
-				kick_rdev_from_array(rdev);
-				found = 1;
-				break;
-			}
-			if (!found) {
-				if (dev == MKDEV(0,0))
-					continue;
-				printk(KERN_WARNING "md%d: removing former faulty %s!\n",
-					mdidx(mddev), partition_name(dev));
-			}
-			remove_descriptor(desc, sb);
-			continue;
-		} else if (disk_faulty(desc)) {
+		ITERATE_RDEV(mddev,rdev,tmp) {
 			/*
-			 * multipath entry marked as faulty, unfaulty it
+			 * Kick all non-fresh devices
 			 */
-			rdev = find_rdev(mddev, dev);
-			if(rdev)
-				mark_disk_spare(desc);
-			else
-				remove_descriptor(desc, sb);
+			__u64 ev1, ev2;
+			ev1 = md_event(rdev->sb);
+			ev2 = md_event(sb);
+			++ev1;
+			if (ev1 < ev2) {
+				printk(KERN_WARNING "md: kicking non-fresh %s from array!\n",
+						partition_name(rdev->dev));
+				kick_rdev_from_array(rdev);
+				continue;
+			}
 		}
 
-		if (dev == MKDEV(0,0))
-			continue;
 		/*
-		 * Is this device present in the rdev ring?
+		 * Fix up changed device names ... but only if this disk has a
+		 * recent update time. Use faulty checksum ones too.
 		 */
-		found = 0;
 		ITERATE_RDEV(mddev,rdev,tmp) {
+			__u64 ev1, ev2, ev3;
+			ev1 = md_event(rdev->sb);
+			ev2 = md_event(sb);
+			ev3 = ev2;
+			--ev3;
+			if ((rdev->dev != rdev->old_dev) &&
+				((ev1 == ev2) || (ev1 == ev3))) {
+				mdp_disk_t *desc;
+
+				printk(KERN_WARNING "md: device name has changed from %s to %s since last import!\n",
+				       partition_name(rdev->old_dev), partition_name(rdev->dev));
+				if (rdev->desc_nr == -1) {
+					MD_BUG();
+					goto abort;
+				}
+				
+				desc = &sb->disks[rdev->desc_nr];
+				if (rdev->old_dev != MKDEV(desc->major, desc->minor)) {
+						MD_BUG();
+						goto abort;
+				}
+				
+				desc->major = MAJOR(rdev->dev);
+				desc->minor = MINOR(rdev->dev);
+				desc = &rdev->sb->this_disk;
+				desc->major = MAJOR(rdev->dev);
+				desc->minor = MINOR(rdev->dev);
+			}
+		}
+		
+		/*
+		 * Remove unavailable and faulty devices ...
+		 *
+		 * note that if an array becomes completely unrunnable due to
+		 * missing devices, we do not write the superblock back, so the
+		 * administrator has a chance to fix things up. The removal thus
+		 * only happens if it's nonfatal to the contents of the array.
+		 */
+		for (i = 0; i < MD_SB_DISKS; i++) {
+			int found;
+			mdp_disk_t *desc;
+			kdev_t dev;
+
+			desc = sb->disks + i;
+			dev = MKDEV(desc->major, desc->minor);
+
 			/*
-			 * Multi-path IO special-case: since we have no
-			 * this_disk descriptor at auto-detect time,
-			 * we cannot check rdev->number.
-			 * We can check the device though.
+			 * We kick faulty devices/descriptors immediately.
 			 */
-			if ((sb->level == -4) && (rdev->dev ==
-					MKDEV(desc->major,desc->minor))) {
-				found = 1;
-				break;
+			if (disk_faulty(desc)) {
+				found = 0;
+				ITERATE_RDEV(mddev,rdev,tmp) {
+					if (rdev->desc_nr != desc->number)
+						continue;
+					printk(KERN_WARNING "md%d: kicking faulty %s!\n",
+						mdidx(mddev),partition_name(rdev->dev));
+					kick_rdev_from_array(rdev);
+					found = 1;
+					break;
+				}
+				if (!found) {
+					if (dev == MKDEV(0,0))
+						continue;
+					printk(KERN_WARNING "md%d: removing former faulty %s!\n",
+						mdidx(mddev), partition_name(dev));
+				}
+				remove_descriptor(desc, sb);
+				continue;
 			}
-			if (rdev->desc_nr == desc->number) {
-				found = 1;
-				break;
+
+			if (dev == MKDEV(0,0))
+				continue;
+			/*
+			 * Is this device present in the rdev ring?
+			 */
+			found = 0;
+			ITERATE_RDEV(mddev,rdev,tmp) {
+				if (rdev->desc_nr == desc->number) {
+					found = 1;
+					break;
+				}
 			}
+			if (found)
+				continue;
+
+			printk(KERN_WARNING "md%d: former device %s is unavailable, removing from array!\n",
+			       mdidx(mddev), partition_name(dev));
+			remove_descriptor(desc, sb);
 		}
-		if (found)
-			continue;
 
-		printk(KERN_WARNING "md%d: former device %s is unavailable, removing from array!\n",
-		       mdidx(mddev), partition_name(dev));
-		remove_descriptor(desc, sb);
 	}
 
 	/*
-	 * Double check wether all devices mentioned in the
+	 * Double check whether all devices mentioned in the
 	 * superblock are in the rdev ring.
 	 */
 	first = 1;
@@ -1429,23 +1447,6 @@
 			MD_BUG();
 			goto abort;
 		}
-		/*
-		 * In the case of Multipath-IO, we have no
-		 * other information source to find out which
-		 * disk is which, only the position of the device
-		 * in the superblock:
-		 */
-		if (mddev->sb->level == -4) {
-			if ((rdev->desc_nr != -1) && (rdev->desc_nr != i)) {
-				MD_BUG();
-				goto abort;
-			}
-			rdev->desc_nr = i;
-			if (!first)
-				rdev->alias_device = 1;
-			else
-				first = 0;
-		}
 	}
 
 	/*
@@ -1460,31 +1461,25 @@
 	/*
 	 * Do a final reality check.
 	 */
-	if (mddev->sb->level != -4) {
-		ITERATE_RDEV(mddev,rdev,tmp) {
-			if (rdev->desc_nr == -1) {
+	ITERATE_RDEV(mddev,rdev,tmp) {
+		if (rdev->desc_nr == -1) {
+			MD_BUG();
+			goto abort;
+		}
+		ITERATE_RDEV(mddev,rdev2,tmp2) {
+			/* is the desc_nr unique?  */
+			if (rdev2 == rdev)
+				continue;
+
+			if (rdev2->desc_nr == rdev->desc_nr) {
 				MD_BUG();
 				goto abort;
 			}
-			/*
-			 * is the desc_nr unique?
-			 */
-			ITERATE_RDEV(mddev,rdev2,tmp2) {
-				if ((rdev2 != rdev) &&
-						(rdev2->desc_nr == rdev->desc_nr)) {
-					MD_BUG();
-					goto abort;
-				}
-			}
-			/*
-			 * is the device unique?
-			 */
-			ITERATE_RDEV(mddev,rdev2,tmp2) {
-				if ((rdev2 != rdev) &&
-						(rdev2->dev == rdev->dev)) {
-					MD_BUG();
-					goto abort;
-				}
+
+			/* is the device unique? */
+			if (rdev2->dev == rdev->dev) {
+				MD_BUG();
+				goto abort;
 			}
 		}
 	}
@@ -1909,6 +1904,28 @@
 		 */
 		mddev->sb_dirty = 0;
 		do_md_stop (mddev, 0);
+	} else {
+		/* Create an rdev for the freshly started md device
+		 * and add to the end of the list */
+		kdev_t dev = MKDEV(MD_MAJOR,mddev->__minor);
+		if (md_import_device(dev,1)) {
+			printk("md: no nested md device found\n");
+		} else {
+			rdev = find_rdev_all(dev);
+			/* This should all be impossible because we _just_
+			 * imported the device! */
+			if (!rdev) {
+				MD_BUG();
+				return;
+			}
+			if (rdev->faulty) {
+				MD_BUG();
+				return;
+			}
+			printk("md: added md%d to the autodetection\n",
+				mdidx(mddev));
+			md_list_add(&rdev->pending, pending_raid_disks.prev);
+		}
 	}
 }
 
@@ -2337,10 +2354,9 @@
 		return -EINVAL;
 	}
 	disk = &mddev->sb->disks[rdev->desc_nr];
-	if (disk_active(disk)) {
-		MD_BUG();
+	if (disk_active(disk))
 		goto busy;
-	}
+
 	if (disk_removed(disk)) {
 		MD_BUG();
 		return -EINVAL;
@@ -2368,6 +2384,50 @@
 	return -EBUSY;
 }
 
+/*
+ * find the maching mdk_disk_t and pass to diskops
+ */
+static int set_mp_params(mddev_t * mddev, kdev_t dev, int state)
+{
+	mdp_disk_t *disk;
+	mdk_rdev_t *rdev;
+
+	if (!mddev->pers)
+		return -ENODEV;
+	if (!mddev->pers->diskop)
+		return -ENXIO;
+
+	rdev = find_rdev(mddev, dev);
+	if (!rdev)
+		return -ENXIO;
+
+	if (rdev->desc_nr == -1) {
+		MD_BUG();
+		return -EINVAL;
+	}
+
+	disk = &mddev->sb->disks[rdev->desc_nr];
+
+#if 0
+	printk("set_mp_params: state: %d dev: %s disk: %s (%d)\n",
+			state,
+			partition_name(dev),
+			partition_name(MKDEV(disk->major,disk->minor)),
+			disk->number);
+	print_rdev(rdev);
+#endif
+	switch (state) {
+	case SET_DISK_ACTIVE:
+		return mddev->pers->diskop(mddev, &disk, DISKOP_ACTIVATE_PATH);
+	case SET_DISK_INACTIVE:
+		return mddev->pers->diskop(mddev, &disk, DISKOP_DISABLE_PATH);
+	case SET_DISK_CLEAN:
+		return mddev->pers->diskop(mddev, &disk, DISKOP_CLEAN_PATH);
+	}
+
+	return -EINVAL;
+}
+
 static int hot_add_disk(mddev_t * mddev, kdev_t dev)
 {
 	int i, err, persistent;
@@ -2859,6 +2919,12 @@
 			goto done_unlock;
 		}
 
+		case SET_DISK_CLEAN:
+		case SET_DISK_ACTIVE:
+		case SET_DISK_INACTIVE:
+			err = set_mp_params(mddev, (kdev_t) arg, cmd);
+			goto done_unlock;
+
 		default:
 			printk(KERN_WARNING "md: %s(pid %d) used obsolete MD ioctl, "
 			       "upgrade your software to use new ictls.\n",
@@ -3274,6 +3340,7 @@
 		}
 		disk = &sb->disks[rdev->desc_nr];
 		if (disk_faulty(disk)) {
+			print_desc(disk);
 			MD_BUG();
 			continue;
 		}
@@ -3521,6 +3588,8 @@
 		sb = mddev->sb;
 		if (!sb)
 			continue;
+		if (sb->level == -4)
+			continue;
 		if (mddev->recovery_running)
 			continue;
 		if (sb->active_disks == sb->raid_disks)
@@ -4047,6 +4116,7 @@
 MD_EXPORT_SYMBOL(md_wakeup_thread);
 MD_EXPORT_SYMBOL(md_print_devices);
 MD_EXPORT_SYMBOL(find_rdev_nr);
+MD_EXPORT_SYMBOL(find_rdev);
 MD_EXPORT_SYMBOL(md_interrupt_thread);
 MD_EXPORT_SYMBOL(mddev_map);
 MD_EXPORT_SYMBOL(md_check_ordering);
drivers/md/multipath.c +413 -155

806 lines not on this page · raw message

include/linux/raid/md_k.h +8 -1
diff -ur linux.orig/include/linux/raid/md_k.h linux/include/linux/raid/md_k.h
@@ -173,12 +173,16 @@
 	mdp_super_t *sb;
 	unsigned long sb_offset;
 
-	int alias_device;		/* device alias to the same disk */
 	int faulty;			/* if faulty do not issue IO requests */
 	int desc_nr;			/* descriptor index in the superblock */
 };
 
 
+static inline int rdev_is_alias(mdk_rdev_t * rdev)
+{
+	return ((rdev->sb->level == -4) && (rdev->desc_nr != 0));
+}
+
 /*
  * disk operations in a working array:
  */
@@ -187,6 +191,9 @@
 #define DISKOP_SPARE_ACTIVE	2
 #define DISKOP_HOT_REMOVE_DISK	3
 #define DISKOP_HOT_ADD_DISK	4
+#define DISKOP_ACTIVATE_PATH	5
+#define DISKOP_DISABLE_PATH	6
+#define DISKOP_CLEAN_PATH	7
 
 typedef struct mdk_personality_s mdk_personality_t;
 
include/linux/raid/md_u.h +5 -0
diff -ur linux.orig/include/linux/raid/md_u.h linux/include/linux/raid/md_u.h
@@ -44,6 +44,11 @@
 #define STOP_ARRAY_RO		_IO (MD_MAJOR, 0x33)
 #define RESTART_ARRAY_RW	_IO (MD_MAJOR, 0x34)
 
+/* multi-path extensions */
+#define SET_DISK_CLEAN		_IO (MD_MAJOR, 0x35)
+#define SET_DISK_ACTIVE		_IO (MD_MAJOR, 0x36)
+#define SET_DISK_INACTIVE	_IO (MD_MAJOR, 0x37)
+
 typedef struct mdu_version_s {
 	int major;
 	int minor;
include/linux/raid/multipath.h +6 -0
diff -ur linux.orig/include/linux/raid/multipath.h linux/include/linux/raid/multipath.h
@@ -15,6 +15,11 @@
 	int		spare;
 
 	int		used_slot;
+
+	atomic_t	nr_pending_buffers;
+	unsigned long	last_start_sector;
+	unsigned long	last_end_sector;
+	int		last_rw;
 };
 
 struct multipath_private_data {
@@ -63,6 +68,7 @@
 	struct buffer_head	*master_bh;
 	struct buffer_head	bh_req;
 	struct multipath_bh	*next_mp; /* next for retry or in free list */
+	struct multipath_info *multipath;
 };
 /* bits for multipath_bh.state */
 #define	MPBH_Uptodate	1
Create.c +4 -4
@@ -52,7 +52,7 @@
 	 * if runstop==run, or raiddisks diskswere used,
 	 * RUN_ARRAY
 	 */
-	int minsize=0, maxsize=0;
+	unsigned long minsize=0, maxsize=0;
 	char *mindisc = NULL;
 	char *maxdisc = NULL;
 	int dnum;
@@ -127,7 +127,7 @@
 	dnum = 0;
 	for (dv=devlist; dv; dv=dv->next, dnum++) {
 		char *dname = dv->devname;
-		int dsize, freesize;
+		unsigned long dsize, freesize;
 		int fd;
 		if (strcasecmp(dname, "missing")==0) {
 			if (first_missing > dnum)
@@ -153,7 +153,7 @@
 			continue;
 		}
 		if (dsize < MD_RESERVED_SECTORS*2) {
-			fprintf(stderr, Name ": %s is too small: %dK\n",
+			fprintf(stderr, Name ": %s is too small: %ldK\n",
 				dname, dsize/2);
 			fail = 1;
 			close(fd);
@@ -164,7 +164,7 @@
 
 		if (size && freesize < size) {
 			fprintf(stderr, Name ": %s is smaller that given size."
-				" %dK < %dK + superblock\n", dname, freesize, size);
+				" %ldK < %dK + superblock\n", dname, freesize, size);
 			fail = 1;
 			close(fd);
 			continue;
Assemble.c +13 -4
diff -ur mdadm-1.0.1.orig/Assemble.c mdadm-1.0.1.lmb/Assemble.c
@@ -250,9 +250,6 @@
 			    devname);
 		    continue;
 		}
-		if (verbose)
-			fprintf(stderr, Name ": %s is identified as a member of %s, slot %d.\n",
-				devname, mddev, super.this_disk.raid_disk);
 		devices[devcnt].devname = devname;
 		devices[devcnt].major = MAJOR(stb.st_rdev);
 		devices[devcnt].minor = MINOR(stb.st_rdev);
@@ -260,7 +257,19 @@
 		devices[devcnt].oldminor = super.this_disk.minor;
 		devices[devcnt].events = md_event(&super);
 		devices[devcnt].utime = super.utime;
-		devices[devcnt].raid_disk = super.this_disk.raid_disk;
+
+		if (super.level == -4) {
+			/* Workaround: In a multipath setup, all superblocks
+			 * are identical (obviously); so they would all be
+			 * assigned the same slot. */
+			devices[devcnt].raid_disk = devcnt;	
+		} else {
+			devices[devcnt].raid_disk = super.this_disk.raid_disk;
+		}
+		if (verbose)
+			fprintf(stderr, Name ": %s is identified as a member of %s, slot %d.\n",
+				devname, mddev, devices[devcnt].raid_disk);
+
 		devices[devcnt].uptodate = 0;
 		if (most_recent < devcnt) {
 			if (devices[devcnt].events
Manage.c +33 -0
diff -ur mdadm-1.0.1.orig/Manage.c mdadm-1.0.1.lmb/Manage.c
@@ -218,6 +218,39 @@
 			fprintf(stderr, Name ": set %s faulty in %s\n",
 				dv->devname, devname);
 			break;
+		
+		case 'c': /* set clean */
+			/* FIXME check current member */
+			if (ioctl(fd, SET_DISK_CLEAN, (unsigned long) stb.st_rdev)) {
+				fprintf(stderr, Name ": set device clean failed for %s:  %s\n",
+					dv->devname, strerror(errno));
+				return 1;
+			}
+			fprintf(stderr, Name ": set %s clean in %s\n",
+				dv->devname, devname);
+			break;
+
+		case 'A': /* activate path */
+			/* FIXME check current member */
+			if (ioctl(fd, SET_DISK_ACTIVE, (unsigned long) stb.st_rdev)) {
+				fprintf(stderr, Name ": set device active failed for %s:  %s\n",
+					dv->devname, strerror(errno));
+				return 1;
+			}
+			fprintf(stderr, Name ": set %s active in %s\n",
+				dv->devname, devname);
+			break;
+
+		case 'I': /* deactivate path */
+			/* FIXME check current member */
+			if (ioctl(fd, SET_DISK_INACTIVE, (unsigned long) stb.st_rdev)) {
+				fprintf(stderr, Name ": set device inactive failed for %s:  %s\n",
+					dv->devname, strerror(errno));
+				return 1;
+			}
+			fprintf(stderr, Name ": set %s inactive in %s\n",
+				dv->devname, devname);
+			break;
 		}
 	}
 	return 0;
ReadMe.c +8 -1
diff -ur mdadm-1.0.1.orig/ReadMe.c mdadm-1.0.1.lmb/ReadMe.c
@@ -135,6 +135,9 @@
     {"stop",      0, 0, 'S'},
     {"readonly",  0, 0, 'o'},
     {"readwrite", 0, 0, 'w'},
+    {"clean",     0, 0, 24},
+    {"active",    0, 0, 25},
+    {"inactive",  0, 0, 26},
 
     /* For Detail/Examine */
     {"brief",	  0, 0, 'b'},
@@ -349,12 +352,16 @@
 "Options that are valid with management mode are:\n"
 "  --add         -a   : hotadd subsequent devices to the array\n"
 "  --remove      -r   : remove subsequent devices, which must not be active\n"
-"  --fail        -f   : mark subsequent devices a faulty\n"
+"  --fail        -f   : mark subsequent devices as faulty\n"
 "  --set-faulty       : same as --fail\n"
 "  --run         -R   : start a partially built array\n"
 "  --stop        -S   : deactive array, releasing all resources\n"
 "  --readonly    -o   : mark array as readonly\n"
 "  --readwrite   -w   : mark array as readwrite\n"
+"For multipath:\n"
+"  --clean       -c   : mark subsequent devices as clean\n"
+"  --active           : mark subsequent devices as active\n"
+"  --inactive         : mark subsequent devices as inactive\n"
 ;
 
 char Help_misc[] =
md_u.h +5 -0
diff -ur mdadm-1.0.1.orig/md_u.h mdadm-1.0.1.lmb/md_u.h
@@ -43,6 +43,11 @@
 #define STOP_ARRAY_RO		_IO (MD_MAJOR, 0x33)
 #define RESTART_ARRAY_RW	_IO (MD_MAJOR, 0x34)
 
+/* multi-path extensions */
+#define SET_DISK_CLEAN		_IO (MD_MAJOR, 0x35)
+#define SET_DISK_ACTIVE		_IO (MD_MAJOR, 0x36)
+#define SET_DISK_INACTIVE	_IO (MD_MAJOR, 0x37)
+
 typedef struct mdu_version_s {
 	int major;
 	int minor;
mdadm.8 +11 -3
diff -ur mdadm-1.0.1.orig/mdadm.8 mdadm-1.0.1.lmb/mdadm.8
@@ -275,12 +275,20 @@
 be failed or spare devices.
 
 .TP
-.BR -f ", " --fail
+.BR -f ", " --fail ", " --set-faulty
 mark listed devices as faulty.
 
 .TP
-.BR --set-faulty
-same as --fail.
+.BR --clean
+mark listed devices as clean. (Only supported by multipath)
+
+.TP
+.BR --active
+mark listed devices as active. (Only supported by multipath)
+
+.TP
+.BR --inactive
+mark listed devices as inactive. (Only supported by multipath)
 
 .SH For Misc mode:
 
mdadm.c +12 -1
diff -ur mdadm-1.0.1.orig/mdadm.c mdadm-1.0.1.lmb/mdadm.c
@@ -262,7 +262,8 @@
 					optarg);
 				exit(2);
 			}
-			if (level != 0 && level != -1 && mode == BUILD) {
+			if (level != 0 && level != -1 && level != -4
+					&& mode == BUILD) {
 				fprintf(stderr, Name ": Raid level %s not permitted with --build.\n",
 					optarg);
 				exit(2);
@@ -432,6 +433,16 @@
 		case O(MANAGE,'f'): /* set faulty */
 			devmode = 'f';
 			continue;
+		case O(MANAGE,24): /* set clean */
+			devmode = 'c';
+			continue;
+		case O(MANAGE,25): /* set active */
+			devmode = 'A';
+			continue;
+		case O(MANAGE,26): /* set inactive */
+			devmode = 'I';
+			continue;
+
 		case O(MANAGE,'R'):
 		case O(ASSEMBLE,'R'):
 		case O(BUILD,'R'):