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

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

diff -u --recursive --new-file v2.2.16/drivers/s390/block/dasd.c linux/drivers/s390/block/dasd.c
@@ -3,11 +3,30 @@
  * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
  * Bugreports.to..: <Linux390@de.ibm.com>
  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
+
+ * History of changes (starts July 2000)
+ * 07/03/00 Adapted code to compile with 2.2 and 2.4 kernels
+ * 07/05/00 Added some missing cases when shutting down a device
+ * 07/07/00 Fixed memory leak in ccw allocation
+            Adapted request function to make it work on 2.4
+ * 07/10/00 Added some code to the request function to dequeue requests
+            that cannot be handled due to errors
+ * 07/10/00 Moved linux/ccwcache.h to asm/
+ * 07/10/00 Fixed a bug when formatting a 'new' device       
+ * 07/10/00 Removed an annoying message from dasd_format 
+ * 07/11/00 Reanimated probeonly mode    
+ * 07/11/00 Reanimated autodetection mode
+ * 07/12/00 fixed a bug in module cleanup
+ * 07/12/00 fixed a bug in dasd_devices_open when having 'unknown' devices
+ * 07/13/00 fixed error message when having no device
+ * 07/13/00 added code for dynamic device recognition
  */
 
+#include <linux/config.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>
@@ -15,1330 +34,1653 @@
 #include <linux/hdreg.h>
 #include <linux/interrupt.h>
 #include <linux/ctype.h>
+#include <asm/ccwcache.h>
+#include <asm/dasd.h>
+#include <linux/blk.h>
+#include <asm/debug.h>
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+#include <linux/devfs_fs_kernel.h>
+#endif /* LINUX_IS_24 */
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif				/* CONFIG_PROC_FS */
+
+#include <asm/atomic.h>
+#include <asm/delay.h>
 #include <asm/io.h>
+#if !(LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
 #include <asm/spinlock.h>
+#endif /* LINUX_IS_24 */
 #include <asm/semaphore.h>
 #include <asm/ebcdic.h>
 #include <asm/uaccess.h>
-
 #include <asm/irq.h>
 #include <asm/s390_ext.h>
+#include <asm/s390dyn.h>
 
-#include <linux/dasd.h>
-#include <linux/blk.h>
-
-#include "dasd_erp.h"
-#include "dasd_types.h"
-#include "dasd_ccwstuff.h"
-
-#include "../../../arch/s390/kernel/debug.h"
-
-#define PRINTK_HEADER DASD_NAME":"
-
-#define DASD_SSCH_RETRIES 2
+#include "dasd.h"
+#ifdef CONFIG_DASD_ECKD
+#include "dasd_eckd.h"
+#endif				/*  CONFIG_DASD_ECKD */
+#ifdef CONFIG_DASD_FBA
+#include "dasd_fba.h" 
+#endif				/*  CONFIG_DASD_FBA */
+#ifdef CONFIG_DASD_MDSK
+#include "dasd_diag.h" 
+#endif				/*  CONFIG_DASD_MDSK */
 
-/* This macro is a little tricky, but makes the code more easy to read... */
-#define MATCH(info,ct,cm,dt,dm) ( \
-(( info -> sid_data.cu_type  ct ) && ( info -> sid_data.cu_model cm )) && \
-(( info -> sid_data.dev_type dt ) && ( info -> sid_data.dev_model dm )) )
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+static struct block_device_operations dasd_device_operations;
+#endif /* VERSION_CODE */
 
 #ifdef MODULE
+#define EXPORT_SYMTAB
 #include <linux/module.h>
 
-char *dasd[DASD_MAX_DEVICES] =
-{NULL,};
-#ifdef CONFIG_DASD_MDSK
-char *dasd_force_mdsk[DASD_MAX_DEVICES] =
-{NULL,};
-#endif
-
-kdev_t ROOT_DEV;
-
 EXPORT_NO_SYMBOLS;
 MODULE_AUTHOR ("Holger Smolinski <Holger.Smolinski@de.ibm.com>");
 MODULE_DESCRIPTION ("Linux on S/390 DASD device driver, Copyright 2000 IBM Corporation");
-MODULE_PARM (dasd, "1-" __MODULE_STRING (DASD_MAX_DEVICES) "s");
-#ifdef CONFIG_DASD_MDSK
-MODULE_PARM (dasd_force_mdsk, "1-" __MODULE_STRING (DASD_MAX_DEVICES) "s");
-#endif
-#endif
+MODULE_SUPPORTED_DEVICE("dasd");
+MODULE_PARM (dasd, "1-" __MODULE_STRING (256)"s");
+EXPORT_SYMBOL (dasd_discipline_enq);
+EXPORT_SYMBOL (dasd_discipline_deq);
+EXPORT_SYMBOL (dasd_start_IO);
+EXPORT_SYMBOL (dasd_int_handler);
+EXPORT_SYMBOL (dasd_alloc_request);
+EXPORT_SYMBOL (dasd_free_request);
 
-/* Prototypes for the functions called from external */
-void dasd_partn_detect (int di);
-int devindex_from_devno (int devno);
-int dasd_is_accessible (int devno);
-void dasd_add_devno_to_ranges (int devno);
+#else
+#endif				/* MODULE */
 
-#ifdef CONFIG_DASD_MDSK
-extern int dasd_force_mdsk_flag[DASD_MAX_DEVICES];
-extern void do_dasd_mdsk_interrupt (struct pt_regs *regs, __u16 code);
-extern int dasd_parse_module_params (void);
-extern void (**ext_mdisk_int) (void);
+/* SECTION: Constant definitions to be used within this file */
+#ifdef PRINTK_HEADER
+#undef PRINTK_HEADER
 #endif
+#define PRINTK_HEADER DASD_NAME":"
 
-void dasd_debug (unsigned long tag);
-void dasd_profile_add (cqr_t *cqr);
-void dasd_proc_init (void);
-
-static int dasd_format (int dev, format_data_t * fdata);
-
-static struct file_operations dasd_device_operations;
-
-spinlock_t dasd_lock;		/* general purpose lock for the dasd driver */
-
-/* All asynchronous I/O should waint on this wait_queue */
-struct wait_queue *dasd_waitq = NULL;
-
-extern dasd_chanq_t *cq_head;
-extern int dasd_probeonly;
-
-debug_info_t *dasd_debug_info;
+#define DASD_QUEUE_LIMIT 10
+#define DASD_SSCH_RETRIES 5
+#define QUEUE_SECTORS 128
+
+/* SECTION: prototypes for static functions of dasd.c (try to eliminate!) */
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+static void do_dasd_request (request_queue_t *);
+#else
+static void do_dasd_request (void);
+#endif /* LINUX_IS_24 */
+static void dasd_do_chanq (void);
+static void schedule_request_fn (void (*func) (void));
+static int dasd_set_device_level (unsigned int, int, dasd_discipline_t *, int);
+static int dasd_oper_handler ( int irq, devreg_t *devreg );
+
+/* SECTION: managing setup of dasd_driver */
+typedef struct dasd_range_t {
+	unsigned int from;
+	unsigned int to;
+	char discipline[4];
+        struct dasd_range_t *next;
+} __attribute__ ((packed)) dasd_range_t;
+
+typedef struct dasd_devreg_t {
+        devreg_t devreg;
+        struct dasd_devreg_t *next;
+} dasd_devreg_t;
+
+static int dasd_probeonly = 1;
+static int dasd_autodetect = 1;
+ 
+static dasd_range_t *dasd_range_head = NULL;
+static dasd_devreg_t *dasd_devreg_head = NULL;
+
+static dasd_devreg_t *
+dasd_create_devreg ( int devno ) {
+        dasd_devreg_t *r = kmalloc ( sizeof(dasd_devreg_t), GFP_KERNEL);
+        memset (r,0,sizeof(dasd_devreg_t));
+        if ( r != NULL ) {
+                r -> devreg.ci.devno = devno;
+                r -> devreg.flag = DEVREG_TYPE_DEVNO;
+                r -> devreg.oper_func = dasd_oper_handler;
+        }
+        return r;
+}
 
-extern dasd_information_t **dasd_information;
+static void
+dasd_add_range (int from, int to)
+{
+        dasd_range_t *temp,*range;
+        int i;
 
-dasd_information_t *dasd_info[DASD_MAX_DEVICES] =
-{NULL,};
-static struct hd_struct dd_hdstruct[DASD_MAX_DEVICES << PARTN_BITS];
-static int dasd_blks[256] =
-{0,};
-static int dasd_secsize[256] =
-{0,};
-static int dasd_blksize[256] =
-{0,};
-static int dasd_maxsecs[256] =
-{0,};
+        range = (dasd_range_t *)kmalloc(sizeof(dasd_range_t),GFP_KERNEL);
+        if ( range == NULL )
+                return;
+        memset(range,0,sizeof(dasd_range_t));
+        range -> from = from;
+        if (to == 0) { /* single devno ? */
+                range -> to = from;
+        }  else {
+                range -> to = to;
+        }
 
-void
-dasd_geninit (struct gendisk *dd)
-{
+        /* chain current range to end of list */
+        if ( dasd_range_head == NULL ) {
+                dasd_range_head = range;
+        } else {
+                for ( temp = dasd_range_head; 
+                      temp && temp->next; 
+                      temp = temp->next );
+                temp->next = range;
+        }
+        /* allocate and chain devreg infos for the devnos... */
+        for ( i = range->from; i <= range->to; i ++ ){
+                dasd_devreg_t *reg = dasd_create_devreg(i);
+                s390_device_register(&reg->devreg);
+                reg->next = dasd_devreg_head;
+                dasd_devreg_head = reg;
+        }
 }
 
-struct gendisk dd_gendisk =
+static int
+dasd_strtoul (char *str, char **stra)
 {
-	major:MAJOR_NR,		/* Major number */
-	major_name:"dasd",	/* Major name */
-	minor_shift:PARTN_BITS,	/* Bits to shift to get real from partn */
-	max_p:1 << PARTN_BITS,	/* Number of partitions per real */
-	max_nr:0,		/* number */
-	init:dasd_geninit,
-	part:dd_hdstruct,	/* hd struct */
-	sizes:dasd_blks,	/* sizes in blocks */
-	nr_real:0,
-	real_devices:NULL,	/* internal */
-	next:NULL		/* next */
-};
+	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;
+}
 
-static atomic_t bh_scheduled = ATOMIC_INIT (0);
+char *dasd[256] = {NULL,}; /* maximum of 256 ranges supplied on parmline */
 
-void
-dasd_schedule_bh (void (*func) (void))
+#ifndef MODULE
+static void 
+dasd_split_parm_string ( char * str ) 
 {
-	static struct tq_struct dasd_tq =
-	{0,};
-	/* Protect against rescheduling, when already running */
-	if (atomic_compare_and_swap (0, 1, &bh_scheduled))
-		return;
-	dasd_tq.routine = (void *) (void *) func;
-	queue_task (&dasd_tq, &tq_immediate);
-	mark_bh (IMMEDIATE_BH);
-		return;
+        char *tmp=str;
+        int count = 0;
+        do {
+                char * end;
+                int len;
+                end = strchr(tmp,',');
+                if ( end == NULL ) { 
+                        len = strlen(tmp) + 1;
+                } else {
+                        len = (long) end - (long) tmp + 1;
+                        *end = '\0';
+                        end ++;
+                }
+                dasd[count] = kmalloc(len * sizeof(char),GFP_ATOMIC);
+                if ( dasd == NULL ) {
+                        printk (KERN_WARNING PRINTK_HEADER
+                                "No memory to 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' );
 	}
 
-void
-sleep_done (struct semaphore *sem)
+static char dasd_parm_string[1024] = {0,};
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+static int
+dasd_setup (char *str) 
 {
-	if (sem != NULL) {
-		up (sem);
+        static int first_time = 1;
+        if ( ! first_time ) {
+                *(dasd_parm_string+strlen(dasd_parm_string))=',';
+        }  else {
+                first_time = 0;
 	}
+        memcpy(dasd_parm_string+strlen(dasd_parm_string),str,strlen(str)+1);
+        return 1;
 }
-
+#else
 void
-sleep (int timeout)
+dasd_setup (char *str, int *ints) 
 {
-	struct semaphore sem = MUTEX_LOCKED;
-	struct timer_list timer;
-
-	init_timer (&timer);
-	timer.data = (unsigned long) &sem;
-	timer.expires = jiffies + timeout;
-	timer.function = (void (*)(unsigned long)) sleep_done;
-	printk (KERN_DEBUG PRINTK_HEADER
-		"Sleeping for timer tics %d\n", timeout);
-	add_timer (&timer);
-	down (&sem);
-	del_timer (&timer);
+        static int first_time = 1;
+        if ( ! first_time ) {
+                *(dasd_parm_string+strlen(dasd_parm_string))=',';
+        }  else {
+                first_time = 0;
+        }
+        memcpy(dasd_parm_string+strlen(dasd_parm_string),str,strlen(str)+1);
 }
+#endif /* LINUX_IS_24 */
 
-#ifdef CONFIG_DASD_ECKD
-extern dasd_operations_t dasd_eckd_operations;
-#endif				/* CONFIG_DASD_ECKD */
-#ifdef CONFIG_DASD_FBA
-extern dasd_operations_t dasd_fba_operations;
-#endif				/* CONFIG_DASD_FBA */
-#ifdef CONFIG_DASD_MDSK
-extern dasd_operations_t dasd_mdsk_operations;
-#endif				/* CONFIG_DASD_MDSK */
 
-dasd_operations_t *dasd_disciplines[] =
-{
-#ifdef CONFIG_DASD_ECKD
-	&dasd_eckd_operations,
-#endif				/* CONFIG_DASD_ECKD */
-#ifdef CONFIG_DASD_FBA
-	&dasd_fba_operations,
-#endif				/* CONFIG_DASD_FBA */
-#ifdef CONFIG_DASD_MDSK
-	&dasd_mdsk_operations,
-#endif				/* CONFIG_DASD_MDSK */
-#ifdef CONFIG_DASD_CKD
-	&dasd_ckd_operations,
-#endif				/* CONFIG_DASD_CKD */
-	NULL
-};
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+__setup("dasd=", dasd_setup);
+#endif /* LINUX_IS_24 */
 
-char *dasd_name[] =
-{
-#ifdef CONFIG_DASD_ECKD
-	"ECKD",
-#endif				/* CONFIG_DASD_ECKD */
-#ifdef CONFIG_DASD_FBA
-	"FBA",
-#endif				/* CONFIG_DASD_FBA */
-#ifdef CONFIG_DASD_MDSK
-	"MDSK",
-#endif				/* CONFIG_DASD_MDSK */
-#ifdef CONFIG_DASD_CKD
-	"CKD",
-#endif				/* CONFIG_DASD_CKD */
-	"END"
-};
+#endif
 
-static int
-do_dasd_ioctl (struct inode *inp, /* unsigned */ int no, unsigned long data)
+void
+dasd_parse (char **str) 
 {
-	int rc;
-	int di;
-	dasd_information_t *dev;
+	char *temp;
+	int from, to;
 
-	di = DEVICE_NR (inp->i_rdev);
-	if (!dasd_info[di]) {
-		PRINT_WARN ("No device registered as %d\n", inp->i_rdev);
-		return -EINVAL;
-	}
-	if ((_IOC_DIR (no) != _IOC_NONE) && (data == 0)) {
-		PRINT_DEBUG ("empty data ptr");
-		return -EINVAL;
-	}
-	dev = dasd_info[di];
-	if (!dev) {
-		PRINT_WARN ("No device registered as %d\n", inp->i_rdev);
-		return -EINVAL;
+      	if ( *str ) { 
+        dasd_probeonly = 0;
 	}
-	PRINT_INFO ("ioctl 0x%08x %s'0x%x'%d(%d) on dev %d/%d (%d) with 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),
-		    MAJOR (inp->i_rdev), MINOR (inp->i_rdev), di, data);
-
-	switch (no) {
-	case BLKGETSIZE:{	/* Return device size */
-			int blocks = dasd_blks[MINOR (inp->i_rdev)] << 1;
-			rc = copy_to_user ((long *) data,
-					   &blocks,
-					   sizeof (long));
+        while (*str) {
+                temp = *str;
+		from = 0;
+		to = 0;
+                if ( strncmp ( *str,"autodetect",strlen("autodetect"))== 0) {
+                        dasd_autodetect = 1;
+                        printk (KERN_INFO "turning to autodetection mode\n");
+                        break;
+                } else if ( strncmp ( *str,"probeonly",strlen("probeonly"))== 0) {
+                        dasd_probeonly = 1;
+                        printk (KERN_INFO "turning to probeonly mode\n");
 			break;
+                } else {
+                        dasd_autodetect = 0;
+                        from = dasd_strtoul (temp, &temp);          
+                        if (*temp == '-') {
+                                temp++;
+                                to = dasd_strtoul (temp, &temp);
 		}
-	case BLKFLSBUF:{
-			rc = fsync_dev (inp->i_rdev);
-			break;
+                        dasd_add_range (from, to);
 		}
-	case BLKRAGET:{
-			rc = copy_to_user ((long *) data,
-					read_ahead + MAJOR_NR, sizeof (long));
-			break;
+                str ++;
 		}
-	case BLKRASET:{
-			rc = copy_from_user (read_ahead + MAJOR_NR,
-					     (long *) data, sizeof (long));
-			break;
 		}
-	case BLKRRPART:{
-			dasd_partn_detect (di);
-			rc = 0;
+
+int
+devindex_from_devno (int devno)
+{
+	int devindex = 0;
+        dasd_range_t *temp;
+        for ( temp = dasd_range_head; temp; temp = temp->next ) {
+		if (devno < temp -> from || devno > temp -> to) {
+			devindex += temp -> to - temp -> from + 1;
+		} else {
+			devindex += devno - temp -> from;
 			break;
 		}
-	case BIODASDRLB:{
-			rc = copy_to_user ((int *) data,
-					   &dasd_info[di]->sizes.label_block,
-					   sizeof (int));
-			break;
 		}
-	case BLKGETBSZ:{
-			rc = copy_to_user ((int *) data,
-					   &dasd_info[di]->sizes.bp_block,
-					   sizeof (int));
-			break;
+        if ( temp == NULL ) 
+                return -ENODEV;
+	return devindex;
 		}
-	case HDIO_GETGEO:{
-			struct hd_geometry geo;
-			dasd_disciplines[dev->type]->fill_geometry (di, &geo);
-			rc = copy_to_user ((struct hd_geometry *) data, &geo,
-					   sizeof (struct hd_geometry));
-			break;
+
+/* SECTION: ALl needed for multiple major numbers */
+
+static major_info_t dasd_major_info[] =
+{
+	{
+		next:NULL /* &dasd_major_info[1] */ ,
+		request_fn:do_dasd_request,
+		read_ahead:8,
+		gendisk:
+		{
+			major:94,
+			major_name:DASD_NAME,
+			minor_shift:DASD_PARTN_BITS,
+			max_p:1 << DASD_PARTN_BITS,
+#if ! (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+			max_nr:DASD_PER_MAJOR,
+#endif /* LINUX_IS_24 */
+			nr_real:DASD_PER_MAJOR,
 		}
-		RO_IOCTLS (inp->i_rdev, data);
-	case BIODASDRSID:{
-			rc = copy_to_user ((void *) data,
-					   &(dev->info.sid_data),
-					   sizeof (senseid_t));
-			break;
 		}
-	case BIODASDRWTB:{
-			int offset = 0;
-			int xlt;
-			rc = copy_from_user (&xlt, (void *) data,
-					     sizeof (int));
-#if 0
-                        PRINT_INFO("Xlating %d to",xlt);
-#endif
-			if (rc)
-				break;
-			offset = dd_gendisk.part[MINOR (inp->i_rdev)].start_sect >>
-			    dev->sizes.s2b_shift;
-			xlt += offset;
 #if 0
-                        printk(" %d \n",xlt);
-#endif
-			rc = copy_to_user ((void *) data, &xlt,
-					   sizeof (int));
-			break;
-		}
-	case BIODASDFORMAT:{
-			/* fdata == NULL is a valid arg to dasd_format ! */
-			format_data_t *fdata = NULL;
-			PRINT_WARN ("called format ioctl\n");
-			if (data) {
-				fdata = kmalloc (sizeof (format_data_t),
-						 GFP_ATOMIC);
-				if (!fdata) {
-					rc = -ENOMEM;
-					break;
-				}
-				rc = copy_from_user (fdata, (void *) data,
-						     sizeof (format_data_t));
-				if (rc)
-					break;
-			}
-			rc = dasd_format (inp->i_rdev, fdata);
-			if (fdata) {
-				kfree (fdata);
+        ,
+	{
+		next:NULL,
+		request_fn:do_dasd_request,
+		read_ahead:8,
+		gendisk:
+		{
+			major:95,
+			major_name:DASD_NAME,
+			minor_shift:DASD_PARTN_BITS,
+			max_p:1 << DASD_PARTN_BITS,
+#if ! (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+			max_nr:DASD_PER_MAJOR,
+#endif /* LINUX_IS_24 */
+			nr_real:DASD_PER_MAJOR,
 			}
-			break;
 		}
-	default:
-		PRINT_WARN ("unknown ioctl number %08x %08lx\n", no, BIODASDFORMAT);
-		rc = -EINVAL;
-		break;
+#endif
+};
+
+static dasd_device_t *
+find_dasd_device (int devindex)
+{
+	major_info_t *major_info = dasd_major_info;
+	while (major_info && devindex > DASD_PER_MAJOR) {
+		devindex -= DASD_PER_MAJOR;
+		major_info = major_info->next;
 	}
-	return rc;
+	if (!major_info)
+                return NULL;
+	return major_info->dasd_device[devindex];
 }
 
-static void
-dasd_end_request (struct request *req, int uptodate)
+static major_info_t *
+major_info_from_devindex (int devindex)
 {
-	struct buffer_head *bh;
-	FUNCTION_ENTRY ("dasd_end_request");
-#if DASD_PARANOIA > 2
-	if (!req) {
-		INTERNAL_CHECK ("end_request called with zero arg%s\n", "");
-	}
-#endif				/* DASD_PARANOIA */
-	while ((bh = req->bh) != NULL) {
-		req->bh = bh->b_reqnext;
-		bh->b_reqnext = NULL;
-		bh->b_end_io (bh, uptodate);
+	major_info_t *major_info = dasd_major_info;
+	while (major_info && devindex > DASD_PER_MAJOR) {
+		devindex -= DASD_PER_MAJOR;
+		major_info = major_info->next;
 	}
-	if (!end_that_request_first (req, uptodate, DEVICE_NAME)) {
-#ifndef DEVICE_NO_RANDOM
-		add_blkdev_randomness (MAJOR (req->rq_dev));
-#endif
-		DEVICE_OFF (req->rq_dev);
-		end_that_request_last (req);
+	return major_info;
 	}
-	FUNCTION_EXIT ("dasd_end_request");
-	return;
+
+int
+major_from_devindex (int devindex)
+{
+	major_info_t *major_info = major_info_from_devindex (devindex);
+	return major_info->gendisk.major;
 }
 
-void
-dasd_wakeup (void)
+static int
+devindex_from_kdev_t (kdev_t dev)
 {
-	wake_up (&dasd_waitq);
+	int devindex = 0;
+	major_info_t *major_info = dasd_major_info;
+	while (major_info &&
+	       MAJOR (dev) != major_info->gendisk.major) {
+		devindex += (1 << (MINORBITS - DASD_PARTN_BITS));
+		major_info = major_info->next;
+	}
+	if (!major_info)
+		devindex = -ENODEV;
+        devindex += MINOR(dev) >> DASD_PARTN_BITS;
+	return devindex;
 }
 
+/* SECTION: managing dasd disciplines */
 
-int
-dasd_watch_volume (int di)
-{
-        int rc = 0;
+static dasd_discipline_t *dasd_disciplines = NULL;
+static spinlock_t discipline_lock;
 
-        return rc;
-}
+/* 
+ * void dasd_discipline_enq (dasd_discipline_t * d)
+ */
 
 void 
-dasd_watcher (void) 
+dasd_discipline_enq (dasd_discipline_t * d)
 {
-        int i = 0;
-        int rc; 
-        do {
-                for ( i = 0; i < DASD_MAX_DEVICES; i++ ) {
-                        if ( dasd_info [i] ) {
-                                rc = dasd_watch_volume ( i );
-                        }
-                }
-                interruptible_sleep_on(&dasd_waitq);
-        } while(1);
+	spin_lock (&discipline_lock);
+	d->next = dasd_disciplines;
+	dasd_disciplines = d;
+	spin_unlock (&discipline_lock);
 }
 
+/* 
+ * int dasd_discipline_deq (dasd_discipline_t * d)
+ */
+
 int
-dasd_unregister_dasd (int di)
+dasd_discipline_deq (dasd_discipline_t * d)
 {
 	int rc = 0;
-	int minor;
-	int i;
-
-	minor = di << PARTN_BITS;
-	if (!dasd_info[di]) {	/* devindex is not free */
-		INTERNAL_CHECK ("trying to free unallocated device %d\n", di);
-		return -ENODEV;
+	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;
+		}
 	}
-	/* delete all that partition stuff */
-	for (i = 0; i < (1 << PARTN_BITS); i++) {
-		dasd_blks[minor] = 0;
-		dasd_secsize[minor + i] = 0;
-		dasd_blksize[minor + i] = 0;
-		dasd_maxsecs[minor + i] = 0;
-	}
-	/* reset DASD to unknown statuss */
-	atomic_set (&dasd_info[di]->status, DASD_INFO_STATUS_UNKNOWN);
-
-	free_irq (dasd_info[di]->info.irq, &(dasd_info[di]->dev_status));
-	if (dasd_info[di]->rdc_data)
-		kfree (dasd_info[di]->rdc_data);
-	kfree (dasd_info[di]);
-	PRINT_INFO ("%04d deleted from list of valid DASDs\n",
-		    dasd_info[di]->info.devno);
+	spin_unlock (&discipline_lock);
 	return rc;
 }
 
-/* Below you find the functions already cleaned up */
-static dasd_type_t
-check_type (dev_info_t * info)
-{
-	dasd_type_t type = dasd_none;
-	int di;
+/* SECTION: (de)queueing of requests to channel program queues */
 
-	FUNCTION_ENTRY ("check_type");
-	di = devindex_from_devno (info->devno);
+/* 
+ * void dasd_chanq_enq (dasd_chanq_t * q, ccw_req_t * cqr)
+ */
 
-#ifdef CONFIG_DASD_MDSK
-	if (MACHINE_IS_VM && dasd_force_mdsk_flag[di] == 1) {
-		type = dasd_mdsk;
+static void
+dasd_chanq_enq (dasd_chanq_t * q, ccw_req_t * cqr)
+{
+	if (q->head != NULL) {
+		q->tail->next = cqr;
 	} else
-#endif				/* CONFIG_DASD_MDSK */
+		q->head = cqr;
+	cqr->next = NULL;
+	q->tail = cqr;
+	q->queued_requests++;
+	atomic_compare_and_swap_debug (&cqr->status, CQR_STATUS_FILLED, CQR_STATUS_QUEUED);
+}
 
-#ifdef CONFIG_DASD_ECKD
-	if (MATCH (info, == 0x3990, ||1, == 0x3390, ||1) ||
-	    MATCH (info, == 0x9343, ||1, == 0x9345, ||1) ||
-	    MATCH (info, == 0x3990, ||1, == 0x3380, ||1)) {
-		type = dasd_eckd;
-	} else
-#endif				/* CONFIG_DASD_ECKD */
-#ifdef CONFIG_DASD_FBA
-	if (MATCH (info, == 0x6310, ||1, == 0x9336, ||1)) {
-		type = dasd_fba;
-	} else
-#endif				/* CONFIG_DASD_FBA */
-#ifdef CONFIG_DASD_MDSK
-	if (MACHINE_IS_VM) {
-		type = dasd_mdsk;
-	} else
-#endif				/* CONFIG_DASD_MDSK */
+/* 
+ * void dasd_chanq_enq_head (dasd_chanq_t * q, ccw_req_t * cqr)
+ */
+
+static void
+dasd_chanq_enq_head (dasd_chanq_t * q, ccw_req_t * cqr)
 	{
-		type = dasd_none;
+	cqr->next = q->head;
+	q->head = cqr;
+	if (q->tail == NULL)
+		q->tail = cqr;
+	q->queued_requests++;
+	atomic_compare_and_swap_debug (&cqr->status, CQR_STATUS_FILLED, CQR_STATUS_QUEUED);
 	}
 
-	FUNCTION_EXIT ("check_type");
-	return type;
-}
+/* 
+ * int dasd_chanq_deq (dasd_chanq_t * q, ccw_req_t * cqr)
+ */
 
 static int
-dasd_read_characteristics (dasd_information_t * info)
+dasd_chanq_deq (dasd_chanq_t * q, ccw_req_t * cqr)
 {
-	int rc = 0;
-	int ct = 0;
-	dev_info_t *di;
-	dasd_type_t dt;
+	ccw_req_t *prev;
 
-	FUNCTION_ENTRY ("read_characteristics");
-	if (info == NULL) {
-		return -ENODEV;
-	}
-	di = &(info->info);
-	if (di == NULL) {
-		return -ENODEV;
-	}
-	dt = check_type (di);
-	/* Some cross-checks, if the cu supports RDC */
-	if (MATCH (di, == 0x2835, ||1, ||1, ||1) ||
-	    MATCH (di, == 0x3830, ||1, ||1, ||1) ||
-	    MATCH (di, == 0x3830, ||1, ||1, ||1) ||
-	    MATCH (di, == 0x3990, <=0x03, == 0x3380, <=0x0d)) {
-		PRINT_WARN ("Device %d (%x/%x at %x/%x) supports no RDC\n",
-			    info->info.irq,
-			    di->sid_data.dev_type,
-			    di->sid_data.dev_model,
-			    di->sid_data.cu_type,
-			    di->sid_data.cu_model);
-		return -EINVAL;
+	if (cqr == NULL)
+		return -ENOENT;
+	if (cqr == (ccw_req_t *) q->head) {
+		q->head = cqr->next;
+		if (q->head == NULL)
+			q->tail = NULL;
+	} else {
+		prev = (ccw_req_t *) q->head;
+		while (prev && prev->next != cqr)
+			prev = prev->next;
+		if (prev == NULL)
+			return -ENOENT;
+		prev->next = cqr->next;
+		if (prev->next == NULL)
+			q->tail = prev;
+	}
+	cqr->next = NULL;
+	q->queued_requests--;
+	return 0;
+}
+
+/* SECTION: Handling of the queue of queues */
+
+#ifdef CONFIG_SMP
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+static spinlock_t cq_lock;		/* spinlock for cq_head */
+#else
+static spinlock_t cq_lock = SPIN_LOCK_UNLOCKED;		/* spinlock for cq_head */
+#endif /* LINUX_IS_24 */
+#endif				/* __SMP__ */
+static dasd_chanq_t *qlist_head = NULL;		/* head of queue of queues */
+
+/* 
+ * void qlist_enq (dasd_chanq_t * q)
+ * queues argument to head of the queue of queues
+ * and marks queue to be active
+ */
+
+static void
+qlist_enq (dasd_chanq_t * q)
+{
+	if (q == NULL) {
+		printk (KERN_WARNING PRINTK_HEADER " NULL queue to be queued to queue of queues\n");
+		return;
 	}
-	switch (dt) {
-#ifdef CONFIG_DASD_ECKD
-	case dasd_eckd:
-		ct = 64;
-		rc = read_dev_chars (info->info.irq,
-				     (void *) &(info->rdc_data), ct);
-		break;
-#endif				/*  CONFIG_DASD_ECKD */
-#ifdef CONFIG_DASD_FBA
-	case dasd_fba:
-		ct = 32;
-		rc = read_dev_chars (info->info.irq,
-				     (void *) &(info->rdc_data), ct);
-		break;
-#endif				/*  CONFIG_DASD_FBA */
-#ifdef CONFIG_DASD_MDSK
-	case dasd_mdsk:
-		ct = 0;
-		break;
-#endif				/*  CONFIG_DASD_FBA */
-	default:
-		INTERNAL_ERROR ("don't know dasd type %d\n", dt);
-	}
-	if (rc) {
-		PRINT_WARN ("RDC resulted in rc=%d\n", rc);
-	}
-	FUNCTION_EXIT ("read_characteristics");
-	return rc;
+	spin_lock (&cq_lock);
+	if (atomic_read (&q->flags) & DASD_CHANQ_ACTIVE) {
+		printk (KERN_WARNING PRINTK_HEADER " Queue already active");
+	}
+	atomic_set_mask (DASD_CHANQ_ACTIVE, &q->flags);
+	q->next_q = qlist_head;
+	qlist_head = q;
+	spin_unlock (&cq_lock);
 }
 
-/* How many sectors must be in a request to dequeue it ? */
-#define QUEUE_BLOCKS 25
-#define QUEUE_SECTORS (QUEUE_BLOCKS << dasd_info[di]->sizes.s2b_shift)
-
-/* How often to retry an I/O before raising an error */
-#define DASD_MAX_RETRIES 5
-
-static inline
- cqr_t *
-dasd_cqr_from_req (struct request *req)
-{
-	cqr_t *cqr = NULL;
-	int di;
-	dasd_information_t *info;
-
-	if (!req) {
-		PRINT_ERR ("No request passed!");
-		return NULL;
-	}
-	di = DEVICE_NR (req->rq_dev);
-	info = dasd_info[di];
-	if (!info)
-		return NULL;
-	/* if applicable relocate block */
-	if (MINOR (req->rq_dev) & ((1 << PARTN_BITS) - 1)) {
-		req->sector +=
-		    dd_gendisk.part[MINOR (req->rq_dev)].start_sect;
-	}
-	/* Now check for consistency */
-	if (!req->nr_sectors) {
-		PRINT_WARN ("req: %p dev: %08x sector: %ld nr_sectors: %ld bh: %p\n",
-		     req, req->rq_dev, req->sector, req->nr_sectors, req->bh);
-		return NULL;
-	}
-	if (((req->sector + req->nr_sectors) >> 1) > info->sizes.kbytes) {
-		printk (KERN_ERR PRINTK_HEADER
-			"Requesting I/O past end of device %d\n",
-			di);
-		return NULL;
-	}
-	cqr = dasd_disciplines[info->type]->get_req_ccw (di, req);
-	if (!cqr) {
-		PRINT_WARN ("empty CQR generated\n");
-	} else {
-		cqr->req = req;
-		cqr->int4cqr = cqr;
-		cqr->devindex = di;
-#ifdef DASD_PROFILE
-		asm volatile ("STCK %0":"=m" (cqr->buildclk));
-#endif				/* DASD_PROFILE */
-		ACS (cqr->status, CQR_STATUS_EMPTY, CQR_STATUS_FILLED);
+/* 
+ * void qlist_deq (dasd_chanq_t * q)
+ * dequeues argument from the queue of queues
+ * and marks queue to be inactive
+ */
+
+static void
+qlist_deq (dasd_chanq_t * q)
+{
+
+	if (qlist_head == NULL) {
+		printk (KERN_ERR PRINTK_HEADER "Channel queue is empty%s\n", "");
+		return;
 	}
-	return cqr;
+	if (q == NULL) {
+		printk (KERN_WARNING PRINTK_HEADER " NULL queue to be dequeued from queue of queues\n");
+		return;
+	}
+	spin_lock (&cq_lock);
+	if (!(atomic_read (&q->flags) & DASD_CHANQ_ACTIVE)) {
+		printk (KERN_WARNING PRINTK_HEADER " Queue not active\n");
+	} else if (qlist_head == q) {
+		qlist_head = q->next_q;
+	} else {
+		dasd_chanq_t *c = qlist_head;
+		while (c->next_q && c->next_q != q)
+			c = c->next_q;
+		if (c->next_q == NULL)
+			printk (KERN_WARNING PRINTK_HEADER " Queue %p not in queue of queues\n", q);
+		else
+			c->next_q = q->next_q;
+	}
+	atomic_clear_mask (DASD_CHANQ_ACTIVE, &q->flags);
+	q->next_q = NULL;
+	spin_unlock (&cq_lock);
 }
 
-int
-dasd_start_IO (cqr_t * cqr)
+/* SECTION: All the gendisk stuff */
+
+
+static int
+dasd_partn_detect (int devindex)
 {
 	int rc = 0;
-	int retries = DASD_SSCH_RETRIES;
-	int di, irq;
-
-	dasd_debug ((unsigned long) cqr);	/* cqr */
 
-	if (!cqr) {
-		PRINT_WARN ("(start_IO) no cqr passed\n");
-		return -EINVAL;
-	}
-#ifdef CONFIG_DASD_MDSK
-	if (cqr->magic == MDSK_MAGIC) {
-		return dasd_mdsk_start_IO (cqr);
-	}
-#endif				/* CONFIG_DASD_MDSK */
-	if (cqr->magic != DASD_MAGIC && cqr->magic != ERP_MAGIC) {
-		PRINT_ERR ("(start_IO) magic number mismatch\n");
-		return -EINVAL;
+	major_info_t *major_info = major_info_from_devindex (devindex);
+	struct gendisk *dd = &major_info->gendisk;
+	int minor = ( devindex & 
+                      (( 1 << (MINORBITS-DASD_PARTN_BITS) ) - 1)) << dd->minor_shift;
+	struct dasd_device_t *device = find_dasd_device (devindex);
+        
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+        register_disk(dd,
+                      MKDEV (dd->major, minor),
+                      1 << DASD_PARTN_BITS,
+                      &dasd_device_operations,
+                      (device->sizes.blocks << device->sizes.s2b_shift));
+#else
+        dd->sizes[minor] = (device->sizes.blocks << device->sizes.s2b_shift) >> 1;
+        resetup_one_dev(dd,minor>>DASD_PARTN_BITS);
+#endif /* LINUX_IS_24 */
+	return rc;
 	}
-	ACS (cqr->status, CQR_STATUS_QUEUED, CQR_STATUS_IN_IO);
-        di = cqr->devindex;
-	irq = dasd_info[di]->info.irq;
-	do {
-		asm volatile ("STCK %0":"=m" (cqr->startclk));
-		rc = do_IO (irq, cqr->cpaddr, (long) cqr, 0x00, cqr->options);
-		switch (rc) {
-		case 0:
-			if (!(cqr->options & DOIO_WAIT_FOR_INTERRUPT))
-                        	atomic_set_mask (DASD_CHANQ_BUSY,
-                                	         &dasd_info[di]->queue.flags);
-			break;
-		case -ENODEV:
-			PRINT_WARN ("cqr %p: 0x%04x error, %d retries left\n",
-				    cqr, dasd_info[di]->info.devno, retries);
-			break;
-		case -EIO:
-			PRINT_WARN ("cqr %p: 0x%04x I/O, %d retries left\n",
-				    cqr, dasd_info[di]->info.devno, retries);
-			break;
-		case -EBUSY:	/* set up timer, try later */
 
-			PRINT_WARN ("cqr %p: 0x%04x busy, %d retries left\n",
-				    cqr, dasd_info[di]->info.devno, retries);
-			break;
-		default:
+/* SECTION: Managing wrappers for ccwcache */
 
-			PRINT_WARN ("cqr %p: 0x%04x %d, %d retries left\n",
-				    cqr, rc, dasd_info[di]->info.devno,
-                                    retries);
-			break;
+#define DASD_EMERGENCY_REQUESTS 16
+
+static ccw_req_t *dasd_emergency_req[DASD_EMERGENCY_REQUESTS]={NULL,};
+static spinlock_t dasd_emergency_req_lock = SPIN_LOCK_UNLOCKED;
+
+static void
+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);
 		}
-	} while (rc && --retries);
-	if (rc) {
-		ACS (cqr->status, CQR_STATUS_IN_IO, CQR_STATUS_ERROR);
         }
-	return rc;
+
+static 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 one page for 'in-use' emergency request\n");
+        }
 }
 
-static inline
-void
-dasd_end_cqr (cqr_t * cqr, int uptodate)
+ccw_req_t *
+dasd_alloc_request (char *magic, int cplength, int datasize)
 {
-	struct request *req = cqr->req;
-	asm volatile ("STCK %0":"=m" (cqr->endclk));
-#ifdef DASD_PROFILE
-	dasd_profile_add (cqr);
-#endif				/* DASD_PROFILE */
-	dasd_chanq_deq (&dasd_info[cqr->devindex]->queue, cqr);
-        if (req) {
-                dasd_end_request (req, uptodate);
+        ccw_req_t *rv = NULL;
+        int i;
+        if ( ( rv = ccw_alloc_request(magic,cplength,datasize )) != NULL ) {
+                return rv;
+        }
+        if ( cplength * sizeof(ccw1_t) + datasize + sizeof(ccw_req_t) > PAGE_SIZE ) {
+                return NULL;
+        }
+        spin_lock(&dasd_emergency_req_lock);
+        for ( i = 0; i < DASD_EMERGENCY_REQUESTS; i++) {
+                if ( dasd_emergency_req[i] != NULL ) {
+                        rv = dasd_emergency_req[i];
+                        dasd_emergency_req[i] = NULL;
+                }
         }
+        spin_unlock(&dasd_emergency_req_lock);
+        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));
+        }
+        return rv;
 }
 
 void
-dasd_dump_sense (devstat_t * stat)
+dasd_free_request (ccw_req_t * request)
 {
-	int sl, sct;
-	if (!stat->flag | DEVSTAT_FLAG_SENSE_AVAIL) {
-		PRINT_INFO ("I/O status w/o sense data\n");
+        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 {
-	printk (KERN_INFO PRINTK_HEADER
-			"-------------------I/O result:-----------\n");
-	for (sl = 0; sl < 4; sl++) {
-		printk (KERN_INFO PRINTK_HEADER "Sense:");
-		for (sct = 0; sct < 8; sct++) {
-			printk (" %2d:0x%02X", 8 * sl + sct,
-				stat->ii.sense.data[8 * sl + sct]);
-		}
-		printk ("\n");
+                ccw_free_request(request);
 	}
 }
+
+/* SECTION: Managing the device queues etc. */
+
+static atomic_t bh_scheduled = ATOMIC_INIT (0);
+static atomic_t request_fn_scheduled = ATOMIC_INIT (0);
+
+static void
+run_bh (void)
+{
+	atomic_set (&bh_scheduled, 0);
+	dasd_do_chanq ();
 }
 
-int
-register_dasd_last (int di)
+void
+dasd_schedule_bh ( void )
 {
-	int rc = 0;
-	int minor;
-	int i;
+	static struct tq_struct bh_tq =
+	{0,};
+	/* Protect against rescheduling, when already running */
+	if (atomic_compare_and_swap (0, 1, &bh_scheduled))
+		return;
+	bh_tq.routine = (void *) (void *) run_bh;
+	queue_task (&bh_tq, &tq_immediate);
+	mark_bh (IMMEDIATE_BH);
+	return;
+}
 
-	rc = dasd_disciplines[dasd_info[di]->type]->fill_sizes_last (di);
-	if (!rc) {
-		ACS (dasd_info[di]->status,
-		     DASD_INFO_STATUS_DETECTED, DASD_INFO_STATUS_FORMATTED);
-	} else {		/* -EMEDIUMTYPE: */
-		ACS (dasd_info[di]->status,
-		     DASD_INFO_STATUS_DETECTED, DASD_INFO_STATUS_ANALYSED);
-	}
-	PRINT_INFO ("%04X (dasd%c):%ld kB <- block: %d on sector %d B\n",
-		    dasd_info[di]->info.devno,
-		    'a' + di,
-		    dasd_info[di]->sizes.kbytes,
-		    dasd_info[di]->sizes.bp_block,
-		    dasd_info[di]->sizes.bp_sector);
-	minor = di << PARTN_BITS;
-	dasd_blks[minor] = dasd_info[di]->sizes.kbytes;
-	for (i = 0; i < (1 << PARTN_BITS); i++) {
-		dasd_secsize[minor + i] = dasd_info[di]->sizes.bp_sector;
-		dasd_blksize[minor + i] = dasd_info[di]->sizes.bp_block;
-		dasd_maxsecs[minor + i] = 252 << dasd_info[di]->sizes.s2b_shift;
+static void
+try_request_fn (void)
+{
+        long flags;
+        spin_lock_irqsave (&io_request_lock,flags);
+	atomic_set (&request_fn_scheduled, 0);
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+        {
+          major_info_t *mi;
+          for (mi=dasd_major_info; mi != NULL; mi = mi->next ) {
+            do_dasd_request(BLK_DEFAULT_QUEUE(mi->gendisk.major));
 	}
-	return rc;
+        }
+#else
+        do_dasd_request ();
+#endif /* LINUX_IS_24 */
+        spin_unlock_irqrestore (&io_request_lock,flags);
 }
 
-void
-dasd_partn_detect (int di)
+static void
+schedule_request_fn (void (*func) (void))
 {
-        int minor = di << PARTN_BITS;
-	while (atomic_read (&dasd_info[di]->status) !=
-	       DASD_INFO_STATUS_FORMATTED) {
-                interruptible_sleep_on(&dasd_info[di]->wait_q);
+	static struct tq_struct req_tq =
+	{0,};
+	/* Protect against rescheduling, when already running */
+	if (func != try_request_fn) {
+		panic (PRINTK_HEADER "Programming error! must call schedule_request_fn (try_request_fn)\n");
 	}
-	dd_gendisk.part[minor].nr_sects = dasd_info[di]->sizes.kbytes << 1;
-	resetup_one_dev (&dd_gendisk, di);
+	if (atomic_compare_and_swap (0, 1, &request_fn_scheduled))
+		return;
+	req_tq.routine = (void *) (void *) func;
+	queue_task (&req_tq, &tq_immediate);
+	mark_bh (IMMEDIATE_BH);
+	return;
 }
 
-void
-dasd_do_chanq (void)
+int
+dasd_start_IO (ccw_req_t * cqr)
 {
-	dasd_chanq_t *qp = NULL;
-	cqr_t *cqr, *next;
-        long flags;
-        int irq;
-        int tasks;
+	int rc = 0;
+	int retries = DASD_SSCH_RETRIES;
+	dasd_device_t *device = cqr->device;
+	int irq, devno;
+	int devindex, partn;
+	major_info_t *major_info;
+	struct request *req;
 
-	atomic_set (&bh_scheduled, 0);
-	dasd_debug (0xc4c40000);	/* DD */
-	for (qp = cq_head; qp != NULL;) {
-/* Get first request */
-		dasd_debug ((unsigned long) qp);
-		cqr = (cqr_t *) (qp->head);
-/* empty queue -> dequeue and proceed */
 		if (!cqr) {
-			dasd_chanq_t *nqp = qp->next_q;
-			cql_deq (qp);
-			qp = nqp;
-			continue;
+		printk (KERN_WARNING PRINTK_HEADER "No request passed to start_io function");
+		return -EINVAL;
 		}
-/* process all requests on that queue */
+	irq = device->devinfo.irq;
+	devno = device->devinfo.devno;
+	req = (struct request *) cqr->req;
+	devindex = devindex_from_kdev_t (req->rq_dev);
+	major_info = major_info_from_devindex (devindex);
+	partn = MINOR (req->rq_dev) & ((1 << major_info->gendisk.minor_shift) - 1);
+	if (strncmp ((char *) &cqr->magic, device->discipline->ebcname, 4)) {
+		printk (KERN_WARNING PRINTK_HEADER
+			"0x%04X on sch %d = /dev/%s (%d:%d)"
+			" magic number of ccw_req_t 0x%08lX doesn't match"
+			" discipline 0x%08lX\n",
+			devno, irq, device->name,
+			major_from_devindex (devindex),
+			devindex << DASD_PARTN_BITS,
+			cqr->magic, *(long *) device->discipline->name);
+		return -EINVAL;
+	}
+	atomic_compare_and_swap_debug (&cqr->status, CQR_STATUS_QUEUED, CQR_STATUS_IN_IO);
 		do {
-                        next = NULL;
-			dasd_debug ((unsigned long) cqr);	/* cqr */
-			if (cqr->magic != DASD_MAGIC &&
-			    cqr->magic != MDSK_MAGIC &&
-			    cqr->magic != ERP_MAGIC) {
-				dasd_debug (0xc4c46ff2);	/* DD?2 */
-				panic ( PRINTK_HEADER "do_cq:"
-					"magic mismatch %p -> %x\n", 
-					cqr, cqr -> magic);
-                                break;
+		asm volatile ("STCK %0":"=m" (cqr->startclk));
+		rc = do_IO (irq, cqr->cpaddr, (long) cqr, 0x00, cqr->options);
+		switch (rc) {
+		case 0:
+                        if (!(cqr->options & DOIO_WAIT_FOR_INTERRUPT)) {
+				atomic_set_mask (DASD_CHANQ_BUSY, &device->queue.flags);
 			}
-                        irq = dasd_info[cqr->devindex]->info.irq;
-                        s390irq_spin_lock_irqsave (irq, flags);
-			switch (atomic_read (&cqr->status)) {
-			case CQR_STATUS_IN_IO:
-                                dasd_debug (0xc4c4c9d6);	/* DDIO */
-                                break;
-			case CQR_STATUS_QUEUED:
-                                dasd_debug (0xc4c4e2e3);	/* DDST */
-				if (dasd_start_IO (cqr) != 0) {
-                                  PRINT_WARN("start_io failed\n");
+                        if ( cqr->expires ) {
+                                cqr->expires += cqr->startclk;
                                 }
                                 break;
-			case CQR_STATUS_ERROR:{
-                                dasd_debug (0xc4c4c5d9);	/* DDER */
-                                if ( ++ cqr->retries  < 2 ) {
-                                        atomic_set (&cqr->status,
-                                                    CQR_STATUS_QUEUED);
-                                        dasd_debug (0xc4c4e2e3);
-                                        if (dasd_start_IO (cqr) == 0) {
-                                                atomic_dec (&qp->
-                                                            dirty_requests);
-                                                break;
-                                        }
-                                }
-                                ACS (cqr->status,
-                                     CQR_STATUS_ERROR,
-                                     CQR_STATUS_FAILED);
+		case -ENODEV:
+			printk (KERN_WARNING PRINTK_HEADER
+			    " devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+				" appears not to be present %d retries left\n",
+				devno, irq, device->name, major_from_devindex (devindex), devindex << DASD_PARTN_BITS,
+				retries);
                                 break;
-                        }
-			case CQR_STATUS_ERP_PEND:{
-					/* This case is entered, when an interrupt
-					   ended with a condittion */
-					dasd_erp_action_t erp_action;
-					erp_t *erp = NULL;
-
-                                        if ( cqr -> magic != ERP_MAGIC ) {
-                                                erp = request_er ();
-                                                if (erp == NULL) {
-                                                        PRINT_WARN ("No memory for ERP%s\n", "");
-                                                        break;
-                                                }
-                                                memset (erp, 0, sizeof (erp_t));
-                                                erp->cqr.magic = ERP_MAGIC;
-                                                erp->cqr.int4cqr = cqr;
-                                                erp->cqr.devindex= cqr->devindex;
-                                                erp_action = dasd_erp_action (cqr);
-                                                if (erp_action) {
-                                                        PRINT_WARN ("Taking ERP action %p\n", erp_action);
-                                                        erp_action (erp);
-                                                }
-                                                dasd_chanq_enq_head(qp, (cqr_t *) erp);
-                                                next = (cqr_t *) erp;
-                                } else {
-                                                PRINT_WARN("ERP_ACTION failed\n");
-                                                ACS (cqr->status,
-                                                     CQR_STATUS_ERP_PEND,
-                                                    CQR_STATUS_FAILED);
-                                }
+		case -EIO:
+			printk (KERN_WARNING PRINTK_HEADER
+			    " devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+				" I/O error %d retries left\n",
+				devno, irq, device->name, major_from_devindex (devindex), devindex << DASD_PARTN_BITS,
+				retries);
                                 break;
-                        }
-			case CQR_STATUS_ERP_ACTIVE:
+		case -EBUSY:	/* set up timer, try later */
+			printk (KERN_WARNING PRINTK_HEADER
+			    " devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+				" is busy %d retries left\n",
+				devno, irq, device->name, major_from_devindex (devindex), devindex << DASD_PARTN_BITS,
+				retries);
 				break;
-			case CQR_STATUS_DONE:{
-                                next = cqr->next;
-                                if (cqr->magic == DASD_MAGIC) {
-                                        dasd_debug (0xc4c49692);
-                                } else if (cqr->magic == ERP_MAGIC) {
-                                        dasd_erp_action_t erp_postaction;
-                                        erp_t *erp = (erp_t *) cqr;
-                                        erp_postaction =
-                                                dasd_erp_postaction (erp);
-                                        if (erp_postaction)
-                                                erp_postaction (erp);
-                                        atomic_dec (&qp->dirty_requests);
-                                } else if (cqr->magic == MDSK_MAGIC) {
-                                } else {
-                                        PRINT_WARN ("unknown magic%s\n", "");
-                                }
-                                dasd_end_cqr (cqr, 1);
+		default:
+			printk (KERN_WARNING PRINTK_HEADER
+			    " devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+				" unknown return code %d, %d retries left."
+			  " Pls report this message to linux390@de.ibm.com\n",
+				devno, irq, device->name, major_from_devindex (devindex), devindex << DASD_PARTN_BITS,
+				rc, retries);
                                 break;
                         }
-			case CQR_STATUS_FAILED: {
-                                next = cqr->next;
-                                if (cqr->magic == DASD_MAGIC) {
-                                        dasd_debug (0xc4c49692);
-                                } else if (cqr->magic == ERP_MAGIC) {
-                                        dasd_erp_action_t erp_postaction;
-                                        erp_t *erp = (erp_t *) cqr;
-                                        erp_postaction =
-                                                dasd_erp_postaction (erp);
-                                        if (erp_postaction)
-                                                erp_postaction (erp);
-                                } else if (cqr->magic == MDSK_MAGIC) {
-                                } else {
-                                        PRINT_WARN ("unknown magic%s\n", "");
+	} while (rc && retries--);
+	if (rc) {
+                atomic_compare_and_swap_debug (&cqr->status, 
+                                               CQR_STATUS_IN_IO, 
+                                               CQR_STATUS_ERROR);
                                 }
-                                dasd_end_cqr (cqr, 0);
-                                atomic_dec (&qp->dirty_requests);
-                                break;
+	return rc;
                         }
-			default:
-                                PRINT_WARN ("unknown cqrstatus\n");
+
+static void
+dasd_end_request (struct request *req, int uptodate)
+{
+	struct buffer_head *bh;
+	while ((bh = req->bh) != NULL) {
+		req->bh = bh->b_reqnext;
+		bh->b_reqnext = NULL;
+		bh->b_end_io (bh, uptodate);
 			}
-                        s390irq_spin_unlock_irqrestore (irq, flags);
-		} while ((cqr = next) != NULL);
-		qp = qp->next_q;
+	if (!end_that_request_first (req, uptodate, DASD_NAME)) {
+#ifndef DEVICE_NO_RANDOM
+		add_blkdev_randomness (MAJOR (req->rq_dev));
+#endif
+		end_that_request_last (req);
 	}
-	spin_lock (&io_request_lock);
-	do_dasd_request ();
-	spin_unlock (&io_request_lock);
-	dasd_debug (0xc4c46d6d);	/* DD__ */
+	return;
 }
 
-/* 
-   The request_fn is called from ll_rw_blk for any new request.
-   We use it to feed the chanqs.
-   This implementation assumes we are serialized by the io_request_lock.
- */
+#undef CURRENT
+#define CURRENT (blk_dev[major].current_request)
 
-#define QUEUE_THRESHOLD 5
-
-void
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+static void
+do_dasd_request (request_queue_t * queue)
+#else
+static void
 do_dasd_request (void)
+#endif /* LINUX_IS_24 */
 {
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+	struct request *req;
+        int go;
+#else
 	struct request *req, *next, *prev;
-	cqr_t *cqr;
+#endif /* LINUX_IS_24 */
+	ccw_req_t *cqr;
 	dasd_chanq_t *q;
 	long flags;
-	int di, irq;
+	int devindex, irq, partn;
 	int broken, busy;
+	dasd_device_t *device;
+	major_info_t *major_info;
+	int devno;
+        int major;
         
-	dasd_debug (0xc4d90000);	/* DR */
-	dasd_debug ((unsigned long) __builtin_return_address (0));
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+        {
+                if ( queue == NULL ) {
+			printk(KERN_ERR PRINTK_HEADER "Null queue !!\n");
+                        return;
+                }
+                go = 1;
+                while (go && !list_empty (&queue->queue_head)) {
+                        req = blkdev_entry_next_request (&queue->queue_head);
+                        major = MAJOR(req->rq_dev);
+                        for (major_info = dasd_major_info; major_info != NULL; major_info = major_info->next ) {
+                                if ( major_info->gendisk.major == major )
+                                        break;
+                        }
+                        if ( major_info == NULL ) {
+                                printk (KERN_ERR PRINTK_HEADER "No major_info\n");
+                                return;
+                        }
+#else
+        for ( major_info = dasd_major_info; major_info != NULL; major_info = major_info->next ) {
+                major = major_info->gendisk.major;
 	prev = NULL;
 	for (req = CURRENT; req != NULL; req = next) {
 		next = req->next;
-		di = DEVICE_NR (req->rq_dev);
-		dasd_debug ((unsigned long) req);	/* req */
-		dasd_debug (0xc4d90000 +	/* DR## */
-                            ((((di/16)<9?(di/16)+0xf0:(di/16)+0xc1))<<8) +
-                            (((di%16)<9?(di%16)+0xf0:(di%16)+0xc1)));
-                irq = dasd_info[di]->info.irq;
-                s390irq_spin_lock_irqsave (irq, flags);
-                q = &dasd_info[di]->queue;
-		busy = atomic_read (&q->flags) & DASD_CHANQ_BUSY;
-		broken = atomic_read (&q->flags) & DASD_REQUEST_Q_BROKEN;
-		if (!busy ||
-		    (!broken &&
-		     (req->nr_sectors >= QUEUE_SECTORS))) {
+                        if (req == &blk_dev[major].plug) { /* remove plug if applicable */
+                                req->next = NULL;
                         if (prev) {
                                 prev->next = next;
                         } else {
                                 CURRENT = next;
                         }
-			req->next = NULL;
-			if (req == &blk_dev[MAJOR_NR].plug) {
-					dasd_debug (0xc4d99787); /* DRpg */
-				goto cont;
-			}
-			cqr = dasd_cqr_from_req (req);
-			if (!cqr) {
-					dasd_debug (0xc4d96ff1); /* DR?1 */
+                                continue;
+			}
+#endif /* LINUX_IS_24 */
+                        devindex = devindex_from_kdev_t(req->rq_dev);
+                        if ( devindex < 0 ) {
+                                printk ( KERN_WARNING PRINTK_HEADER 
+                                         "requesting I/O on nonexistent device %d -> %d\n",
+                                         devindex,req->rq_dev);
 				dasd_end_request (req, 0);
-				goto cont;
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+                                blkdev_dequeue_request (req);
+#else
+                                req->next = NULL;
+                                if (prev) {
+                                        prev->next = next;
+					} else {
+                                        CURRENT = next;
                         }
-                        dasd_debug ((unsigned long) cqr);	/* cqr */
-                        dasd_chanq_enq (q, cqr);
-			if (!(atomic_read (&q->flags) & DASD_CHANQ_ACTIVE)) {
-                                cql_enq_head (q);
+#endif /* LINUX_IS_24 */
+                                continue;
                         }
-			if (!busy) {
-				atomic_clear_mask (DASD_REQUEST_Q_BROKEN,
-						   &q->flags);
-				if ( atomic_read (&q->dirty_requests) == 0 ) {
-					if (dasd_start_IO (cqr) == 0) {
-					} else {
-						dasd_schedule_bh (dasd_do_chanq);
+                        device = find_dasd_device (devindex);
+                        if ( device == NULL ) {
+                                printk ( KERN_WARNING PRINTK_HEADER 
+                                         "requesting I/O on nonexistent device\n");
+				dasd_end_request(req,0);
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+                                blkdev_dequeue_request (req);
+#else
+                                req->next = NULL;
+                                if (prev) {
+                                        prev->next = next;
+                                } else {
+                                        CURRENT = next;
+                                }
+#endif /* LINUX_IS_24 */
+                                continue;
+                        }
+                        irq = device->devinfo.irq;
+                        s390irq_spin_lock_irqsave (irq, flags);
+                        devno = device->devinfo.devno;
+                        q = & device->queue;
+                        busy = atomic_read (&q->flags) & DASD_CHANQ_BUSY;
+                        broken = atomic_read (&q->flags) & DASD_REQUEST_Q_BROKEN;
+                        partn = MINOR (req->rq_dev) & ((1 << major_info->gendisk.minor_shift) - 1);
+                        if ( ! busy ||
+                             ( ! broken &&
+                               (req->nr_sectors >= QUEUE_SECTORS))) {
+                                if (device->discipline == NULL) {
+                                        printk (KERN_WARNING PRINTK_HEADER
+                                                " devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+                                                " is not assigned to a discipline\n",
+                                                devno, irq, device->name, major, devindex << DASD_PARTN_BITS);
+                                        dasd_end_request (req, 0);
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+                                        blkdev_dequeue_request (req);
+#else
+                                        req->next = NULL;
+                                       	if (prev) {
+                                                prev->next = next;
+                         	        } else {
+                                       	        CURRENT = next;
+                                        }
+#endif /* LINUX_IS_24 */	
+					s390irq_spin_unlock_irqrestore (irq, flags);
+                                	continue;
+                                }
+                                if (device->discipline->build_cp_from_req == NULL) {
+                                        printk (KERN_WARNING PRINTK_HEADER
+                                                " devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+                                                " discipline %s hast no builder function\n",
+                                                devno, irq, device->name,major, devindex << DASD_PARTN_BITS,
+                                                device->discipline->name);
+                                        dasd_end_request (req, 0);
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+                        	        blkdev_dequeue_request (req);
+#else
+                                	req->next = NULL;
+                                	if (prev) {
+                                        	prev->next = next;
+                                	} else {
+                                        	CURRENT = next;
+                                	}
+#endif /* LINUX_IS_24 */
+					s390irq_spin_unlock_irqrestore (irq, flags);
+					continue;                 
+		                }
+                                req->sector += major_info->gendisk.part[MINOR(req->rq_dev)].start_sect;
+                                cqr = device->discipline->build_cp_from_req (device, req);
+                                if (cqr == NULL) {
+                                        atomic_set_mask (DASD_REQUEST_Q_BROKEN, &q->flags);
+                                        printk (KERN_WARNING PRINTK_HEADER
+                                                " devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+                                                " Could not create channel program for request %p\n",
+                                                devno, irq, device->name, major, devindex << DASD_PARTN_BITS, req);
+                                /* put request back to queue*/
+                                        req->sector -= major_info->gendisk.part[MINOR(req->rq_dev)].start_sect;
+                                        s390irq_spin_unlock_irqrestore (irq, flags);
+                                        continue;
+                                } 
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+                                blkdev_dequeue_request (req);
+#else
+                                req->next = NULL;
+                                if (prev) {
+                                        prev->next = next;
+                                } else {
+                                        CURRENT = next;
                                 }
+#endif /* LINUX_IS_24 */
+                                dasd_chanq_enq (q, cqr);
+                                if (!(atomic_read (&q->flags) & DASD_CHANQ_ACTIVE)) {
+                                        qlist_enq (q);
+                                }
+                                if (!busy) {
+                                        atomic_clear_mask (DASD_REQUEST_Q_BROKEN, &q->flags);
+                                        if (atomic_read (&q->dirty_requests) == 0) {
+                                                if (device->discipline->start_IO == NULL) {
+                                                        printk (KERN_WARNING PRINTK_HEADER
+                                                                " devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+                                                                " dicsipline %s has no starter function\n",
+                                                                devno, irq, device->name, major, devindex << DASD_PARTN_BITS,
+                                                                device->discipline->name);
+                                                } else {
+                                                        if (device->discipline->start_IO (cqr) != 0) {
+                                                                printk (KERN_DEBUG PRINTK_HEADER
+                                                                        " devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+                                                                        " starting of request from req_fn failed, postponing\n",
+                                                                        devno, irq, device->name, major, devindex << DASD_PARTN_BITS);
+                                                                dasd_schedule_bh ();	/* initiate bh to run */
+                                                        }
+                                                }
+                                        } else {
+						dasd_schedule_bh();
+                                        } 
+                                }
+                        } else {
+                                atomic_set_mask (DASD_REQUEST_Q_BROKEN, &q->flags);
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+                                go = 0;
+#else
+                                prev = req;
+#endif /* LINUX_IS_24 */
                         }
+                        s390irq_spin_unlock_irqrestore (irq, flags);
+                }
+        }
+        return;
+}
+
+static void
+dasd_do_chanq (void)
+{
+	dasd_chanq_t *qp = NULL;
+	dasd_chanq_t *nqp;
+	dasd_device_t *device;
+	ccw_req_t *cqr, *next;
+	long flags;
+	int irq;
+	int devno, devindex;
+	int rc = -1;
+	volatile int cqrstatus;
+
+	for (qp = qlist_head; qp != NULL; qp = nqp) {
+		/* Get first request */
+		cqr = (ccw_req_t *) (qp->head);
+		nqp = qp->next_q;
+/* empty queue -> dequeue and proceed */
+		if (!cqr) {
+			qlist_deq (qp);
+			continue;
+		}
+/* process all requests on that queue */
+		do {
+			dasd_discipline_t *discipline;
+			next = NULL;
+			/* Sanity check... walk through disciplines */
+			for (discipline = dasd_disciplines;
+			     discipline != NULL;
+			     discipline = discipline->next)
+				if (!strncmp ((char *) &cqr->magic, discipline->ebcname, 4))
+					break;
+			if (!discipline) {	/* 1st sanity check */
+				panic (PRINTK_HEADER
+				       "in dasd_do_chanq: magic no mismatch %p -> 0x%lX\n",
+				       cqr, cqr->magic);
+			}
+			device = (dasd_device_t *) (cqr->device);
+			if (discipline != device->discipline) {		/* 1st sanity check */
+				printk (KERN_WARNING PRINTK_HEADER
+					"in dasd_do_chanq: discipline mismatch %p -> 0x%lX\n",
+					cqr, cqr->magic);
+				discipline = device->discipline;
+			}
+			irq = device->devinfo.irq;
+			devno = device->devinfo.devno;
+			devindex = devindex_from_devno (devno);
+
+			s390irq_spin_lock_irqsave (irq, flags);
+
+			cqrstatus = atomic_read (&cqr->status);
+			switch (cqrstatus) {
+			case CQR_STATUS_QUEUED:
+				if (discipline->start_IO &&
+				    ((rc = discipline->start_IO (cqr)) == 0)) {
+                                } else {
+					printk (KERN_WARNING PRINTK_HEADER
+						" devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+						" Failing to start I/O operation with rc %d\n",
+						devno, irq, device->name, major_from_devindex (devindex), devindex << DASD_PARTN_BITS, rc);
+					switch (rc) {
+					case EBUSY:
+						if (cqr->retries--) {
+							printk (KERN_WARNING PRINTK_HEADER
+								" devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+								" retrying %d retries left\n",
+								devno, irq, device->name, major_from_devindex (devindex), devindex << DASD_PARTN_BITS, cqr->retries);
+							break;
+						}
+					default:{	/* Fallthrough ?? */
+							printk (KERN_WARNING PRINTK_HEADER
+								" devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+								" Giving up this request!\n",
+								devno, irq, device->name, major_from_devindex (devindex), devindex << DASD_PARTN_BITS);
+							atomic_compare_and_swap_debug (&cqr->status, CQR_STATUS_QUEUED, CQR_STATUS_FAILED);
+							break;
+						}
+					}
+				}
+				break;
+			case CQR_STATUS_IN_IO:{
+					unsigned long long now;
+					unsigned long long delta;
+
+					asm volatile ("STCK %0":"=m" (now));
+					if (cqr->expires && cqr->startclk &&
+					    cqr->expires < now) {
+                                                delta = cqr->expires - cqr->startclk;
+						printk (KERN_ERR PRINTK_HEADER
+							" devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+							" I/O operation outstanding longer than %Ld usecs on req %p\n",
+							devno, irq, device->name, major_from_devindex (devindex), devindex << DASD_PARTN_BITS, delta >> 12, cqr);
+						if ( cqr->retries-- ) {
+							printk (KERN_WARNING PRINTK_HEADER
+								" devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+								" waiting %d more times\n",
+								devno, irq, device->name, major_from_devindex (devindex), devindex << DASD_PARTN_BITS, cqr->retries);
+							cqr->expires += delta;
+							break;
+						} else {
+							printk (KERN_WARNING PRINTK_HEADER
+								" devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+								" You should disable that device by issueing '@#?!'\n",		/* FIXME */
+								devno, irq, device->name, major_from_devindex (devindex), devindex << DASD_PARTN_BITS);
+							atomic_compare_and_swap_debug (&cqr->status, CQR_STATUS_IN_IO, CQR_STATUS_FAILED);
+							break;
+						}
+					}
+					break;
+				}
+			case CQR_STATUS_ERROR:{
+					dasd_erp_action_fn_t erp_action;
+					ccw_req_t *erp_cqr = NULL;
+					if (discipline->erp_action &&
+					    ((erp_action = discipline->erp_action (cqr)) != NULL)) {
+                                                printk (KERN_WARNING PRINTK_HEADER
+                                                        " devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+							" Taking error recovery action %p on req %p \n",
+							devno, irq, device->name, major_from_devindex (devindex), devindex << DASD_PARTN_BITS, erp_action,cqr);
+						erp_cqr = erp_action (cqr);
+					} else {
+						printk (KERN_WARNING PRINTK_HEADER
+							" devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+                                                        " No error recovery action\n",
+							devno, irq, device->name, major_from_devindex (devindex), devindex << DASD_PARTN_BITS);
+						atomic_compare_and_swap_debug (&cqr->status, CQR_STATUS_ERROR, CQR_STATUS_FAILED);
+					}
+					if ( erp_cqr != NULL ) {
+						dasd_chanq_enq_head (qp, erp_cqr);
+						next = erp_cqr;		/* prefer execution of erp ccw */
+					}
+					break;
+				}
+			case CQR_STATUS_DONE:{
+                                        dasd_erp_postaction_fn_t erp_postaction;
+                                        next = cqr->next;
+					if (cqr->refers && cqr->function) {	/* we deal with an ERP */
+						if (discipline->erp_postaction &&
+						    ((erp_postaction = discipline->erp_postaction (cqr)) != NULL)) {
+							printk (KERN_WARNING PRINTK_HEADER
+								" devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+								" postprocessing successful error recovery action %p\n",
+								devno, irq, device->name, major_from_devindex (devindex), devindex << DASD_PARTN_BITS, erp_postaction);
+							erp_postaction (cqr, 1);
+                                                        atomic_dec (&device->queue.dirty_requests);
+						} else {
+							printk (KERN_WARNING PRINTK_HEADER
+								" devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+								" No procedure to postprocess error recovery action"
+                                                                " giving up request",
+								devno, irq, device->name, major_from_devindex (devindex), devindex << DASD_PARTN_BITS);
+							atomic_compare_and_swap_debug (&cqr->refers->status, CQR_STATUS_ERROR, CQR_STATUS_FAILED);
+						}
+					} else if ( cqr->req ) {
+						asm volatile ("STCK %0":"=m" (cqr->endclk));
+						dasd_end_request (cqr->req, 1);
+#ifdef DASD_PROFILE
+						dasd_profile_add (cqr);
+#endif				/* DASD_PROFILE */
+					} 
+					dasd_chanq_deq (&device->queue, cqr);
+                                        dasd_free_request(cqr);
+					break;
+				}
+			case CQR_STATUS_FAILED:{
+					dasd_erp_postaction_fn_t erp_postaction;
+					next = cqr->next;
+					if (cqr->refers && cqr->function) {	/* we deal with an ERP */
+						if (discipline->erp_postaction &&
+						    ((erp_postaction = discipline->erp_postaction (cqr)) != NULL)) {
+							printk (KERN_WARNING PRINTK_HEADER
+								" devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+								" postprocessing unsuccessful error recovery action %p\n",
+								devno, irq, device->name, major_from_devindex (devindex), devindex << DASD_PARTN_BITS, erp_postaction);
+							erp_postaction (cqr, 0);
+                                                        atomic_dec (&device->queue.dirty_requests);
+
+						} else {
+							printk (KERN_WARNING PRINTK_HEADER
+								" devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+								" No procedure to postprocess unsuccessful error recovery action"
+							 " giving up request",
+								devno, irq, device->name, major_from_devindex (devindex), devindex << DASD_PARTN_BITS);
+							atomic_compare_and_swap_debug (&cqr->refers->status, CQR_STATUS_ERROR, CQR_STATUS_FAILED);
+						}
+					} else if (cqr->req) {
+						asm volatile ("STCK %0":"=m" (cqr->endclk));
+						dasd_end_request (cqr->req, 0);
+#ifdef DASD_PROFILE
+						dasd_profile_add (cqr);
+#endif				/* DASD_PROFILE */
+					} else {
+						printk (KERN_WARNING PRINTK_HEADER
+							"Internal error in " __FILE__ " on line %d."
+							" inconsistent content of ccw_req_t"
+							" refers = %p,function = %p, request = %p"
+							" Pls send this message and your System.map to"
+						     " linux390@de.ibm.com\n",
+							__LINE__, cqr->refers, cqr->function, cqr->req);
+					}
+					dasd_chanq_deq (&device->queue, cqr);
+                                        dasd_free_request(cqr);
+					break;
+				}
+			default:{
+					printk (KERN_WARNING PRINTK_HEADER
+						"Internal error in " __FILE__ " on line %d."
+					  " inconsistent content of ccw_req_t"
+						" cqrstatus = %d"
+						" Pls send this message and your System.map to"
+						" linux390@de.ibm.com\n",
+						__LINE__, cqrstatus);
 			}
-                } else {
-                        dasd_debug (0xc4d9c2d9);	/* DRBR */
-			atomic_set_mask (DASD_REQUEST_Q_BROKEN, &q->flags);
-			prev = req;
 		}
-        cont:
                 s390irq_spin_unlock_irqrestore (irq, flags);
+		} while ((cqr = next) != NULL);
 	}
-	dasd_debug (0xc4d96d6d);	/* DR__ */
+	schedule_request_fn (try_request_fn);
+        return;
 }
 
 void
-dasd_handler (int irq, void *ds, struct pt_regs *regs)
+dasd_int_handler (int irq, void *ds, struct pt_regs *regs)
 {
 	devstat_t *stat = (devstat_t *) ds;
 	int ip;
-	cqr_t *cqr;
+	ccw_req_t *cqr;
 	int done_fast_io = 0;
-	dasd_era_t era;
+	dasd_era_t era = dasd_era_fatal;
+	dasd_device_t *device;
+	int devno = -1, devindex = -1;
+        
+#undef ERP_DEBUG
+#ifdef ERP_DEBUG 
         static int counter = 0; 
+#endif
 
-	dasd_debug (0xc4c80000);	/* DH */
 	if (!stat) {
 		PRINT_ERR ("handler called without devstat");
 		return;
 	}
 	ip = stat->intparm;
-	dasd_debug (ip);	/* intparm */
 	if (!ip) {		/* no intparm: unsolicited interrupt */
-		dasd_debug (0xc4c8a489);	/* DHui */
 		PRINT_INFO ("%04X caught unsolicited interrupt\n",
 			    stat->devno);
 		return;
 	}
 		if (ip & 0x80000001) {
-			dasd_debug (0xc4c8a489);	/* DHui */
 		PRINT_INFO ("%04X  caught spurious interrupt with parm %08x\n",
 			    stat->devno, ip);
 			return;
 		}
-		cqr = (cqr_t *) ip;
-	if (cqr->magic == DASD_MAGIC || cqr->magic == ERP_MAGIC) {
+	cqr = (ccw_req_t *) ip;
+	device = (dasd_device_t *) cqr->device;
+	devno = device->devinfo.devno;
+	devindex = devindex_from_devno (devno);
+	if (device == NULL) {
+		printk (KERN_WARNING PRINTK_HEADER
+		     " IRQ on devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+			" belongs to NULL device\n",
+			devno, irq, device->name, major_from_devindex (devindex), devindex << DASD_PARTN_BITS);
+	}
+	if (device->devinfo.irq != irq) {
+		printk (KERN_WARNING PRINTK_HEADER
+		     " IRQ on devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+			" doesn't belong to device irq %d\n",
+			devno, irq, device->name, major_from_devindex (devindex), devindex << DASD_PARTN_BITS,
+			device->devinfo.irq);
+		return;
+	}
+	if (device->discipline == NULL) {
+		printk (KERN_WARNING PRINTK_HEADER
+			" devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+			" is not assigned to a discipline\n",
+			devno, irq, device->name, major_from_devindex (devindex), devindex << DASD_PARTN_BITS);
+	}
+	if (strncmp (device->discipline->ebcname, (char *) &cqr->magic, 4)) {
+		printk (KERN_WARNING PRINTK_HEADER
+			"0x%04X on sch %d : /dev/%s (%d:%d)"
+			" magic number of ccw_req_t 0x%08lX doesn't match"
+			" discipline 0x%08X\n",
+			devno, irq, device->name,
+			major_from_devindex (devindex),
+			devindex << DASD_PARTN_BITS,
+			cqr->magic, *(int *) (&device->discipline->name));
+		return;
+	}
 		asm volatile ("STCK %0":"=m" (cqr->stopclk));
 		if ((stat->cstat == 0x00 &&
 		     stat->dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) ||
-		    ((era = dasd_erp_examine (cqr, stat)) == dasd_era_none)) {
-			dasd_debug (0xc4c89692);	/* DHok */
-#if 0
-                        if ( counter < 20 || cqr -> magic == ERP_MAGIC) {
-                                counter ++;
-#endif
-                                ACS (cqr->status, CQR_STATUS_IN_IO, CQR_STATUS_DONE);
-#if 0
-                        } else {
-                                counter=0;
-                                PRINT_WARN ("Faking I/O error\n");
-                                ACS (cqr->status, CQR_STATUS_IN_IO, CQR_STATUS_ERP_PEND);
-                                atomic_inc (&dasd_info[cqr->devindex]->
-                                            queue.dirty_requests);
+	    (device->discipline->examine_error &&
+	     (era = device->discipline->examine_error (cqr, stat)) == dasd_era_none)) {
+#ifdef ERP_DEBUG
+                if ( ++counter % 137 == 0 ) {
+                        printk ( KERN_INFO PRINTK_HEADER "Faking I/O error to recover from\n");
+                        era = dasd_era_recover;
+                        stat->flag |= DEVSTAT_FLAG_SENSE_AVAIL;
+                        stat->dstat |= 0x02;
+                        goto error_fake_done;
                         }
 #endif
-			if (atomic_read (&dasd_info[cqr->devindex]->status) ==
-			    DASD_INFO_STATUS_DETECTED) {
-				register_dasd_last (cqr->devindex);
-                                if ( dasd_info[cqr->devindex]->wait_q ) {
-                                        wake_up( &dasd_info[cqr->devindex]->
-                                                wait_q);
-                                }
-			}
+		atomic_compare_and_swap_debug (&cqr->status, CQR_STATUS_IN_IO, CQR_STATUS_DONE);
+                atomic_compare_and_swap (DASD_DEVICE_LEVEL_ANALYSIS_PENDING,
+					 DASD_DEVICE_LEVEL_ANALYSIS_PREPARED,
+                                         &device->level);
 			if (cqr->next &&
 			    (atomic_read (&cqr->next->status) ==
 			     CQR_STATUS_QUEUED)) {
-				dasd_debug (0xc4c8e2e3);	/* DHST */
 				if (dasd_start_IO (cqr->next) == 0) {
 					done_fast_io = 1;
-				} else {
 			}
 		}
 		} else {	/* only visited in case of error ! */
-		dasd_debug (0xc4c8c5d9);	/* DHER */
-                        dasd_dump_sense (stat);
-		if (!cqr->dstat)
-			cqr->dstat = kmalloc (sizeof (devstat_t),
-					      GFP_ATOMIC);
+#ifdef ERP_DEBUG
+        error_fake_done:
+#endif
+		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\n");
 		}
-                        atomic_inc (&dasd_info[cqr->devindex]->
-                                    queue.dirty_requests);
+		if (device->discipline &&
+		    device->discipline->dump_sense) {
+			char *errmsg = device->discipline->dump_sense (device, cqr);
+			if (errmsg != NULL) {
+				printk ("%s", errmsg);
+				free_page ((unsigned long) errmsg);
+			} else {
+				printk (KERN_WARNING PRINTK_HEADER
+					"No memory to dump error message\n");
+			}
+		}
+		atomic_inc (&device->queue.dirty_requests);
 		/* errorprocessing */
 			if (era == dasd_era_fatal) {
 				PRINT_WARN ("ERP returned fatal error\n");
-				ACS (cqr->status,
+			atomic_compare_and_swap_debug (&cqr->status,
 				     CQR_STATUS_IN_IO, CQR_STATUS_FAILED);
 			} else {
-                                ACS (cqr->status,
-                                     CQR_STATUS_IN_IO, CQR_STATUS_ERP_PEND);
+			atomic_compare_and_swap_debug (&cqr->status,
+                                                       CQR_STATUS_IN_IO, CQR_STATUS_ERROR);
                         }
 	}
 	if (done_fast_io == 0)
-		atomic_clear_mask (DASD_CHANQ_BUSY,
-				   &dasd_info[cqr->devindex]->
-				   queue.flags);
+		atomic_clear_mask (DASD_CHANQ_BUSY, &device->queue.flags);
         
-	if (cqr->flags & DASD_DO_IO_SLEEP) {
-		dasd_debug (0xc4c8a6a4);	/* DHwu */
-		dasd_wakeup ();
-	} else if (! (cqr->options & DOIO_WAIT_FOR_INTERRUPT) ){
-                dasd_debug (0xc4c8a293);	/* DHsl */
-                        dasd_schedule_bh (dasd_do_chanq);
-	} else {
-                dasd_debug (0x64686f6f);	/* DH_g */
-                dasd_debug (cqr->flags);	/* DH_g */
-        }
-        } else {
-                dasd_debug (0xc4c86ff1);	/* DH?1 */
-                PRINT_ERR ("handler:magic mismatch on %p %08x\n",
-                           cqr, cqr->magic);
-                return;
-        }
-	dasd_debug (0xc4c86d6d);	/* DHwu */
-}
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+        wake_up (&device->wait_q);
+#else
+	if (device->wait_q) {
+		wake_up (&device->wait_q);
+	}
+#endif /* LINUX_IS_24 */
+	dasd_schedule_bh ();
+}
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+static wait_queue_head_t watcher_queue;
+#else
+static struct wait_queue watcher_queue_Qend = {NULL,};
+static struct wait_queue *watcher_queue = &watcher_queue_Qend;
+#endif /* LINUX_IS_24 */
 
-static int
-dasd_format (int dev, format_data_t * fdata)
+static void
+dasd_watcher (void)
 {
-	int rc;
-	int devindex = DEVICE_NR (dev);
-	dasd_chanq_t *q;
-	cqr_t *cqr;
-	int irq;
-	long flags;
-	PRINT_INFO ("%04X called format on %x\n",
-		    dasd_info[devindex]->info.devno, dev);
-	if (MINOR (dev) & (0xff >> (8 - PARTN_BITS))) {
-		PRINT_WARN ("Can't format partition! minor %x %x\n",
-			    MINOR (dev), 0xff >> (8 - PARTN_BITS));
-		return -EINVAL;
+	do {
+		dasd_schedule_bh ();
+		schedule_request_fn (try_request_fn);
+		interruptible_sleep_on_timeout (&watcher_queue, 5 * HZ);
+	} while (1);
+        }
+
+/* SECTION: Some stuff related to error recovery */
+
+ccw_req_t *
+default_erp_action (ccw_req_t * cqr)
+{
+	ccw_req_t *erp = ccw_alloc_request ((char *) &cqr->magic, 1, 0);
+
+	erp->cpaddr->cmd_code = CCW_CMD_NOOP;
+	erp->function = default_erp_action;
+	erp->refers = cqr;
+	erp->device = cqr->device;
+        erp->magic = cqr->magic;
+	atomic_set (&erp->status, CQR_STATUS_FILLED);
+        if ( cqr->startclk && cqr->expires )
+                cqr->expires -= cqr->startclk;
+        
+	if (cqr->retries++ <= 16) {
+                atomic_compare_and_swap_debug (&cqr->status,
+                                               CQR_STATUS_ERROR,
+					       CQR_STATUS_QUEUED);
+        } else {
+		printk (KERN_WARNING PRINTK_HEADER "ERP retry count exceeded\n");
+		atomic_compare_and_swap_debug (&cqr->status,
+					       CQR_STATUS_ERROR,
+					       CQR_STATUS_FAILED);
 	}
-	down (&dasd_info[devindex]->sem);
-	atomic_set (&dasd_info[devindex]->status,
-		    DASD_INFO_STATUS_UNKNOWN);
-	if (dasd_info[devindex]->open_count == 1) {
-		rc = dasd_disciplines[dasd_info[devindex]->type]->
-		    dasd_format (devindex, fdata);
-		if (rc) {
-			PRINT_WARN ("Formatting failed rc=%d\n", rc);
-			up (&dasd_info[devindex]->sem);
-			return rc;
-		}
-	} else {
-		PRINT_WARN ("device is open! %d\n", dasd_info[devindex]->open_count);
-		up (&dasd_info[devindex]->sem);
+	return erp;
+}
+
+int
+default_erp_postaction (ccw_req_t * cqr, int success)
+{
+	int rc = 0;
+	if (cqr->refers == NULL || cqr->function == NULL) {
+		printk (KERN_WARNING PRINTK_HEADER
+			"ERP postaction called for non ERP cqr\n");
 		return -EINVAL;
 	}
-#if DASD_PARANOIA > 1
-	if (!dasd_disciplines[dasd_info[devindex]->type]->fill_sizes_first) {
-		INTERNAL_CHECK ("No fill_sizes for dt=%d\n", dasd_info[devindex]->type);
-	} else
-#endif				/* DASD_PARANOIA */
-	{
-		ACS (dasd_info[devindex]->status,
-		     DASD_INFO_STATUS_UNKNOWN, DASD_INFO_STATUS_DETECTED);
-		irq = dasd_info[devindex]->info.irq;
-		PRINT_INFO ("%04X reacessing, irq %x, index %d\n",
-			    get_devno_by_irq (irq), irq, devindex);
-		s390irq_spin_lock_irqsave (irq, flags);
-		q = &dasd_info[devindex]->queue;
-		cqr = dasd_disciplines[dasd_info[devindex]->type]->
-		    fill_sizes_first (devindex);
-		dasd_chanq_enq (q, cqr);
-		if (!(atomic_read (&q->flags) & DASD_CHANQ_ACTIVE)) {
-			cql_enq_head (q);
-		}
-		dasd_schedule_bh (dasd_do_chanq);
-		s390irq_spin_unlock_irqrestore (irq, flags);
-	}
-	up (&dasd_info[devindex]->sem);
+	if (cqr->function != default_erp_action) {
+		printk (KERN_WARNING PRINTK_HEADER
+                        "default ERP postaction called for non default ERP cqr\n");
+		return -EINVAL;
+        }
 	return rc;
 }
 
+/* SECTION: The helpers of the struct file_operations */
+
 static int
-register_dasd (int irq, dasd_type_t dt, dev_info_t * info)
+dasd_format (dasd_device_t * device, format_data_t * fdata)
 {
 	int rc = 0;
-	int di;
-	unsigned long flags;
-	dasd_chanq_t *q;
-	cqr_t *cqr;
-	static spinlock_t register_lock = SPIN_LOCK_UNLOCKED;
-	spin_lock (&register_lock);
-	FUNCTION_ENTRY ("register_dasd");
-	di = devindex_from_devno (info->devno);
-	if (di < 0) {
-		INTERNAL_CHECK ("Can't get index for devno %d\n", info->devno);
-		return -ENODEV;
-	}
-	if (dasd_info[di]) {	/* devindex is not free */
-		INTERNAL_CHECK ("reusing allocated deviceindex %d\n", di);
-		return -ENODEV;
-	}
-	dasd_info[di] = (dasd_information_t *)
-	    kmalloc (sizeof (dasd_information_t), GFP_ATOMIC);
-	if (dasd_info[di] == NULL) {
-		PRINT_WARN ("No memory for dasd_info_t on irq %d\n", irq);
-		return -ENOMEM;
-	}
-	memset (dasd_info[di], 0, sizeof (dasd_information_t));
-	memcpy (&(dasd_info[di]->info), info, sizeof (dev_info_t));
-	spin_lock_init (&dasd_info[di]->queue.f_lock);
-	spin_lock_init (&dasd_info[di]->queue.q_lock);
-	dasd_info[di]->type = dt;
-	dasd_info[di]->irq = irq;
-	dasd_info[di]->sem = MUTEX;
-#ifdef CONFIG_DASD_MDSK
-	if (dt == dasd_mdsk) {
-		dasd_info[di]->rdc_data = kmalloc (
-				 sizeof (dasd_characteristics_t), GFP_ATOMIC);
-		if (!dasd_info[di]->rdc_data) {
-			PRINT_WARN ("No memory for char on irq %d\n", irq);
-			goto unalloc;
-		}
-		dasd_info[di]->rdc_data->mdsk.dev_nr = dasd_info[di]->
-		    info.devno;
-		dasd_info[di]->rdc_data->mdsk.rdc_len =
-		    sizeof (dasd_mdsk_characteristics_t);
-	} else
-#endif				/* CONFIG_DASD_MDSK */
-	rc = dasd_read_characteristics (dasd_info[di]);
-	if (rc) {
-		PRINT_WARN ("RDC returned error %d\n", rc);
-		rc = -ENODEV;
-		goto unalloc;
-	}
-#if DASD_PARANOIA > 1
-	if (dasd_disciplines[dt]->ck_characteristics)
-#endif				/* DASD_PARANOIA */
-		rc = dasd_disciplines[dt]->
-		    ck_characteristics (dasd_info[di]->rdc_data);
-
-	if (rc) {
-		INTERNAL_CHECK ("Discipline returned non-zero when"
-				"checking device characteristics%s\n", "");
-		rc = -ENODEV;
-		goto unalloc;
-	}
-#ifdef CONFIG_DASD_MDSK
-	if (dt == dasd_mdsk) {
+	int devno = device->devinfo.devno;
+	int irq = device->devinfo.irq;
+	int devindex = devindex_from_devno (devno);
 
-	} else
-#endif				/* CONFIG_DASD_MDSK */
-	rc = request_irq (irq, dasd_handler, 0, "dasd",
-			  &(dasd_info[di]->dev_status));
-	ACS (dasd_info[di]->status,
-	     DASD_INFO_STATUS_UNKNOWN, DASD_INFO_STATUS_DETECTED);
-	if (rc) {
-#if DASD_PARANOIA > 0
+	if (device->open_count != 1) {
 		printk (KERN_WARNING PRINTK_HEADER
-			"Cannot register irq %d, rc=%d\n",
-			irq, rc);
-#endif				/* DASD_PARANOIA */
-		rc = -ENODEV;
-		goto unalloc;
-	}
-#if DASD_PARANOIA > 1
-	if (!dasd_disciplines[dt]->fill_sizes_first) {
-		INTERNAL_CHECK ("No fill_sizes for dt=%d\n", dt);
-		goto unregister;
-	}
-#endif				/* DASD_PARANOIA */
-	irq = dasd_info[di]->info.irq;
-	PRINT_INFO ("%04X trying to access, irq %x, index %d\n",
-		    get_devno_by_irq (irq), irq, di);
-	s390irq_spin_lock_irqsave (irq, flags);
-	q = &dasd_info[di]->queue;
-	cqr = dasd_disciplines[dt]->fill_sizes_first (di);
-	dasd_chanq_enq (q, cqr);
-	if (!(atomic_read (&q->flags) & DASD_CHANQ_ACTIVE)) {
-		cql_enq_head (q);
-	}
-	if (dasd_start_IO (cqr) != 0) {
-		dasd_schedule_bh (dasd_do_chanq);
+			" devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+		      " you shouldn't format a device that is already open\n",
+			devno, irq, device->name, major_from_devindex (devindex), devindex << DASD_PARTN_BITS);
+		return -EINVAL;
 	}
-	s390irq_spin_unlock_irqrestore (irq, flags);
-
-	goto exit;
-
-      unregister:
-	free_irq (irq, &(dasd_info[di]->dev_status));
-      unalloc:
-	kfree (dasd_info[di]);
-	dasd_info[di] = NULL;
-      exit:
-	spin_unlock (&register_lock);
-	FUNCTION_EXIT ("register_dasd");
+        dasd_set_device_level( device->devinfo.irq,
+                               DASD_DEVICE_LEVEL_RECOGNIZED,
+                               device->discipline,
+                               0);
+	if (device->discipline->format_device)
+          rc = device->discipline->format_device (device, fdata);
+		if (rc) {
+                printk (KERN_WARNING PRINTK_HEADER
+                        " devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+			" Formatting failed with rc = %d\n",
+			devno, irq, device->name, major_from_devindex (devindex), devindex << DASD_PARTN_BITS, rc);
+			return rc;
+		}
+	printk (KERN_WARNING PRINTK_HEADER
+		" devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+		" Formatting finished successfully rc = %d\n",
+		devno, irq, device->name, major_from_devindex (devindex), devindex << DASD_PARTN_BITS, rc);
+        dasd_set_device_level( device->devinfo.irq,
+                               DASD_DEVICE_LEVEL_ANALYSIS_PENDING,
+                               device->discipline,
+                               0);
+        udelay(1500000);
+        dasd_set_device_level( device->devinfo.irq,
+                               DASD_DEVICE_LEVEL_ANALYSED,
+                               device->discipline,
+                               0);
 	return rc;
 }
 
 static int
-probe_for_dasd (int irq)
+do_dasd_ioctl (struct inode *inp, /* unsigned */ int no, unsigned long data)
 {
-	int rc;
-	dev_info_t info;
-	dasd_type_t dt;
-
-	rc = get_dev_info_by_irq (irq, &info);
-
-	if (rc == -ENODEV) {	/* end of device list */
-		return rc;
-	} else if ((info.status & DEVSTAT_DEVICE_OWNED)) {
-		return -EBUSY;
-	} else if ((info.status & DEVSTAT_NOT_OPER)) {
-		return -ENODEV;
-	}
-#if DASD_PARANOIA > 2
-	else {
-		INTERNAL_CHECK ("unknown rc %d of get_dev_info", rc);
-		return rc;
+	int rc = 0;
+	int devindex = devindex_from_kdev_t (inp->i_rdev);
+	dasd_device_t *device = find_dasd_device (devindex);
+	major_info_t *major_info = major_info_from_devindex (devindex);
+        
+	if (!device) {
+		printk (KERN_WARNING PRINTK_HEADER
+			"No device registered as 0x%04x (%d)\n",
+			inp->i_rdev, devindex);
+		return -EINVAL;
 	}
-#endif				/* DASD_PARANOIA */
-
-	dt = check_type (&info);	/* make a first guess */
-
-	if (dt == dasd_none) {
-		return -ENODEV;
+	if ((_IOC_DIR (no) != _IOC_NONE) && (data == 0)) {
+		PRINT_DEBUG ("empty data ptr");
+		return -EINVAL;
 	}
-		if (!dasd_is_accessible (info.devno)) {
-			return -ENODEV;
+#if 0
+	printk (KERN_DEBUG PRINTK_HEADER
+		"ioctl 0x%08x %s'0x%x'%d(%d) on /dev/%s (%d:%d,"
+		" devno 0x%04X on irq %d) with 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),
+		device->name, MAJOR (inp->i_rdev), MINOR (inp->i_rdev),
+		device->devinfo.devno, device->devinfo.irq,
+		data);
+#endif
+	switch (no) {
+	case BLKGETSIZE:{	/* Return device size */
+			int blocks = blk_size[MAJOR (inp->i_rdev)][MINOR (inp->i_rdev)] << 1;
+			rc = copy_to_user ((long *) data, &blocks, sizeof (long));
+			break;
 		}
-	if (!dasd_disciplines[dt]->ck_devinfo) {
-			INTERNAL_ERROR ("no ck_devinfo function%s\n", "");
-			return -ENODEV;
+	case BLKFLSBUF:{
+			rc = fsync_dev (inp->i_rdev);
+			break;
 		}
-	rc = dasd_disciplines[dt]->ck_devinfo (&info);
-	if (rc) {
-			return rc;
+	case BLKRAGET:{
+			rc = copy_to_user ((long *) data, read_ahead + MAJOR (inp->i_rdev), sizeof (long));
+			break;
 		}
-	if (dasd_probeonly) {
-		PRINT_INFO ("%04X not enabled due to probeonly mode\n",
-			    info.devno);
-		dasd_add_devno_to_ranges (info.devno);
-			return -ENODEV;
-	} else {
-		rc = register_dasd (irq, dt, &info);
-	}
-		if (rc) {
-		PRINT_WARN ("%04X not enabled due to errors\n",
-			    info.devno);
-		} else {
-		PRINT_INFO ("%04X is (dasd%c) minor %d (%s)\n",
-				    info.devno,
-			    'a' + devindex_from_devno (info.devno),
-			       devindex_from_devno (info.devno) << PARTN_BITS,
-				    dasd_name[dt]);
+	case BLKRASET:{
+			rc = copy_from_user (read_ahead + MAJOR (inp->i_rdev), (long *) data, sizeof (long));
+			break;
 		}
+	case BLKRRPART:{
+			dasd_partn_detect (devindex);
+			rc = 0;
+			break;
+		}
+	case BLKGETBSZ:{
+			rc = copy_to_user ((int *) data, &blksize_size[MAJOR (inp->i_rdev)][MINOR (inp->i_rdev)],
+					   sizeof (int));
+			break;
+		}
+	case HDIO_GETGEO:{
+			struct hd_geometry geo =
+			{0,};
+			if (device->discipline->fill_geometry)
+				device->discipline->fill_geometry (device, &geo);
+			rc = copy_to_user ((struct hd_geometry *) data, &geo,
+					   sizeof (struct hd_geometry));
+			break;
+		}
+#if ! (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+		RO_IOCTLS (inp->i_rdev, data);
+#endif /* LINUX_IS_24 */
+	case BIODASDRSID:{
+			rc = copy_to_user ((void *) data,
+					   &(device->devinfo.sid_data),
+					   sizeof (senseid_t));
+			break;
+		}
+	case BIODASDRWTB:{
+			int offset = 0;
+			int xlt;
 
-	return rc;
-}
-
-static int
-register_major (int major)
-{
-	int rc = 0;
-
-	FUNCTION_ENTRY ("register_major");
-	rc = register_blkdev (major, DASD_NAME, &dasd_device_operations);
-#if DASD_PARANOIA > 1
-	if (rc) {
-		PRINT_WARN ("registering major -> rc=%d aborting... \n", rc);
-		return rc;
+			rc = copy_from_user (&xlt, (void *) data,
+					     sizeof (int));
+			if (rc)
+				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));
+			break;
+		}
+	case BIODASDFORMAT:{
+			/* fdata == NULL is a valid arg to dasd_format ! */
+			int partn;
+			format_data_t *fdata = NULL;
+			if (data) {
+				fdata = kmalloc (sizeof (format_data_t),
+						 GFP_ATOMIC);
+				if (!fdata) {
+					rc = -ENOMEM;
+					break;
+				}
+				rc = copy_from_user (fdata, (void *) data,
+						     sizeof (format_data_t));
+				if (rc)
+					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));
+				return -EINVAL;
+			}
+			rc = dasd_format (device, fdata);
+			if (fdata) {
+				kfree (fdata);
+			}
+			break;
+		}
+	case BIODASDEXCP:{
+			printk (KERN_WARNING PRINTK_HEADER
+				"Unsupported ioctl BIODASDEXCP\n");
+                        break;
+		}
+        default:{
+          printk (KERN_WARNING PRINTK_HEADER
+                  "unknown ioctl 0x%08x %s'0x%x'%d(%d) on /dev/%s (%d:%d,"
+                  " devno 0x%04X on irq %d) with 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),
+                  device->name, MAJOR (inp->i_rdev), MINOR (inp->i_rdev),
+                  device->devinfo.devno, device->devinfo.irq,
+                  data);
+          rc = -EINVAL;
+          break;
+		}
 	}
-#endif				/* DASD_PARANOIA */
-	blk_dev[major].request_fn = do_dasd_request;
-	FUNCTION_CONTROL ("successfully registered major: %d\n", major);
-	FUNCTION_EXIT ("register_major");
 	return rc;
 }
 
-/* 
-   Below you find functions which are called from outside. Some of them may be
-   static, because they are called by their function pointers only. Thus static
-   modifier is to make sure, that they are only called via the kernel's methods
- */
+/* SECTION: The members of the struct file_operations */
 
 static int
 dasd_ioctl (struct inode *inp, struct file *filp,
 	    unsigned int no, unsigned long data)
-{
+	{
 	int rc = 0;
-	FUNCTION_ENTRY ("dasd_ioctl");
 	if ((!inp) || !(inp->i_rdev)) {
 		return -EINVAL;
-	}
+		}
 	rc = do_dasd_ioctl (inp, no, data);
-	FUNCTION_EXIT ("dasd_ioctl");
 	return rc;
 }
 
@@ -1346,31 +1688,45 @@
 dasd_open (struct inode *inp, struct file *filp)
 {
 	int rc = 0;
-	dasd_information_t *dev;
-	FUNCTION_ENTRY ("dasd_open");
+	int devindex;
+	int partn;
+	dasd_device_t *device;
+	major_info_t *major_info;
+
 	if ((!inp) || !(inp->i_rdev)) {
 		return -EINVAL;
 	}
-	dev = dasd_info[DEVICE_NR (inp->i_rdev)];
-	if (!dev) {
-		PRINT_DEBUG ("No device registered as %d (%d)\n",
-			    inp->i_rdev, DEVICE_NR (inp->i_rdev));
+	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;
+	}
+	devindex = devindex_from_kdev_t (inp->i_rdev);
+	if (devindex < 0) {
+		printk (KERN_WARNING PRINTK_HEADER
+			"No device registered as %d\n", inp->i_rdev);
+		return devindex;
+	}
+	device = find_dasd_device (devindex);
+	major_info = major_info_from_devindex (devindex);
+	partn = MINOR (inp->i_rdev) & ((1 << major_info->gendisk.minor_shift) - 1);
+	if (device == NULL) {
+		printk (KERN_WARNING PRINTK_HEADER
+			"No device registered as %d\n", inp->i_rdev);
+		return -EINVAL;
+	}
+	if (atomic_read (&device->level) < DASD_DEVICE_LEVEL_RECOGNIZED ||
+	    device->discipline == NULL) {
+		printk (KERN_WARNING PRINTK_HEADER
+			" devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+			" Cannot open unrecognized device\n",
+		     device->devinfo.devno, device->devinfo.irq, device->name,
+			MAJOR (inp->i_rdev), MINOR (inp->i_rdev));
 		return -EINVAL;
 	}
-	down (&dev->sem);
-	up (&dev->sem);
 #ifdef MODULE
 	MOD_INC_USE_COUNT;
 #endif				/* MODULE */
-#if DASD_PARANOIA > 2
-	if (dev->open_count < 0) {
-		INTERNAL_ERROR ("open count cannot be less than 0: %d",
-				dev->open_count);
-		return -EINVAL;
-	}
-#endif				/* DASD_PARANOIA */
-	dev->open_count++;
-	FUNCTION_EXIT ("dasd_open");
+	device->open_count++;
 	return rc;
 }
 
@@ -1378,37 +1734,38 @@
 dasd_release (struct inode *inp, struct file *filp)
 {
 	int rc = 0;
-	dasd_information_t *dev;
-	FUNCTION_ENTRY ("dasd_release");
+	dasd_device_t *device;
+	int devindex;
+
 	if ((!inp) || !(inp->i_rdev)) {
 		return -EINVAL;
 	}
-	dev = dasd_info[DEVICE_NR (inp->i_rdev)];
-	if (!dev) {
-		PRINT_WARN ("No device registered as %d\n", inp->i_rdev);
+	devindex = devindex_from_kdev_t (inp->i_rdev);
+	device = find_dasd_device (devindex);
+	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;
 	}
+	if(device->open_count--) {
 #ifdef MODULE
 	MOD_DEC_USE_COUNT;
 #endif				/* MODULE */
-#if DASD_PARANOIA > 2
-	if (!dev->open_count) {
-		PRINT_WARN ("device %d has not been opened before:\n",
-			    inp->i_rdev);
-	}
-#endif				/* DASD_PARANOIA */
-	dev->open_count--;
-#if DASD_PARANOIA > 2
-	if (dev->open_count < 0) {
-		INTERNAL_ERROR ("open count cannot be less than 0: %d",
-				dev->open_count);
-		return -EINVAL;
 	}
-#endif				/* DASD_PARANOIA */
-	FUNCTION_EXIT ("dasd_release");
 	return rc;
 }
 
+/* SECTION: All that stuff related to major numbers */
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+static struct
+block_device_operations dasd_device_operations =
+{
+	ioctl:dasd_ioctl,
+	open:dasd_open,
+	release:dasd_release,
+};
+#else
 static struct
 file_operations dasd_device_operations =
 {
@@ -1419,156 +1776,894 @@
 	open:dasd_open,
 	release:dasd_release,
 };
+#endif /* LINUX_IS_24 */
 
-int
-dasd_init (void)
+static major_info_t *
+get_new_major_info (void)
 {
+	major_info_t *major_info = NULL;
+	major_info = kmalloc (sizeof (major_info_t), GFP_KERNEL);
+	if (major_info) {
+                major_info_t *temp = dasd_major_info;
+                while (temp->next)
+                        temp = temp->next;
+		temp->next = major_info;
+                
+		memset (major_info, 0, sizeof (major_info_t));
+		major_info->read_ahead = 8;
+		major_info->request_fn = do_dasd_request;
+                
+		major_info->gendisk.major_name = DASD_NAME;
+		major_info->gendisk.minor_shift = DASD_PARTN_BITS;
+		major_info->gendisk.max_p = 1 << DASD_PARTN_BITS;
+#if !(LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+		major_info->gendisk.max_nr= 1 << DASD_PARTN_BITS;
+#endif /* LINUX_IS_24 */
+		major_info->gendisk.nr_real=DASD_PER_MAJOR;
+	}
+	return major_info;
+}
+
+static int
+dasd_register_major (major_info_t * major_info)
+{
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+        request_queue_t *q;
+#endif /* LINUX_IS_24 */
 	int rc = 0;
-	int i;
+	int major;
 
-	PRINT_INFO ("initializing...\n");
-	atomic_set (&bh_scheduled, 0);
-	spin_lock_init (&dasd_lock);
-#ifdef CONFIG_DASD_MDSK
-	/*
-	 * enable service-signal external interruptions,
-	 * Control Register 0 bit 22 := 1
-	 * (besides PSW bit 7 must be set to 1 somewhere for external
-	 * interruptions)
-	 */
-	ctl_set_bit (0, 9);
-	register_external_interrupt (0x2603, do_dasd_mdsk_interrupt);
-#endif
-	dasd_debug_info = debug_register ("dasd", 1, 4);
-	/* First register to the major number */
+	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;
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+	rc = devfs_register_blkdev (major, DASD_NAME, &dasd_device_operations);
+#else
+	rc = register_blkdev (major, DASD_NAME, &dasd_device_operations);
+#endif /* LINUX_IS_24 */
+	if (rc < 0) {
+		printk (KERN_WARNING PRINTK_HEADER
+		      "Cannot register to major no %d, rc = %d\n", major, rc);
+	return rc;
+	} else if (rc > 0) {
+		if (major == 0) {
+			major = rc;
+			rc = 0;
+		} else {
+			printk (KERN_DEBUG PRINTK_HEADER
+			    "Unknown condition when registering major number."
+			  "Please report this line to Linux390@de.ibm.com\n");
+		}
+	} else {
+		if (major == 0) {
+			printk (KERN_DEBUG PRINTK_HEADER
+				"Unknown condition when registering to dynamic major number."
+			  "Please report this line to Linux390@de.ibm.com\n");
 
-	rc = register_major (MAJOR_NR);
-#if DASD_PARANOIA > 1
-	if (rc) {
-		PRINT_WARN ("registering major_nr returned rc=%d\n", rc);
+		}
+	}
+        major_info->dasd_device = (dasd_device_t **) kmalloc( DASD_PER_MAJOR * sizeof(dasd_device_t*), 
+                                                              GFP_ATOMIC);
+        memset ( major_info->dasd_device ,0,DASD_PER_MAJOR * sizeof(dasd_device_t*));
+        blk_size[major] = major_info->blk_size = 
+                (int *) kmalloc( (1<<MINORBITS) * sizeof(int), GFP_ATOMIC);
+        memset ( major_info->blk_size ,0,(1<<MINORBITS) * sizeof(int));
+        blksize_size[major] = major_info->blksize_size = 
+                (int *) kmalloc( (1<<MINORBITS) * sizeof(int), GFP_ATOMIC);
+        memset ( major_info->blksize_size ,0,(1<<MINORBITS) * sizeof(int));
+        hardsect_size[major] = major_info->hardsect_size = 
+                (int *) kmalloc( (1<<MINORBITS) * sizeof(int), GFP_ATOMIC);
+        memset ( major_info->hardsect_size ,0,(1<<MINORBITS) * sizeof(int));
+        max_sectors[major] = major_info->max_sectors = 
+                (int *) kmalloc( (1<<MINORBITS) * sizeof(int), GFP_ATOMIC);
+        memset ( major_info->max_sectors ,0,(1<<MINORBITS) * sizeof(int));
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+        q = BLK_DEFAULT_QUEUE (major);
+        blk_init_queue (q, do_dasd_request);
+        blk_queue_headactive (BLK_DEFAULT_QUEUE (major), 0);
+#else
+	blk_dev[major].request_fn = major_info->request_fn;
+#endif /* LINUX_IS_24 */
+
+        
+	/* finally do the gendisk stuff */
+        major_info->gendisk.part = kmalloc ((1 << MINORBITS) * 
+                                            sizeof (struct hd_struct),
+                                            GFP_ATOMIC);
+        memset (major_info->gendisk.part,0,(1 << MINORBITS) * 
+                sizeof (struct hd_struct));
+	major_info->gendisk.major = major;
+	major_info->gendisk.next = gendisk_head;
+        major_info->gendisk.sizes = major_info->blk_size;
+	gendisk_head = &major_info->gendisk;
+	return major;
+}
+
+static int
+dasd_unregister_major (major_info_t * major_info)
+{
+	int rc = 0;
+	int major;
+        struct gendisk *dd,*prev=NULL;
+
+	if (major_info == NULL) {
+                return -EINVAL;
+	}
+	major = major_info->gendisk.major;
+	rc = unregister_blkdev (major, DASD_NAME);
+	if (rc < 0) {
+		printk (KERN_WARNING PRINTK_HEADER
+                        "Cannot unregister from major no %d, rc = %d\n", major, rc);
 		return rc;
+	} 
+#if !(LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+        blk_dev[major].request_fn = NULL;
+#endif /* LINUX_IS_24 */
+        blk_size[major]= NULL;
+        blksize_size[major]= NULL;
+        hardsect_size[major]= NULL;
+        max_sectors[major]= NULL;
+
+        kfree(major_info->dasd_device);
+        kfree(major_info->blk_size);
+        kfree(major_info->blksize_size);
+        kfree(major_info->hardsect_size);
+        kfree(major_info->max_sectors);
+        kfree(major_info->gendisk.part);
+        
+	/* finally do the gendisk stuff */
+        for (dd = gendisk_head; dd ; dd = dd -> next ) {
+                if ( dd == &major_info->gendisk ) {
+                        if ( prev )
+                                prev->next = dd->next;
+                        else 
+                                gendisk_head = dd->next;
+                        break;
+                }
+                prev = dd;
+        }
+        if ( dd == NULL ) {
+                return -ENOENT;
+        }
+        if  ( major_info->gendisk.major > 128 )
+                kfree(major_info);
+	return rc;
+}
+/* SECTION: Management of device list */
+
+/* This one is needed for naming 18000+ possible dasd devices */
+int
+dasd_device_name (char *str, int index, int partition, struct gendisk *hd)
+{
+	int len = 0;
+        char first,second,third;
+
+        if ( hd ) {
+                index = devindex_from_kdev_t (MKDEV(hd->major,index<<hd->minor_shift));
+}
+        third = index % 26;
+        second = (index / 26) % 27;
+	first = ((index / 26) / 27) % 27;
+
+	len = sprintf (str, "dasd");
+	if (first) {
+                len += sprintf (str + len, "%c", first + 'a' - 1 );
 	}
-#endif	/* DASD_PARANOIA */ read_ahead[MAJOR_NR] = 8;
-        blk_size[MAJOR_NR] = dasd_blks;
-	hardsect_size[MAJOR_NR] = dasd_secsize;
-	blksize_size[MAJOR_NR] = dasd_blksize;
-	max_sectors[MAJOR_NR] = dasd_maxsecs;
-#ifdef CONFIG_PROC_FS
-	dasd_proc_init ();
-#endif				/* CONFIG_PROC_FS */
-	/* Now scan the device list for DASDs */
-	for (i = 0; i < NR_IRQS; i++) {
-		int irc;	/* Internal return code */
-		irc = probe_for_dasd (i);
-		switch (irc) {
-		case 0:
-			break;
-		case -ENODEV:
-		case -EBUSY:
-			break;
-		case -EMEDIUMTYPE:
-			PRINT_WARN ("DASD not formatted%s\n", "");
+	if (second) {
+                len += sprintf (str + len, "%c", second + 'a' - 1 );
+	}
+        len += sprintf (str + len, "%c", third + 'a' );
+	if (partition) {
+		if (partition > 9) {
+			return -EINVAL;
+		} else {
+			len += sprintf (str + len, "%d", partition);
+	}
+	}
+	str[len] = '\0';
+	return 0;
+}
+
+static void
+dasd_not_oper_handler ( int irq, int status ) {
+        int devno,devindex;
+        dasd_device_t *device;
+        devno = get_devno_by_irq(irq);
+        if ( devno < 0 ) {
+		printk (KERN_WARNING PRINTK_HEADER
+                         "not_oper_handler called on irq %d no devno!\n", irq);
+                return;
+	}
+        printk ( KERN_INFO PRINTK_HEADER
+                 "not_oper_handler called on irq %d devno %04X\n", irq,devno);
+        devindex = devindex_from_devno(devno);
+        device = find_dasd_device(devindex);
+        dasd_set_device_level( irq, DASD_DEVICE_LEVEL_UNKNOWN, NULL, 0 );
+	}
+
+static int
+dasd_enable_single_volume ( int irq ) {
+        int rc = 0;
+        dasd_set_device_level (irq, DASD_DEVICE_LEVEL_ANALYSIS_PENDING,
+                               NULL, 0);
+	printk (KERN_INFO PRINTK_HEADER "waiting for response...\n");
+        {
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+                static wait_queue_head_t wait_queue;
+                init_waitqueue_head(&wait_queue);
+#else
+                static struct wait_queue *wait_queue = NULL;
+#endif /* LINUX_IS_24 */
+                interruptible_sleep_on_timeout (&wait_queue, (5 * HZ) >> 1 );
+        }
+        dasd_set_device_level (irq, DASD_DEVICE_LEVEL_ANALYSED,
+                               NULL, 0);
+	return rc;
+}
+
+static int
+dasd_oper_handler ( int irq, devreg_t *devreg ) {
+        int devno;
+        int devindex;
+	int rc;
+        devno = get_devno_by_irq(irq);
+        if ( devno == -ENODEV )
+                return -ENODEV;
+        do {
+                devindex = devindex_from_devno(devno);
+                if ( dasd_autodetect ) {
+                        dasd_add_range(devno,0);
+                } else {
+		return -ENODEV;
+	}
+        } while ( devindex == -ENODEV );
+        rc = dasd_enable_single_volume(irq);
+		return rc;
+	}
+
+/* 
+ * int
+ * dasd_set_device_level (unsigned int irq, int desired_level,
+ *                        dasd_discipline_t * discipline, int flags)
+ */
+
+static int
+dasd_set_device_level (unsigned int irq, int desired_level,
+		       dasd_discipline_t * discipline, int flags)
+{
+	int rc = 0;
+	int devno;
+	int devindex;
+	dasd_device_t *device;
+	int current_level;
+	major_info_t *major_info = NULL;
+	int i, minor;
+	ccw_req_t *cqr = NULL;
+        int ind;
+        dasd_discipline_t *temp;
+        struct gendisk *dd;
+
+	devno = get_devno_by_irq (irq);
+	if (devno < 0) {
+		printk (KERN_WARNING PRINTK_HEADER " no device appears to be connected to SCH %d\n", irq);
+		return -ENODEV;
+	}
+	devindex = devindex_from_devno (devno);
+	if (devindex < 0) {
+		printk (KERN_WARNING PRINTK_HEADER " device %d is not in list of known DASDs\n", irq);
+			return -ENODEV;
+		}
+	device = find_dasd_device (devindex);
+        while ( (major_info = major_info_from_devindex (devindex)) == NULL ) {
+                if ((rc = dasd_register_major (major_info)) > 0) {
+                        printk (KERN_INFO PRINTK_HEADER
+                                "Registered successfully to another major number: %u\n", rc);
+                } else {
+                        printk (KERN_WARNING PRINTK_HEADER
+                                "Couldn't register successfully to another major no\n");
+                        return -ERANGE;
+		}
+		}
+        ind = devindex & (DASD_PER_MAJOR-1);
+        device = major_info->dasd_device[ind];
+        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));
+                major_info->dasd_device[ind] = device;
+                dasd_device_name (device->name, devindex, 0,NULL);
+	}
+        device->kdev = MKDEV(major_info->gendisk.major,ind << DASD_PARTN_BITS);
+        device->major_info = major_info;
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+        init_waitqueue_head(&device->wait_q);
+#endif /* KERNEL_VERSION */
+        minor = MINOR(device->kdev);
+	current_level = atomic_read (&device->level);
+	if (desired_level > current_level) {
+		switch (current_level) {
+		case DASD_DEVICE_LEVEL_UNKNOWN:	/* Find a discipline */
+			rc = get_dev_info_by_irq (irq, &device->devinfo);
+			if (rc < 0) {
+				break;
+			}
+                        for ( temp = dasd_disciplines; temp != NULL; temp = temp->next ) {
+                                if ( discipline == NULL || temp == discipline ) {
+                                        if (temp->id_check)
+                                                if (temp->id_check (&device->devinfo))
+                                                        continue;
+                                        if (temp->check_characteristics) {
+                                                if (temp->check_characteristics (device)) 
+                                                        continue;
+	}
+                                        discipline = temp;
+                                        break;
+                                }
+                        }
+			if (discipline && !rc) {
+				printk (KERN_INFO PRINTK_HEADER
+					" devno 0x%04X on subchannel %d (%s) is /dev/%s (%d:%d)\n",
+					devno, irq, discipline->name,
+                                        device->name, major_from_devindex (devindex),
+					(devindex % 64) << DASD_PARTN_BITS);
+		} else {
+				break;
+		}
+			device->discipline = discipline;
+			if (device->discipline->int_handler) {
+                                s390_request_irq_special(irq, 
+                                                         device->discipline->int_handler, 
+                                                         dasd_not_oper_handler,
+                                                         0, 
+                                                         DASD_NAME, 
+                                                         &device->dev_status);
+			}
+			atomic_compare_and_swap_debug (&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);
+                                                atomic_compare_and_swap_debug (&device->level,
+                                                                               DASD_DEVICE_LEVEL_RECOGNIZED,
+                                                                               DASD_DEVICE_LEVEL_ANALYSIS_PENDING);
+                                                s390irq_spin_unlock_irqrestore (irq, flags);
+}
+                                }
+                        } else {
+                                atomic_compare_and_swap_debug (& 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;
+                        }
+			}
+			for (i = 0; i < (1 << DASD_PARTN_BITS); i++) {
+                                if (i == 0)
+					major_info->blk_size[minor] = (device->sizes.blocks << device->sizes.s2b_shift) >> 1;
+				else
+					major_info->blk_size[minor + i] = 0;
+				major_info->hardsect_size[minor + i] = device->sizes.bp_block;
+				major_info->blksize_size[minor + i] = device->sizes.bp_block;
+                                if (major_info->blksize_size[minor + i] < 1024 )
+                                        major_info->blksize_size[minor + i] = 1024;
+                                
+				major_info->max_sectors[minor + i] = 200 << device->sizes.s2b_shift;	/* FIXME !!! */
+			}
+			atomic_compare_and_swap_debug (&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;
+#if !(LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+#ifndef MODULE
+                        if ( flags & 0x80 )
+#endif
+#endif /* KERNEL_VERSION */
+                                dasd_partn_detect(devindex);
+			if (desired_level == DASD_DEVICE_LEVEL_ANALYSED)
+				break;
+		case DASD_DEVICE_LEVEL_ANALYSED:	/* Fallthrough ?? */
+
 			break;
 		default:
-			INTERNAL_CHECK ("probe_for_dasd: unknown rc=%d", irc);
+			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_PARTITIONED:	/* Fallthrough ?? */
+                        /* delete the partition information */
+			for (i = 0; i < (1 << DASD_PARTN_BITS); i++) {
+                                struct hd_struct *p = &major_info->gendisk.part[minor+i];
+                                p->start_sect = 0;
+                                p->nr_sects = 0;
+                                p->type = 0;
+                        }
+			atomic_compare_and_swap_debug (&device->level,
+                                                       DASD_DEVICE_LEVEL_PARTITIONED,
+                                                       DASD_DEVICE_LEVEL_ANALYSED);
+			if (desired_level == DASD_DEVICE_LEVEL_ANALYSED)
+				break;
+		case DASD_DEVICE_LEVEL_ANALYSED:	/* Fallthrough ?? */
+			atomic_compare_and_swap_debug (&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:
+			for (i = 0; i < (1 << DASD_PARTN_BITS); i++) {
+                                major_info->blk_size[minor] = 0;
+                                major_info->hardsect_size[minor + i] = 0;
+                                major_info->blksize_size[minor + i] = 0;
+                                major_info->max_sectors[minor + i] = 0;
+			}
+                        memset( &device->sizes,0,sizeof(dasd_sizes_t));
+                        atomic_compare_and_swap_debug (&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 ?? */
+			atomic_compare_and_swap_debug (&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;
+			atomic_compare_and_swap_debug (&device->level,
+                                                       DASD_DEVICE_LEVEL_RECOGNIZED,
+                                                       DASD_DEVICE_LEVEL_UNKNOWN);
+			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;
 		}
 	}
-	FUNCTION_CONTROL ("detection loop completed %s partn check...\n", "");
-/* Finally do the genhd stuff */
-	dd_gendisk.next = gendisk_head;
-	gendisk_head = &dd_gendisk;
-	dasd_information = dasd_info;	/* to enable genhd to know about DASD */
-        tod_wait (1000000);
-
-	/* wait on root filesystem before detecting partitions */
-	if (MAJOR (ROOT_DEV) == DASD_MAJOR) {
-		int count = 10;
-		i = DEVICE_NR (ROOT_DEV);
-		if (dasd_info[i] == NULL) {
-			panic ("root device not accessible\n");
-		}
-                while ((atomic_read (&dasd_info[i]->status) !=
-                        DASD_INFO_STATUS_FORMATTED) &&
-                       count ) {
-                        PRINT_INFO ("Waiting on root volume...%d seconds left\n", count);
-			tod_wait (1000000);
-			count--;
-		}
-		if (count == 0) {
-			panic ("Waiting on root volume...giving up!\n");
-		}
-	}
-	for (i = 0; i < DASD_MAX_DEVICES; i++) {
-		if (dasd_info[i]) {
-			if (atomic_read (&dasd_info[i]->status) ==
-			    DASD_INFO_STATUS_FORMATTED) {
-				dasd_partn_detect (i);
-			} else {	/* start kernel thread for devices not ready now */
-				kernel_thread (dasd_partn_detect, (void *) i,
-                                               CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+	if (rc) {
+		goto exit;
+	}
+      nomem:
+	rc = -ENOMEM;
+      exit:
+	return 0;
+}
+
+
+/* SECTION: Procfs stuff */
+typedef struct {
+        char *data;
+        int len;
+} tempinfo_t;
+
+#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
+};
+#endif /* KERNEL_VERSION */
+static struct proc_dir_entry* dasd_devices_entry;
+
+
+static int
+dasd_devices_open (struct inode* inode, struct file*  file )
+{
+	int rc = 0;
+	int size = 0;
+	int len = 0;
+	major_info_t * temp = dasd_major_info;
+        tempinfo_t *info;
+
+	info = (tempinfo_t *)vmalloc(sizeof(tempinfo_t)); 
+        if( info == NULL ) {
+                printk ( KERN_WARNING "No memory available for data\n");
+                return -ENOMEM;
+	} else {
+                file->private_data = (void *)info;
+	}
+        while ( temp ) {
+                int i;
+                for ( i = 0; i < 1 << (MINORBITS - DASD_PARTN_BITS); i ++ ) {
+                        dasd_device_t *device = temp->dasd_device[i];
+                        if ( device ) {
+				size+=128;
+			}
+		}
+		temp = temp->next;
+	}
+	temp = dasd_major_info;
+	info->data=(char*)vmalloc(size); /* FIXME! determine space needed in a better way */
+        if( size && info->data == NULL ) {
+		printk ( KERN_WARNING "No memory available for data\n");
+                vfree ( info );
+		return -ENOMEM;
+	}
+	while ( temp ) {
+		int i;
+		for ( i = 0; i < 1 << (MINORBITS - DASD_PARTN_BITS); i ++ ) {
+			dasd_device_t *device = temp->dasd_device[i];
+			if ( device ) {
+				len += sprintf ( info->data + len,
+						 "%04X(%s) at (%d:%d) is %7s:",
+						 device->devinfo.devno,
+						 device->discipline ? device->discipline->name : "none",
+						 temp->gendisk.major,i<<DASD_PARTN_BITS,
+						 device->name);
+                                switch ( atomic_read(&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, %d blocks, %d MB\n",
+                                                         device->sizes.bp_block,
+                                                         device->sizes.blocks,
+                                                         ((device->sizes.bp_block>>9)*device->sizes.blocks)>>11);
+                                        break;
+                                case DASD_DEVICE_LEVEL_ANALYSIS_PENDING:
+                                        len += sprintf ( info->data + len,"busy   \n");
+                                        break;
+				case DASD_DEVICE_LEVEL_ANALYSIS_PREPARED:
+					len += sprintf ( info->data + len,"n/f    \n");
+					break;
+                                case DASD_DEVICE_LEVEL_ANALYSED:
+                                        len += sprintf ( info->data + len,"active ");
+                                        len += sprintf ( info->data + len," at blocksize: %d, %d blocks, %d MB\n",
+                                                         device->sizes.bp_block,
+                                                         device->sizes.blocks,
+                                                         ((device->sizes.bp_block>>9)*device->sizes.blocks)>>11);
+                                        break;
+                                default:
+                                        len += sprintf ( info->data + len,"no stat\n");
+                                        break;
+                                }
 			}
 		}
+		temp = temp->next;
 	}
+        info->len=len;
 	return rc;
 }
 
-#ifdef MODULE
+#define MIN(a,b) ((a)<(b)?(a):(b))
 
-int
-init_module (void)
+static ssize_t 
+dasd_devices_read (  struct  file *file, char*   user_buf,  size_t  user_len, loff_t* offset )
 {
-	int rc = 0;
+        loff_t len;
+        tempinfo_t* p_info = (tempinfo_t*)file->private_data;
 
-	PRINT_INFO ("Initializing module\n");
-	rc = dasd_parse_module_params ();
-	if (rc == 0) {
-		PRINT_INFO ("module parameters parsed successfully\n");
-	} else {
-		PRINT_WARN ("parsing parameters returned rc=%d\n", rc);
+        if(*offset >= p_info->len)
+        {
+                return 0; /* EOF */
+	}
+        else
+        {
+                len = MIN(user_len, (p_info->len - *offset));
+                copy_to_user(user_buf, &(p_info->data[*offset]), len);
+                (*offset) += len;
+                return len;  /* number of bytes "read" */
 	}
-	rc = dasd_init ();
-	if (rc == 0) {
-		PRINT_INFO ("module initialized successfully\n");
-	} else {
-		PRINT_WARN ("initializing module returned rc=%d\n", rc);
+}
+
+static int
+dasd_devices_close (struct inode* inode, struct file*  file)
+{
+	int rc = 0;
+        tempinfo_t* p_info = (tempinfo_t*)file->private_data;
+        if ( p_info ) {
+                if ( p_info->data ) vfree(p_info->data);
+                vfree(p_info);
 	}
-	FUNCTION_EXIT ("init_module");
 	return rc;
 }
 
+
+static struct file_operations dasd_devices_file_ops =
+{
+  NULL,          /* lseek */
+  dasd_devices_read,  /* read */
+  NULL, /* dasd_devices_write, */   /* write */
+  NULL,          /* readdir */
+  NULL,          /* select */
+  NULL,          /* ioctl */
+  NULL,          /* mmap */
+  dasd_devices_open,    /* open */
+  NULL,          /* flush */
+  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 */
+};
+
+void
+dasd_proc_init (void)
+{
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+        dasd_proc_root_entry = create_proc_entry("dasd", 
+                                                 S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR,
+                                                 &proc_root);
+        dasd_devices_entry = create_proc_entry("devices", 
+                                               S_IFREG | S_IRUGO | S_IWUSR,
+                                               dasd_proc_root_entry);
+#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);
+        }
+#endif /* LINUX_IS_24 */
+}
+
 void
-cleanup_module (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("dasd", &proc_root);
+#else
+        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 */
+}
+
+/* SECTION: Initializing the driver */
+
+int
+dasd_init (void)
 {
 	int rc = 0;
-	struct gendisk *genhd = gendisk_head, *prev = NULL;
+	int irq;
+	int j;
+	major_info_t *major_info;
+        dasd_range_t *range;
+
+	printk (KERN_INFO PRINTK_HEADER "initializing...\n");
+        genhd_dasd_name = dasd_device_name;
+#ifndef MODULE
+        dasd_split_parm_string(dasd_parm_string);
+#endif /* ! MODULE */
+        dasd_parse(dasd);
+
+        dasd_proc_init();
+        dasd_init_emergency_req();
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+        init_waitqueue_head(&watcher_queue);
+        spin_lock_init(&cq_lock);
+#endif /* LINUX_IS_24 */
+
+	for (major_info = dasd_major_info; major_info; major_info = major_info->next) {
+		if ((rc = dasd_register_major (major_info)) > 0) {
+			printk (KERN_INFO PRINTK_HEADER
+				"Registered successfully to major no %u\n", major_info->gendisk.major);
+		} else {
+			printk (KERN_WARNING PRINTK_HEADER
+				"Couldn't register successfully to major no %d\n", major_info->gendisk.major);
+	}
+		}
+        
+#ifdef CONFIG_DASD_ECKD
+	rc = dasd_eckd_init ();
+	if (rc==0)  {
+		printk (KERN_INFO PRINTK_HEADER
+			"Registered ECKD discipline successfully\n");
+	}
+#endif				/* CONFIG_DASD_ECKD */
+#ifdef CONFIG_DASD_FBA
+	rc = dasd_fba_init ();
+	if (rc == 0) {
+		printk (KERN_INFO PRINTK_HEADER
+			"Registered FBA discipline successfully\n");
+	}
+#endif				/* CONFIG_DASD_FBA */
+#ifdef CONFIG_DASD_MDSK
+	rc = dasd_diag_init ();
+	if (rc == 0)  {
+		printk (KERN_INFO PRINTK_HEADER
+			"Registered MDSK discipline successfully\n");
+	}
+#endif				/* CONFIG_DASD_MDSK */
+	rc = 0;
+	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_set_device_level (irq, DASD_DEVICE_LEVEL_ANALYSIS_PENDING,
+						       NULL, 0);
+	}
+			}
+        if ( dasd_autodetect ) {
+                for ( irq = get_irq_first(); irq != -ENODEV; irq = get_irq_next(irq) ) {
+                        int devno = get_devno_by_irq(irq);
+                        int index = devindex_from_devno(devno);
+                        if ( index == -ENODEV ) { /* not included in ranges */
+                                dasd_add_range (devno,0);
+				dasd_set_device_level (irq, DASD_DEVICE_LEVEL_ANALYSIS_PENDING,
+						       NULL, 0);
+		}
+	}
+}
+	printk (KERN_INFO PRINTK_HEADER "waiting for responses...\n");
+{
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+                static wait_queue_head_t wait_queue;
+                init_waitqueue_head(&wait_queue);
+#else
+                static struct wait_queue *wait_queue = NULL;
+#endif /* LINUX_IS_24 */
+                interruptible_sleep_on_timeout (&wait_queue, (5 * HZ) >> 1 );
+	}
+	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_set_device_level (irq, DASD_DEVICE_LEVEL_ANALYSED,
+						       NULL, 0);
+	}
+		}
+	}
 
-	PRINT_INFO ("trying to unload module \n");
+	printk (KERN_INFO PRINTK_HEADER "initialization completed\n");
+	return rc;
+}
 
-	/* unregister gendisk stuff */
-	for (genhd = gendisk_head; genhd; prev = genhd, genhd = genhd->next) {
-		if (genhd == dd_gendisk) {
-			if (prev)
-				prev->next = genhd->next;
-			else {
-				gendisk_head = genhd->next;
+void
+cleanup_dasd (void)
+{
+        int j,rc=0;
+        int irq;
+        major_info_t *major_info;
+        dasd_range_t *range,*next;
+        dasd_devreg_t *reg;
+
+	printk (KERN_INFO PRINTK_HEADER "shutting down\n");
+
+        dasd_proc_cleanup();
+
+	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_set_device_level (irq, DASD_DEVICE_LEVEL_UNKNOWN,
+						       NULL, 0);
+                                kfree(find_dasd_device(devindex_from_devno(j)));
 			}
-			break;
 		}
 	}
-	/* unregister devices */
-	for (i = 0; i = DASD_MAX_DEVICES; i++) {
-		if (dasd_info[i])
-			dasd_unregister_dasd (i);
+	for (major_info = dasd_major_info; major_info; major_info = major_info->next) {
+		if ((rc = dasd_unregister_major (major_info)) == 0) {
+			printk (KERN_INFO PRINTK_HEADER
+				"Unregistered successfully from major no %u\n", major_info->gendisk.major);
+		} else {
+			printk (KERN_WARNING PRINTK_HEADER
+				"Couldn't unregister successfully from major no %d rc = %d\n", major_info->gendisk.major,rc);
+		}
+	}
+        dasd_cleanup_emergency_req();
+        
+        range = dasd_range_head;
+        while ( range ) {
+                next = range -> next;
+                kfree (range);
+                if ( next == NULL )
+			break;
+                else
+                        range = next;
+		}
+        dasd_range_head = NULL;
+
+        while ( dasd_devreg_head ) {
+                reg = dasd_devreg_head->next;
+                kfree ( dasd_devreg_head );
+                dasd_devreg_head = reg;
+	}
+	printk (KERN_INFO PRINTK_HEADER "shutdown completed\n");
 	}
 
-	if (rc == 0) {
-		PRINT_INFO ("module unloaded successfully\n");
-	} else {
-		PRINT_WARN ("module unloaded with errors\n");
+#ifdef MODULE
+int
+init_module ( void )
+{
+        int rc=0;
+        return dasd_init(); 
+        return rc;
 	}
+
+void 
+cleanup_module ( void ) 
+{
+        cleanup_dasd();
+        return;
 }
-#endif				/* MODULE */
+#endif
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.

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