diff -u --recursive --new-file /usr/src/linux/MAGIC linux/MAGIC
--- /usr/src/linux/MAGIC	Sat Jan 14 08:32:05 1995
+++ linux/MAGIC	Sat Mar 25 15:22:07 1995
@@ -52,6 +52,7 @@
 0x00	fd.h
 0x03	hdreg.h
 0x06	lp.h
+0x09	md.h
 0x12	fs.h
 'C'	soundcard.h
 'K'	kd.h
diff -u --recursive --new-file /usr/src/linux/arch/i386/config.in linux/arch/i386/config.in
--- /usr/src/linux/arch/i386/config.in	Thu Feb 16 22:28:47 1995
+++ linux/arch/i386/config.in	Sat Mar 25 08:32:34 1995
@@ -22,6 +22,7 @@
 fi
 
 bool 'XT harddisk support' CONFIG_BLK_DEV_XD n
+bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD n
 bool 'Networking support' CONFIG_NET y
 bool 'Limit memory to low 16MB' CONFIG_MAX_16M n
 bool 'PCI bios support' CONFIG_PCI n
diff -u --recursive --new-file /usr/src/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- /usr/src/linux/drivers/block/Makefile	Sat Mar 18 09:23:57 1995
+++ linux/drivers/block/Makefile	Sat Mar 25 08:32:34 1995
@@ -89,6 +89,11 @@
 BLOCK_MODULE_OBJS := $(BLOCK_MODULE_OBJS) sonycd535.o
 endif
 
+ifdef CONFIG_BLK_DEV_MD
+OBJS := $(OBJS) md.o
+SRCS := $(SRCS) md.c
+endif
+
 all: block.a
 
 block.a: $(OBJS)
diff -u --recursive --new-file /usr/src/linux/drivers/block/blk.h linux/drivers/block/blk.h
--- /usr/src/linux/drivers/block/blk.h	Thu Feb 16 22:28:34 1995
+++ linux/drivers/block/blk.h	Sat Mar 25 13:13:03 1995
@@ -59,6 +59,9 @@
 #ifdef CONFIG_SBPCD
 extern unsigned long sbpcd_init(unsigned long, unsigned long);
 #endif CONFIG_SBPCD
+#ifdef CONFIG_BLK_DEV_MD
+extern unsigned long md_init(unsigned long mem_start, unsigned long mem_end);
+#endif
 extern void set_device_ro(int dev,int flag);
 
 extern void floppy_init(void);
@@ -134,6 +137,15 @@
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
+/* Kludge to use the same number for both char and block major numbers */
+#elif  (MAJOR_NR == MD_MAJOR) && defined(MD_DRIVER)
+
+#define DEVICE_NAME "Multiple devices driver"
+#define DEVICE_REQUEST do_md_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
 #elif (MAJOR_NR == SCSI_TAPE_MAJOR)
 
 #define DEVICE_NAME "scsitape"
@@ -227,7 +239,7 @@
 
 #endif /* MAJOR_NR == whatever */
 
-#if (MAJOR_NR != SCSI_TAPE_MAJOR) && !defined(IDE_DRIVER)
+#if ((MAJOR_NR != SCSI_TAPE_MAJOR) && !defined(IDE_DRIVER)) || defined(MD_DRIVER)
 
 #ifndef CURRENT
 #define CURRENT (blk_dev[MAJOR_NR].current_request)
@@ -279,11 +291,11 @@
 			panic(DEVICE_NAME ": block not locked"); \
 	}
 
-#endif /* (MAJOR_NR != SCSI_TAPE_MAJOR) && !defined(IDE_DRIVER) */
+#endif /* ((MAJOR_NR != SCSI_TAPE_MAJOR) && !defined(IDE_DRIVER)) || defined(MD_DRIVER) */
 
 /* end_request() - SCSI devices have their own version */
 
-#if ! SCSI_MAJOR(MAJOR_NR)
+#if ! SCSI_MAJOR(MAJOR_NR) || defined (MD_DRIVER)
 
 #ifdef IDE_DRIVER
 static void end_request(byte uptodate, byte hwif) {
diff -u --recursive --new-file /usr/src/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- /usr/src/linux/drivers/block/ll_rw_blk.c	Thu Feb 16 22:28:47 1995
+++ linux/drivers/block/ll_rw_blk.c	Sat Mar 25 08:32:34 1995
@@ -21,6 +21,10 @@
 #include <asm/io.h>
 #include "blk.h"
 
+#ifdef CONFIG_BLK_DEV_MD
+#include <linux/md.h>
+#endif
+
 /*
  * The request-struct contains all necessary data
  * to load a nr of sectors into memory
@@ -224,10 +228,20 @@
 	unsigned int sector, count;
 	struct request * req;
 	int rw_ahead, max_req;
+	dev_t rdev;
 
 /* WRITEA/READA is special case - it is not really needed, so if the */
 /* buffer is locked, we just forget about it, else it's a normal read */
 	rw_ahead = (rw == READA || rw == WRITEA);
+
+	count = bh->b_size >> 9;
+#ifdef CONFIG_BLK_DEV_MD
+	map_to_real (bh, 0, 0, &rdev, &sector);
+#else
+	rdev=bh->b_dev;
+	sector = bh->b_blocknr * count;
+#endif
+
 	if (rw_ahead) {
 		if (bh->b_lock)
 			return;
@@ -240,10 +254,9 @@
 		printk("Bad block dev command, must be R/W/RA/WA\n");
 		return;
 	}
-	count = bh->b_size >> 9;
-	sector = bh->b_blocknr * count;
+
 	if (blk_size[major])
-		if (blk_size[major][MINOR(bh->b_dev)] < (sector + count)>>1) {
+		if (blk_size[major][MINOR(rdev)] < (sector + count)>>1) {
 			bh->b_dirt = bh->b_uptodate = 0;
 			bh->b_req = 0;
 			return;
@@ -287,7 +300,7 @@
 #endif CONFIG_BLK_DEV_HD
 			req = req->next;
 		while (req) {
-			if (req->dev == bh->b_dev &&
+			if (req->dev == rdev &&
 			    !req->sem &&
 			    req->cmd == rw &&
 			    req->sector + req->nr_sectors == sector &&
@@ -301,7 +314,7 @@
 				return;
 			}
 
-			if (req->dev == bh->b_dev &&
+			if (req->dev == rdev &&
 			    !req->sem &&
 			    req->cmd == rw &&
 			    req->sector - count == sector &&
@@ -323,7 +336,7 @@
 	}
 
 /* find an unused request. */
-	req = get_request(max_req, bh->b_dev);
+	req = get_request(max_req, rdev);
 
 /* if no request available: if rw_ahead, forget it; otherwise try again. */
 	if (! req) {
@@ -359,24 +372,35 @@
 	struct request * req;
 	unsigned int major = MAJOR(dev);
 	struct semaphore sem = MUTEX_LOCKED;
+	int sector = page<<3;
+	dev_t rdev=(dev_t) dev;
+
+#ifdef CONFIG_BLK_DEV_MD
+	if (map_to_real (NULL, rdev, sector, &rdev, &sector))
+	{
+	  printk ("Oops ! Bad mapping in ll_rw_page. rdev=%04x, page=%d\n", rdev, page);
+	  return;
+	}
+	major=MAJOR(rdev);
+#endif
 
 	if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
-		printk("Trying to read nonexistent block-device %04x (%d)\n",dev,page*8);
+		printk("Trying to read nonexistent block-device %04x (%d)\n",rdev,page*8);
 		return;
 	}
 	if (rw!=READ && rw!=WRITE)
 		panic("Bad block dev command, must be R/W");
-	if (rw == WRITE && is_read_only(dev)) {
-		printk("Can't page to read-only device 0x%X\n",dev);
+	if (rw == WRITE && is_read_only(rdev)) {
+		printk("Can't page to read-only device 0x%X\n",rdev);
 		return;
 	}
 	cli();
-	req = get_request_wait(NR_REQUEST, dev);
+	req = get_request_wait(NR_REQUEST, rdev);
 	sti();
 /* fill up the request-info, and add it to the queue */
 	req->cmd = rw;
 	req->errors = 0;
-	req->sector = page<<3;
+	req->sector = sector;
 	req->nr_sectors = 8;
 	req->current_nr_sectors = 8;
 	req->buffer = buffer;
@@ -394,8 +418,15 @@
 void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
 {
 	unsigned int major;
+#ifdef CONFIG_BLK_DEV_MD
+	int nr_plugged=0;
+	struct blk_dev_struct *plugged[MAX_REAL];
+	struct request plug[MAX_REAL];
+	dev_t rdev;
+#else
 	struct request plug;
 	int plugged;
+#endif CONFIG_BLK_DEV_MD
 	int correct_size;
 	struct blk_dev_struct * dev;
 	int i;
@@ -445,6 +476,49 @@
 	   from starting until we have shoved all of the blocks into the
 	   queue, and then we let it rip.  */
 
+#ifdef CONFIG_BLK_DEV_MD
+	for (i=0; i<nr; i++)
+	{
+	  int maj;
+	  
+	  if (map_to_real (bh[i], 0, 0, &rdev, NULL))
+	  {
+	    printk ("device md%d not ready, givin' up !\n", MINOR(bh[i]->b_dev));
+	    goto sorry;
+	  }
+
+	  maj=MAJOR(rdev);
+
+	  cli();
+	  if (!(blk_dev+maj)->current_request && nr>1)
+	  {
+	    plugged[nr_plugged]=blk_dev+maj;
+	    plugged[nr_plugged]->current_request=plug+nr_plugged;
+	    plug[nr_plugged].dev=-1;
+	    plug[nr_plugged++].next=NULL;
+	  }
+	  sti();
+
+	  if (bh[i])
+	  {
+	    bh[i]->b_req=1;
+	    make_request (maj, rw, bh[i]);
+	    if (rw == READ || rw == READA)
+	      kstat.pgpgin++;
+	    else
+	      kstat.pgpgout++;
+	  }
+	}
+
+	for (i=0; i<nr_plugged; i++)
+	{
+	  cli();
+	  plugged[i]->current_request=plug[i].next;
+	  (plugged[i]->request_fn)();
+	  sti();
+	}
+	return;
+#else
 	plugged = 0;
 	cli();
 	if (!dev->current_request && nr > 1) {
@@ -471,6 +545,7 @@
 		sti();
 	}
 	return;
+#endif CONFIG_BLK_DEV_MD
 
       sorry:
 	for (i = 0; i < nr; i++) {
@@ -482,8 +557,8 @@
 
 void ll_rw_swap_file(int rw, int dev, unsigned int *b, int nb, char *buf)
 {
-	int i;
-	int buffersize;
+	dev_t rdev;
+	int buffersize, sector, i;
 	struct request * req;
 	unsigned int major = MAJOR(dev);
 	struct semaphore sem = MUTEX_LOCKED;
@@ -506,12 +581,21 @@
 
 	for (i=0; i<nb; i++, buf += buffersize)
 	{
+	        sector=(b[i]*buffersize)>>9;
+#ifdef CONFIG_BLK_DEV_MD
+		if (map_to_real (NULL, dev, sector, &rdev, &sector))
+		{
+		  printk ("Oops ! Bad mapping in ll_rw_swap_file. dev=%04x, sector=%d\n", dev, sector);
+		  return;
+		}
+		major=MAJOR(rdev);
+#endif
 		cli();
-		req = get_request_wait(NR_REQUEST, dev);
+		req = get_request_wait(NR_REQUEST, rdev);
 		sti();
 		req->cmd = rw;
 		req->errors = 0;
-		req->sector = (b[i] * buffersize) >> 9;
+		req->sector = sector;
 		req->nr_sectors = buffersize >> 9;
 		req->current_nr_sectors = buffersize >> 9;
 		req->buffer = buf;
@@ -562,6 +646,9 @@
 #ifdef CONFIG_SBPCD
 	mem_start = sbpcd_init(mem_start, mem_end);
 #endif CONFIG_SBPCD
+#ifdef CONFIG_BLK_DEV_MD
+	mem_start = md_init(mem_start, mem_end);
+#endif CONFIG_BLK_DEV_MD
 	if (ramdisk_size)
 		mem_start += rd_init(mem_start, ramdisk_size*1024);
 	return mem_start;
diff -u --recursive --new-file /usr/src/linux/drivers/block/md.c linux/drivers/block/md.c
--- /usr/src/linux/drivers/block/md.c
+++ linux/drivers/block/md.c	Sat Mar 25 12:58:35 1995
@@ -0,0 +1,606 @@
+
+/*
+   md.c : Multiple Devices driver for Linux
+          Copyright (C) 1994, 1995 Marc ZYNGIER
+	  <zyngier@amertume.ufr-info-p7.ibp.fr> or
+	  <maz@gloups.fdn.fr>
+
+   A lot of inspiration came from hd.c ...
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+   
+   You should have received a copy of the GNU General Public License
+   (for example /usr/src/linux/COPYING); if not, write to the Free
+   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+*/
+
+#define MD_VERSION "v0.15"
+
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/md.h>
+#include <linux/hdreg.h>
+#include <linux/fs.h>
+#include <errno.h>
+
+#define MAJOR_NR MD_MAJOR
+#define MD_DRIVER
+
+#include "blk.h"
+
+static int nb_dev[MAX_MD_DEV]={0, };  /* Real devices count */
+static int md_size[MAX_MD_DEV]={0, }; /* Size of md device (in blocks) */
+
+static int busy[MAX_MD_DEV]={0, };
+static struct hd_struct md_hd_struct[MAX_MD_DEV];
+static int md_blocksizes[MAX_MD_DEV];
+
+static void md_geninit (void);
+
+static struct gendisk md_gendisk=
+{
+  MD_MAJOR,
+  "md",
+  0,
+  1,
+  MAX_MD_DEV,
+  md_geninit,
+  md_hd_struct,
+  md_size,
+  MAX_MD_DEV,
+  NULL,
+  NULL
+};
+
+static int nr_zones[MAX_MD_DEV]={0, };
+static struct real_dev devices[MAX_MD_DEV][MAX_REAL];
+static struct strip_zone *strip_zone[MAX_MD_DEV]; /* Only used whed STRIPPED */
+static int nr_strip_zones[MAX_MD_DEV]={0, };
+
+/* Those are global, since functions defined in md.h need them. */
+
+int repartition[MAX_MD_DEV]={0, }; /* Default is linear, factor=0 */
+struct md_hash *md_hash_table[MAX_MD_DEV]={NULL, };
+union md_union md_smallest[MAX_MD_DEV]={{NULL}, };
+
+static void create_strip_zones (int minor)
+{
+  int i, j, c=0;
+  int current_offset=0;
+  struct real_dev *smallest_by_zone;
+  
+  nr_strip_zones[minor]=1;
+  
+  for (i=1; i<nb_dev[minor]; i++)
+  {
+    for (j=0; j<i; j++)
+      if (devices[minor][i].size==devices[minor][j].size)
+      {
+	c=1;
+	break;
+      }
+
+    if (!c)
+      nr_strip_zones[minor]++;
+
+    c=0;
+  }
+
+  strip_zone[minor]=kmalloc (sizeof (struct strip_zone)*nr_strip_zones[minor],
+			     GFP_KERNEL);
+
+  md_smallest[minor].zone=NULL;
+  
+  for (i=0; i<nr_strip_zones[minor]; i++)
+  {
+    strip_zone[minor][i].dev_offset=current_offset;
+    smallest_by_zone=NULL;
+    c=0;
+
+    for (j=0; j<nb_dev[minor]; j++)
+      if (devices[minor][j].size>current_offset)
+      {
+	strip_zone[minor][i].dev[c++]=devices[minor]+j;
+	if (!smallest_by_zone ||
+	    smallest_by_zone->size > devices[minor][j].size)
+	  smallest_by_zone=devices[minor]+j;
+      }
+
+    strip_zone[minor][i].nb_dev=c;
+    strip_zone[minor][i].size=(smallest_by_zone->size-current_offset)*c;
+
+    if (!md_smallest[minor].zone ||
+	md_smallest[minor].zone->size > strip_zone[minor][i].size)
+      md_smallest[minor].zone=strip_zone[minor]+i;
+
+    strip_zone[minor][i].zone_offset=i ? (strip_zone[minor][i-1].zone_offset +
+					  strip_zone[minor][i-1].size) : 0;
+    current_offset=smallest_by_zone->size;
+  }
+}
+
+static void create_stripped_hash_table (int minor)
+{
+  int current=0, i=0, size, zone0_size, nb_zone;
+
+  create_strip_zones (minor);
+  
+  nb_zone=nr_zones[minor]=
+    md_size[minor]/md_smallest[minor].zone->size +
+    (md_size[minor]%md_smallest[minor].zone->size ? 1 : 0);
+  
+  md_hash_table[minor]=kmalloc (sizeof (struct md_hash)*nb_zone, GFP_KERNEL);
+
+  size=strip_zone[minor][current].size;
+
+  while (current<nr_strip_zones[minor])
+  {
+    md_hash_table[minor][i].unit0.zone=strip_zone[minor]+current;
+
+    if (size>=md_smallest[minor].zone->size)/* If we completly fill the slot */
+    {
+      md_hash_table[minor][i++].unit1.zone=NULL;
+      size-=md_smallest[minor].zone->size;
+
+      if (!size)
+      {
+	if (++current==nr_strip_zones[minor]) continue;
+	size=strip_zone[minor][current].size;
+      }
+
+      continue;
+    }
+
+    if (++current==nr_strip_zones[minor]) /* Last dev, set unit1 as NULL */
+    {
+      md_hash_table[minor][i].unit1.zone=NULL;
+      continue;
+    }
+
+    zone0_size=size;		/* Here, we use a 2nd dev to fill the slot */
+    size=strip_zone[minor][current].size;
+    md_hash_table[minor][i++].unit1.zone=strip_zone[minor]+current;
+    size-=(md_smallest[minor].zone->size - zone0_size);
+  }
+}
+
+
+static void create_linear_hash_table (int minor)
+{
+  int current=0, i=0, size, dev0_size, nb_zone;
+
+  nb_zone=nr_zones[minor]=
+    md_size[minor]/md_smallest[minor].dev->size +
+    (md_size[minor]%md_smallest[minor].dev->size ? 1 : 0);
+  
+  md_hash_table[minor]=kmalloc (sizeof (struct md_hash)*nb_zone, GFP_KERNEL);
+
+  size=devices[minor][current].size;
+
+  while (current<nb_dev[minor])
+  {
+    md_hash_table[minor][i].unit0.dev=devices[minor]+current;
+
+    if (size>=md_smallest[minor].dev->size) /* If we completly fill the slot */
+    {
+      md_hash_table[minor][i++].unit1.dev=NULL;
+      size-=md_smallest[minor].dev->size;
+
+      if (!size)
+      {
+	if (++current==nb_dev[minor]) continue;
+	size=devices[minor][current].size;
+      }
+
+      continue;
+    }
+
+    if (++current==nb_dev[minor]) /* Last dev, set dev1 as NULL */
+    {
+      md_hash_table[minor][i].unit1.dev=NULL;
+      continue;
+    }
+
+    dev0_size=size;		/* Here, we use a 2nd dev to fill the slot */
+    size=devices[minor][current].size;
+    md_hash_table[minor][i++].unit1.dev=devices[minor]+current;
+    size-=(md_smallest[minor].dev->size - dev0_size);
+  }
+}
+
+
+static int create_md_hash_table (int minor)
+{
+  int i;
+  
+  switch (PERSONALITY(minor))
+  {
+    case LINEAR:
+    create_linear_hash_table (minor);
+    break;
+
+    case STRIPPED:
+    /* Resize devices according to the factor */
+    md_size[minor]=0;
+
+    for (i=0; i<nb_dev[minor]; i++)
+    {
+      devices[minor][i].size &= ~((1 << FACTOR_SHIFT(FACTOR(minor))) - 1);
+      md_size[minor] += devices[minor][i].size;
+    }
+      
+    create_stripped_hash_table (minor);
+    break;
+
+    default:
+    printk ("Unknown personality %x\n", PERSONALITY(minor));
+    return -EINVAL;
+  }
+
+  return 0;
+}
+
+
+static struct gendisk *find_gendisk (dev_t dev)
+{
+  struct gendisk *tmp=gendisk_head;
+
+  while (tmp != NULL)
+  {
+    if (tmp->major==MAJOR(dev))
+      return (tmp);
+    
+    tmp=tmp->next;
+  }
+
+  return (NULL);
+}
+
+
+/* Picked up from genhd.c */
+static char *partition_name (dev_t dev)
+{
+  static char name[10];		/* This should be long
+				   enough for a device name ! */
+  struct gendisk *hd=find_gendisk (dev);
+  char base_name = (hd->major == IDE1_MAJOR) ? 'c' : 'a';
+  int minor=MINOR(dev);
+  
+  sprintf(name, "%s%c%d",
+	  hd->major_name,
+	  base_name + (minor >> hd->minor_shift),
+	  minor & ((1 << hd->minor_shift) - 1));
+  return (name);
+}
+
+
+static int md_ioctl (struct inode *inode, struct file *file,
+                     unsigned int cmd, unsigned long arg)
+{
+  int minor, index, err;
+  struct gendisk *gen_real;
+  struct hd_geometry *loc = (struct hd_geometry *) arg;
+
+  if (!suser())
+    return -EACCES;
+
+  if (((minor=MINOR(inode->i_rdev))>=MAX_MD_DEV))
+    return -EINVAL;
+
+  switch (cmd)
+  {
+    case REGISTER_DEV:
+    if (MAJOR((dev_t) arg)==MD_MAJOR || nb_dev[minor]==MAX_REAL)
+      return -EINVAL;
+
+    if (!fs_may_mount ((dev_t) arg) || md_hash_table[minor])
+      return -EBUSY;
+
+    if (!(gen_real=find_gendisk ((dev_t) arg)))
+      return -ENOENT;
+
+    index=nb_dev[minor]++;
+    devices[minor][index].dev=(dev_t) arg;
+
+    /* Lock the device by inserting a dummy inode. This doesn't
+       smeel very good, but I need to be consistent with the
+       mount stuff, specially with fs_may_mount. If someone have
+       a better idea, please help ! */
+    
+    devices[minor][index].inode=get_empty_inode ();
+    devices[minor][index].inode->i_dev=(dev_t) arg; /* don't care about
+						       other fields */
+    insert_inode_hash (devices[minor][index].inode);
+    
+    /* Devices sizes are rounded to a multiple of page (needed for
+       paging). This is NOT done by fdisk when partitionning,
+       but that's a DOS thing anyway... */
+    
+    devices[minor][index].size=gen_real->sizes[MINOR((dev_t) arg)] & (PAGE_MASK>>10);
+    devices[minor][index].offset=index ?
+      (devices[minor][index-1].offset + devices[minor][index-1].size) : 0;
+
+    if (!index || md_smallest[minor].dev->size > devices[minor][index].size)
+      md_smallest[minor].dev=devices[minor]+index;
+
+    if (!index)
+      md_size[minor]=devices[minor][index].size;
+    else
+      md_size[minor]+=devices[minor][index].size;
+
+    printk("REGISTER_DEV %s to md%x done\n", partition_name((dev_t) arg), minor);
+    break;
+
+    case START_MD:
+    if (!nb_dev[minor])
+      return -EINVAL;
+
+    if (md_hash_table[minor])
+      return -EBUSY;
+
+    repartition[minor]=(int) arg;
+    
+    if (PERSONALITY(minor)==STRIPPED)
+    {
+      int i, min=1 << FACTOR_SHIFT(FACTOR(minor));
+
+      for (i=0; i<nb_dev[minor]; i++)
+	if (devices[minor][i].size<min)
+	{
+	  printk ("Cannot use %dk chunks on dev %s\n", min,
+		  partition_name (devices[minor][i].dev));
+	  return -EINVAL;
+	}
+    }
+    
+    if ((err=create_md_hash_table (minor)))
+      return err;
+
+    /* FIXME : We assume here we have blocks
+       that are twice as large as sectors.
+       THIS MAY NOT BE TRUE !!! */
+    md_hd_struct[minor].start_sect=0;
+    md_hd_struct[minor].nr_sects=md_size[minor]<<1;
+
+    printk ("START_DEV md%x ", minor);
+    switch (PERSONALITY(minor))
+    {
+      case LINEAR:
+      printk ("linear\n");
+      break;
+
+      case STRIPPED:
+      printk ("stripped, factor %d\n", FACTOR (minor));
+      break;
+    }
+    
+    break;
+
+    case STOP_MD:
+    if (inode->i_count>1 || busy[minor]>1) /* ioctl : one open channel */
+    {
+      printk ("STOP_MD md%x failed : i_count=%d, busy=%d\n", minor, inode->i_count, busy[minor]);
+      return -EBUSY;
+    }
+
+    if (md_hash_table[minor])
+    {
+      /*  The device won't exist anymore -> flush it now */
+      fsync_dev (inode->i_rdev);
+      invalidate_buffers (inode->i_rdev);
+      if (PERSONALITY(minor)==STRIPPED)
+	kfree (strip_zone[minor]);
+      kfree (md_hash_table[minor]);
+    }
+
+    /* Remove locks. */
+    for (index=0; index<nb_dev[minor]; index++)
+      clear_inode (devices[minor][index].inode);
+    
+    nb_dev[minor]=md_size[minor]=0;
+    md_hash_table[minor]=NULL;
+    md_smallest[minor].dev=NULL;
+    printk ("STOP_DEV md%x\n", minor);
+    break;
+
+    case BLKGETSIZE:   /* Return device size */
+    if  (!arg)  return -EINVAL;
+    err=verify_area (VERIFY_WRITE, (long *) arg, sizeof(long));
+    if (err)
+      return err;
+    put_fs_long (md_hd_struct[MINOR(inode->i_rdev)].nr_sects, (long *) arg);
+    break;
+
+    case BLKFLSBUF:
+    fsync_dev (inode->i_rdev);
+    invalidate_buffers (inode->i_rdev);
+    break;
+
+    case HDIO_GETGEO:
+    if (!loc)  return -EINVAL;
+    err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
+    if (err)
+      return err;
+    put_fs_byte(2, (char *) &loc->heads);
+    put_fs_byte(4, (char *) &loc->sectors);
+    put_fs_word(md_hd_struct[minor].nr_sects/8, (short *) &loc->cylinders);
+    put_fs_long(md_hd_struct[MINOR(inode->i_rdev)].start_sect,
+		(long *) &loc->start);
+    break;
+    
+    default:
+    printk ("Unknown md_ioctl %d\n", cmd);
+    return -EINVAL;
+  }
+
+  return (0);
+}
+
+
+static int md_open (struct inode *inode, struct file *file)
+{
+  int minor=MINOR(inode->i_rdev);
+
+  busy[minor]++;
+  /* printk ("md_open called. busy[%d] is now %d\n", minor, busy[minor]);*/
+  return (0);			/* Always succed */
+}
+
+
+static void md_release (struct inode *inode, struct file *file)
+{
+  int minor=MINOR(inode->i_rdev);
+
+  sync_dev (inode->i_rdev);
+  busy[minor]--;
+}
+
+
+static struct file_operations md_fops=
+{
+  NULL,
+  block_read,
+  block_write,
+  NULL,
+  NULL,
+  md_ioctl,
+  NULL,
+  md_open,
+  md_release,
+  block_fsync
+};
+
+
+static void do_md_request (void)
+{
+  INIT_REQUEST;
+  printk ("Oops ! Got unexpected md request on dev md%x\n", CURRENT->dev);
+  end_request (0);
+}
+
+
+static void md_geninit (void)
+{
+  int i;
+  
+  for(i=0;i<MAX_MD_DEV;i++)
+  {
+    md_blocksizes[i] = 1024;
+    md_gendisk.part[i].start_sect=-1;
+  }
+
+  blksize_size[MAJOR_NR] = md_blocksizes;
+}
+
+
+unsigned long md_init (unsigned long mem_start,
+		       unsigned long mem_long)
+{
+  printk ("md driver %s MAX_MD_DEV=%d, MAX_REAL=%d\n", MD_VERSION, MAX_MD_DEV, MAX_REAL);
+
+  if (register_blkdev (MD_MAJOR, "md", &md_fops))
+  {
+    printk ("Unable to get major %d for md\n", MD_MAJOR);
+    return (mem_start);
+  }
+
+  blk_dev[MD_MAJOR].request_fn=DEVICE_REQUEST;
+  read_ahead[MD_MAJOR]=0;
+  md_gendisk.next=gendisk_head;
+
+  gendisk_head=&md_gendisk;
+  
+  return (mem_start);
+}
+
+int get_md_status (char *page)
+{
+  int sz=0, i, j;
+
+  for (i=0; i<MAX_MD_DEV; i++)
+  {
+    sz+=sprintf (page+sz, "md%d : %sactive", i, md_hash_table[i] ? "" : "in");
+
+    if (md_hash_table[i])
+      switch (PERSONALITY(i))
+      {
+	case LINEAR:
+	sz+=sprintf (page+sz, " linear");
+	break;
+	
+	case STRIPPED:
+	sz+=sprintf (page+sz, " stripped(%d)", FACTOR(i));
+	break;
+	
+	default:
+	sz+=sprintf (page+sz, " unknown");
+      }
+    
+    for (j=0; j<nb_dev[i]; j++)
+      sz+=sprintf (page+sz, " %s", partition_name(devices[i][j].dev));
+
+    if (nb_dev[i])
+      sz+=sprintf (page+sz, " %d blocks\n", md_size[i]);
+    else
+      sz+=sprintf (page+sz, "\n");
+
+    if (!md_hash_table[i])
+      continue;
+    
+#ifdef MD_DEBUG
+  {
+    int k;
+    switch (PERSONALITY(i))
+    {
+      case LINEAR:
+      sz+=sprintf (page+sz, "      ");
+      for (j=0; j<nr_zones[i]; j++)
+      {
+	sz+=sprintf (page+sz, "[%s",
+		     partition_name (md_hash_table[i][j].unit0.dev->dev));
+	if (md_hash_table[i][j].unit1.dev)
+	  sz+=sprintf (page+sz, "/%s] ",
+		       partition_name(md_hash_table[i][j].unit1.dev->dev));
+	else
+	  sz+=sprintf (page+sz, "] ");
+      }
+      sz+=sprintf (page+sz, "\n");
+      break;
+      
+      case STRIPPED:
+      sz+=sprintf (page+sz, "      ");
+      for (j=0; j<nr_zones[i]; j++)
+      {
+	sz+=sprintf (page+sz, "[z%d",
+		     md_hash_table[i][j].unit0.zone-strip_zone[i]);
+	if (md_hash_table[i][j].unit1.zone)
+	  sz+=sprintf (page+sz, "/z%d] ",
+		       md_hash_table[i][j].unit1.zone-strip_zone[i]);
+	else
+	  sz+=sprintf (page+sz, "] ");
+      }
+
+      sz+=sprintf (page+sz, "\n");
+      
+      for (j=0; j<nr_strip_zones[i]; j++)
+      {
+	sz+=sprintf (page+sz, "      z%d=[", j);
+	for (k=0; k<strip_zone[i][j].nb_dev; k++)
+	  sz+=sprintf (page+sz, "%s/",
+		       partition_name(strip_zone[i][j].dev[k]->dev));
+	sz--;
+	sz+=sprintf (page+sz, "] zo=%d do=%d s=%d\n",
+		     strip_zone[i][j].zone_offset,
+		     strip_zone[i][j].dev_offset,
+		     strip_zone[i][j].size);
+      }
+    }
+  }
+#endif
+  }
+
+  return (sz);
+}
diff -u --recursive --new-file /usr/src/linux/fs/proc/array.c linux/fs/proc/array.c
--- /usr/src/linux/fs/proc/array.c	Sat Mar  4 20:20:03 1995
+++ linux/fs/proc/array.c	Sat Mar 25 08:32:34 1995
@@ -44,6 +44,10 @@
 #include <linux/delay.h>
 #include <linux/mm.h>
 
+#ifdef CONFIG_BLK_DEV_MD
+#include <linux/md.h>
+#endif
+
 #include <asm/segment.h>
 #include <asm/pgtable.h>
 #include <asm/io.h>
@@ -804,6 +808,11 @@
 
 		case PROC_IOPORTS:
 			return get_ioport_list(page);
+
+#ifdef CONFIG_BLK_DEV_MD
+		case PROC_MD:
+			return get_md_status(page);
+#endif
 	}
 	return -EBADF;
 }
diff -u --recursive --new-file /usr/src/linux/fs/proc/root.c linux/fs/proc/root.c
--- /usr/src/linux/fs/proc/root.c	Sat Jan 14 08:32:18 1995
+++ linux/fs/proc/root.c	Sat Mar 25 08:32:34 1995
@@ -80,6 +80,9 @@
 #ifdef CONFIG_PROFILE
 	{ PROC_PROFILE,		7, "profile"},
 #endif
+#ifdef CONFIG_BLK_DEV_MD
+	{ PROC_MD,              6, "mdstat"},
+#endif
 };
 
 #define NR_ROOT_DIRENTRY ((sizeof (root_dir))/(sizeof (root_dir[0])))
diff -u --recursive --new-file /usr/src/linux/include/linux/major.h linux/include/linux/major.h
--- /usr/src/linux/include/linux/major.h	Sat Mar  4 20:20:08 1995
+++ linux/include/linux/major.h	Sat Mar 25 08:34:45 1995
@@ -26,7 +26,7 @@
  *  6 - lp
  *  7 - /dev/vcs*
  *  8 -                        scsi disk
- *  9 - scsi tape
+ *  9 - scsi tape              multiple devices driver
  * 10 - mice
  * 11 -                        scsi cdrom
  * 12 - qic02 tape
@@ -60,6 +60,7 @@
 #define VCS_MAJOR	7
 #define SCSI_DISK_MAJOR	8
 #define SCSI_TAPE_MAJOR	9
+#define MD_MAJOR        9
 #define MOUSE_MAJOR	10
 #define SCSI_CDROM_MAJOR 11
 #define QIC02_TAPE_MAJOR 12
diff -u --recursive --new-file /usr/src/linux/include/linux/md.h linux/include/linux/md.h
--- /usr/src/linux/include/linux/md.h
+++ linux/include/linux/md.h	Sat Mar 25 08:32:34 1995
@@ -0,0 +1,179 @@
+
+/*
+   md.h : Multiple Devices driver for Linux
+          Copyright (C) 1994, 1995 Marc ZYNGIER
+	  <zyngier@amertume.ufr-info-p7.ibp.fr> or
+	  <maz@gloups.fdn.fr>
+	  
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+   
+   You should have received a copy of the GNU General Public License
+   (for example /usr/src/linux/COPYING); if not, write to the Free
+   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+*/
+
+#ifndef _MD_H
+#define _MD_H
+
+#include <sys/types.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <linux/mm.h>
+
+/* ioctls */
+#define REGISTER_DEV (MD_MAJOR<<8 | 1)
+#define START_MD     (MD_MAJOR<<8 | 2)
+#define STOP_MD      (MD_MAJOR<<8 | 3)
+
+/*
+   personalities are made of the personality itself in the upper 16 bits
+   and the cluster factor in the lower 16 bits.
+ */
+#define PERSONALITY_SHIFT 16
+#define FACTOR_MASK       ((1U << PERSONALITY_SHIFT) -1)
+#define PERSONALITY_MASK  (~FACTOR_MASK)
+#define FACTOR(a)         (repartition[(a)] & FACTOR_MASK)
+#define PERSONALITY(a)    (repartition[(a)] & PERSONALITY_MASK)
+
+#define LINEAR            (1UL << PERSONALITY_SHIFT)
+#define STRIPPED           (2UL << PERSONALITY_SHIFT)
+
+#ifdef __KERNEL__
+
+#define MAX_REAL     8
+#define MAX_MD_DEV   4
+
+#define FACTOR_SHIFT(a) (PAGE_SHIFT + (a) - 10)
+
+struct real_dev
+{
+  dev_t dev;			/* Device number */
+  int size;			/* device size (in blocks) */
+  int offset;			/* real device offset (in blocks) in md dev
+				   (only used in linear mode) */
+  struct inode *inode;		/* lock inode */
+};
+
+struct strip_zone
+{
+  int zone_offset;		/* Zone offset in md_dev */
+  int dev_offset;		/* Zone offset in real dev */
+  int size;			/* Zone size */
+  int nb_dev;			/* Number of devices attached to the zone */
+  struct real_dev *dev[MAX_REAL]; /* Devices attached to the zone */
+};
+
+union md_union
+{
+  struct real_dev *dev;
+  struct strip_zone *zone;
+};
+
+struct md_hash
+{
+  union md_union unit0, unit1;
+};
+
+extern struct md_hash *md_hash_table[MAX_MD_DEV];
+extern union md_union md_smallest[MAX_MD_DEV];
+extern int repartition[MAX_MD_DEV];
+
+extern inline int map_to_real (struct buffer_head *bh, dev_t log_dev,
+			       int log_sector, dev_t *dev, int *sector)
+{
+  static struct md_hash *hash;
+  struct real_dev *tmp_dev;
+  struct strip_zone *zone;
+  int mask, blk_in_cluster, cluster, factor;
+  unsigned long block=bh ? bh->b_blocknr : log_sector>>1;
+  dev_t md_dev=bh ? bh->b_dev : log_dev;
+
+  if (MAJOR(md_dev)!=MD_MAJOR)
+  {
+    if (dev)
+      *dev=md_dev;
+    if (sector)
+      *sector=(bh ? (bh->b_size>>9)*block : log_sector);
+    
+    return 0;
+  }
+  
+  if (!md_hash_table[MINOR(md_dev)])
+  {
+    printk ("Oops ! md_hash_table[%04x]=NULL\n", MINOR(md_dev));
+    return -1;
+  }
+
+  switch (PERSONALITY(MINOR(md_dev)))
+  {
+    case LINEAR:
+    
+    hash=md_hash_table[MINOR(md_dev)]+(block/md_smallest[MINOR(md_dev)].dev->size);
+    
+    if (block >= (hash->unit0.dev->size + hash->unit0.dev->offset))
+    {
+      if (!hash->unit1.dev)
+	printk ("md_bh_to_real : hash->unit1.dev==NULL for block %ld\n", block);
+      tmp_dev=hash->unit1.dev;
+    }
+    else
+      tmp_dev=hash->unit0.dev;
+    
+    if (dev)
+      *dev=tmp_dev->dev;
+    
+    if (!sector)
+      return 0;
+    
+    if (block >= (tmp_dev->size + tmp_dev->offset) || block < tmp_dev->offset)
+      printk ("Block %ld out of bounds on dev %04x size %d offset %d\n", block, tmp_dev->dev, tmp_dev->size, tmp_dev->offset);
+    
+    *sector=(block-(tmp_dev->offset))*(bh ? (bh->b_size>>9) : 2);
+    
+    return 0;
+
+    case STRIPPED:
+    factor=FACTOR(MINOR(md_dev));
+    
+    hash=md_hash_table[MINOR(md_dev)]+(block/md_smallest[MINOR(md_dev)].zone->size);
+        
+    if (block >= (hash->unit0.zone->size +
+		  hash->unit0.zone->zone_offset))
+    {
+      if (!hash->unit1.zone)
+	printk ("md_bh_to_real : hash->unit1.dev==NULL for block %ld\n", block);
+      zone=hash->unit1.zone;
+    }
+    else
+      zone=hash->unit0.zone;
+
+    mask=(1UL << FACTOR_SHIFT(factor)) - 1;
+    blk_in_cluster=block & mask;
+    cluster=block & ~mask;
+    tmp_dev=zone->dev[((cluster >> FACTOR_SHIFT(factor)) % zone->nb_dev)];
+    
+    if (dev)
+      *dev=tmp_dev->dev;
+    
+    if (!sector)
+      return 0;
+    
+    *sector=((cluster - zone->zone_offset) / zone->nb_dev +
+	     blk_in_cluster + zone->dev_offset)*(bh ? (bh->b_size>>9) : 2);
+    
+    return 0;
+
+    default:
+    printk ("Unknown personality %04x for md%d\n", PERSONALITY(MINOR(md_dev)), MINOR(md_dev));
+    return (-1);
+  }
+}
+    
+
+extern int get_md_status (char *page);
+
+#endif __KERNEL__
+#endif _MD_H
diff -u --recursive --new-file /usr/src/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- /usr/src/linux/include/linux/proc_fs.h	Thu Feb 16 22:28:39 1995
+++ linux/include/linux/proc_fs.h	Sat Mar 25 08:32:34 1995
@@ -30,7 +30,10 @@
 	PROC_KSYMS,
 	PROC_DMA,	
 	PROC_IOPORTS,
-	PROC_PROFILE /* whether enabled or not */
+	PROC_PROFILE, /* whether enabled or not */
+#ifdef CONFIG_BLK_DEV_MD
+	PROC_MD
+#endif
 };
 
 enum pid_directory_inos {
