--- linux-2.3.40.orig/CREDITS	Tue Jan 25 03:38:20 2000
+++ linux-2.3.40/CREDITS	Tue Jan 25 03:38:20 2000
@@ -1423,6 +1423,13 @@
 D: XF86_8514
 D: cfdisk (curses based disk partitioning program)
 
+N: Heinz Mauelshagen
+E: mge@EZ-Darmstadt.Telekom.de
+D: Logical Volume Manager
+S: Bartningstr. 12
+S: 64289 Darmstadt
+S: Germany
+
 N: Mike McLagan
 E: mike.mclagan@linux.org
 W: http://www.invlogic.com/~mmclagan
--- linux-2.3.40.orig/MAINTAINERS	Tue Jan 25 03:38:20 2000
+++ linux-2.3.40/MAINTAINERS	Tue Jan 25 03:38:20 2000
@@ -543,6 +543,13 @@
 W:	http://people.redhat.com/zab/maestro/
 S:	Supported
 
+LOGICAL VOLUME MANAGER
+P:	Heinz Mauelshagen
+M:	linux-LVM@EZ-Darmstadt.Telekom.de
+L:	linux-LVM@msede.com
+W:	http://linux.msede.com/lvm
+S:	Maintained
+
 M68K
 P:	Jes Sorensen
 M:	Jes.Sorensen@cern.ch
--- linux-2.3.40.orig/Documentation/Configure.help	Tue Jan 25 03:38:20 2000
+++ linux-2.3.40/Documentation/Configure.help	Tue Jan 25 03:38:20 2000
@@ -1253,6 +1253,30 @@
   called on26.o. You must also have a high-level driver for the type
   of device that you want to support.
 
+Logical Volume Manager (LVM) support
+CONFIG_BLK_DEV_LVM
+  This driver lets you combine several hard disks, hard disk partitions,
+  multiple devices or even loop devices (for evaluation purposes) into
+  a volume group. Imagine a volume group as a kind of virtual disk.
+  Logical volumes, which can be thought of as virtual partitions,
+  can be created in the volume group.  You can resize volume groups and
+  logical volumes after creation time, corresponding to new capacity needs.
+  Logical volumes are accessed as block devices named
+  /dev/VolumeGroupName/LogicalVolumeName.
+
+  For details see /usr/src/linux/Documentaion/LVM-HOWTO.
+
+  To get the newest software see <http://linux.msede.com/lvm>.
+
+Logical Volume Manager proc filesystem information
+CONFIG_LVM_PROC_FS
+  If you say Y here, you are able to access overall Logical Volume Manager,
+  Volume Group, Logical and Physical Volume information in /proc/lvm.
+  
+  To use this option, you have to check, that the "proc filesystem support"
+  (CONFIG_PROC_FS) is enabled too.
+
+
 Multiple devices driver support
 CONFIG_BLK_DEV_MD
   This driver lets you combine several hard disk partitions into one
@@ -10828,20 +10852,6 @@
   You can say M here to compile this driver as a module; the module is
   called sb.o.
 
-Generic OPL2/OPL3 FM synthesizer support
-CONFIG_SOUND_ADLIB
-  Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4).
-  Answering Y is usually a safe and recommended choice, however some
-  cards may have software (TSR) FM emulation. Enabling FM support with
-  these cards may cause trouble (I don't currently know of any such
-  cards, however). 
-
-  Please read the file Documentation/sound/OPL3 if your card has an
-  OPL3 chip.
-
-  If unsure, say Y.
-  
-
 #Loopback MIDI device support
 #CONFIG_SOUND_VMIDI
 ###
@@ -11117,8 +11127,16 @@
 
 FM synthesizer (YM3812/OPL-3) support
 CONFIG_SOUND_YM3812
-  Answer Y here, unless you know you will not need the option.
+  Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4).
+  Answering Y is usually a safe and recommended choice, however some
+  cards may have software (TSR) FM emulation. Enabling FM support with
+  these cards may cause trouble (I don't currently know of any such
+  cards, however).
+  Please read the file Documentation/sound/OPL3 if your card has an
+  OPL3 chip.
 
+  If unsure, say Y.
+ 
 Sun Audio support
 CONFIG_SUN_AUDIO
   This is support for the sound cards on Sun workstations. The code
--- linux-2.3.40.orig/Documentation/LVM-HOWTO	Tue Jan 25 03:38:20 2000
+++ linux-2.3.40/Documentation/LVM-HOWTO	Tue Jan 25 03:38:20 2000
@@ -0,0 +1,118 @@
+Heinz Mauelshagen's LVM (Logical Volume Manager) howto.             01/28/1999
+
+
+Abstract:
+---------
+The LVM adds a kind of virtual disks and virtual partitions functionality
+to the Linux operating system
+
+It achieves this by adding an additional layer between the physical peripherals
+and the i/o interface in the kernel.
+
+This allows the concatenation of several disk partitions or total disks
+(so-called physical volumes or PVs) or even multiple devices
+to form a storage pool (so-called Volume Group or VG) with
+allocation units called physical extents (called PE).
+You can think of the volume group as a virtual disk.
+Please see scenario below.
+
+Some or all PEs of this VG then can be allocated to so-called Logical Volumes
+or LVs in units called logical extents or LEs.
+Each LE is mapped to a corresponding PE.
+LEs and PEs are equal in size.
+Logical volumes are a kind of virtual partitions.
+
+
+The LVs can be used through device special files similar to the known
+/dev/sd[a-z]* or /dev/hd[a-z]* named /dev/VolumeGroupName/LogicalVolumeName.
+
+But going beyond this, you are able to extend or reduce
+VGs _AND_ LVs at runtime!
+
+So...
+If for example the capacity of a LV gets too small and your VG containing
+this LV is full, you could add another PV to that VG and simply extend
+the LV afterwards.
+If you reduce or delete a LV you can use the freed capacity for different
+LVs in the same VG.
+
+
+The above scenario looks like this:
+
+     /------------------------------------------\
+     |  /--PV2---\      VG 1      /--PVn---\    |
+     |  |-VGDA---|                |-VGDA-- |    |
+     |  |PE1PE2..|                |PE1PE2..|    |
+     |  |        |     ......     |        |    |
+     |  |        |                |        |    |
+     |  |    /-----------------------\     |    |
+     |  |    \-------LV 1------------/     |    |
+     |  |   ..PEn|                |   ..PEn|    |
+     |  \--------/                \--------/    |
+     \------------------------------------------/
+
+PV 1 could be /dev/sdc1 sized 3GB
+PV n could be /dev/sde1 sized 4GB
+VG 1 could be test_vg
+LV 1 could be /dev/test_vg/test_lv
+VGDA is the volume group descriptor area holding the LVM metadata
+PE1 up to PEn is the number of physical extents on each disk(partition)
+
+
+
+Installation steps see INSTALL and insmod(1)/modprobe(1), kmod/kerneld(8)
+to load the logical volume manager module if you did not bind it
+into the kernel.
+
+
+Configuration steps for getting the above scenario:
+
+1. Set the partition system id to 0xFE on /dev/sdc1 and /dev/sde1.
+
+2. do a "pvcreate /dev/sd[ce]1"
+   For testing purposes you can use more than one partition on a disk.
+   You should not use more than one partition because in the case of
+   a striped LV you'll have a performance breakdown.
+
+3. do a "vgcreate test_vg /dev/sd[ce]1" to create the new VG named "test_vg"
+   which has the total capacity of both partitions.
+   vgcreate activates (transfers the metadata into the LVM driver in the kernel)
+   the new volume group too to be able to create LVs in the next step.
+
+4. do a "lvcreate -L1500 -ntest_lv test_vg" to get a 1500MB linear LV named
+   "test_lv" and it's block device special "/dev/test_vg/test_lv".
+
+   Or do a "lvcreate -i2 -I4 -l1500 -nanother_test_lv test_vg" to get a 100 LE
+   large logical volume with 2 stripes and stripesize 4 KB.
+
+5. For example generate a filesystem in one LV with
+   "mke2fs /dev/test_vg/test_lv" and mount it.
+
+6. extend /dev/test_vg/test_lv to 1600MB with relative size by
+   "lvextend -L+100 /dev/test_vg/test_lv"
+   or with absolute size by
+   "lvextend -L1600 /dev/test_vg/test_lv"
+ 
+7. reduce /dev/test_vg/test_lv to 900 logical extents with relative extents by
+   "lvreduce -l-700 /dev/test_vg/test_lv"
+   or with absolute extents by
+   "lvreduce -l900 /dev/test_vg/test_lv"
+ 
+9. rename a VG by deactivating it with
+   "vgchange -an test_vg"   # only VGs with _no_ open LVs can be deactivated!
+   "vgrename test_vg whatever"
+   and reactivate it again by
+   "vgchange -ay whatever"
+
+9. rename a LV after closing it by
+   "lvchange -an /dev/whatever/test_lv" # only closed LVs can be deactivated
+   "lvrename  /dev/whatever/test_lv  /dev/whatever/whatvolume"
+   or by
+   "lvrename  whatever test_lv whatvolume"
+   and reactivate it again by
+   "lvchange -ay /dev/whatever/whatvolume"
+
+10. if you own Ted Tso's resize2fs program, you are able to resize the
+    ext2 type filesystems contained in logical volumes without destroyiing
+    the data by
+    "e2fsadm -L+100 /dev/test_vg/another_test_lv"
--- linux-2.3.40.orig/drivers/block/Config.in	Tue Jan 25 03:38:20 2000
+++ linux-2.3.40/drivers/block/Config.in	Tue Jan 25 03:38:20 2000
@@ -195,6 +195,10 @@
 
 comment 'Additional Block Devices'
 
+tristate 'Logical volume manager (LVM) support' CONFIG_BLK_DEV_LVM N
+if [ "$CONFIG_BLK_DEV_LVM" != "n" ]; then
+  bool '   LVM information in proc filesystem' CONFIG_LVM_PROC_FS Y
+fi
 tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP
 if [ "$CONFIG_NET" = "y" ]; then
    tristate 'Network block device support' CONFIG_BLK_DEV_NBD
--- linux-2.3.40.orig/drivers/block/Makefile	Tue Jan 25 03:38:20 2000
+++ linux-2.3.40/drivers/block/Makefile	Tue Jan 25 03:38:20 2000
@@ -322,6 +322,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_BLK_DEV_LVM),y)
+L_OBJS += lvm.o
+else
+   ifeq ($(CONFIG_BLK_DEV_LVM),m)
+   M_OBJS += lvm.o
+   endif
+endif
+
 ifeq ($(CONFIG_BLK_DEV_MD),y)
 LX_OBJS += md.o
 
--- linux-2.3.40.orig/drivers/block/README.lvm	Tue Jan 25 03:38:20 2000
+++ linux-2.3.40/drivers/block/README.lvm	Tue Jan 25 03:38:20 2000
@@ -0,0 +1,8 @@
+
+This is the Logical Volume Manager driver for Linux,
+
+Tools, library that manage logical volumes can be found
+at <http://linux.msede.com/lvm>.
+
+There you can obtain actual driver versions too.
+
--- linux-2.3.40.orig/drivers/block/ll_rw_blk.c	Tue Jan 25 03:38:20 2000
+++ linux-2.3.40/drivers/block/ll_rw_blk.c	Tue Jan 25 03:38:20 2000
@@ -3,6 +3,8 @@
  *
  * Copyright (C) 1991, 1992 Linus Torvalds
  * Copyright (C) 1994,      Karl Keyte: Added support for disk statistics
+ * Copyright (C) 1999,      Heinz Mauelshagen: fixed queue handling with
+ *                                             LVM/MD in ll_rw_block()
  */
 
 /*
@@ -18,6 +20,7 @@
 #include <linux/mm.h>
 #include <linux/init.h>
 #include <linux/smp_lock.h>
+#include <linux/lvm.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -26,6 +29,13 @@
 
 #include <linux/module.h>
 
+/* function pointer to the LVM driver remapping function
+   which will be setup during driver/module init; neccessary
+   to be able to load LVM as a module */
+
+int ( *lvm_map_ptr) ( int, kdev_t *, unsigned long *,
+                         unsigned long, int) = NULL;
+
 /*
  * MAC Floppy IWM hooks
  */
@@ -34,6 +44,10 @@
 extern int mac_floppy_init(void);
 #endif
 
+#ifdef CONFIG_STRAM_SWAP
+extern int stram_device_init( void );
+#endif
+
 /*
  * The request-struct contains all necessary data
  * to load a nr of sectors into memory
@@ -582,6 +596,7 @@
 		/* MD and loop can't handle plugging without deadlocking */
 		if (major != MD_MAJOR && major != LOOP_MAJOR && 
 		    major != DDV_MAJOR && major != NBD_MAJOR
+                    && major != LVM_BLK_MAJOR
 		    && q->use_plug)
 			plug_device(q); /* is atomic */
 	} else switch (major) {
@@ -803,8 +818,9 @@
 	major = MAJOR(bh[0]->b_dev);
 	if (!(q = get_queue(bh[0]->b_dev))) {
 		printk(KERN_ERR
-	"ll_rw_block: Trying to read nonexistent block-device %s (%ld)\n",
-		kdevname(bh[0]->b_dev), bh[0]->b_blocknr);
+	               "ll_rw_block: Trying to access nonexistent "
+			"block-device %s (%ld)\n",
+			kdevname(bh[0]->b_dev), bh[0]->b_blocknr);
 		goto sorry;
 	}
 
@@ -826,12 +842,34 @@
 			goto sorry;
 		}
 
-		/* Md remaps blocks now */
+		/* LVM and MD remap blocks now */
 		bh[i]->b_rdev = bh[i]->b_dev;
 		bh[i]->b_rsector=bh[i]->b_blocknr*(bh[i]->b_size >> 9);
+#if defined CONFIG_BLK_DEV_LVM || defined CONFIG_BLK_DEV_LVM_MODULE
+                major = MAJOR(bh[i]->b_dev);
+                if ( major == LVM_BLK_MAJOR) {
+                   if ( lvm_map_ptr == NULL) {
+                              printk ( KERN_ERR
+                               "Bad lvm_map_ptr in ll_rw_block\n");
+                      goto sorry;
+                   }
+                   if ( ( lvm_map_ptr) ( MINOR ( bh[i]->b_dev),
+                                         &bh[i]->b_rdev,
+                                         &bh[i]->b_rsector,
+                                         bh[i]->b_size >> 9,
+                                         rw) != 0) {
+		      printk ( KERN_ERR
+                               "Bad lvm_map in ll_rw_block\n");
+                      goto sorry;
+                   }
+                   /* remap major too ... */
+                   major = MAJOR(bh[i]->b_rdev);
+               }
+#endif
 #ifdef CONFIG_BLK_DEV_MD
 		if (major==MD_MAJOR &&
-		    md_map (MINOR(bh[i]->b_dev), &bh[i]->b_rdev,
+                    /* changed             v   to allow LVM to remap */
+                    md_map (MINOR(bh[i]->b_rdev), &bh[i]->b_rdev,
 			    &bh[i]->b_rsector, bh[i]->b_size >> 9)) {
 		        printk (KERN_ERR
 				"Bad md_map in ll_rw_block\n");
@@ -849,11 +887,21 @@
 	for (i = 0; i < nr; i++) {
 		set_bit(BH_Req, &bh[i]->b_state);
 #ifdef CONFIG_BLK_DEV_MD
-		if (MAJOR(bh[i]->b_dev) == MD_MAJOR) {
-			md_make_request(MINOR (bh[i]->b_dev), rw, bh[i]);
+		if (MAJOR(bh[i]->b_rdev) == MD_MAJOR) {
+                        /* remap device for MD too      v */
+			md_make_request(MINOR (bh[i]->b_rdev), rw, bh[i]);
 			continue;
 		}
 #endif
+        	/* get correct device request queue after
+                   LVM and/or MD have remapped this buffer */
+		if (!(q = get_queue(bh[i]->b_rdev))) {
+			printk(KERN_ERR
+			"ll_rw_block: Trying to access nonexistent "
+	       		"block-device %s (%ld)\n",
+			kdevname(bh[0]->b_rdev), bh[0]->b_rsector>>1);
+			goto sorry;
+		}
 		__make_request(q, MAJOR(bh[i]->b_rdev), rw, bh[i]);
 	}
 
@@ -874,9 +922,6 @@
 	return;
 }
 
-#ifdef CONFIG_STRAM_SWAP
-extern int stram_device_init( void );
-#endif
 
 /*
  * First step of what used to be end_request
@@ -1034,6 +1079,9 @@
 #ifdef CONFIG_SJCD
 	sjcd_init();
 #endif CONFIG_SJCD
+#ifdef CONFIG_BLK_DEV_LVM
+	lvm_init();
+#endif
 #ifdef CONFIG_BLK_DEV_MD
 	md_init();
 #endif CONFIG_BLK_DEV_MD
--- linux-2.3.40.orig/drivers/block/lvm.c	Tue Jan 25 03:38:20 2000
+++ linux-2.3.40/drivers/block/lvm.c	Tue Jan 25 03:38:20 2000
@@ -0,0 +1,2536 @@
+/*
+ * kernel/lvm.c
+ *
+ * Copyright (C) 1997 - 2000  Heinz Mauelshagen, Germany
+ *
+ * February-November 1997
+ * April-May,July-August,November 1998
+ * January-March,May,July,September,October 1999
+ * January 2000
+ *
+ *
+ * LVM driver 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.
+ * 
+ * LVM driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA. 
+ *
+ */
+
+/*
+ * Changelog
+ *
+ *    09/11/1997 - added chr ioctls VG_STATUS_GET_COUNT
+ *                 and VG_STATUS_GET_NAMELIST
+ *    18/01/1998 - change lvm_chr_open/close lock handling
+ *    30/04/1998 - changed LV_STATUS ioctl to LV_STATUS_BYNAME and
+ *               - added   LV_STATUS_BYINDEX ioctl
+ *               - used lvm_status_byname_req_t and
+ *                      lvm_status_byindex_req_t vars
+ *    04/05/1998 - added multiple device support
+ *    08/05/1998 - added support to set/clear extendable flag in volume group
+ *    09/05/1998 - changed output of lvm_proc_get_info() because of
+ *                 support for free (eg. longer) logical volume names
+ *    12/05/1998 - added spin_locks (thanks to Pascal van Dam
+ *                 <pascal@ramoth.xs4all.nl>)
+ *    25/05/1998 - fixed handling of locked PEs in lvm_map() and lvm_chr_ioctl()
+ *    26/05/1998 - reactivated verify_area by access_ok
+ *    07/06/1998 - used vmalloc/vfree instead of kmalloc/kfree to go
+ *                 beyond 128/256 KB max allocation limit per call
+ *               - #ifdef blocked spin_lock calls to avoid compile errors
+ *                 with 2.0.x
+ *    11/06/1998 - another enhancement to spinlock code in lvm_chr_open()
+ *                 and use of LVM_VERSION_CODE instead of my own macros
+ *                 (thanks to  Michael Marxmeier <mike@msede.com>)
+ *    07/07/1998 - added statistics in lvm_map()
+ *    08/07/1998 - saved statistics in do_lv_extend_reduce()
+ *    25/07/1998 - used __initfunc macro
+ *    02/08/1998 - changes for official char/block major numbers
+ *    07/08/1998 - avoided init_module() and cleanup_module() to be static
+ *    30/08/1998 - changed VG lv_open counter from sum of LV lv_open counters
+ *                 to sum of LVs open (no matter how often each is)
+ *    01/09/1998 - fixed lvm_gendisk.part[] index error
+ *    07/09/1998 - added copying of lv_current_pe-array
+ *                 in LV_STATUS_BYINDEX ioctl
+ *    17/11/1998 - added KERN_* levels to printk
+ *    13/01/1999 - fixed LV index bug in do_lv_create() which hit lvrename
+ *    07/02/1999 - fixed spinlock handling bug in case of LVM_RESET
+ *                 by moving spinlock code from lvm_chr_open()
+ *                 to lvm_chr_ioctl()
+ *               - added LVM_LOCK_LVM ioctl to lvm_chr_ioctl()
+ *               - allowed LVM_RESET and retrieval commands to go ahead;
+ *                 only other update ioctls are blocked now
+ *               - fixed pv->pe to NULL for pv_status
+ *               - using lv_req structure in lvm_chr_ioctl() now
+ *               - fixed NULL ptr reference bug in do_lv_extend_reduce()
+ *                 caused by uncontiguous PV array in lvm_chr_ioctl(VG_REDUCE)
+ *    09/02/1999 - changed BLKRASET and BLKRAGET in lvm_char_ioctl() to
+ *                 handle lgoical volume private read ahead sector
+ *               - implemented LV read_ahead handling with lvm_blk_read()
+ *                 and lvm_blk_write()
+ *    10/02/1999 - implemented 2.[12].* support function lvm_hd_name()
+ *                 to be used in drivers/block/genhd.c by disk_name()
+ *    12/02/1999 - fixed index bug in lvm_blk_ioctl(), HDIO_GETGEO
+ *               - enhanced gendisk insert/remove handling
+ *    16/02/1999 - changed to dynamic block minor number allocation to
+ *                 have as much as 99 volume groups with 256 logical volumes
+ *                 as the grand total; this allows having 1 volume group with
+ *                 up to 256 logical volumes in it
+ *    21/02/1999 - added LV open count information to proc filesystem
+ *               - substituted redundant LVM_RESET code by calls
+ *                 to do_vg_remove()
+ *    22/02/1999 - used schedule_timeout() to be more responsive
+ *                 in case of do_vg_remove() with lots of logical volumes
+ *    19/03/1999 - fixed NULL pointer bug in module_init/lvm_init
+ *    17/05/1999 - used DECLARE_WAIT_QUEUE_HEAD macro (>2.3.0)
+ *               - enhanced lvm_hd_name support
+ *    03/07/1999 - avoided use of KERNEL_VERSION macro based ifdefs and
+ *                 memcpy_tofs/memcpy_fromfs macro redefinitions
+ *    06/07/1999 - corrected reads/writes statistic counter copy in case
+ *                 of striped logical volume
+ *    28/07/1999 - implemented snapshot logical volumes
+ *                 - lvm_chr_ioctl
+ *                   - LV_STATUS_BYINDEX
+ *                   - LV_STATUS_BYNAME
+ *                 - do_lv_create
+ *                 - do_lv_remove
+ *                 - lvm_map
+ *                 - new lvm_snapshot_remap_block
+ *                 - new lvm_snapshot_remap_new_block
+ *    18/12/1999 - ported to 2.3.33
+ *    24/01/2000 - ported to 2.3.40 and added Alan Cox's changes
+ *                 (intense pointer use and task state fix...)
+ *               - fixed wronge rdev_tmp during PE_LOCK
+ *
+ */
+
+/*
+ * TODO
+ *
+ *   - implement special handling of unavailable physical volumes
+ *
+ */
+
+static char *lvm_version = "LVM version 0.8i (Stock)  by Heinz Mauelshagen  (02/10/1999)\n";
+static char *lvm_short_version = "version 0.8i (Stock)  (02/10/1999)";
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+
+#include <linux/hdreg.h>
+#include <linux/stat.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/blkdev.h>
+#include <linux/genhd.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+
+#include <asm/ioctl.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+
+#include <linux/errno.h>
+#include <linux/lvm.h>
+
+#define	LVM_CORRECT_READ_AHEAD( a) \
+   if      ( a < LVM_MIN_READ_AHEAD || \
+             a > LVM_MAX_READ_AHEAD) a = LVM_MAX_READ_AHEAD;
+
+
+/*
+ * External function prototypes
+ */
+#ifdef MODULE
+int init_module(void);
+void cleanup_module(void);
+#else
+extern int lvm_init(void);
+#endif
+
+static void lvm_dummy_device_request(request_queue_t*);
+static int lvm_blk_ioctl(struct inode *, struct file *, unsigned int,
+			 unsigned long);
+static int lvm_blk_open(struct inode *, struct file *);
+
+static ssize_t lvm_blk_read(struct file *, char *, size_t, loff_t *);
+static ssize_t lvm_blk_write(struct file *, const char *, size_t, loff_t *);
+
+static int lvm_chr_open(struct inode *, struct file *);
+
+static int lvm_chr_close(struct inode *, struct file *);
+static int lvm_blk_close(struct inode *, struct file *);
+
+static int lvm_chr_ioctl(struct inode *, struct file *, unsigned int,
+			 unsigned long);
+
+#if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS
+static int lvm_proc_get_info ( char *, char **, off_t, int);
+static int (*lvm_proc_get_info_ptr)(char *, char **, off_t, int) =
+   &lvm_proc_get_info;
+#endif
+
+void lvm_hd_name(char *, int);
+/* End external function prototypes */
+
+
+/*
+ * Internal function prototypes
+ */
+static void lvm_init_vars(void);
+extern int (*lvm_map_ptr) (int, kdev_t *, unsigned long *,
+			   unsigned long, int);
+static int lvm_snapshot_remap_block(ulong *, kdev_t *, int);
+static int lvm_snapshot_remap_new_block(ulong *, kdev_t *, int);
+
+extern void (*lvm_hd_name_ptr) (char *, int);
+static int lvm_map(int, kdev_t *, unsigned long *, unsigned long, int);
+static int do_vg_create(int, void *);
+static int do_vg_remove(int);
+static int do_lv_create(int, char *, lv_t *);
+static int do_lv_remove(int, char *, int);
+static int do_lv_extend_reduce(int, char *, lv_t *);
+static void lvm_geninit(struct gendisk *);
+static struct inode *lvm_get_inode(int);
+inline int lvm_strlen(char *);
+inline void lvm_memcpy(char *, char *, int);
+inline int lvm_strcmp(char *, char *);
+inline char *lvm_strrchr(char *, char c);
+/* END Internal function prototypes */
+
+
+/* volume group descriptor area pointers */
+static vg_t *vg[ABS_MAX_VG];
+static pv_t *pvp = NULL;
+static lv_t *lvp = NULL;
+static pe_t *pep = NULL;
+static pe_t *pep1 = NULL;
+
+
+/* map from block minor number to VG and LV numbers */
+typedef struct {
+	int vg_number;
+	int lv_number;
+} vg_lv_map_t;
+static vg_lv_map_t vg_lv_map[ABS_MAX_LV];
+
+
+/* Request structures (lvm_chr_ioctl()) */
+static pv_change_req_t pv_change_req;
+static pv_flush_req_t pv_flush_req;
+static pv_status_req_t pv_status_req;
+static pe_lock_req_t pe_lock_req;
+static le_remap_req_t le_remap_req;
+static lv_req_t lv_req;
+
+static char pv_name[NAME_LEN];
+/* static char rootvg[NAME_LEN] = { 0, }; */
+static uint lv_open = 0;
+static const char *const lvm_name = LVM_NAME;
+static int lock = 0;
+static int loadtime = 0;
+static uint vg_count = 0;
+static long lvm_chr_open_count = 0;
+static ushort lvm_iop_version = LVM_DRIVER_IOP_VERSION;
+static DECLARE_WAIT_QUEUE_HEAD(lvm_wait);
+static DECLARE_WAIT_QUEUE_HEAD(lvm_map_wait);
+
+static spinlock_t lvm_lock = SPIN_LOCK_UNLOCKED;
+
+
+static struct file_operations lvm_chr_fops =
+{
+	open:		lvm_chr_open,
+	release:	lvm_chr_close,
+	ioctl:		lvm_chr_ioctl,
+};
+
+/*
+ *	Hopefully we can get rid of this as the block device stuff tidies up
+ *	and the LVM tidies up.
+ */
+ 
+static struct file_operations lvm_blk_fops =
+{
+	open:		lvm_blk_open,
+	release:	blkdev_close,
+	read:		lvm_blk_read,
+	write:		lvm_blk_write,
+	ioctl:		lvm_blk_ioctl,
+	fsync:		block_fsync,
+};
+
+/*
+ *	Block device operations. Nice idea but because they are chained off
+ *	file operations adds a whole layer of indirection, and missing
+ *	exports and stuff. Should now be sorted.
+ */
+ 
+struct block_device_operations lvm_blk_bdops = 
+{
+	open:		lvm_blk_open,
+	release:	lvm_blk_close,
+	ioctl:		lvm_blk_ioctl,
+};
+
+/* gendisk structures */
+static struct hd_struct lvm_hd_struct[MAX_LV];
+static int lvm_blocksizes[MAX_LV] = {0,};
+static int lvm_size[MAX_LV] = {0,};
+static struct gendisk lvm_gendisk =
+{
+	LVM_BLOCK_MAJOR,	/* major # */
+	LVM_NAME,		/* name of major */
+	0,			/* number of times minor is shifted
+				   to get real minor */
+	1,			/* maximum partitions per device */
+	lvm_hd_struct,		/* partition table */
+	lvm_size,		/* device size in blocks, copied
+				   to block_size[] */
+	MAX_LV,			/* number or real devices */
+};
+
+
+#ifdef MODULE
+/*
+ * Module initialization...
+ */
+int init_module(void)
+#else
+/*
+ * Driver initialization...
+ */
+int __init lvm_init(void)
+#endif				/* #ifdef MODULE */
+{
+	struct gendisk *gendisk_ptr = NULL;
+
+	lvm_init_vars();
+
+	if (register_chrdev(LVM_CHAR_MAJOR, lvm_name, &lvm_chr_fops) < 0) {
+		printk(KERN_ERR "%s -- register_chrdev failed\n", lvm_name);
+		return -EIO;
+	}
+	if (register_blkdev(LVM_BLOCK_MAJOR, lvm_name, &lvm_blk_bdops) < 0) {
+		printk("%s -- register_blkdev failed\n", lvm_name);
+		if (unregister_chrdev(LVM_CHAR_MAJOR, lvm_name) < 0)
+			printk(KERN_ERR "%s -- unregister_chrdev failed\n", lvm_name);
+		return -EIO;
+	}
+	create_proc_info_entry(LVM_NAME, S_IFREG | S_IRUGO,
+				&proc_root, lvm_proc_get_info_ptr);
+
+#ifdef DEBUG
+	printk(KERN_INFO "%s -- registered\n", lvm_name);
+#endif
+
+	 blk_init_queue(BLK_DEFAULT_QUEUE(LVM_BLOCK_MAJOR),
+			lvm_dummy_device_request);
+
+	/* insert our gendisk at the corresponding major */
+	lvm_geninit(&lvm_gendisk);
+	if (gendisk_head != NULL) {
+		gendisk_ptr = gendisk_head;
+		while (gendisk_ptr->next != NULL &&
+		       gendisk_ptr->major > lvm_gendisk.major) {
+			gendisk_ptr = gendisk_ptr->next;
+		}
+		lvm_gendisk.next = gendisk_ptr->next;
+		gendisk_ptr->next = &lvm_gendisk;
+	} else {
+		gendisk_head = &lvm_gendisk;
+		lvm_gendisk.next = NULL;
+	}
+
+	/* reference from drivers/block/ll_rw_blk.c */
+	lvm_map_ptr = lvm_map;
+
+	/* reference from drivers/block/genhd.c */
+	lvm_hd_name_ptr = lvm_hd_name;
+
+	/* optional read root VGDA */
+/*
+	if ( *rootvg != 0) {
+		vg_read_with_pv_and_lv ( rootvg, &vg);
+	}
+*/
+
+	printk(KERN_INFO
+	       "%s%s -- "
+#ifdef MODULE
+	       "Module"
+#else
+	       "Driver"
+#endif
+	       " successfully initialized\n",
+	       lvm_version, lvm_name);
+
+	return 0;
+}				/* init_module () / lvm_init () */
+
+
+#ifdef MODULE
+/*
+ * Module cleanup...
+ */
+void cleanup_module(void)
+{
+	struct gendisk *gendisk_ptr = NULL, *gendisk_ptr_prev = NULL;
+
+	gendisk_ptr = gendisk_ptr_prev = gendisk_head;
+	while (gendisk_ptr != NULL) {
+		if (gendisk_ptr == &lvm_gendisk)
+			break;
+		gendisk_ptr_prev = gendisk_ptr;
+		gendisk_ptr = gendisk_ptr->next;
+	}
+	/* delete our gendisk from chain */
+	if (gendisk_ptr == &lvm_gendisk)
+		gendisk_ptr_prev->next = gendisk_ptr->next;
+
+	blk_size[LVM_BLOCK_MAJOR] = NULL;
+	blksize_size[LVM_BLOCK_MAJOR] = NULL;
+
+	remove_proc_entry(LVM_NAME, &proc_root);
+
+	if (unregister_chrdev(LVM_CHAR_MAJOR, lvm_name) < 0) {
+		printk(KERN_ERR "%s -- unregister_chrdev failed\n", lvm_name);
+	}
+	if (unregister_blkdev(LVM_BLOCK_MAJOR, lvm_name) < 0) {
+		printk(KERN_ERR "%s -- unregister_blkdev failed\n", lvm_name);
+	}
+	/* reference from linux/drivers/block/ll_rw_blk.c */
+	lvm_map_ptr = NULL;
+
+	/* reference from linux/drivers/block/genhd.c */
+	lvm_hd_name_ptr = NULL;
+
+	blk_cleanup_queue (BLK_DEFAULT_QUEUE(LVM_BLOCK_MAJOR));
+
+	printk(KERN_INFO "%s -- Module successfully deactivated\n", lvm_name);
+
+	return;
+}				/* void cleanup_module () */
+#endif				/* #ifdef MODULE */
+
+
+/*
+ * support function to initialize lvm variables
+ */
+void __init lvm_init_vars(void)
+{
+	int v;
+
+	loadtime = CURRENT_TIME;
+
+	pe_lock_req.lock = UNLOCK_PE;
+	pe_lock_req.data.lv_dev = \
+	    pe_lock_req.data.pv_dev = \
+	    pe_lock_req.data.pv_offset = 0;
+
+	/* Initialize VG pointers */
+	for (v = 0; v < ABS_MAX_VG; v++)
+		vg[v] = NULL;
+
+	/* Initialize LV -> VG association */
+	for (v = 0; v < ABS_MAX_LV; v++) {
+		/* index ABS_MAX_VG never used for real VG */
+		vg_lv_map[v].vg_number = ABS_MAX_VG;
+		vg_lv_map[v].lv_number = -1;
+	}
+
+	return;
+}				/* lvm_init_vars () */
+
+
+/********************************************************************
+ *
+ * Character device functions
+ *
+ ********************************************************************/
+
+/*
+ * character device open routine
+ */
+static int lvm_chr_open(struct inode *inode,
+			struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+
+#ifdef DEBUG
+	printk(KERN_DEBUG
+	 "%s -- lvm_chr_open MINOR: %d  VG#: %d  mode: 0x%X  lock: %d\n",
+	       lvm_name, minor, VG_CHR(minor), file->f_mode, lock);
+#endif
+
+	/* super user validation */
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	/* Group special file open */
+	if (VG_CHR(minor) > MAX_VG)
+		return -ENXIO;
+
+	MOD_INC_USE_COUNT;
+
+	lvm_chr_open_count++;
+	return 0;
+}				/* lvm_chr_open () */
+
+
+/*
+ * character device i/o-control routine
+ *
+ * Only one changing process can do ioctl at one time, others will block.
+ *
+ */
+
+static int lvm_chr_ioctl(struct inode *inode, struct file *file,
+			 uint command, ulong a)
+{
+	int minor = MINOR(inode->i_rdev);
+	int extendable;
+	ulong l, le, p, v;
+	ulong size;
+	void *arg = (void *) a;
+	vg_t *vp;
+	struct inode *inode_sav;
+	lv_status_byname_req_t lv_status_byname_req;
+	lv_status_byindex_req_t lv_status_byindex_req;
+	lv_t lv;
+
+	/* otherwise cc will complain about unused variables */
+	(void) lvm_lock;
+
+#ifdef DEBUG_IOCTL
+	printk(KERN_DEBUG
+	       "%s -- lvm_chr_ioctl: command: 0x%X  MINOR: %d  "
+	       "VG#: %d  mode: 0x%X\n",
+	       lvm_name, command, minor, VG_CHR(minor), file->f_mode);
+#endif
+	vp = vg[VG_CHR(minor)];
+
+	switch (command) {
+	case LVM_LOCK_LVM:
+		/* lock the LVM */
+lock_try_again:
+		spin_lock(&lvm_lock);
+		if (lock != 0 && lock != current->pid) {
+#ifdef DEBUG_IOCTL
+			printk(KERN_INFO "lvm_chr_ioctl: %s is locked by pid %d ...\n",
+			       lvm_name, lock);
+#endif
+			spin_unlock(&lvm_lock);
+			interruptible_sleep_on(&lvm_wait);
+			if (signal_pending(current))
+				return -EINTR;
+			goto lock_try_again;
+		}
+		lock = current->pid;
+		spin_unlock(&lvm_lock);
+		return 0;
+
+	case LVM_GET_IOP_VERSION:
+		/* check lvm version to ensure driver/tools+lib interoperability */
+		if (copy_to_user(arg, &lvm_iop_version, sizeof(ushort)) != 0)
+			return -EFAULT;
+		return 0;
+
+	case PE_LOCK_UNLOCK:
+		/* lock/unlock i/o to a physical extent to move it to another
+		   physical volume (move's done in user space's pvmove) */
+		if (vp == NULL)
+			return -ENXIO;
+		if (copy_from_user(&pe_lock_req, arg, sizeof(pe_lock_req_t)) != 0)
+			return -EFAULT;
+
+		switch (pe_lock_req.lock) {
+		case LOCK_PE:
+			for (p = 0; p < vp->pv_max; p++) {
+				if (vp->pv[p] != NULL &&
+				    pe_lock_req.data.pv_dev == vp->pv[p]->pv_dev)
+					break;
+			}
+
+			if (p == vp->pv_max)
+				return -ENXIO;
+
+			pe_lock_req.lock = UNLOCK_PE;
+			fsync_dev(pe_lock_req.data.lv_dev);
+			pe_lock_req.lock = LOCK_PE;
+			break;
+
+		case UNLOCK_PE:
+			pe_lock_req.lock = UNLOCK_PE;
+			pe_lock_req.data.lv_dev = \
+			    pe_lock_req.data.pv_dev = \
+			    pe_lock_req.data.pv_offset = 0;
+			wake_up(&lvm_map_wait);
+			break;
+
+		default:
+			return -EINVAL;
+		}
+
+		return 0;
+
+	case LE_REMAP:
+		/* remap a logical extent (after moving the physical extent) */
+		if (vp == NULL)
+			return -ENXIO;
+		if (copy_from_user(&le_remap_req, arg,
+				   sizeof(le_remap_req_t)) != 0)
+			return -EFAULT;
+
+		for (l = 0; l < vp->lv_max; l++) {
+			if (vp->lv[l] != NULL &&
+			    lvm_strcmp(vp->lv[l]->lv_name,
+				       le_remap_req.lv_name) == 0) {
+				for (le = 0; le < vp->lv[l]->lv_allocated_le;
+				     le++) {
+					if (vp->lv[l]->lv_current_pe[le].dev ==
+					    le_remap_req.old_dev &&
+					    vp->lv[l]->lv_current_pe[le].pe ==
+					    le_remap_req.old_pe) {
+						vp->lv[l]->lv_current_pe[le].dev =
+						    le_remap_req.new_dev;
+						vp->lv[l]->lv_current_pe[le].pe =
+						    le_remap_req.new_pe;
+						return 0;
+					}
+				}
+				return -EINVAL;
+			}
+		}
+
+		return -ENXIO;
+
+	case VG_CREATE:
+		/* create a VGDA */
+		return do_vg_create(minor, arg);
+
+	case VG_REMOVE:
+		/* remove an inactive VGDA */
+		return do_vg_remove(minor);
+
+	case VG_EXTEND:
+		/* extend a volume group */
+		if (vp == NULL)
+			return -ENXIO;
+		if (vp->pv_cur < vp->pv_max) {
+			for (p = 0; p < vp->pv_max; p++) {
+				if (vp->pv[p] == NULL) {
+					if ((vp->pv[p] =
+					vmalloc(sizeof(pv_t))) == NULL) {
+						printk(KERN_CRIT
+						       "%s -- VG_EXTEND: vmalloc error PV\n", lvm_name);
+						return -ENOMEM;
+					}
+					if (copy_from_user(vp->pv[p], arg,
+						      sizeof(pv_t)) != 0)
+						return -EFAULT;
+
+					vp->pv[p]->pv_status = PV_ACTIVE;
+					/* We don't need the PE list
+					   in kernel space like LVs pe_t list */
+					vp->pv[p]->pe = NULL;
+					vp->pv_cur++;
+					vp->pv_act++;
+					vp->pe_total += vp->pv[p]->pe_total;
+					/* insert a dummy inode for fs_may_mount */
+					vp->pv[p]->inode = lvm_get_inode(vp->pv[p]->pv_dev);
+					return 0;
+				}
+			}
+		}
+		return -EPERM;
+
+	case VG_REDUCE:
+		/* reduce a volume group */
+		if (vp == NULL)
+			return -ENXIO;
+		if (copy_from_user(pv_name, arg, sizeof(pv_name)) != 0)
+			return -EFAULT;
+
+		for (p = 0; p < vp->pv_max; p++) {
+			if (vp->pv[p] != NULL &&
+			    lvm_strcmp(vp->pv[p]->pv_name,
+				       pv_name) == 0) {
+				if (vp->pv[p]->lv_cur > 0)
+					return -EPERM;
+				vp->pe_total -= vp->pv[p]->pe_total;
+				vp->pv_cur--;
+				vp->pv_act--;
+#ifdef DEBUG_VFREE
+				printk(KERN_DEBUG
+				 "%s -- vfree %d\n", lvm_name, __LINE__);
+#endif
+				vp->pv[p]->inode->i_state |= I_FREEING;
+				clear_inode(vp->pv[p]->inode);
+				vfree(vp->pv[p]);
+				/* Make PV pointer array contiguous */
+				for (; p < vp->pv_max - 1; p++)
+					vp->pv[p] = vp->pv[p + 1];
+				vp->pv[p + 1] = NULL;
+				return 0;
+			}
+		}
+		return -ENXIO;
+
+	case VG_SET_EXTENDABLE:
+		/* set/clear extendability flag of volume group */
+		if (vp == NULL)
+			return -ENXIO;
+		if (copy_from_user(&extendable, arg, sizeof(extendable)) != 0)
+			return -EFAULT;
+
+		if (extendable == VG_EXTENDABLE ||
+		    extendable == ~VG_EXTENDABLE) {
+			if (extendable == VG_EXTENDABLE)
+				vp->vg_status |= VG_EXTENDABLE;
+			else
+				vp->vg_status &= ~VG_EXTENDABLE;
+		} else
+			return -EINVAL;
+		return 0;
+
+	case VG_STATUS:
+		/* get volume group data (only the vg_t struct) */
+		if (vp == NULL)
+			return -ENXIO;
+		if (copy_to_user(arg, vp, sizeof(vg_t)) != 0)
+			return -EFAULT;
+
+		return 0;
+
+	case VG_STATUS_GET_COUNT:
+		/* get volume group count */
+		if (copy_to_user(arg, &vg_count, sizeof(vg_count)) != 0)
+			return -EFAULT;
+
+		return 0;
+
+	case VG_STATUS_GET_NAMELIST:
+		/* get volume group count */
+		for (l = v = 0; v < ABS_MAX_VG; v++) {
+			if (vg[v] != NULL) {
+				if (copy_to_user(arg + l++ * NAME_LEN,
+						 vg[v]->vg_name,
+						 NAME_LEN) != 0)
+					return -EFAULT;
+			}
+		}
+		return 0;
+
+	case LV_CREATE:
+	case LV_REMOVE:
+	case LV_EXTEND:
+	case LV_REDUCE:
+		/* create, remove, extend or reduce a logical volume */
+		if (vp == NULL)
+			return -ENXIO;
+		if (copy_from_user(&lv_req, arg, sizeof(lv_req)) != 0)
+			return -EFAULT;
+
+		if (command != LV_REMOVE) {
+			if (copy_from_user(&lv, lv_req.lv, sizeof(lv_t)) != 0)
+				return -EFAULT;
+		}
+		switch (command) {
+		case LV_CREATE:
+			return do_lv_create(minor, lv_req.lv_name, &lv);
+
+		case LV_REMOVE:
+			return do_lv_remove(minor, lv_req.lv_name, -1);
+
+		case LV_EXTEND:
+		case LV_REDUCE:
+			return do_lv_extend_reduce(minor, lv_req.lv_name, &lv);
+		}
+
+	case LV_STATUS_BYNAME:
+		/* get status of a logical volume by name */
+		if (vp == NULL)
+			return -ENXIO;
+		if (copy_from_user(&lv_status_byname_req, arg,
+				   sizeof(lv_status_byname_req_t)) != 0)
+			return -EFAULT;
+
+		if (lv_status_byname_req.lv == NULL)
+			return -EINVAL;
+		if (copy_from_user(&lv, lv_status_byname_req.lv,
+				   sizeof(lv_t)) != 0)
+			return -EFAULT;
+
+		for (l = 0; l < vp->lv_max; l++) {
+			if (vp->lv[l] != NULL &&
+			    lvm_strcmp(vp->lv[l]->lv_name,
+				    lv_status_byname_req.lv_name) == 0) {
+				if (copy_to_user(lv_status_byname_req.lv,
+						 vp->lv[l],
+						 sizeof(lv_t)) != 0)
+					return -EFAULT;
+
+				if (lv.lv_current_pe != NULL) {
+					size = vp->lv[l]->lv_allocated_le *
+					    sizeof(pe_t);
+					if (copy_to_user(lv.lv_current_pe,
+							 vp->lv[l]->lv_current_pe,
+							 size) != 0)
+						return -EFAULT;
+				}
+				if (lv.lv_exception != NULL &&
+				    vp->lv[l]->lv_exception != NULL) {
+					if (copy_to_user(lv.lv_exception,
+							 vp->lv[l]->lv_exception,
+					    sizeof(lv_exception_t)) != 0)
+						return -EFAULT;
+				}
+				return 0;
+			}
+		}
+		return -ENXIO;
+
+	case LV_STATUS_BYINDEX:
+		/* get status of a logical volume by index */
+		if (vp == NULL)
+			return -ENXIO;
+		if (copy_from_user(&lv_status_byindex_req, arg,
+				   sizeof(lv_status_byindex_req)) != 0)
+			return -EFAULT;
+
+		if ((lvp = lv_status_byindex_req.lv) == NULL)
+			return -EINVAL;
+		l = lv_status_byindex_req.lv_index;
+		if (vp->lv[l] == NULL)
+			return -ENXIO;
+
+		if (copy_from_user(&lv, lvp, sizeof(lv_t)) != 0)
+			return -EFAULT;
+
+		if (copy_to_user(lvp, vp->lv[l],
+				 sizeof(lv_t)) != 0)
+			return -EFAULT;
+
+		if (lv.lv_current_pe != NULL) {
+			size = vp->lv[l]->lv_allocated_le * sizeof(pe_t);
+			if (copy_to_user(lv.lv_current_pe,
+				 vp->lv[l]->lv_current_pe,
+					 size) != 0)
+				return -EFAULT;
+		}
+		if (lv.lv_exception != NULL &&
+		    vp->lv[l]->lv_exception != NULL) {
+			if (copy_to_user(lv.lv_exception,
+				  vp->lv[l]->lv_exception,
+					 sizeof(lv_exception_t)) != 0)
+				return -EFAULT;
+		}
+		return 0;
+
+	case PV_CHANGE:
+		/* change a physical volume */
+		if (vp == NULL)
+			return -ENXIO;
+		if (copy_from_user(&pv_change_req, arg,
+				   sizeof(pv_change_req)) != 0)
+			return -EFAULT;
+
+		for (p = 0; p < vp->pv_max; p++) {
+			if (vp->pv[p] != NULL &&
+			    lvm_strcmp(vp->pv[p]->pv_name,
+				       pv_change_req.pv_name) == 0) {
+				inode_sav = vp->pv[p]->inode;
+				if (copy_from_user(vp->pv[p],
+						   pv_change_req.pv,
+						   sizeof(pv_t)) != 0)
+					return -EFAULT;
+
+				/* We don't need the PE list
+				   in kernel space as with LVs pe_t list */
+				vp->pv[p]->pe = NULL;
+				vp->pv[p]->inode = inode_sav;
+				return 0;
+			}
+		}
+		return -ENXIO;
+
+	case PV_STATUS:
+		/* get physical volume data (pv_t structure only) */
+		if (vp == NULL)
+			return -ENXIO;
+		if (copy_from_user(&pv_status_req, arg,
+				   sizeof(pv_status_req)) != 0)
+			return -EFAULT;
+
+		for (p = 0; p < vp->pv_max; p++) {
+			if (vp->pv[p] != NULL) {
+				if (lvm_strcmp(vp->pv[p]->pv_name,
+					   pv_status_req.pv_name) == 0) {
+					if (copy_to_user(pv_status_req.pv,
+						vp->pv[p],
+						      sizeof(pv_t)) != 0)
+						return -EFAULT;
+					return 0;
+				}
+			}
+		}
+		return -ENXIO;
+
+	case PV_FLUSH:
+		/* physical volume buffer flush/invalidate */
+		if (copy_from_user(&pv_flush_req, arg, sizeof(pv_flush_req)) != 0)
+			return -EFAULT;
+
+		for (v = 0; v < ABS_MAX_VG; v++) {
+			if (vg[v] == NULL)
+				continue;
+			for (p = 0; p < vg[v]->pv_max; p++) {
+				if (vg[v]->pv[p] != NULL &&
+				    lvm_strcmp(vg[v]->pv[p]->pv_name,
+					    pv_flush_req.pv_name) == 0) {
+					fsync_dev(vg[v]->pv[p]->pv_dev);
+					invalidate_buffers(vg[v]->pv[p]->pv_dev);
+					return 0;
+				}
+			}
+		}
+		return 0;
+
+	default:
+		return -EINVAL;
+
+	}
+
+	return 0;
+}				/* lvm_chr_ioctl */
+
+
+/*
+ * character device close routine
+ */
+
+static int lvm_chr_close(struct inode *inode, struct file *file)
+{
+	if (lvm_chr_open_count > 0)
+		lvm_chr_open_count--;
+	if (lock == current->pid) {
+		lock = 0;	/* release lock */
+		wake_up_interruptible(&lvm_wait);
+	}
+	MOD_DEC_USE_COUNT;
+	return 0;
+}				/* lvm_chr_close () */
+
+
+
+/********************************************************************
+ *
+ * Block device functions
+ *
+ ********************************************************************/
+
+/*
+ * block device open routine
+ */
+static int lvm_blk_open(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	vg_t *vp = vg[VG_BLK(minor)];
+
+#ifdef DEBUG_LVM_BLK_OPEN
+	printk(KERN_DEBUG
+	  "%s -- lvm_blk_open MINOR: %d  VG#: %d  LV#: %d  mode: 0x%X\n",
+	    lvm_name, minor, VG_BLK(minor), LV_BLK(minor), file->f_mode);
+#endif
+
+	if (vp != NULL &&
+	    (vp->vg_status & VG_ACTIVE) &&
+	    vp->lv[LV_BLK(minor)] != NULL &&
+	    LV_BLK(minor) >= 0 &&
+	    LV_BLK(minor) < vp->lv_max) {
+
+		/* Check parallel LV spindown (LV remove) */
+		if (vp->lv[LV_BLK(minor)]->lv_status & LV_SPINDOWN)
+			return -EPERM;
+
+		/* Check inactive LV and open for read/write */
+		if (file->f_mode & O_RDWR) {
+			if (!(vp->lv[LV_BLK(minor)]->lv_status & LV_ACTIVE))
+				return -EPERM;
+			if (!(vp->lv[LV_BLK(minor)]->lv_access & LV_WRITE))
+				return -EACCES;
+		}
+		if (vp->lv[LV_BLK(minor)]->lv_open == 0)
+			vp->lv_open++;
+		vp->lv[LV_BLK(minor)]->lv_open++;
+
+		MOD_INC_USE_COUNT;
+
+#ifdef DEBUG_LVM_BLK_OPEN
+		printk(KERN_DEBUG
+		       "%s -- lvm_blk_open MINOR: %d  VG#: %d  LV#: %d  size: %d\n",
+		       lvm_name, minor, VG_BLK(minor), LV_BLK(minor),
+		       vp->lv[LV_BLK(minor)]->lv_size);
+#endif
+
+		/* Override the file operations */
+		file->f_op = &lvm_blk_fops;
+		return 0;
+	}
+	return -ENXIO;
+}				/* lvm_blk_open () */
+
+
+/*
+ * block device read
+ */
+static ssize_t lvm_blk_read(struct file *file, char *buffer,
+			    size_t size, loff_t * offset)
+{
+	int major = MAJOR(file->f_dentry->d_inode->i_rdev);
+	int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+
+	read_ahead[major] = vg[VG_BLK(minor)]->lv[LV_BLK(minor)]->lv_read_ahead;
+	return block_read(file, buffer, size, offset);
+}
+
+
+/*
+ * block device write
+ */
+static ssize_t lvm_blk_write(struct file *file, const char *buffer,
+			     size_t size, loff_t * offset)
+{
+	int major = MAJOR(file->f_dentry->d_inode->i_rdev);
+	int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+
+	read_ahead[major] = vg[VG_BLK(minor)]->lv[LV_BLK(minor)]->lv_read_ahead;
+	return block_write(file, buffer, size, offset);
+}
+
+
+/*
+ * block device i/o-control routine
+ */
+static int lvm_blk_ioctl(struct inode *inode, struct file *file,
+			 uint command, ulong a)
+{
+	int minor = MINOR(inode->i_rdev);
+	void *arg = (void *) a;
+	struct hd_geometry *hd = (struct hd_geometry *) a;
+	vg_t *vp;
+
+#ifdef DEBUG_IOCTL
+	printk(KERN_DEBUG
+	       "%s -- lvm_blk_ioctl MINOR: %d  command: 0x%X  arg: %X  "
+	       "VG#: %dl  LV#: %d\n",
+	       lvm_name, minor, command, (ulong) arg,
+	       VG_BLK(minor), LV_BLK(minor));
+#endif
+
+	vp = vg[VG_BLK(minor)];
+
+	switch (command) {
+	case BLKGETSIZE:
+		/* return device size */
+#ifdef DEBUG_IOCTL
+		printk(KERN_DEBUG
+		       "%s -- lvm_blk_ioctl -- BLKGETSIZE: %u\n",
+		lvm_name, vp->lv[LV_BLK(minor)]->lv_size);
+#endif
+		copy_to_user((long *) arg, &vp-> \
+			     lv[LV_BLK(minor)]->lv_size,
+			     sizeof(vp->lv[LV_BLK(minor)]->lv_size));
+		break;
+
+
+		/* flush buffer cache */
+	case BLKFLSBUF:
+		/* super user validation */
+		if (!capable(CAP_SYS_ADMIN))
+			return -EACCES;
+
+#ifdef DEBUG_IOCTL
+		printk(KERN_DEBUG
+		       "%s -- lvm_blk_ioctl -- BLKFLSBUF\n", lvm_name);
+#endif
+		fsync_dev(inode->i_rdev);
+		break;
+
+	case BLKRASET:
+		/* set read ahead for block device */
+		/* super user validation */
+		if (!capable(CAP_SYS_ADMIN))
+			return -EACCES;
+
+#ifdef DEBUG_IOCTL
+		printk(KERN_DEBUG
+		       "%s -- lvm_blk_ioctl -- BLKRASET: %d sectors for %02X:%02X\n",
+		       lvm_name, (long) arg, MAJOR(inode->i_rdev), minor);
+#endif
+		if ((long) arg < LVM_MIN_READ_AHEAD ||
+		    (long) arg > LVM_MAX_READ_AHEAD)
+			return -EINVAL;
+		vp->lv[LV_BLK(minor)]->lv_read_ahead = (long) arg;
+		break;
+
+	case BLKRAGET:
+		/* get current read ahead setting */
+#ifdef DEBUG_IOCTL
+		printk(KERN_DEBUG
+		       "%s -- lvm_blk_ioctl -- BLKRAGET\n", lvm_name);
+#endif
+		copy_to_user((long *) arg,
+		    &vg[VG_BLK(minor)]->lv[LV_BLK(minor)]->lv_read_ahead,
+			  sizeof(vg[VG_BLK(minor)]->lv[LV_BLK(minor)]-> \
+				 lv_read_ahead));
+		break;
+
+	case HDIO_GETGEO:
+		/* get disk geometry */
+#ifdef DEBUG_IOCTL
+		printk(KERN_DEBUG
+		       "%s -- lvm_blk_ioctl -- HDIO_GETGEO\n", lvm_name);
+#endif
+		if (hd == NULL)
+			return -EINVAL;
+		{
+			unsigned char heads = 64;
+			unsigned char sectors = 32;
+			long start = 0;
+			short cylinders = vp->lv[LV_BLK(minor)]->lv_size /
+					heads / sectors;
+
+			if (copy_to_user((char *) &hd->heads, &heads,
+					 sizeof(heads)) != 0 ||
+			    copy_to_user((char *) &hd->sectors, &sectors,
+					 sizeof(sectors)) != 0 ||
+			    copy_to_user((short *) &hd->cylinders,
+				   &cylinders, sizeof(cylinders)) != 0 ||
+			    copy_to_user((long *) &hd->start, &start,
+					 sizeof(start)) != 0)
+				return -EFAULT;
+		}
+
+#ifdef DEBUG_IOCTL
+		printk(KERN_DEBUG
+		       "%s -- lvm_blk_ioctl -- cylinders: %d\n",
+		       lvm_name, vp->lv[LV_BLK(minor)]-> \
+		       lv_size / heads / sectors);
+#endif
+		break;
+
+	case LV_SET_ACCESS:
+		/* set access flags of a logical volume */
+		/* super user validation */
+		if (!capable(CAP_SYS_ADMIN))
+			return -EACCES;
+		vp->lv[LV_BLK(minor)]->lv_access = (ulong) arg;
+		break;
+
+	case LV_SET_STATUS:
+		/* set status flags of a logical volume */
+		/* super user validation */
+		if (!capable(CAP_SYS_ADMIN))
+			return -EACCES;
+		if (!((ulong) arg & LV_ACTIVE) &&
+		    vp->lv[LV_BLK(minor)]->lv_open > 1)
+			return -EPERM;
+		vp->lv[LV_BLK(minor)]->lv_status = (ulong) arg;
+		break;
+
+	case LV_SET_ALLOCATION:
+		/* set allocation flags of a logical volume */
+		/* super user validation */
+		if (!capable(CAP_SYS_ADMIN))
+			return -EACCES;
+		vp->lv[LV_BLK(minor)]->lv_allocation = (ulong) arg;
+		break;
+
+
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}				/* lvm_blk_ioctl () */
+
+
+/*
+ * block device close routine
+ */
+static int lvm_blk_close(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	vg_t *vp = vg[VG_BLK(minor)];
+	
+#ifdef DEBUG
+	printk(KERN_DEBUG
+	       "%s -- lvm_blk_close MINOR: %d  VG#: %d  LV#: %d\n",
+	       lvm_name, minor, VG_BLK(minor), LV_BLK(minor));
+#endif
+
+	sync_dev(inode->i_rdev);
+	if (vp->lv[LV_BLK(minor)]->lv_open == 1)
+		vp->lv_open--;
+	vp->lv[LV_BLK(minor)]->lv_open--;
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}				/* lvm_blk_close () */
+
+
+#if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS
+
+/*
+ * Support function /proc-Filesystem
+ */
+#define  LVM_PROC_BUF   ( i == 0 ? dummy_buf : &buf[sz])
+
+static int lvm_proc_get_info(char *page, char **start, off_t pos, int count) {
+	int c, i, l, p, v, vg_counter, pv_counter, lv_counter, lv_open_counter,
+		lv_open_total, pe_t_bytes, seconds;
+	static off_t sz;
+	off_t sz_last;
+	char allocation_flag, inactive_flag, rw_flag, stripes_flag;
+	char *lv_name = NULL;
+	static char *buf = NULL;
+	static char dummy_buf[160];	/* sized for 2 lines */
+
+#ifdef DEBUG_LVM_PROC_GET_INFO
+	printk(KERN_DEBUG
+	       "%s - lvm_proc_get_info CALLED  pos: %lu  count: %d\n",
+	       lvm_name, pos, count);
+#endif
+
+	if (pos == 0 || buf == NULL) {
+		sz_last = vg_counter = pv_counter = lv_counter = \
+		    lv_open_counter = lv_open_total = pe_t_bytes = 0;
+
+		/* search for activity */
+		for (v = 0; v < ABS_MAX_VG; v++) {
+			if (vg[v] != NULL) {
+				vg_counter++;
+				pv_counter += vg[v]->pv_cur;
+				lv_counter += vg[v]->lv_cur;
+				if (vg[v]->lv_cur > 0) {
+					for (l = 0; l < vg[v]->lv_max; l++) {
+						if (vg[v]->lv[l] != NULL) {
+							pe_t_bytes += vg[v]->lv[l]->lv_allocated_le;
+							if (vg[v]->lv[l]->lv_open > 0) {
+								lv_open_counter++;
+								lv_open_total += vg[v]->lv[l]->lv_open;
+							}
+						}
+					}
+				}
+			}
+		}
+		pe_t_bytes *= sizeof(pe_t);
+
+		if (buf != NULL) {
+#ifdef DEBUG_VFREE
+			printk(KERN_DEBUG
+			       "%s -- vfree %d\n", lvm_name, __LINE__);
+#endif
+			vfree(buf);
+			buf = NULL;
+		}
+		/* 2 times: first to get size to allocate buffer,
+		   2nd to fill the vmalloced buffer */
+		for (i = 0; i < 2; i++) {
+			sz = 0;
+			sz += sprintf(LVM_PROC_BUF,
+				      "LVM "
+#ifdef MODULE
+				      "module"
+#else
+				      "driver"
+#endif
+				      " %s\n\n"
+				    "Total:  %d VG%s  %d PV%s  %d LV%s ",
+				      lvm_short_version,
+				  vg_counter, vg_counter == 1 ? "" : "s",
+				  pv_counter, pv_counter == 1 ? "" : "s",
+				 lv_counter, lv_counter == 1 ? "" : "s");
+			sz += sprintf(LVM_PROC_BUF,
+				      "(%d LV%s open",
+				      lv_open_counter,
+				      lv_open_counter == 1 ? "" : "s");
+			if (lv_open_total > 0)
+				sz += sprintf(LVM_PROC_BUF,
+					      " %d times)\n",
+					      lv_open_total);
+			else
+				sz += sprintf(LVM_PROC_BUF, ")");
+			sz += sprintf(LVM_PROC_BUF,
+				      "\nGlobal: %lu bytes vmalloced   IOP version: %d   ",
+				      vg_counter * sizeof(vg_t) +
+				      pv_counter * sizeof(pv_t) +
+				      lv_counter * sizeof(lv_t) +
+				      pe_t_bytes + sz_last,
+				      lvm_iop_version);
+
+			seconds = CURRENT_TIME - loadtime;
+			if (seconds < 0)
+				loadtime = CURRENT_TIME + seconds;
+			if (seconds / 86400 > 0) {
+				sz += sprintf(LVM_PROC_BUF, "%d day%s ",
+					      seconds / 86400,
+					      seconds / 86400 == 0 ||
+					 seconds / 86400 > 1 ? "s" : "");
+			}
+			sz += sprintf(LVM_PROC_BUF, "%d:%02d:%02d active\n",
+				      (seconds % 86400) / 3600,
+				      (seconds % 3600) / 60,
+				      seconds % 60);
+
+			if (vg_counter > 0) {
+				for (v = 0; v < ABS_MAX_VG; v++) {
+					/* volume group */
+					if (vg[v] != NULL) {
+						inactive_flag = ' ';
+						if (!(vg[v]->vg_status & VG_ACTIVE))
+							inactive_flag = 'I';
+						sz += sprintf(LVM_PROC_BUF,
+							      "\nVG: %c%s  [%d PV, %d LV/%d open] "
+						      " PE Size: %d KB\n"
+							      "  Usage [KB/PE]: %d /%d total  "
+							      "%d /%d used  %d /%d free",
+							   inactive_flag,
+							  vg[v]->vg_name,
+							   vg[v]->pv_cur,
+							   vg[v]->lv_cur,
+							  vg[v]->lv_open,
+						     vg[v]->pe_size >> 1,
+							      vg[v]->pe_size * vg[v]->pe_total >> 1,
+							 vg[v]->pe_total,
+							      vg[v]->pe_allocated * vg[v]->pe_size >> 1,
+						     vg[v]->pe_allocated,
+							      (vg[v]->pe_total - vg[v]->pe_allocated) *
+						     vg[v]->pe_size >> 1,
+							      vg[v]->pe_total - vg[v]->pe_allocated);
+
+						/* physical volumes */
+						sz += sprintf(LVM_PROC_BUF,
+							      "\n  PV%s ",
+							      vg[v]->pv_cur == 1 ? ": " : "s:");
+						c = 0;
+						for (p = 0; p < vg[v]->pv_max; p++) {
+							if (vg[v]->pv[p] != NULL) {
+								inactive_flag = 'A';
+								if (!(vg[v]->pv[p]->pv_status & PV_ACTIVE))
+									inactive_flag = 'I';
+								allocation_flag = 'A';
+								if (!(vg[v]->pv[p]->pv_allocatable & PV_ALLOCATABLE))
+									allocation_flag = 'N';
+								sz += sprintf(LVM_PROC_BUF,
+									      "[%c%c] %-21s %8d /%-6d  "
+									      "%8d /%-6d  %8d /%-6d",
+									      inactive_flag,
+									      allocation_flag,
+									      vg[v]->pv[p]->pv_name,
+									      vg[v]->pv[p]->pe_total *
+									      vg[v]->pv[p]->pe_size >> 1,
+									      vg[v]->pv[p]->pe_total,
+									      vg[v]->pv[p]->pe_allocated *
+									      vg[v]->pv[p]->pe_size >> 1,
+									      vg[v]->pv[p]->pe_allocated,
+									      (vg[v]->pv[p]->pe_total -
+									       vg[v]->pv[p]->pe_allocated) *
+									      vg[v]->pv[p]->pe_size >> 1,
+									      vg[v]->pv[p]->pe_total -
+									      vg[v]->pv[p]->pe_allocated);
+								c++;
+								if (c < vg[v]->pv_cur)
+									sz += sprintf(LVM_PROC_BUF,
+										      "\n       ");
+							}
+						}
+
+						/* logical volumes */
+						sz += sprintf(LVM_PROC_BUF,
+							   "\n    LV%s ",
+							      vg[v]->lv_cur == 1 ? ": " : "s:");
+						c = 0;
+						for (l = 0; l < vg[v]->lv_max; l++) {
+							if (vg[v]->lv[l] != NULL) {
+								inactive_flag = 'A';
+								if (!(vg[v]->lv[l]->lv_status & LV_ACTIVE))
+									inactive_flag = 'I';
+								rw_flag = 'R';
+								if (vg[v]->lv[l]->lv_access & LV_WRITE)
+									rw_flag = 'W';
+								allocation_flag = 'D';
+								if (vg[v]->lv[l]->lv_allocation & LV_CONTIGUOUS)
+									allocation_flag = 'C';
+								stripes_flag = 'L';
+								if (vg[v]->lv[l]->lv_stripes > 1)
+									stripes_flag = 'S';
+								sz += sprintf(LVM_PROC_BUF,
+									      "[%c%c%c%c",
+									      inactive_flag,
+								 rw_flag,
+									      allocation_flag,
+									      stripes_flag);
+								if (vg[v]->lv[l]->lv_stripes > 1)
+									sz += sprintf(LVM_PROC_BUF, "%-2d",
+										      vg[v]->lv[l]->lv_stripes);
+								else
+									sz += sprintf(LVM_PROC_BUF, "  ");
+								lv_name = lvm_strrchr(vg[v]->lv[l]->lv_name, '/');
+								if (lv_name != NULL)
+									lv_name++;
+								else
+									lv_name = vg[v]->lv[l]->lv_name;
+								sz += sprintf(LVM_PROC_BUF, "] %-25s", lv_name);
+								if (lvm_strlen(lv_name) > 25)
+									sz += sprintf(LVM_PROC_BUF,
+										      "\n                              ");
+								sz += sprintf(LVM_PROC_BUF, "%9d /%-6d   ",
+									      vg[v]->lv[l]->lv_size >> 1,
+									      vg[v]->lv[l]->lv_size / vg[v]->pe_size);
+
+								if (vg[v]->lv[l]->lv_open == 0)
+									sz += sprintf(LVM_PROC_BUF, "close");
+								else
+									sz += sprintf(LVM_PROC_BUF, "%dx open",
+										      vg[v]->lv[l]->lv_open);
+								c++;
+								if (c < vg[v]->lv_cur)
+									sz += sprintf(LVM_PROC_BUF,
+										      "\n         ");
+							}
+						}
+						if (vg[v]->lv_cur == 0)
+							sz += sprintf(LVM_PROC_BUF, "none");
+						sz += sprintf(LVM_PROC_BUF, "\n");
+					}
+				}
+			}
+			if (buf == NULL) {
+				if ((buf = vmalloc(sz)) == NULL) {
+					sz = 0;
+					return sprintf(page, "%s - vmalloc error at line %d\n",
+						     lvm_name, __LINE__);
+				}
+			}
+			sz_last = sz;
+		}
+	}
+	if (pos > sz - 1) {
+		vfree(buf);
+		buf = NULL;
+		return 0;
+	}
+	*start = &buf[pos];
+	if (sz - pos < count)
+		return sz - pos;
+	else
+		return count;
+}				/* lvm_proc_get_info () */
+#endif				/* #if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS */
+
+
+/*
+ * block device support function for /usr/src/linux/drivers/block/ll_rw_blk.c
+ * (see init_module/lvm_init)
+ */
+
+static int lvm_map(int minor, kdev_t * rdev,
+		   ulong * rsector, ulong size, int rw)
+{
+	ulong index;
+	ulong rsector_sav;
+	kdev_t rdev_sav;
+	lv_t *lv = vg[VG_BLK(minor)]->lv[LV_BLK(minor)];
+
+	if (!(lv->lv_status & LV_ACTIVE)) {
+		printk(KERN_ALERT
+		       "%s - lvm_map: ll_rw_blk for inactive LV %s\n",
+		       lvm_name, lv->lv_name);
+		return -1;
+	}
+#ifdef WRITEA
+	if ((rw == WRITE || rw == WRITEA) &&
+#else
+	if (rw == WRITE &&
+#endif
+	    !(lv->lv_access & LV_WRITE)) {
+		printk(KERN_CRIT
+		    "%s - lvm_map: ll_rw_blk write for readonly LV %s\n",
+		       lvm_name, lv->lv_name);
+		return -1;
+	}
+#ifdef DEBUG_MAP_SIZE
+	if (size != 2)
+		printk(KERN_DEBUG
+		"%s - lvm_map minor:%d size:%lu ", lvm_name, minor, size);
+#endif
+
+#ifdef DEBUG_MAP
+	printk(KERN_DEBUG
+	       "%s - lvm_map minor:%d  *rdev: %02d:%02d  *rsector: %lu  "
+	       "size:%lu\n",
+	       lvm_name, minor,
+	       MAJOR(*rdev),
+	       MINOR(*rdev),
+	       *rsector, size);
+#endif
+
+	if (*rsector + size > lv->lv_size) {
+		printk(KERN_ALERT
+		   "%s - lvm_map *rsector: %lu + size: %lu too large for"
+		       " minor: %2d\n", lvm_name, *rsector, size, minor);
+		return -1;
+	}
+	rsector_sav = *rsector;
+	rdev_sav = *rdev;
+
+lvm_second_remap:
+
+	/* linear mapping */
+	if (lv->lv_stripes < 2) {
+		index = *rsector / vg[VG_BLK(minor)]->pe_size;	/* get the index */
+		*rsector = lv->lv_current_pe[index].pe +
+		    (*rsector % vg[VG_BLK(minor)]->pe_size);
+		*rdev = lv->lv_current_pe[index].dev;
+
+#ifdef DEBUG_MAP
+		printk(KERN_DEBUG
+		       "lv_current_pe[%ld].pe: %ld  rdev: %02d:%02d  rsector:%ld\n",
+		       index,
+		       lv->lv_current_pe[index].pe,
+		       MAJOR(*rdev),
+		       MINOR(*rdev),
+		       *rsector);
+#endif
+
+		/* striped mapping */
+	} else {
+		ulong stripe_index;
+		ulong stripe_length;
+
+		stripe_length = vg[VG_BLK(minor)]->pe_size * lv->lv_stripes;
+		stripe_index = (*rsector % stripe_length) / lv->lv_stripesize;
+		index = *rsector / stripe_length +
+		    (stripe_index % lv->lv_stripes) *
+		    (lv->lv_allocated_le / lv->lv_stripes);
+		*rsector = lv->lv_current_pe[index].pe +
+		    (*rsector % stripe_length) -
+		    (stripe_index % lv->lv_stripes) * lv->lv_stripesize -
+		    stripe_index / lv->lv_stripes *
+		    (lv->lv_stripes - 1) * lv->lv_stripesize;
+		*rdev = lv->lv_current_pe[index].dev;
+	}
+
+#ifdef DEBUG_MAP
+	printk(KERN_DEBUG
+	     "lv_current_pe[%ld].pe: %ld  rdev: %02d:%02d  rsector:%ld\n"
+	       "stripe_length: %ld  stripe_index: %ld\n",
+	       index,
+	       lv->lv_current_pe[index].pe,
+	       MAJOR(*rdev),
+	       MINOR(*rdev),
+	       *rsector,
+	       stripe_length,
+	       stripe_index);
+#endif
+
+	/* handle physical extents on the move */
+	if (pe_lock_req.lock == LOCK_PE) {
+		if (*rdev == pe_lock_req.data.pv_dev &&
+		    *rsector >= pe_lock_req.data.pv_offset &&
+		    *rsector < (pe_lock_req.data.pv_offset +
+				vg[VG_BLK(minor)]->pe_size)) {
+			sleep_on(&lvm_map_wait);
+			*rsector = rsector_sav;
+			*rdev = rdev_sav;
+			goto lvm_second_remap;
+		}
+	}
+	/* statistic */
+#ifdef WRITEA
+	if (rw == WRITE || rw == WRITEA)
+#else
+	if (rw == WRITE)
+#endif
+		lv->lv_current_pe[index].writes++;
+	else
+		lv->lv_current_pe[index].reads++;
+
+	/* snapshot volume exception handling on physical address base */
+	if (lv->lv_exception != NULL) {
+		spin_lock(&lv->lv_exception->lv_snapshot_lock);
+		if (lv->lv_exception->lv_remap_ptr <= lv->lv_exception->lv_remap_end) {
+			if (lv->lv_access & LV_SNAPSHOT_ORG) {
+				/* for write, check if it is neccessary to
+				   create a new remapped block */
+#ifdef WRITEA
+				if (rw == WRITE || rw == WRITEA)
+#else
+				if (rw == WRITE)
+#endif
+				{
+					rdev_sav = *rdev;
+					rsector_sav = *rsector;
+					if (lvm_snapshot_remap_block(rsector, rdev, minor) == FALSE) {
+						/* create a new mapping */
+						lvm_snapshot_remap_new_block(rsector, rdev, minor);
+					}
+					*rdev = rdev_sav;
+					*rsector = rsector_sav;
+				}
+			} else
+				lvm_snapshot_remap_block(rsector, rdev, minor);
+		}
+		spin_unlock(&lv->lv_exception->lv_snapshot_lock);
+	}
+	return 0;
+}				/* lvm_map () */
+
+
+/*
+ * lvm_map snapshot logical volume support functions
+ */
+
+
+/*
+ * figure out physical sector/rdev pair remapping
+ */
+static int lvm_snapshot_remap_block(ulong * rsector, kdev_t * rdev, int minor)
+{
+	lv_t *lv = vg[VG_BLK(minor)]->lv[LV_BLK(minor)];
+	lv_t *lv_org, *lv_snapshot;
+	lv_block_exception_t *lv_block_exception =
+	lv->lv_exception->lv_block_exception;
+	int i;
+	int lv_remap_ptr = lv->lv_exception->lv_remap_ptr;
+	int lv_remap_end = lv->lv_exception->lv_remap_end;
+	kdev_t last_dev;
+
+
+	if (lv_remap_ptr < lv_remap_end) {
+		for (i = 0; i < lv_remap_ptr; i++) {
+			if (*rsector >= lv_block_exception[i].rsector_org &&
+			    *rsector < lv_block_exception[i].rsector_org +
+			    lv->lv_exception->lv_chunk_size &&
+			    *rdev == lv_block_exception[i].rdev_org) {
+				*rdev = lv_block_exception[i].rdev_new;
+				*rsector = lv_block_exception[i].rsector_new +
+				    (*rsector % lv->lv_exception->lv_chunk_size);
+				return TRUE;
+			}
+		}
+		return FALSE;
+	}
+	/* give up, if we don't have capacity left */
+	lv->lv_exception->lv_remap_ptr = lv->lv_exception->lv_remap_end + 1;
+	spin_unlock(&lv->lv_exception->lv_snapshot_lock);
+
+	if (lv->lv_access & LV_SNAPSHOT_ORG) {
+		lv_org = lv;
+		lv_snapshot = vg[VG_BLK(minor)]->lv[LV_BLK(lv->lv_snapshot_minor)];
+	} else {
+		lv_org = vg[VG_BLK(minor)]->lv[LV_BLK(lv->lv_snapshot_minor)];
+		lv_snapshot = lv;
+	}
+
+	fsync_dev(lv_org->lv_dev);
+	invalidate_buffers(lv_snapshot->lv_dev);
+
+	last_dev = 0;
+	for (i = 0; i < lv_remap_end; i++) {
+		if (lv_block_exception[i].rdev_new != last_dev) {
+			fsync_dev(lv_block_exception[i].rdev_new);
+			invalidate_buffers(lv_block_exception[i].rdev_new);
+			last_dev = lv_block_exception[i].rdev_new;
+		}
+	}
+
+	printk(KERN_INFO
+	       "%s -- giving up to snapshot %s on %s\n",
+	       lvm_name, lv_org->lv_name, lv_snapshot->lv_name);
+
+	return TRUE;
+}
+
+
+
+/*
+ * copy on write handler for snapshot logical volumes
+ *
+ * get new block(s), read the original one(s), store it on the new one(s)
+ *
+ */
+static int lvm_snapshot_remap_new_block(ulong * rsector,
+					kdev_t * rdev,
+					int minor)
+{
+	lv_t *lv = vg[VG_BLK(minor)]->lv[LV_BLK(minor)];
+	lv_block_exception_t *lv_block_exception =
+	lv->lv_exception->lv_block_exception;
+	int end, error, i;
+	int lv_remap_ptr = lv->lv_exception->lv_remap_ptr;
+	ulong rblock_start;
+	static struct buffer_head *bh_list[LVM_SNAPSHOT_MAX_CHUNK],
+	*bh_new[LVM_SNAPSHOT_MAX_CHUNK];
+
+
+	if (lv_remap_ptr < lv->lv_exception->lv_remap_end) {
+		lv->lv_exception->lv_remap_ptr++;
+
+		rblock_start = *rsector >> 1;
+		rblock_start -= rblock_start % (lv->lv_exception->lv_chunk_size >> 1);
+		end = lv->lv_exception->lv_chunk_size / (lvm_blocksizes[minor] >> 9);
+		lv_block_exception[lv_remap_ptr].rdev_org = *rdev;
+		lv_block_exception[lv_remap_ptr].rsector_org = rblock_start << 1;
+
+		for (i = 0; i < end; i++) {
+			if ((bh_list[i] = getblk(*rdev,
+						 rblock_start +
+				       i * (lvm_blocksizes[minor] >> 10),
+				       lvm_blocksizes[minor])) == NULL) {
+				printk(KERN_CRIT
+				       "%s -- oops, unable to getblk %d from block "
+				       "%lu for %02d:%02d\n",
+				       lvm_name, lvm_blocksizes[minor],
+				       rblock_start + i * (lvm_blocksizes[minor] >> 10),
+				       MAJOR(*rdev), MINOR(*rdev));
+				for (i--; i >= 0; i--)
+					brelse(bh_list[i]);
+				goto lvm_snapshot_remap_new_block_error;
+			}
+		}
+
+		ll_rw_block(READ, end, bh_list);
+
+		for (error = i = 0; i < end; i++) {
+			if (!buffer_uptodate(bh_list[i]))
+				wait_on_buffer(bh_list[i]);
+			if (!buffer_uptodate(bh_list[i])) {
+				printk(KERN_CRIT
+				       "%s -- oops, buffer %d (b_rdev: %02d:%02d  "
+				       "b_rsector: %ld) not uptodate at line %d\n",
+				       lvm_name, i,
+				       MAJOR(bh_list[i]->b_rdev),
+				       MINOR(bh_list[i]->b_rdev),
+				       bh_list[i]->b_rsector,
+				       __LINE__);
+				error++;
+			}
+			if (error > 0) {
+				for (i = 0; i < end; i++) brelse(bh_list[i]);
+				goto lvm_snapshot_remap_new_block_error;
+			}
+		}
+
+
+		/* remap exception block */
+		*rdev = lv_block_exception[lv_remap_ptr].rdev_new;
+		*rsector = lv_block_exception[lv_remap_ptr].rsector_new +
+		    (*rsector % lv->lv_exception->lv_chunk_size);
+
+		rblock_start = lv_block_exception[lv_remap_ptr].rsector_new >> 1;
+
+		for (i = 0; i < end; i++) {
+			if ((bh_new[i] = getblk(*rdev, rblock_start +
+				       i * (lvm_blocksizes[minor] >> 10),
+				       lvm_blocksizes[minor])) == NULL) {
+				printk(KERN_CRIT
+				       "%s -- oops, unable to getblk %d for block "
+				       "%lu from %02d:%02d\n",
+				       lvm_name, lvm_blocksizes[minor],
+				       rblock_start + i * (lvm_blocksizes[minor] >> 10),
+				       MAJOR(*rdev), MINOR(*rdev));
+				for (--i; i >= 0; i--)
+					brelse(bh_new[i]);
+				for (i = 0; i < end; i++)
+					brelse(bh_list[i]);
+				goto lvm_snapshot_remap_new_block_error;
+			}
+		}
+
+		/* Copy data */
+		for (i = 0; i < end; i++) {
+			lvm_memcpy(bh_new[i]->b_data, bh_list[i]->b_data,
+				   bh_new[i]->b_size);
+			mark_buffer_dirty(bh_new[i], 0);
+			brelse(bh_new[i]);
+			brelse(bh_list[i]);
+		}
+
+		return TRUE;
+	}
+	return FALSE;
+
+lvm_snapshot_remap_new_block_error:
+	/* end criteria for lvm_map() */
+	lv->lv_exception->lv_remap_ptr = lv->lv_exception->lv_remap_end + 1;
+	printk(KERN_CRIT
+	       "%s -- I/O ERROR during snapshot\n", lvm_name);
+	lvm_snapshot_remap_block(rsector, rdev, minor);
+	return FALSE;
+}
+/*
+ * end lvm_map snapshot logical volume support functions
+ */
+
+
+/*
+ * internal support functions
+ */
+
+/*
+ * generate "hard disk" name
+ */
+void lvm_hd_name(char *buf, int minor)
+{
+	int len = 0;
+	vg_t *vp;
+	lv_t *lvp;
+
+	if ((vp=vg[VG_BLK(minor)]) == NULL ||
+	    (lvp=vp->lv[LV_BLK(minor)]) == NULL)
+		return;
+	len = lvm_strlen(lvp->lv_name) - 5;
+	lvm_memcpy(buf, &lvp->lv_name[5], len);
+	buf[len] = 0;
+	return;
+}
+
+
+/*
+ * this one never should be called...
+ */
+static void lvm_dummy_device_request(request_queue_t *t)
+{
+	printk(KERN_EMERG
+	       "%s -- oops, got lvm request?\n", lvm_name);
+	return;
+}
+
+
+/*
+ * character device support function VGDA create
+ */
+
+int do_vg_create(int minor, void *arg)
+{
+	int snaporg_minor;
+	ulong l, p;
+	lv_t lv;
+	vg_t *vp;
+
+	if (vg[VG_CHR(minor)] != NULL)
+		return -EPERM;
+
+	if ((vp = vg[VG_CHR(minor)] = vmalloc(sizeof(vg_t))) == NULL) {
+		printk(KERN_CRIT
+		       "%s -- VG_CREATE: vmalloc error VG\n", lvm_name);
+		return -ENOMEM;
+	}
+	/* get the volume group structure */
+	if (copy_from_user(vp, arg, sizeof(vg_t)) != 0) {
+		vfree(vp);
+		vg[VG_CHR(minor)] = NULL;
+		return -EFAULT;
+	}
+	/* we are not that active so far... */
+	vp->vg_status &= ~VG_ACTIVE;
+
+	vp->pe_allocated = 0;
+	if (vp->pv_max > ABS_MAX_PV) {
+		printk(KERN_WARNING
+		       "%s -- Can't activate VG: ABS_MAX_PV too small\n",
+		       lvm_name);
+		vfree(vp);
+		vg[VG_CHR(minor)] = NULL;
+		return -EPERM;
+	}
+	if (vp->lv_max > ABS_MAX_LV) {
+		printk(KERN_WARNING
+		"%s -- Can't activate VG: ABS_MAX_LV too small for %u\n",
+		       lvm_name, vp->lv_max);
+		vfree(vp);
+		vg[VG_CHR(minor)] = NULL;
+		return -EPERM;
+	}
+	/* get the physical volume structures */
+	vp->pv_act = vp->pv_cur = 0;
+	for (p = 0; p < vp->pv_max; p++) {
+		/* user space address */
+		if ((pvp = vp->pv[p]) != NULL) {
+			vp->pv[p] = vmalloc(sizeof(pv_t));
+			if (vp->pv[p] == NULL) {
+				printk(KERN_CRIT
+				       "%s -- VG_CREATE: vmalloc error PV\n", lvm_name);
+				do_vg_remove(minor);
+				return -ENOMEM;
+			}
+			if (copy_from_user(vp->pv[p], pvp,
+					   sizeof(pv_t)) != 0) {
+				do_vg_remove(minor);
+				return -EFAULT;
+			}
+			/* We don't need the PE list
+			   in kernel space as with LVs pe_t list (see below) */
+			vp->pv[p]->pe = NULL;
+			vp->pv[p]->pe_allocated = 0;
+			vp->pv[p]->pv_status = PV_ACTIVE;
+			vp->pv_act++;
+			vp->pv_cur++;
+
+			/* insert a dummy inode for fs_may_mount */
+			vp->pv[p]->inode = lvm_get_inode(vp->pv[p]->pv_dev);
+		}
+	}
+
+	/* get the logical volume structures */
+	vp->lv_cur = 0;
+	for (l = 0; l < vp->lv_max; l++) 
+	{
+		/* user space address */
+		if ((lvp = vp->lv[l]) != NULL) {
+			if (copy_from_user(&lv, lvp, sizeof(lv_t)) != 0) {
+				do_vg_remove(minor);
+				return -EFAULT;
+			}
+			vp->lv[l] = NULL;
+			if (do_lv_create(minor, lv.lv_name, &lv) != 0) {
+				do_vg_remove(minor);
+				return -EFAULT;
+			}
+		}
+	}
+
+	/* Second path to correct snapshot logical volumes which are not
+	   in place during first path above */
+	for (l = 0; l < vp->lv_max; l++) {
+		lv_t *lvt = vp->lv[l];
+		lv_t *lvs;
+		
+		if (lvt != NULL &&
+		    lvt->lv_access & LV_SNAPSHOT) {
+			snaporg_minor = lvt->lv_snapshot_minor;
+			lvs = vp->lv[LV_BLK(snaporg_minor)];
+			if (lvs != NULL) {
+				lvs->lv_access |= LV_SNAPSHOT_ORG;
+				lvs->lv_exception = lvt->lv_exception;
+				lvt->lv_current_pe = lvs->lv_current_pe;
+				lvt->lv_allocated_le = lvs->lv_allocated_le;
+				lvt->lv_current_le = lvs->lv_current_le;
+			}
+		}
+	}
+
+	vg_count++;
+
+	/* let's go active */
+	vp->vg_status |= VG_ACTIVE;
+	MOD_INC_USE_COUNT;
+	return 0;
+}				/* do_vg_create () */
+
+
+/*
+ * character device support function VGDA remove
+ */
+
+static int do_vg_remove(int minor)
+{
+	int l, p;
+	vg_t *vp;
+
+	if ((vp=vg[VG_CHR(minor)]) == NULL)
+		return -ENXIO;
+
+	if (vp->lv_open > 0)
+		return -EPERM;
+
+	/* let's go inactive */
+	vp->vg_status &= ~VG_ACTIVE;
+
+	/* free LVs */
+	/* first free snapshot logical volumes */
+	for (l = 0; l < vp->lv_max; l++) {
+		if (vp->lv[l] != NULL &&
+		    vp->lv[l]->lv_access & LV_SNAPSHOT) {
+			do_lv_remove(minor, NULL, l);
+			current->state = TASK_UNINTERRUPTIBLE;
+			schedule_timeout(1);
+		}
+	}
+	/* then free the rest */
+	for (l = 0; l < vp->lv_max; l++) {
+		if (vp->lv[l] != NULL) {
+			do_lv_remove(minor, NULL, l);
+			current->state = TASK_UNINTERRUPTIBLE;
+			schedule_timeout(1);
+		}
+	}
+
+	/* free PVs */
+	for (p = 0; p < vp->pv_max; p++) {
+		if (vp->pv[p] != NULL) {
+#ifdef DEBUG_VFREE
+			printk(KERN_DEBUG
+			       "%s -- vfree %d\n", lvm_name, __LINE__);
+#endif
+			vp->pv[p]->inode->i_state |= I_FREEING;
+			clear_inode(vp->pv[p]->inode);
+			vfree(vp->pv[p]);
+			vp->pv[p] = NULL;
+		}
+	}
+
+#ifdef DEBUG_VFREE
+	printk(KERN_DEBUG "%s -- vfree %d\n", lvm_name, __LINE__);
+#endif
+	vfree(vp);
+	vg[VG_CHR(minor)] = NULL;
+
+	vg_count--;
+	MOD_DEC_USE_COUNT;
+	return 0;
+}				/* do_vg_remove () */
+
+
+/*
+ * character device support function logical volume create
+ */
+
+static int do_lv_create(int minor, char *lv_name, lv_t * lv)
+{
+	int l, le, l_new, p, size, snaporg_minor;
+	ulong lv_status_save;
+	lv_exception_t *lve = lv->lv_exception;
+	lv_block_exception_t *lvbe = NULL;
+	vg_t *vp;
+	lv_t *lvp, *lvs;
+
+	/* use precopied logical volume */
+	if ((pep = lv->lv_current_pe) == NULL)
+		return -EINVAL;
+
+	/* in case of lv_remove(), lv_create() pair; for eg. lvrename does this */
+	l_new = -1;
+	
+	vp = vg[VG_CHR(minor)];
+	if (vp->lv[lv->lv_number] == NULL)
+		l_new = lv->lv_number;
+	else {
+		for (l = 0; l < vp->lv_max; l++) {
+			if (lvm_strcmp(vp->lv[l]->lv_name, lv_name) == 0)
+				return -EEXIST;
+			if (vp->lv[l] == NULL)
+				if (l_new == -1)
+					l_new = l;
+		}
+	}
+	if (l_new == -1)
+		return -EPERM;
+	l = l_new;
+
+	if ((lvp = vp->lv[l] = vmalloc(sizeof(lv_t))) == NULL) {;
+		printk(KERN_CRIT "%s -- LV_CREATE: vmalloc error LV\n", lvm_name);
+		return -ENOMEM;
+	}
+	/* copy preloaded LV */
+	lvm_memcpy((char *) lvp, (char *) lv, sizeof(lv_t));
+	lv_status_save = lvp->lv_status;
+	lvp->lv_status &= ~LV_ACTIVE;
+
+	/* get the PE structures from user space if this
+	   is no snapshot logical volume */
+	if (!(lvp->lv_access & LV_SNAPSHOT)) {
+		size = lvp->lv_allocated_le * sizeof(pe_t);
+		if ((lvp->lv_current_pe =  vmalloc(size)) == NULL) 
+		{
+			printk(KERN_CRIT
+			       "%s -- LV_CREATE: vmalloc error LV_CURRENT_PE of %d Byte\n",
+			       lvm_name, size);
+#ifdef DEBUG_VFREE
+			printk(KERN_DEBUG "%s -- vfree %d\n", lvm_name, __LINE__);
+#endif
+			vfree(lvp);
+			vp->lv[l] = NULL;
+			return -ENOMEM;
+		}
+		if (copy_from_user(lvp->lv_current_pe, pep, size)) {
+			vfree(lvp->lv_current_pe);
+			vfree(lvp);
+			vp->lv[l] = NULL;
+			return -EFAULT;
+		}
+	} else {
+		/* Get snapshot exception data and block list */
+		if (lve != NULL) {
+			if ((lvp->lv_exception = vmalloc(sizeof(lv_exception_t))) == NULL) 
+			{
+				printk(KERN_CRIT
+				       "%s -- LV_CREATE: vmalloc error LV_EXCEPTION at line %d "
+				       "of %d Byte\n",
+				       lvm_name, __LINE__, sizeof(lv_exception_t));
+				vfree(lvp);
+				vp->lv[l] = NULL;
+				return -ENOMEM;
+			}
+			if (copy_from_user(lvp->lv_exception, lve,
+					   sizeof(lv_exception_t))) {
+				vfree(lvp->lv_exception);
+				vfree(lvp);
+				vp->lv[l] = NULL;
+				return -EFAULT;
+			}
+			lvbe = lvp->lv_exception->lv_block_exception;
+			size = lvp->lv_exception->lv_remap_end *
+			    sizeof(lv_block_exception_t);
+			if ((lvp->lv_exception->lv_block_exception =
+			     vmalloc(size)) == NULL) {
+				printk(KERN_CRIT
+				       "%s -- LV_CREATE: vmalloc error LV_BLOCK_EXCEPTION "
+				       "at line %d of %d Byte\n",
+				       lvm_name, __LINE__, size);
+#ifdef DEBUG_VFREE
+				printk(KERN_DEBUG "%s -- vfree %d\n", lvm_name, __LINE__);
+#endif
+				vfree(lvp->lv_exception);
+				vfree(lvp);
+				vp->lv[l] = NULL;
+				return -ENOMEM;
+			}
+			if (copy_from_user(
+				lvp->lv_exception->lv_block_exception,
+						  lvbe, size)) 
+			{
+				vfree(lvp->lv_exception->lv_block_exception);
+				vfree(lvp->lv_exception);
+				vfree(lvp);
+				vp->lv[l] = NULL;
+				return -EFAULT;
+			}
+			lvp->lv_exception->lv_snapshot_lock = SPIN_LOCK_UNLOCKED;
+
+			snaporg_minor = lvp->lv_snapshot_minor;
+			
+			lvs = vp->lv[LV_BLK(snaporg_minor)];
+			
+			if (lvs != NULL) {
+				lvs->lv_access |= LV_SNAPSHOT_ORG;
+				lvs->lv_snapshot_minor = MINOR(lvp->lv_dev);
+				lvs->lv_exception = lvp->lv_exception;
+				lvp->lv_current_pe = lvs->lv_current_pe;
+				lvp->lv_allocated_le = lvs->lv_allocated_le;
+				lvp->lv_current_le = lvs->lv_current_le;
+				lvp->lv_size = lvs->lv_size;
+				/* sync the original logical volume */
+				fsync_dev(lvs->lv_dev);
+			} else {
+				vfree(lvp->lv_exception->lv_block_exception);
+				vfree(lvp->lv_exception);
+				vfree(lvp);
+				vp->lv[l] = NULL;
+				return -EFAULT;
+			}
+		} else {
+			vfree(lvp);
+			vp->lv[l] = NULL;
+			return -EINVAL;
+		}
+	}			/* if ( lvp->lv_access & LV_SNAPSHOT) */
+
+	/* correct the PE count in PVs if this is no snapshot logical volume */
+	if (!(lvp->lv_access & LV_SNAPSHOT)) {
+		for (le = 0; le < lvp->lv_allocated_le; le++) {
+			vp->pe_allocated++;
+			for (p = 0; p < vp->pv_cur; p++) {
+				if (vp->pv[p]->pv_dev ==  lvp->lv_current_pe[le].dev)
+					vp->pv[p]->pe_allocated++;
+			}
+		}
+	}
+	lvm_gendisk.part[MINOR(lvp->lv_dev)].start_sect = 0;
+	lvm_gendisk.part[MINOR(lvp->lv_dev)].nr_sects = vp->lv[l]->lv_size;
+	lvm_size[MINOR(lvp->lv_dev)] = lvp->lv_size >> 1;
+	vg_lv_map[MINOR(lvp->lv_dev)].vg_number =  vp->vg_number;
+	vg_lv_map[MINOR(lvp->lv_dev)].lv_number =  lvp->lv_number;
+
+	LVM_CORRECT_READ_AHEAD(lvp->lv_read_ahead);
+	vp->lv_cur++;
+	lvp->lv_status = lv_status_save;
+
+	return 0;
+}				/* do_lv_create () */
+
+
+/*
+ * character device support function logical volume remove
+ */
+static int do_lv_remove(int minor, char *lv_name, int l)
+{
+	uint le, p;
+	int snaporg_minor;
+	vg_t *vp;
+	lv_t *lvp, *lvs;
+
+
+	vp = vg[VG_CHR(minor)];
+
+	if (l == -1) {
+		for (l = 0; l < vp->lv_max; l++) {
+			if ((lvp=vp->lv[l]) != NULL &&
+			    lvm_strcmp(lvp->lv_name, lv_name) == 0) {
+				break;
+			}
+		}
+	}
+	lvp = vp->lv[l];
+	if (l < vp->lv_max) {
+		if (lvp->lv_open > 0)
+			return -EBUSY;
+
+#ifdef DEBUG
+		printk(KERN_DEBUG
+		       "%s -- fsync_dev and "
+		       "invalidate_buffers for %s [%s] in %s\n",
+		       lvm_name, lvp->lv_name,
+		       kdevname(lvp->lv_dev),
+		       vp->vg_name);
+#endif
+
+		/* check for deletion of snapshot source while
+		   snapshot volume still exists */
+		if ((lvp->lv_access & LV_SNAPSHOT_ORG) &&
+				 lvp->lv_snapshot_minor != 0)
+			return -EPERM;
+
+		lvp->lv_status |= LV_SPINDOWN;
+
+		/* sync the buffers if this is no snapshot logical volume */
+		if (!(lvp->lv_access & LV_SNAPSHOT))
+			fsync_dev(lvp->lv_dev);
+
+		lvp->lv_status &= ~LV_ACTIVE;
+
+		/* invalidate the buffers */
+		invalidate_buffers(lvp->lv_dev);
+
+		/* reset generic hd */
+		lvm_gendisk.part[MINOR(lvp->lv_dev)].start_sect = -1;
+		lvm_gendisk.part[MINOR(lvp->lv_dev)].nr_sects = 0;
+		lvm_size[MINOR(lvp->lv_dev)] = 0;
+
+		/* reset VG/LV mapping */
+		vg_lv_map[MINOR(lvp->lv_dev)].vg_number = ABS_MAX_VG;
+		vg_lv_map[MINOR(lvp->lv_dev)].lv_number = -1;
+
+		/* correct the PE count in PVs if this is no snapshot logical volume */
+		if (!(lvp->lv_access & LV_SNAPSHOT)) {
+			for (le = 0; le < lvp->lv_allocated_le; le++) {
+				vp->pe_allocated--;
+				for (p = 0; p < vp->pv_cur; p++) {
+					if (vp->pv[p]->pv_dev ==
+					    lvp->lv_current_pe[le].dev)
+						vp->pv[p]->pe_allocated--;
+				}
+			}
+			/* only if this is no snapshot logical volume because we share
+			   the lv_current_pe[] structs with the original logical volume */
+			vfree(lvp->lv_current_pe);
+		} else {
+			snaporg_minor = lvp->lv_snapshot_minor;
+
+			lvs = vp->lv[LV_BLK(snaporg_minor)];
+			
+			/* if we deleted snapshot original logical volume before
+			   the snapshot volume (in case og VG_REMOVE for eg.) */
+			if (lvs != NULL) {
+				lvs->lv_access &= ~LV_SNAPSHOT_ORG;
+				lvs->lv_exception = NULL;
+				lvs->lv_snapshot_minor = 0;
+			}
+			if (lvp->lv_exception != NULL) {
+				if (lvp->lv_exception->lv_block_exception
+				    != NULL)
+					vfree(lvp->lv_exception \
+					      ->lv_block_exception);
+				vfree(lvp->lv_exception);
+			}
+		}
+
+#ifdef DEBUG_VFREE
+		printk(KERN_DEBUG "%s -- vfree %d\n", lvm_name, __LINE__);
+#endif
+		vfree(lvp);
+		vp->lv[l] = NULL;
+		vp->lv_cur--;
+		return 0;
+	}
+	return -ENXIO;
+}				/* do_lv_remove () */
+
+
+/*
+ * character device support function logical volume extend / reduce
+ */
+static int do_lv_extend_reduce(int minor, char *lv_name, lv_t * lv)
+{
+	int l, le, p, size, old_allocated_le;
+	uint32_t end, lv_status_save;
+	pe_t *pe;
+	vg_t *vp;
+	lv_t *lvp = NULL;
+
+	if ((pep = lv->lv_current_pe) == NULL)
+		return -EINVAL;
+
+	vp = vg[VG_CHR(minor)];
+	
+	for (l = 0; l < vp->lv_max; l++) {
+		if ((lvp=vp->lv[l]) != NULL &&
+		    lvm_strcmp(lvp->lv_name, lv_name) == 0)
+			break;
+	}
+	if (l == vp->lv_max)
+		return -ENXIO;
+
+	/* check for active snapshot */
+	if (lv->lv_exception != NULL &&
+		lv->lv_exception->lv_remap_ptr < lv->lv_exception->lv_remap_end)
+		return -EPERM;
+
+	if ((pe = vmalloc(size = lv->lv_current_le * sizeof(pe_t))) == NULL) {
+		printk(KERN_CRIT
+		"%s -- do_lv_extend_reduce: vmalloc error LV_CURRENT_PE "
+		       "of %d Byte\n", lvm_name, size);
+		return -ENOMEM;
+	}
+	/* get the PE structures from user space */
+	if (copy_from_user(pe, pep, size)) {
+		vfree(pe);
+		return -EFAULT;
+	}
+	if ((pe = vmalloc(size = lv->lv_current_le * sizeof(pe_t))) == NULL) {
+		printk(KERN_CRIT
+		"%s -- do_lv_extend_reduce: vmalloc error LV_CURRENT_PE "
+		       "of %d Byte\n", lvm_name, size);
+		return -ENOMEM;
+	}
+	/* get the PE structures from user space */
+	if (copy_from_user(pe, pep, size)) {
+		vfree(pe);
+		return -EFAULT;
+	}
+#ifdef DEBUG
+	printk(KERN_DEBUG
+	       "%s -- fsync_dev and "
+	       "invalidate_buffers for %s [%s] in %s\n",
+	       lvm_name, lvp->lv_name,
+	       kdevname(lvp->lv_dev),
+	       vp->vg_name);
+#endif
+
+	lvp->lv_status |= LV_SPINDOWN;
+	fsync_dev(lvp->lv_dev);
+	lvp->lv_status &= ~LV_ACTIVE;
+	invalidate_buffers(lvp->lv_dev);
+
+	/* reduce allocation counters on PV(s) */
+	for (le = 0; le < lvp->lv_allocated_le; le++) {
+		vp->pe_allocated--;
+		for (p = 0; p < vp->pv_cur; p++) {
+			if (vp->pv[p]->pv_dev ==
+			lvp->lv_current_pe[le].dev) {
+				vp->pv[p]->pe_allocated--;
+				break;
+			}
+		}
+	}
+
+#ifdef DEBUG_VFREE
+	printk(KERN_DEBUG "%s -- vfree %d\n", lvm_name, __LINE__);
+#endif
+
+	/* save pointer to "old" lv/pe pointer array */
+	pep1 = lvp->lv_current_pe;
+	end = lvp->lv_current_le;
+
+	/* save open counter */
+	lv_open = lvp->lv_open;
+
+	/* save # of old allocated logical extents */
+	old_allocated_le = lvp->lv_allocated_le;
+
+	/* copy preloaded LV */
+	lvm_memcpy((char *) lvp, (char *) lv, sizeof(lv_t));
+	lv_status_save = lvp->lv_status;
+	lvp->lv_status |= LV_SPINDOWN;
+	lvp->lv_status &= ~LV_ACTIVE;
+	lvp->lv_current_pe = pe;
+	lvp->lv_open = lv_open;
+
+	/* save availiable i/o statistic data */
+	/* linear logical volume */
+	if (lvp->lv_stripes < 2) {
+		/* Check what last LE shall be used */
+		if (end > lvp->lv_current_le)
+			end = lvp->lv_current_le;
+		for (le = 0; le < end; le++) {
+			lvp->lv_current_pe[le].reads = pep1[le].reads;
+			lvp->lv_current_pe[le].writes = pep1[le].writes;
+		}
+		/* striped logical volume */
+	} else {
+		uint i, j, source, dest, end, old_stripe_size, new_stripe_size;
+
+		old_stripe_size = old_allocated_le / lvp->lv_stripes;
+		new_stripe_size = lvp->lv_allocated_le / lvp->lv_stripes;
+		end = old_stripe_size;
+		if (end > new_stripe_size)
+			end = new_stripe_size;
+		for (i = source = dest = 0;
+		     i < lvp->lv_stripes; i++) {
+			for (j = 0; j < end; j++) {
+				lvp->lv_current_pe[dest + j].reads =
+				    pep1[source + j].reads;
+				lvp->lv_current_pe[dest + j].writes =
+				    pep1[source + j].writes;
+			}
+			source += old_stripe_size;
+			dest += new_stripe_size;
+		}
+	}
+	vfree(pep1);
+	pep1 = NULL;
+
+
+	/* extend the PE count in PVs */
+	for (le = 0; le < lvp->lv_allocated_le; le++) {
+		vp->pe_allocated++;
+		for (p = 0; p < vp->pv_cur; p++) {
+			if (vp->pv[p]->pv_dev == lvp->lv_current_pe[le].dev) 
+			{
+				vp->pv[p]->pe_allocated++;
+				break;
+			}
+		}
+	}
+
+	lvm_gendisk.part[MINOR(lvp->lv_dev)].start_sect = 0;
+	lvm_gendisk.part[MINOR(lvp->lv_dev)].nr_sects = lvp->lv_size;
+	lvm_size[MINOR(lvp->lv_dev)] = lvp->lv_size >> 1;
+	/* vg_lv_map array doesn't have to be changed here */
+
+	lvp->lv_status = lv_status_save;
+	LVM_CORRECT_READ_AHEAD(lvp->lv_read_ahead);
+
+	return 0;
+}				/* do_lv_extend_reduce () */
+
+
+/*
+ * support function initialize gendisk variables
+ */
+void __init lvm_geninit(struct gendisk *lvm_gdisk)
+{
+	int i = 0;
+
+#ifdef DEBUG_GENDISK
+	printk(KERN_DEBUG "%s -- lvm_gendisk\n", lvm_name);
+#endif
+
+	for (i = 0; i < MAX_LV; i++) {
+		lvm_gendisk.part[i].start_sect = -1;	/* avoid partition check */
+		lvm_size[i] = lvm_gendisk.part[i].nr_sects = 0;
+		lvm_blocksizes[i] = BLOCK_SIZE;
+	}
+
+	blksize_size[LVM_BLOCK_MAJOR] = lvm_blocksizes;
+	blk_size[LVM_BLOCK_MAJOR] = lvm_size;
+
+	return;
+}				/* lvm_gen_init () */
+
+
+/*
+ * support function to get an empty inode
+ *
+ * Gets an empty inode to be inserted into the inode hash,
+ * so that a physical volume can't be mounted.
+ * This is analog to drivers/block/md.c
+ *
+ * Is this the real thing?
+ *
+ */
+struct inode *lvm_get_inode(int dev)
+{
+	struct inode *inode_this = NULL;
+
+	/* Lock the device by inserting a dummy inode. */
+	inode_this = get_empty_inode();
+	inode_this->i_dev = inode_this->i_rdev = dev;
+	insert_inode_hash(inode_this);
+	return inode_this;
+}
+
+
+/* my strlen */
+inline int lvm_strlen(char *s1)
+{
+	int len = 0;
+
+	while (s1[len] != 0) len++;
+	return len;
+}
+
+
+/* my strcmp */
+inline int lvm_strcmp(char *s1, char *s2)
+{
+	while (*s1 != 0 && *s2 != 0) {
+		if (*s1 != *s2)
+			return -1;
+		s1++;
+		s2++;
+	}
+	if (*s1 == 0 && *s2 == 0)
+		return 0;
+	return -1;
+}
+
+
+/* my strrchr */
+inline char *lvm_strrchr(char *s1, char c)
+{
+	char *s2 = NULL;
+
+	while (*s1 != 0) {
+		if (*s1 == c)
+			s2 = s1;
+		s1++;
+	}
+	return s2;
+}
+
+
+/* my memcpy */
+inline void lvm_memcpy(char *dest, char *source, int size)
+{
+	for (; size > 0; size--) *dest++ = *source++;
+}
--- linux-2.3.40.orig/fs/partitions/check.c	Tue Jan 25 03:38:21 2000
+++ linux-2.3.40/fs/partitions/check.c	Tue Jan 25 03:38:21 2000
@@ -36,6 +36,11 @@
 extern void rd_load(void);
 extern void initrd_load(void);
 
+#if defined CONFIG_BLK_DEV_LVM || defined CONFIG_BLK_DEV_LVM_MODULE
+#include <linux/lvm.h>
+void ( *lvm_hd_name_ptr) ( char *, int) = NULL;
+#endif
+
 struct gendisk *gendisk_head;
 
 static int (*check_part[])(struct gendisk *hd, kdev_t dev, unsigned long first_sect, int first_minor) = {
@@ -87,6 +92,13 @@
 	 * This requires special handling here.
 	 */
 	switch (hd->major) {
+#if defined CONFIG_BLK_DEV_LVM || defined CONFIG_BLK_DEV_LVM_MODULE
+		case LVM_BLK_MAJOR:
+			*buf = 0;
+			if ( lvm_hd_name_ptr != NULL)
+				( lvm_hd_name_ptr) ( buf, minor);
+			return buf;
+#endif
 		case IDE9_MAJOR:
 			unit += 2;
 		case IDE8_MAJOR:
--- linux-2.3.40.orig/include/linux/lvm.h	Tue Jan 25 03:38:21 2000
+++ linux-2.3.40/include/linux/lvm.h	Tue Jan 25 03:38:21 2000
@@ -0,0 +1,805 @@
+/*
+ * kernel/lvm.h
+ *
+ * Copyright (C) 1997 - 2000  Heinz Mauelshagen, Germany
+ *
+ * February-November 1997
+ * May-July 1998
+ * January-March,July,September,October 1999
+ *
+ * lvm 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.
+ * 
+ * lvm is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA. 
+ *
+ */
+
+/*
+ * Changelog
+ *
+ *    10/10/1997 - beginning of new structure creation
+ *    12/05/1998 - incorporated structures from lvm_v1.h and deleted lvm_v1.h
+ *    07/06/1998 - avoided LVM_KMALLOC_MAX define by using vmalloc/vfree
+ *                 instead of kmalloc/kfree
+ *    01/07/1998 - fixed wrong LVM_MAX_SIZE
+ *    07/07/1998 - extended pe_t structure by ios member (for statistic)
+ *    02/08/1998 - changes for official char/block major numbers
+ *    07/08/1998 - avoided init_module() and cleanup_module() to be static
+ *    29/08/1998 - seprated core and disk structure type definitions
+ *    01/09/1998 - merged kernel integration version (mike)
+ *    20/01/1999 - added LVM_PE_DISK_OFFSET macro for use in
+ *                 vg_read_with_pv_and_lv(), pv_move_pe(), pv_show_pe_text()...
+ *    18/02/1999 - added definition of time_disk_t structure for;
+ *                 keeps time stamps on disk for nonatomic writes (future)
+ *    15/03/1999 - corrected LV() and VG() macro definition to use argument
+ *                 instead of minor
+ *    03/07/1999 - define for genhd.c name handling
+ *    23/07/1999 - implemented snapshot part
+ *
+ */
+
+
+#ifndef _LVM_H_INCLUDE
+#define _LVM_H_INCLUDE
+
+#define	_LVM_H_VERSION	"LVM 0.8 (17/09/1999)"
+
+/*
+ * preprocessor definitions
+ */
+
+/* lots of debugging output (see driver source)
+   #define DEBUG_LVM_GET_INFO
+   #define DEBUG
+   #define DEBUG_MAP
+   #define DEBUG_MAP_SIZE
+   #define DEBUG_IOCTL
+   #define DEBUG_READ
+   #define DEBUG_GENDISK
+   #define DEBUG_VG_CREATE
+   #define DEBUG_LVM_BLK_OPEN
+   #define DEBUG_VFREE
+ */
+/*
+ * end of preprocessor definitions
+ */
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+   /* for 2.0.x series */
+#ifndef KERNEL_VERSION
+#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+#endif
+
+#include <linux/kdev_t.h>
+#include <linux/major.h>
+
+/* leave this for now until major.h is updated (mike) */
+#ifndef	LVM_BLOCK_MAJOR
+#define	LVM_BLOCK_MAJOR	58
+#endif
+#ifndef	LVM_CHAR_MAJOR
+#define	LVM_CHAR_MAJOR	109
+#endif
+
+#if !defined ( LVM_BLOCK_MAJOR) || !defined ( LVM_CHAR_MAJOR)
+#error Bad include/linux/major.h - LVM MAJOR undefined
+#endif
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION ( 2, 1 ,0)
+#ifndef	uint8_t
+#define uint8_t __u8
+#endif
+#ifndef	uint16_t
+#define uint16_t __u16
+#endif
+#ifndef	uint32_t
+#define uint32_t __u32
+#endif
+#ifndef	uint64_t
+#define uint64_t __u64
+#endif
+#endif
+
+#define LVM_STRUCT_VERSION	1	/* structure version */
+
+/* set the default structure version */
+#if ( LVM_STRUCT_VERSION == 1)
+#define pv_t pv_v1_t
+#define lv_t lv_v2_t
+#define vg_t vg_v1_t
+#define pv_disk_t pv_disk_v1_t
+#define lv_disk_t lv_disk_v1_t
+#define vg_disk_t vg_disk_v1_t
+#define lv_exception_t lv_v2_exception_t
+#endif
+
+
+/*
+ * i/o protocoll version
+ *
+ * defined here for the driver and defined seperate in the
+ * user land LVM parts
+ *
+ */
+#define	LVM_DRIVER_IOP_VERSION	        5
+
+#define LVM_NAME        "lvm"
+
+/*
+ * VG/LV indexing macros
+ */
+/* character minor maps directly to volume group */
+#define	VG_CHR(a) ( a)
+
+/* block minor indexes into a volume group/logical volume indirection table */
+#define	VG_BLK(a)	( vg_lv_map[a].vg_number)
+#define LV_BLK(a)	( vg_lv_map[a].lv_number)
+
+/*
+ * absolute limits for VGs, PVs per VG and LVs per VG
+ */
+#define ABS_MAX_VG	99
+#define ABS_MAX_PV	256
+#define ABS_MAX_LV	256	/* caused by 8 bit minor */
+
+#define MAX_VG  ABS_MAX_VG
+#define MAX_LV	ABS_MAX_LV
+#define	MAX_PV	ABS_MAX_PV
+
+#if ( MAX_VG > ABS_MAX_VG)
+#undef MAX_VG
+#define MAX_VG ABS_MAX_VG
+#endif
+
+#if ( MAX_LV > ABS_MAX_LV)
+#undef MAX_LV
+#define MAX_LV ABS_MAX_LV
+#endif
+
+
+/*
+ * VGDA: default disk spaces and offsets
+ *
+ *   there's space after the structures for later extensions.
+ *
+ *   offset            what                                size
+ *   ---------------   ----------------------------------  ------------
+ *   0                 physical volume structure           ~500 byte
+ *
+ *   1K                volume group structure              ~200 byte
+ *
+ *   5K                time stamp structure                ~
+ *
+ *   6K                namelist of physical volumes        128 byte each
+ *
+ *   6k + n * 128byte  n logical volume structures         ~300 byte each
+ *
+ *   + m * 328byte     m physical extent alloc. structs    4 byte each
+ *
+ *   End of disk -     first physical extent               typical 4 megabyte
+ *   PE total *
+ *   PE size
+ *
+ *
+ */
+
+/* DONT TOUCH THESE !!! */
+/* base of PV structure in disk partition */
+#define	LVM_PV_DISK_BASE  	0L
+
+/* size reserved for PV structure on disk */
+#define	LVM_PV_DISK_SIZE  	1024L
+
+/* base of VG structure in disk partition */
+#define	LVM_VG_DISK_BASE  	LVM_PV_DISK_SIZE
+
+/* size reserved for VG structure */
+#define	LVM_VG_DISK_SIZE  	( 9 * 512L)
+
+/* size reserved for timekeeping */
+#define	LVM_TIMESTAMP_DISK_BASE	( LVM_VG_DISK_BASE +  LVM_VG_DISK_SIZE)
+#define	LVM_TIMESTAMP_DISK_SIZE	512L	/* reserved for timekeeping */
+
+/* name list of physical volumes on disk */
+#define	LVM_PV_NAMELIST_DISK_BASE ( LVM_TIMESTAMP_DISK_BASE + \
+                                    LVM_TIMESTAMP_DISK_SIZE)
+
+/* now for the dynamically calculated parts of the VGDA */
+#define	LVM_LV_DISK_OFFSET(a, b) ( (a)->lv_on_disk.base + sizeof ( lv_t) * b)
+#define	LVM_DISK_SIZE(pv) 	 ( (pv)->pe_on_disk.base + \
+                                   (pv)->pe_on_disk.size)
+#define	LVM_PE_DISK_OFFSET(pe, pv)	( pe * pv->pe_size + \
+					  ( LVM_DISK_SIZE ( pv) / SECTOR_SIZE))
+#define	LVM_PE_ON_DISK_BASE(pv) \
+   { int rest; \
+     pv->pe_on_disk.base = pv->lv_on_disk.base + pv->lv_on_disk.size; \
+     if ( ( rest = pv->pe_on_disk.base % SECTOR_SIZE) != 0) \
+        pv->pe_on_disk.base += ( SECTOR_SIZE - rest); \
+   }
+/* END default disk spaces and offsets for PVs */
+
+
+/*
+ * LVM_PE_T_MAX corresponds to:
+ *
+ * 8KB PE size can map a ~512 MB logical volume at the cost of 1MB memory,
+ *
+ * 128MB PE size can map a 8TB logical volume at the same cost of memory.
+ *
+ * Default PE size of 4 MB gives a maximum logical volume size of 256 GB.
+ *
+ * Maximum PE size of 16GB gives a maximum logical volume size of 1024 TB.
+ *
+ * AFAIK, the actual kernels limit this to 1 TB.
+ *
+ * Should be a sufficient spectrum ;*)
+ */
+
+/* This is the usable size of disk_pe_t.le_num !!!        v     v */
+#define	LVM_PE_T_MAX		( ( 1 << ( sizeof ( uint16_t) * 8)) - 2)
+
+#define	LVM_LV_SIZE_MAX(a)	( ( long long) LVM_PE_T_MAX * (a)->pe_size)
+#define	LVM_MIN_PE_SIZE		( 8L * 2)	/* 8 KB in sectors */
+#define	LVM_MAX_PE_SIZE		( 16L * 1024L * 1024L * 2)	/* 16GB in sectors */
+#define	LVM_DEFAULT_PE_SIZE	( 4096L * 2)	/* 4 MB in sectors */
+#define	LVM_DEFAULT_STRIPE_SIZE	16L	/* 16 KB  */
+#define	LVM_MIN_STRIPE_SIZE	2L	/* 1 KB in sectors */
+#define	LVM_MAX_STRIPE_SIZE	( 512L * 2)	/* 512 KB in sectors */
+#define	LVM_MAX_STRIPES		128	/* max # of stripes */
+#define	LVM_MAX_SIZE            ( 1024LU * 1024 * 1024 * 2)	/* 1TB[sectors] */
+#define	LVM_MAX_MIRRORS    	2	/* future use */
+#define	LVM_MIN_READ_AHEAD	2	/* minimum read ahead sectors */
+#define	LVM_MAX_READ_AHEAD	120	/* maximum read ahead sectors */
+#define	LVM_MAX_LV_IO_TIMEOUT	60	/* seconds I/O timeout (future use) */
+#define	LVM_PARTITION           0xfe	/* LVM partition id */
+#define	LVM_NEW_PARTITION       0x8e	/* new LVM partition id (10/09/1999) */
+#define	LVM_PE_SIZE_PV_SIZE_REL	5	/* max relation PV size and PE size */
+
+#define	LVM_SNAPSHOT_MIN_CHUNK	1	/* 1k */
+#define	LVM_SNAPSHOT_DEF_CHUNK	64
+#define	LVM_SNAPSHOT_MAX_CHUNK	8192
+
+#define	UNDEF	-1
+#define FALSE	0
+#define TRUE	1
+
+
+/*
+ * ioctls
+ */
+/* volume group */
+#define	VG_CREATE               _IOW ( 0xfe, 0x00, 1)
+#define	VG_REMOVE               _IOW ( 0xfe, 0x01, 1)
+
+#define	VG_EXTEND               _IOW ( 0xfe, 0x03, 1)
+#define	VG_REDUCE               _IOW ( 0xfe, 0x04, 1)
+
+#define	VG_STATUS               _IOWR ( 0xfe, 0x05, 1)
+#define	VG_STATUS_GET_COUNT     _IOWR ( 0xfe, 0x06, 1)
+#define	VG_STATUS_GET_NAMELIST  _IOWR ( 0xfe, 0x07, 1)
+
+#define	VG_SET_EXTENDABLE       _IOW ( 0xfe, 0x08, 1)
+
+
+/* logical volume */
+#define	LV_CREATE               _IOW ( 0xfe, 0x20, 1)
+#define	LV_REMOVE               _IOW ( 0xfe, 0x21, 1)
+
+#define	LV_ACTIVATE             _IO ( 0xfe, 0x22)
+#define	LV_DEACTIVATE           _IO ( 0xfe, 0x23)
+
+#define	LV_EXTEND               _IOW ( 0xfe, 0x24, 1)
+#define	LV_REDUCE               _IOW ( 0xfe, 0x25, 1)
+
+#define	LV_STATUS_BYNAME        _IOWR ( 0xfe, 0x26, 1)
+#define	LV_STATUS_BYINDEX       _IOWR ( 0xfe, 0x27, 1)
+
+#define LV_SET_ACCESS           _IOW ( 0xfe, 0x28, 1)
+#define LV_SET_ALLOCATION       _IOW ( 0xfe, 0x29, 1)
+#define LV_SET_STATUS           _IOW ( 0xfe, 0x2a, 1)
+
+#define LE_REMAP                _IOW ( 0xfe, 0x2b, 1)
+
+
+/* physical volume */
+#define	PV_STATUS               _IOWR ( 0xfe, 0x40, 1)
+#define	PV_CHANGE               _IOWR ( 0xfe, 0x41, 1)
+#define	PV_FLUSH                _IOW ( 0xfe, 0x42, 1)
+
+/* physical extent */
+#define	PE_LOCK_UNLOCK          _IOW ( 0xfe, 0x50, 1)
+
+/* i/o protocol version */
+#define	LVM_GET_IOP_VERSION     _IOR ( 0xfe, 0x98, 1)
+
+#ifdef LVM_TOTAL_RESET
+/* special reset function for testing purposes */
+#define	LVM_RESET               _IO ( 0xfe, 0x99)
+#endif
+
+/* lock the logical volume manager */
+#define	LVM_LOCK_LVM            _IO ( 0xfe, 0x100)
+/* END ioctls */
+
+
+/*
+ * Status flags
+ */
+/* volume group */
+#define	VG_ACTIVE            0x01	/* vg_status */
+#define	VG_EXPORTED          0x02	/*     "     */
+#define	VG_EXTENDABLE        0x04	/*     "     */
+
+#define	VG_READ              0x01	/* vg_access */
+#define	VG_WRITE             0x02	/*     "     */
+
+/* logical volume */
+#define	LV_ACTIVE            0x01	/* lv_status */
+#define	LV_SPINDOWN          0x02	/*     "     */
+
+#define	LV_READ              0x01	/* lv_access */
+#define	LV_WRITE             0x02	/*     "     */
+#define	LV_SNAPSHOT          0x04	/*     "     */
+#define	LV_SNAPSHOT_ORG      0x08	/*     "     */
+
+#define	LV_BADBLOCK_ON       0x01	/* lv_badblock */
+
+#define	LV_STRICT            0x01	/* lv_allocation */
+#define	LV_CONTIGUOUS        0x02	/*       "       */
+
+/* physical volume */
+#define	PV_ACTIVE            0x01	/* pv_status */
+#define	PV_ALLOCATABLE       0x02	/* pv_allocatable */
+
+
+/*
+ * Structure definitions core/disk follow
+ *
+ * conditional conversion takes place on big endian architectures
+ * in functions * pv_copy_*(), vg_copy_*() and lv_copy_*()
+ *
+ */
+
+#define	NAME_LEN		128	/* don't change!!! */
+#define	UUID_LEN		16	/* don't change!!! */
+
+/* remap physical sector/rdev pairs */
+typedef struct {
+	ulong rsector_org;
+	kdev_t rdev_org;
+	ulong rsector_new;
+	kdev_t rdev_new;
+} lv_block_exception_t;
+
+
+/* disk stored pe information */
+typedef struct {
+	uint16_t lv_num;
+	uint16_t le_num;
+} disk_pe_t;
+
+/* disk stored PV, VG, LV and PE size and offset information */
+typedef struct {
+	uint32_t base;
+	uint32_t size;
+} lvm_disk_data_t;
+
+
+/*
+ * Structure Physical Volume (PV) Version 1
+ */
+
+/* core */
+typedef struct {
+	uint8_t id[2];		/* Identifier */
+	uint16_t version;	/* HM lvm version */
+	lvm_disk_data_t pv_on_disk;
+	lvm_disk_data_t vg_on_disk;
+	lvm_disk_data_t pv_namelist_on_disk;
+	lvm_disk_data_t lv_on_disk;
+	lvm_disk_data_t pe_on_disk;
+	uint8_t pv_name[NAME_LEN];
+	uint8_t vg_name[NAME_LEN];
+	uint8_t system_id[NAME_LEN];	/* for vgexport/vgimport */
+	kdev_t pv_dev;
+	uint32_t pv_number;
+	uint32_t pv_status;
+	uint32_t pv_allocatable;
+	uint32_t pv_size;	/* HM */
+	uint32_t lv_cur;
+	uint32_t pe_size;
+	uint32_t pe_total;
+	uint32_t pe_allocated;
+	uint32_t pe_stale;	/* for future use */
+	disk_pe_t *pe;		/* HM */
+	struct inode *inode;	/* HM */
+} pv_v1_t;
+
+/* disk */
+typedef struct {
+	uint8_t id[2];		/* Identifier */
+	uint16_t version;	/* HM lvm version */
+	lvm_disk_data_t pv_on_disk;
+	lvm_disk_data_t vg_on_disk;
+	lvm_disk_data_t pv_namelist_on_disk;
+	lvm_disk_data_t lv_on_disk;
+	lvm_disk_data_t pe_on_disk;
+	uint8_t pv_name[NAME_LEN];
+	uint8_t vg_name[NAME_LEN];
+	uint8_t system_id[NAME_LEN];	/* for vgexport/vgimport */
+	uint32_t pv_major;
+	uint32_t pv_number;
+	uint32_t pv_status;
+	uint32_t pv_allocatable;
+	uint32_t pv_size;	/* HM */
+	uint32_t lv_cur;
+	uint32_t pe_size;
+	uint32_t pe_total;
+	uint32_t pe_allocated;
+	uint32_t dummy1;
+	uint32_t dummy2;
+	uint32_t dummy3;
+} pv_disk_v1_t;
+
+
+/*
+ * Structure Physical Volume (PV) Version 2 (future!)
+ */
+
+typedef struct {
+	uint8_t id[2];		/* Identifier */
+	uint16_t version;	/* HM lvm version */
+	lvm_disk_data_t pv_on_disk;
+	lvm_disk_data_t vg_on_disk;
+	lvm_disk_data_t pv_uuid_on_disk;
+	lvm_disk_data_t lv_on_disk;
+	lvm_disk_data_t pe_on_disk;
+	uint8_t pv_name[NAME_LEN];
+	uint8_t vg_name[NAME_LEN];
+	uint8_t system_id[NAME_LEN];	/* for vgexport/vgimport */
+	kdev_t pv_dev;
+	uint32_t pv_number;
+	uint32_t pv_status;
+	uint32_t pv_allocatable;
+	uint32_t pv_size;	/* HM */
+	uint32_t lv_cur;
+	uint32_t pe_size;
+	uint32_t pe_total;
+	uint32_t pe_allocated;
+	uint32_t pe_stale;	/* for future use */
+	disk_pe_t *pe;		/* HM */
+	struct inode *inode;	/* HM */
+	/* delta to version 1 starts here */
+	uint8_t pv_uuid[UUID_LEN];
+	uint32_t pv_atime;	/* PV access time */
+	uint32_t pv_ctime;	/* PV creation time */
+	uint32_t pv_mtime;	/* PV modification time */
+} pv_v2_t;
+
+
+/*
+ * Structures for Logical Volume (LV)
+ */
+
+/* core PE information */
+typedef struct {
+	kdev_t dev;
+	uint32_t pe;		/* to be changed if > 2TB */
+	uint32_t reads;
+	uint32_t writes;
+} pe_t;
+
+typedef struct {
+	uint8_t lv_name[NAME_LEN];
+	kdev_t old_dev;
+	kdev_t new_dev;
+	ulong old_pe;
+	ulong new_pe;
+} le_remap_req_t;
+
+
+
+/*
+ * Structure Logical Volume (LV) Version 1
+ */
+
+/* core */
+typedef struct {
+	uint8_t lv_name[NAME_LEN];
+	uint8_t vg_name[NAME_LEN];
+	uint32_t lv_access;
+	uint32_t lv_status;
+	uint32_t lv_open;	/* HM */
+	kdev_t lv_dev;		/* HM */
+	uint32_t lv_number;	/* HM */
+	uint32_t lv_mirror_copies;	/* for future use */
+	uint32_t lv_recovery;	/*       "        */
+	uint32_t lv_schedule;	/*       "        */
+	uint32_t lv_size;
+	pe_t *lv_current_pe;	/* HM */
+	uint32_t lv_current_le;	/* for future use */
+	uint32_t lv_allocated_le;
+	uint32_t lv_stripes;
+	uint32_t lv_stripesize;
+	uint32_t lv_badblock;	/* for future use */
+	uint32_t lv_allocation;
+	uint32_t lv_io_timeout;	/* for future use */
+	uint32_t lv_read_ahead;
+} lv_v1_t;
+
+/* disk */
+typedef struct {
+	uint8_t lv_name[NAME_LEN];
+	uint8_t vg_name[NAME_LEN];
+	uint32_t lv_access;
+	uint32_t lv_status;
+	uint32_t lv_open;	/* HM */
+	uint32_t lv_dev;	/* HM */
+	uint32_t lv_number;	/* HM */
+	uint32_t lv_mirror_copies;	/* for future use */
+	uint32_t lv_recovery;	/*       "        */
+	uint32_t lv_schedule;	/*       "        */
+	uint32_t lv_size;
+	uint32_t dummy;
+	uint32_t lv_current_le;	/* for future use */
+	uint32_t lv_allocated_le;
+	uint32_t lv_stripes;
+	uint32_t lv_stripesize;
+	uint32_t lv_badblock;	/* for future use */
+	uint32_t lv_allocation;
+	uint32_t lv_io_timeout;	/* for future use */
+	uint32_t lv_read_ahead;	/* HM, for future use */
+} lv_disk_v1_t;
+
+
+/*
+ * Structure Logical Volume (LV) Version 2
+ */
+
+typedef struct {
+	spinlock_t lv_snapshot_lock;
+	lv_block_exception_t *lv_block_exception;
+	uint32_t lv_chunk_size;
+	uint32_t lv_remap_end;
+	uint32_t lv_remap_ptr;
+} lv_v2_exception_t;
+
+/* core */
+typedef struct {
+	uint8_t lv_name[NAME_LEN];
+	uint8_t vg_name[NAME_LEN];
+	uint32_t lv_access;
+	uint32_t lv_status;
+	uint32_t lv_open;	/* HM */
+	kdev_t lv_dev;		/* HM */
+	uint32_t lv_number;	/* HM */
+	uint32_t lv_mirror_copies;	/* for future use */
+	uint32_t lv_recovery;	/*       "        */
+	uint32_t lv_schedule;	/*       "        */
+	uint32_t lv_size;
+	pe_t *lv_current_pe;	/* HM */
+	uint32_t lv_current_le;	/* for future use */
+	uint32_t lv_allocated_le;
+	uint32_t lv_stripes;
+	uint32_t lv_stripesize;
+	uint32_t lv_badblock;	/* for future use */
+	uint32_t lv_allocation;
+	uint32_t lv_io_timeout;	/* for future use */
+	uint32_t lv_read_ahead;
+	/* delta to version 1 starts here */
+	int lv_snapshot_minor;
+	lv_v2_exception_t *lv_exception;
+} lv_v2_t;
+
+/* disk */
+typedef struct {
+	uint8_t lv_name[NAME_LEN];
+	uint8_t vg_name[NAME_LEN];
+	uint32_t lv_access;
+	uint32_t lv_status;
+	uint32_t lv_open;	/* HM */
+	uint32_t lv_dev;	/* HM */
+	uint32_t lv_number;	/* HM */
+	uint32_t lv_mirror_copies;	/* for future use */
+	uint32_t lv_recovery;	/*       "        */
+	uint32_t lv_schedule;	/*       "        */
+	uint32_t lv_size;
+	uint32_t dummy;
+	uint32_t lv_current_le;	/* for future use */
+	uint32_t lv_allocated_le;
+	uint32_t lv_stripes;
+	uint32_t lv_stripesize;
+	uint32_t lv_badblock;	/* for future use */
+	uint32_t lv_allocation;
+	uint32_t lv_io_timeout;	/* for future use */
+	uint32_t lv_read_ahead;	/* HM, for future use */
+} lv_disk_v2_t;
+
+
+/*
+ * Structure Volume Group (VG) Version 1
+ */
+
+typedef struct {
+	uint8_t vg_name[NAME_LEN];	/* volume group name */
+	uint32_t vg_number;	/* volume group number */
+	uint32_t vg_access;	/* read/write */
+	uint32_t vg_status;	/* active or not */
+	uint32_t lv_max;	/* maximum logical volumes */
+	uint32_t lv_cur;	/* current logical volumes */
+	uint32_t lv_open;	/* open    logical volumes */
+	uint32_t pv_max;	/* maximum physical volumes */
+	uint32_t pv_cur;	/* current physical volumes FU */
+	uint32_t pv_act;	/* active physical volumes */
+	uint32_t dummy;		/* was obsolete max_pe_per_pv */
+	uint32_t vgda;		/* volume group descriptor arrays FU */
+	uint32_t pe_size;	/* physical extent size in sectors */
+	uint32_t pe_total;	/* total of physical extents */
+	uint32_t pe_allocated;	/* allocated physical extents */
+	uint32_t pvg_total;	/* physical volume groups FU */
+	struct proc_dir_entry *proc;
+	pv_t *pv[ABS_MAX_PV + 1];	/* physical volume struct pointers */
+	lv_t *lv[ABS_MAX_LV + 1];	/* logical  volume struct pointers */
+} vg_v1_t;
+
+typedef struct {
+	uint8_t vg_name[NAME_LEN];	/* volume group name */
+	uint32_t vg_number;	/* volume group number */
+	uint32_t vg_access;	/* read/write */
+	uint32_t vg_status;	/* active or not */
+	uint32_t lv_max;	/* maximum logical volumes */
+	uint32_t lv_cur;	/* current logical volumes */
+	uint32_t lv_open;	/* open    logical volumes */
+	uint32_t pv_max;	/* maximum physical volumes */
+	uint32_t pv_cur;	/* current physical volumes FU */
+	uint32_t pv_act;	/* active physical volumes */
+	uint32_t dummy;
+	uint32_t vgda;		/* volume group descriptor arrays FU */
+	uint32_t pe_size;	/* physical extent size in sectors */
+	uint32_t pe_total;	/* total of physical extents */
+	uint32_t pe_allocated;	/* allocated physical extents */
+	uint32_t pvg_total;	/* physical volume groups FU */
+} vg_disk_v1_t;
+
+/*
+ * Structure Volume Group (VG) Version 2
+ */
+
+typedef struct {
+	uint8_t vg_name[NAME_LEN];	/* volume group name */
+	uint32_t vg_number;	/* volume group number */
+	uint32_t vg_access;	/* read/write */
+	uint32_t vg_status;	/* active or not */
+	uint32_t lv_max;	/* maximum logical volumes */
+	uint32_t lv_cur;	/* current logical volumes */
+	uint32_t lv_open;	/* open    logical volumes */
+	uint32_t pv_max;	/* maximum physical volumes */
+	uint32_t pv_cur;	/* current physical volumes FU */
+	uint32_t pv_act;	/* future: active physical volumes */
+	uint32_t max_pe_per_pv;	/* OBSOLETE maximum PE/PV */
+	uint32_t vgda;		/* volume group descriptor arrays FU */
+	uint32_t pe_size;	/* physical extent size in sectors */
+	uint32_t pe_total;	/* total of physical extents */
+	uint32_t pe_allocated;	/* allocated physical extents */
+	uint32_t pvg_total;	/* physical volume groups FU */
+	struct proc_dir_entry *proc;
+	pv_t *pv[ABS_MAX_PV + 1];	/* physical volume struct pointers */
+	lv_t *lv[ABS_MAX_LV + 1];	/* logical  volume struct pointers */
+	/* delta to version 1 starts here */
+	uint8_t vg_uuid[UUID_LEN];	/*  volume group UUID */
+	time_t vg_atime;	/* VG access time */
+	time_t vg_ctime;	/* VG creation time */
+	time_t vg_mtime;	/* VG modification time */
+} vg_v2_t;
+
+
+/*
+ * Timekeeping structure on disk (0.7 feature)
+ *
+ * Holds several timestamps for start/stop time of non
+ * atomic VGDA disk i/o operations
+ *
+ */
+
+typedef struct {
+	uint32_t seconds;	/* seconds since the epoch */
+	uint32_t jiffies;	/* micro timer */
+} lvm_time_t;
+
+#define	TIMESTAMP_ID_SIZE	2
+typedef struct {
+	uint8_t id[TIMESTAMP_ID_SIZE];	/* Identifier */
+	lvm_time_t pv_vg_lv_pe_io_begin;
+	lvm_time_t pv_vg_lv_pe_io_end;
+	lvm_time_t pv_io_begin;
+	lvm_time_t pv_io_end;
+	lvm_time_t vg_io_begin;
+	lvm_time_t vg_io_end;
+	lvm_time_t lv_io_begin;
+	lvm_time_t lv_io_end;
+	lvm_time_t pe_io_begin;
+	lvm_time_t pe_io_end;
+	lvm_time_t pe_move_io_begin;
+	lvm_time_t pe_move_io_end;
+	uint8_t dummy[LVM_TIMESTAMP_DISK_SIZE -
+		      TIMESTAMP_ID_SIZE -
+		      12 * sizeof(lvm_time_t)];
+	/* ATTENTION  ^^ */
+}
+timestamp_disk_t;
+
+/* same on disk and in core so far */
+typedef timestamp_disk_t timestamp_t;
+
+/* function identifiers for timestamp actions */
+typedef enum {
+	PV_VG_LV_PE_IO_BEGIN,
+	PV_VG_LV_PE_IO_END,
+	PV_IO_BEGIN,
+	PV_IO_END,
+	VG_IO_BEGIN,
+	VG_IO_END,
+	LV_IO_BEGIN,
+	LV_IO_END,
+	PE_IO_BEGIN,
+	PE_IO_END,
+	PE_MOVE_IO_BEGIN,
+	PE_MOVE_IO_END
+} ts_fct_id_t;
+
+
+/*
+ * Request structures for ioctls
+ */
+
+/* Request structure PV_STATUS */
+typedef struct {
+	char pv_name[NAME_LEN];
+	pv_t *pv;
+} pv_status_req_t, pv_change_req_t;
+
+/* Request structure PV_FLUSH */
+typedef struct {
+	char pv_name[NAME_LEN];
+} pv_flush_req_t;
+
+
+/* Request structure PE_MOVE */
+typedef struct {
+	enum {
+		LOCK_PE, UNLOCK_PE
+	} lock;
+	struct {
+		kdev_t lv_dev;
+		kdev_t pv_dev;
+		uint32_t pv_offset;
+	} data;
+} pe_lock_req_t;
+
+
+/* Request structure LV_STATUS_BYNAME */
+typedef struct {
+	char lv_name[NAME_LEN];
+	lv_t *lv;
+} lv_status_byname_req_t, lv_req_t;
+
+/* Request structure LV_STATUS_BYINDEX */
+typedef struct {
+	ulong lv_index;
+	lv_t *lv;
+} lv_status_byindex_req_t;
+
+#endif				/* #ifndef _LVM_H_INCLUDE */
--- linux-2.3.40.orig/kernel/ksyms.c	Tue Jan 25 03:38:21 2000
+++ linux-2.3.40/kernel/ksyms.c	Tue Jan 25 03:38:21 2000
@@ -71,7 +71,14 @@
 };
 #endif
 
-
+#ifdef CONFIG_BLK_DEV_LVM_MODULE
+   extern int (*lvm_map_ptr) ( int, kdev_t *, unsigned long *,
+                               unsigned long, int);
+   extern void (*lvm_hd_name_ptr) ( char*, int);
+   EXPORT_SYMBOL(lvm_map_ptr);
+   EXPORT_SYMBOL(lvm_hd_name_ptr);
+#endif
+ 
 #ifdef CONFIG_KMOD
 EXPORT_SYMBOL(request_module);
 #endif
@@ -248,6 +255,7 @@
 /* block device driver support */
 EXPORT_SYMBOL(block_read);
 EXPORT_SYMBOL(block_write);
+EXPORT_SYMBOL(block_fsync);
 EXPORT_SYMBOL(wait_for_request);
 EXPORT_SYMBOL(blksize_size);
 EXPORT_SYMBOL(hardsect_size);
@@ -258,6 +266,7 @@
 EXPORT_SYMBOL(bmap);
 EXPORT_SYMBOL(sync_dev);
 EXPORT_SYMBOL(blkdev_open);
+EXPORT_SYMBOL(blkdev_close);
 EXPORT_SYMBOL(blkdev_get);
 EXPORT_SYMBOL(blkdev_put);
 EXPORT_SYMBOL(ioctl_by_bdev);
