patch-2.4.8 linux/drivers/s390/block/dasd.c

Next file: linux/drivers/s390/block/dasd_3370_erp.c
Previous file: linux/drivers/s390/block/Makefile
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.7/linux/drivers/s390/block/dasd.c linux/drivers/s390/block/dasd.c
@@ -1,14 +1,13 @@
-/* 
+/*
  * File...........: linux/drivers/s390/block/dasd.c
  * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
- *                  Horst Hummel <Horst.Hummel@de.ibm.com>  
+ *                  Horst Hummel <Horst.Hummel@de.ibm.com> 
  *                  Carsten Otte <Cotte@de.ibm.com>
  * Bugreports.to..: <Linux390@de.ibm.com>
- * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
+ * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
  *
  * History of changes (starts July 2000)
  * 11/09/00 complete redesign after code review
- * 02/01/01 removed some warnings
  * 02/01/01 added dynamic registration of ioctls
  *          fixed bug in registration of new majors
  *          fixed handling of request during dasd_end_request
@@ -18,20 +17,34 @@
  *          fixed some race conditions related to modules
  *          added devfs suupport
  * 03/06/01 refined dynamic attach/detach for leaving devices which are online.
- * 06/09/01 refined dynamic modifiaction of devices
- *          renewed debug feature exploitation
+ * 03/09/01 refined dynamic modifiaction of devices
+ * 03/12/01 moved policy in dasd_format to dasdfmt (renamed BIODASDFORMAT)
+ * 03/19/01 added BIODASDINFO-ioctl
+ *          removed 2.2 compatibility
+ * 04/27/01 fixed PL030119COT (dasd_disciplines does not work)
+ * 04/30/01 fixed PL030146HSM (module locking with dynamic ioctls)
+ *          fixed PL030130SBA (handling of invalid ranges)
+ * 05/02/01 fixed PL030145SBA (killing dasdmt)
+ *          fixed PL030149SBA (status of 'accepted' devices)
+ *          fixed PL030146SBA (BUG in ibm.c after adding device)
+ *          added BIODASDPRRD ioctl interface
+ * 05/11/01 fixed  PL030164MVE (trap in probeonly mode)
+ * 05/15/01 fixed devfs support for unformatted devices
+ * 06/26/01 hopefully fixed PL030172SBA,PL030234SBA
+ * 07/09/01 fixed PL030324MSH (wrong statistics output)
+ * 07/16/01 merged in new fixes for handling low-mem situations
  */
 
-#include <linux/module.h>
 #include <linux/config.h>
 #include <linux/version.h>
+#include <linux/kmod.h>
 #include <linux/init.h>
 #include <linux/blkdev.h>
 #include <linux/stddef.h>
 #include <linux/kernel.h>
 #include <linux/tqueue.h>
 #include <linux/timer.h>
-#include <linux/malloc.h>
+#include <linux/slab.h>
 #include <linux/genhd.h>
 #include <linux/hdreg.h>
 #include <linux/interrupt.h>
@@ -42,9 +55,9 @@
 #include <linux/spinlock.h>
 #include <linux/devfs_fs_kernel.h>
 #include <linux/blkpg.h>
+#include <linux/wait.h>
 
 #include <asm/ccwcache.h>
-#include <asm/dasd.h>
 #include <asm/debug.h>
 
 #include <asm/atomic.h>
@@ -57,6 +70,9 @@
 #include <asm/s390_ext.h>
 #include <asm/s390dyn.h>
 #include <asm/idals.h>
+#include <asm/dasd.h>
+
+#include "dasd_int.h"
 
 #ifdef CONFIG_DASD_ECKD
 #include "dasd_eckd.h"
@@ -68,25 +84,40 @@
 #include "dasd_diag.h"
 #endif				/*  CONFIG_DASD_DIAG */
 
+/* SECTION: exported variables of dasd.c */
+
+debug_info_t *dasd_debug_area;
+
 MODULE_AUTHOR ("Holger Smolinski <Holger.Smolinski@de.ibm.com>");
 MODULE_DESCRIPTION ("Linux on S/390 DASD device driver,"
 		    " Copyright 2000 IBM Corporation");
 MODULE_SUPPORTED_DEVICE ("dasd");
 MODULE_PARM (dasd, "1-" __MODULE_STRING (256) "s");
-EXPORT_SYMBOL (dasd_discipline_enq);
-EXPORT_SYMBOL (dasd_discipline_deq);
+MODULE_PARM (dasd_disciplines, "1-" __MODULE_STRING (8) "s");
+EXPORT_SYMBOL (dasd_chanq_enq_head);
+EXPORT_SYMBOL (dasd_debug_area);
+EXPORT_SYMBOL (dasd_chanq_enq);
+EXPORT_SYMBOL (dasd_chanq_deq);
+EXPORT_SYMBOL (dasd_discipline_add);
+EXPORT_SYMBOL (dasd_discipline_del);
 EXPORT_SYMBOL (dasd_start_IO);
+EXPORT_SYMBOL (dasd_term_IO);
+EXPORT_SYMBOL (dasd_schedule_bh);
 EXPORT_SYMBOL (dasd_int_handler);
+EXPORT_SYMBOL (dasd_oper_handler);
 EXPORT_SYMBOL (dasd_alloc_request);
 EXPORT_SYMBOL (dasd_free_request);
-EXPORT_SYMBOL(dasd_ioctl_no_register);  
-EXPORT_SYMBOL(dasd_ioctl_no_unregister);
+EXPORT_SYMBOL (dasd_ioctl_no_register);
+EXPORT_SYMBOL (dasd_ioctl_no_unregister);
+EXPORT_SYMBOL (dasd_default_erp_action);
+EXPORT_SYMBOL (dasd_default_erp_postaction);
+EXPORT_SYMBOL (dasd_sleep_on_req);
+EXPORT_SYMBOL (dasd_set_normalized_cda);
 
 /* SECTION: Constant definitions to be used within this file */
 
-#define PRINTK_HEADER DASD_NAME": "
+#define PRINTK_HEADER DASD_NAME":"
 
-#define DASD_EMERGENCY_REQUESTS 16
 #define DASD_MIN_SIZE_FOR_QUEUE 32
 #undef CONFIG_DYNAMIC_QUEUE_MIN_SIZE
 #define DASD_CHANQ_MAX_SIZE 6
@@ -94,27 +125,36 @@
 /* SECTION: prototypes for static functions of dasd.c */
 
 static request_fn_proc do_dasd_request;
-static int dasd_set_device_level (unsigned int, int, dasd_discipline_t *, int);
+static int dasd_set_device_level (unsigned int, dasd_discipline_t *, int);
 static request_queue_t *dasd_get_queue (kdev_t kdev);
 static void cleanup_dasd (void);
-int dasd_fillgeo(int kdev,struct hd_geometry *geo);
-
+static void dasd_plug_device (dasd_device_t * device);
+static int dasd_fillgeo (int kdev, struct hd_geometry *geo);
+static void dasd_enable_ranges (dasd_range_t *, dasd_discipline_t *, int); 
+static void dasd_disable_ranges (dasd_range_t *, dasd_discipline_t *, int, int); 
+static void dasd_enable_single_device ( unsigned long);
+static inline int dasd_state_init_to_ready(dasd_device_t*);
+static inline void dasd_setup_partitions ( dasd_device_t *);
+static inline int dasd_setup_blkdev(dasd_device_t*);
+static inline int dasd_disable_blkdev(dasd_device_t*);
+static void dasd_flush_chanq ( dasd_device_t * device, int destroy ); 
+static void dasd_flush_request_queues ( dasd_device_t * device, int destroy );
 static struct block_device_operations dasd_device_operations;
-
+static inline dasd_device_t ** dasd_device_from_devno (int);
+static void dasd_process_queues (dasd_device_t * device);
 /* SECTION: static variables of dasd.c */
 
 static devfs_handle_t dasd_devfs_handle;
-
-/* SECTION: exported variables of dasd.c */
-
-debug_info_t *dasd_debug_area;
+static wait_queue_head_t dasd_init_waitq;
+static atomic_t dasd_init_pending = ATOMIC_INIT (0);
 
 #ifdef CONFIG_DASD_DYNAMIC
+
 /* SECTION: managing dynamic configuration of dasd_driver */
 
-static struct list_head dasd_devreg_head = LIST_HEAD_INIT(dasd_devreg_head);
+static struct list_head dasd_devreg_head = LIST_HEAD_INIT (dasd_devreg_head);
 
-/* 
+/*
  * function: dasd_create_devreg
  * creates a dasd_devreg_t related to a devno
  */
@@ -131,7 +171,7 @@
 	return r;
 }
 
-/* 
+/*
  * function: dasd_destroy_devreg
  * destroys the dasd_devreg_t given as argument
  */
@@ -149,44 +189,44 @@
 static int dasd_probeonly = 1;	/* is true, when probeonly mode is active */
 static int dasd_autodetect = 1;	/* is true, when autodetection is active */
 
-/* dasd_range_t are used for ordering the DASD devices */
-typedef struct dasd_range_t {
-	unsigned int from;	/* first DASD in range */
-	unsigned int to;	/* last DASD in range */
-	char discipline[4];	/* placeholder to force discipline */
-	struct dasd_range_t *next;	/* next one in linked list */
-} dasd_range_t;
-
-static dasd_range_t *dasd_range_head = NULL;	/* anchor for list of ranges */
+static dasd_range_t dasd_range_head =
+    { list:LIST_HEAD_INIT (dasd_range_head.list) };
 static spinlock_t range_lock = SPIN_LOCK_UNLOCKED;
-static spinlock_t dasd_open_count_lock;
 
-/* 
+/*
  * function: dasd_create_range
- * creates a dasd_range_t according to the arguments 
+ * creates a dasd_range_t according to the arguments
  * FIXME: no check is performed for reoccurrence of a devno
  */
 static inline dasd_range_t *
-dasd_create_range (int from, int to)
+dasd_create_range (int from, int to, int features)
 {
 	dasd_range_t *range = NULL;
+        int i;
+
+	if ( from > to ) {
+                printk (KERN_WARNING PRINTK_HEADER "Adding device range %04X-%04X: range invalid, ignoring.\n",from,to);
+		return NULL;
+	}
+	for (i=from;i<=to;i++) {
+                if (dasd_device_from_devno(i)) {
+                        printk (KERN_WARNING PRINTK_HEADER "device range %04X-%04X: device %04X is already in a range.\n",from,to,i);
+                }
+        }
 	range = (dasd_range_t *) kmalloc (sizeof (dasd_range_t), GFP_KERNEL);
 	if (range == NULL)
 		return NULL;
 	memset (range, 0, sizeof (dasd_range_t));
 	range->from = from;
-	if (to == 0) {		/* single devno ? */
-		range->to = from;
-	} else {
-		range->to = to;
-	}
+        range->to = to;
+        range->features = features;
 	return range;
 }
 
-/* 
+/*
  * function dasd_destroy_range
  * destroy a range allocated wit dasd_crate_range
- * CAUTION: must not be callen in arunning sysztem, because it destroys 
+ * CAUTION: must not be callen in arunning sysztem, because it destroys
  * the mapping of DASDs
  */
 static inline void
@@ -195,68 +235,50 @@
 	kfree (range);
 }
 
-/* 
+/*
  * function: dasd_append_range
- * appends the range given as argument to the list anchored at dasd_range_head. 
+ * appends the range given as argument to the list anchored at dasd_range_head.
  */
 static inline void
 dasd_append_range (dasd_range_t * range)
 {
-	dasd_range_t *temp;
 	long flags;
 
 	spin_lock_irqsave (&range_lock, flags);
-	if (dasd_range_head == NULL) {
-		dasd_range_head = range;
-	} else {
-		for (temp = dasd_range_head;
-		     temp && temp->next;
-		     temp = temp->next) ;
-		temp->next = range;
-	}
+	list_add_tail (&range->list, &dasd_range_head.list);
 	spin_unlock_irqrestore (&range_lock, flags);
 }
 
 /*
  * function dasd_dechain_range
  * removes a range from the chain of ranges
- * CAUTION: must not be called in a running system because it destroys 
+ * CAUTION: must not be called in a running system because it destroys
  * the mapping of devices
  */
 static inline void
 dasd_dechain_range (dasd_range_t * range)
 {
-	dasd_range_t *temp, *prev = NULL;
 	unsigned long flags;
 
 	spin_lock_irqsave (&range_lock, flags);
-	for (temp = dasd_range_head; temp != NULL; temp = temp->next) {
-		if (temp == range)
-			break;
-		prev = temp;
-	}
-	if (!temp)
-		BUG ();
-	if (prev) {
-		prev->next = temp->next;
-	} else {
-		dasd_range_head = temp->next;
-	}
+	list_del (&range->list);
 	spin_unlock_irqrestore (&range_lock, flags);
 }
 
-/* 
+/*
  * function: dasd_add_range
  * creates a dasd_range_t according to the arguments and
  * appends it to the list of ranges
- * additionally a devreg_t is created and added to the list of devregs 
+ * additionally a devreg_t is created and added to the list of devregs
  */
-static inline dasd_range_t*
-dasd_add_range (int from, int to)
+static inline dasd_range_t *
+dasd_add_range (int from, int to, int features)
 {
 	dasd_range_t *range;
-	range = dasd_create_range (from, to);
-	if (!range) return NULL;
+
+	range = dasd_create_range (from, to, features);
+	if (!range)
+		return NULL;
 
 	dasd_append_range (range);
 #ifdef CONFIG_DASD_DYNAMIC
@@ -266,14 +288,14 @@
 		for (i = range->from; i <= range->to; i++) {
 			dasd_devreg_t *reg = dasd_create_devreg (i);
 			s390_device_register (&reg->devreg);
-                        list_add(&reg->list,&dasd_devreg_head);
+			list_add (&reg->list, &dasd_devreg_head);
 		}
 	}
 #endif				/* CONFIG_DASD_DYNAMIC */
 	return range;
 }
 
-/* 
+/*
  * function: dasd_remove_range
  * removes a range and the corresponding devregs from all of the chains
  * CAUTION: must not be called in a running system because it destroys
@@ -287,10 +309,10 @@
 	{
 		int i;
 		for (i = range->from; i <= range->to; i++) {
-                        struct list_head *l;
-                        dasd_devreg_t *reg = NULL;
+			struct list_head *l;
+			dasd_devreg_t *reg = NULL;
 			list_for_each (l, &dasd_devreg_head) {
-                                reg = list_entry(l,dasd_devreg_t,list);
+				reg = list_entry (l, dasd_devreg_t, list);
 				if (reg->devreg.flag == DEVREG_TYPE_DEVNO &&
 				    reg->devreg.ci.devno == i &&
 				    reg->devreg.oper_func == dasd_oper_handler)
@@ -319,9 +341,11 @@
 	dasd_range_t *temp;
 	int devindex = 0;
 	unsigned long flags;
+	struct list_head *l;
 
 	spin_lock_irqsave (&range_lock, flags);
-	for (temp = dasd_range_head; temp; temp = temp->next) {
+	list_for_each (l, &dasd_range_head.list) {
+		temp = list_entry (l, dasd_range_t, list);
 		if (devno >= temp->from && devno <= temp->to) {
 			spin_unlock_irqrestore (&range_lock, flags);
 			return devindex + devno - temp->from;
@@ -332,19 +356,43 @@
 	return -ENODEV;
 }
 
+/*
+ * function: dasd_devno_from_devindex
+ */
+static int
+dasd_devno_from_devindex (int devindex)
+{
+	dasd_range_t *temp;
+	unsigned long flags;
+	struct list_head *l;
+
+	spin_lock_irqsave (&range_lock, flags);
+	list_for_each (l, &dasd_range_head.list) {
+		temp = list_entry (l, dasd_range_t, list);
+                if ( devindex < temp->to - temp->from + 1) {
+			spin_unlock_irqrestore (&range_lock, flags);
+			return temp->from + devindex;
+		}
+		devindex -= temp->to - temp->from + 1;
+	}
+	spin_unlock_irqrestore (&range_lock, flags);
+	return -ENODEV;
+}
+
 /* SECTION: parsing the dasd= parameter of the parmline/insmod cmdline */
 
-/* 
+/*
  * char *dasd[] is intended to hold the ranges supplied by the dasd= statement
  * it is named 'dasd' to directly be filled by insmod with the comma separated
  * strings when running as a module.
  * a maximum of 256 ranges can be supplied, as the parmline is limited to
  * <1024 Byte anyway.
  */
-char *dasd[256] = {NULL,};
+char *dasd[256];
+char *dasd_disciplines[8];
 
 #ifndef MODULE
-/* 
+/*
  * function: dasd_split_parm_string
  * splits the parmline given to the kernel into comma separated strings
  * which are filled into the 'dasd[]' array, to be parsed later on
@@ -354,7 +402,7 @@
 {
 	char *tmp = str;
 	int count = 0;
-	do {
+	while (tmp != NULL && *tmp != '\0') {
 		char *end;
 		int len;
 		end = strchr (tmp, ',');
@@ -366,27 +414,28 @@
 			end++;
 		}
 		dasd[count] = kmalloc (len * sizeof (char), GFP_ATOMIC);
-		if (dasd == NULL) {
+		if (dasd[count] == NULL) {
 			printk (KERN_WARNING PRINTK_HEADER
-			    "can't store dasd= parameter no %d\n", count + 1);
+				"can't store dasd= parameter no %d\n",
+				count + 1);
 			break;
 		}
 		memset (dasd[count], 0, len * sizeof (char));
 		memcpy (dasd[count], tmp, len * sizeof (char));
 		count++;
 		tmp = end;
-	} while (tmp != NULL && *tmp != '\0');
+	};
 }
 
-/* 
+/*
  * dasd_parm_string holds a concatenated version of all 'dasd=' parameters
- * supplied in the parmline, which is later to be split by 
+ * supplied in the parmline, which is later to be split by
  * dasd_split_parm_string
  * FIXME: why first concatenate then split ?
  */
-static char dasd_parm_string[1024] __initdata = {0,};
+static char dasd_parm_string[1024] __initdata = { 0, };
 
-/* 
+/*
  * function: dasd_setup
  * is invoked for any single 'dasd=' parameter supplied in the parmline
  * it merges all the arguments into dasd_parm_string
@@ -400,44 +449,90 @@
 	}
 	strcat (dasd_parm_string, str);
 }
-/* 
- * function: dasd_call_setup 
+
+/*
+ * function: dasd_call_setup
  * is the 2.4 version of dasd_setup and
  * is invoked for any single 'dasd=' parameter supplied in the parmline
  */
 int __init
 dasd_call_setup (char *str)
 {
-        int dummy;
-        dasd_setup(str,&dummy);
+	int dummy;
+	dasd_setup (str, &dummy);
+	return 1;
+}
+
+int __init
+dasd_disciplines_setup (char *str)
+{
 	return 1;
 }
 
 __setup ("dasd=", dasd_call_setup);
+__setup ("dasd_disciplines=", dasd_disciplines_setup);
 
-#endif /* MODULE */
+#endif				/* MODULE */
 
-/* 
+/*
  * function: dasd_strtoul
  * provides a wrapper to simple_strtoul to strip leading '0x' and
  * interpret any argument to dasd=[range,...] as hexadecimal
  */
 static inline int
-dasd_strtoul (char *str, char **stra)
+dasd_strtoul (char *str, char **stra, int* features)
 {
-	char *temp = str;
-	int val;
-	if (*temp == '0') {
-		temp++;		/* strip leading zero */
-		if (*temp == 'x')
-			temp++;	/* strip leading x */
-	}
-	val = simple_strtoul (temp, &temp, 16);		/* interpret anything as hex */
-	*stra = temp;
-	return val;
+        char *temp=str;
+        char *buffer;
+        int val,i,start;
+
+        buffer=(char*)kmalloc((strlen(str)+1)*sizeof(char),GFP_ATOMIC);
+        if (buffer==NULL) {
+            printk (KERN_WARNING PRINTK_HEADER
+                    "can't parse dasd= parameter %s due to low memory\n",
+                    str);
+        }
+
+        /* remove leading '0x' */
+        if (*temp == '0') {
+                temp++;         /* strip leading zero */
+                if (*temp == 'x')
+                        temp++; /* strip leading x */
+        }
+
+        /* copy device no to buffer and convert to decimal */
+        for (i=0;isxdigit(temp[i]);i++)
+                buffer[i]=temp[i];
+        buffer[i]='\0';
+
+        val = simple_strtoul (buffer, &buffer, 16);
+
+        /* check for features - e.g. (ro) ; the '\0', ')' and '-' stops check */
+        if (temp[i]=='(') {
+
+                while (temp[i]!='\0' && temp[i]!=')'&&temp[i]!='-') { 
+                        start=++i;      
+        
+                        /* move next feature to buffer */
+                        for (;temp[i]!='\0'&&temp[i]!=':'&&temp[i]!=')'&&temp[i]!='-';i++)
+                                buffer[i-start]=temp[i];
+                        buffer[i-start]='\0';
+
+                        if (strlen(buffer)) { 
+                                if (!strcmp(buffer,"ro")) { /* handle 'ro' feature */
+                                        (*features) |= DASD_FEATURE_READONLY;
+                                        break;
+                                }
+                                printk (KERN_WARNING PRINTK_HEADER 
+                                        "unsupported feature: %s, ignoring setting",buffer);
+                        }
+                }
+        }
+        *stra = temp+i;
+        return val;
 }
 
-/* 
+/*
  * function: dasd_parse
  * examines the strings given in the string array str and
  * creates and adds the ranges to the apropriate lists
@@ -447,6 +542,7 @@
 {
 	char *temp;
 	int from, to;
+        int features = 0;
 
 	if (*str) {
 		/* turn off probeonly mode, if any dasd parameter is present */
@@ -468,12 +564,13 @@
 		} else {
 			/* turn off autodetect mode, if any range is present */
 			dasd_autodetect = 0;
-			from = dasd_strtoul (temp, &temp);
+			from = dasd_strtoul (temp, &temp, &features);
+                        to = from;
 			if (*temp == '-') {
 				temp++;
-				to = dasd_strtoul (temp, &temp);
+				to = dasd_strtoul (temp, &temp, &features);
 			}
-			dasd_add_range (from, to);
+			dasd_add_range (from, to ,features);
 		}
 		str++;
 	}
@@ -483,18 +580,16 @@
 
 static spinlock_t dasd_major_lock = SPIN_LOCK_UNLOCKED;
 
-static major_info_t dasd_major_info[] =
-{
+static major_info_t dasd_major_info[] = {
 	{
-		list: LIST_HEAD_INIT(dasd_major_info[1].list )
-	},
+	      list:LIST_HEAD_INIT (dasd_major_info[1].list)
+	 },
 	{
-		list: LIST_HEAD_INIT(dasd_major_info[0].list ),
-		gendisk: {
-                        INIT_GENDISK(94,DASD_NAME,DASD_PARTN_BITS,DASD_PER_MAJOR)
-		},
-		flags : DASD_MAJOR_INFO_IS_STATIC
-	}
+	      list:LIST_HEAD_INIT (dasd_major_info[0].list),
+	      gendisk:{
+	  INIT_GENDISK (94, DASD_NAME, DASD_PARTN_BITS, DASD_PER_MAJOR)
+	  },
+      flags:DASD_MAJOR_INFO_IS_STATIC}
 };
 
 static major_info_t *
@@ -504,17 +599,21 @@
 
 	major_info = kmalloc (sizeof (major_info_t), GFP_KERNEL);
 	if (major_info) {
-		static major_info_t temp_major_info =
-		{
-			gendisk: {
-				INIT_GENDISK(0,DASD_NAME,DASD_PARTN_BITS,DASD_PER_MAJOR)
-			}
+		static major_info_t temp_major_info = {
+			gendisk:{
+				 INIT_GENDISK (0, DASD_NAME, DASD_PARTN_BITS,
+					       DASD_PER_MAJOR)}
 		};
 		memcpy (major_info, &temp_major_info, sizeof (major_info_t));
 	}
 	return major_info;
 }
 
+/*
+ * register major number
+ * is called with the 'static' major_info during init of the driver or 'NULL' to
+ * allocate an additional dynamic major.
+ */
 static int
 dasd_register_major (major_info_t * major_info)
 {
@@ -522,67 +621,86 @@
 	int major;
 	unsigned long flags;
 
+        /* allocate dynamic major */
 	if (major_info == NULL) {
 		major_info = get_new_major_info ();
 		if (!major_info) {
 			printk (KERN_WARNING PRINTK_HEADER
 				"Cannot get memory to allocate another major number\n");
 			return -ENOMEM;
-		} else {
-			printk (KERN_INFO PRINTK_HEADER
-				"Created another major number\n");
 		}
 	}
+
 	major = major_info->gendisk.major;
-	major_info->gendisk.de_arr = (devfs_handle_t*)
-		kmalloc(DASD_PER_MAJOR * sizeof(devfs_handle_t), GFP_KERNEL);
-	memset(major_info->gendisk.de_arr,0,DASD_PER_MAJOR * sizeof(devfs_handle_t));
-	major_info->gendisk.flags = (char*)
-		kmalloc(DASD_PER_MAJOR * sizeof(char), GFP_KERNEL);
-	memset(major_info->gendisk.flags,0,DASD_PER_MAJOR * sizeof(char));
 
+        /* init devfs array */
+	major_info->gendisk.de_arr = (devfs_handle_t *)
+	    kmalloc (DASD_PER_MAJOR * sizeof (devfs_handle_t), GFP_KERNEL);
+	memset (major_info->gendisk.de_arr, 0,
+		DASD_PER_MAJOR * sizeof (devfs_handle_t));
+
+        /* init flags */
+	major_info->gendisk.flags = (char *)
+	    kmalloc (DASD_PER_MAJOR * sizeof (char), GFP_KERNEL);
+	memset (major_info->gendisk.flags, 0, DASD_PER_MAJOR * sizeof (char));
+
+        /* register blockdevice */
 	rc = devfs_register_blkdev (major, DASD_NAME, &dasd_device_operations);
 	if (rc < 0) {
 		printk (KERN_WARNING PRINTK_HEADER
-		      "Cannot register to major no %d, rc = %d\n", major, rc);
-		return rc;
+			"Cannot register to major no %d, rc = %d\n", major, rc);
+		goto out_reg_blkdev; 
 	} else {
 		major_info->flags |= DASD_MAJOR_INFO_REGISTERED;
 	}
-        /* Insert the new major info into dasd_major_info if needed */
-        if (!(major_info->flags & DASD_MAJOR_INFO_IS_STATIC) ){
-                spin_lock_irqsave (&dasd_major_lock, flags);
-		list_add_tail(&major_info->list,&dasd_major_info[0].list);
+
+	/* Insert the new major info into dasd_major_info if needed (dynamic major) */
+	if (!(major_info->flags & DASD_MAJOR_INFO_IS_STATIC)) {
+		spin_lock_irqsave (&dasd_major_lock, flags);
+		list_add_tail (&major_info->list, &dasd_major_info[0].list);
 		spin_unlock_irqrestore (&dasd_major_lock, flags);
-        }
+	}
+
 	if (major == 0) {
 		major = rc;
 		rc = 0;
 	}
-	major_info->dasd_device = (dasd_device_t **) kmalloc (DASD_PER_MAJOR * sizeof (dasd_device_t *),
-							      GFP_ATOMIC);
+
+        /* init array of devices */
+	major_info->dasd_device =
+	    (dasd_device_t **) kmalloc (DASD_PER_MAJOR *
+					sizeof (dasd_device_t *), GFP_ATOMIC);
 	if (!major_info->dasd_device)
-		goto out_devfs;
-	memset (major_info->dasd_device, 0, DASD_PER_MAJOR * sizeof (dasd_device_t *));
+		goto out_devices;
+	memset (major_info->dasd_device, 0,
+		DASD_PER_MAJOR * sizeof (dasd_device_t *));
+
+        /* init blk_size */
 	blk_size[major] =
 	    (int *) kmalloc ((1 << MINORBITS) * sizeof (int), GFP_ATOMIC);
 	if (!blk_size[major])
-		goto out_dasd_device;
+		goto out_blk_size;
 	memset (blk_size[major], 0, (1 << MINORBITS) * sizeof (int));
+
+        /* init blksize_size */
 	blksize_size[major] =
 	    (int *) kmalloc ((1 << MINORBITS) * sizeof (int), GFP_ATOMIC);
 	if (!blksize_size[major])
-		goto out_blk_size;
+		goto out_blksize_size;
 	memset (blksize_size[major], 0, (1 << MINORBITS) * sizeof (int));
+
+        /* init_hardsect_size */
 	hardsect_size[major] =
 	    (int *) kmalloc ((1 << MINORBITS) * sizeof (int), GFP_ATOMIC);
 	if (!hardsect_size[major])
-		goto out_blksize_size;
+		goto out_hardsect_size;
 	memset (hardsect_size[major], 0, (1 << MINORBITS) * sizeof (int));
+
+        /* init max_sectors */
 	max_sectors[major] =
 	    (int *) kmalloc ((1 << MINORBITS) * sizeof (int), GFP_ATOMIC);
 	if (!max_sectors[major])
-		goto out_hardsect_size;
+		goto out_max_sectors;
 	memset (max_sectors[major], 0, (1 << MINORBITS) * sizeof (int));
 
 	/* finally do the gendisk stuff */
@@ -590,29 +708,65 @@
 					    sizeof (struct hd_struct),
 					    GFP_ATOMIC);
 	if (!major_info->gendisk.part)
-		goto out_max_sectors;
+		goto out_gendisk;
 	memset (major_info->gendisk.part, 0, (1 << MINORBITS) *
 		sizeof (struct hd_struct));
 
-	INIT_BLK_DEV(major,do_dasd_request,dasd_get_queue,NULL);
+	INIT_BLK_DEV (major, do_dasd_request, dasd_get_queue, NULL);
 
 	major_info->gendisk.major = major;
 	major_info->gendisk.next = gendisk_head;
 	major_info->gendisk.sizes = blk_size[major];
 	gendisk_head = &major_info->gendisk;
 	return major;
-out_max_sectors:
-	kfree(max_sectors[major]);
-out_hardsect_size:
-	kfree(hardsect_size[major]);
-out_blksize_size:
-	kfree(blksize_size[major]);
-out_blk_size:
-	kfree(blk_size[major]);
-out_dasd_device:
-	kfree(major_info->dasd_device);
-out_devfs:
-	devfs_unregister_blkdev(major, DASD_NAME);
+
+        /* error handling - free the prior allocated memory */  
+      out_gendisk:
+	kfree (max_sectors[major]);
+	max_sectors[major] = NULL;
+
+      out_max_sectors:
+	kfree (hardsect_size[major]);
+	hardsect_size[major] = NULL;
+
+      out_hardsect_size:
+	kfree (blksize_size[major]);
+	blksize_size[major] = NULL;
+
+      out_blksize_size:
+	kfree (blk_size[major]);
+	blk_size[major] = NULL;
+
+      out_blk_size:
+	kfree (major_info->dasd_device);
+
+      out_devices:
+	/* Delete the new major info from dasd_major_info list if needed (dynamic) +*/
+	if (!(major_info->flags & DASD_MAJOR_INFO_IS_STATIC)) {
+		spin_lock_irqsave (&dasd_major_lock, flags);
+		list_del (&major_info->list);
+		spin_unlock_irqrestore (&dasd_major_lock, flags);
+	}
+
+        /* unregister blockdevice */
+	rc = devfs_unregister_blkdev (major, DASD_NAME);
+	if (rc < 0) {
+		printk (KERN_WARNING PRINTK_HEADER
+			"Unable to unregister from major no %d, rc = %d\n", major,
+			rc);
+	} else {
+		major_info->flags &= ~DASD_MAJOR_INFO_REGISTERED;
+	}
+
+      out_reg_blkdev:
+        kfree (major_info->gendisk.flags);
+        kfree (major_info->gendisk.de_arr);
+
+	/* Delete the new major info from dasd_major_info if needed */
+	if (!(major_info->flags & DASD_MAJOR_INFO_IS_STATIC)) {
+		kfree (major_info);
+	}
+
 	return -ENOMEM;
 }
 
@@ -628,11 +782,7 @@
 		return -EINVAL;
 	}
 	major = major_info->gendisk.major;
-        INIT_BLK_DEV(major,NULL,NULL,NULL);
-	blk_size[major] = NULL;
-	blksize_size[major] = NULL;
-	hardsect_size[major] = NULL;
-	max_sectors[major] = NULL;
+	INIT_BLK_DEV (major, NULL, NULL, NULL);
 
 	/* do the gendisk stuff */
 	for (dd = gendisk_head; dd; dd = dd->next) {
@@ -648,34 +798,43 @@
 	if (dd == NULL) {
 		return -ENOENT;
 	}
-	kfree (major_info->gendisk.de_arr);
-	kfree (major_info->gendisk.flags);
 	kfree (major_info->dasd_device);
+	kfree (major_info->gendisk.part);
+
 	kfree (blk_size[major]);
 	kfree (blksize_size[major]);
 	kfree (hardsect_size[major]);
 	kfree (max_sectors[major]);
-	kfree (major_info->gendisk.part);
+
+	blk_size[major]      = NULL;
+	blksize_size[major]  = NULL;
+	hardsect_size[major] = NULL;
+	max_sectors[major]   = NULL;
 
 	rc = devfs_unregister_blkdev (major, DASD_NAME);
 	if (rc < 0) {
 		printk (KERN_WARNING PRINTK_HEADER
-		  "Cannot unregister from major no %d, rc = %d\n", major, rc);
+			"Cannot unregister from major no %d, rc = %d\n", major,
+			rc);
 		return rc;
 	} else {
 		major_info->flags &= ~DASD_MAJOR_INFO_REGISTERED;
 	}
-        /* Delete the new major info from dasd_major_info if needed */
-        if (!(major_info->flags & DASD_MAJOR_INFO_IS_STATIC)) {
-                spin_lock_irqsave (&dasd_major_lock, flags);
-		list_del(&major_info->list);
+
+	kfree (major_info->gendisk.flags);
+	kfree (major_info->gendisk.de_arr);
+
+	/* Delete the new major info from dasd_major_info if needed */
+	if (!(major_info->flags & DASD_MAJOR_INFO_IS_STATIC)) {
+		spin_lock_irqsave (&dasd_major_lock, flags);
+		list_del (&major_info->list);
 		spin_unlock_irqrestore (&dasd_major_lock, flags);
 		kfree (major_info);
-        }
+	}
 	return rc;
 }
 
-/* 
+/*
  * function: dasd_device_from_kdev
  * finds the device structure corresponding to the kdev supplied as argument
  * in the major_info structures and returns it or NULL when not found
@@ -688,9 +847,9 @@
 	unsigned long flags;
 
 	spin_lock_irqsave (&dasd_major_lock, flags);
-	list_for_each(l,&dasd_major_info[0].list) {
-		major_info = list_entry(l,major_info_t,list);
-		if ( major_info->gendisk.major == MAJOR(kdev) )
+	list_for_each (l, &dasd_major_info[0].list) {
+		major_info = list_entry (l, major_info_t, list);
+		if (major_info->gendisk.major == MAJOR (kdev))
 			break;
 	}
 	spin_unlock_irqrestore (&dasd_major_lock, flags);
@@ -699,10 +858,10 @@
 	return NULL;
 }
 
-/* 
+/*
  * function: dasd_device_from_devno
- * finds the address of the device structure corresponding to the devno 
- * supplied as argument in the major_info structures and returns 
+ * finds the address of the device structure corresponding to the devno
+ * supplied as argument in the major_info structures and returns
  * it or NULL when not found
  */
 static inline dasd_device_t **
@@ -714,8 +873,8 @@
 	unsigned long flags;
 
 	spin_lock_irqsave (&dasd_major_lock, flags);
-	list_for_each(l,&dasd_major_info[0].list) {
-		major_info = list_entry(l,major_info_t,list);
+	list_for_each (l, &dasd_major_info[0].list) {
+		major_info = list_entry (l, major_info_t, list);
 		if (devindex < DASD_PER_MAJOR) {
 			spin_unlock_irqrestore (&dasd_major_lock, flags);
 			return &major_info->dasd_device[devindex];
@@ -726,70 +885,105 @@
 	return NULL;
 }
 
+/*
+ * function: dasd_features_from_devno
+ * finds the device range corresponding to the devno
+ * supplied as argument in the major_info structures and returns
+ * the features set for it
+ */
+
+static int
+dasd_features_from_devno (int devno)
+{
+	dasd_range_t *temp;
+	int devindex = 0;
+	unsigned long flags;
+	struct list_head *l;
+
+	spin_lock_irqsave (&range_lock, flags);
+	list_for_each (l, &dasd_range_head.list) {
+		temp = list_entry (l, dasd_range_t, list);
+		if (devno >= temp->from && devno <= temp->to) {
+			spin_unlock_irqrestore (&range_lock, flags);
+			return temp->features;
+		}
+		devindex += temp->to - temp->from + 1;
+	}
+	spin_unlock_irqrestore (&range_lock, flags);
+	return -ENODEV;
+}
+
+
+
 /* SECTION: managing dasd disciplines */
 
 /* anchor and spinlock for list of disciplines */
-static dasd_discipline_t *dasd_disciplines;
+static struct list_head dasd_disc_head = LIST_HEAD_INIT(dasd_disc_head);
 static spinlock_t discipline_lock = SPIN_LOCK_UNLOCKED;
 
-/* 
- * function dasd_discipline_enq 
+/*
+ * function dasd_discipline_enq
  * chains the discpline given as argument to the head of disiplines
  * head chaining policy is required to allow module disciplines to
  * be preferred against those, who are statically linked
  */
-void
+static inline void
 dasd_discipline_enq (dasd_discipline_t * d)
 {
-	spin_lock (&discipline_lock);
-	d->next = dasd_disciplines;
-	dasd_disciplines = d;
-	spin_unlock (&discipline_lock);
+    list_add(&d->list, &dasd_disc_head);
 }
 
-/* 
- * function dasd_discipline_deq 
+/*
+ * function dasd_discipline_deq
  * removes the discipline given as argument from the list of disciplines
  */
-int
+static inline void
 dasd_discipline_deq (dasd_discipline_t * d)
 {
-	int rc = 0;
-	spin_lock (&discipline_lock);
-	if (dasd_disciplines == d) {
-		dasd_disciplines = dasd_disciplines->next;
-	} else {
-		dasd_discipline_t *b;
-		b = dasd_disciplines;
-		while (b && b->next != d)
-			b = b->next;
-		if (b != NULL) {
-			b->next = b->next->next;
-		} else {
-			rc = -ENOENT;
-		}
-	}
-	spin_unlock (&discipline_lock);
-	return rc;
+        list_del(&d->list);
+}
+
+void
+dasd_discipline_add (dasd_discipline_t * d)
+{
+        unsigned long flags;
+        MOD_INC_USE_COUNT;
+	spin_lock_irqsave (&discipline_lock,flags);
+        dasd_discipline_enq (d);
+	spin_unlock_irqrestore (&discipline_lock,flags);
+        dasd_enable_ranges (&dasd_range_head, d, DASD_STATE_ONLINE);
+}
+
+void dasd_discipline_del (dasd_discipline_t * d)
+{
+        unsigned long flags;
+	spin_lock_irqsave (&discipline_lock,flags);
+        dasd_disable_ranges(&dasd_range_head, d, DASD_STATE_DEL, 1);
+        dasd_discipline_deq (d);
+	spin_unlock_irqrestore (&discipline_lock,flags);
+        MOD_DEC_USE_COUNT;
 }
 
 static inline dasd_discipline_t *
-dasd_find_discipline (dasd_device_t * device)
+dasd_find_disc (dasd_device_t * device, dasd_discipline_t *d)
 {
-	dasd_discipline_t *temp;
-	for (temp = dasd_disciplines; temp != NULL; temp = temp->next) {
-		if (temp->id_check)
-			if (temp->id_check (&device->devinfo)) {
-				continue;
-                        }
-		if (temp->check_characteristics) {
-			if (temp->check_characteristics (device)) {
-				continue;
-                        }
-		}
-		break;
-	}
-	return temp;
+        dasd_discipline_t *t;
+        struct list_head *l = d ? &d->list : dasd_disc_head.next;
+        do {
+                t = list_entry(l,dasd_discipline_t,list);
+                if ( ( t->id_check == NULL ||
+                       t->id_check (&device->devinfo) == 0 ) &&
+                     ( t->check_characteristics == NULL ||
+                       t->check_characteristics (device) == 0 ) )
+                        break;
+                l = l->next;
+                if ( d || 
+                     l == &dasd_disc_head ) {
+                        t = NULL;
+                        break;
+                }
+         } while ( 1 );
+	return t;
 }
 
 /* SECTION: profiling stuff */
@@ -813,9 +1007,9 @@
 }
 
 /*
- * function dasd_profile_add 
+ * function dasd_profile_add
  * adds the profiling information from the cqr given as argument to the
- * global and device specific profiling information 
+ * global and device specific profiling information
  */
 void
 dasd_profile_add (ccw_req_t * cqr)
@@ -840,8 +1034,10 @@
 		memset (&device->profile, 0, sizeof (dasd_profile_info_t));
 	};
 
-        dasd_global_profile.dasd_io_reqs++;
-        device->profile.dasd_io_reqs++;
+	dasd_global_profile.dasd_io_reqs++;
+	device->profile.dasd_io_reqs++;
+	dasd_global_profile.dasd_io_sects+=sectors;
+	device->profile.dasd_io_sects+=sectors;
 	dasd_profile_add_counter (sectors, dasd_io_secs, device);
 	dasd_profile_add_counter (tottime, dasd_io_times, device);
 	dasd_profile_add_counter (tottimeps, dasd_io_timps, device);
@@ -851,54 +1047,168 @@
 	dasd_profile_add_counter (endtime, dasd_io_time3, device);
 }
 
-/* SECTION: (de)queueing of requests to channel program queues */
 
-/* 
- * function dasd_chanq_enq 
- * appends the cqr given as argument to the queue
- * has to be called with the queue lock (namely the s390_irq_lock) acquired
+/* SECTION: All the gendisk stuff */
+
+
+/* SECTION: Managing wrappers for ccwcache */
+
+/*
+ * function dasd_alloc_request
+ * tries to return space for a channel program of length cplength with
+ * additional data of size datasize.
+ * If the ccwcache cannot fulfill the request it tries the emergeny requests
+ * before giving up finally
+ * FIXME: initialization of ccw_req_t should be done by function of ccwcache
  */
-static inline void
-dasd_chanq_enq (dasd_chanq_t * q, ccw_req_t * cqr)
+ccw_req_t *
+dasd_alloc_request (char *magic, int cplength, int datasize, dasd_device_t* device)
 {
-	if (q->head != NULL) {
-		q->tail->next = cqr;
-	} else
+	ccw_req_t *rv = NULL;
+	int i;
+	unsigned long flags;
+
+	if ((rv = ccw_alloc_request (magic, cplength, datasize)) != NULL) {
+		return rv;
+	}
+	if ((((sizeof (ccw_req_t) + 7) & -8) +
+	     cplength * sizeof (ccw1_t) + datasize) > PAGE_SIZE) {
+		BUG ();
+		}
+        if (device->lowmem_cqr==NULL) {
+                DASD_MESSAGE (KERN_WARNING, device, "Low memory! Using emergency request %p",device->lowmem_ccws);
+                device->lowmem_cqr=device->lowmem_ccws;
+                rv = device->lowmem_ccws;
+		memset (rv, 0, PAGE_SIZE);
+		strncpy ((char *) (&rv->magic), magic, 4);
+		ASCEBC ((char *) (&rv->magic), 4);
+		rv->cplength = cplength;
+		rv->datasize = datasize;
+		rv->data = (void *) ((long) rv + PAGE_SIZE - datasize);
+		rv->cpaddr = (ccw1_t *) ((long) rv + sizeof (ccw_req_t));
+        } else {
+                DASD_MESSAGE (KERN_WARNING, device,"Refusing emergency mem for request NULL, already in use at %p.",device->lowmem_ccws);
+	}
+	return rv;
+}
+
+/*
+ * function dasd_free_request
+ * returns a ccw_req_t to the appropriate cache or emergeny request line
+ */
+void
+dasd_free_request (ccw_req_t * request, dasd_device_t* device)
+{
+#ifdef CONFIG_ARCH_S390X
+    ccw1_t* ccw;
+    /* clear any idals used for chain */
+    ccw=request->cpaddr-1;
+    do {
+            ccw++;
+            if ((ccw->cda < (unsigned long) device->lowmem_idals) || (ccw->cda >= (unsigned long) device->lowmem_idals+PAGE_SIZE))
+                    clear_normalized_cda (ccw);
+            else {
+                    if (device->lowmem_idal_ptr != device->lowmem_idals)
+                            DASD_MESSAGE (KERN_WARNING, device, "Freeing emergency idals from request at %p.",request);
+                    device->lowmem_idal_ptr = device->lowmem_idals;
+                    device->lowmem_cqr=NULL;
+            }
+    } while ((ccw->flags & CCW_FLAG_CC) || (ccw->flags & CCW_FLAG_DC));
+#endif
+    if (request != device->lowmem_ccws) { /* compare to lowmem_ccws to protect usage of lowmem_cqr for IDAL only ! */
+		ccw_free_request (request);
+    } else {
+            DASD_MESSAGE (KERN_WARNING, device,  "Freeing emergency request at %p",request);
+            device->lowmem_cqr=NULL;
+	}
+}
+
+int
+dasd_set_normalized_cda ( ccw1_t * cp, unsigned long address, ccw_req_t* request, dasd_device_t* device )
+{
+#ifdef CONFIG_ARCH_S390X
+	int nridaws;
+        int count = cp->count;
+        
+        if (set_normalized_cda (cp, address)!=-ENOMEM) {
+            return 0;
+        }
+
+        if ((device->lowmem_cqr!=NULL) && (device->lowmem_cqr!=request)) {
+            DASD_MESSAGE (KERN_WARNING, device, "Refusing emergency idals for request %p, memory is already in use for request %p",request,device->lowmem_cqr);
+            return -ENOMEM;
+        }
+        device->lowmem_cqr=request;
+        if (device->lowmem_idal_ptr == device->lowmem_idals) {
+            DASD_MESSAGE (KERN_WARNING,device, "Low memory! Using emergency IDALs for request %p.\n",request);
+        }
+        nridaws = ((address & (IDA_BLOCK_SIZE-1)) + count + 
+		   (IDA_BLOCK_SIZE-1)) >> IDA_SIZE_LOG;
+	if ( device->lowmem_idal_ptr>=device->lowmem_idals + PAGE_SIZE ) {
+		/* Ouch! No Idals left for emergency request */
+		BUG();
+	}
+	cp->flags |= CCW_FLAG_IDA;
+	cp->cda = (__u32)(unsigned long)device->lowmem_idal_ptr;
+        do {
+		*((long*)device->lowmem_idal_ptr) = address;
+		address = (address & -(IDA_BLOCK_SIZE)) + (IDA_BLOCK_SIZE);
+		nridaws --;
+                device->lowmem_idal_ptr += sizeof(unsigned long);
+        } while ( nridaws > 0 );
+#else 
+        cp -> cda = address;
+#endif
+	return 0;
+}
+
+
+/* SECTION: (de)queueing of requests to channel program queues */
+
+/*
+ * function dasd_chanq_enq
+ * appends the cqr given as argument to the queue
+ * has to be called with the queue lock (namely the s390_irq_lock) acquired
+ */
+inline void
+dasd_chanq_enq (dasd_chanq_t * q, ccw_req_t * cqr)
+{
+	if (q->head != NULL) {
+		q->tail->next = cqr;
+	} else
 		q->head = cqr;
 	cqr->next = NULL;
 	q->tail = cqr;
 	check_then_set (&cqr->status, CQR_STATUS_FILLED, CQR_STATUS_QUEUED);
 }
 
-/* 
+/*
  * function dasd_chanq_enq_head
  * chains the cqr given as argument to the queue head
  * has to be called with the queue lock (namely the s390_irq_lock) acquired
  */
-static inline void
+inline void
 dasd_chanq_enq_head (dasd_chanq_t * q, ccw_req_t * cqr)
 {
 	cqr->next = q->head;
 	q->head = cqr;
 	if (q->tail == NULL)
 		q->tail = cqr;
-	check_then_set (&cqr->status,
-		       CQR_STATUS_FILLED,
-		       CQR_STATUS_QUEUED);
+	check_then_set (&cqr->status, CQR_STATUS_FILLED, CQR_STATUS_QUEUED);
 }
 
-/* 
+/*
  * function dasd_chanq_deq
  * dechains the cqr given as argument from the queue
  * has to be called with the queue lock (namely the s390_irq_lock) acquired
  */
-int
+inline void
 dasd_chanq_deq (dasd_chanq_t * q, ccw_req_t * cqr)
 {
 	ccw_req_t *prev;
 
 	if (cqr == NULL)
-                BUG ();
+		BUG ();
 
 	if (cqr == q->head) {
 		q->head = cqr->next;
@@ -910,138 +1220,82 @@
 		while (prev && prev->next != cqr)
 			prev = prev->next;
 		if (prev == NULL)
-			return -ENOENT;
+			return;
 		prev->next = cqr->next;
 		if (prev->next == NULL)
 			q->tail = prev;
 	}
 	cqr->next = NULL;
-	return 0;
 }
 
-/* SECTION: All the gendisk stuff */
+/* SECTION: Managing the device queues etc. */
 
-/* 
- * function dasd_partn_detect
- * calls the function in genhd, which is appropriate to setup a partitioned disk
+/*
+ * function dasd_start_IO
+ * attempts to start the IO and returns an appropriate return code
  */
-static void
-dasd_partn_detect (dasd_device_t * dev)
+int
+dasd_term_IO (ccw_req_t * cqr)
 {
-	major_info_t *major_info = dev->major_info;
-	struct gendisk *dd = &major_info->gendisk;
-	int minor = MINOR (dev->kdev);
-
-	register_disk (dd,
-		       MKDEV (dd->major, minor),
-		       1 << DASD_PARTN_BITS,
-		       &dasd_device_operations,
-		       (dev->sizes.blocks << dev->sizes.s2b_shift));
-}
-
-/* SECTION: Managing wrappers for ccwcache */
-
-/* array and spinlock of emergency requests */
-static ccw_req_t *dasd_emergency_req[DASD_EMERGENCY_REQUESTS];
-static spinlock_t dasd_emergency_req_lock = SPIN_LOCK_UNLOCKED;
+	int rc = 0;
+	dasd_device_t *device = cqr->device;
+	int irq;
+        int retries = 0;
 
-/* 
- * function dasd_init_emergeny_req
- * allocates emergeny requests
- */
-static inline void __init
-dasd_init_emergency_req (void)
-{
-	int i;
-	for (i = 0; i < DASD_EMERGENCY_REQUESTS; i++) {
-		dasd_emergency_req[i] = (ccw_req_t *) get_free_page (GFP_KERNEL);
-		memset (dasd_emergency_req[i], 0, PAGE_SIZE);
+	if (!cqr) {
+		BUG ();
 	}
-}
-
-/* 
- * function dasd_cleanup_emergeny_req
- * tries to free emergeny requests skipping those, which are currently in use
- */
-static inline void
-dasd_cleanup_emergency_req (void)
-{
-	int i;
-	for (i = 0; i < DASD_EMERGENCY_REQUESTS; i++) {
-		if (dasd_emergency_req[i])
-			free_page ((long) (dasd_emergency_req[i]));
-		else
-			printk (KERN_WARNING PRINTK_HEADER
-				"losing page for emergency request in use\n");
+	irq = device->devinfo.irq;
+	if (strncmp ((char *) &cqr->magic, device->discipline->ebcname, 4)) {
+		DASD_MESSAGE (KERN_WARNING, device,
+			      " ccw_req_t 0x%08X magic doesn't match"
+			      " discipline 0x%08X\n",
+			      cqr->magic,
+			      *(unsigned int *) device->discipline->name);
+		return -EINVAL;
 	}
-}
-
-/* 
- * function dasd_alloc_request
- * tries to return space for a channel program of length cplength with
- * additional data of size datasize. 
- * If the ccwcache cannot fulfill the request it tries the emergeny requests
- * before giving up finally
- * FIXME: initialization of ccw_req_t should be done by function of ccwcache
- */
-ccw_req_t *
-dasd_alloc_request (char *magic, int cplength, int datasize)
-{
-	ccw_req_t *rv = NULL;
-	int i;
-	unsigned long flags;
 
-	if ((rv = ccw_alloc_request (magic, cplength, datasize)) != NULL) {
-		return rv;
-	}
-	if ((((sizeof (ccw_req_t) + 7) & -8) + 
-             cplength*sizeof(ccw1_t) + datasize) > PAGE_SIZE) {
-            BUG();
+        while ( retries < 5 ) {
+            if ( retries < 2 )
+                rc = halt_IO(irq, (long)cqr, 
+                             cqr->options | DOIO_WAIT_FOR_INTERRUPT);
+            else
+                rc = clear_IO(irq, (long)cqr, 
+                              cqr->options | DOIO_WAIT_FOR_INTERRUPT);
+            switch (rc) {
+            case 0:
+		break;
+            case -ENODEV:
+		DASD_MESSAGE (KERN_WARNING, device, "%s",
+			      "device gone, retry\n");
+		break;
+            case -EIO:
+		DASD_MESSAGE (KERN_WARNING, device, "%s",
+			      "I/O error, retry\n");
+		break;
+            case -EBUSY:
+		DASD_MESSAGE (KERN_WARNING, device, "%s",
+			      "device busy, retry later\n");
+		break;
+            default:
+		DASD_MESSAGE (KERN_ERR, device,
+			      "line %d unknown RC=%d, please report"
+			      " to linux390@de.ibm.com\n", __LINE__, rc);
+		BUG ();
+		break;
+            }
+            if (rc == 0) {
+		check_then_set (&cqr->status,
+				CQR_STATUS_IN_IO, CQR_STATUS_FAILED);
+                asm volatile ("STCK %0":"=m" (cqr->stopclk));
+                break;
+            }
+            retries ++;
         }
-	spin_lock_irqsave (&dasd_emergency_req_lock, flags);
-	for (i = 0; i < DASD_EMERGENCY_REQUESTS; i++) {
-		if (dasd_emergency_req[i] != NULL) {
-			rv = dasd_emergency_req[i];
-			dasd_emergency_req[i] = NULL;
-			break;
-		}
-	}
-	spin_unlock_irqrestore (&dasd_emergency_req_lock, flags);
-	if (rv) {
-		memset (rv, 0, PAGE_SIZE);
-		rv->cache = (kmem_cache_t *) (dasd_emergency_req + i);
-		strncpy ((char *) (&rv->magic), magic, 4);
-		ASCEBC ((char *) (&rv->magic), 4);
-		rv->cplength = cplength;
-		rv->datasize = datasize;
-		rv->data = (void *) ((long) rv + PAGE_SIZE - datasize);
-		rv->cpaddr = (ccw1_t *) ((long) rv + sizeof (ccw_req_t));
-	} else {
-		panic ("No way to fulfill request for I/O request\n");
-	}
-	return rv;
+	return rc;
 }
 
-/* 
- * function dasd_free_request
- * returns a ccw_req_t to the appropriate cache or emergeny request line
- */
-void
-dasd_free_request (ccw_req_t * request)
-{
-	if ((request->cache >= (kmem_cache_t *) dasd_emergency_req) &&
-	    (request->cache < (kmem_cache_t *) (dasd_emergency_req +
-						DASD_EMERGENCY_REQUESTS))) {
-		*((ccw_req_t **) (request->cache)) = request;
-	} else {
-		ccw_free_request (request);
-	}
-}			
-
-/* SECTION: Managing the device queues etc. */
-
-
-/* 
+/*
  * function dasd_start_IO
  * attempts to start the IO and returns an appropriate return code
  */
@@ -1051,7 +1305,7 @@
 	int rc = 0;
 	dasd_device_t *device = cqr->device;
 	int irq;
-        unsigned long long now;
+	unsigned long long now;
 
 	if (!cqr) {
 		BUG ();
@@ -1066,75 +1320,80 @@
 		return -EINVAL;
 	}
 
-        asm volatile ("STCK %0":"=m" (now));
-        rc = do_IO (irq, cqr->cpaddr, (long) cqr, cqr->lpm, cqr->options);
-        switch (rc) {
-        case 0:
-            break;
-        case -ENODEV:
-                check_then_set (&cqr->status, 
-                               CQR_STATUS_QUEUED, CQR_STATUS_FAILED);
-            break;
-        case -EIO:
-                check_then_set (&cqr->status, 
-                               CQR_STATUS_QUEUED, CQR_STATUS_FAILED);
-            break;
-        case -EBUSY:
-                DASD_MESSAGE (KERN_WARNING, device,"%s",
-                              "device busy, retry later\n");
-                break;
-        default:
-                DASD_MESSAGE (KERN_ERR, device,
-                              "line %d unknown RC=%d, please report"
-                              " to linux390@de.ibm.com\n",
-                              __LINE__, rc);
-                BUG();
-            break;
-        }
+	asm volatile ("STCK %0":"=m" (now));
+	rc = do_IO (irq, cqr->cpaddr, (long) cqr, cqr->lpm, cqr->options);
+	switch (rc) {
+	case 0:
+		break;
+	case -ENODEV:
+		check_then_set (&cqr->status,
+				CQR_STATUS_QUEUED, CQR_STATUS_FAILED);
+		break;
+	case -EIO:
+		check_then_set (&cqr->status,
+				CQR_STATUS_QUEUED, CQR_STATUS_FAILED);
+		break;
+	case -EBUSY:
+		DASD_MESSAGE (KERN_WARNING, device, "%s",
+			      "device busy, retry later\n");
+		break;
+	default:
+		DASD_MESSAGE (KERN_ERR, device,
+			      "line %d unknown RC=%d, please report"
+			      " to linux390@de.ibm.com\n", __LINE__, rc);
+		BUG ();
+		break;
+	}
 	if (rc == 0) {
-                check_then_set (&cqr->status, 
-                               CQR_STATUS_QUEUED, CQR_STATUS_IN_IO);
-                cqr->startclk = now;
+		check_then_set (&cqr->status,
+				CQR_STATUS_QUEUED, CQR_STATUS_IN_IO);
+		cqr->startclk = now;
 	}
 	return rc;
 }
 
-/* 
- * function sleep_on_req
+/*
+ * function dasd_sleep_on_req
  * attempts to start the IO and waits for completion
  * FIXME: replace handmade sleeping by wait_event
  */
-static int
-sleep_on_req (ccw_req_t * req)
+int
+dasd_sleep_on_req (ccw_req_t * req)
 {
 	unsigned long flags;
 	int cs;
 	int rc = 0;
 	dasd_device_t *device = (dasd_device_t *) req->device;
 
+        if ( signal_pending(current) ) {
+                return -ERESTARTSYS;
+        }
 	s390irq_spin_lock_irqsave (device->devinfo.irq, flags);
 	dasd_chanq_enq (&device->queue, req);
-        /* let the bh start the request to keep them in order */
-	dasd_schedule_bh (device); 
-        
+	/* let the bh start the request to keep them in order */
+	dasd_schedule_bh (device);
 	do {
 		s390irq_spin_unlock_irqrestore (device->devinfo.irq, flags);
-                wait_event (device->wait_q,
-                            (((cs=req->status)==CQR_STATUS_DONE)||
-                            (cs==CQR_STATUS_FAILED)));
+		wait_event ( device->wait_q,
+			     (((cs = req->status) == CQR_STATUS_DONE) ||
+			     (cs == CQR_STATUS_FAILED) ||
+                             signal_pending(current)));
 		s390irq_spin_lock_irqsave (device->devinfo.irq, flags);
-		cs = req->status;
+                if ( signal_pending(current) ) {
+                        rc = -ERESTARTSYS;
+		     	if (req->status == CQR_STATUS_IN_IO ) 
+                        	device->discipline->term_IO(req);
+                        break;
+                } else if ( req->status == CQR_STATUS_FAILED) {
+                        rc = -EIO;
+                        break;
+                }
 	} while (cs != CQR_STATUS_DONE && cs != CQR_STATUS_FAILED);
-
 	s390irq_spin_unlock_irqrestore (device->devinfo.irq, flags);
-	if (cs == CQR_STATUS_FAILED) {
-		rc = -EIO;
-	}
 	return rc;
+}				/* end dasd_sleep_on_req */
 
-}				/* end sleep_on_req */
-
-/* 
+/*
  * function dasd_end_request
  * posts the buffer_cache about a finalized request
  * FIXME: for requests splitted to serveral cqrs
@@ -1142,7 +1401,8 @@
 static inline void
 dasd_end_request (struct request *req, int uptodate)
 {
-	while (end_that_request_first (req, uptodate, DASD_NAME)) {}
+	while (end_that_request_first (req, uptodate, DASD_NAME)) {
+	}
 #ifndef DEVICE_NO_RANDOM
 	add_blkdev_randomness (MAJOR (req->rq_dev));
 #endif
@@ -1150,7 +1410,7 @@
 	return;
 }
 
-/* 
+/*
  * function dasd_get_queue
  * returns the queue corresponding to a device behind a kdev
  */
@@ -1158,10 +1418,10 @@
 dasd_get_queue (kdev_t kdev)
 {
 	dasd_device_t *device = dasd_device_from_kdev (kdev);
-	return &device->request_queue;
+	return device->request_queue;
 }
 
-/* 
+/*
  * function dasd_check_expire_time
  * check the request given as argument for expiration
  * and returns 0 if not yet expired, nonzero else
@@ -1173,18 +1433,17 @@
 	int rc = 0;
 
 	asm volatile ("STCK %0":"=m" (now));
-	if ( cqr->expires && 
-             cqr->expires + cqr->startclk <  now) {
-		DASD_MESSAGE (KERN_ERR, ((dasd_device_t*)cqr->device),
+	if (cqr->expires && cqr->expires + cqr->startclk < now) {
+		DASD_MESSAGE (KERN_ERR, ((dasd_device_t *) cqr->device),
 			      "IO timeout 0x%08lx%08lx usecs in req %p\n",
-			      (long) (cqr->expires >> 44), 
-                              (long) (cqr->expires >> 12), cqr);
-		cqr->expires <<=1;
+			      (long) (cqr->expires >> 44),
+			      (long) (cqr->expires >> 12), cqr);
+		cqr->expires <<= 1;
 	}
 	return rc;
 }
 
-/* 
+/*
  * function dasd_finalize_request
  * implemets the actions to perform, when a request is finally finished
  * namely in status CQR_STATUS_DONE || CQR_STATUS_FAILED
@@ -1192,190 +1451,226 @@
 static inline void
 dasd_finalize_request (ccw_req_t * cqr)
 {
-        dasd_device_t *device = cqr->device;
-        
+	dasd_device_t *device = cqr->device;
+
 	asm volatile ("STCK %0":"=m" (cqr->endclk));
 	if (cqr->req) {
-                dasd_end_request (cqr->req, (cqr->status == CQR_STATUS_DONE));
-                dasd_profile_add (cqr);
+		dasd_profile_add (cqr);
+		dasd_end_request (cqr->req, (cqr->status == CQR_STATUS_DONE));
 		/* free request if nobody is waiting on it */
-		dasd_free_request (cqr);
+		dasd_free_request (cqr, cqr->device);
 	} else {
-		/* during format we don't have the request structure */
+                if ( cqr == device->init_cqr && /* bring late devices online */
+                     device->level <= DASD_STATE_ONLINE ) { 
+                        device->timer.function = dasd_enable_single_device; 
+                        device->timer.data     = (unsigned long) device;
+                        device->timer.expires  = jiffies;
+                        add_timer(&device->timer);
+                }
 		/* notify sleeping task about finished postprocessing */
 		wake_up (&device->wait_q);
+                
 	}
 	return;
 }
 
-/* 
+/*
  * function dasd_process_queues
  * transfers the requests on the queue given as argument to the chanq
  * if possible, the request ist started on a fastpath
  */
 static void
-dasd_process_queues (dasd_device_t *device)
+dasd_process_queues (dasd_device_t * device)
 {
-        unsigned long flags;
-        struct request *req;
-        request_queue_t * queue = &device->request_queue;
+	unsigned long flags;
+	struct request *req;
+	request_queue_t *queue = device->request_queue;
 	dasd_chanq_t *qp = &device->queue;
-        int irq = device -> devinfo.irq;
-        ccw_req_t *final_requests= NULL;
-        static int chanq_min_size = DASD_MIN_SIZE_FOR_QUEUE;
-        int chanq_max_size = DASD_CHANQ_MAX_SIZE;
-        ccw_req_t * cqr=NULL,*temp;
-        dasd_erp_postaction_fn_t erp_postaction;
-
-        s390irq_spin_lock_irqsave (irq, flags);
-        /* First we dechain the requests, processed with completed status */
-        while ( qp -> head && 
-                ((qp -> head -> status == CQR_STATUS_DONE) || 
-                 (qp -> head -> status == CQR_STATUS_FAILED) ||
-		 (qp -> head -> status == CQR_STATUS_ERROR) ) ) {
+	int irq = device->devinfo.irq;
+	ccw_req_t *final_requests = NULL;
+	static int chanq_min_size = DASD_MIN_SIZE_FOR_QUEUE;
+	int chanq_max_size = DASD_CHANQ_MAX_SIZE;
+	ccw_req_t *cqr = NULL, *temp;
+	dasd_erp_postaction_fn_t erp_postaction;
+
+
+	s390irq_spin_lock_irqsave (irq, flags);
+
+	/* First we dechain the requests, processed with completed status */
+	while (qp->head &&
+	       ((qp->head->status == CQR_STATUS_DONE  ) ||
+		(qp->head->status == CQR_STATUS_FAILED) ||
+		(qp->head->status == CQR_STATUS_ERROR )   )) {
+
 		dasd_erp_action_fn_t erp_action;
-		ccw_req_t *erp_cqr = NULL;
+		ccw_req_t            *erp_cqr = NULL;
+
 		/*  preprocess requests with CQR_STATUS_ERROR */
-		if (qp -> head -> status == CQR_STATUS_ERROR) {
-			if ((qp -> head -> dstat -> flag & DEVSTAT_HALT_FUNCTION) ||
-			    (qp->head->retries-- == 0 ) ||
-			    (device->discipline->erp_action==NULL) ||
-			    ((erp_action=device->discipline->erp_action(qp->head))==NULL)||
-			    ((erp_cqr = erp_action(qp->head))== NULL)) {
-				check_then_set (&qp->head->status,
+		if (qp->head->status == CQR_STATUS_ERROR) {
+
+                        qp->head->retries--; 
+
+			if (qp->head->dstat->flag & DEVSTAT_HALT_FUNCTION) {
+
+                                check_then_set (&qp->head->status,
                                                 CQR_STATUS_ERROR,
                                                 CQR_STATUS_FAILED);
-                                continue;
+
+                        } else if ((device->discipline->erp_action == NULL                          ) ||
+                                   ((erp_action = device->discipline->erp_action (qp->head)) == NULL)   ) {
+                                
+				erp_cqr = dasd_default_erp_action (qp->head);
+
+			} else { /* call discipline ERP action */
+
+                                erp_cqr = erp_action (qp->head);
+                        }
+                        continue;
+
+		} else if (qp->head->refers) {	/* we deal with a finished ERP */
+
+			if (qp->head->status == CQR_STATUS_DONE) {
+
+                                DASD_MESSAGE (KERN_DEBUG, device, "%s",
+                                              "ERP successful");
 			} else {
-                                if (erp_cqr != qp->head){
-                                        dasd_chanq_enq_head (qp, erp_cqr);
-                                }
-				/* chain of completed requests is now broken */
-				continue; 
+
+                                DASD_MESSAGE (KERN_ERR, device, "%s",
+                                              "ERP unsuccessful");
 			}
-		} else if ( qp -> head -> refers ) { /* we deal with an ERP */
-                        char *uptodatestr;
-                        if ( qp -> head -> status == CQR_STATUS_DONE) {
-                                uptodatestr = "ERP successful";
-                        } else {
-                                uptodatestr = "ERP unsuccessful";
-                        }
-                        
-                        if (device->discipline->erp_postaction == NULL ||
-                            ((erp_postaction = device->discipline->erp_postaction (qp->head)) == NULL)) {
-                                /* 
-                                                         * maybe we shoud set it to FAILED, 
-                                                         * because we are very paranoid ;) 
-                                                         */
-                                erp_postaction = default_erp_postaction;
+
+			if ((device->discipline->erp_postaction == NULL                              )||
+			    ((erp_postaction = device->discipline->erp_postaction (qp->head)) == NULL)  ) {
+
+                                dasd_default_erp_postaction (qp->head);
+
+			} else {  /* call ERP postaction of discipline */
+
+                                erp_postaction (qp->head);
                         }
-                        DASD_MESSAGE (KERN_INFO, device,
-                                      "%s: postaction [<%p>]\n",
-                                      uptodatestr, erp_postaction);
-                        erp_postaction (qp->head);
-                        continue;
-                }
-                
+
+			continue;
+		}
+
 		/* dechain request now */
-                if ( final_requests == NULL )
-                        final_requests = qp -> head;
-                cqr = qp -> head;
-                qp -> head = qp -> head -> next;
+		if (final_requests == NULL)
+			final_requests = qp->head;
+
+		cqr      = qp->head;
+		qp->head = qp->head->next;
+
 		if (qp->head == NULL)
 			qp->tail = NULL;
-        }
-        if ( cqr )
-            cqr -> next = NULL;
-        /* Now we try to fetch requests from the request queue */
-        for (temp = cqr; temp != NULL ;temp=temp-> next )
-                if ( temp ->status == CQR_STATUS_QUEUED)
-                        chanq_max_size --;
-	while ( (! queue->plugged) &&
-		(! list_empty(&queue->queue_head)) && 
-                (req=dasd_next_request(queue)) != NULL) {
-                /* queue empty or certain critera fulfilled -> transfer */
-                if ( qp -> head == NULL ||
-                     chanq_max_size > 0 ||
-                     (req->nr_sectors >= chanq_min_size)) {
-                        ccw_req_t *cqr;
-                        /* relocate request according to partition table */
-                        req->sector += device->major_info->gendisk.part[MINOR (req->rq_dev)].start_sect;
-                        cqr = device->discipline->build_cp_from_req (device, req);
-                        if (cqr == NULL) {
-                                DASD_MESSAGE (KERN_WARNING, device,
-                                              "CCW creation failed on request %p\n", req);
-				/* revert relocation of request */
-                                req->sector -= device->major_info->gendisk.part[MINOR (req->rq_dev)].start_sect;
-                                break; /* terminate request queue loop */
-                                
-                        } 
+
+	} /* end while over completed requests */
+
+	if (cqr)
+		cqr->next = NULL;
+	/* Now clean the requests with final status */
+	while (final_requests) { 
+		temp = final_requests;
+		final_requests = temp->next;
+		dasd_finalize_request (temp);
+	}
+	/* Now we try to fetch requests from the request queue */
+	for (temp = cqr; temp != NULL; temp = temp->next)
+		if (temp->status == CQR_STATUS_QUEUED)
+			chanq_max_size--;
+	while ((atomic_read(&device->plugged) == 0) &&
+               (!queue->plugged) &&
+	       (!list_empty (&queue->queue_head)) &&
+	       (req = dasd_next_request (queue)) != NULL) {
+		/* queue empty or certain critera fulfilled -> transfer */
+		if (qp->head == NULL ||
+		    chanq_max_size > 0 || (req->nr_sectors >= chanq_min_size)) {
+			ccw_req_t *cqr = NULL;
+                        if (is_read_only(device->kdev) && req->cmd == WRITE) {
+                            DASD_MESSAGE (KERN_WARNING, device,
+                                          "rejecting write request %p\n",
+                                          req);
+                                dasd_end_request (req, 0);
+                                dasd_dequeue_request (queue,req);
+                        } else {
+                            /* relocate request according to partition table */
+                            req->sector +=
+                                device->major_info->gendisk.
+                                part[MINOR (req->rq_dev)].start_sect;
+                            cqr = device->discipline->build_cp_from_req (device, req);
+                            if (cqr == NULL) {
+				    DASD_MESSAGE (KERN_WARNING, device,
+                                                  "CCW creation failed on request %p\n",
+                                                  req);
+                                    /* revert relocation of request */
+                                    req->sector -=
+                                        device->major_info->gendisk.
+                                        part[MINOR (req->rq_dev)].start_sect;
+                                    break;	/* terminate request queue loop */
+                                    
+                            }
 #ifdef CONFIG_DYNAMIC_QUEUE_MIN_SIZE
-                        chanq_min_size = (chanq_min_size + req->nr_sectors)>>1; 
-#endif /* CONFIG_DYNAMIC_QUEUE_MIN_SIZE */
-                        dasd_dequeue_request(queue,req);
-			dasd_chanq_enq (qp, cqr);
-                } else { /* queue not empty OR criteria not met */
-                        break; /* terminate request queue loop */
-                }
-        }
-        /* we process the requests with non-final status */
-        if ( qp -> head ) {
-                switch ( qp->head->status ) {
-                case CQR_STATUS_QUEUED:
-                        /* try to start the first I/O that can be started */
-                        if ( device->discipline->start_IO (qp->head) != 0) 
-                                BUG();
-                        break;
-                case CQR_STATUS_IN_IO:
-                        /* Check, if to invoke the missing interrupt handler */
-                        if ( dasd_check_expire_time (qp->head) ) {
-                                /* to be filled with MIH */
+                            chanq_min_size =
+                                (chanq_min_size + req->nr_sectors) >> 1;
+#endif				/* CONFIG_DYNAMIC_QUEUE_MIN_SIZE */
+                            dasd_dequeue_request (queue, req);
+                            dasd_chanq_enq (qp, cqr);
                         }
-                        break;
+		} else {	/* queue not empty OR criteria not met */
+			break;	/* terminate request queue loop */
+		}
+	}
+	/* we process the requests with non-final status */
+	if (qp->head) {
+		switch (qp->head->status) {
+		case CQR_STATUS_QUEUED:
+			/* try to start the first I/O that can be started */
+			if (device->discipline->start_IO == NULL)
+				BUG ();
+                        device->discipline->start_IO(qp->head);
+			break;
+		case CQR_STATUS_IN_IO:
+			/* Check, if to invoke the missing interrupt handler */
+			if (dasd_check_expire_time (qp->head)) {
+				/* to be filled with MIH */
+			}
+			break;
 
-                case CQR_STATUS_PENDING:
-                        /* just wait */
-                        break;
-                default: 
-                        BUG();
-                }
-        }
-        /* Now clean the requests with final status */
-        while ( final_requests ) {
-                cqr = final_requests;
-                final_requests = cqr-> next;
-                dasd_finalize_request( cqr );
-        }
-        s390irq_spin_unlock_irqrestore (irq, flags);
+		case CQR_STATUS_PENDING:
+			/* just wait */
+			break;
+		default:
+			BUG ();
+		}
+	}
+	s390irq_spin_unlock_irqrestore (irq, flags);
 }
 
-/* 
+/*
  * function dasd_run_bh
  * acquires the locks needed and then runs the bh
  */
 static void
-dasd_run_bh (dasd_device_t *device)
+dasd_run_bh (dasd_device_t * device)
 {
 	long flags;
 	spin_lock_irqsave (&io_request_lock, flags);
-	atomic_set(&device->bh_scheduled,0);
+	atomic_set (&device->bh_scheduled, 0);
 	dasd_process_queues (device);
 	spin_unlock_irqrestore (&io_request_lock, flags);
 }
 
-/* 
+/*
  * function dasd_schedule_bh
  * schedules the request_fn to run with next run_bh cycle
  */
 void
-dasd_schedule_bh (dasd_device_t *device)
+dasd_schedule_bh (dasd_device_t * device)
 {
 	/* Protect against rescheduling, when already running */
-        if (atomic_compare_and_swap(0,1,&device->bh_scheduled)) {
-                return;
-        }
+	if (atomic_compare_and_swap (0, 1, &device->bh_scheduled)) {
+		return;
+	}
 
-	INIT_LIST_HEAD(&device->bh_tq.list);
+	INIT_LIST_HEAD (&device->bh_tq.list);
 	device->bh_tq.sync = 0;
 	device->bh_tq.routine = (void *) (void *) dasd_run_bh;
 	device->bh_tq.data = device;
@@ -1385,46 +1680,24 @@
 	return;
 }
 
-/* 
+/*
  * function do_dasd_request
- * is called from ll_rw_blk.c and provides the caller of 
+ * is called from ll_rw_blk.c and provides the caller of
  * dasd_process_queues
  */
-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
 static void
 do_dasd_request (request_queue_t * queue)
 {
-        dasd_device_t *device = (dasd_device_t *)
-                ((long)queue-(long)offsetof (dasd_device_t, request_queue));
+        dasd_device_t *device = (dasd_device_t *)queue->queuedata;
 	dasd_process_queues (device);
 }
-#else
-static void
-do_dasd_request (void)
-{
-	major_info_t *major_info;
-	dasd_device_t *device;
-	int i;
-
-	for (major_info = dasd_major_info;
-	     major_info != NULL;
-	     major_info = major_info->next) {
-		for (i = 0; i < DASD_PER_MAJOR; i++) {
-			device = major_info->dasd_device[i];
-			if (!device)
-				continue;	/* remove indentation level */
-			dasd_process_queues (device);
-		}
-	}
-}
-#endif				/* LINUX_IS_24 */
 
 /*
- * DASD_HANDLE_STATE_CHANGE_PENDING 
+ * DASD_HANDLE_STATE_CHANGE_PENDING
  *
  * DESCRIPTION
  *   Handles the state change pending interrupt.
- *   Search for the device related request queue and check if the first 
+ *   Search for the device related request queue and check if the first
  *   cqr in queue in in status 'CQR_STATUE_PENDING'.
  *   If so the status is set to 'CQR_STATUS_QUEUED' to reactivate
  *   the device.
@@ -1432,43 +1705,42 @@
  *  PARAMETER
  *   stat               device status of state change pending interrupt.
  */
-void 
-dasd_handle_state_change_pending (devstat_t *stat)
+void
+dasd_handle_state_change_pending (devstat_t * stat)
 {
-        dasd_device_t **device_addr;
-        ccw_req_t     *cqr;
+	dasd_device_t **device_addr;
+	ccw_req_t *cqr;
 
 	device_addr = dasd_device_from_devno (stat->devno);
 
-        if (device_addr == NULL) {
-		printk (KERN_INFO PRINTK_HEADER
-                        "unable to find device for state change pending "
-                        "interrupt: devno%04X\n",
-                        stat->devno);
-        } else {
-                /* re-activate first request in queue */
-                cqr = (*device_addr)->queue.head;
+	if (device_addr == NULL) {
 
-                if (cqr->status == CQR_STATUS_PENDING) {
+		printk (KERN_DEBUG PRINTK_HEADER
+			"unable to find device for state change pending "
+			"interrupt: devno%04X\n", stat->devno);
+	} else {
+		/* re-activate first request in queue */
+		cqr = (*device_addr)->queue.head;
 
-                        DASD_MESSAGE (KERN_INFO, (*device_addr),
-                                      "%s",
-                                      "device request queue restarted by "
-                                      "state change pending interrupt\n");
+		if (cqr->status == CQR_STATUS_PENDING) {
 
-                        del_timer(&(*device_addr)->timer);
+			DASD_MESSAGE (KERN_DEBUG, (*device_addr),
+				      "%s",
+				      "device request queue restarted by "
+				      "state change pending interrupt\n");
 
-                        check_then_set(&cqr->status, 
-                                       CQR_STATUS_PENDING, 
-                                       CQR_STATUS_QUEUED);
+			del_timer (&(*device_addr)->timer);
 
-                        dasd_schedule_bh(*device_addr);
+			check_then_set (&cqr->status,
+					CQR_STATUS_PENDING, CQR_STATUS_QUEUED);
 
-                }
-        }
-} /* end dasd_handle_state_change_pending */
+			dasd_schedule_bh (*device_addr);
 
-/* 
+		}
+	}
+}				/* end dasd_handle_state_change_pending */
+
+/*
  * function dasd_int_handler
  * is the DASD driver's default interrupt handler for SSCH-IO
  */
@@ -1476,16 +1748,14 @@
 dasd_int_handler (int irq, void *ds, struct pt_regs *regs)
 {
 	int ip;
-        int devno;
 	ccw_req_t *cqr;
 	dasd_device_t *device;
         unsigned long long now;
-#ifdef ERP_DEBUG
-	static int counter = 0;
-#endif
 	dasd_era_t era = dasd_era_none; /* default is everything is okay */
 	devstat_t *stat = (devstat_t *)ds;
 
+        DASD_DRIVER_DEBUG_EVENT (4, dasd_int_handler,
+                                 "Interrupt: IRQ 0x%x",irq);
         asm volatile ("STCK %0":"=m" (now));
         if (stat == NULL) {
                 BUG();
@@ -1495,145 +1765,148 @@
         if (stat->dstat & (DEV_STAT_ATTENTION | 
                            DEV_STAT_DEV_END   |
                            DEV_STAT_UNIT_EXCEP )) {
-
+                DASD_DRIVER_DEBUG_EVENT (2, dasd_int_handler,
+                                         "State change Interrupt: %04X",
+                                         stat->devno);
                 dasd_handle_state_change_pending (stat);
                 //return; /* TBD */
         }
 
 	ip = stat->intparm;
 	if (!ip) {		/* no intparm: unsolicited interrupt */
-		printk (KERN_INFO PRINTK_HEADER
+                DASD_DRIVER_DEBUG_EVENT (2, dasd_int_handler,
+                                         "Unsolicited Interrupt: %04X",
+                                         stat->devno);
+		printk (KERN_DEBUG PRINTK_HEADER
                         "unsolicited interrupt: irq0x%x devno%04X\n",
                         irq,stat->devno);
 		return;
 	}
 	if (ip & 0x80000001) {
-		printk (KERN_INFO PRINTK_HEADER
+                DASD_DRIVER_DEBUG_EVENT (2, dasd_int_handler,
+                                         "spurious Interrupt: %04X",
+                                         stat->devno);
+		printk (KERN_DEBUG PRINTK_HEADER
                         "spurious interrupt: irq0x%x devno%04X, parm %08x\n",
                         irq,stat->devno,ip);
 		return;
 	}
 	cqr = (ccw_req_t *)(long)ip;
 	device = (dasd_device_t *) cqr->device;
-	if (device == NULL || device != ds-offsetof(dasd_device_t,dev_status)) {
+	if (device == NULL || 
+            device != ds-offsetof(dasd_device_t,dev_status)) {
                 BUG();
 	}
-	devno = device->devinfo.devno;
 	if (device->devinfo.irq != irq) {
                 BUG();
 	}
 	if (strncmp (device->discipline->ebcname, (char *) &cqr->magic, 4)) {
                 BUG();
 	}
-#ifdef ERP_DEBUG
-                if ((++counter % 937 >= 0) &&
-                    (  counter % 937 <= 10) &&
-                    (  counter < 5000    ) &&
-                    (  counter > 2000    )   ){
-                        static int fake_count = 0;
-                        printk ( KERN_INFO PRINTK_HEADER "***********************************************\n");
-                        printk ( KERN_INFO PRINTK_HEADER "Faking I/O error to recover from; cntr=%i / %02X\n",counter,++fake_count);
-                        printk ( KERN_INFO PRINTK_HEADER "***********************************************\n");
+#ifdef ERP_FAKE
+        {
+                static int counter    = 0;
+                static int fake_count = 0;
+
+                if ((++counter % 937 >=   0) &&
+                    (  counter % 937 <=  10) && 
+                    (  counter       < 5000) && 
+                    (  counter       > 2000)   ) {
+
+                        char *sense = stat->ii.sense.data;
+                        
+                        printk (KERN_INFO PRINTK_HEADER
+                                "***********************************************\n");
+                        printk (KERN_INFO PRINTK_HEADER
+                                "Faking I/O error to recover from; cntr=%i / %02X\n",
+                                counter, ++fake_count);
+                        printk (KERN_INFO PRINTK_HEADER
+                                "***********************************************\n");
+
                         era = dasd_era_recover;
-                        stat->flag |= DEVSTAT_FLAG_SENSE_AVAIL;
+                        stat->flag  |= DEVSTAT_FLAG_SENSE_AVAIL;
                         stat->dstat |= 0x02;
-// sense 32
-                        {
-                                char *sense = stat->ii.sense.data;
-                                sense [25] = 0x1D;
-                                sense [27] = 0x00;
-                                //sense [25] = (fake_count % 256); //0x1B;
-                                //sense [27] = 0x00;
-                        }
-// sense 24
-//                        {
-//                                char *sense = stat->ii.sense.data;
-//                                sense [0] = (counter % 0xFF); //0x1B;
-//                                sense [1] = ((counter * 7) % 0xFF); //0x1B;
-//                                sense [2] = (fake_count % 0xFF); //0x1B;
-//                                sense [27] = 0x80;
-//                        }
-
-/*
-                memset(stat->ii.sense.data,0,32);
-                stat->ii.sense.data[2] = 0x06;
-                stat->ii.sense.data[4] = 0x04;
-                stat->ii.sense.data[5] = 0x60;
-                stat->ii.sense.data[6] = 0x41;
-                stat->ii.sense.data[8] = 0xff;
-                stat->ii.sense.data[9] = 0xff;
-                stat->ii.sense.data[15] = 0x05;
-                stat->ii.sense.data[16] = 0x21;
-                stat->ii.sense.data[18] = 0x60;
-                stat->ii.sense.data[19] = 0x3b;
-                stat->ii.sense.data[20] = 0x24;
-                stat->ii.sense.data[21] = 0x61;
-                stat->ii.sense.data[22] = 0x65;
-                stat->ii.sense.data[23] = 0x03;
-                stat->ii.sense.data[24] = 0x04;
-                stat->ii.sense.data[25] = 0x10;
-                stat->ii.sense.data[26] = 0x4e;
-*/
-        }
-#endif
-        /* first of all lets try to find out the appropriate era_action */
-        if ( stat->flag & DEVSTAT_FLAG_SENSE_AVAIL ||
-             stat->dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END) ) {
-                /* anything abnormal ? */
-                if ( device->discipline->examine_error == NULL ||
-                     stat->flag & DEVSTAT_HALT_FUNCTION ) {
-                        era = dasd_era_fatal;
-                } else {
-                        era = device->discipline->examine_error (cqr, stat);
+                        memset(sense,0,32);
+
+                        /* 32 byte sense (byte 27 bit 1 = 0)*/
+                        sense[25] = 0x1D;
+//                        sense [25] = (fake_count % 256); //0x1B;
+
+                        /* 24 byte sense (byte 27 bit 1 = 1)*/
+//                        sense [0] = (counter % 0xFF); //0x1B;
+//                        sense [1] = ((counter * 7) % 0xFF); //0x1B;
+//                        sense [2] = (fake_count % 0xFF); //0x1B;
+//                        sense [27] = 0x80;
                 }
         }
+#endif /* ERP_FAKE */
+        /* first of all lets try to find out the appropriate era_action */
+        DASD_DEVICE_DEBUG_EVENT (4, device," Int: CS/DS 0x%04X",
+                                 ((stat->cstat<<8)|stat->dstat));
+	/* first of all lets try to find out the appropriate era_action */
+	if (stat->flag & DEVSTAT_FLAG_SENSE_AVAIL ||
+	    stat->dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) {
+		/* anything abnormal ? */
+		if (device->discipline->examine_error == NULL ||
+		    stat->flag & DEVSTAT_HALT_FUNCTION) {
+			era = dasd_era_fatal;
+		} else {
+			era = device->discipline->examine_error (cqr, stat);
+		}
+                DASD_DRIVER_DEBUG_EVENT (1, dasd_int_handler," era_code %d",
+                                         era);
+	}
         if ( era == dasd_era_none ) {
-		if (device->level == DASD_DEVICE_LEVEL_ANALYSIS_PENDING)
-			device->level = DASD_DEVICE_LEVEL_ANALYSIS_PREPARED;
                 check_then_set(&cqr->status, 
                                CQR_STATUS_IN_IO, CQR_STATUS_DONE);
                 cqr->stopclk=now;
-                cqr=cqr->next;
 		/* start the next queued request if possible -> fast_io */
-                if (cqr->status == CQR_STATUS_QUEUED) {
-                        if (device->discipline->start_IO (cqr) != 0) {
+                if (cqr->next &&
+                    cqr->next->status == CQR_STATUS_QUEUED) {
+                        if (device->discipline->start_IO (cqr->next) != 0) {
                                 printk (KERN_WARNING PRINTK_HEADER
                                         "Interrupt fastpath failed!\n");
                         } 
                 }
         } else { /* error */
-                if (cqr->dstat == NULL)
-                        cqr->dstat = kmalloc (sizeof (devstat_t), GFP_ATOMIC);
-                if (cqr->dstat) {
-                        memcpy (cqr->dstat, stat, sizeof (devstat_t));
-                } else {
-                        PRINT_ERR ("no memory for dstat...ignoring\n");
-                }
-                /* dump sense data */
-                if (device->discipline &&
-                    device->discipline->dump_sense) {
-                        char *errmsg = device->discipline->dump_sense (device, cqr);
-                        if (errmsg != NULL) {
-                                printk ("Sense data:\n%s", errmsg);
-                                free_page ((unsigned long) errmsg);
-                        } else {
-                                printk (KERN_WARNING PRINTK_HEADER
-                                        "No memory to dump error message\n");
-                        }
-                }
-                switch(era) {
-                case dasd_era_fatal:
-                        check_then_set (&cqr->status,CQR_STATUS_IN_IO,
-                                        CQR_STATUS_FAILED);
-                        break;
-                case dasd_era_recover:
-                        check_then_set (&cqr->status,CQR_STATUS_IN_IO,
-                                        CQR_STATUS_ERROR);
-                        break;
-                default:
-                        BUG();
-                }
-        }                
+		if (cqr->dstat == NULL)
+			cqr->dstat = kmalloc (sizeof (devstat_t), GFP_ATOMIC);
+		if (cqr->dstat) {
+			memcpy (cqr->dstat, stat, sizeof (devstat_t));
+		} else {
+			PRINT_ERR ("no memory for dstat...ignoring\n");
+		}
+
+#ifdef ERP_DEBUG
+		/* dump sense data */
+		if (device->discipline            && 
+                    device->discipline->dump_sense  ) {
+
+                        device->discipline->dump_sense (device, 
+                                                        cqr);
+		}
+#endif
+
+		switch (era) {
+		case dasd_era_fatal:
+			check_then_set (&cqr->status, CQR_STATUS_IN_IO,
+					CQR_STATUS_FAILED);
+			break;
+		case dasd_era_recover:
+			check_then_set (&cqr->status, CQR_STATUS_IN_IO,
+					CQR_STATUS_ERROR);
+			break;
+		default:
+			BUG ();
+		}
+	}
+        if ( cqr == device->init_cqr &&
+             ( cqr->status == CQR_STATUS_DONE ||
+               cqr->status == CQR_STATUS_FAILED )){
+                dasd_state_init_to_ready(device);
+                if ( atomic_read(&dasd_init_pending) == 0)
+                        wake_up (&dasd_init_waitq);
+        }
 	dasd_schedule_bh (device);
 }
 
@@ -1653,26 +1926,39 @@
  *   erp                CQR performing the ERP
  */
 ccw_req_t *
-default_erp_action (ccw_req_t * cqr)
+dasd_default_erp_action (ccw_req_t * cqr)
 {
-	ccw_req_t *erp = dasd_alloc_request ((char *) &cqr->magic, 1, 0);
 
-	printk (KERN_WARNING PRINTK_HEADER
-		"Default ERP called... \n");
+        dasd_device_t *device = cqr->device;
+	ccw_req_t     *erp    = dasd_alloc_request ((char *) &cqr->magic, 1, 0, cqr->device);
+
+	printk (KERN_DEBUG PRINTK_HEADER "Default ERP called... \n");
+
+	if (!erp) {
+
+                DASD_MESSAGE (KERN_ERR, device, "%s",
+                              "Unable to allocate ERP request");
+                
+                check_then_set (&cqr->status,
+                                CQR_STATUS_ERROR,
+                                CQR_STATUS_FAILED);
 
-        if (erp == NULL)
-               return NULL;
+                return cqr;
+	}
 
 	erp->cpaddr->cmd_code = CCW_CMD_TIC;
-	erp->cpaddr->cda = (__u32)(void *)cqr->cpaddr;
-	erp->function = default_erp_action;
-	erp->refers = cqr;
+	erp->cpaddr->cda = (__u32) (void *) cqr->cpaddr;
+	erp->function = dasd_default_erp_action;
+	erp->refers = (unsigned int) (unsigned long) cqr;
 	erp->device = cqr->device;
 	erp->magic = cqr->magic;
 	erp->retries = 16;
 
 	erp->status = CQR_STATUS_FILLED;
 
+        dasd_chanq_enq_head (&device->queue,
+                             erp);
+
 	return erp;
 }
 
@@ -1683,6 +1969,8 @@
  *   Frees all ERPs of the current ERP Chain and set the status
  *   of the original CQR either to CQR_STATUS_DONE if ERP was successful
  *   or to CQR_STATUS_FAILED if ERP was NOT successful.
+ *   NOTE: This function is only called if no discipline postaction
+ *         is available
  *
  * PARAMETER
  *   erp                current erp_head
@@ -1691,96 +1979,63 @@
  *   cqr                pointer to the original CQR
  */
 ccw_req_t *
-default_erp_postaction (ccw_req_t * erp)
+dasd_default_erp_postaction (ccw_req_t *erp)
 {
-	ccw_req_t *cqr = NULL, *free_erp = NULL;
-	dasd_device_t *device = NULL;
-        int success;
-        
-	device = (dasd_device_t *) (erp->device);
+
+	ccw_req_t     *cqr      = NULL, 
+                      *free_erp = NULL;
+	dasd_device_t *device   = erp->device;
+	int           success;
+
+	if (erp->refers   == NULL || 
+            erp->function == NULL   ) {
+
+		BUG ();
+	}
 
 	if (erp->status == CQR_STATUS_DONE)
 		success = 1;
 	else
 		success = 0;
 
-#ifdef ERP_DEBUG
-
-	/* print current erp_chain */
-        printk (KERN_WARNING PRINTK_HEADER
-                "default ERP postaction called for erp chain:\n");
-        {
-                ccw_req_t *temp_erp = NULL;
-                for (temp_erp = erp; temp_erp != NULL; temp_erp = temp_erp->refers){
-                        printk(KERN_WARNING PRINTK_HEADER 
-                               "       erp %p refers to %p with erp function %p\n",
-                               temp_erp,
-                               temp_erp->refers,
-                               temp_erp->function );
-                }
-        }
-
-#endif /* ERP_DEBUG*/
-
-	if (erp->refers == NULL || erp->function == NULL) {
-                BUG();
-	}
-	if (erp->function != default_erp_action) {
-                printk (KERN_WARNING PRINTK_HEADER
-                        "default ERP postaction called ERP action [<%p>]\n",
-                        erp->function);
-	}
 	/* free all ERPs - but NOT the original cqr */
-        
 	while (erp->refers != NULL) {
-                free_erp = erp;
-		erp = erp->refers;
+
+		free_erp = erp;
+		erp      = erp->refers;
+
 		/* remove the request from the device queue */
-		dasd_chanq_deq (&device->queue,	free_erp);
+		dasd_chanq_deq (&device->queue,
+                                free_erp);
+
 		/* free the finished erp request */
-		dasd_free_request (free_erp);
+		dasd_free_request (free_erp, free_erp->device);
 	}
-        
+
 	/* save ptr to original cqr */
 	cqr = erp;
 
-#ifdef ERP_DEBUG
-	printk (KERN_INFO PRINTK_HEADER
-		"default_erp_postaction - left original request = %p \n",cqr);
-#endif /* ERP_DEBUG */
-
 	/* set corresponding status to original cqr */
 	if (success) {
+
 		check_then_set (&cqr->status, 
-                                CQR_STATUS_ERROR, 
-                                CQR_STATUS_DONE);
+                                CQR_STATUS_ERROR,
+				CQR_STATUS_DONE);
 	} else {
+
 		check_then_set (&cqr->status,
-                                CQR_STATUS_ERROR,
+				CQR_STATUS_ERROR, 
                                 CQR_STATUS_FAILED);
 	}
 
-#ifdef ERP_DEBUG
-	/* print current erp_chain */
-	printk (KERN_WARNING PRINTK_HEADER
-		"default ERP postaction finished with remaining chain:\n");
-	{
-		ccw_req_t *temp_erp = NULL;
-		for (temp_erp = cqr; temp_erp != NULL; temp_erp = temp_erp->refers) {
-			printk (KERN_WARNING PRINTK_HEADER
-				" erp %p refers to %p \n",
-				temp_erp, temp_erp->refers);
-		}
-	}
-#endif /* ERP_DEBUG */
-
 	return cqr;
-}				/* end default_erp_postaction */
+
+} /* end default_erp_postaction */
 
 /* SECTION: The helpers of the struct file_operations */
 
-/* 
- * function dasd_format 
+/*
+ * function dasd_format
  * performs formatting of _device_ according to _fdata_
  * Note: The discipline's format_function is assumed to deliver formatting
  * commands to format a single unit of the device. In terms of the ECKD
@@ -1791,196 +2046,114 @@
 dasd_format (dasd_device_t * device, format_data_t * fdata)
 {
 	int rc = 0;
-        int format_done = 0;
-	ccw_req_t *req = NULL;
-	format_data_t temp =
-	{
-		fdata->start_unit,
-		fdata->stop_unit,
-		fdata->blksize,
-		fdata->intensity
-	};
-        
-        spin_lock (&dasd_open_count_lock);
-	if (device->open_count != 1) {
-		DASD_MESSAGE (KERN_INFO, device,
-			      "device is already open %d times",
-			      device->open_count);
-                spin_unlock(&dasd_open_count_lock);
-		return -EINVAL;
-	}
-	if (!device->discipline->format_device) {
-                spin_unlock(&dasd_open_count_lock);
-		return -EINVAL;
-	}
-        device->open_count = -1;
-        spin_unlock (&dasd_open_count_lock);
-	/* downgrade state of the device */
-	dasd_set_device_level (device->devinfo.irq,
-			       DASD_DEVICE_LEVEL_RECOGNIZED,
-			       device->discipline,
-			       0);
-	DASD_MESSAGE (KERN_INFO, device, 
-                      "Starting format from %d to %d (%d B blocks flags %d",
-                      fdata->start_unit,
-                      fdata->stop_unit,
-                      fdata->blksize,
-                      fdata->intensity);
-	/* Invalidate first track */
-	if (fdata->start_unit == DASD_FORMAT_DEFAULT_START_UNIT &&
-	    fdata->stop_unit  == DASD_FORMAT_DEFAULT_STOP_UNIT  &&
-	    fdata->intensity  == DASD_FORMAT_DEFAULT_INTENSITY    ) {
-		format_data_t temp2 =
-		{0, 0, fdata->blksize, 0x04};
-		DASD_MESSAGE (KERN_INFO, device, 
-                              "%s", 
-                              "Invalidating first track...");
-		req = device->discipline->format_device (device, &temp2);
-		if (req) {
-			rc = sleep_on_req (req);
-			dasd_free_request (req);	/* request is no longer used */
-		} else {
-			rc = -EINVAL;
-		}
-		if (rc) {
-                        DASD_MESSAGE (KERN_WARNING, device, 
-                                      "%s",
-                                      "Can't invalidate Track 0\n");
-		} else {
-                        DASD_MESSAGE (KERN_INFO, device, 
-                                      "%s", 
-                                      "...Invalidation complete");
-                }
-		temp.start_unit++;
-	}
-	/* format remainnig tracks of device */
-	while (!rc                                                                 &&
-	       ((req = device->discipline->format_device (device, &temp)) != NULL)   ) {
-                                format_done=1;
-		if ((rc = sleep_on_req (req)) != 0) {
+	int openct = atomic_read (&device->open_count);
 
-
-			DASD_MESSAGE (KERN_WARNING, device,
-				      " Formatting failed with rc = %d\n",
-				      rc);
+	if (openct > 1) {
+		DASD_MESSAGE (KERN_WARNING, device, "%s",
+			      "dasd_format: device is open! expect errors.");
+	}
+	DASD_MESSAGE (KERN_INFO, device,
+		      "formatting units %d to %d (%d B blocks) flags %d",
+		      fdata->start_unit, fdata->stop_unit,
+		      fdata->blksize, fdata->intensity);
+	while ((!rc) && (fdata->start_unit <= fdata->stop_unit)) {
+                ccw_req_t *req;
+                dasd_format_fn_t ffn = device->discipline->format_device;
+		ffn = device->discipline->format_device;
+		if (ffn == NULL)
+			break;
+		req = ffn (device, fdata);
+		if (req == NULL) {
+			rc = -ENOMEM;
 			break;
 		}
-                
-		dasd_free_request (req);	/* request is no longer used */
-		temp.start_unit++;
+		if ((rc = dasd_sleep_on_req (req)) != 0) {
+			DASD_MESSAGE (KERN_WARNING, device,
+				      " Formatting of unit %d failed with rc = %d\n",
+				      fdata->start_unit, rc);
+			break;
+		} 
+		dasd_free_request (req, device);	/* request is no longer used */
+	        if ( signal_pending(current) )
+			break;		
+		fdata->start_unit++;
 	}
-
-	if (!rc         &&
-	    req == NULL   ) {
-		if (fdata->start_unit == DASD_FORMAT_DEFAULT_START_UNIT &&
-		    fdata->stop_unit  == DASD_FORMAT_DEFAULT_STOP_UNIT  &&
-	    	    fdata->intensity  == DASD_FORMAT_DEFAULT_INTENSITY    ) {
-			format_data_t temp2 =
-			{0, 0, fdata->blksize, fdata->intensity};
-			DASD_MESSAGE (KERN_INFO, device, 
-                                      "%s", 
-                                      "Revalidating first track...");
-			req = device->discipline->format_device (device, &temp2);
-			if (req) {
-				rc = sleep_on_req (req);
-				dasd_free_request (req);	/* request is no longer used */
-			} else {
-				rc = -EINVAL;
-			}
-			if (rc) {
-				DASD_MESSAGE (KERN_WARNING, device,
-                                              "%s",
-                                              "Can't revalidate Track 0\n");
-			} else {
-                                DASD_MESSAGE (KERN_INFO, device, 
-                                              "%s", 
-                                              "...Revalidation complete");
-                        }
-		}
-	}			/* end if no more requests */
-
-        /* check if at least one format cp was build in discipline */
-        if (!format_done) {
-                rc = -EINVAL;
-        }
-
-	if (rc)
-		DASD_MESSAGE (KERN_WARNING, device,
-			      "%s", " Formatting finished unsuccessfully");
-	else
-		DASD_MESSAGE (KERN_INFO, device,
-			      "%s", " Formatting finished successfully");
-
-        /* 
-         * re-analyse device
-         */
-        dasd_set_device_level (device->devinfo.irq,
-                               DASD_DEVICE_LEVEL_ONLINE,
-                               device->discipline,
-                               0);
-        udelay (1500000);
-                
-        dasd_set_device_level (device->devinfo.irq,
-                               DASD_DEVICE_LEVEL_ONLINE,
-                               device->discipline,
-                               0);
-
-        spin_lock (&dasd_open_count_lock);
-        device->open_count=1;
-        spin_unlock (&dasd_open_count_lock);
 	return rc;
 }				/* end dasd_format */
 
-static struct list_head dasd_ioctls = LIST_HEAD_INIT(dasd_ioctls);
+static struct list_head dasd_ioctls = LIST_HEAD_INIT (dasd_ioctls);
 
 static dasd_ioctl_list_t *
-dasd_find_ioctl( int no )
+dasd_find_ioctl (int no)
 {
 	struct list_head *curr;
-	list_for_each(curr,&dasd_ioctls){
-		if (list_entry(curr,dasd_ioctl_list_t,list)->no == no ){
-			return list_entry(curr,dasd_ioctl_list_t,list);
+	list_for_each (curr, &dasd_ioctls) {
+		if (list_entry (curr, dasd_ioctl_list_t, list)->no == no) {
+			return list_entry (curr, dasd_ioctl_list_t, list);
 		}
 	}
 	return NULL;
 }
 
 int
-dasd_ioctl_no_register ( int no, dasd_ioctl_fn_t handler )
+dasd_ioctl_no_register (struct module *owner, int no, dasd_ioctl_fn_t handler)
 {
 	dasd_ioctl_list_t *new;
-	if (dasd_find_ioctl(no))
+	if (dasd_find_ioctl (no))
 		return -EBUSY;
-	new = kmalloc(sizeof(dasd_ioctl_list_t),GFP_KERNEL);
- 	if ( new ==  NULL )
+	new = kmalloc (sizeof (dasd_ioctl_list_t), GFP_KERNEL);
+	if (new == NULL)
 		return -ENOMEM;
-	new -> no = no;
-	new -> handler = handler;
-	list_add(&new->list,&dasd_ioctls);
-#ifdef MODULE
-        MOD_INC_USE_COUNT;
-#endif
+	new->owner = owner;
+	new->no = no;
+	new->handler = handler;
+	list_add (&new->list, &dasd_ioctls);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
 int
-dasd_ioctl_no_unregister ( int no, dasd_ioctl_fn_t handler )
-{	
-	dasd_ioctl_list_t *old = dasd_find_ioctl(no);
-	if ( old == NULL )
+dasd_ioctl_no_unregister (struct module *owner, int no, dasd_ioctl_fn_t handler)
+{
+	dasd_ioctl_list_t *old = dasd_find_ioctl (no);
+	if (old == NULL)
 		return -ENOENT;
-	if ( old->no != no ||
-	     old->handler != handler )
+	if (old->no != no || old->handler != handler || owner != old->owner )
 		return -EINVAL;
-	list_del(&old->list);
-	kfree(old);
-#ifdef MODULE
-        MOD_DEC_USE_COUNT;
-#endif
+	list_del (&old->list);
+	kfree (old);
+	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
 static int
+dasd_revalidate (dasd_device_t * device)
+{
+        int rc = 0;
+	int i;
+	kdev_t kdev = device->kdev;
+	int openct = atomic_read (&device->open_count);
+	int start = MINOR (kdev);
+	if (openct != 1) {
+		DASD_MESSAGE (KERN_WARNING, device, "%s",
+			      "BLKRRPART: device is open! expect errors.");
+	}
+	for (i = (1 << DASD_PARTN_BITS) - 1; i >= 0; i--) {
+                int major = device->major_info->gendisk.major;
+		int minor = start + i;
+		kdev_t devi = MKDEV (major, minor);
+		struct super_block *sb = get_super (devi);
+		sync_dev (devi);
+		if (sb)
+			invalidate_inodes (sb);
+		invalidate_buffers (devi);
+	}
+        dasd_destroy_partitions(device);
+        dasd_setup_partitions(device);
+        return rc;
+
+}
+static int
 do_dasd_ioctl (struct inode *inp, /* unsigned */ int no, unsigned long data)
 {
 	int rc = 0;
@@ -2009,31 +2182,37 @@
 		_IOC_DIR (no) == (_IOC_READ | _IOC_WRITE) ? "rw" : "u",
 		_IOC_TYPE (no), _IOC_NR (no), _IOC_SIZE (no),
 		device->name, MAJOR (inp->i_rdev), MINOR (inp->i_rdev),
-		device->devinfo.devno, device->devinfo.irq,
-		data);
+		device->devinfo.devno, device->devinfo.irq, data);
 #endif
 	switch (no) {
+        case DASDAPIVER: {
+                       int ver = DASD_API_VERSION;
+                       rc = copy_to_user ((int *) data, &ver, sizeof (int));
+                       if (rc)
+                               rc = -EFAULT;
+                       break;
+        }
 	case BLKGETSIZE:{	/* Return device size */
-			long blocks = blk_size[MAJOR (inp->i_rdev)][MINOR (inp->i_rdev)] << 1;
-			rc = copy_to_user ((long *) data, &blocks, sizeof (long));
+			long blocks = major_info->gendisk.sizes 
+                                      [MINOR (inp->i_rdev)] << 1;
+			rc =
+			    copy_to_user ((long *) data, &blocks,
+					  sizeof (long));
 			if (rc)
 				rc = -EFAULT;
 			break;
 		}
 	case BLKRRPART:{
-                        if (!capable(CAP_SYS_ADMIN)) {
-                            rc = -EACCES;
-                            break;
-                        }
-			fsync_dev(inp->i_rdev);
-			dasd_partn_detect (device);
-			invalidate_buffers(inp->i_rdev);
-			rc = 0;
+			if (!capable (CAP_SYS_ADMIN)) {
+				rc = -EACCES;
+				break;
+			}
+			rc = dasd_revalidate (device);
 			break;
 		}
 	case HDIO_GETGEO:{
-			struct hd_geometry geo = {0,};
-			rc = dasd_fillgeo(inp->i_rdev, &geo);
+			struct hd_geometry geo = { 0, };
+			rc = dasd_fillgeo (inp->i_rdev, &geo);
 			if (rc)
 				break;
 
@@ -2043,148 +2222,208 @@
 				rc = -EFAULT;
 			break;
 		}
-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
-        case BLKSSZGET:
-        case BLKROSET:
-        case BLKROGET:
-        case BLKRASET:
-        case BLKRAGET:
-        case BLKFLSBUF:
-        case BLKPG: 
-        case BLKELVGET:
-        case BLKELVSET:
-                return blk_ioctl(inp->i_rdev, no, data);
-                break;
-#else
-        case BLKRASET:
-                if(!capable(CAP_SYS_ADMIN))
-                        return -EACCES;
-                if(!dev || arg > 0xff)
-                        return -EINVAL;
-                read_ahead[MAJOR(dev)] = arg;
-                rc = 0;
-                break;
-        case BLKRAGET:
-                if (!arg)
-                        return -EINVAL;
-                rc = put_user(read_ahead[MAJOR(dev)], (long *) arg);
-                break;
-        case BLKSSZGET: {
-            /* Block size of media */
-            rc = copy_to_user((int *)data,
-                              &blksize_size[MAJOR(device->kdev)]
-                              [MINOR(device->kdev)],
-                              sizeof(int)) ? -EFAULT : 0;
-        }
-        RO_IOCTLS (inp->i_rdev, data);
-	case BLKFLSBUF:{
-                if (!capable(CAP_SYS_ADMIN)) 
-                        return -EACCES;
-                fsync_dev(inp->i_rdev);
-                invalidate_buffers(inp->i_rdev);
-                rc = 0;
-                break;
+	case BIODASDDISABLE:{
+			if (!capable (CAP_SYS_ADMIN)) {
+				rc = -EACCES;
+				break;
+			}
+                        if ( device->level > DASD_STATE_ACCEPT) {
+                                if ( device->request_queue)
+                                        dasd_flush_request_queues(device,0);
+                                dasd_flush_chanq(device,0);
+                                dasd_disable_blkdev(device);
+                                dasd_set_device_level (device->devinfo.devno, 
+                                                       device->discipline, 
+                                                       DASD_STATE_ACCEPT);
+                        }
+                        break;
         }
-#endif				/* LINUX_IS_24 */
-	case BIODASDRSID:{
-			rc = copy_to_user ((void *) data,
-					   &(device->devinfo.sid_data),
-					   sizeof (senseid_t)) ? -EFAULT : 0;
-			break;
-		}
-	case BIODASDRWTB:{
-			int offset = 0;
-			int xlt;
-			rc = copy_from_user (&xlt, (void *) data,
-					     sizeof (int)) ? -EFAULT : 0;
-			if (rc)
+	case BIODASDENABLE:{
+                        dasd_range_t range = { 
+                                from: device->devinfo.devno,
+                                to: device->devinfo.devno 
+                        };
+			if (!capable (CAP_SYS_ADMIN)) {
+				rc = -EACCES;
+				break;
+			}
+                        dasd_enable_ranges (&range, device->discipline, 0);
+                        break;
+        }
+	case BIODASDFMT:{
+			/* fdata == NULL is no longer a valid arg to dasd_format ! */
+			int partn = MINOR (inp->i_rdev) &
+			    ((1 << major_info->gendisk.minor_shift) - 1);
+			format_data_t fdata;
+
+			if (!capable (CAP_SYS_ADMIN)) {
+				rc = -EACCES;
 				break;
-			offset = major_info->gendisk.part[MINOR (inp->i_rdev)].start_sect >>
-			    device->sizes.s2b_shift;
-			xlt += offset;
-			rc = copy_to_user ((void *) data, &xlt,
-					   sizeof (int)) ? -EFAULT : 0;
-			break;
-		}
-	case BIODASDFORMAT:{
-			/* fdata == NULL is a valid arg to dasd_format ! */
-			int partn;
-			format_data_t fdata =
-			{
-				DASD_FORMAT_DEFAULT_START_UNIT,
-				DASD_FORMAT_DEFAULT_STOP_UNIT,
-				DASD_FORMAT_DEFAULT_BLOCKSIZE,
-				DASD_FORMAT_DEFAULT_INTENSITY};
-
-                        if (!capable(CAP_SYS_ADMIN)) {
-                            rc = -EACCES;
-                            break;
+			}
+                        if (dasd_features_from_devno(device->devinfo.devno)&DASD_FEATURE_READONLY) {
+                                rc = -EROFS;
+                                break;
                         }
-			if (data) {
-				rc = copy_from_user (&fdata, (void *) data,
-						     sizeof (format_data_t));
-				if (rc) {
-					rc = -EFAULT;
-					break;
-				}
+			if (!data) {
+				rc = -EINVAL;
+				break;
+			}
+			rc = copy_from_user (&fdata, (void *) data,
+					     sizeof (format_data_t));
+			if (rc) {
+				rc = -EFAULT;
+				break;
 			}
-			partn = MINOR (inp->i_rdev) & ((1 << major_info->gendisk.minor_shift) - 1);
 			if (partn != 0) {
-				printk (KERN_WARNING PRINTK_HEADER
-					" devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
-				     " Cannot low-level format a partition\n",
-					device->devinfo.devno, device->devinfo.irq, device->name,
-				    MAJOR (inp->i_rdev), MINOR (inp->i_rdev));
+				DASD_MESSAGE (KERN_WARNING, device, "%s",
+					      "Cannot low-level format a partition");
 				return -EINVAL;
 			}
 			rc = dasd_format (device, &fdata);
 			break;
 		}
+	case BIODASDPRRST:{
+			if (!capable (CAP_SYS_ADMIN)) {
+				rc = -EACCES;
+				break;
+			}
+			memset (&device->profile, 0,
+				sizeof (dasd_profile_info_t));
+			break;
+		}
+	case BIODASDPRRD:{
+			rc = copy_to_user((long *)data,
+					  (long *)&device->profile,
+					  sizeof(dasd_profile_info_t));
+			if (rc)
+				rc = -EFAULT;
+			break;
+		}
 	case BIODASDRSRV:{
-            ccw_req_t *req;
-            if (!capable(CAP_SYS_ADMIN)) {
-                rc = -EACCES;
-                break;
-            }
-            req = device->discipline->reserve (device);
-            rc = sleep_on_req (req);
-            dasd_free_request (req);
-            break;
-        }
+			ccw_req_t *req;
+			if (!capable (CAP_SYS_ADMIN)) {
+				rc = -EACCES;
+				break;
+			}
+			req = device->discipline->reserve (device);
+			rc = dasd_sleep_on_req (req);
+			dasd_free_request (req, device);
+			break;
+		}
 	case BIODASDRLSE:{
-            ccw_req_t *req;
-            if (!capable(CAP_SYS_ADMIN)) {
-                rc = -EACCES;
-                break;
-            }
-            req = device->discipline->release (device);
-            rc = sleep_on_req (req);
-            dasd_free_request (req);
-            break;
-        }
+			ccw_req_t *req;
+			if (!capable (CAP_SYS_ADMIN)) {
+				rc = -EACCES;
+				break;
+			}
+			req = device->discipline->release (device);
+			rc = dasd_sleep_on_req (req);
+			dasd_free_request (req, device);
+			break;
+		}
 	case BIODASDSLCK:{
 			printk (KERN_WARNING PRINTK_HEADER
 				"Unsupported ioctl BIODASDSLCK\n");
 			break;
 		}
+	case BIODASDINFO:{
+			dasd_information_t dasd_info;
+			unsigned long flags;
+			rc = device->discipline->fill_info (device, &dasd_info);
+                        dasd_info.label_block = device->sizes.pt_block;
+			dasd_info.devno = device->devinfo.devno;
+			dasd_info.schid = device->devinfo.irq;
+			dasd_info.cu_type = device->devinfo.sid_data.cu_type;
+			dasd_info.cu_model = device->devinfo.sid_data.cu_model;
+			dasd_info.dev_type = device->devinfo.sid_data.dev_type;
+			dasd_info.dev_model = device->devinfo.sid_data.dev_model;
+			dasd_info.open_count =
+			    atomic_read (&device->open_count);
+			dasd_info.status = device->level;
+			if (device->discipline) {
+				memcpy (dasd_info.type,
+					device->discipline->name, 4);
+			} else {
+				memcpy (dasd_info.type, "none", 4);
+			}
+			dasd_info.req_queue_len = 0;
+			dasd_info.chanq_len = 0;
+			if (device->request_queue->request_fn) {
+				struct list_head *l;
+				ccw_req_t *cqr = device->queue.head;
+				spin_lock_irqsave (&io_request_lock, flags);
+				list_for_each (l,
+					       &device->request_queue->
+					       queue_head) {
+					dasd_info.req_queue_len++;
+				}
+				spin_unlock_irqrestore (&io_request_lock,
+							flags);
+				s390irq_spin_lock_irqsave (device->devinfo.irq,
+							   flags);
+				while (cqr) {
+					cqr = cqr->next;
+					dasd_info.chanq_len++;
+				}
+				s390irq_spin_unlock_irqrestore (device->devinfo.
+								irq, flags);
+			}
+			rc =
+			    copy_to_user ((long *) data, (long *) &dasd_info,
+					  sizeof (dasd_information_t));
+			if (rc)
+				rc = -EFAULT;
+			break;
+		}
+#if 0 /* needed for XFS */
+	case BLKBSZSET:{
+		int bsz;
+		rc = copy_from_user ((long *)&bsz,(long *)data,sizeof(int));
+		if ( rc ) {
+			rc = -EFAULT;
+		} else {
+			if ( bsz >= device->sizes.bp_block )
+				rc = blk_ioctl (inp->i_rdev, no, data);
+			else
+				rc = -EINVAL; 
+		}
+		break;
+		}
+#endif /* 0 */
+	case BLKSSZGET:
+	case BLKROSET:
+	case BLKROGET:
+	case BLKRASET:
+	case BLKRAGET:
+	case BLKFLSBUF:
+	case BLKPG:
+	case BLKELVGET:
+	case BLKELVSET:
+		return blk_ioctl (inp->i_rdev, no, data);
+		break;
 	default:{
 
-			dasd_ioctl_list_t *old = dasd_find_ioctl(no);
-			if ( old ) {
-				rc = old->handler(inp,no,data);
+			dasd_ioctl_list_t *old = dasd_find_ioctl (no);
+			if (old) {
+				if ( old->owner )
+					__MOD_INC_USE_COUNT(old->owner);
+				rc = old->handler (inp, no, data);
+				if ( old->owner )
+					__MOD_DEC_USE_COUNT(old->owner);
 			} else {
 				DASD_MESSAGE (KERN_INFO, device,
-					    "ioctl 0x%08x=%s'0x%x'%d(%d) data %8lx\n",
+					      "ioctl 0x%08x=%s'0x%x'%d(%d) data %8lx\n",
 					      no,
 					      _IOC_DIR (no) == _IOC_NONE ? "0" :
 					      _IOC_DIR (no) == _IOC_READ ? "r" :
-					      _IOC_DIR (no) == _IOC_WRITE ? "w" :
-					   _IOC_DIR (no) == (_IOC_READ | _IOC_WRITE) ?
-					      "rw" : "u",
-					 _IOC_TYPE (no), _IOC_NR (no), _IOC_SIZE (no),
+					      _IOC_DIR (no) ==
+					      _IOC_WRITE ? "w" : _IOC_DIR (no)
+					      == (_IOC_READ | _IOC_WRITE) ? "rw"
+					      : "u", _IOC_TYPE (no),
+					      _IOC_NR (no), _IOC_SIZE (no),
 					      data);
-				rc = -EINVAL;
-			}	
+				rc = -ENOTTY;
+			}
 			break;
 		}
 	}
@@ -2209,37 +2448,46 @@
 dasd_open (struct inode *inp, struct file *filp)
 {
 	int rc = 0;
+        unsigned long flags;
 	dasd_device_t *device;
 
+        MOD_INC_USE_COUNT;
 	if ((!inp) || !(inp->i_rdev)) {
-		return -EINVAL;
+		rc = -EINVAL;
+                goto fail;
 	}
 	if (dasd_probeonly) {
-		printk ("\n" KERN_INFO PRINTK_HEADER "No access to device (%d:%d) due to probeonly mode\n", MAJOR (inp->i_rdev), MINOR (inp->i_rdev));
-		return -EPERM;
+		printk ("\n" KERN_INFO PRINTK_HEADER
+			"No access to device (%d:%d) due to probeonly mode\n",
+			MAJOR (inp->i_rdev), MINOR (inp->i_rdev));
+		rc = -EPERM;
+                goto fail;
 	}
+        spin_lock_irqsave(&discipline_lock,flags);
 	device = dasd_device_from_kdev (inp->i_rdev);
-	if (device == NULL) {
+	if (device == NULL ) {
 		printk (KERN_WARNING PRINTK_HEADER
-			"No device registered as (%d:%d)\n", MAJOR (inp->i_rdev), MINOR (inp->i_rdev));
-		return -ENODEV;
+			"No device registered as (%d:%d)\n",
+			MAJOR (inp->i_rdev), MINOR (inp->i_rdev));
+		rc = -ENODEV;
+                goto unlock;
 	}
-	if (device->level < DASD_DEVICE_LEVEL_RECOGNIZED ||
-	    device->discipline == NULL) {
+	if (device->level < DASD_STATE_ACCEPT ) {
 		DASD_MESSAGE (KERN_WARNING, device,
 			      " %s", " Cannot open unrecognized device\n");
-		return -EINVAL;
+		rc = -ENODEV;
+                goto unlock;
 	}
-        spin_lock(&dasd_open_count_lock);
-        if (device->open_count == -1) {
-            spin_unlock (&dasd_open_count_lock);
-            return -EBUSY;
+	if (atomic_inc_return (&device->open_count) == 1 ) {
+                if ( device->discipline->owner )
+                        __MOD_INC_USE_COUNT(device->discipline->owner);
+        } else {
+                MOD_DEC_USE_COUNT;
         }
-#ifdef MODULE
-	MOD_INC_USE_COUNT;
-#endif				/* MODULE */
-	device->open_count++;
-        spin_unlock (&dasd_open_count_lock);
+ unlock:
+        spin_unlock_irqrestore(&discipline_lock,flags);
+ fail:
+        if (rc) MOD_DEC_USE_COUNT;
 	return rc;
 }
 
@@ -2247,28 +2495,41 @@
 dasd_release (struct inode *inp, struct file *filp)
 {
 	int rc = 0;
+        int count;
 	dasd_device_t *device;
 
 	if ((!inp) || !(inp->i_rdev)) {
-		return -EINVAL;
+		rc = -EINVAL;
+                goto out;
 	}
 	device = dasd_device_from_kdev (inp->i_rdev);
 	if (device == NULL) {
 		printk (KERN_WARNING PRINTK_HEADER
 			"No device registered as %d:%d\n",
 			MAJOR (inp->i_rdev), MINOR (inp->i_rdev));
-		return -EINVAL;
+		rc = -EINVAL;
+                goto out;
 	}
-        spin_lock(&dasd_open_count_lock);
-	if (device->open_count--) {
-#ifdef MODULE
-		MOD_DEC_USE_COUNT;
-#endif				/* MODULE */
-	} 
-	fsync_dev(inp->i_rdev); /* sync the device */
-	if (device->open_count == 0) /* finally invalidate buffers */
-		invalidate_buffers(inp->i_rdev);
-        spin_unlock(&dasd_open_count_lock);
+
+	if (device->level < DASD_STATE_ACCEPT ) {
+		DASD_MESSAGE (KERN_WARNING, device,
+			      " %s", " Cannot release unrecognized device\n");
+		rc = -ENODEV;
+                goto out;
+	}
+	fsync_dev (inp->i_rdev);	/* sync the device */
+        count = atomic_dec_return (&device->open_count);
+        if ( count == 0) {
+                invalidate_buffers (inp->i_rdev);
+                if ( device->discipline->owner )
+                        __MOD_DEC_USE_COUNT(device->discipline->owner);
+                MOD_DEC_USE_COUNT;
+	} else if ( count == -1 ) { /* paranoia only */
+                atomic_set (&device->open_count,0);
+                printk (KERN_WARNING PRINTK_HEADER
+                        "release called with open count==0\n");
+        }
+ out:
 	return rc;
 }
 
@@ -2278,11 +2539,6 @@
 	open:dasd_open,
 	release:dasd_release,
 	ioctl:dasd_ioctl,
-#if ! (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
-	read:block_read,
-	write:block_write,
-	fsync:block_fsync,
-#endif				/* LINUX_IS_24 */
 };
 
 /* SECTION: Management of device list */
@@ -2294,16 +2550,9 @@
 		return -EINVAL;
 
 	device->discipline->fill_geometry (device, geo);
-	geo->start = device->major_info->
-			gendisk.part[MINOR(kdev)].start_sect;
-
-	/* This is a hack.  dasdfmt and ibm.c expect geo.start 
-	   to contain the block number of the label block when
-	   it calls HDIO_GETGEO on the first partition. */
-	if (geo->start == 0)
-                geo->start = device->sizes.pt_block;
-
-	return 0;
+	geo->start = device->major_info->gendisk.part[MINOR(kdev)].start_sect 
+		     >> device->sizes.s2b_shift;;
+        return 0;
 } 
 
 
@@ -2315,11 +2564,11 @@
 	char first, second, third;
 
 	if (hd) {
-		major_info_t *major_info=NULL;
+		major_info_t *major_info = NULL;
 		struct list_head *l;
 
-		list_for_each(l,&dasd_major_info[0].list) {
-			major_info = list_entry(l,major_info_t,list); 
+		list_for_each (l, &dasd_major_info[0].list) {
+			major_info = list_entry (l, major_info_t, list);
 			if (&major_info->gendisk == hd) {
 				break;
 			}
@@ -2330,14 +2579,14 @@
 		}
 	}
 	third = index % 26;
-	second = ((index-26) / 26) % 26;
-	first = (((index-702) / 26) / 26) % 26;
+	second = ((index - 26) / 26) % 26;
+	first = (((index - 702) / 26) / 26) % 26;
 
 	len = sprintf (str, "dasd");
-	if (index>701) {
+	if (index > 701) {
 		len += sprintf (str + len, "%c", first + 'a');
 	}
-	if (index>25) {
+	if (index > 25) {
 		len += sprintf (str + len, "%c", second + 'a');
 	}
 	len += sprintf (str + len, "%c", third + 'a');
@@ -2352,19 +2601,214 @@
 	return 0;
 }
 
-#ifdef CONFIG_DASD_DYNAMIC
 static void
-dasd_plug_device (dasd_device_t *device)
+dasd_plug_device (dasd_device_t * device)
 {
-        device->request_queue.plugged = 1; /* inhibit further calls of request_fn */
+	atomic_set(&device->plugged,1);	
+}
+
+static void
+dasd_unplug_device (dasd_device_t * device)
+{
+	atomic_set(&device->plugged,0);	
+        dasd_schedule_bh(device);
+}
+
+static void
+dasd_flush_chanq ( dasd_device_t * device, int destroy ) 
+{
+        ccw_req_t *cqr;
+        unsigned long flags;
+        if ( destroy ) {
+                s390irq_spin_lock_irqsave (device->devinfo.irq, flags);
+                cqr = device->queue.head;
+                while ( cqr != NULL ) {
+                        if ( cqr -> status == CQR_STATUS_IN_IO )
+                                device->discipline->term_IO (cqr);
+                        if ( cqr->status != CQR_STATUS_DONE ||
+                             cqr->status != CQR_STATUS_FAILED ) {
+                                cqr->status = CQR_STATUS_FAILED;
+                        }
+                        dasd_schedule_bh(device);
+                        cqr = cqr->next;
+                }
+                s390irq_spin_unlock_irqrestore (device->devinfo.irq, flags);
+        }
+        wait_event( device->wait_q, device->queue.head == NULL );
+}
+
+static void
+dasd_flush_request_queues ( dasd_device_t * device, int destroy )
+{
+        int i;
+        int major = MAJOR(device->kdev);
+        int minor = MINOR(device->kdev);
+        for ( i = 0; i < (1 << DASD_PARTN_BITS); i ++) {
+                if ( destroy )
+                        destroy_buffers(MKDEV(major,minor+i)); 
+                else
+                        invalidate_buffers(MKDEV(major,minor+i)); 
+        } 
+}
+
+static int
+dasd_disable_volume ( dasd_device_t * device, int force ) 
+{
+        int rc = 0;
+        int target  = DASD_STATE_KNOWN;
+        int count = atomic_read (&device->open_count);
+        int part;
+        
+	if ( count ) {
+		DASD_MESSAGE (KERN_EMERG, device, "%s",
+			      "device has vanished although it was open!");
+        }
+        if ( force ) {
+                dasd_flush_chanq(device,force);
+                dasd_flush_request_queues(device,force);
+                dasd_disable_blkdev(device);
+                target = DASD_STATE_DEL;
+        }
+
+#if 0
+        /* unregister devfs entries */
+        for (part = 0; part < (1 << DASD_PARTN_BITS); part++) {
+                devfs_unregister(device->major_info->gendisk.part[MINOR(device->kdev)+part].de);
+                device->major_info->gendisk.part[MINOR(device->kdev)+part].de = NULL;
+        }                        
+#endif
+        
+        DASD_MESSAGE (KERN_WARNING, device, 
+                      "disabling device, target state:%d",target);
+        dasd_set_device_level (device->devinfo.devno, 
+                               device->discipline, 
+                               target);
+        return rc;
+}
+
+static void
+dasd_disable_ranges (dasd_range_t *range, 
+                     dasd_discipline_t *d,
+                     int all, int force ) 
+{
+        dasd_range_t *rrange;
+        int j;
+
+        if (range == &dasd_range_head) {
+                rrange = list_entry (range->list.next, 
+                                     dasd_range_t, list);
+        } else {
+                rrange = range;
+        }
+        do {
+                for (j = rrange->from; j <= rrange->to; j++) {
+                        dasd_device_t **dptr;
+                        dasd_device_t *device;
+                        dptr = dasd_device_from_devno(j);
+                        if ( dptr == NULL ) {
+                            continue;
+                        }
+                        device = *dptr;
+                        if (device == NULL ||
+                            (d != NULL &&
+                             device -> discipline != d))
+                                continue;
+                        
+                        dasd_disable_volume(device, force);
+                }
+                rrange = list_entry (rrange->list.next, dasd_range_t, list);
+        } while ( all && rrange && rrange != range );
+}
+
+static void 
+dasd_enable_single_device ( unsigned long arg ) {
+        dasd_device_t * device =(dasd_device_t *) arg;
+        int devno = device->devinfo.devno;
+        dasd_range_t range = { from: devno, to:devno };
+        dasd_enable_ranges (&range,NULL,0);
 }
 
 static void
-dasd_unplug_device (dasd_device_t *device)
+dasd_enable_ranges (dasd_range_t *range, dasd_discipline_t *d, int all ) 
 {
-        generic_unplug_device(&device->request_queue);
+        int retries = 0;
+	int j;
+        kdev_t tempdev;
+	dasd_range_t *rrange;
+
+	if (range == NULL)
+		return;
+        
+        do {
+                if (range == &dasd_range_head) {
+                        rrange = list_entry (range->list.next, 
+                                             dasd_range_t, list);
+                } else {
+                        rrange = range;
+                }
+                do {
+                        for (j = rrange->from; j <= rrange->to; j++) {
+                                if ( dasd_devindex_from_devno(j) < 0 )
+                                        continue;
+                                dasd_set_device_level (j, d, DASD_STATE_ONLINE);
+                        }
+                        rrange = list_entry (rrange->list.next, dasd_range_t, list);
+                } while ( all && rrange && rrange != range );
+
+                if (atomic_read (&dasd_init_pending) == 0) /* we are done, exit loop */
+                        break;
+
+                if ( retries == 0 ) {
+                        printk (KERN_INFO PRINTK_HEADER
+                                "waiting for responses...\n");
+                } else if ( retries < 5 ) {
+                        printk (KERN_INFO PRINTK_HEADER
+                                "waiting a little bit longer...\n");
+                } else {
+                        printk (KERN_INFO PRINTK_HEADER
+                                "giving up, enable late devices manually!\n");
+                        break;
+                }
+                interruptible_sleep_on_timeout (&dasd_init_waitq, (1 * HZ));
+                retries ++;
+        } while (1);
+        /* now setup block devices */
+
+        /* Now do block device and partition setup */
+        if (range == &dasd_range_head) {
+                rrange = list_entry (range->list.next, 
+                                     dasd_range_t, list);
+        } else {
+                rrange = range;
+        }
+        do {
+                for (j = rrange->from; j <= rrange->to; j++) {
+                        dasd_device_t **dptr;
+                        dasd_device_t *device;
+                        if ( dasd_devindex_from_devno(j) < 0 )
+                                continue;
+                        dptr = dasd_device_from_devno(j);
+                        device = *dptr;
+                        if (device == NULL )
+                                continue;
+                        if ( ((d == NULL && device->discipline != NULL) ||
+                              (device->discipline == d )) &&
+                             device->level >= DASD_STATE_READY &&
+                             device->request_queue == NULL ) {
+                                if (dasd_features_from_devno(j)&DASD_FEATURE_READONLY) {
+                                    for (tempdev=device->kdev;tempdev<(device->kdev +(1 << DASD_PARTN_BITS));tempdev++)
+                                        set_device_ro (tempdev, 1);
+                                     printk (KERN_WARNING PRINTK_HEADER "setting read-only mode for device /dev/%s\n",device->name);
+                                }
+                                dasd_setup_blkdev(device);
+                                dasd_setup_partitions(device);
+                        }
+                }
+                rrange = list_entry (rrange->list.next, dasd_range_t, list);
+        } while ( all && rrange && rrange != range );
 }
 
+#ifdef CONFIG_DASD_DYNAMIC
 static void
 dasd_not_oper_handler (int irq, int status)
 {
@@ -2373,13 +2817,12 @@
 	struct list_head *l;
 	int i, devno = -ENODEV;
 
-        /* find out devno of leaving device: CIO has already deleted this information ! */
-	list_for_each(l,&dasd_major_info[0].list) {
-		major_info=list_entry(l, major_info_t,list);	
+	/* find out devno of leaving device: CIO has already deleted this information ! */
+	list_for_each (l, &dasd_major_info[0].list) {
+		major_info = list_entry (l, major_info_t, list);
 		for (i = 0; i < DASD_PER_MAJOR; i++) {
 			device = major_info->dasd_device[i];
-			if (device &&
-			    device->devinfo.irq == irq) {
+			if (device && device->devinfo.irq == irq) {
 				devno = device->devinfo.devno;
 				break;
 			}
@@ -2392,360 +2835,561 @@
 			"not_oper_handler called on irq %d no devno!\n", irq);
 		return;
 	}
-
-	if (device->open_count != 0) {
-                DASD_MESSAGE(KERN_ALERT,device,"%s",
-                             "open device has gone. please repair!");
-                dasd_set_device_level (irq, DASD_DEVICE_LEVEL_ANALYSED, 
-                                       NULL, 0);
-	} else {
-                DASD_MESSAGE(KERN_INFO,device,"%s","device has gone");
-                dasd_set_device_level (irq, DASD_DEVICE_LEVEL_UNKNOWN, 
-                                       NULL, 0);
-        }
+        dasd_disable_volume(device, 0);
 }
 
-static int
-dasd_enable_single_volume (int irq)
+int
+dasd_oper_handler (int irq, devreg_t * devreg)
 {
+	int devno;
 	int rc = 0;
-	dasd_set_device_level (irq, DASD_DEVICE_LEVEL_ONLINE,
-			       NULL, 0);
-	printk (KERN_INFO PRINTK_HEADER "waiting for response...\n");
-	{
-		static wait_queue_head_t wait_queue;
-		init_waitqueue_head (&wait_queue);
-		interruptible_sleep_on_timeout (&wait_queue, (5 * HZ) >> 1);
+	major_info_t *major_info = NULL;
+        dasd_range_t *rptr,range;
+        dasd_device_t *device = NULL;
+	struct list_head *l;
+        int i;
+
+	devno = get_devno_by_irq (irq);
+	if (devno == -ENODEV) {
+		rc = -ENODEV;
+                goto out;
 	}
-	dasd_set_device_level (irq, DASD_DEVICE_LEVEL_ONLINE, NULL, 0);
+	/* find out devno of leaving device: CIO has already deleted this information ! */
+	list_for_each (l, &dasd_major_info[0].list) {
+		major_info = list_entry (l, major_info_t, list);
+		for (i = 0; i < DASD_PER_MAJOR; i++) {
+			device = major_info->dasd_device[i];
+			if (device && device->devinfo.irq == irq) {
+				devno = device->devinfo.devno;
+				break;
+			}
+		}
+		if (devno != -ENODEV)
+			break;
+	}
+	if (devno < 0) {
+                BUG();
+	}
+        if ( device &&
+             device->level == DASD_STATE_READY ) {
+            dasd_set_device_level (device->devinfo.devno, 
+                                   device->discipline, DASD_STATE_ONLINE);
+
+        } else {
+            if (dasd_autodetect) {
+		rptr = dasd_add_range (devno, devno, DASD_DEFAULT_FEATURES);
+                if ( rptr == NULL ) {
+                    rc = -ENOMEM;
+                    goto out;
+                }
+            } else {
+                range.from = devno;
+                range.to = devno;
+                rptr = &range;
+            }
+            dasd_enable_ranges (rptr, NULL, 0);
+        }
+ out:
 	return rc;
 }
+#endif				/* CONFIG_DASD_DYNAMIC */
+
+static inline dasd_device_t **
+dasd_find_device_addr ( int devno ) 
+{
+        dasd_device_t **device_addr;
+
+	DASD_DRIVER_DEBUG_EVENT (1, dasd_find_device_addr, 
+                                 "devno %04X", devno);
+	if ( dasd_devindex_from_devno (devno) < 0 ) {
+                DASD_DRIVER_DEBUG_EXCEPTION (1, dasd_find_device_addr, 
+                                             "no dasd: devno %04X",
+                                             devno);
+		return NULL;
+	}
+        /* allocate major numbers on demand  for new devices */
+	while ((device_addr = dasd_device_from_devno (devno)) == NULL) {
+                int rc;
+
+		if ((rc = dasd_register_major (NULL)) <= 0) {
+
+                        DASD_DRIVER_DEBUG_EXCEPTION (1, dasd_find_device_addr, 
+                                                     "%s",
+                                                     "out of major numbers!");
+                        break;
+		}
+	}
+        return device_addr;
+}
+
+static inline int
+dasd_state_del_to_new (dasd_device_t **addr ) 
+{
+        dasd_device_t* device;
+        int rc = 0;
+	if (*addr == NULL) { /* allocate device descriptor on demand for new device */
+                device = kmalloc (sizeof (dasd_device_t), GFP_ATOMIC);
+		if (device == NULL ) {
+			rc = -ENOMEM;
+                        goto out;
+		}
+		memset (device, 0, sizeof (dasd_device_t));
+                *addr = device;
+                device->lowmem_ccws = (void*)get_free_page (GFP_ATOMIC|GFP_DMA);
+                if (device->lowmem_ccws == NULL) {
+                        rc = -ENOMEM;
+                        goto noccw;
+	}
+#ifdef CONFIG_ARCH_S390X
+                device->lowmem_idals =
+                    device->lowmem_idal_ptr = (void*) get_free_page (GFP_ATOMIC|GFP_DMA);
+                if (device->lowmem_idals == NULL) {
+                        rc = -ENOMEM;
+                        goto noidal;
+                }                
+#endif
+}
+        goto out;
+ noidal:
+        free_page ((long) device->lowmem_ccws);
+ noccw:
+        kfree(device);
+ out:
+        return rc;
+}
+
+static inline int
+dasd_state_new_to_del (dasd_device_t **addr )
+{
+        dasd_device_t *device = *addr;
+        if (device && device->private) {
+                kfree(device->private);
+                device->private = NULL;
+        }
+#ifdef CONFIG_ARCH_S390X
+        free_page ((long)(device->lowmem_idals));
+#endif
+        free_page((long)(device->lowmem_ccws));
+        kfree(device);
+        *addr = NULL; 
+        return 0;
+}
+
+static inline int
+dasd_state_new_to_known (dasd_device_t **dptr, int devno, dasd_discipline_t *disc) 
+{
+        int rc = 0;
+	umode_t devfs_perm  = S_IFBLK | S_IRUSR | S_IWUSR;
+        struct list_head *l;
+        major_info_t *major_info = NULL;
+        int i;
+        dasd_device_t *device = *dptr;
+        devfs_handle_t dir;
+        char buffer[5];
+        
+
+	list_for_each (l, &dasd_major_info[0].list) {
+                major_info = list_entry (l, major_info_t, list);
+		for (i = 0; i < DASD_PER_MAJOR; i++) {
+			if (major_info->dasd_device[i] == device) {
+				device->kdev = MKDEV (major_info->gendisk.major,
+                                                      i << DASD_PARTN_BITS);
+				break;
+			}
+		}
+		if (i < DASD_PER_MAJOR) /* we found one */
+			break;
+	}
+        if ( major_info == NULL || major_info == &dasd_major_info[0] ) 
+                BUG();
+
+        device->major_info = major_info;
+        dasd_device_name (device->name,
+                          (((long)dptr -
+                            (long)device->major_info->dasd_device) /
+                           sizeof (dasd_device_t *)),
+                          0, &device->major_info->gendisk);
+        init_waitqueue_head (&device->wait_q);
+        
+        rc = get_dev_info_by_devno (devno, &device->devinfo);
+        if ( rc ) {
+                goto out;
+        }
+        if ( devno != device->devinfo.devno )
+                BUG();
+        device->discipline = dasd_find_disc (device, disc);
+        if ( device->discipline == NULL ) {
+                rc = -ENODEV;
+                goto out;
+        }
+        sprintf (buffer, "%04x", device->devinfo.devno);
+        dir = devfs_mk_dir (dasd_devfs_handle, buffer, device);
+        device->major_info->gendisk.de_arr[MINOR(device->kdev)
+                                          >> DASD_PARTN_BITS] = dir;
+	if (dasd_features_from_devno(device->devinfo.devno)&DASD_FEATURE_READONLY) {
+	        devfs_perm &= ~(S_IWUSR);
+	}
+        device->devfs_entry = devfs_register (dir,"device",DEVFS_FL_DEFAULT,
+                                              MAJOR(device->kdev),
+                                              MINOR(device->kdev),
+                                              devfs_perm,
+                                              &dasd_device_operations,NULL);
+        device->level = DASD_STATE_KNOWN;
+ out:
+        return rc;
+}
+
+static inline int
+dasd_state_known_to_new (dasd_device_t *device ) 
+{
+        int rc = 0;
+        /* don't reset to zeros because of persistent data durich detach/attach! */
+        devfs_unregister(device->devfs_entry);
+        devfs_unregister(device->major_info->gendisk.de_arr[MINOR(device->kdev) >> DASD_PARTN_BITS]);
+
+        return rc;
+}
+
+static inline int
+dasd_state_known_to_accept (dasd_device_t *device) 
+{
+        int rc = 0;
+        device->debug_area = debug_register (device->name, 0, 2, 
+                                             3 * sizeof (long));
+        debug_register_view (device->debug_area, &debug_sprintf_view);
+        debug_register_view (device->debug_area, &debug_hex_ascii_view);
+        DASD_DEVICE_DEBUG_EVENT (0, device,"%p debug area created",device);
+        
+        if (device->discipline->int_handler) {
+                rc = s390_request_irq_special (device->devinfo.irq,
+                                               device->discipline->int_handler,
+                                               dasd_not_oper_handler,
+                                               0, DASD_NAME,
+                                               &device->dev_status);
+                if ( rc ) {
+                        printk("No request IRQ\n");
+                        goto out;
+                }
+        }
+        device->level = DASD_STATE_ACCEPT;
+ out:
+        return rc;
+}
+
+static inline int
+dasd_state_accept_to_known (dasd_device_t *device ) 
+{
+        if ( device->discipline == NULL )
+                goto out;
+        if (device->discipline->int_handler) {
+                free_irq (device->devinfo.irq, &device->dev_status);
+        }
+        DASD_DEVICE_DEBUG_EVENT (0, device,"%p debug area deleted",device);
+        if ( device->debug_area != NULL )
+                debug_unregister (device->debug_area);
+        device->discipline = NULL;
+        device->level = DASD_STATE_KNOWN;
+ out:
+        return 0;
+}
+
+static inline int
+dasd_state_accept_to_init (dasd_device_t *device) 
+{
+        int rc = 0;
+        unsigned long flags;
+
+        if ( device->discipline->init_analysis ) {
+                device->init_cqr=device->discipline->init_analysis (device);
+                if ( device->init_cqr != NULL ) {
+                        if ( device->discipline->start_IO == NULL )
+                                BUG();
+                        atomic_inc (&dasd_init_pending);
+                        s390irq_spin_lock_irqsave (device->devinfo.irq, 
+                                                   flags);
+                        rc = device->discipline->start_IO (device->init_cqr);
+                        s390irq_spin_unlock_irqrestore(device->devinfo.irq, 
+                                                       flags);
+                        if ( rc )
+                                goto out;
+                        device->level = DASD_STATE_INIT;
+                } else {
+                        rc = -ENOMEM;
+                }
+        } else {
+                rc = dasd_state_init_to_ready ( device ); 
+        }
+ out:
+        return rc;
+}
+
+static inline int
+dasd_state_init_to_ready (dasd_device_t *device ) 
+{
+        int rc = 0;    
+        if (device->discipline->do_analysis != NULL)
+                if ( device->discipline->do_analysis (device) == 0 ) 
+                        switch (device->sizes.bp_block) {
+                        case 512:
+                        case 1024:
+                        case 2048:
+                        case 4096:
+                                break;
+                        default:
+                                rc = -EMEDIUMTYPE;
+                        }
+        if ( device->init_cqr ) {
+                atomic_dec(&dasd_init_pending);
+                device->init_cqr = NULL; /* This one is no longer needed */
+        }
+        device->level = DASD_STATE_READY;
+        return rc;
+}
+
+static inline int
+dasd_state_ready_to_accept (dasd_device_t *device ) 
+{
+        int rc = 0;
+        unsigned long flags;
+
+        if ( device->init_cqr != NULL ) {
+                if ( device->discipline->term_IO == NULL )
+                        BUG();
+                s390irq_spin_lock_irqsave (device->devinfo.irq, flags);
+                device->discipline->term_IO (device->init_cqr);
+                s390irq_spin_unlock_irqrestore(device->devinfo.irq, flags);
+                atomic_dec (&dasd_init_pending);
+                dasd_free_request (device->init_cqr, device);
+                device->init_cqr = NULL;
+        }
+        memset(&device->sizes,0,sizeof(dasd_sizes_t));
+        device->level = DASD_STATE_ACCEPT;
+        return rc;
+}
+
+static inline int
+dasd_state_ready_to_online (dasd_device_t *device ) 
+{
+        int rc = 0;
+        dasd_unplug_device (device);
+        device->level = DASD_STATE_ONLINE;
+        return rc;
+}
+
+static inline int
+dasd_state_online_to_ready (dasd_device_t *device ) 
+{
+        int rc = 0;
+        dasd_plug_device (device);
+        device->level = DASD_STATE_READY;
+        return rc;
+}
+
+static inline int
+dasd_setup_blkdev (dasd_device_t *device ) 
+{
+        int rc = 0;
+        int i;
+        int major = MAJOR(device->kdev);
+        int minor = MINOR(device->kdev);
+
+        for (i = 0; i < (1 << DASD_PARTN_BITS); i++) {
+                if (i == 0)
+                        device->major_info->gendisk.sizes[minor] =
+                                (device->sizes.blocks << device->
+                                 sizes.s2b_shift) >> 1;
+                else
+                        device->major_info->gendisk.sizes[minor + i] = 0;
+                hardsect_size[major][minor + i] = device->sizes.bp_block;
+                blksize_size[major][minor + i] = device->sizes.bp_block;
+                max_sectors[major][minor + i] =
+                        device->discipline->max_blocks << 
+                        device->sizes.s2b_shift;
+		device->major_info->gendisk.part[minor+i].start_sect = 0;
+		device->major_info->gendisk.part[minor+i].nr_sects = 0;
+        }
+        device->request_queue = kmalloc(sizeof(request_queue_t),GFP_KERNEL);
+        device->request_queue->queuedata = device;
+        blk_init_queue (device->request_queue, do_dasd_request);
+        blk_queue_headactive (device->request_queue, 0);
+        elevator_init (&(device->request_queue->elevator),ELEVATOR_NOOP);
+        return rc;
+}
+
+static inline int
+dasd_disable_blkdev (dasd_device_t *device ) 
+{
+        int i;
+        int major = MAJOR(device->kdev);
+        int minor = MINOR(device->kdev);
+
+        for (i = 0; i < (1 << DASD_PARTN_BITS); i++) {
+                destroy_buffers(MKDEV(major,minor+i));
+                device->major_info->gendisk.sizes[minor + i] = 0;
+                hardsect_size[major][minor + i] = 0;
+                blksize_size[major][minor + i] = 0;
+                max_sectors[major][minor + i] = 0;
+        }
+        if (device->request_queue) {
+            blk_cleanup_queue (device->request_queue);
+            kfree(device->request_queue);
+            device->request_queue = NULL;
+        }
+        return 0;
+}
+
+
+/*
+ * function dasd_setup_partitions
+ * calls the function in genhd, which is appropriate to setup a partitioned disk
+ */
+static inline void
+dasd_setup_partitions ( dasd_device_t * device ) 
+{
+	register_disk (&device->major_info->gendisk,
+                       device->kdev,
+		       1 << DASD_PARTN_BITS,
+		       &dasd_device_operations,
+		       (device->sizes.blocks << device->sizes.s2b_shift));
+}
 
-int
-dasd_oper_handler (int irq, devreg_t * devreg)
+static inline void
+dasd_destroy_partitions ( dasd_device_t * device ) 
 {
-	int devno;
-	int rc;
-	devno = get_devno_by_irq (irq);
-        printk (KERN_WARNING PRINTK_HEADER "Oper handler called\n");
-	if (devno == -ENODEV) {
-          printk (KERN_WARNING PRINTK_HEADER "NODEV\n");
-		return -ENODEV;
+        int i;
+        int major = MAJOR(device->kdev);
+        int minor = MINOR(device->kdev);
+        
+        for (i = 0; i < (1 << DASD_PARTN_BITS); i++) {
+                device->major_info->gendisk.part[minor+i].start_sect = 0;
+                device->major_info->gendisk.part[minor+i].nr_sects   = 0;
         }
-	if (dasd_autodetect) {
-		dasd_add_range (devno, 0);
-	}
-	rc = dasd_enable_single_volume (irq);
-	return rc;
+        devfs_register_partitions(&device->major_info->gendisk,
+                                  MINOR(device->kdev),1);
 }
-#endif	/* CONFIG_DASD_DYNAMIC */
 
-/* 
- * function dasd_set_device_level 
+static inline void
+dasd_resetup_partitions ( dasd_device_t * device ) 
+{
+    BUG();
+    dasd_destroy_partitions ( device ) ;
+    dasd_setup_partitions ( device ) ;
+}
+
+/*
+ * function dasd_set_device_level
  */
 static int
-dasd_set_device_level (unsigned int irq, int desired_level,
-		       dasd_discipline_t * discipline, int flags)
+dasd_set_device_level (unsigned int devno, 
+                       dasd_discipline_t * discipline,
+                       int to_state)
 {
 	int rc = 0;
-	int devno;
-	dasd_device_t **device_addr, *device;
-	int current_level;
-	major_info_t *major_info = NULL;
-	struct list_head *l;
-	int i, minor, major;
-	ccw_req_t *cqr = NULL;
-	struct gendisk *dd;
+        dasd_device_t **device_addr;
+        dasd_device_t *device;
+        int from_state;
 
-	devno = get_devno_by_irq (irq);
-	if (devno < 0) { /* e.g. when device has been detached before */
-		/* search in device list */
-		list_for_each(l,&dasd_major_info[0].list) {
-			major_info = list_entry(l,major_info_t,list);	
-			for (i = 0; i < DASD_PER_MAJOR; i++) {
-				device = major_info->dasd_device[i];
-				if (device && device->devinfo.irq == irq) {
-					devno = device->devinfo.devno;
-					break;
-				}
-			}
-			if (devno == -ENODEV)
-				return -ENODEV;
-		}
-	}
-	if (dasd_devindex_from_devno (devno) < 0) {
-		return -ENODEV;
-	}
-	while ((device_addr = dasd_device_from_devno (devno)) == NULL) {
-		if ((rc = dasd_register_major (NULL)) > 0) {
-			printk (KERN_INFO PRINTK_HEADER
-				"Registered to major number: %u\n", rc);
-		} else {
-			printk (KERN_WARNING PRINTK_HEADER
-				"Couldn't register to another major no\n");
-			return -ERANGE;
-		}
-	}
-	device = *device_addr;
-	if (!device) {		/* allocate device descriptor */
-		device = kmalloc (sizeof (dasd_device_t), GFP_ATOMIC);
-		if (!device) {
-			printk (KERN_WARNING PRINTK_HEADER " No memory for device descriptor\n");
-			goto nomem;
-		}
-		memset (device, 0, sizeof (dasd_device_t));
-		*device_addr = device;
-	}
-	list_for_each(l,&dasd_major_info[0].list) {
-		int i;
-		major_info = list_entry(l,major_info_t,list);	
-		for (i = 0; i < DASD_PER_MAJOR; i++) {
-			if (major_info->dasd_device[i] == device) {
-				device->kdev = MKDEV (major_info->gendisk.major, i << DASD_PARTN_BITS);
-				break;
-			}
-		}
-		if (i < DASD_PER_MAJOR)
-			break;
-	}
-	if (major_info == &dasd_major_info[0]) {
-		return -ENODEV;
-	}
-	minor = MINOR (device->kdev);
-	major = MAJOR (device->kdev);
-	current_level = device->level;
-	if (desired_level > current_level) {
-		switch (current_level) {
-		case DASD_DEVICE_LEVEL_UNKNOWN:	/* Find a discipline */
-                        device->major_info = major_info;
-                        dasd_device_name (device->name,
-                                          ((long) device_addr -
-                                           (long) device->major_info->dasd_device) /
-                                          sizeof (dasd_device_t *),
-                                          0, &major_info->gendisk);
-			rc = get_dev_info_by_irq (irq, &device->devinfo);
-			if (rc < 0) {
-				break;
-			}
-			discipline = dasd_find_discipline (device);
-			if (discipline && !rc) {
-                                DASD_MESSAGE (KERN_INFO, device,
-                                              "%s", " ");
-			} else {
-                                break;
-			}
-			device->discipline = discipline;
-                        device->debug_area = debug_register(device->name,0,2,3*sizeof(long));
-                        debug_register_view(device->debug_area,&debug_sprintf_view);
-                        debug_register_view(device->debug_area,&debug_hex_ascii_view);
-			if (device->discipline->int_handler) {
-#ifdef CONFIG_DASD_DYNAMIC
-				s390_request_irq_special (irq,
-                                                          device->discipline->int_handler,
-                                                          dasd_not_oper_handler,
-							  0,
-							  DASD_NAME,
-							  &device->dev_status);
-#else				/* !defined(CONFIG_DASD_DYNAMIC) */
-				request_irq (irq,
-					     device->discipline->int_handler,
-					     0,
-					     DASD_NAME,
-					     &device->dev_status);
-#endif				/* CONFIG_DASD_DYNAMIC */
-			}
-			device->proc_dir = (struct proc_dir_entry *)
-			    kmalloc (sizeof (struct proc_dir_entry), GFP_KERNEL);
-			if (device->proc_dir) {
-				memset (device->proc_dir, 0, sizeof (struct proc_dir_entry));
-				device->proc_info = (struct proc_dir_entry *)
-				    kmalloc (sizeof (struct proc_dir_entry), GFP_KERNEL);
-				if (device->proc_info) {
-					memset (device->proc_info, 0,
-					      sizeof (struct proc_dir_entry));
-				}
-				device->proc_stats = (struct proc_dir_entry *)
-				    kmalloc (sizeof (struct proc_dir_entry), GFP_KERNEL);
-				if (device->proc_stats) {
-					memset (device->proc_stats, 0,
-					      sizeof (struct proc_dir_entry));
-				}
-			}
-			init_waitqueue_head (&device->wait_q);
-			check_then_set (&device->level,
-				       DASD_DEVICE_LEVEL_UNKNOWN,
-				       DASD_DEVICE_LEVEL_RECOGNIZED);
-			if (desired_level == DASD_DEVICE_LEVEL_RECOGNIZED)
-				break;
-		case DASD_DEVICE_LEVEL_RECOGNIZED:	/* Fallthrough ?? */
-			if (device->discipline->init_analysis) {
-				cqr = device->discipline->init_analysis (device);
-				if (cqr != NULL) {
-					dasd_chanq_enq (&device->queue, cqr);
-					if (device->discipline->start_IO) {
-						long flags;
-						s390irq_spin_lock_irqsave (irq, flags);
-						device->discipline->start_IO (cqr);
-						check_then_set (&device->level,
-                                                                DASD_DEVICE_LEVEL_RECOGNIZED,
-                                                                DASD_DEVICE_LEVEL_ANALYSIS_PENDING);
-						s390irq_spin_unlock_irqrestore (irq, flags);
-					}
-				}
-			} else {
-				check_then_set (&device->level, DASD_DEVICE_LEVEL_RECOGNIZED,
-                                                DASD_DEVICE_LEVEL_ANALYSIS_PREPARED);
-			}
-			if (desired_level >= DASD_DEVICE_LEVEL_ANALYSIS_PENDING)
-				break;
-		case DASD_DEVICE_LEVEL_ANALYSIS_PENDING:	/* Fallthrough ?? */
-			return -EAGAIN;
-		case DASD_DEVICE_LEVEL_ANALYSIS_PREPARED:	/* Re-entering here ! */
-			if (device->discipline->do_analysis)
-                                if (device->discipline->do_analysis (device))
-                                        return -ENODEV;
-			switch (device->sizes.bp_block) {
-			case 512:
-			case 1024:
-			case 2048:
-			case 4096:
-				break;
-			default:
-				{
-					printk (KERN_INFO PRINTK_HEADER
-						"/dev/%s (devno 0x%04X): Detected invalid blocksize of %d bytes"
-						" Did you format the drive?\n",
-						device->name, devno, device->sizes.bp_block);
-					return -EMEDIUMTYPE;
-				}
-			}
-			blk_init_queue (&device->request_queue, do_dasd_request);
-			blk_queue_headactive (&device->request_queue, 0);
-			elevator_init(&device->request_queue.elevator, ELEVATOR_NOOP);
-			for (i = 0; i < (1 << DASD_PARTN_BITS); i++) {
-                                if (i == 0)
-                                        blk_size[major][minor] = (device->sizes.blocks << device->sizes.s2b_shift) >> 1;
-				else
-					blk_size[major][minor + i] = 0;
-				hardsect_size[major][minor + i] = device->sizes.bp_block;
-				blksize_size[major][minor + i] = device->sizes.bp_block;
-				if (blksize_size[major][minor + i] < 1024)
-					blksize_size[major][minor + i] = 1024;
-
-				max_sectors[major][minor + i] =
-                                        device->discipline->max_blocks << device->sizes.s2b_shift;
-			}
-			check_then_set (&device->level,
-				       DASD_DEVICE_LEVEL_ANALYSIS_PREPARED,
-				       DASD_DEVICE_LEVEL_ANALYSED);
-			dd = &major_info->gendisk;
-			dd->sizes[minor] = (device->sizes.blocks <<
-					    device->sizes.s2b_shift) >> 1;
-                        dd->part[minor].start_sect = 0;
-			{
-				char buffer[5];
-				sprintf(buffer,"%04X",device->devinfo.devno);
-				dd->de_arr[minor>>DASD_PARTN_BITS] = devfs_mk_dir(dasd_devfs_handle,buffer,NULL);
-			}
-#if !(LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
-#ifndef MODULE
-			if (flags & 0x80)
+        device_addr = dasd_find_device_addr ( devno );
+        if ( device_addr == NULL ) {
+                rc = -ENODEV;
+                goto out;
+        }
+        device = *device_addr;
+
+        if ( device == NULL ) {
+                from_state = DASD_STATE_DEL;
+                if ( to_state == DASD_STATE_DEL )
+                        goto out;
+        } else {
+                from_state = device->level;
+        }
+
+        if ( from_state == to_state )
+                goto out;
+
+        if ( to_state < from_state )
+                goto shutdown;
+
+        /* First check for bringup */
+        if ( from_state <= DASD_STATE_DEL &&
+             to_state >= DASD_STATE_NEW ) { 
+                rc = dasd_state_del_to_new(device_addr);
+                if ( rc ) {
+                        goto bringup_fail;
+                }
+                device = *device_addr;
+        }
+        if ( from_state <= DASD_STATE_NEW &&
+             to_state >= DASD_STATE_KNOWN ) { 
+                rc = dasd_state_new_to_known( device_addr, devno, discipline );
+                if ( rc ) {
+                        goto bringup_fail;
+                }
+        }
+        if ( from_state <= DASD_STATE_KNOWN &&
+             to_state >= DASD_STATE_ACCEPT ) { 
+                rc = dasd_state_known_to_accept(device);
+                if ( rc ) {
+                        goto bringup_fail;
+                }
+        }
+        if ( dasd_probeonly ) {
+            goto out;
+        }
+        if ( from_state <= DASD_STATE_ACCEPT &&
+             to_state >= DASD_STATE_INIT ) { 
+                rc = dasd_state_accept_to_init(device);
+                if ( rc ) {
+                        goto bringup_fail;
+                }
+        }
+        if ( from_state <= DASD_STATE_INIT &&
+             to_state >= DASD_STATE_READY ) { 
+                rc = -EAGAIN;
+                goto out;
+        }
+        if ( from_state <= DASD_STATE_READY &&
+             to_state >= DASD_STATE_ONLINE ) { 
+                rc = dasd_state_ready_to_online(device);
+                if ( rc ) {
+                        goto bringup_fail;
+                }
+        }
+        goto out;
+ bringup_fail:   /* revert changes */
+#if 0
+        printk (KERN_DEBUG PRINTK_HEADER
+                "failed to set device from state %d to %d at level %d rc %d. Reverting...\n",
+                from_state,to_state,device->level,rc);
 #endif
-#endif				/* KERNEL_VERSION */
-				dasd_partn_detect (device);
-			if (desired_level == DASD_DEVICE_LEVEL_ANALYSED)
-				break;
-		case DASD_DEVICE_LEVEL_ANALYSED:	/* Fallthrough ?? */
-                        dasd_unplug_device(device);
-			check_then_set (&device->level,
-                                        DASD_DEVICE_LEVEL_ANALYSED,
-                                        DASD_DEVICE_LEVEL_ONLINE);
-                        
-			if (desired_level == DASD_DEVICE_LEVEL_ONLINE)
-                                break;
-		case DASD_DEVICE_LEVEL_ONLINE:	
-                        break;
-		default:
-			printk (KERN_WARNING PRINTK_HEADER
-				"Internal error in " __FILE__ " on line %d."
-				" validate_dasd called from %p with "
-				" desired_level = %d, current_level =%d"
-				" Pls send this message and your System.map to"
-				" linux390@de.ibm.com\n",
-				__LINE__, __builtin_return_address (0),
-				desired_level, current_level);
-			break;
-		}
-	} else if (desired_level < current_level) {	/* donwgrade device status */
-		switch (current_level) {
-		case DASD_DEVICE_LEVEL_ONLINE:	/* Fallthrough ?? */
-                        dasd_plug_device(device); 
-			check_then_set (&device->level,
-                                        DASD_DEVICE_LEVEL_ONLINE,
-                                        DASD_DEVICE_LEVEL_ANALYSED);
-			if (desired_level == DASD_DEVICE_LEVEL_ANALYSED)
-                                break;
-                case DASD_DEVICE_LEVEL_ANALYSED:	/* Fallthrough ?? */
-			for (i = 0; i < (1 << DASD_PARTN_BITS); i++) {
-                                __invalidate_buffers(MKDEV(major,minor),1);
-                                blk_size[major][minor] = 0;
-				hardsect_size[major][minor + i] = 0;
-				blksize_size[major][minor + i] = 0;
-				max_sectors[major][minor + i] = 0;
-			}
-			memset (&device->sizes, 0, sizeof (dasd_sizes_t));
-			blk_cleanup_queue (&device->request_queue);
-                        check_then_set (&device->level,
-                                        DASD_DEVICE_LEVEL_ANALYSED,
-                                        DASD_DEVICE_LEVEL_ANALYSIS_PREPARED);
-                        if (desired_level == DASD_DEVICE_LEVEL_ANALYSIS_PREPARED)
-                                break;
-		case DASD_DEVICE_LEVEL_ANALYSIS_PREPARED:
-			check_then_set (&device->level,
-				       DASD_DEVICE_LEVEL_ANALYSIS_PREPARED,
-				       DASD_DEVICE_LEVEL_ANALYSIS_PENDING);
-			if (desired_level == DASD_DEVICE_LEVEL_ANALYSIS_PENDING)
-				break;
-		case DASD_DEVICE_LEVEL_ANALYSIS_PENDING:	/* Fallthrough ?? */
-			check_then_set (&device->level,
-				       DASD_DEVICE_LEVEL_ANALYSIS_PENDING,
-				       DASD_DEVICE_LEVEL_RECOGNIZED);
-			if (desired_level == DASD_DEVICE_LEVEL_RECOGNIZED)
-				break;
-		case DASD_DEVICE_LEVEL_RECOGNIZED:	/* Fallthrough ?? */
-			if (device->discipline->int_handler) {
-				free_irq (irq, &device->dev_status);
-			}
-			device->discipline = NULL;
-                        debug_unregister(device->debug_area);
-			check_then_set (&device->level,
-				       DASD_DEVICE_LEVEL_RECOGNIZED,
-				       DASD_DEVICE_LEVEL_UNKNOWN);
-                        *device_addr = NULL;
-                        kfree(device);
-			if (desired_level == DASD_DEVICE_LEVEL_UNKNOWN)
-				break;
-		case DASD_DEVICE_LEVEL_UNKNOWN:
-			break;
-		default:
-			printk (KERN_WARNING PRINTK_HEADER
-				"Internal error in " __FILE__ " on line %d."
-				" validate_dasd called from %p with "
-				" desired_level = %d, current_level =%d"
-				" Pls send this message and your System.map to"
-				" linux390@de.ibm.com\n",
-				__LINE__, __builtin_return_address (0),
-				desired_level, current_level);
-			break;
-		}
-	}
-	if (rc) {
-		goto exit;
-	}
-      nomem:
-	rc = -ENOMEM;
-      exit:
-	return 0;
+        to_state = from_state;
+        from_state = device->level;
+        
+        /* now do a shutdown */
+ shutdown: 
+        if ( from_state >= DASD_STATE_ONLINE &&
+             to_state <= DASD_STATE_READY ) 
+                if (dasd_state_online_to_ready(device))
+                        BUG();
+        if ( from_state >= DASD_STATE_READY &&
+                      to_state <= DASD_STATE_ACCEPT ) 
+                if ( dasd_state_ready_to_accept(device))
+                        BUG();
+        if ( from_state >= DASD_STATE_ACCEPT &&
+             to_state <= DASD_STATE_KNOWN ) 
+                if ( dasd_state_accept_to_known(device))
+                        BUG();
+        if ( from_state >= DASD_STATE_KNOWN &&
+             to_state <= DASD_STATE_NEW ) 
+                if ( dasd_state_known_to_new(device))
+                        BUG();
+        if ( from_state >= DASD_STATE_NEW &&
+             to_state <= DASD_STATE_DEL) 
+                if (dasd_state_new_to_del(device_addr))
+                        BUG();
+        goto out;
+ out:
+        return rc;
 }
 
 /* SECTION: Procfs stuff */
@@ -2754,29 +3398,16 @@
 	int len;
 } tempinfo_t;
 
-void dasd_fill_inode (struct inode* inode, int fill) {
-    if (fill)
-        MOD_INC_USE_COUNT;
-    else
-        MOD_DEC_USE_COUNT;
+void
+dasd_fill_inode (struct inode *inode, int fill)
+{
+	if (fill)
+		MOD_INC_USE_COUNT;
+	else
+		MOD_DEC_USE_COUNT;
 }
 
-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
 static struct proc_dir_entry *dasd_proc_root_entry = NULL;
-#else
-static struct proc_dir_entry dasd_proc_root_entry =
-{
-	low_ino:0,
-	namelen:4,
-	name:"dasd",
-	mode:S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR | S_IWGRP,
-	nlink:1,
-	uid:0,
-	gid:0,
-	size:0,
-        fill_inode:dasd_fill_inode
-};
-#endif				/* KERNEL_VERSION */
 static struct proc_dir_entry *dasd_devices_entry;
 static struct proc_dir_entry *dasd_statistics_entry;
 
@@ -2786,94 +3417,138 @@
 	int rc = 0;
 	int size = 1;
 	int len = 0;
-	major_info_t *temp = dasd_major_info;
+	major_info_t *temp = NULL;
 	struct list_head *l;
 	tempinfo_t *info;
 	int i;
+        unsigned long flags;
+        int index = 0;
 
+        MOD_INC_USE_COUNT;
+        spin_lock_irqsave(&discipline_lock,flags);
 	info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t));
 	if (info == NULL) {
-		printk (KERN_WARNING "No memory available for data\n");
-		return -ENOMEM;
+                printk (KERN_WARNING "No memory available for data\n");
+                MOD_DEC_USE_COUNT;
+                return -ENOMEM;
 	} else {
 		file->private_data = (void *) info;
 	}
-	list_for_each(l,&dasd_major_info[0].list) {
-		temp = list_entry(l,major_info_t,list);	
-		for (i = 0; i < 1 << (MINORBITS - DASD_PARTN_BITS); i++) {
-			dasd_device_t *device = temp->dasd_device[i];
-			if (device) {
-				size += 128;
-			}
-		}
+	list_for_each (l, &dasd_major_info[0].list) {
+                size += 128 * 1 << (MINORBITS - DASD_PARTN_BITS);
 	}
-	temp = dasd_major_info;
-	info->data = (char *) vmalloc (size);	/* FIXME! determine space needed in a better way */
+	info->data = (char *) vmalloc (size);	
+	DASD_DRIVER_DEBUG_EVENT (1, dasd_devices_open, "area: %p, size 0x%x",
+				 info->data, size);
 	if (size && info->data == NULL) {
 		printk (KERN_WARNING "No memory available for data\n");
 		vfree (info);
+                MOD_DEC_USE_COUNT;
 		return -ENOMEM;
 	}
-	list_for_each(l,&dasd_major_info[0].list) {
-		temp = list_entry(l,major_info_t,list);	
+	list_for_each (l, &dasd_major_info[0].list) {
+		temp = list_entry (l, major_info_t, list);
 		for (i = 0; i < 1 << (MINORBITS - DASD_PARTN_BITS); i++) {
-			dasd_device_t *device = temp->dasd_device[i];
+			dasd_device_t *device;
+                        int devno = dasd_devno_from_devindex(index+i);
+                        if ( devno == -ENODEV )
+                                continue;
+                        device = temp->dasd_device[i];
 			if (device) {
 				len += sprintf (info->data + len,
-						"%04X(%s) at (%d:%d) is %7s:",
+						"%04X(%s) at (%3d:%3d) is %7s:",
 						device->devinfo.devno,
-						device->discipline ? device->discipline->name : "none",
-				    temp->gendisk.major, i << DASD_PARTN_BITS,
+						device->discipline ?
+						device->
+						discipline->name : "none",
+						temp->gendisk.major,
+						i << DASD_PARTN_BITS,
 						device->name);
 				switch (device->level) {
-				case DASD_DEVICE_LEVEL_UNKNOWN:
-					len += sprintf (info->data + len, "unknown\n");
-					break;
-				case DASD_DEVICE_LEVEL_RECOGNIZED:
-					len += sprintf (info->data + len, "passive");
-					len += sprintf (info->data + len, " at blocksize: %d, %ld blocks, %ld MB\n",
-							device->sizes.bp_block,
-							device->sizes.blocks,
-							((device->sizes.bp_block >> 9) * device->sizes.blocks) >> 11);
+				case DASD_STATE_NEW:
+                                        len +=
+					    sprintf (info->data + len,
+						     "new");
+                                        break;
+				case DASD_STATE_KNOWN:
+					len +=
+					    sprintf (info->data + len,
+						     "detected");
 					break;
-				case DASD_DEVICE_LEVEL_ANALYSIS_PENDING:
-					len += sprintf (info->data + len, "busy   \n");
+				case DASD_STATE_ACCEPT:
+                                        len += sprintf (info->data + len,"accepted");
 					break;
-				case DASD_DEVICE_LEVEL_ANALYSIS_PREPARED:
-					len += sprintf (info->data + len, "n/f    \n");
+				case DASD_STATE_INIT:
+					len +=
+					    sprintf (info->data + len,
+						     "busy   ");
 					break;
-				case DASD_DEVICE_LEVEL_ANALYSED:
-					len += sprintf (info->data + len, "active ");
-					len += sprintf (info->data + len, " at blocksize: %d, %ld blocks, %ld MB\n",
-							device->sizes.bp_block,
-							device->sizes.blocks,
-							((device->sizes.bp_block >> 9) * device->sizes.blocks) >> 11);
-					break;
-				case DASD_DEVICE_LEVEL_ONLINE:
-					len += sprintf (info->data + len, "active ");
-					len += sprintf (info->data + len, " at blocksize: %d, %ld blocks, %ld MB\n",
-							device->sizes.bp_block,
-							device->sizes.blocks,
-							((device->sizes.bp_block >> 9) * device->sizes.blocks) >> 11);
+				case DASD_STATE_READY:
+				case DASD_STATE_ONLINE:
+                                    if ( atomic_read(&device->plugged) )
+                                        len +=
+                                            sprintf (info->data + len,
+                                                     "fenced ");
+                                    else
+                                        len +=
+                                            sprintf (info->data + len,
+                                                     "active ");
+                                    if ( device->sizes.bp_block == 512 ||
+                                         device->sizes.bp_block == 1024 ||
+                                         device->sizes.bp_block == 2048 ||
+                                         device->sizes.bp_block == 4096 )
+					len +=
+					    sprintf (info->data + len,
+						     "at blocksize: %d, %ld blocks, %ld MB",
+						     device->sizes.bp_block,
+						     device->sizes.blocks,
+						     ((device->
+						       sizes.bp_block >> 9) *
+						      device->sizes.
+						      blocks) >> 11);
+                                    else
+                                        len +=
+                                            sprintf (info->data + len,
+                                                     "n/f    ");
 					break;
 				default:
-					len += sprintf (info->data + len, "no stat\n");
+					len +=
+					    sprintf (info->data + len,
+						     "no stat");
 					break;
 				}
-			}
+			} else {
+                                char buffer[7];
+                                dasd_device_name (buffer, i, 0, &temp->gendisk);
+                                if ( devno < 0  ) {
+                                        len += sprintf (info->data + len,
+                                                        "none");
+                                } else {
+                                        len += sprintf (info->data + len,
+                                                        "%04X",devno);
+                                }
+                                len += sprintf (info->data + len,
+                                                "(none) at (%3d:%3d) is %7s: unknown",
+						temp->gendisk.major,
+						i << DASD_PARTN_BITS,
+						buffer);
+                        }
+                        if ( dasd_probeonly )
+                            len += sprintf(info->data + len,"(probeonly)");
+                        len += sprintf(info->data + len,"\n");
 		}
+                index += 1 << (MINORBITS - DASD_PARTN_BITS);
 	}
 	info->len = len;
-#ifdef MODULE
-        MOD_INC_USE_COUNT;
-#endif
+        spin_unlock_irqrestore(&discipline_lock,flags);
 	return rc;
 }
 
 #define MIN(a,b) ((a)<(b)?(a):(b))
 
 static ssize_t
-dasd_devices_read (struct file *file, char *user_buf, size_t user_len, loff_t * offset)
+dasd_generic_read (struct file *file, char *user_buf, size_t user_len,
+		   loff_t * offset)
 {
 	loff_t len;
 	tempinfo_t *p_info = (tempinfo_t *) file->private_data;
@@ -2890,85 +3565,68 @@
 }
 
 static ssize_t
-dasd_devices_write (struct file *file, const char *user_buf, size_t user_len, loff_t * offset)
+dasd_devices_write (struct file *file, const char *user_buf,
+		    size_t user_len, loff_t * offset)
 {
 	char *buffer = vmalloc (user_len+1);
 	int off = 0;
 	char *temp;
-        int irq;
-        int j,target;
-        dasd_range_t *rptr, range;
+	dasd_range_t range;
+        int features = 0;
 
 	if (buffer == NULL)
 		return -ENOMEM;
 	if (copy_from_user (buffer, user_buf, user_len)) {
-		vfree(buffer);
+		vfree (buffer);
 		return -EFAULT;
 	}
 	buffer[user_len] = 0;
-	printk (KERN_INFO PRINTK_HEADER "Now executing %s\n", buffer);
-        if (strncmp ( buffer, "set ",4) &&
-            strncmp ( buffer, "add ",4)){
-                printk (KERN_WARNING PRINTK_HEADER 
-                        "/proc/dasd/devices: only 'set' and 'add' are supported verbs");
-                return -EINVAL;
-        }
-        off += 4;
-        while (!isalnum(buffer[off])) off++;
+	printk (KERN_INFO PRINTK_HEADER "/proc/dasd/devices: '%s'\n", buffer);
+	if (strncmp (buffer, "set ", 4) && strncmp (buffer, "add ", 4)) {
+		printk (KERN_WARNING PRINTK_HEADER
+			"/proc/dasd/devices: only 'set' and 'add' are supported verbs\n");
+		return -EINVAL;
+	}
+	off += 4;
+	while (buffer[off] && !isalnum (buffer[off]))
+		off++;
 	if (!strncmp (buffer + off, "device", strlen ("device"))) {
-		off += strlen("device");
-		while (!isalnum(buffer[off])) off++;
-        }
+		off += strlen ("device");
+		while (buffer[off] && !isalnum (buffer[off]))
+			off++;
+	}
 	if (!strncmp (buffer + off, "range=", strlen ("range="))) {
-		off += strlen("range=");
-		while (!isalnum(buffer[off])) off++;
-        }
-        temp = buffer+off;
-        range.from = dasd_strtoul (temp, &temp);
-        range.to = range.from;
-        if (*temp == '-') {
-                temp++;
-                range.to = dasd_strtoul (temp, &temp);
-        }
-        off = (long)temp - (long)buffer;
-        if ( !strncmp ( buffer, "add",strlen("add"))) {
-                rptr = dasd_add_range (range.from, range.to);
-        } else {
-                rptr = &range;
-        }
-        while (!isalnum(buffer[off])) off++;
-        printk (KERN_INFO PRINTK_HEADER 
-                "varying device range %04X-%04X\n", rptr->from, rptr->to);
-        if ( !strncmp ( buffer, "add",strlen("add")) ||
-             !strncmp ( buffer+off, "on",strlen("on")) ) {
-                target = DASD_DEVICE_LEVEL_ONLINE;
-                for (j = rptr->from; j <= rptr->to; j++) {
-			irq = get_irq_by_devno (j);
-			if (irq >= 0) {
-				dasd_set_device_level (irq, DASD_DEVICE_LEVEL_ONLINE, NULL, 0);
-			}
-		}
-		printk (KERN_INFO PRINTK_HEADER "waiting for responses...\n");
-		{
-			static wait_queue_head_t wait_queue;
-			init_waitqueue_head (&wait_queue);
-			interruptible_sleep_on_timeout (&wait_queue, (5 * HZ) );
+		off += strlen ("range=");
+		while (buffer[off] && !isalnum (buffer[off]))
+			off++;
+	}
+	
+	temp = buffer + off;
+	range.from = dasd_strtoul (temp, &temp, &features);
+	range.to = range.from;
+
+	if (*temp == '-') {
+		temp++;
+		range.to = dasd_strtoul (temp, &temp, &features);
+	}
+
+	off = (long) temp - (long) buffer;
+	if (!strncmp (buffer, "add", strlen ("add"))) {
+		dasd_add_range (range.from, range.to, features);
+                dasd_enable_ranges (&range, NULL, 0);
+	} else { 
+		while (buffer[off] && !isalnum (buffer[off]))
+			off++;
+		if (!strncmp (buffer + off, "on", strlen ("on"))) {
+        	        dasd_enable_ranges (&range, NULL, 0);
+		} else if (!strncmp (buffer + off, "off", strlen ("off"))) {
+        	        dasd_disable_ranges (&range, NULL, 0, 1);
+		} else {
+			printk (KERN_WARNING PRINTK_HEADER
+			"/proc/dasd/devices: parse error in '%s'", buffer);
 		}
-        } else if ( !strncmp ( buffer+off, "off",strlen("off"))) {
-                target = DASD_DEVICE_LEVEL_UNKNOWN;
-        } else {
-                printk (KERN_WARNING PRINTK_HEADER 
-                        "/proc/dasd/devices: parse error in '%s'", buffer);
-                vfree (buffer);
-                return -EINVAL;
-                
-        }
-        for (j = rptr->from; j <= rptr->to; j++) {
-                irq = get_irq_by_devno (j);
-                if (irq >= 0) {
-                        dasd_set_device_level (irq, target, NULL, 0);
-                }
-        }
+	}
+	vfree (buffer);
 	return user_len;
 }
 
@@ -2982,25 +3640,18 @@
 			vfree (p_info->data);
 		vfree (p_info);
 	}
-#ifdef MODULE
-        MOD_DEC_USE_COUNT;
-#endif
+	MOD_DEC_USE_COUNT;
 	return rc;
 }
 
-static struct file_operations dasd_devices_file_ops =
-{
-	read:dasd_devices_read,	/* read */
+static struct file_operations dasd_devices_file_ops = {
+	read:dasd_generic_read,	/* read */
 	write:dasd_devices_write,	/* write */
 	open:dasd_devices_open,	/* open */
 	release:dasd_devices_close,	/* close */
 };
 
-static struct inode_operations dasd_devices_inode_ops =
-{
-#if !(LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
-	default_file_ops:&dasd_devices_file_ops		/* file ops */
-#endif				/* LINUX_IS_24 */
+static struct inode_operations dasd_devices_inode_ops = {
 };
 
 static int
@@ -3011,9 +3662,11 @@
 	tempinfo_t *info;
 	int shift, i, help = 0;
 
+        MOD_INC_USE_COUNT;
 	info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t));
 	if (info == NULL) {
 		printk (KERN_WARNING "No memory available for data\n");
+                MOD_DEC_USE_COUNT;
 		return -ENOMEM;
 	} else {
 		file->private_data = (void *) info;
@@ -3023,99 +3676,154 @@
 		printk (KERN_WARNING "No memory available for data\n");
 		vfree (info);
 		file->private_data = NULL;
+                MOD_DEC_USE_COUNT;
 		return -ENOMEM;
 	}
 	for (shift = 0, help = dasd_global_profile.dasd_io_reqs;
-	     help > 8192;
-	     help = help >> 1, shift++) ;
-	len = sprintf (info->data, "%ld dasd I/O requests\n", dasd_global_profile.dasd_io_reqs);
-	len += sprintf (info->data + len, "__<4 ___8 __16 __32 __64 _128 _256 _512 __1k __2k __4k __8k _16k _32k _64k 128k\n");
-	len += sprintf (info->data + len, "_256 _512 __1M __2M __4M __8M _16M _32M _64M 128M 256M 512M __1G __2G __4G _>4G\n");
+	     help > 8192; help = help >> 1, shift++) ;
+	len =
+	    sprintf (info->data, "%d dasd I/O requests\n",
+		     dasd_global_profile.dasd_io_reqs);
+	len +=
+	    sprintf (info->data + len, "with %d sectors(512B each)\n",
+		     dasd_global_profile.dasd_io_sects);
+	len +=
+	    sprintf (info->data + len,
+		     "__<4 ___8 __16 __32 __64 _128 _256 _512 __1k __2k __4k __8k _16k _32k _64k 128k\n");
+	len +=
+	    sprintf (info->data + len,
+		     "_256 _512 __1M __2M __4M __8M _16M _32M _64M 128M 256M 512M __1G __2G __4G _>4G\n");
 	len += sprintf (info->data + len, "Histogram of sizes (512B secs)\n");
 	for (i = 0; i < 16; i++) {
-		len += sprintf (info->data + len, "%4ld ", dasd_global_profile.dasd_io_secs[i] >> shift);
+		len +=
+		    sprintf (info->data + len, "%4d ",
+			     dasd_global_profile.dasd_io_secs[i] >> shift);
 	}
 	len += sprintf (info->data + len, "\n");
 	len += sprintf (info->data + len, "Histogram of I/O times\n");
 	for (i = 0; i < 16; i++) {
-		len += sprintf (info->data + len, "%4ld ", dasd_global_profile.dasd_io_times[i] >> shift);
+		len +=
+		    sprintf (info->data + len, "%4d ",
+			     dasd_global_profile.dasd_io_times[i] >> shift);
 	}
 	len += sprintf (info->data + len, "\n");
 	for (; i < 32; i++) {
-		len += sprintf (info->data + len, "%4ld ", dasd_global_profile.dasd_io_times[i] >> shift);
+		len +=
+		    sprintf (info->data + len, "%4d ",
+			     dasd_global_profile.dasd_io_times[i] >> shift);
 	}
 	len += sprintf (info->data + len, "\n");
-	len += sprintf (info->data + len, "Histogram of I/O times per sector\n");
+	len +=
+	    sprintf (info->data + len, "Histogram of I/O times per sector\n");
 	for (i = 0; i < 16; i++) {
-		len += sprintf (info->data + len, "%4ld ", dasd_global_profile.dasd_io_timps[i] >> shift);
+		len +=
+		    sprintf (info->data + len, "%4d ",
+			     dasd_global_profile.dasd_io_timps[i] >> shift);
 	}
 	len += sprintf (info->data + len, "\n");
 	for (; i < 32; i++) {
-		len += sprintf (info->data + len, "%4ld ", dasd_global_profile.dasd_io_timps[i] >> shift);
+		len +=
+		    sprintf (info->data + len, "%4d ",
+			     dasd_global_profile.dasd_io_timps[i] >> shift);
 	}
 	len += sprintf (info->data + len, "\n");
 	len += sprintf (info->data + len, "Histogram of I/O time till ssch\n");
 	for (i = 0; i < 16; i++) {
-		len += sprintf (info->data + len, "%4ld ", dasd_global_profile.dasd_io_time1[i] >> shift);
+		len +=
+		    sprintf (info->data + len, "%4d ",
+			     dasd_global_profile.dasd_io_time1[i] >> shift);
 	}
 	len += sprintf (info->data + len, "\n");
 	for (; i < 32; i++) {
-		len += sprintf (info->data + len, "%4ld ", dasd_global_profile.dasd_io_time1[i] >> shift);
+		len +=
+		    sprintf (info->data + len, "%4d ",
+			     dasd_global_profile.dasd_io_time1[i] >> shift);
 	}
 	len += sprintf (info->data + len, "\n");
-	len += sprintf (info->data + len, "Histogram of I/O time between ssch and irq\n");
+	len +=
+	    sprintf (info->data + len,
+		     "Histogram of I/O time between ssch and irq\n");
 	for (i = 0; i < 16; i++) {
-		len += sprintf (info->data + len, "%4ld ", dasd_global_profile.dasd_io_time2[i] >> shift);
+		len +=
+		    sprintf (info->data + len, "%4d ",
+			     dasd_global_profile.dasd_io_time2[i] >> shift);
 	}
 	len += sprintf (info->data + len, "\n");
 	for (; i < 32; i++) {
-		len += sprintf (info->data + len, "%4ld ", dasd_global_profile.dasd_io_time2[i] >> shift);
+		len +=
+		    sprintf (info->data + len, "%4d ",
+			     dasd_global_profile.dasd_io_time2[i] >> shift);
 	}
 	len += sprintf (info->data + len, "\n");
-	len += sprintf (info->data + len, "Histogram of I/O time between ssch and irq per sector\n");
+	len +=
+	    sprintf (info->data + len,
+		     "Histogram of I/O time between ssch and irq per sector\n");
 	for (i = 0; i < 16; i++) {
-		len += sprintf (info->data + len, "%4ld ", dasd_global_profile.dasd_io_time2ps[i] >> shift);
+		len +=
+		    sprintf (info->data + len, "%4d ",
+			     dasd_global_profile.dasd_io_time2ps[i] >> shift);
 	}
 	len += sprintf (info->data + len, "\n");
 	for (; i < 32; i++) {
-		len += sprintf (info->data + len, "%4ld ", dasd_global_profile.dasd_io_time2ps[i] >> shift);
+		len +=
+		    sprintf (info->data + len, "%4d ",
+			     dasd_global_profile.dasd_io_time2ps[i] >> shift);
 	}
 	len += sprintf (info->data + len, "\n");
-	len += sprintf (info->data + len, "Histogram of I/O time between irq and end\n");
+	len +=
+	    sprintf (info->data + len,
+		     "Histogram of I/O time between irq and end\n");
 	for (i = 0; i < 16; i++) {
-		len += sprintf (info->data + len, "%4ld ", dasd_global_profile.dasd_io_time3[i] >> shift);
+		len +=
+		    sprintf (info->data + len, "%4d ",
+			     dasd_global_profile.dasd_io_time3[i] >> shift);
 	}
 	len += sprintf (info->data + len, "\n");
 	for (; i < 32; i++) {
-		len += sprintf (info->data + len, "%4ld ", dasd_global_profile.dasd_io_time3[i] >> shift);
+		len +=
+		    sprintf (info->data + len, "%4d ",
+			     dasd_global_profile.dasd_io_time3[i] >> shift);
 	}
 	len += sprintf (info->data + len, "\n");
 	info->len = len;
-#ifdef MODULE
-        MOD_INC_USE_COUNT;
-#endif
 	return rc;
 }
 
-static struct file_operations dasd_statistics_file_ops =
+static ssize_t
+dasd_statistics_write (struct file *file, const char *user_buf,
+		       size_t user_len, loff_t * offset)
 {
-	read:dasd_devices_read,	/* read */
+	char *buffer = vmalloc (user_len);
+
+	if (buffer == NULL)
+		return -ENOMEM;
+	if (copy_from_user (buffer, user_buf, user_len)) {
+		vfree (buffer);
+		return -EFAULT;
+	}
+	buffer[user_len] = 0;
+	printk (KERN_INFO PRINTK_HEADER "/proc/dasd/statictics: '%s'\n",
+		buffer);
+	if (strncmp (buffer, "reset", 4)) {
+		memset (&dasd_global_profile, 0, sizeof (dasd_profile_info_t));
+	}
+	return user_len;
+}
+
+static struct file_operations dasd_statistics_file_ops = {
+	read:dasd_generic_read,	/* read */
 	open:dasd_statistics_open,	/* open */
+	write:dasd_statistics_write,	/* write */
 	release:dasd_devices_close,	/* close */
 };
 
-static struct inode_operations dasd_statistics_inode_ops =
-{
-#if !(LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
-	default_file_ops:&dasd_statistics_file_ops	/* file ops */
-#endif				/* LINUX_IS_24 */
+static struct inode_operations dasd_statistics_inode_ops = {
 };
 
 int
 dasd_proc_init (void)
 {
 	int rc = 0;
-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
 	dasd_proc_root_entry = proc_mkdir ("dasd", &proc_root);
 	dasd_devices_entry = create_proc_entry ("devices",
 						S_IFREG | S_IRUGO | S_IWUSR,
@@ -3127,204 +3835,175 @@
 						   dasd_proc_root_entry);
 	dasd_statistics_entry->proc_fops = &dasd_statistics_file_ops;
 	dasd_statistics_entry->proc_iops = &dasd_statistics_inode_ops;
-#else
-	proc_register (&proc_root, &dasd_proc_root_entry);
-	dasd_devices_entry = (struct proc_dir_entry *) kmalloc (sizeof (struct proc_dir_entry), GFP_ATOMIC);
-	if (dasd_devices_entry) {
-		memset (dasd_devices_entry, 0, sizeof (struct proc_dir_entry));
-		dasd_devices_entry->name = "devices";
-		dasd_devices_entry->namelen = strlen ("devices");
-		dasd_devices_entry->low_ino = 0;
-		dasd_devices_entry->mode = (S_IFREG | S_IRUGO | S_IWUSR);
-		dasd_devices_entry->nlink = 1;
-		dasd_devices_entry->uid = 0;
-		dasd_devices_entry->gid = 0;
-		dasd_devices_entry->size = 0;
-		dasd_devices_entry->get_info = NULL;
-		dasd_devices_entry->ops = &dasd_devices_inode_ops;
-		proc_register (&dasd_proc_root_entry, dasd_devices_entry);
-	}
-	dasd_statistics_entry = (struct proc_dir_entry *) kmalloc (sizeof (struct proc_dir_entry), GFP_ATOMIC);
-	if (dasd_statistics_entry) {
-		memset (dasd_statistics_entry, 0, sizeof (struct proc_dir_entry));
-		dasd_statistics_entry->name = "statistics";
-		dasd_statistics_entry->namelen = strlen ("statistics");
-		dasd_statistics_entry->low_ino = 0;
-		dasd_statistics_entry->mode = (S_IFREG | S_IRUGO | S_IWUSR);
-		dasd_statistics_entry->nlink = 1;
-		dasd_statistics_entry->uid = 0;
-		dasd_statistics_entry->gid = 0;
-		dasd_statistics_entry->size = 0;
-		dasd_statistics_entry->get_info = NULL;
-		dasd_statistics_entry->ops = &dasd_statistics_inode_ops;
-		proc_register (&dasd_proc_root_entry, dasd_statistics_entry);
-	}
-#endif				/* LINUX_IS_24 */
 	return rc;
 }
 
 void
 dasd_proc_cleanup (void)
 {
-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
 	remove_proc_entry ("devices", dasd_proc_root_entry);
 	remove_proc_entry ("statistics", dasd_proc_root_entry);
 	remove_proc_entry ("dasd", &proc_root);
-#else
-	proc_unregister (&dasd_proc_root_entry, dasd_statistics_entry->low_ino);
-	kfree (dasd_statistics_entry);
-	proc_unregister (&dasd_proc_root_entry, dasd_devices_entry->low_ino);
-	kfree (dasd_devices_entry);
-	proc_unregister (&proc_root, dasd_proc_root_entry.low_ino);
-#endif				/* LINUX_IS_24 */
 }
 
+int
+dasd_request_module ( void *name ) {
+	int rc = -ERESTARTSYS;
+    	strcpy(current->comm, name);
+   	daemonize();
+   	while ( current->fs->root == NULL ) { /* wait for root-FS */
+        	DECLARE_WAIT_QUEUE_HEAD(wait);
+        	sleep_on_timeout(&wait,HZ); /* wait in steps of one second */
+	} 
+	while ( (rc=request_module(name)) != 0 ) {
+        	DECLARE_WAIT_QUEUE_HEAD(wait);
+		printk ( KERN_INFO "request_module returned %d for %s\n",rc,(char*)name);
+        	sleep_on_timeout(&wait,5* HZ); /* wait in steps of 5 seconds */
+    	}
+    	return rc;
+}
+
+
 /* SECTION: Initializing the driver */
 int __init
 dasd_init (void)
 {
 	int rc = 0;
 	int irq;
-	int j;
-	major_info_t *major_info=NULL;
+	major_info_t *major_info = NULL;
 	struct list_head *l;
-	dasd_range_t *range;
 
 	printk (KERN_INFO PRINTK_HEADER "initializing...\n");
-        dasd_debug_area = debug_register(DASD_NAME,0,2,3*sizeof(long));
-        debug_register_view(dasd_debug_area,&debug_sprintf_view);
-        debug_register_view(dasd_debug_area,&debug_hex_ascii_view);
-        
-        if ( dasd_debug_area == NULL ) {
-                goto failed;
-        }
-        DASD_DRIVER_DEBUG_EVENT(0,dasd_init,"%s","ENTRY");
-	dasd_devfs_handle = devfs_mk_dir(NULL,DASD_NAME,NULL);
-        if ( dasd_devfs_handle < 0 ) {
-                DASD_DRIVER_DEBUG_EVENT(1,dasd_init,"%s","no devfs");
-                goto failed;
-        }
-	list_for_each(l,&dasd_major_info[0].list) {
-		major_info=list_entry(l,major_info_t,list);
-                if ((rc = dasd_register_major (major_info)) > 0) {
-                        DASD_DRIVER_DEBUG_EVENT(1,dasd_init,
-                                                "major %d: success",major_info->gendisk.major);
-                        printk (KERN_INFO PRINTK_HEADER
-                                "Registered successfully to major no %u\n", major_info->gendisk.major);
+	dasd_debug_area = debug_register (DASD_NAME, 0, 2, 4 * sizeof (long));
+	debug_register_view (dasd_debug_area, &debug_sprintf_view);
+	debug_register_view (dasd_debug_area, &debug_hex_ascii_view);
+
+	init_waitqueue_head (&dasd_init_waitq);
+
+	if (dasd_debug_area == NULL) {
+		goto failed;
+	}
+	DASD_DRIVER_DEBUG_EVENT (0, dasd_init, "%s", "ENTRY");
+	dasd_devfs_handle = devfs_mk_dir (NULL, DASD_NAME, NULL);
+	if (dasd_devfs_handle < 0) {
+		DASD_DRIVER_DEBUG_EVENT (1, dasd_init, "%s", "no devfs");
+		goto failed;
+	}
+	list_for_each (l, &dasd_major_info[0].list) {
+		major_info = list_entry (l, major_info_t, list);
+		if ((rc = dasd_register_major (major_info)) > 0) {
+			DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
+						 "major %d: success",
+						 major_info->gendisk.major);
+			printk (KERN_INFO PRINTK_HEADER
+				"Registered successfully to major no %u\n",
+				major_info->gendisk.major);
 		} else {
-                        DASD_DRIVER_DEBUG_EVENT(1,dasd_init,
-                                                "major %d: failed",major_info->gendisk.major);
+			DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
+						 "major %d: failed",
+						 major_info->gendisk.major);
 			printk (KERN_WARNING PRINTK_HEADER
-				"Couldn't register successfully to major no %d\n", major_info->gendisk.major);
+				"Couldn't register successfully to major no %d\n",
+				major_info->gendisk.major);
 			/* revert registration of major infos */
-                        goto failed;
+			goto failed;
 		}
 	}
 #ifndef MODULE
 	dasd_split_parm_string (dasd_parm_string);
 #endif				/* ! MODULE */
 	dasd_parse (dasd);
-	dasd_init_emergency_req ();
 
 	rc = dasd_proc_init ();
 	if (rc) {
-                DASD_DRIVER_DEBUG_EVENT(1,dasd_init,
-                                        "%s","no proc-FS");
-                goto failed;
+		DASD_DRIVER_DEBUG_EVENT (1, dasd_init, "%s", "no proc-FS");
+		goto failed;
 	}
 	genhd_dasd_name = dasd_device_name;
-	genhd_dasd_fillgeo = dasd_fillgeo;
 
+	if (dasd_autodetect) {	/* update device range to all devices */
+		for (irq = get_irq_first (); irq != -ENODEV;
+		     irq = get_irq_next (irq)) {
+			int devno = get_devno_by_irq (irq);
+			int index = dasd_devindex_from_devno (devno);
+			if (index == -ENODEV) {	/* not included in ranges */
+				DASD_DRIVER_DEBUG_EVENT (2, dasd_init,
+							 "add %04X to range",
+							 devno);
+				dasd_add_range (devno, devno, DASD_DEFAULT_FEATURES);
+			}
+		}
+	}
+
+	if (MACHINE_IS_VM) {
+#ifdef CONFIG_DASD_DIAG
+		rc = dasd_diag_init ();
+		if (rc == 0) {
+			DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
+						 "DIAG discipline %s",
+						 "success");
+			printk (KERN_INFO PRINTK_HEADER
+				"Registered DIAG discipline successfully\n");
+		} else {
+			DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
+						 "DIAG discipline %s",
+						 "failed");
+			goto failed;
+		}
+#endif /* CONFIG_DASD_DIAG */
+#if defined(CONFIG_DASD_DIAG_MODULE) && defined(CONFIG_DASD_AUTO_DIAG)
+                kernel_thread(dasd_request_module,"dasd_diag_mod",SIGCHLD);
+#endif /* CONFIG_DASD_AUTO_DIAG */
+	}
 #ifdef CONFIG_DASD_ECKD
 	rc = dasd_eckd_init ();
 	if (rc == 0) {
-                DASD_DRIVER_DEBUG_EVENT(1,dasd_init,
-                                        "ECKD discipline %s","success");
+		DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
+					 "ECKD discipline %s", "success");
 		printk (KERN_INFO PRINTK_HEADER
 			"Registered ECKD discipline successfully\n");
 	} else {
-                DASD_DRIVER_DEBUG_EVENT(1,dasd_init,
-                                        "ECKD discipline %s","failed");
-                goto failed;
-	}
-#endif				/* CONFIG_DASD_ECKD */
+		DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
+					 "ECKD discipline %s", "failed");
+		goto failed;
+	}
+#endif /* CONFIG_DASD_ECKD */
+#if defined(CONFIG_DASD_ECKD_MODULE) && defined(CONFIG_DASD_AUTO_ECKD)
+        kernel_thread(dasd_request_module,"dasd_eckd_mod",SIGCHLD);
+#endif /* CONFIG_DASD_AUTO_ECKD */
 #ifdef CONFIG_DASD_FBA
 	rc = dasd_fba_init ();
 	if (rc == 0) {
-                DASD_DRIVER_DEBUG_EVENT(1,dasd_init,
-                                        "FBA discipline %s","success");
+		DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
+					 "FBA discipline %s", "success");
 
 		printk (KERN_INFO PRINTK_HEADER
 			"Registered FBA discipline successfully\n");
 	} else {
-                DASD_DRIVER_DEBUG_EVENT(1,dasd_init,
-                                        "FBA discipline %s","failed");
-                goto failed;
-	}
-#endif				/* CONFIG_DASD_FBA */
-#ifdef CONFIG_DASD_DIAG
-	if (MACHINE_IS_VM) {
-		rc = dasd_diag_init ();
-		if (rc == 0) {
-                        DASD_DRIVER_DEBUG_EVENT(1,dasd_init,
-                                                "DIAG discipline %s","success");
-			printk (KERN_INFO PRINTK_HEADER
-				"Registered DIAG discipline successfully\n");
-		} else {
-                        DASD_DRIVER_DEBUG_EVENT(1,dasd_init,
-                                                "DIAG discipline %s","failed");
-                        goto failed;
-		}
-	}
-#endif				/* CONFIG_DASD_DIAG */
-	rc = 0;
-	if (dasd_autodetect) { /* update device range to all devices */
-		for (irq = get_irq_first (); irq != -ENODEV; 
-                     irq = get_irq_next (irq)) {
-			int devno = get_devno_by_irq (irq);
-			int index = dasd_devindex_from_devno (devno);
-			if (index == -ENODEV) {		/* not included in ranges */
-                                DASD_DRIVER_DEBUG_EVENT(2,dasd_init,
-                                                        "add %04X to range",
-                                                        devno);
-				dasd_add_range (devno, 0);
-                        }
+		DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
+					 "FBA discipline %s", "failed");
+		goto failed;
+	}
+#endif /* CONFIG_DASD_FBA */
+#if defined(CONFIG_DASD_FBA_MODULE) && defined(CONFIG_DASD_AUTO_FBA)
+        kernel_thread(dasd_request_module,"dasd_fba_mod",SIGCHLD);
+#endif /* CONFIG_DASD_AUTO_FBA */
+        {
+                char **disc=dasd_disciplines;
+                while (*disc) {
+                        kernel_thread(dasd_request_module,*disc,SIGCHLD);
+                        disc++;
                 }
         }
-	for (range = dasd_range_head; range; range = range->next) {
-		for (j = range->from; j <= range->to; j++) {
-			irq = get_irq_by_devno (j);
-                        if (irq >= 0)
-                                DASD_DRIVER_DEBUG_EVENT(2,dasd_init,
-                                                        "1st step in initialization irq 0x%x",irq);
-                        dasd_set_device_level (irq, DASD_DEVICE_LEVEL_ONLINE,
-                                               NULL, 0);
-		}
-	}
-	printk (KERN_INFO PRINTK_HEADER "waiting for responses...\n");
-	{
-		static wait_queue_head_t wait_queue;
-		init_waitqueue_head (&wait_queue);
-		interruptible_sleep_on_timeout (&wait_queue,
-						(5 * HZ) );
-	}
-	for (range = dasd_range_head; range; range = range->next) {
-		for (j = range->from; j <= range->to; j++) {
-			irq = get_irq_by_devno (j);
-			if (irq >= 0) {
-                                DASD_DRIVER_DEBUG_EVENT(2,dasd_init,
-                                                        "2nd step in initialization irq 0x%x",irq);
-				dasd_set_device_level (irq, DASD_DEVICE_LEVEL_ONLINE,
-						       NULL, 0);
-			}
-		}
-	}
-        goto out;
- failed:
-	printk (KERN_INFO PRINTK_HEADER "initialization not performed due to errors\n");
-        cleanup_dasd();
- out:
-        DASD_DRIVER_DEBUG_EVENT(0,dasd_init,"%s","LEAVE");
+
+	rc = 0;
+	goto out;
+      failed:
+	printk (KERN_INFO PRINTK_HEADER
+		"initialization not performed due to errors\n");
+	cleanup_dasd ();
+      out:
+	DASD_DRIVER_DEBUG_EVENT (0, dasd_init, "%s", "LEAVE");
 	printk (KERN_INFO PRINTK_HEADER "initialization finished\n");
 	return rc;
 }
@@ -3332,95 +4011,78 @@
 static void
 cleanup_dasd (void)
 {
-	int i,j,rc;
-	int irq;
-	major_info_t *major_info=NULL;
-	struct list_head *l;
-	dasd_range_t *range, *next;
+	int i,rc=0;
+	major_info_t *major_info = NULL;
+	struct list_head *l,*n;
+	dasd_range_t *range;
 
 	printk (KERN_INFO PRINTK_HEADER "shutting down\n");
         DASD_DRIVER_DEBUG_EVENT(0,"cleanup_dasd","%s","ENTRY");
-	for (range = dasd_range_head; range; range = range->next) {
-		for (j = range->from; j <= range->to; j++) {
-			irq = get_irq_by_devno (j);
-			if (irq >= 0) {
-                                DASD_DRIVER_DEBUG_EVENT(2,"cleanup_dasd",
-                                                        "shutdown irq 0x%x",irq);
-				dasd_set_device_level (irq, DASD_DEVICE_LEVEL_UNKNOWN,
-						       NULL, 0);
-			}
-		}
-	}
+	dasd_disable_ranges (&dasd_range_head, NULL, 1, 1);
+        if (MACHINE_IS_VM) {
 #ifdef CONFIG_DASD_DIAG
-	if (MACHINE_IS_VM) {
-		dasd_diag_cleanup ();
-                DASD_DRIVER_DEBUG_EVENT(1,"cleanup_dasd",
-                                        "DIAG discipline %s","success");
+                dasd_diag_cleanup ();
+                DASD_DRIVER_DEBUG_EVENT (1, "cleanup_dasd",
+                                         "DIAG discipline %s", "success");
                 printk (KERN_INFO PRINTK_HEADER
-                        "De-Registered DIAG discipline successfully\n");
+			"De-Registered DIAG discipline successfully\n");
+#endif /* CONFIG_DASD_ECKD_BUILTIN */
 	}
-#endif				/* CONFIG_DASD_DIAG */
 #ifdef CONFIG_DASD_FBA
 	dasd_fba_cleanup ();
-        DASD_DRIVER_DEBUG_EVENT(1,"cleanup_dasd",
-                                "FBA discipline %s","success");
-        printk (KERN_INFO PRINTK_HEADER
-                "De-Registered FBA discipline successfully\n");
-#endif				/* CONFIG_DASD_FBA */
+	DASD_DRIVER_DEBUG_EVENT (1, "cleanup_dasd",
+				 "FBA discipline %s", "success");
+	printk (KERN_INFO PRINTK_HEADER
+		"De-Registered FBA discipline successfully\n");
+#endif /* CONFIG_DASD_ECKD_BUILTIN */
 #ifdef CONFIG_DASD_ECKD
 	dasd_eckd_cleanup ();
-        DASD_DRIVER_DEBUG_EVENT(1,"cleanup_dasd",
-                                "ECKD discipline %s","success");
-        printk (KERN_INFO PRINTK_HEADER
-                "De-Registered ECKD discipline successfully\n");
-#endif				/* CONFIG_DASD_ECKD */
+	DASD_DRIVER_DEBUG_EVENT (1, "cleanup_dasd",
+				 "ECKD discipline %s", "success");
+	printk (KERN_INFO PRINTK_HEADER
+		"De-Registered ECKD discipline successfully\n");
+#endif /* CONFIG_DASD_ECKD_BUILTIN */
         
 	dasd_proc_cleanup ();
-	dasd_cleanup_emergency_req ();
-
-	list_for_each(l,&dasd_major_info[0].list) {
-		major_info=list_entry(l,major_info_t,list);	
+        
+	list_for_each_safe (l, n, &dasd_major_info[0].list) {
+		major_info = list_entry (l, major_info_t, list);
 		for (i = 0; i < DASD_PER_MAJOR; i++) {
 			kfree (major_info->dasd_device[i]);
 		}
-		if ((major_info -> flags & DASD_MAJOR_INFO_REGISTERED) &&
+		if ((major_info->flags & DASD_MAJOR_INFO_REGISTERED) &&
 		    (rc = dasd_unregister_major (major_info)) == 0) {
-                        DASD_DRIVER_DEBUG_EVENT(1,"cleanup_dasd",
-                                                "major %d: success",major_info->gendisk.major);
+			DASD_DRIVER_DEBUG_EVENT (1, "cleanup_dasd",
+						 "major %d: success",
+						 major_info->gendisk.major);
 			printk (KERN_INFO PRINTK_HEADER
-				"Unregistered successfully from major no %u\n", major_info->gendisk.major);
+				"Unregistered successfully from major no %u\n",
+				major_info->gendisk.major);
 		} else {
-                        DASD_DRIVER_DEBUG_EVENT(1,"cleanup_dasd",
-                                                "major %d: failed",major_info->gendisk.major);
+			DASD_DRIVER_DEBUG_EVENT (1, "cleanup_dasd",
+						 "major %d: failed",
+						 major_info->gendisk.major);
 			printk (KERN_WARNING PRINTK_HEADER
-				"Couldn't unregister successfully from major no %d rc = %d\n", major_info->gendisk.major, rc);
-		}
-	}
-
+				"Couldn't unregister successfully from major no %d rc = %d\n",
+				major_info->gendisk.major, rc);
+  		}
+  	}
+	list_for_each_safe (l, n, &dasd_range_head.list) {
+		range = list_entry (l, dasd_range_t, list);
+                dasd_remove_range(range);
+        }
 
-	range = dasd_range_head;
-	while (range) {
-		next = range->next;
-                dasd_remove_range (range);
-		if (next == NULL)
-			break;
-		else
-			range = next;
-	}
-	dasd_range_head = NULL;
-        
 #ifndef MODULE
-        for( j = 0; j < 256; j++ )
-                if ( dasd[j] ) {
-                        kfree(dasd[j]);
-                        dasd[j] = NULL;
+        for( i = 0; i < 256; i++ )
+                if ( dasd[i] ) {
+                        kfree(dasd[i]);
+                        dasd[i] = NULL;
                 }
 #endif /* MODULE */
         if (dasd_devfs_handle) 
                 devfs_unregister(dasd_devfs_handle);
         if (dasd_debug_area != NULL )
                 debug_unregister(dasd_debug_area);
-
 	printk (KERN_INFO PRINTK_HEADER "shutdown completed\n");
         DASD_DRIVER_DEBUG_EVENT(0,"cleanup_dasd","%s","LEAVE");
 }
@@ -3430,7 +4092,7 @@
 init_module (void)
 {
 	int rc = 0;
-	return dasd_init ();
+	rc = dasd_init ();
 	return rc;
 }
 
@@ -3449,7 +4111,7 @@
  * of the file.
  * ---------------------------------------------------------------------------
  * Local variables:
- * c-indent-level: 4 
+ * c-indent-level: 4
  * c-brace-imaginary-offset: 0
  * c-brace-offset: -4
  * c-argdecl-indent: 4

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)