diff -u --recursive --new-file /usr/src/linux/MAGIC linux/MAGIC
--- /usr/src/linux/MAGIC	Sat Jan 14 08:32:05 1995
+++ linux/MAGIC	Thu Feb 16 22:31:33 1995
@@ -53,6 +53,7 @@
 0x03	hdreg.h
 0x06	lp.h
 0x12	fs.h
+0x1e	md.h
 'C'	soundcard.h
 'K'	kd.h
 'M'	soundcard.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	Thu Feb 16 22:31:33 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	Thu Feb 16 22:28:34 1995
+++ linux/drivers/block/Makefile	Thu Feb 16 22:31:33 1995
@@ -84,6 +84,11 @@
 SRCS := $(SRCS) sonycd535.c
 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	Thu Feb 16 22:31:33 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);
@@ -221,6 +224,14 @@
 
 #define DEVICE_NAME "Matsushita CD-ROM controller #4"
 #define DEVICE_REQUEST do_sbpcd4_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif  (MAJOR_NR == MD_MAJOR)
+
+#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)
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	Thu Feb 16 22:31:33 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,26 @@
 	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
+	if (MAJOR(bh->b_dev)==MD_MAJOR)
+	  md_bh_to_real (bh, &rdev, &sector);
+	else
+	{
+	  rdev=bh->b_dev;
+	  sector = bh->b_blocknr * count;
+	}
+#else
+	rdev=bh->b_dev;
+	sector = bh->b_blocknr * count;
+#endif
+
 	if (rw_ahead) {
 		if (bh->b_lock)
 			return;
@@ -240,10 +260,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 +306,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 +320,7 @@
 				return;
 			}
 
-			if (req->dev == bh->b_dev &&
+			if (req->dev == rdev &&
 			    !req->sem &&
 			    req->cmd == rw &&
 			    req->sector - count == sector &&
@@ -323,7 +342,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,6 +378,23 @@
 	struct request * req;
 	unsigned int major = MAJOR(dev);
 	struct semaphore sem = MUTEX_LOCKED;
+	int sector = page<<3;
+
+#ifdef CONFIG_BLK_DEV_MD
+	struct real_dev *md_rdev=NULL;
+	
+	if (major==MD_MAJOR)
+	{
+	  if (!(md_rdev=locate_dev (dev, sector)))
+	  {
+	    printk ("Oops ! Bad locate_dev in ll_rw_page. dev=%04x, page=%d\n", dev, page);
+	    return;
+	  }
+	  dev=md_rdev->dev;
+	  major=MAJOR(md_rdev->dev);
+	  real_sector (md_rdev, &sector);
+	}
+#endif
 
 	if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
 		printk("Trying to read nonexistent block-device %04x (%d)\n",dev,page*8);
@@ -376,7 +412,7 @@
 /* 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 +430,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 +488,53 @@
 	   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 (major==MD_MAJOR)
+	  {
+	    if (md_bh_to_real (bh[i], &rdev, NULL))
+	    {
+	      printk ("device md%d not ready, givin' up !\n", MINOR(bh[i]->b_dev));
+	      goto sorry;
+	    }
+	  }
+	  else
+	    rdev=bh[i]->b_dev;
+	  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 +561,7 @@
 		sti();
 	}
 	return;
+#endif CONFIG_BLK_DEV_MD
 
       sorry:
 	for (i = 0; i < nr; i++) {
@@ -482,12 +573,16 @@
 
 void ll_rw_swap_file(int rw, int dev, unsigned int *b, int nb, char *buf)
 {
-	int i;
-	int buffersize;
+	int i, major_is_md, rdev;
+	int buffersize, sector;
 	struct request * req;
 	unsigned int major = MAJOR(dev);
 	struct semaphore sem = MUTEX_LOCKED;
 
+#ifdef CONFIG_BLK_DEV_MD
+	struct real_dev *md_rdev;
+#endif
+
 	if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
 		printk("ll_rw_swap_file: trying to swap nonexistent block-device\n");
 		return;
@@ -503,15 +598,31 @@
 	}
 	
 	buffersize = PAGE_SIZE / nb;
+	major_is_md=(major==MD_MAJOR);
+	rdev=dev;
 
 	for (i=0; i<nb; i++, buf += buffersize)
 	{
+	        sector=(b[i]*buffersize)>>9;
+#ifdef CONFIG_BLK_DEV_MD
+	        if (major_is_md)
+		{
+		  if (!(md_rdev=locate_dev (dev, sector)))
+		  {
+		    printk ("Oops ! Bad locate_dev in ll_rw_swap_file. dev=%04x, sector=%d\n", dev, sector);
+		    return;
+		  }
+		  rdev=md_rdev->dev;
+		  major=MAJOR(md_rdev->dev);
+		  real_sector (md_rdev, &sector);
+		}
+#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 +673,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	Fri Feb 17 08:25:59 1995
@@ -0,0 +1,391 @@
+
+/*
+   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.13"
+
+#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
+
+#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, };
+struct md_hash *md_hash_table[MAX_MD_DEV]={NULL, };
+struct real_dev *md_smallest_dev[MAX_MD_DEV]={NULL, };
+static struct real_dev devices[MAX_MD_DEV][MAX_REAL];
+
+static void create_md_hash_table (int minor)
+{
+  int current=0, i=0, size, dev0_size, nb_zone;
+
+  nb_zone=nr_zones[minor]=
+    md_size[minor]/md_smallest_dev[minor]->size +
+    (md_size[minor]%md_smallest_dev[minor]->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].dev0=devices[minor]+current;
+
+    if (size>=md_smallest_dev[minor]->size) /* If we completly fill the slot */
+    {
+      md_hash_table[minor][i++].dev1=NULL;
+      size-=md_smallest_dev[minor]->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].dev1=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++].dev1=devices[minor]+current;
+    size-=(md_smallest_dev[minor]->size - dev0_size);
+  }
+}
+
+
+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). It should be already done by fdisk when
+       partitionning, but I would NOT trust such a DOS thing... */
+    
+    devices[minor][index].size=gen_real->sizes[MINOR((dev_t) arg)] & ~3;
+    devices[minor][index].offset=index ?
+      (devices[minor][index-1].offset + devices[minor][index-1].size) : 0;
+
+    if (!index || md_smallest_dev[minor]->size > devices[minor][index].size)
+      md_smallest_dev[minor]=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;
+
+    create_md_hash_table (minor);
+
+    /* 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\n", minor);
+    break;
+
+    case STOP_MD:
+    /* FIXME: We must check that the device is not
+       mounted nor opened BEFORE trying to stop it !! */
+    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);
+      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_dev[minor]=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 :", i);
+    sz+=sprintf (page+sz," %sactive", md_hash_table[i] ? "" : "in");
+    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])
+    {
+      sz+=sprintf (page+sz, "      ");
+      for (j=0; j<nr_zones[i]; j++)
+      {
+	/* FIXME: remove this when debugged (un jour mon prince viendra...) */
+	if (!md_hash_table[i][j].dev0)
+	{
+	  printk ("md_hash_table[%d][%d].dev0==NULL\n", i, j);
+	  continue;
+	}
+	sz+=sprintf (page+sz, "[%s",
+		     partition_name (md_hash_table[i][j].dev0->dev));
+	if (md_hash_table[i][j].dev1)
+	  sz+=sprintf (page+sz, "/%s] ",
+		       partition_name(md_hash_table[i][j].dev1->dev));
+	else
+	  sz+=sprintf (page+sz, "] ");
+      }
+
+      sz+=sprintf (page+sz, "\n");
+    }	
+  }
+
+  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	Thu Feb 16 22:28:51 1995
+++ linux/fs/proc/array.c	Thu Feb 16 22:31: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	Thu Feb 16 22:31: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	Thu Feb 16 22:28:52 1995
+++ linux/include/linux/major.h	Thu Feb 16 22:31:34 1995
@@ -47,6 +47,7 @@
  * 27 - qic117 tape            matsushita cdrom 3     minors 0..3
  * 28 -                        matsushita cdrom 4     minors 0..3
  * 29 -                        aztech/orchid/okano/wearnes cdrom
+ * 30 -                        multiple devices driver (md)
  */
 
 #define UNNAMED_MAJOR	0
@@ -80,6 +81,7 @@
 #define MATSUSHITA_CDROM4_MAJOR 28
 #define QIC117_TAPE_MAJOR 27
 #define AZTECH_CDROM_MAJOR 29
+#define MD_MAJOR 30
 
 /*
  * Tests for SCSI devices.
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	Thu Feb 16 22:31:34 1995
@@ -0,0 +1,125 @@
+
+/*
+   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>
+
+#define REGISTER_DEV (MD_MAJOR<<8 | 1)
+#define START_MD     (MD_MAJOR<<8 | 2)
+#define STOP_MD      (MD_MAJOR<<8 | 3)
+
+#ifdef __KERNEL__
+
+struct real_dev
+{
+  dev_t dev;
+  int size;			/* device size (in blocks) */
+  int offset;			/* real device offset (in blocks) in md dev */
+  struct inode *inode;		/* lock inode */
+};
+
+struct md_hash
+{
+  struct real_dev *dev0, *dev1;
+};
+
+#define MAX_REAL     8
+#define MAX_MD_DEV   4
+
+extern struct md_hash *md_hash_table[MAX_MD_DEV];
+extern struct real_dev *md_smallest_dev[MAX_MD_DEV];
+
+extern inline struct real_dev *locate_dev (dev_t dev, int sector)
+{
+  static struct md_hash *hash;
+  int block;
+
+  block=sector>>1;
+  
+  if (!md_hash_table[MINOR(dev)])
+  {
+    printk ("Oops ! md_hash_table[%02x]=NULL\n", MINOR(dev));
+    return NULL;
+  }
+  
+  hash=md_hash_table[MINOR(dev)]+(block/md_smallest_dev[MINOR(dev)]->size);
+
+  if (block >= (hash->dev0->size + hash->dev0->offset))
+  {
+    if (!hash->dev1)
+      printk ("hash->dev1==NULL for block %d\n", block);
+    return (hash->dev1);
+  }
+
+  if (!(hash->dev0))
+    printk ("hash->dev0==NULL for block %d\n", block);
+  return (hash->dev0);
+}
+
+extern inline void real_sector (struct real_dev *dev, int *sector)
+{
+  int block;
+
+  block=(*sector)>>1;
+  if (block >= (dev->size + dev->offset) || block < dev->offset)
+    printk ("Block %d out of bounds on dev %04x size %d offset %d\n", block, dev->dev, dev->size, dev->offset);
+  *sector=(block-dev->offset)<<1;
+}
+
+extern inline int md_bh_to_real (struct buffer_head *bh, dev_t *dev, int *sector)
+{
+  static struct md_hash *hash;
+  struct real_dev *tmp_dev;
+    
+  if (!md_hash_table[MINOR(bh->b_dev)])
+  {
+    printk ("Oops ! md_hash_table[%04x]=NULL\n", MINOR(bh->b_dev));
+    return -1;
+  }
+  
+  hash=md_hash_table[MINOR(bh->b_dev)]+(bh->b_blocknr/md_smallest_dev[MINOR(bh->b_dev)]->size);
+
+  if (bh->b_blocknr >= (hash->dev0->size + hash->dev0->offset))
+  {
+    if (!hash->dev1)
+      printk ("md_bh_to_real : hash->dev1==NULL for block %ld\n", bh->b_blocknr);
+    tmp_dev=hash->dev1;
+  }
+  else
+    tmp_dev=hash->dev0;
+
+  if (dev)
+    *dev=tmp_dev->dev;
+
+  if (!sector)
+    return 0;
+  
+  if (bh->b_blocknr >= (tmp_dev->size + tmp_dev->offset) || bh->b_blocknr < tmp_dev->offset)
+    printk ("Block %ld out of bounds on dev %04x size %d offset %d\n", bh->b_blocknr, tmp_dev->dev, tmp_dev->size, tmp_dev->offset);
+
+  *sector=(bh->b_blocknr-(tmp_dev->offset))*(bh->b_size>>9);
+
+  return 0;
+}
+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	Thu Feb 16 22:31: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 {
