!C99Shell v. 1.0 pre-release build #13!

Software: Apache/2.0.54 (Unix) mod_perl/1.99_09 Perl/v5.8.0 mod_ssl/2.0.54 OpenSSL/0.9.7l DAV/2 FrontPage/5.0.2.2635 PHP/4.4.0 mod_gzip/2.0.26.1a 

uname -a: Linux snow.he.net 4.4.276-v2-mono-1 #1 SMP Wed Jul 21 11:21:17 PDT 2021 i686 

uid=99(nobody) gid=98(nobody) groups=98(nobody) 

Safe-mode: OFF (not secure)

/usr/src/linux-2.4.18-xfs-1.1/drivers/s390/block/   drwxr-xr-x
Free 318.32 GB of 458.09 GB (69.49%)
Home    Back    Forward    UPDIR    Refresh    Search    Buffer    Encoder    Tools    Proc.    FTP brute    Sec.    SQL    PHP-code    Update    Feedback    Self remove    Logout    


Viewing file:     dasd.c (130.84 KB)      -rw-r--r--
Select action/file-type:
(+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/*
 * File...........: linux/drivers/s390/block/dasd.c
 * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
 *                  Horst Hummel <Horst.Hummel@de.ibm.com> 
 *                  Carsten Otte <Cotte@de.ibm.com>
 * Bugreports.to..: <Linux390@de.ibm.com>
 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
 *
 * History of changes (starts July 2000)
 * 11/09/00 complete redesign after code review
 * 02/01/01 added dynamic registration of ioctls
 *          fixed bug in registration of new majors
 *          fixed handling of request during dasd_end_request
 *          fixed handling of plugged queues
 *          fixed partition handling and HDIO_GETGEO
 *          fixed traditional naming scheme for devices beyond 702
 *          fixed some race conditions related to modules
 *          added devfs suupport
 * 03/06/01 refined dynamic attach/detach for leaving devices which are online.
 * 03/09/01 refined dynamic modifiaction of devices
 * 03/12/01 moved policy in dasd_format to dasdfmt (renamed BIODASDFORMAT)
 * 03/19/01 added BIODASDINFO-ioctl
 *          removed 2.2 compatibility
 * 04/27/01 fixed PL030119COT (dasd_disciplines does not work)
 * 04/30/01 fixed PL030146HSM (module locking with dynamic ioctls)
 *          fixed PL030130SBA (handling of invalid ranges)
 * 05/02/01 fixed PL030145SBA (killing dasdmt)
 *          fixed PL030149SBA (status of 'accepted' devices)
 *          fixed PL030146SBA (BUG in ibm.c after adding device)
 *          added BIODASDPRRD ioctl interface
 * 05/11/01 fixed  PL030164MVE (trap in probeonly mode)
 * 05/15/01 fixed devfs support for unformatted devices
 * 06/26/01 hopefully fixed PL030172SBA,PL030234SBA
 * 07/09/01 fixed PL030324MSH (wrong statistics output)
 * 07/16/01 merged in new fixes for handling low-mem situations
 */

#include <linux/config.h>
#include <linux/version.h>
#include <linux/kmod.h>
#include <linux/init.h>
#include <linux/blkdev.h>
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/tqueue.h>
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/genhd.h>
#include <linux/hdreg.h>
#include <linux/interrupt.h>
#include <linux/ctype.h>
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
#endif                /* CONFIG_PROC_FS */
#include <linux/spinlock.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/blkpg.h>
#include <linux/wait.h>

#include <asm/ccwcache.h>
#include <asm/debug.h>

#include <asm/atomic.h>
#include <asm/delay.h>
#include <asm/io.h>
#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 <asm/idals.h>
#include <asm/dasd.h>

#include "dasd_int.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_DIAG
#include "dasd_diag.h"
#endif                /*  CONFIG_DASD_DIAG */

/* SECTION: exported variables of dasd.c */

debug_info_t *dasd_debug_area;

MODULE_AUTHOR ("Holger Smolinski <Holger.Smolinski@de.ibm.com>");
MODULE_DESCRIPTION ("Linux on S/390 DASD device driver,"
            " Copyright 2000 IBM Corporation");
MODULE_SUPPORTED_DEVICE ("dasd");
MODULE_PARM (dasd, "1-" __MODULE_STRING (256) "s");
MODULE_PARM (dasd_disciplines, "1-" __MODULE_STRING (8) "s");
EXPORT_SYMBOL (dasd_chanq_enq_head);
EXPORT_SYMBOL (dasd_debug_area);
EXPORT_SYMBOL (dasd_chanq_enq);
EXPORT_SYMBOL (dasd_chanq_deq);
EXPORT_SYMBOL (dasd_discipline_add);
EXPORT_SYMBOL (dasd_discipline_del);
EXPORT_SYMBOL (dasd_start_IO);
EXPORT_SYMBOL (dasd_term_IO);
EXPORT_SYMBOL (dasd_schedule_bh);
EXPORT_SYMBOL (dasd_int_handler);
EXPORT_SYMBOL (dasd_oper_handler);
EXPORT_SYMBOL (dasd_alloc_request);
EXPORT_SYMBOL (dasd_free_request);
EXPORT_SYMBOL (dasd_ioctl_no_register);
EXPORT_SYMBOL (dasd_ioctl_no_unregister);
EXPORT_SYMBOL (dasd_default_erp_action);
EXPORT_SYMBOL (dasd_default_erp_postaction);
EXPORT_SYMBOL (dasd_sleep_on_req);
EXPORT_SYMBOL (dasd_set_normalized_cda);
EXPORT_SYMBOL (dasd_device_from_kdev);

/* SECTION: Constant definitions to be used within this file */

#define PRINTK_HEADER DASD_NAME":"
#undef  DASD_PROFILE            /* fill profile information - used for */
                                /* statistics and perfomance           */

#define DASD_MIN_SIZE_FOR_QUEUE 32
#undef CONFIG_DYNAMIC_QUEUE_MIN_SIZE
#define DASD_CHANQ_MAX_SIZE 6

/* SECTION: prototypes for static functions of dasd.c */

static request_fn_proc do_dasd_request;
static int dasd_set_device_level (unsigned int, dasd_discipline_t *, int);
static request_queue_t *dasd_get_queue (kdev_t kdev);
static void cleanup_dasd (void);
static void dasd_plug_device (dasd_device_t * device);
static int dasd_fillgeo (int kdev, struct hd_geometry *geo);
static void dasd_enable_ranges (dasd_range_t *, dasd_discipline_t *, int); 
static void dasd_disable_ranges (dasd_range_t *, dasd_discipline_t *, int, int); 
static void dasd_enable_single_device ( unsigned long);
static inline int dasd_state_init_to_ready(dasd_device_t*);
static inline void dasd_setup_partitions ( dasd_device_t *);
static inline void dasd_destroy_partitions ( dasd_device_t *);
static inline int dasd_setup_blkdev(dasd_device_t*);
static void dasd_deactivate_queue (dasd_device_t *);
static inline int dasd_disable_blkdev(dasd_device_t*);
static void dasd_flush_chanq ( dasd_device_t * device, int destroy ); 
static void dasd_flush_request_queues ( dasd_device_t * device, int destroy );
static struct block_device_operations dasd_device_operations;
static inline dasd_device_t ** dasd_device_from_devno (int);
static void dasd_process_queues (dasd_device_t * device);
/* SECTION: static variables of dasd.c */

static devfs_handle_t dasd_devfs_handle;
static wait_queue_head_t dasd_init_waitq;
static atomic_t dasd_init_pending = ATOMIC_INIT (0);

#ifdef CONFIG_DASD_DYNAMIC

/* SECTION: managing dynamic configuration of dasd_driver */

static struct list_head dasd_devreg_head = LIST_HEAD_INIT (dasd_devreg_head);

/*
 * function: dasd_create_devreg
 * creates a dasd_devreg_t related to a devno
 */
static inline dasd_devreg_t *
dasd_create_devreg (int devno)
{
    dasd_devreg_t *r = kmalloc (sizeof (dasd_devreg_t), GFP_KERNEL);
    if (r != NULL) {
        memset (r, 0, sizeof (dasd_devreg_t));
        r->devreg.ci.devno = devno;
        r->devreg.flag = DEVREG_TYPE_DEVNO;
        r->devreg.oper_func = dasd_oper_handler;
    }
    return r;
}

/*
 * function: dasd_destroy_devreg
 * destroys the dasd_devreg_t given as argument
 */
static inline void
dasd_destroy_devreg (dasd_devreg_t * devreg)
{
    kfree (devreg);
}

#endif                /* CONFIG_DASD_DYNAMIC */

/* SECTION: managing setup of dasd_driver */

/* default setting is probeonly, autodetect */
static int dasd_probeonly = 1;    /* is true, when probeonly mode is active */
static int dasd_autodetect = 1;    /* is true, when autodetection is active */

static dasd_range_t dasd_range_head =
    { list:LIST_HEAD_INIT (dasd_range_head.list) };
static spinlock_t range_lock = SPIN_LOCK_UNLOCKED;

/*
 * function: dasd_create_range
 * creates a dasd_range_t according to the arguments
 * FIXME: no check is performed for reoccurrence of a devno
 */
static inline dasd_range_t *
dasd_create_range (int from, int to, int features)
{
    dasd_range_t *range = NULL;
        int i;

    if ( from > to ) {
                printk (KERN_WARNING PRINTK_HEADER 
                        "Adding device range %04x-%04x: range invalid, ignoring.\n",
                        from,
                        to);

        return NULL;
    }
    for (i=from;i<=to;i++) {
                if (dasd_device_from_devno(i)) {
                        printk (KERN_WARNING PRINTK_HEADER 
                                "device range %04x-%04x: device %04x is already in a range.\n",
                                from,
                                to,
                                i);
                }
        }
    range = (dasd_range_t *) kmalloc (sizeof (dasd_range_t), GFP_KERNEL);
    if (range == NULL)
        return NULL;
    memset (range, 0, sizeof (dasd_range_t));
    range->from = from;
        range->to = to;
        range->features = features;
    return range;
}

/*
 * function dasd_destroy_range
 * destroy a range allocated wit dasd_crate_range
 * CAUTION: must not be callen in arunning sysztem, because it destroys
 * the mapping of DASDs
 */
static inline void
dasd_destroy_range (dasd_range_t * range)
{
    kfree (range);
}

/*
 * function: dasd_append_range
 * appends the range given as argument to the list anchored at dasd_range_head.
 */
static inline void
dasd_append_range (dasd_range_t * range)
{
    long flags;

    spin_lock_irqsave (&range_lock, flags);
    list_add_tail (&range->list, &dasd_range_head.list);
    spin_unlock_irqrestore (&range_lock, flags);
}

/*
 * function dasd_dechain_range
 * removes a range from the chain of ranges
 * CAUTION: must not be called in a running system because it destroys
 * the mapping of devices
 */
static inline void
dasd_dechain_range (dasd_range_t * range)
{
    unsigned long flags;

    spin_lock_irqsave (&range_lock, flags);
    list_del (&range->list);
    spin_unlock_irqrestore (&range_lock, flags);
}

/*
 * function: dasd_add_range
 * creates a dasd_range_t according to the arguments and
 * appends it to the list of ranges
 * additionally a devreg_t is created and added to the list of devregs
 */
static inline dasd_range_t *
dasd_add_range (int from, int to, int features)
{
    dasd_range_t *range;

    range = dasd_create_range (from, to, features);
    if (!range)
        return NULL;

    dasd_append_range (range);
#ifdef CONFIG_DASD_DYNAMIC
    /* allocate and chain devreg infos for the devnos... */
    {
        int i;
        for (i = range->from; i <= range->to; i++) {
            dasd_devreg_t *reg = dasd_create_devreg (i);
            s390_device_register (&reg->devreg);
            list_add (&reg->list, &dasd_devreg_head);
        }
    }
#endif                /* CONFIG_DASD_DYNAMIC */
    return range;
}

/*
 * function: dasd_remove_range
 * removes a range and the corresponding devregs from all of the chains
 * CAUTION: must not be called in a running system because it destroys
 * the mapping of devices!
 */
static inline void
dasd_remove_range (dasd_range_t * range)
{
#ifdef CONFIG_DASD_DYNAMIC
    /* deallocate and dechain devreg infos for the devnos... */
    {
        int i;
        for (i = range->from; i <= range->to; i++) {
            struct list_head *l;
            dasd_devreg_t *reg = NULL;
            list_for_each (l, &dasd_devreg_head) {
                reg = list_entry (l, dasd_devreg_t, list);
                if (reg->devreg.flag == DEVREG_TYPE_DEVNO &&
                    reg->devreg.ci.devno == i &&
                    reg->devreg.oper_func == dasd_oper_handler)
                    break;
            }
            if (l == &dasd_devreg_head)
                BUG ();
                        list_del(&reg->list);
            s390_device_unregister (&reg->devreg);
            dasd_destroy_devreg (reg);
        }
    }
#endif                /* CONFIG_DASD_DYNAMIC */
    dasd_dechain_range (range);
    dasd_destroy_range (range);
}

/* 
 * function: dasd_devindex_from_devno
 * finds the logical number of the devno supplied as argument in the list
 * of dasd ranges and returns it or ENODEV when not found
 */
static int
dasd_devindex_from_devno (int devno)
{
    dasd_range_t *temp;
    int devindex = 0;
    unsigned long flags;
    struct list_head *l;

    spin_lock_irqsave (&range_lock, flags);
    list_for_each (l, &dasd_range_head.list) {
        temp = list_entry (l, dasd_range_t, list);
        if (devno >= temp->from && devno <= temp->to) {
            spin_unlock_irqrestore (&range_lock, flags);
            return devindex + devno - temp->from;
        }
        devindex += temp->to - temp->from + 1;
    }
    spin_unlock_irqrestore (&range_lock, flags);
    return -ENODEV;
}

/*
 * function: dasd_devno_from_devindex
 */
static int
dasd_devno_from_devindex (int devindex)
{
    dasd_range_t *temp;
    unsigned long flags;
    struct list_head *l;

    spin_lock_irqsave (&range_lock, flags);
    list_for_each (l, &dasd_range_head.list) {
        temp = list_entry (l, dasd_range_t, list);
                if ( devindex < temp->to - temp->from + 1) {
            spin_unlock_irqrestore (&range_lock, flags);
            return temp->from + devindex;
        }
        devindex -= temp->to - temp->from + 1;
    }
    spin_unlock_irqrestore (&range_lock, flags);
    return -ENODEV;
}

/* SECTION: parsing the dasd= parameter of the parmline/insmod cmdline */

/*
 * char *dasd[] is intended to hold the ranges supplied by the dasd= statement
 * it is named 'dasd' to directly be filled by insmod with the comma separated
 * strings when running as a module.
 * a maximum of 256 ranges can be supplied, as the parmline is limited to
 * <1024 Byte anyway.
 */
char *dasd[256];
char *dasd_disciplines[8];

#ifndef MODULE
/*
 * function: dasd_split_parm_string
 * splits the parmline given to the kernel into comma separated strings
 * which are filled into the 'dasd[]' array, to be parsed later on
 */
static void
dasd_split_parm_string (char *str)
{
    char *tmp = str;
    int count = 0;
    while (tmp != NULL && *tmp != '\0') {
        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[count] == NULL) {
            printk (KERN_WARNING PRINTK_HEADER
                "can't store dasd= parameter no %d\n",
                count + 1);
            break;
        }
        memset (dasd[count], 0, len * sizeof (char));
        memcpy (dasd[count], tmp, len * sizeof (char));
        count++;
        tmp = end;
    };
}

/*
 * dasd_parm_string holds a concatenated version of all 'dasd=' parameters
 * supplied in the parmline, which is later to be split by
 * dasd_split_parm_string
 * FIXME: why first concatenate then split ?
 */
static char dasd_parm_string[1024] __initdata = { 0, };

/*
 * function: dasd_setup
 * is invoked for any single 'dasd=' parameter supplied in the parmline
 * it merges all the arguments into dasd_parm_string
 */
void __init
dasd_setup (char *str, int *ints)
{
    int len = strlen (dasd_parm_string);
    if (len != 0) {
        strcat (dasd_parm_string, ",");
    }
    strcat (dasd_parm_string, str);
}

/*
 * function: dasd_call_setup
 * is the 2.4 version of dasd_setup and
 * is invoked for any single 'dasd=' parameter supplied in the parmline
 */
int __init
dasd_call_setup (char *str)
{
    int dummy;
    dasd_setup (str, &dummy);
    return 1;
}

int __init
dasd_disciplines_setup (char *str)
{
    return 1;
}

__setup ("dasd=", dasd_call_setup);
__setup ("dasd_disciplines=", dasd_disciplines_setup);

#endif                /* MODULE */

/*
 * function: dasd_strtoul
 * provides a wrapper to simple_strtoul to strip leading '0x' and
 * interpret any argument to dasd=[range,...] as hexadecimal
 */
static inline int
dasd_strtoul (char *str, char **stra, int* features)
{
        char *temp=str;
        char *buffer;
        int val,i,start;

        buffer=(char*)kmalloc((strlen(str)+1)*sizeof(char),GFP_ATOMIC);
        if (buffer==NULL) {
            printk (KERN_WARNING PRINTK_HEADER
                    "can't parse dasd= parameter %s due to low memory\n",
                    str);
        }

        /* remove leading '0x' */
        if (*temp == '0') {
                temp++;         /* strip leading zero */
                if (*temp == 'x')
                        temp++; /* strip leading x */
        }

        /* copy device no to buffer and convert to decimal */
        for (i=0; temp[i]!='\0' && temp[i]!='(' && 
                  temp[i]!='-'  && temp[i]!=' '; i++){
                if (isxdigit(temp[i])) {
                        buffer[i]=temp[i];
                } else {
                        return -EINVAL;
                }
        }

        buffer[i]='\0';

        val = simple_strtoul (buffer, &buffer, 16);

        /* check for features - e.g. (ro) ; the '\0', ')' and '-' stops check */
        *features = DASD_DEFAULT_FEATURES;

        if (temp[i]=='(') {

                while (temp[i]!='\0' && temp[i]!=')'&&temp[i]!='-') { 
                        start=++i;      
        
                        /* move next feature to buffer */
                        for (;temp[i]!='\0'&&temp[i]!=':'&&temp[i]!=')'&&temp[i]!='-';i++)
                                buffer[i-start]=temp[i];
                        buffer[i-start]='\0';

                        if (strlen(buffer)) { 
                                if (!strcmp(buffer,"ro")) { /* handle 'ro' feature */
                                        (*features) |= DASD_FEATURE_READONLY;
                                        break;
                                }
                                printk (KERN_WARNING PRINTK_HEADER 
                                        "unsupported feature: %s, ignoring setting",
                                        buffer);
                        }
                }
        }

        *stra = temp+i;
        return val;
}

/*
 * function: dasd_parse
 * examines the strings given in the string array str and
 * creates and adds the ranges to the apropriate lists
 */
static int
dasd_parse (char **str)
{
    char *temp;
    int from, to;
        int features;
        int rc = 0;

    if (*str) {
        /* turn off probeonly mode, if any dasd parameter is present */
        dasd_probeonly = 0;
        dasd_autodetect = 0;
    }
    while (*str) {
        temp = *str;
        from = 0;
        to = 0;
        if (strcmp ("autodetect", *str) == 0) {
            dasd_autodetect = 1;
            printk (KERN_INFO "turning to autodetection mode\n");
            break;
        } else if (strcmp ("probeonly", *str) == 0) {
            dasd_probeonly = 1;
            printk (KERN_INFO "turning to probeonly mode\n");
            break;
        } else {
            /* turn off autodetect mode, if any range is present */
            dasd_autodetect = 0;
            from = dasd_strtoul (temp, &temp, &features);
                        to = from;
            if (*temp == '-') {
                temp++;
                to = dasd_strtoul (temp, &temp, &features);
            }
                        if (from == -EINVAL ||
                            to   == -EINVAL    ) {
                                rc = -EINVAL;
                                break;
                        } else {
                                dasd_add_range (from, to ,features);
                        }
                }
        str++;
    }

        return rc;
}

/* SECTION: Dealing with devices registered to multiple major numbers */

static spinlock_t dasd_major_lock = SPIN_LOCK_UNLOCKED;

static major_info_t dasd_major_info[] = {
    {
          list:LIST_HEAD_INIT (dasd_major_info[1].list)
     },
    {
          list:LIST_HEAD_INIT (dasd_major_info[0].list),
          gendisk:{
      INIT_GENDISK (94, DASD_NAME, DASD_PARTN_BITS, DASD_PER_MAJOR)
      },
      flags:DASD_MAJOR_INFO_IS_STATIC}
};

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) {
        static major_info_t temp_major_info = {
            gendisk:{
                 INIT_GENDISK (0, DASD_NAME, DASD_PARTN_BITS,
                           DASD_PER_MAJOR)}
        };
        memcpy (major_info, &temp_major_info, sizeof (major_info_t));
    }
    return major_info;
}

/*
 * register major number
 * is called with the 'static' major_info during init of the driver or 'NULL' to
 * allocate an additional dynamic major.
 */
static int
dasd_register_major (major_info_t * major_info)
{
    int rc = 0;
    int major;
    unsigned long flags;

        /* allocate dynamic major */
    if (major_info == NULL) {
        major_info = get_new_major_info ();
        if (!major_info) {
            printk (KERN_WARNING PRINTK_HEADER
                "Cannot get memory to allocate another major number\n");
            return -ENOMEM;
        }
    }

    major = major_info->gendisk.major;

        /* init devfs array */
    major_info->gendisk.de_arr = (devfs_handle_t *)
        kmalloc (DASD_PER_MAJOR * sizeof (devfs_handle_t), GFP_KERNEL);
    if(major_info->gendisk.de_arr == NULL)
        goto out_gd_de_arr;

    memset (major_info->gendisk.de_arr, 0,
        DASD_PER_MAJOR * sizeof (devfs_handle_t));

        /* init flags */
    major_info->gendisk.flags = (char *)
        kmalloc (DASD_PER_MAJOR * sizeof (char), GFP_KERNEL);
    if(major_info->gendisk.flags == NULL)
        goto out_gd_flags;

    memset (major_info->gendisk.flags, 0, DASD_PER_MAJOR * sizeof (char));

        /* register blockdevice */
    rc = devfs_register_blkdev (major, DASD_NAME, &dasd_device_operations);
    if (rc < 0) {
        printk (KERN_WARNING PRINTK_HEADER
            "Cannot register to major no %d, rc = %d\n",
                        major, 
                        rc);
        goto out_reg_blkdev; 
    } else {
        major_info->flags |= DASD_MAJOR_INFO_REGISTERED;
    }

    /* Insert the new major info into dasd_major_info if needed (dynamic major) */
    if (!(major_info->flags & DASD_MAJOR_INFO_IS_STATIC)) {
        spin_lock_irqsave (&dasd_major_lock, flags);
        list_add_tail (&major_info->list, &dasd_major_info[0].list);
        spin_unlock_irqrestore (&dasd_major_lock, flags);
    }

    if (major == 0) {
        major = rc;
        rc = 0;
    }

        /* init array of devices */
    major_info->dasd_device =
        (dasd_device_t **) kmalloc (DASD_PER_MAJOR *
                    sizeof (dasd_device_t *), GFP_ATOMIC);
    if (!major_info->dasd_device)
        goto out_devices;
    memset (major_info->dasd_device, 0,
        DASD_PER_MAJOR * sizeof (dasd_device_t *));

        /* init blk_size */
    blk_size[major] =
        (int *) kmalloc ((1 << MINORBITS) * sizeof (int), GFP_ATOMIC);
    if (!blk_size[major])
        goto out_blk_size;
    memset (blk_size[major], 0, (1 << MINORBITS) * sizeof (int));

        /* init blksize_size */
    blksize_size[major] =
        (int *) kmalloc ((1 << MINORBITS) * sizeof (int), GFP_ATOMIC);
    if (!blksize_size[major])
        goto out_blksize_size;
    memset (blksize_size[major], 0, (1 << MINORBITS) * sizeof (int));

        /* init_hardsect_size */
    hardsect_size[major] =
        (int *) kmalloc ((1 << MINORBITS) * sizeof (int), GFP_ATOMIC);
    if (!hardsect_size[major])
        goto out_hardsect_size;
    memset (hardsect_size[major], 0, (1 << MINORBITS) * sizeof (int));

        /* init max_sectors */
    max_sectors[major] =
        (int *) kmalloc ((1 << MINORBITS) * sizeof (int), GFP_ATOMIC);
    if (!max_sectors[major])
        goto out_max_sectors;
    memset (max_sectors[major], 0, (1 << MINORBITS) * sizeof (int));

    /* finally do the gendisk stuff */
    major_info->gendisk.part = kmalloc ((1 << MINORBITS) *
                        sizeof (struct hd_struct),
                        GFP_ATOMIC);
    if (!major_info->gendisk.part)
        goto out_gendisk;
    memset (major_info->gendisk.part, 0, (1 << MINORBITS) *
        sizeof (struct hd_struct));

    INIT_BLK_DEV (major, do_dasd_request, dasd_get_queue, NULL);

    major_info->gendisk.sizes = blk_size[major];
    major_info->gendisk.major = major;
    add_gendisk (&major_info->gendisk);
    return major;

        /* error handling - free the prior allocated memory */  
      out_gendisk:
    kfree (max_sectors[major]);
    max_sectors[major] = NULL;

      out_max_sectors:
    kfree (hardsect_size[major]);
    hardsect_size[major] = NULL;

      out_hardsect_size:
    kfree (blksize_size[major]);
    blksize_size[major] = NULL;

      out_blksize_size:
    kfree (blk_size[major]);
    blk_size[major] = NULL;

      out_blk_size:
    kfree (major_info->dasd_device);

      out_devices:
    /* Delete the new major info from dasd_major_info list if needed (dynamic) +*/
    if (!(major_info->flags & DASD_MAJOR_INFO_IS_STATIC)) {
        spin_lock_irqsave (&dasd_major_lock, flags);
        list_del (&major_info->list);
        spin_unlock_irqrestore (&dasd_major_lock, flags);
    }

        /* unregister blockdevice */
    rc = devfs_unregister_blkdev (major, DASD_NAME);
    if (rc < 0) {
        printk (KERN_WARNING PRINTK_HEADER
            "Unable to unregister from major no %d, rc = %d\n", 
                        major,
            rc);
    } else {
        major_info->flags &= ~DASD_MAJOR_INFO_REGISTERED;
    }

out_reg_blkdev:
    kfree (major_info->gendisk.flags);

out_gd_flags:
    kfree (major_info->gendisk.de_arr);

out_gd_de_arr:
    /* Delete the new major info from dasd_major_info if needed */
    if (!(major_info->flags & DASD_MAJOR_INFO_IS_STATIC)) {
        kfree (major_info);
    }

    return -ENOMEM;
}

static int
dasd_unregister_major (major_info_t * major_info)
{
    int rc = 0;
    int major;
    unsigned long flags;

    if (major_info == NULL) {
        return -EINVAL;
    }
    major = major_info->gendisk.major;
    INIT_BLK_DEV (major, NULL, NULL, NULL);

    del_gendisk (&major_info->gendisk);

    kfree (major_info->dasd_device);
    kfree (major_info->gendisk.part);

    kfree (blk_size[major]);
    kfree (blksize_size[major]);
    kfree (hardsect_size[major]);
    kfree (max_sectors[major]);

    blk_size[major]      = NULL;
    blksize_size[major]  = NULL;
    hardsect_size[major] = NULL;
    max_sectors[major]   = NULL;

    rc = devfs_unregister_blkdev (major, DASD_NAME);
    if (rc < 0) {
        printk (KERN_WARNING PRINTK_HEADER
            "Cannot unregister from major no %d, rc = %d\n",
                        major,
            rc);
        return rc;
    } else {
        major_info->flags &= ~DASD_MAJOR_INFO_REGISTERED;
    }

    kfree (major_info->gendisk.flags);
    kfree (major_info->gendisk.de_arr);

    /* Delete the new major info from dasd_major_info if needed */
    if (!(major_info->flags & DASD_MAJOR_INFO_IS_STATIC)) {
        spin_lock_irqsave (&dasd_major_lock, flags);
        list_del (&major_info->list);
        spin_unlock_irqrestore (&dasd_major_lock, flags);
        kfree (major_info);
    }
    return rc;
}

/*
 * function: dasd_device_from_kdev
 * finds the device structure corresponding to the kdev supplied as argument
 * in the major_info structures and returns it or NULL when not found
 */
dasd_device_t *
dasd_device_from_kdev (kdev_t kdev)
{
    major_info_t *major_info = NULL;
    struct list_head *l;
    unsigned long flags;

    spin_lock_irqsave (&dasd_major_lock, flags);
    list_for_each (l, &dasd_major_info[0].list) {
        major_info = list_entry (l, major_info_t, list);
        if (major_info->gendisk.major == MAJOR (kdev))
            break;
    }
    spin_unlock_irqrestore (&dasd_major_lock, flags);
    if (major_info != &dasd_major_info[0])
        return major_info->dasd_device[MINOR (kdev) >> DASD_PARTN_BITS];
    return NULL;
}

/*
 * function: dasd_device_from_devno
 * finds the address of the device structure corresponding to the devno
 * supplied as argument in the major_info structures and returns
 * it or NULL when not found
 */
static inline dasd_device_t **
dasd_device_from_devno (int devno)
{
    major_info_t *major_info;
    struct list_head *l;
    int devindex = dasd_devindex_from_devno (devno);
    unsigned long flags;

    spin_lock_irqsave (&dasd_major_lock, flags);
    list_for_each (l, &dasd_major_info[0].list) {
        major_info = list_entry (l, major_info_t, list);
        if (devindex < DASD_PER_MAJOR) {
            spin_unlock_irqrestore (&dasd_major_lock, flags);
            return &major_info->dasd_device[devindex];
        }
        devindex -= DASD_PER_MAJOR;
    }
    spin_unlock_irqrestore (&dasd_major_lock, flags);
    return NULL;
}

/*
 * function: dasd_features_from_devno
 * finds the device range corresponding to the devno
 * supplied as argument in the major_info structures and returns
 * the features set for it
 */

static int
dasd_features_from_devno (int devno)
{
    dasd_range_t *temp;
    int devindex = 0;
    unsigned long flags;
    struct list_head *l;

    spin_lock_irqsave (&range_lock, flags);
    list_for_each (l, &dasd_range_head.list) {
        temp = list_entry (l, dasd_range_t, list);
        if (devno >= temp->from && devno <= temp->to) {
            spin_unlock_irqrestore (&range_lock, flags);
            return temp->features;
        }
        devindex += temp->to - temp->from + 1;
    }
    spin_unlock_irqrestore (&range_lock, flags);
    return -ENODEV;
}



/* SECTION: managing dasd disciplines */

/* anchor and spinlock for list of disciplines */
static struct list_head dasd_disc_head = LIST_HEAD_INIT(dasd_disc_head);
static spinlock_t discipline_lock = SPIN_LOCK_UNLOCKED;

/*
 * function dasd_discipline_enq
 * chains the discpline given as argument to the head of disiplines
 * head chaining policy is required to allow module disciplines to
 * be preferred against those, who are statically linked
 */
static inline void
dasd_discipline_enq (dasd_discipline_t * d)
{
    list_add(&d->list, &dasd_disc_head);
}

/*
 * function dasd_discipline_deq
 * removes the discipline given as argument from the list of disciplines
 */
static inline void
dasd_discipline_deq (dasd_discipline_t * d)
{
        list_del(&d->list);
}

void
dasd_discipline_add (dasd_discipline_t * d)
{
        unsigned long flags;
        MOD_INC_USE_COUNT;
    spin_lock_irqsave (&discipline_lock,flags);
        dasd_discipline_enq (d);
    spin_unlock_irqrestore (&discipline_lock,flags);
        dasd_enable_ranges (&dasd_range_head, d, DASD_STATE_ONLINE);
}

void dasd_discipline_del (dasd_discipline_t * d)
{
        unsigned long flags;
    spin_lock_irqsave (&discipline_lock,flags);
        dasd_disable_ranges(&dasd_range_head, d, DASD_STATE_DEL, 1);
        dasd_discipline_deq (d);
    spin_unlock_irqrestore (&discipline_lock,flags);
        MOD_DEC_USE_COUNT;
}

static inline dasd_discipline_t *
dasd_find_disc (dasd_device_t * device, dasd_discipline_t *d)
{
        dasd_discipline_t *t;
        struct list_head *l = d ? &d->list : dasd_disc_head.next;
        do {
                t = list_entry(l,dasd_discipline_t,list);
                if ( ( t->id_check == NULL ||
                       t->id_check (&device->devinfo) == 0 ) &&
                     ( t->check_characteristics == NULL ||
                       t->check_characteristics (device) == 0 ) )
                        break;
                l = l->next;
                if ( d || 
                     l == &dasd_disc_head ) {
                        t = NULL;
                        break;
                }
         } while ( 1 );
    return t;
}

/* SECTION: profiling stuff */

static dasd_profile_info_t dasd_global_profile;

#ifdef DASD_PROFILE
/*
 * macro: dasd_profile_add_counter
 * increments counter in global and local profiling structures
 * according to the value
 */
#define dasd_profile_add_counter( value, counter, device ) \
{ \
        int ind; \
        long help; \
    for (ind = 0, help = value >> 3; \
             ind < 31 && help; \
             help = help >> 1, ind++) {} \
    dasd_global_profile.counter[ind]++; \
        device->profile.counter[ind]++; \
}

/*
 * function dasd_profile_add
 * adds the profiling information from the cqr given as argument to the
 * global and device specific profiling information
 */
void
dasd_profile_add (ccw_req_t * cqr)
{
    long strtime, irqtime, endtime, tottime; /* in microsecnds*/
    long tottimeps, sectors;
    dasd_device_t *device = cqr->device;

    if (!cqr->req)        /* safeguard against abnormal cqrs */
        return;

        if ((!cqr->buildclk) ||
            (!cqr->startclk) ||
            (!cqr->stopclk ) ||
            (!cqr->endclk  ) ||
            (!(sectors = ((struct request *) (cqr->req))->nr_sectors)))
                return;

    strtime = ((cqr->startclk - cqr->buildclk) >> 12);
    irqtime = ((cqr->stopclk - cqr->startclk) >> 12);
    endtime = ((cqr->endclk - cqr->stopclk) >> 12);
    tottime = ((cqr->endclk - cqr->buildclk) >> 12);
    tottimeps = tottime / sectors;

    if (!dasd_global_profile.dasd_io_reqs) {
        memset (&dasd_global_profile, 0, sizeof (dasd_profile_info_t));
    };
    if (!device->profile.dasd_io_reqs) {
        memset (&device->profile, 0, sizeof (dasd_profile_info_t));
    };

    dasd_global_profile.dasd_io_reqs++;
    device->profile.dasd_io_reqs++;
    dasd_global_profile.dasd_io_sects+=sectors;
    device->profile.dasd_io_sects+=sectors;
    dasd_profile_add_counter (sectors, dasd_io_secs, device);
    dasd_profile_add_counter (tottime, dasd_io_times, device);
    dasd_profile_add_counter (tottimeps, dasd_io_timps, device);
    dasd_profile_add_counter (strtime, dasd_io_time1, device);
    dasd_profile_add_counter (irqtime, dasd_io_time2, device);
    dasd_profile_add_counter (irqtime / sectors, dasd_io_time2ps, device);
    dasd_profile_add_counter (endtime, dasd_io_time3, device);
}
#endif

/* SECTION: All the gendisk stuff */


/* SECTION: Managing wrappers for ccwcache */

/*
 * function dasd_alloc_request
 * tries to return space for a channel program of length cplength with
 * additional data of size datasize.
 * If the ccwcache cannot fulfill the request it tries the emergeny requests
 * before giving up finally
 * FIXME: initialization of ccw_req_t should be done by function of ccwcache
 */
ccw_req_t *
dasd_alloc_request (char *magic, int cplength, int datasize, dasd_device_t* device)
{
    ccw_req_t *rv = NULL;

    if ((rv = ccw_alloc_request (magic, cplength, datasize)) != NULL) {
        return rv;
    }
    if ((((sizeof (ccw_req_t) + 7) & -8) +
         cplength * sizeof (ccw1_t) + datasize) > PAGE_SIZE) {
        BUG ();
        }
        if (device->lowmem_cqr==NULL) {
                DASD_DRIVER_DEBUG_EVENT (2, dasd_alloc_request,
                                         "(%04x) Low memory! Using emergency request %p.",
                                         device->devinfo.devno,
                                         device->lowmem_ccws);

                device->lowmem_cqr=device->lowmem_ccws;
                rv = device->lowmem_ccws;
        memset (rv, 0, PAGE_SIZE);
        strncpy ((char *) (&rv->magic), magic, 4);
        ASCEBC ((char *) (&rv->magic), 4);
        rv->cplength = cplength;
        rv->datasize = datasize;
        rv->data = (void *) ((long) rv + PAGE_SIZE - datasize);
        rv->cpaddr = (ccw1_t *) ((long) rv + sizeof (ccw_req_t));
        } else {
                DASD_DRIVER_DEBUG_EVENT (2, dasd_alloc_request,
                                         "(%04x) Refusing emergency mem for request "
                                         "NULL, already in use at %p.",
                                         device->devinfo.devno,
                                         device->lowmem_ccws);
    }
    return rv;
}

/*
 * function dasd_free_request
 * returns a ccw_req_t to the appropriate cache or emergeny request line
 */
void
dasd_free_request (ccw_req_t * request, dasd_device_t* device)
{
#ifdef CONFIG_ARCH_S390X
        ccw1_t* ccw;
        /* clear any idals used for chain */
        ccw=request->cpaddr-1;
        do {
                ccw++;
                if ((ccw->cda < (unsigned long) device->lowmem_idals           ) || 
                    (ccw->cda >= (unsigned long) device->lowmem_idals+PAGE_SIZE)   )
                        clear_normalized_cda (ccw);
                else {
                        if (device->lowmem_idal_ptr != device->lowmem_idals)
                                DASD_MESSAGE (KERN_WARNING, device,
                                              "Freeing emergency idals from request at %p.",
                                              request);
                        device->lowmem_idal_ptr = device->lowmem_idals;
                        device->lowmem_cqr=NULL;
                }
        } while ((ccw->flags & CCW_FLAG_CC) || 
                 (ccw->flags & CCW_FLAG_DC)   );
#endif
        if (request != device->lowmem_ccws) { 
                /* compare to lowmem_ccws to protect usage of lowmem_cqr for IDAL only ! */
        ccw_free_request (request);
        } else {
                DASD_MESSAGE (KERN_WARNING, device,
                              "Freeing emergency request at %p",
                              request);
                device->lowmem_cqr=NULL;
    }
}

int
dasd_set_normalized_cda (ccw1_t * cp, unsigned long address, 
                         ccw_req_t* request, dasd_device_t* device )
{
#ifdef CONFIG_ARCH_S390X
    int nridaws;
        int count = cp->count;
        
        if (set_normalized_cda (cp, address)!=-ENOMEM) {
                return 0;
        }

        if ((device->lowmem_cqr!=NULL) && (device->lowmem_cqr!=request)) {
                DASD_MESSAGE (KERN_WARNING, device, 
                              "Refusing emergency idals for request %p, memory"
                              " is already in use for request %p",
                              request,
                              device->lowmem_cqr);
                return -ENOMEM;
        }
        device->lowmem_cqr=request;
        if (device->lowmem_idal_ptr == device->lowmem_idals) {
            DASD_MESSAGE (KERN_WARNING,device, 
                          "Low memory! Using emergency IDALs for request %p.\n",
                          request);
        }
        nridaws = ((address & (IDA_BLOCK_SIZE-1)) + count + 
           (IDA_BLOCK_SIZE-1)) >> IDA_SIZE_LOG;
    if ( device->lowmem_idal_ptr>=device->lowmem_idals + PAGE_SIZE ) {
        /* Ouch! No Idals left for emergency request */
        BUG();
    }
    cp->flags |= CCW_FLAG_IDA;
    cp->cda = (__u32)(unsigned long)device->lowmem_idal_ptr;
        do {
        *((long*)device->lowmem_idal_ptr) = address;
        address = (address & -(IDA_BLOCK_SIZE)) + (IDA_BLOCK_SIZE);
        nridaws --;
                device->lowmem_idal_ptr += sizeof(unsigned long);
        } while ( nridaws > 0 );
#else 
        cp -> cda = address;
#endif
    return 0;
}


/* SECTION: (de)queueing of requests to channel program queues */

/*
 * function dasd_chanq_enq
 * appends the cqr given as argument to the queue
 * has to be called with the queue lock (namely the s390_irq_lock) acquired
 */
inline void
dasd_chanq_enq (dasd_chanq_t * q, ccw_req_t * cqr)
{
    if (q->head != NULL) {
        q->tail->next = cqr;
    } else
        q->head = cqr;
    cqr->next = NULL;
    q->tail = cqr;
    check_then_set (&cqr->status, 
                        CQR_STATUS_FILLED, 
                        CQR_STATUS_QUEUED);

       
#ifdef DASD_PROFILE
        /* save profile information for non erp cqr */
        if (cqr->refers == NULL) {
                unsigned int  counter = 0;
                ccw_req_t     *ptr;
                dasd_device_t *device = cqr->device;

                /* count the length of the chanq for statistics */
                for (ptr = q->head; 
                     ptr->next != NULL && counter <=31; 
                     ptr = ptr->next) {
                        counter++;
                }                
                
                dasd_global_profile.dasd_io_nr_req[counter]++;
                device->profile.dasd_io_nr_req[counter]++;
        }
#endif 
}

/*
 * function dasd_chanq_enq_head
 * chains the cqr given as argument to the queue head
 * has to be called with the queue lock (namely the s390_irq_lock) acquired
 */
inline void
dasd_chanq_enq_head (dasd_chanq_t * q, ccw_req_t * cqr)
{
    cqr->next = q->head;
    q->head = cqr;
    if (q->tail == NULL)
        q->tail = cqr;
    check_then_set (&cqr->status, CQR_STATUS_FILLED, CQR_STATUS_QUEUED);
}

/*
 * function dasd_chanq_deq
 * dechains the cqr given as argument from the queue
 * has to be called with the queue lock (namely the s390_irq_lock) acquired
 */
inline void
dasd_chanq_deq (dasd_chanq_t * q, ccw_req_t * cqr)
{
    ccw_req_t *prev;

    if (cqr == NULL)
        BUG ();

    if (cqr == q->head) {
        q->head = cqr->next;
        if (q->head == NULL)
            q->tail = NULL;

    } else {
        prev = q->head;
        while (prev && prev->next != cqr)
            prev = prev->next;
        if (prev == NULL)
            return;
        prev->next = cqr->next;
        if (prev->next == NULL)
            q->tail = prev;
    }
    cqr->next = NULL;
}

/* SECTION: Managing the device queues etc. */

/*
 * DASD_TERM_IO
 *
 * attempts to terminate the the current IO and set it to failed if termination
 * was successful.
 * returns an appropriate return code
 */
int
dasd_term_IO (ccw_req_t * cqr)
{
    int rc = 0;
    dasd_device_t *device = cqr->device;
    int irq;
        int retries = 0;

    if (!cqr) {
        BUG ();
    }
    irq = device->devinfo.irq;
    if (strncmp ((char *) &cqr->magic, device->discipline->ebcname, 4)) {
        DASD_MESSAGE (KERN_WARNING, device,
                  " ccw_req_t 0x%08x magic doesn't match"
                  " discipline 0x%08x\n",
                  cqr->magic,
                  *(unsigned int *) device->discipline->name);
        return -EINVAL;
    }
        
        while ((retries < 5                    ) &&
               (cqr->status == CQR_STATUS_IN_IO)   ) {

                if ( retries < 2 )
                        rc = halt_IO(irq, (long)cqr, 
                                     cqr->options | DOIO_WAIT_FOR_INTERRUPT);
                else
                        rc = clear_IO(irq, (long)cqr, 
                                      cqr->options | DOIO_WAIT_FOR_INTERRUPT);

                switch (rc) {
                case 0:         /* termination successful */
                        check_then_set (&cqr->status,
                                        CQR_STATUS_IN_IO, 
                                        CQR_STATUS_FAILED);
                        
                        asm volatile ("STCK %0":"=m" (cqr->stopclk));
                        break;
                case -ENODEV:
                        DASD_MESSAGE (KERN_WARNING, device, "%s",
                                      "device gone, retry\n");
                        break;
                case -EIO:
                        DASD_MESSAGE (KERN_WARNING, device, "%s",
                                      "I/O error, retry\n");
                        break;
                case -EBUSY:
                        DASD_MESSAGE (KERN_WARNING, device, "%s",
                                      "device busy, retry later\n");
                        break;
                default:
                        DASD_MESSAGE (KERN_ERR, device,
                                      "line %d unknown RC=%d, please report"
                                      " to linux390@de.ibm.com\n", 
                                      __LINE__, 
                                      rc);
                        BUG ();
                        break;
                }

                retries ++;
        }
    return rc;
}

/*
 * function dasd_start_IO
 * attempts to start the IO and returns an appropriate return code
 */
int
dasd_start_IO (ccw_req_t * cqr)
{
    int rc = 0;
    dasd_device_t *device = cqr->device;
    int irq;
    unsigned long long now;

    if (!cqr) {
        BUG ();
    }
    irq = device->devinfo.irq;
    if (strncmp ((char *) &cqr->magic, device->discipline->ebcname, 4)) {
        DASD_MESSAGE (KERN_WARNING, device,
                  " ccw_req_t 0x%08x magic doesn't match"
                  " discipline 0x%08x\n",
                  cqr->magic,
                  *(unsigned int *) device->discipline->name);
        return -EINVAL;
    }

    asm volatile ("STCK %0":"=m" (now));
        cqr->startclk = now;

    rc = do_IO (irq, cqr->cpaddr, (long) cqr, cqr->lpm, cqr->options);

    switch (rc) {
    case 0:
                if (cqr->options & DOIO_WAIT_FOR_INTERRUPT) {
                        /* request already finished (synchronous IO) */
                        DASD_MESSAGE (KERN_ERR, device, "%s",
                                      " do_IO finished request... "
                                      "DOIO_WAIT_FOR_INTERRUPT was set");
                        check_then_set (&cqr->status,
                                        CQR_STATUS_QUEUED, 
                                        CQR_STATUS_DONE);

                        cqr->stopclk = now;
                        dasd_schedule_bh (device);
                        
                } else {
                        check_then_set (&cqr->status,
                                        CQR_STATUS_QUEUED, 
                                        CQR_STATUS_IN_IO);
                }
        break;
    case -EBUSY:
        DASD_MESSAGE (KERN_WARNING, device, "%s",
                  "device busy, retry later\n");
        break;
    case -ETIMEDOUT: 
        DASD_MESSAGE (KERN_WARNING, device, "%s",
                  "request timeout - terminated\n");
    case -ENODEV:
    case -EIO:
        check_then_set (&cqr->status,
                CQR_STATUS_QUEUED, 
                                CQR_STATUS_FAILED);

                cqr->stopclk = now;
                dasd_schedule_bh (device);
        break;
    default:
        DASD_MESSAGE (KERN_ERR, device,
                  "line %d unknown RC=%d, please report"
                  " to linux390@de.ibm.com\n", __LINE__, rc);
        BUG ();
        break;
    }

    return rc;
}

/*
 * function dasd_sleep_on_req
 * attempts to start the IO and waits for completion
 * FIXME: replace handmade sleeping by wait_event
 */
int
dasd_sleep_on_req (ccw_req_t * req)
{
    unsigned long flags;
    int cs;
    int rc = 0;
    dasd_device_t *device = (dasd_device_t *) req->device;

        if ( signal_pending(current) ) {
                return -ERESTARTSYS;
        }
    s390irq_spin_lock_irqsave (device->devinfo.irq, flags);
    dasd_chanq_enq (&device->queue, req);
    /* let the bh start the request to keep them in order */
    dasd_schedule_bh (device);
    do {
        s390irq_spin_unlock_irqrestore (device->devinfo.irq, flags);
        wait_event ( device->wait_q,
                 (((cs = req->status) == CQR_STATUS_DONE) ||
                 (cs == CQR_STATUS_FAILED) ||
                             signal_pending(current)));
        s390irq_spin_lock_irqsave (device->devinfo.irq, flags);
                if ( signal_pending(current) ) {
                        rc = -ERESTARTSYS;
                 if (req->status == CQR_STATUS_IN_IO ) 
                            device->discipline->term_IO(req);
                        break;
                } else if ( req->status == CQR_STATUS_FAILED) {
                        rc = -EIO;
                        break;
                }
    } while (cs != CQR_STATUS_DONE && cs != CQR_STATUS_FAILED);
    s390irq_spin_unlock_irqrestore (device->devinfo.irq, flags);
    return rc;
}                /* end dasd_sleep_on_req */

/*
 * function dasd_end_request
 * posts the buffer_cache about a finalized request
 * FIXME: for requests splitted to serveral cqrs
 */
static inline void
dasd_end_request (struct request *req, int uptodate)
{
    while (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);
    return;
}

/*
 * function dasd_get_queue
 * returns the queue corresponding to a device behind a kdev
 */
static request_queue_t *
dasd_get_queue (kdev_t kdev)
{
    dasd_device_t *device = dasd_device_from_kdev (kdev);

    if (!device) {
        return NULL;
    }

    return device->request_queue;
}

/*
 * function dasd_check_expire_time
 * check the request given as argument for expiration
 * and returns 0 if not yet expired, EIO else
 */
static inline int
dasd_check_expire_time (ccw_req_t * cqr)
{
    unsigned long long now;
    int rc = 0;

    asm volatile ("STCK %0":"=m" (now));
    if (cqr->expires && cqr->expires + cqr->startclk < now) {
        DASD_MESSAGE (KERN_ERR, ((dasd_device_t *) cqr->device),
                  "IO timeout 0x%08lx%08lx usecs in req %p\n",
                  (long) (cqr->expires >> 44),
                  (long) (cqr->expires >> 12), cqr);
        cqr->expires <<= 1;
                rc = -EIO;
    }
    return rc;
}

/*
 * function dasd_finalize_request
 * implemets the actions to perform, when a request is finally finished
 * namely in status CQR_STATUS_DONE || CQR_STATUS_FAILED
 */
static inline void
dasd_finalize_request (ccw_req_t * cqr)
{
    dasd_device_t *device = cqr->device;

    asm volatile ("STCK %0":"=m" (cqr->endclk));
    if (cqr->req) {
#ifdef DASD_PROFILE
        dasd_profile_add (cqr);
#endif
        dasd_end_request (cqr->req, (cqr->status == CQR_STATUS_DONE));
        /* free request if nobody is waiting on it */
        dasd_free_request (cqr, cqr->device);
    } else {
                if ( cqr == device->init_cqr && /* bring late devices online */
                     device->level <= DASD_STATE_ONLINE ) { 
                        device->timer.function = dasd_enable_single_device; 
                        device->timer.data     = (unsigned long) device;
                        device->timer.expires  = jiffies;
                        add_timer(&device->timer);
                }
        /* notify sleeping task about finished postprocessing */
        wake_up (&device->wait_q);
                
    }
    return;
}

/*
 * function dasd_process_queues
 * transfers the requests on the queue given as argument to the chanq
 * if possible, the request ist started on a fastpath
 */
static void
dasd_process_queues (dasd_device_t * device)
{
    unsigned long flags;
    struct request *req;
    request_queue_t *queue = device->request_queue;
    dasd_chanq_t *qp = &device->queue;
    int irq = device->devinfo.irq;
    ccw_req_t *final_requests = NULL;
    static int chanq_min_size = DASD_MIN_SIZE_FOR_QUEUE;
    int chanq_max_size = DASD_CHANQ_MAX_SIZE;
    ccw_req_t *cqr = NULL, *temp;
    dasd_erp_postaction_fn_t erp_postaction;


    s390irq_spin_lock_irqsave (irq, flags);

    /* First we dechain the requests, processed with completed status */
    while (qp->head &&
           ((qp->head->status == CQR_STATUS_DONE  ) ||
        (qp->head->status == CQR_STATUS_FAILED) ||
        (qp->head->status == CQR_STATUS_ERROR )   )) {

        dasd_erp_action_fn_t erp_action;
        ccw_req_t            *erp_cqr = NULL;

        /*  preprocess requests with CQR_STATUS_ERROR */
        if (qp->head->status == CQR_STATUS_ERROR) {

                        qp->head->retries--; 

            if (qp->head->dstat->flag & DEVSTAT_HALT_FUNCTION) {

                                check_then_set (&qp->head->status,
                                                CQR_STATUS_ERROR,
                                                CQR_STATUS_FAILED);

                                asm volatile ("STCK %0":"=m" (qp->head->stopclk));

                        } else if ((device->discipline->erp_action == NULL                          ) ||
                                   ((erp_action = device->discipline->erp_action (qp->head)) == NULL)   ) {
                                
                erp_cqr = dasd_default_erp_action (qp->head);

            } else { /* call discipline ERP action */

                                erp_cqr = erp_action (qp->head);
                        }
                        continue;

        } else if (qp->head->refers) {    /* we deal with a finished ERP */

            if (qp->head->status == CQR_STATUS_DONE) {

                                DASD_MESSAGE (KERN_DEBUG, device, "%s",
                                              "ERP successful");
            } else {

                                DASD_MESSAGE (KERN_ERR, device, "%s",
                                              "ERP unsuccessful");
            }

            if ((device->discipline->erp_postaction == NULL                              )||
                ((erp_postaction = device->discipline->erp_postaction (qp->head)) == NULL)  ) {

                                dasd_default_erp_postaction (qp->head);

            } else {  /* call ERP postaction of discipline */

                                erp_postaction (qp->head);
                        }

            continue;
        }

        /* dechain request now */
        if (final_requests == NULL)
            final_requests = qp->head;

        cqr      = qp->head;
        qp->head = qp->head->next;

        if (qp->head == NULL)
            qp->tail = NULL;

    } /* end while over completed requests */

    if (cqr)
        cqr->next = NULL;
    /* Now clean the requests with final status */
    while (final_requests) { 
        temp = final_requests;
        final_requests = temp->next;
        dasd_finalize_request (temp);
    }
    /* Now we try to fetch requests from the request queue */
    for (temp = cqr; temp != NULL; temp = temp->next)
        if (temp->status == CQR_STATUS_QUEUED)
            chanq_max_size--;
    while ((atomic_read(&device->plugged) == 0) &&
               (!queue->plugged) &&
           (!list_empty (&queue->queue_head)) &&
           (req = dasd_next_request (queue)) != NULL) {
        /* queue empty or certain critera fulfilled -> transfer */
        if (qp->head == NULL ||
            chanq_max_size > 0 || (req->nr_sectors >= chanq_min_size)) {
            ccw_req_t *cqr = NULL;
                        if (is_read_only(device->kdev) && req->cmd == WRITE) {

                                DASD_DRIVER_DEBUG_EVENT (3, dasd_int_handler,
                                                         "(%04x) Rejecting write request %p\n",
                                                         device->devinfo.devno,
                                                         req);

                                dasd_end_request (req, 0);
                                dasd_dequeue_request (queue,req);
                        } else {
                            /* relocate request according to partition table */
                            req->sector +=
                                device->major_info->gendisk.
                                part[MINOR (req->rq_dev)].start_sect;
                            cqr = device->discipline->build_cp_from_req (device, req);
                            if (cqr == NULL) {

                                    DASD_DRIVER_DEBUG_EVENT (3, dasd_int_handler,
                                                             "(%04x) CCW creation failed "
                                                             "on request %p\n",
                                                             device->devinfo.devno,
                                                             req);
                                    /* revert relocation of request */
                                    req->sector -=
                                        device->major_info->gendisk.
                                        part[MINOR (req->rq_dev)].start_sect;
                                    break;    /* terminate request queue loop */
                                    
                            }
#ifdef CONFIG_DYNAMIC_QUEUE_MIN_SIZE
                            chanq_min_size =
                                (chanq_min_size + req->nr_sectors) >> 1;
#endif                /* CONFIG_DYNAMIC_QUEUE_MIN_SIZE */
                            dasd_dequeue_request (queue, req);
                            dasd_chanq_enq (qp, cqr);
                        }
        } else {    /* queue not empty OR criteria not met */
            break;    /* terminate request queue loop */
        }
    }
    /* we process the requests with non-final status */
    if (qp->head) {
        switch (qp->head->status) {
        case CQR_STATUS_QUEUED:
            /* try to start the first I/O that can be started */
            if (device->discipline->start_IO == NULL)
                BUG ();
                        device->discipline->start_IO(qp->head);
            break;
        case CQR_STATUS_IN_IO:
            /* Check, if to invoke the missing interrupt handler */
            if (dasd_check_expire_time (qp->head)) {
                /* to be filled with MIH */
            }
            break;

        case CQR_STATUS_PENDING:
            /* just wait */
            break;
        default:
            BUG ();
        }
    }
    s390irq_spin_unlock_irqrestore (irq, flags);

} /* end dasd_process_queues */

/*
 * function dasd_run_bh
 * acquires the locks needed and then runs the bh
 */
static void
dasd_run_bh (dasd_device_t * device)
{
    long flags;
    spin_lock_irqsave (&io_request_lock, flags);
    atomic_set (&device->bh_scheduled, 0);
    dasd_process_queues (device);
    spin_unlock_irqrestore (&io_request_lock, flags);
}

/*
 * function dasd_schedule_bh
 * schedules the request_fn to run with next run_bh cycle
 */
void
dasd_schedule_bh (dasd_device_t * device)
{
    /* Protect against rescheduling, when already running */
    if (atomic_compare_and_swap (0, 1, &device->bh_scheduled)) {
        return;
    }

    INIT_LIST_HEAD (&device->bh_tq.list);
    device->bh_tq.sync = 0;
    device->bh_tq.routine = (void *) (void *) dasd_run_bh;
    device->bh_tq.data = device;

    queue_task (&device->bh_tq, &tq_immediate);
    mark_bh (IMMEDIATE_BH);
    return;
}

/*
 * function do_dasd_request
 * is called from ll_rw_blk.c and provides the caller of
 * dasd_process_queues
 */
static void
do_dasd_request (request_queue_t * queue)
{
        dasd_device_t *device = (dasd_device_t *)queue->queuedata;
    dasd_process_queues (device);
}

/*
 * DASD_HANDLE_STATE_CHANGE_PENDING
 *
 * DESCRIPTION
 *   Handles the state change pending interrupt.
 *   Search for the device related request queue and check if the first
 *   cqr in queue in in status 'CQR_STATUE_PENDING'.
 *   If so the status is set to 'CQR_STATUS_QUEUED' to reactivate
 *   the device.
 *
 *  PARAMETER
 *   stat               device status of state change pending interrupt.
 */
void
dasd_handle_state_change_pending (devstat_t * stat)
{
    dasd_device_t **device_addr;
    ccw_req_t *cqr;

    device_addr = dasd_device_from_devno (stat->devno);

    if (device_addr == NULL) {

        printk (KERN_DEBUG PRINTK_HEADER
            "unable to find device for state change pending "
            "interrupt: devno%04x\n", 
                        stat->devno);
                return;
    } 

        /* re-activate first request in queue */
        cqr = (*device_addr)->queue.head;
        
        if (cqr->status == CQR_STATUS_PENDING) {
                
                DASD_MESSAGE (KERN_DEBUG, (*device_addr), "%s",
                              "device request queue restarted by "
                              "state change pending interrupt\n");
                
                del_timer (&(*device_addr)->timer);
                
                check_then_set (&cqr->status,
                                CQR_STATUS_PENDING, CQR_STATUS_QUEUED);
                
                dasd_schedule_bh (*device_addr);
                
        }

} /* end dasd_handle_state_change_pending */

/*
 * function dasd_int_handler
 * is the DASD driver's default interrupt handler for SSCH-IO
 */
void
dasd_int_handler (int irq, void *ds, struct pt_regs *regs)
{
    int ip;
    ccw_req_t *cqr;
    dasd_device_t *device;
        unsigned long long now;
    dasd_era_t era = dasd_era_none; /* default is everything is okay */
    devstat_t *stat = (devstat_t *)ds;

        if (stat == NULL) {
                BUG();
    }
        DASD_DRIVER_DEBUG_EVENT (6, dasd_int_handler,
                                 "Interrupt: IRQ %02x, stat %02x, devno %04x",
                                 irq,
                                 stat->dstat,
                                 stat->devno);
        asm volatile ("STCK %0":"=m" (now));

        /* first of all check for state change pending interrupt */
        if ((stat->dstat & DEV_STAT_ATTENTION ) && 
            (stat->dstat & DEV_STAT_DEV_END   ) &&
            (stat->dstat & DEV_STAT_UNIT_EXCEP)   ) {
                DASD_DRIVER_DEBUG_EVENT (2, dasd_int_handler,
                                         "State change Interrupt: %04x",
                                         stat->devno);
                dasd_handle_state_change_pending (stat);
                return;
        }

    ip = stat->intparm;
    if (!ip) {        /* no intparm: unsolicited interrupt */
                DASD_DRIVER_DEBUG_EVENT (2, dasd_int_handler,
                                         "Unsolicited Interrupt: %04x",
                                         stat->devno);
        printk (KERN_DEBUG PRINTK_HEADER
                        "unsolicited interrupt: irq 0x%x devno %04x\n",
                        irq,
                        stat->devno);
        return;
    }
    if (ip & 0x80000001) {
                DASD_DRIVER_DEBUG_EVENT (2, dasd_int_handler,
                                         "spurious Interrupt: %04x",
                                         stat->devno);
        printk (KERN_DEBUG PRINTK_HEADER
                        "spurious interrupt: irq 0x%x devno %04x, parm %08x\n",
                        irq,
                        stat->devno,ip);
        return;
    }

    cqr = (ccw_req_t *)(long)ip;

        /* check status - the request might have been killed because of dyn dettach */
    if (cqr->status != CQR_STATUS_IN_IO) {
                DASD_DRIVER_DEBUG_EVENT (2, dasd_int_handler,
                                         "invalid status %02x on device %04x",
                                         cqr->status,
                                         stat->devno);

        printk (KERN_DEBUG PRINTK_HEADER
                        "invalid status: irq 0x%x devno %04x, status %02x\n",
                        irq,
                        stat->devno,
                        cqr->status);
        return;
    }

    device = (dasd_device_t *) cqr->device;
    if (device == NULL || 
            device != ds-offsetof(dasd_device_t,dev_status)) {
                BUG();
    }
    if (device->devinfo.irq != irq) {
                BUG();
    }
    if (strncmp (device->discipline->ebcname, (char *) &cqr->magic, 4)) {
                BUG();
    }

        /* first of all lets try to find out the appropriate era_action */
        DASD_DEVICE_DEBUG_EVENT (4, device," Int: CS/DS 0x%04x",
                                 ((stat->cstat<<8)|stat->dstat));

    /* first of all lets try to find out the appropriate era_action */
    if (stat->flag & DEVSTAT_FLAG_SENSE_AVAIL ||
        stat->dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) {
        /* anything abnormal ? */
        if (device->discipline->examine_error == NULL ||
            stat->flag & DEVSTAT_HALT_FUNCTION) {
            era = dasd_era_fatal;
        } else {
            era = device->discipline->examine_error (cqr, stat);
        }
                DASD_DRIVER_DEBUG_EVENT (1, dasd_int_handler," era_code %d",
                                         era);
    }
        if ( era == dasd_era_none ) {
                check_then_set(&cqr->status, 
                               CQR_STATUS_IN_IO, 
                               CQR_STATUS_DONE);

                cqr->stopclk=now;
        /* start the next queued request if possible -> fast_io */
                if (cqr->next &&
                    cqr->next->status == CQR_STATUS_QUEUED) {
                        if (device->discipline->start_IO (cqr->next) != 0) {
                                printk (KERN_WARNING PRINTK_HEADER
                                        "Interrupt fastpath failed!\n");
                        } 
                }
        } else { /* error */
        if (cqr->dstat == NULL)
            cqr->dstat = kmalloc (sizeof (devstat_t), GFP_ATOMIC);
        if (cqr->dstat) {
            memcpy (cqr->dstat, stat, sizeof (devstat_t));
        } else {
            PRINT_ERR ("no memory for dstat...ignoring\n");
        }

#ifdef ERP_DEBUG
        /* dump sense data */
        if (device->discipline            && 
                    device->discipline->dump_sense  ) {

                        device->discipline->dump_sense (device, 
                                                        cqr);
        }
#endif

        switch (era) {
        case dasd_era_fatal:
            check_then_set (&cqr->status, 
                                        CQR_STATUS_IN_IO,
                    CQR_STATUS_FAILED);

                        cqr->stopclk = now;
            break;
        case dasd_era_recover:
            check_then_set (&cqr->status, 
                                        CQR_STATUS_IN_IO,
                    CQR_STATUS_ERROR);
            break;
        default:
            BUG ();
        }
    }
        if ( cqr == device->init_cqr &&
             ( cqr->status == CQR_STATUS_DONE ||
               cqr->status == CQR_STATUS_FAILED )){
                dasd_state_init_to_ready(device);
                if ( atomic_read(&dasd_init_pending) == 0)
                        wake_up (&dasd_init_waitq);
        }
    dasd_schedule_bh (device);

} /* end dasd_int_handler */

/* SECTION: Some stuff related to error recovery */

/*
 * DEFAULT_ERP_ACTION
 *
 * DESCRIPTION
 *   sets up the default-ERP ccw_req_t, namely one, which performs a TIC
 *   to the original channel program with a retry counter of 16
 *
 * PARAMETER
 *   cqr                failed CQR
 *
 * RETURN VALUES
 *   erp                CQR performing the ERP
 */
ccw_req_t *
dasd_default_erp_action (ccw_req_t * cqr)
{

        dasd_device_t *device = cqr->device;
    ccw_req_t     *erp    = dasd_alloc_request ((char *) &cqr->magic, 1, 0, cqr->device);

    printk (KERN_DEBUG PRINTK_HEADER "Default ERP called... \n");

    if (!erp) {

                DASD_MESSAGE (KERN_ERR, device, "%s",
                              "Unable to allocate ERP request");
                
                check_then_set (&cqr->status,
                                CQR_STATUS_ERROR,
                                CQR_STATUS_FAILED);

                asm volatile ("STCK %0":"=m" (cqr->stopclk));

                return cqr;
    }

    erp->cpaddr->cmd_code = CCW_CMD_TIC;
    erp->cpaddr->cda = (__u32) (addr_t) cqr->cpaddr;
    erp->function = dasd_default_erp_action;
    erp->refers = cqr;
    erp->device = cqr->device;
    erp->magic = cqr->magic;
    erp->retries = 16;

    erp->status = CQR_STATUS_FILLED;

        dasd_chanq_enq_head (&device->queue,
                             erp);

    return erp;

} /* end dasd_default_erp_action */

/*
 * DEFAULT_ERP_POSTACTION
 *
 * DESCRIPTION
 *   Frees all ERPs of the current ERP Chain and set the status
 *   of the original CQR either to CQR_STATUS_DONE if ERP was successful
 *   or to CQR_STATUS_FAILED if ERP was NOT successful.
 *   NOTE: This function is only called if no discipline postaction
 *         is available
 *
 * PARAMETER
 *   erp                current erp_head
 *
 * RETURN VALUES
 *   cqr                pointer to the original CQR
 */
ccw_req_t *
dasd_default_erp_postaction (ccw_req_t *erp)
{

    ccw_req_t     *cqr      = NULL, 
                      *free_erp = NULL;
    dasd_device_t *device   = erp->device;
    int           success;

    if (erp->refers   == NULL || 
            erp->function == NULL   ) {

        BUG ();
    }

    if (erp->status == CQR_STATUS_DONE)
        success = 1;
    else
        success = 0;

    /* free all ERPs - but NOT the original cqr */
    while (erp->refers != NULL) {

        free_erp = erp;
        erp      = erp->refers;

        /* remove the request from the device queue */
        dasd_chanq_deq (&device->queue,
                                free_erp);

        /* free the finished erp request */
        dasd_free_request (free_erp, free_erp->device);
    }

    /* save ptr to original cqr */
    cqr = erp;

    /* set corresponding status to original cqr */
    if (success) {

        check_then_set (&cqr->status, 
                                CQR_STATUS_ERROR,
                CQR_STATUS_DONE);
    } else {

        check_then_set (&cqr->status,
                CQR_STATUS_ERROR, 
                                CQR_STATUS_FAILED);

                asm volatile ("STCK %0":"=m" (cqr->stopclk));
    }

    return cqr;

} /* end default_erp_postaction */

/* SECTION: The helpers of the struct file_operations */

/*
 * function dasd_format
 * performs formatting of _device_ according to _fdata_
 * Note: The discipline's format_function is assumed to deliver formatting
 * commands to format a single unit of the device. In terms of the ECKD
 * devices this means CCWs are generated to format a single track.
 */

static int
dasd_format (dasd_device_t * device, format_data_t * fdata)
{
    int rc = 0;
    int openct = atomic_read (&device->open_count);

    if (openct > 1) {
        DASD_MESSAGE (KERN_WARNING, device, "%s",
                  "dasd_format: device is open! expect errors.");
    }
    DASD_MESSAGE (KERN_INFO, device,
              "formatting units %d to %d (%d B blocks) flags %d",
              fdata->start_unit, 
                      fdata->stop_unit,
              fdata->blksize, 
                      fdata->intensity);
    while ((!rc) && (fdata->start_unit <= fdata->stop_unit)) {
                ccw_req_t *req;
                dasd_format_fn_t ffn = device->discipline->format_device;
        ffn = device->discipline->format_device;
        if (ffn == NULL)
            break;
        req = ffn (device, fdata);
        if (req == NULL) {
            rc = -ENOMEM;
            break;
        }
        if ((rc = dasd_sleep_on_req (req)) != 0) {
            DASD_MESSAGE (KERN_WARNING, device,
                      " Formatting of unit %d failed with rc = %d\n",
                      fdata->start_unit, rc);
            break;
        } 
        dasd_free_request (req, device);    /* request is no longer used */
            if ( signal_pending(current) ) {
            rc = -ERESTARTSYS;
            break;        
                }
        fdata->start_unit++;
    }
    return rc;
}                /* end dasd_format */

static struct list_head dasd_ioctls = LIST_HEAD_INIT (dasd_ioctls);

static dasd_ioctl_list_t *
dasd_find_ioctl (int no)
{
    struct list_head *curr;
    list_for_each (curr, &dasd_ioctls) {
        if (list_entry (curr, dasd_ioctl_list_t, list)->no == no) {
            return list_entry (curr, dasd_ioctl_list_t, list);
        }
    }
    return NULL;
}

int
dasd_ioctl_no_register (struct module *owner, int no, dasd_ioctl_fn_t handler)
{
    dasd_ioctl_list_t *new;
    if (dasd_find_ioctl (no))
        return -EBUSY;
    new = kmalloc (sizeof (dasd_ioctl_list_t), GFP_KERNEL);
    if (new == NULL)
        return -ENOMEM;
    new->owner = owner;
    new->no = no;
    new->handler = handler;
    list_add (&new->list, &dasd_ioctls);
    MOD_INC_USE_COUNT;
    return 0;
}

int
dasd_ioctl_no_unregister (struct module *owner, int no, dasd_ioctl_fn_t handler)
{
    dasd_ioctl_list_t *old = dasd_find_ioctl (no);
    if (old == NULL)
        return -ENOENT;
    if (old->no != no || old->handler != handler || owner != old->owner )
        return -EINVAL;
    list_del (&old->list);
    kfree (old);
    MOD_DEC_USE_COUNT;
    return 0;
}

static int
dasd_revalidate (dasd_device_t * device)
{
        int rc = 0;
    int i;
    kdev_t kdev = device->kdev;
    int openct = atomic_read (&device->open_count);
    int start = MINOR (kdev);
    if (openct != 1) {
        DASD_MESSAGE (KERN_WARNING, device, "%s",
                  "BLKRRPART: device is open! expect errors.");
    }
    for (i = (1 << DASD_PARTN_BITS) - 1; i >= 0; i--) {
                int major = device->major_info->gendisk.major;
        invalidate_device(MKDEV (major, start+i), 1);
    }
        dasd_destroy_partitions(device);
        dasd_setup_partitions(device);
        return rc;

}
static int
do_dasd_ioctl (struct inode *inp, /* unsigned */ int no, unsigned long data)
{
    int rc = 0;
    dasd_device_t *device = dasd_device_from_kdev (inp->i_rdev);
    major_info_t *major_info;

    if (!device) {
        printk (KERN_WARNING PRINTK_HEADER
            "No device registered as device (%d:%d)\n",
            MAJOR (inp->i_rdev), 
                        MINOR (inp->i_rdev));
        return -EINVAL;
    }
    if ((_IOC_DIR (no) != _IOC_NONE) && (data == 0)) {
        PRINT_DEBUG ("empty data ptr");
        return -EINVAL;
    }
    major_info = device->major_info;
#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 DASDAPIVER: {
            int ver = DASD_API_VERSION;
            rc = put_user(ver, (int *) data);
            break;
        }
    case BLKGETSIZE:{    /* Return device size */
            long blocks = major_info->gendisk.sizes 
                                      [MINOR (inp->i_rdev)] << 1;
            rc = put_user(blocks, (long *) data);
            break;
        }
    case BLKGETSIZE64:{
            u64 blocks = major_info->gendisk.sizes 
                                      [MINOR (inp->i_rdev)];
            rc = put_user(blocks << 10, (u64 *) data);
            break;
        }
    case BLKRRPART:{
            if (!capable (CAP_SYS_ADMIN)) {
                rc = -EACCES;
                break;
            }
            rc = dasd_revalidate (device);
            break;
        }
    case HDIO_GETGEO:{
            struct hd_geometry geo = { 0, };
            rc = dasd_fillgeo (inp->i_rdev, &geo);
            if (rc)
                break;

            rc = copy_to_user ((struct hd_geometry *) data, &geo,
                       sizeof (struct hd_geometry));
            if (rc)
                rc = -EFAULT;
            break;
        }
    case BIODASDDISABLE:{
            if (!capable (CAP_SYS_ADMIN)) {
                rc = -EACCES;
                break;
            }
                        if ( device->level > DASD_STATE_ACCEPT) {
                                dasd_deactivate_queue(device);
                                if ( device->request_queue)
                                        dasd_flush_request_queues(device,0);
                                dasd_flush_chanq(device,0);
                                dasd_disable_blkdev(device);
                                dasd_set_device_level (device->devinfo.devno, 
                                                       device->discipline, 
                                                       DASD_STATE_ACCEPT);
                        }
                        break;
        }
    case BIODASDENABLE:{
                        dasd_range_t range = { 
                                from: device->devinfo.devno,
                                to: device->devinfo.devno 
                        };
            if (!capable (CAP_SYS_ADMIN)) {
                rc = -EACCES;
                break;
            }
                        dasd_enable_ranges (&range, device->discipline, 0);
                        break;
        }
    case BIODASDFMT:{
            /* fdata == NULL is no longer a valid arg to dasd_format ! */
            int partn = MINOR (inp->i_rdev) &
                ((1 << major_info->gendisk.minor_shift) - 1);
            format_data_t fdata;

            if (!capable (CAP_SYS_ADMIN)) {
                rc = -EACCES;
                break;
            }
                        if (dasd_features_from_devno(device->devinfo.devno)&DASD_FEATURE_READONLY) {
                                rc = -EROFS;
                                break;
                        }
            if (!data) {
                rc = -EINVAL;
                break;
            }
            rc = copy_from_user (&fdata, (void *) data,
                         sizeof (format_data_t));
            if (rc) {
                rc = -EFAULT;
                break;
            }
            if (partn != 0) {
                DASD_MESSAGE (KERN_WARNING, device, "%s",
                          "Cannot low-level format a partition");
                return -EINVAL;
            }
            rc = dasd_format (device, &fdata);
            break;
        }
    case BIODASDPRRST:{     /* reset device profile information */
            if (!capable (CAP_SYS_ADMIN)) {
                rc = -EACCES;
                break;
            }
            memset (&device->profile, 0,
                sizeof (dasd_profile_info_t));
            break;
        }
    case BIODASDPRRD:{      /* retrun device profile information */
            rc = copy_to_user((long *)data,
                      (long *)&device->profile,
                      sizeof(dasd_profile_info_t));
            if (rc)
                rc = -EFAULT;
            break;
        }
    case BIODASDRSRV:{      /* reserve */
            ccw_req_t *req;
            if (!capable (CAP_SYS_ADMIN)) {
                rc = -EACCES;
                break;
            }
            req = device->discipline->reserve (device);
            rc = dasd_sleep_on_req (req);
            dasd_free_request (req, device);
            break;
        }
    case BIODASDRLSE:{      /* release */
            ccw_req_t *req;
            if (!capable (CAP_SYS_ADMIN)) {
                rc = -EACCES;
                break;
            }
            req = device->discipline->release (device);
            rc = dasd_sleep_on_req (req);
            dasd_free_request (req, device);
            break;
        }
    case BIODASDSLCK:{      /* steal lock - unconditional reserve */
            ccw_req_t *req;
            if (!capable (CAP_SYS_ADMIN)) {
                rc = -EACCES;
                break;
            }
            req = device->discipline->steal_lock (device);
            rc = dasd_sleep_on_req (req);
            dasd_free_request (req, device);
            break;
        }
    case BIODASDINFO:{
            dasd_information_t dasd_info;
            unsigned long flags;
            rc = device->discipline->fill_info (device, &dasd_info);
                        dasd_info.label_block = device->sizes.pt_block;
            dasd_info.devno = device->devinfo.devno;
            dasd_info.schid = device->devinfo.irq;
            dasd_info.cu_type = device->devinfo.sid_data.cu_type;
            dasd_info.cu_model = device->devinfo.sid_data.cu_model;
            dasd_info.dev_type = device->devinfo.sid_data.dev_type;
            dasd_info.dev_model = device->devinfo.sid_data.dev_model;
            dasd_info.open_count =
                atomic_read (&device->open_count);
            dasd_info.status = device->level;
            if (device->discipline) {
                memcpy (dasd_info.type,
                    device->discipline->name, 4);
            } else {
                memcpy (dasd_info.type, "none", 4);
            }
            dasd_info.req_queue_len = 0;
            dasd_info.chanq_len = 0;
            if (device->request_queue->request_fn) {
                struct list_head *l;
                ccw_req_t *cqr = device->queue.head;
                spin_lock_irqsave (&io_request_lock, flags);
                list_for_each (l,
                           &device->request_queue->
                           queue_head) {
                    dasd_info.req_queue_len++;
                }
                spin_unlock_irqrestore (&io_request_lock,
                            flags);
                s390irq_spin_lock_irqsave (device->devinfo.irq,
                               flags);
                while (cqr) {
                    cqr = cqr->next;
                    dasd_info.chanq_len++;
                }
                s390irq_spin_unlock_irqrestore (device->devinfo.
                                irq, flags);
            }
            rc =
                copy_to_user ((long *) data, (long *) &dasd_info,
                      sizeof (dasd_information_t));
            if (rc)
                rc = -EFAULT;
            break;
        }
#if 0 /* needed for XFS */
    case BLKBSZSET:{
        int bsz;
        rc = copy_from_user ((long *)&bsz,(long *)data,sizeof(int));
        if ( rc ) {
            rc = -EFAULT;
        } else {
            if ( bsz >= device->sizes.bp_block )
                rc = blk_ioctl (inp->i_rdev, no, data);
            else
                rc = -EINVAL; 
        }
        break;
        }
#endif /* 0 */
    case BLKSSZGET:
    case BLKROSET:
    case BLKROGET:
    case BLKRASET:
    case BLKRAGET:
    case BLKFLSBUF:
    case BLKPG:
    case BLKELVGET:
    case BLKELVSET:
        return blk_ioctl (inp->i_rdev, no, data);
        break;
    default:{

            dasd_ioctl_list_t *old = dasd_find_ioctl (no);
            if (old) {
                if ( old->owner )
                    __MOD_INC_USE_COUNT(old->owner);
                rc = old->handler (inp, no, data);
                if ( old->owner )
                    __MOD_DEC_USE_COUNT(old->owner);
            } else {
                DASD_MESSAGE (KERN_INFO, device,
                          "ioctl 0x%08x=%s'0x%x'%d(%d) data %8lx\n",
                          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),
                          data);
                rc = -ENOTTY;
            }
            break;
        }
    }
    return rc;
}

/* 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;
    if ((!inp) || !(inp->i_rdev)) {
        return -EINVAL;
    }
    rc = do_dasd_ioctl (inp, no, data);
    return rc;
}

static int
dasd_open (struct inode *inp, struct file *filp)
{
    int rc = 0;
        unsigned long flags;
    dasd_device_t *device;

    if ((!inp) || !(inp->i_rdev)) {
        rc = -EINVAL;
                goto fail;
    }
    if (dasd_probeonly) {
        printk ("\n" KERN_INFO PRINTK_HEADER
            "No access to device (%d:%d) due to probeonly mode\n",
            MAJOR (inp->i_rdev), 
                        MINOR (inp->i_rdev));
        rc = -EPERM;
                goto fail;
    }
        spin_lock_irqsave(&discipline_lock,flags);
    device = dasd_device_from_kdev (inp->i_rdev);
    if (!device) {
        printk (KERN_WARNING PRINTK_HEADER
            "No device registered as (%d:%d)\n",
            MAJOR (inp->i_rdev), 
                        MINOR (inp->i_rdev));
        rc = -ENODEV;
                goto unlock;
    }
    if (device->level <= DASD_STATE_ACCEPT ) {
        DASD_MESSAGE (KERN_WARNING, device, " %s", 
                              " Cannot open unrecognized device\n");
        rc = -ENODEV;
                goto unlock;
    }
    if (atomic_inc_return (&device->open_count) == 1 ) {
                if ( device->discipline->owner )
                        __MOD_INC_USE_COUNT(device->discipline->owner);
        }
 unlock:
        spin_unlock_irqrestore(&discipline_lock,flags);
 fail:
    return rc;
}

/*
 * DASD_RELEASE
 *
 * DESCRIPTION
 */
static int
dasd_release (struct inode *inp, struct file *filp)
{
    int rc = 0;
        int count;
    dasd_device_t *device;

    if ((!inp) || !(inp->i_rdev)) {
        rc = -EINVAL;
                goto out;
    }
    device = dasd_device_from_kdev (inp->i_rdev);
    if (!device) {
        printk (KERN_WARNING PRINTK_HEADER
            "No device registered as %d:%d\n",
            MAJOR (inp->i_rdev), 
                        MINOR (inp->i_rdev));
        rc = -EINVAL;
                goto out;
    }

    if (device->level < DASD_STATE_ACCEPT ) {
        DASD_MESSAGE (KERN_WARNING, device, " %s",
                              " Cannot release unrecognized device\n");
        rc = -ENODEV;
                goto out;
    }
        count = atomic_dec_return (&device->open_count);
        if ( count == 0) {
                invalidate_buffers (inp->i_rdev);
                if ( device->discipline->owner )
                        __MOD_DEC_USE_COUNT(device->discipline->owner);
    } else if ( count == -1 ) { /* paranoia only */
                atomic_set (&device->open_count,0);
                printk (KERN_WARNING PRINTK_HEADER
                        "release called with open count==0\n");
        }
 out:
    return rc;
}

static struct
block_device_operations dasd_device_operations =
{
    owner:THIS_MODULE,
    open:dasd_open,
    release:dasd_release,
    ioctl:dasd_ioctl,
};

/* SECTION: Management of device list */
int
dasd_fillgeo(int kdev,struct hd_geometry *geo)
{
    dasd_device_t *device = dasd_device_from_kdev (kdev);

    if (!device)
                return -EINVAL;

        if (!device->discipline->fill_geometry)
        return -EINVAL;

    device->discipline->fill_geometry (device, geo);
    geo->start = device->major_info->gendisk.part[MINOR(kdev)].start_sect 
             >> device->sizes.s2b_shift;;
        return 0;



/* 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) {
        major_info_t *major_info = NULL;
        struct list_head *l;

        list_for_each (l, &dasd_major_info[0].list) {
            major_info = list_entry (l, major_info_t, list);
            if (&major_info->gendisk == hd) {
                break;
            }
            index += DASD_PER_MAJOR;
        }
        if (major_info == &dasd_major_info[0]) {
            return -EINVAL;
        }
    }
    third = index % 26;
    second = ((index - 26) / 26) % 26;
    first = (((index - 702) / 26) / 26) % 26;

    len = sprintf (str, "dasd");
    if (index > 701) {
        len += sprintf (str + len, "%c", first + 'a');
    }
    if (index > 25) {
        len += sprintf (str + len, "%c", second + 'a');
    }
    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_plug_device (dasd_device_t * device)
{
    atomic_set(&device->plugged,1);    
}

static void
dasd_unplug_device (dasd_device_t * device)
{
    atomic_set(&device->plugged,0);    
        dasd_schedule_bh(device);
}

static void
dasd_flush_chanq ( dasd_device_t * device, int destroy ) 
{
        ccw_req_t *cqr;
        unsigned long flags;
        if ( destroy ) {
                s390irq_spin_lock_irqsave (device->devinfo.irq, flags);
                cqr = device->queue.head;
                while ( cqr != NULL ) {
                        if ( cqr->status == CQR_STATUS_IN_IO )
                                device->discipline->term_IO (cqr);
                        if ( cqr->status != CQR_STATUS_DONE ||
                             cqr->status != CQR_STATUS_FAILED ) {

                                cqr->status = CQR_STATUS_FAILED;
                                asm volatile ("STCK %0":"=m" (cqr->stopclk));

                        }
                        dasd_schedule_bh(device);
                        cqr = cqr->next;
                }
                s390irq_spin_unlock_irqrestore (device->devinfo.irq, flags);
        }
        wait_event( device->wait_q, device->queue.head == NULL );
}

static void
dasd_flush_request_queues ( dasd_device_t * device, int destroy )
{
        int i;
        int major = MAJOR(device->kdev);
        int minor = MINOR(device->kdev);
        for ( i = 0; i < (1 << DASD_PARTN_BITS); i ++) {
                if ( destroy )
                        destroy_buffers(MKDEV(major,minor+i)); 
                else
                        invalidate_buffers(MKDEV(major,minor+i)); 
        } 
}

static int
dasd_disable_volume ( dasd_device_t * device, int force ) 
{
        int rc = 0;
        int target  = DASD_STATE_KNOWN;
        int count = atomic_read (&device->open_count);
        
    if ( count ) {
        DASD_MESSAGE (KERN_EMERG, device, "%s",
                  "device has vanished although it was open!");
        }
        if ( force ) {
                dasd_deactivate_queue(device);
                dasd_flush_chanq(device,force);
                dasd_flush_request_queues(device,force);
                dasd_disable_blkdev(device);
                target = DASD_STATE_DEL;
        }

        /* unregister partitions ('ungrok_partitions') */
        devfs_register_partitions(&device->major_info->gendisk,
                                  MINOR(device->kdev),1);
        
        DASD_MESSAGE (KERN_WARNING, device, 
                      "disabling device, target state: %d",target);

        dasd_set_device_level (device->devinfo.devno, 
                               device->discipline, 
                               target);
        return rc;
}

static void
dasd_disable_ranges (dasd_range_t *range, 
                     dasd_discipline_t *d,
                     int all, int force ) 
{
        dasd_range_t *rrange;
        int j;

        if (range == &dasd_range_head) {
                rrange = list_entry (range->list.next, 
                                     dasd_range_t, list);
        } else {
                rrange = range;
        }
        do {
                for (j = rrange->from; j <= rrange->to; j++) {
                        dasd_device_t **dptr;
                        dasd_device_t *device;
                        dptr = dasd_device_from_devno(j);
                        if ( dptr == NULL ) {
                            continue;
                        }
                        device = *dptr;
                        if (device == NULL ||
                            (d != NULL &&
                             device -> discipline != d))
                                continue;
                        
                        dasd_disable_volume(device, force);
                }
                rrange = list_entry (rrange->list.next, dasd_range_t, list);
        } while ( all && rrange && rrange != range );
}

static void 
dasd_enable_single_device ( unsigned long arg ) {
        dasd_device_t * device =(dasd_device_t *) arg;
        int devno = device->devinfo.devno;
        dasd_range_t range = { from: devno, to:devno };
        dasd_enable_ranges (&range,NULL,0);
}

static void
dasd_enable_ranges (dasd_range_t *range, dasd_discipline_t *d, int all ) 
{
        int retries = 0;
    int j;
        kdev_t tempdev;
    dasd_range_t *rrange;

    if (range == NULL)
        return;
        
        do {
                if (range == &dasd_range_head) {
                        rrange = list_entry (range->list.next, 
                                             dasd_range_t, list);
                } else {
                        rrange = range;
                }
                do {
                        for (j = rrange->from; j <= rrange->to; j++) {
                                if ( dasd_devindex_from_devno(j) < 0 )
                                        continue;
                                dasd_set_device_level (j, d, DASD_STATE_ONLINE);
                        }
                        rrange = list_entry (rrange->list.next, dasd_range_t, list);
                } while ( all && rrange && rrange != range );

                if (atomic_read (&dasd_init_pending) == 0) /* we are done, exit loop */
                        break;

                if ( retries == 0 ) {
                        printk (KERN_INFO PRINTK_HEADER
                                "waiting for responses...\n");
                } else if ( retries < 5 ) {
                        printk (KERN_INFO PRINTK_HEADER
                                "waiting a little bit longer...\n");
                } else {
                        printk (KERN_INFO PRINTK_HEADER
                                "giving up, enable late devices manually!\n");
                        break;
                }
                interruptible_sleep_on_timeout (&dasd_init_waitq, (1 * HZ));
                retries ++;
        } while (1);
        /* now setup block devices */

        /* Now do block device and partition setup */
        if (range == &dasd_range_head) {
                rrange = list_entry (range->list.next, 
                                     dasd_range_t, list);
        } else {
                rrange = range;
        }
        do {
                for (j = rrange->from; j <= rrange->to; j++) {
                        dasd_device_t **dptr;
                        dasd_device_t *device;
                        if ( dasd_devindex_from_devno(j) < 0 )
                                continue;
                        dptr = dasd_device_from_devno(j);
                        device = *dptr;
                        if (device == NULL )
                                continue;
                        if ( ((d == NULL && device->discipline != NULL) ||
                              (device->discipline == d )) &&
                             device->level >= DASD_STATE_READY &&
                             device->request_queue == NULL ) {
                                if (dasd_features_from_devno(j)&DASD_FEATURE_READONLY) {
                                        for (tempdev=device->kdev;
                                             tempdev<(device->kdev +(1 << DASD_PARTN_BITS));
                                             tempdev++)
                                                set_device_ro (tempdev, 1);

                                        printk (KERN_WARNING PRINTK_HEADER 
                                                "setting read-only mode for device /dev/%s\n",
                                                device->name);
                                }
                                dasd_setup_blkdev(device);
                                dasd_setup_partitions(device);
                        }
                }
                rrange = list_entry (rrange->list.next, dasd_range_t, list);
        } while ( all && rrange && rrange != range );
}

#ifdef CONFIG_DASD_DYNAMIC
/*
 * DASD_NOT_OPER_HANDLER
 *
 * DESCRIPTION
 *   handles leaving devices
 */
static void
dasd_not_oper_handler (int irq, int status)
{
    dasd_device_t *device = NULL;
    major_info_t *major_info = NULL;
    struct list_head *l;
    int i, devno = -ENODEV;

    /* find out devno of leaving device: CIO has already deleted this information ! */
    list_for_each (l, &dasd_major_info[0].list) {
        major_info = list_entry (l, major_info_t, list);
        for (i = 0; i < DASD_PER_MAJOR; i++) {
            device = major_info->dasd_device[i];
            if (device && device->devinfo.irq == irq) {
                devno = device->devinfo.devno;
                break;
            }
        }
        if (devno != -ENODEV)
            break;
    }

    DASD_DRIVER_DEBUG_EVENT (5, dasd_not_oper_handler, 
                                 "called for devno %04x", 
                                 devno);

    if (devno < 0) {
        printk (KERN_WARNING PRINTK_HEADER
            "not_oper_handler called on irq 0x%04x no devno!\n", 
                        irq);
        return;
    }
        dasd_disable_volume(device, 1);
}

/*
 * DASD_OPER_HANDLER
 *
 * DESCRIPTION
 *   called by the machine check handler to make an device operational
 */
int
dasd_oper_handler (int irq, devreg_t * devreg)
{
    int devno;
    int rc = 0;
    major_info_t *major_info = NULL;
        dasd_range_t *rptr,range;
        dasd_device_t *device = NULL;
    struct list_head *l;
        int i;

    devno = get_devno_by_irq (irq);
    if (devno == -ENODEV) {
        rc = -ENODEV;
                goto out;
    }

    DASD_DRIVER_DEBUG_EVENT (5, dasd_oper_handler, 
                                 "called for devno %04x", 
                                 devno);

    /* find out devno of device */
    list_for_each (l, &dasd_major_info[0].list) {
        major_info = list_entry (l, major_info_t, list);
        for (i = 0; i < DASD_PER_MAJOR; i++) {
            device = major_info->dasd_device[i];
            if (device && device->devinfo.irq == irq) {
                devno = device->devinfo.devno;
                break;
            }
        }
        if (devno != -ENODEV)
            break;
    }
    if (devno < 0) {
                BUG();
    }
        if ( device &&
             device->level == DASD_STATE_READY ) {
            dasd_set_device_level (device->devinfo.devno, 
                                   device->discipline, DASD_STATE_ONLINE);

        } else {
            if (dasd_autodetect) {
        rptr = dasd_add_range (devno, devno, DASD_DEFAULT_FEATURES);
                if ( rptr == NULL ) {
                    rc = -ENOMEM;
                    goto out;
                }
            } else {
                range.from = devno;
                range.to = devno;
                rptr = &range;
            }
            dasd_enable_ranges (rptr, NULL, 0);
        }
 out:
    return rc;
}
#endif                /* CONFIG_DASD_DYNAMIC */

static inline dasd_device_t **
dasd_find_device_addr ( int devno ) 
{
        dasd_device_t **device_addr;

    DASD_DRIVER_DEBUG_EVENT (1, dasd_find_device_addr, 
                                 "devno %04x", 
                                 devno);
    if ( dasd_devindex_from_devno (devno) < 0 ) {
                DASD_DRIVER_DEBUG_EXCEPTION (1, dasd_find_device_addr, 
                                             "no dasd: devno %04x",
                                             devno);
        return NULL;
    }
        /* allocate major numbers on demand  for new devices */
    while ((device_addr = dasd_device_from_devno (devno)) == NULL) {
                int rc;

        if ((rc = dasd_register_major (NULL)) <= 0) {

                        DASD_DRIVER_DEBUG_EXCEPTION (1, dasd_find_device_addr, 
                                                     "%s",
                                                     "out of major numbers!");
                        break;
        }
    }
        return device_addr;
}

static inline int
dasd_state_del_to_new (dasd_device_t **addr ) 
{
        dasd_device_t* device;
        int rc = 0;
    if (*addr == NULL) { /* allocate device descriptor on demand for new device */
                device = kmalloc (sizeof (dasd_device_t), GFP_ATOMIC);
        if (device == NULL ) {
            rc = -ENOMEM;
                        goto out;
        }
        memset (device, 0, sizeof (dasd_device_t));
                *addr = device;
                device->lowmem_ccws = (void*)get_free_page (GFP_ATOMIC|GFP_DMA);
                if (device->lowmem_ccws == NULL) {
                        rc = -ENOMEM;
                        goto noccw;
    }
#ifdef CONFIG_ARCH_S390X
                device->lowmem_idals =
                    device->lowmem_idal_ptr = (void*) get_free_page (GFP_ATOMIC|GFP_DMA);
                if (device->lowmem_idals == NULL) {
                        rc = -ENOMEM;
                        goto noidal;
                }                
#endif
}
        goto out;
#ifdef CONFIG_ARCH_S390X
 noidal:
        free_page ((long) device->lowmem_ccws);
#endif
 noccw:
        kfree(device);
 out:
        return rc;
}

static inline int
dasd_state_new_to_del (dasd_device_t **addr )
{
        dasd_device_t *device = *addr;
        if (device && device->private) {
                kfree(device->private);
                device->private = NULL;
        }
#ifdef CONFIG_ARCH_S390X
        free_page ((long)(device->lowmem_idals));
#endif
        free_page((long)(device->lowmem_ccws));
        kfree(device);
        *addr = NULL; 
        return 0;
}

static inline int
dasd_state_new_to_known (dasd_device_t **dptr, int devno, dasd_discipline_t *disc) 
{
        int rc = 0;
    umode_t devfs_perm  = S_IFBLK | S_IRUSR | S_IWUSR;
        struct list_head *l;
        major_info_t *major_info = NULL;
        int i;
        dasd_device_t *device = *dptr;
        devfs_handle_t dir;
        char buffer[5];
        

    list_for_each (l, &dasd_major_info[0].list) {
                major_info = list_entry (l, major_info_t, list);
        for (i = 0; i < DASD_PER_MAJOR; i++) {
            if (major_info->dasd_device[i] == device) {
                device->kdev = MKDEV (major_info->gendisk.major,
                                                      i << DASD_PARTN_BITS);
                break;
            }
        }
        if (i < DASD_PER_MAJOR) /* we found one */
            break;
    }
        if ( major_info == NULL || major_info == &dasd_major_info[0] ) 
                BUG();

        device->major_info = major_info;
        dasd_device_name (device->name,
                          (((long)dptr -
                            (long)device->major_info->dasd_device) /
                           sizeof (dasd_device_t *)),
                          0, &device->major_info->gendisk);
        init_waitqueue_head (&device->wait_q);
        
        rc = get_dev_info_by_devno (devno, &device->devinfo);
        if ( rc ) {
                goto out;
        }

    DASD_DRIVER_DEBUG_EVENT (5, dasd_state_new_to_known, 
                                 "got devinfo CU-type %04x and dev-type %04x", 
                                 device->devinfo.sid_data.cu_type,
                                 device->devinfo.sid_data.dev_type);


        if ( devno != device->devinfo.devno )
                BUG();
        device->discipline = dasd_find_disc (device, disc);
        if ( device->discipline == NULL ) {
                rc = -ENODEV;
                goto out;
        }
        sprintf (buffer, "%04x", 
                 device->devinfo.devno);
        dir = devfs_mk_dir (dasd_devfs_handle, buffer, device);
        device->major_info->gendisk.de_arr[MINOR(device->kdev)
                                          >> DASD_PARTN_BITS] = dir;
    if (dasd_features_from_devno(device->devinfo.devno)&DASD_FEATURE_READONLY) {
            devfs_perm &= ~(S_IWUSR);
    }
        device->devfs_entry = devfs_register (dir,"device",DEVFS_FL_DEFAULT,
                                              MAJOR(device->kdev),
                                              MINOR(device->kdev),
                                              devfs_perm,
                                              &dasd_device_operations,NULL);
        device->level = DASD_STATE_KNOWN;
 out:
        return rc;
}

static inline int
dasd_state_known_to_new (dasd_device_t *device ) 
{
        int rc = 0;
        /* don't reset to zeros because of persistent data durich detach/attach! */
        devfs_unregister(device->devfs_entry);
        devfs_unregister(device->major_info->gendisk.de_arr[MINOR(device->kdev) >> DASD_PARTN_BITS]);

        return rc;
}

static inline int
dasd_state_known_to_accept (dasd_device_t *device) 
{
        int rc = 0;
        device->debug_area = debug_register (device->name, 0, 2, 
                                             3 * sizeof (long));
        debug_register_view (device->debug_area, &debug_sprintf_view);
        debug_register_view (device->debug_area, &debug_hex_ascii_view);
        DASD_DEVICE_DEBUG_EVENT (0, device,"%p debug area created",
                                 device);
        
        if (device->discipline->int_handler) {
                rc = s390_request_irq_special (device->devinfo.irq,
                                               device->discipline->int_handler,
                                               dasd_not_oper_handler,
                                               0, DASD_NAME,
                                               &device->dev_status);
                if ( rc ) {
                        printk("No request IRQ\n");
                        goto out;
                }
        }
        device->level = DASD_STATE_ACCEPT;
 out:
        return rc;
}

static inline int
dasd_state_accept_to_known (dasd_device_t *device ) 
{
        if ( device->discipline == NULL )
                goto out;
        if (device->discipline->int_handler) {
                free_irq (device->devinfo.irq, &device->dev_status);
        }
        DASD_DEVICE_DEBUG_EVENT (0, device,"%p debug area deleted",
                                 device);
        if ( device->debug_area != NULL )
                debug_unregister (device->debug_area);
        device->discipline = NULL;
        device->level = DASD_STATE_KNOWN;
 out:
        return 0;
}

static inline int
dasd_state_accept_to_init (dasd_device_t *device) 
{
        int rc = 0;
        unsigned long flags;

        if ( device->discipline->init_analysis ) {
                device->init_cqr=device->discipline->init_analysis (device);
                if ( device->init_cqr != NULL ) {
                        if ( device->discipline->start_IO == NULL )
                                BUG();
                        atomic_inc (&dasd_init_pending);
                        s390irq_spin_lock_irqsave (device->devinfo.irq, 
                                                   flags);
                        rc = device->discipline->start_IO (device->init_cqr);
                        s390irq_spin_unlock_irqrestore(device->devinfo.irq, 
                                                       flags);
                        if ( rc )
                                goto out;
                        device->level = DASD_STATE_INIT;
                } else {
                        rc = -ENOMEM;
                }
        } else {
                rc = dasd_state_init_to_ready ( device ); 
        }
 out:
        return rc;
}

static inline int
dasd_state_init_to_ready (dasd_device_t *device ) 
{
        int rc = 0;    
        if (device->discipline->do_analysis != NULL)
                if ( device->discipline->do_analysis (device) == 0 ) 
                        switch (device->sizes.bp_block) {
                        case 512:
                        case 1024:
                        case 2048:
                        case 4096:
                                break;
                        default:
                                rc = -EMEDIUMTYPE;
                        }
        if ( device->init_cqr ) {
                /* This pointer is no longer needed, BUT dont't free the       */ 
                /* memory, because this is done in bh for finished request!!!! */
                atomic_dec(&dasd_init_pending);
                device->init_cqr = NULL; 
        }
        device->level = DASD_STATE_READY;
        return rc;
}

static inline int
dasd_state_ready_to_accept (dasd_device_t *device ) 
{
        int rc = 0;
        unsigned long flags;

        s390irq_spin_lock_irqsave (device->devinfo.irq, flags);
        if ( device->init_cqr != NULL &&  atomic_read(&dasd_init_pending) != 0 ) {
                if ( device->discipline->term_IO == NULL )
                        BUG();
                device->discipline->term_IO (device->init_cqr);
                atomic_dec (&dasd_init_pending);
                dasd_free_request (device->init_cqr, device);
                device->init_cqr = NULL;
        }
        s390irq_spin_unlock_irqrestore(device->devinfo.irq, flags);
        memset(&device->sizes,0,sizeof(dasd_sizes_t));
        device->level = DASD_STATE_ACCEPT;
        return rc;
}

static inline int
dasd_state_ready_to_online (dasd_device_t *device ) 
{
        int rc = 0;
        dasd_unplug_device (device);
        device->level = DASD_STATE_ONLINE;
        return rc;
}

static inline int
dasd_state_online_to_ready (dasd_device_t *device ) 
{
        int rc = 0;
        dasd_plug_device (device);
        device->level = DASD_STATE_READY;
        return rc;
}

static inline int
dasd_setup_blkdev (dasd_device_t *device ) 
{
        int rc = 0;
        int i;
        int major = MAJOR(device->kdev);
        int minor = MINOR(device->kdev);

        for (i = 0; i < (1 << DASD_PARTN_BITS); i++) {
                if (i == 0)
                        device->major_info->gendisk.sizes[minor] =
                                (device->sizes.blocks << device->
                                 sizes.s2b_shift) >> 1;
                else
                        device->major_info->gendisk.sizes[minor + i] = 0;
                hardsect_size[major][minor + i] = device->sizes.bp_block;
                blksize_size[major][minor + i] = device->sizes.bp_block;
                max_sectors[major][minor + i] =
                        device->discipline->max_blocks << 
                        device->sizes.s2b_shift;
        device->major_info->gendisk.part[minor+i].start_sect = 0;
        device->major_info->gendisk.part[minor+i].nr_sects = 0;
        }
        device->request_queue = kmalloc(sizeof(request_queue_t),GFP_KERNEL);
        device->request_queue->queuedata = device;
        blk_init_queue (device->request_queue, do_dasd_request);
        blk_queue_headactive (device->request_queue, 0);
        elevator_init (&(device->request_queue->elevator),ELEVATOR_NOOP);
        return rc;
}

static void
dasd_deactivate_queue (dasd_device_t *device)
{
        int i;
        int minor = MINOR(device->kdev);

        for (i = 0; i < (1 << DASD_PARTN_BITS); i++) {
                device->major_info->gendisk.sizes[minor + i] = 0;
        }
}

static inline int
dasd_disable_blkdev (dasd_device_t *device ) 
{
        int i;
        int major = MAJOR(device->kdev);
        int minor = MINOR(device->kdev);

        for (i = 0; i < (1 << DASD_PARTN_BITS); i++) {
                destroy_buffers(MKDEV(major,minor+i));
                device->major_info->gendisk.sizes[minor + i] = 0;
                hardsect_size[major][minor + i] = 0;
                blksize_size[major][minor + i] = 0;
                max_sectors[major][minor + i] = 0;
        }
        if (device->request_queue) {
            blk_cleanup_queue (device->request_queue);
            kfree(device->request_queue);
            device->request_queue = NULL;
        }
        return 0;
}


/*
 * function dasd_setup_partitions
 * calls the function in genhd, which is appropriate to setup a partitioned disk
 */
static inline void
dasd_setup_partitions ( dasd_device_t * device ) 
{
    register_disk (&device->major_info->gendisk,
                       device->kdev,
               1 << DASD_PARTN_BITS,
               &dasd_device_operations,
               (device->sizes.blocks << device->sizes.s2b_shift));
}

static inline void
dasd_destroy_partitions ( dasd_device_t * device ) 
{
        int i;
        int minor = MINOR(device->kdev);
        
        for (i = 0; i < (1 << DASD_PARTN_BITS); i++) {
                device->major_info->gendisk.part[minor+i].start_sect = 0;
                device->major_info->gendisk.part[minor+i].nr_sects   = 0;
        }
        devfs_register_partitions(&device->major_info->gendisk,
                                  MINOR(device->kdev),1);
}

static inline void
dasd_resetup_partitions ( dasd_device_t * device ) 
{
    BUG();
    dasd_destroy_partitions ( device ) ;
    dasd_setup_partitions ( device ) ;
}

/*
 * function dasd_set_device_level
 */
static int
dasd_set_device_level (unsigned int devno, 
                       dasd_discipline_t * discipline,
                       int to_state)
{
    int rc = 0;
        dasd_device_t **device_addr;
        dasd_device_t *device;
        int from_state;

        device_addr = dasd_find_device_addr ( devno );
        if ( device_addr == NULL ) {
                rc = -ENODEV;
                goto out;
        }
        device = *device_addr;

        if ( device == NULL ) {
                from_state = DASD_STATE_DEL;
                if ( to_state == DASD_STATE_DEL )
                        goto out;
        } else {
                from_state = device->level;
        }

        DASD_DRIVER_DEBUG_EVENT (3, dasd_set_device_level,
                                 "devno %04x; from %i to %i",
                                 devno,
                                 from_state,
                                 to_state);

        if ( from_state == to_state )
                goto out;

        if ( to_state < from_state )
                goto shutdown;

        /* First check for bringup */
        if ( from_state <= DASD_STATE_DEL &&
             to_state >= DASD_STATE_NEW ) { 
                rc = dasd_state_del_to_new(device_addr);
                if ( rc ) {
                        goto bringup_fail;
                }
                device = *device_addr;
        }
        if ( from_state <= DASD_STATE_NEW &&
             to_state >= DASD_STATE_KNOWN ) { 
                rc = dasd_state_new_to_known( device_addr, devno, discipline );
                if ( rc ) {
                        goto bringup_fail;
                }
        }
        if ( from_state <= DASD_STATE_KNOWN &&
             to_state >= DASD_STATE_ACCEPT ) { 
                rc = dasd_state_known_to_accept(device);
                if ( rc ) {
                        goto bringup_fail;
                }
        }
        if ( dasd_probeonly ) {
            goto out;
        }
        if ( from_state <= DASD_STATE_ACCEPT &&
             to_state >= DASD_STATE_INIT ) { 
                rc = dasd_state_accept_to_init(device);
                if ( rc ) {
                        goto bringup_fail;
                }
        }
        if ( from_state <= DASD_STATE_INIT &&
             to_state >= DASD_STATE_READY ) { 
                rc = -EAGAIN;
                goto out;
        }
        if ( from_state <= DASD_STATE_READY &&
             to_state >= DASD_STATE_ONLINE ) { 
                rc = dasd_state_ready_to_online(device);
                if ( rc ) {
                        goto bringup_fail;
                }
        }
        goto out;
 bringup_fail:   /* revert changes */
#if 0
        printk (KERN_DEBUG PRINTK_HEADER
                "failed to set device from state %d to %d at "
                "level %d rc %d. Reverting...\n",
                from_state,
                to_state,
                device->level,
                rc);
#endif
        to_state = from_state;
        from_state = device->level;
        
        /* now do a shutdown */
 shutdown: 
        if ( from_state >= DASD_STATE_ONLINE &&
             to_state <= DASD_STATE_READY ) 
                if (dasd_state_online_to_ready(device))
                        BUG();

        if ( from_state >= DASD_STATE_READY &&
             to_state <= DASD_STATE_ACCEPT ) 
                if ( dasd_state_ready_to_accept(device))
                        BUG();

        if ( from_state >= DASD_STATE_ACCEPT &&
             to_state <= DASD_STATE_KNOWN ) 
                if ( dasd_state_accept_to_known(device))
                        BUG();

        if ( from_state >= DASD_STATE_KNOWN &&
             to_state <= DASD_STATE_NEW ) 
                if ( dasd_state_known_to_new(device))
                        BUG();

        if ( from_state >= DASD_STATE_NEW &&
             to_state <= DASD_STATE_DEL) 
                if (dasd_state_new_to_del(device_addr))
                        BUG();
        goto out;
 out:
        return rc;
}

/* SECTION: Procfs stuff */
typedef struct {
    char *data;
    int len;
} tempinfo_t;

void
dasd_fill_inode (struct inode *inode, int fill)
{
    if (fill)
        MOD_INC_USE_COUNT;
    else
        MOD_DEC_USE_COUNT;
}

static struct proc_dir_entry *dasd_proc_root_entry = NULL;
static struct proc_dir_entry *dasd_devices_entry;
static struct proc_dir_entry *dasd_statistics_entry;

static int
dasd_devices_open (struct inode *inode, struct file *file)
{
    int rc = 0;
    int size = 1;
    int len = 0;
    major_info_t *temp = NULL;
    struct list_head *l;
    tempinfo_t *info;
    int i;
        unsigned long flags;
        int index = 0;

        MOD_INC_USE_COUNT;
        spin_lock_irqsave(&discipline_lock,flags);
    info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t));
    if (info == NULL) {
                printk (KERN_WARNING "No memory available for data\n");
                MOD_DEC_USE_COUNT;
                return -ENOMEM;
    } else {
        file->private_data = (void *) info;
    }
    list_for_each (l, &dasd_major_info[0].list) {
                size += 128 * 1 << (MINORBITS - DASD_PARTN_BITS);
    }
    info->data = (char *) vmalloc (size);    
    DASD_DRIVER_DEBUG_EVENT (1, dasd_devices_open, "area: %p, size 0x%x",
                 info->data, 
                                 size);
    if (size && info->data == NULL) {
        printk (KERN_WARNING "No memory available for data\n");
        vfree (info);
                MOD_DEC_USE_COUNT;
        return -ENOMEM;
    }
    list_for_each (l, &dasd_major_info[0].list) {
        temp = list_entry (l, major_info_t, list);
        for (i = 0; i < 1 << (MINORBITS - DASD_PARTN_BITS); i++) {
            dasd_device_t *device;
                        int devno = dasd_devno_from_devindex(index+i);
                        int features;

                        if ( devno == -ENODEV )
                                continue;

                        features = dasd_features_from_devno(devno);
                        if (features < DASD_DEFAULT_FEATURES)
                                features = DASD_DEFAULT_FEATURES;

                        device = temp->dasd_device[i];
            if (device) {

                len += sprintf (info->data + len,
                        "%04x(%s) at (%3d:%3d) is %-7s%4s: ",
                        device->devinfo.devno,
                        device->discipline ?
                        device->
                        discipline->name : "none",
                        temp->gendisk.major,
                        i << DASD_PARTN_BITS,
                        device->name,
                                                (features & DASD_FEATURE_READONLY) ? 
                                                "(ro)" : " ");
                                
                switch (device->level) {
                case DASD_STATE_NEW:
                                        len +=
                        sprintf (info->data + len,
                             "new");
                                        break;
                case DASD_STATE_KNOWN:
                    len +=
                        sprintf (info->data + len,
                             "detected");
                    break;
                case DASD_STATE_ACCEPT:
                                        len += sprintf (info->data + len,"accepted");
                    break;
                case DASD_STATE_INIT:
                    len +=
                        sprintf (info->data + len,
                             "busy   ");
                    break;
                case DASD_STATE_READY:
                case DASD_STATE_ONLINE:
                                    if ( atomic_read(&device->plugged) )
                                        len +=
                                            sprintf (info->data + len,
                                                     "fenced ");
                                    else
                                        len +=
                                            sprintf (info->data + len,
                                                     "active ");
                                    if ( device->sizes.bp_block == 512 ||
                                         device->sizes.bp_block == 1024 ||
                                         device->sizes.bp_block == 2048 ||
                                         device->sizes.bp_block == 4096 )
                    len +=
                        sprintf (info->data + len,
                             "at blocksize: %d, %ld blocks, %ld MB",
                             device->sizes.bp_block,
                             device->sizes.blocks,
                             ((device->
                               sizes.bp_block >> 9) *
                              device->sizes.
                              blocks) >> 11);
                                    else
                                        len +=
                                            sprintf (info->data + len,
                                                     "n/f    ");
                    break;
                default:
                    len +=
                        sprintf (info->data + len,
                             "no stat");
                    break;
                }
            } else {
                                char buffer[7];
                                dasd_device_name (buffer, i, 0, &temp->gendisk);
                                if ( devno < 0  ) {
                                        len += sprintf (info->data + len,
                                                        "none");
                                } else {
                                        len += sprintf (info->data + len,
                                                        "%04x",devno);
                                }
                                len += sprintf (info->data + len,
                                                "(none) at (%3d:%3d) is %-7s%4s: unknown",
                        temp->gendisk.major,
                        i << DASD_PARTN_BITS,
                        buffer,
                                                (features & DASD_FEATURE_READONLY) ? 
                                                "(ro)" : " ");
                        }
                        if ( dasd_probeonly )
                            len += sprintf(info->data + len,"(probeonly)");
                        len += sprintf(info->data + len,"\n");
        }
                index += 1 << (MINORBITS - DASD_PARTN_BITS);
    }
    info->len = len;
        spin_unlock_irqrestore(&discipline_lock,flags);
    return rc;
}

#define MIN(a,b) ((a)<(b)?(a):(b))

static ssize_t
dasd_generic_read (struct file *file, char *user_buf, size_t user_len,
           loff_t * offset)
{
    loff_t len;
    tempinfo_t *p_info = (tempinfo_t *) file->private_data;

    if (*offset >= p_info->len) {
        return 0;    /* EOF */
    } else {
        len = MIN (user_len, (p_info->len - *offset));
        if (copy_to_user (user_buf, &(p_info->data[*offset]), len))
            return -EFAULT;
        (*offset) += len;
        return len;    /* number of bytes "read" */
    }
}

static ssize_t
dasd_devices_write (struct file *file, const char *user_buf,
            size_t user_len, loff_t * offset)
{
    char *buffer = vmalloc (user_len+1);
    int off = 0;
    char *temp;
    dasd_range_t range;
        int features;

    if (buffer == NULL)
        return -ENOMEM;
    if (copy_from_user (buffer, user_buf, user_len)) {
        vfree (buffer);
        return -EFAULT;
    }

        /* replace LF with '\0' */
        if (buffer[user_len -1] == '\n') {
                buffer[user_len -1] = '\0';
        } else {
                buffer[user_len] = '\0';
        }

    printk (KERN_INFO PRINTK_HEADER "/proc/dasd/devices: '%s'\n", buffer);
    if (strncmp (buffer, "set ", 4) && strncmp (buffer, "add ", 4)) {
        printk (KERN_WARNING PRINTK_HEADER
            "/proc/dasd/devices: only 'set' and 'add' are supported verbs\n");
        return -EINVAL;
    }
    off += 4;
    while (buffer[off] && !isalnum (buffer[off]))
        off++;
    if (!strncmp (buffer + off, "device", strlen ("device"))) {
        off += strlen ("device");
        while (buffer[off] && !isalnum (buffer[off]))
            off++;
    }
    if (!strncmp (buffer + off, "range=", strlen ("range="))) {
        off += strlen ("range=");
        while (buffer[off] && !isalnum (buffer[off]))
            off++;
    }
    
    temp = buffer + off;
    range.from = dasd_strtoul (temp, &temp, &features);
    range.to = range.from;

    if (*temp == '-') {
        temp++;
        range.to = dasd_strtoul (temp, &temp, &features);
    }

        if (range.from == -EINVAL ||
            range.to   == -EINVAL   ) {
                
                printk (KERN_WARNING PRINTK_HEADER
                        "/proc/dasd/devices: range parse error in '%s'\n", 
                        buffer);
        } else {
                off = (long) temp - (long) buffer;
                if (!strncmp (buffer, "add", strlen ("add"))) {
                        dasd_add_range (range.from, range.to, features);
                        dasd_enable_ranges (&range, NULL, 0);
                } else { 
                        while (buffer[off] && !isalnum (buffer[off]))
                                off++;
                        if (!strncmp (buffer + off, "on", strlen ("on"))) {
                                dasd_enable_ranges (&range, NULL, 0);
                        } else if (!strncmp (buffer + off, "off", strlen ("off"))) {
                                dasd_disable_ranges (&range, NULL, 0, 1);
                        } else {
                                printk (KERN_WARNING PRINTK_HEADER
                                        "/proc/dasd/devices: parse error in '%s'\n",
                                        buffer);
                        }
                }
        }

    vfree (buffer);
    return user_len;
}

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);
    }
    MOD_DEC_USE_COUNT;
    return rc;
}

static struct file_operations dasd_devices_file_ops = {
    read:dasd_generic_read,    /* read */
    write:dasd_devices_write,    /* write */
    open:dasd_devices_open,    /* open */
    release:dasd_devices_close,    /* close */
};

static struct inode_operations dasd_devices_inode_ops = {
};

static int
dasd_statistics_open (struct inode *inode, struct file *file)
{
    int rc = 0;
    int len = 0;
    tempinfo_t *info;
    int shift, i, help = 0;

        MOD_INC_USE_COUNT;
    info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t));
    if (info == NULL) {
        printk (KERN_WARNING "No memory available for data\n");
                MOD_DEC_USE_COUNT;
        return -ENOMEM;
    } else {
        file->private_data = (void *) info;
    }
    info->data = (char *) vmalloc (PAGE_SIZE);    /* FIXME! determine space needed in a better way */
    if (info->data == NULL) {
        printk (KERN_WARNING "No memory available for data\n");
        vfree (info);
        file->private_data = NULL;
                MOD_DEC_USE_COUNT;
        return -ENOMEM;
    }
        
        /* prevent couter 'ouverflow' on output */
    for (shift = 0, help = dasd_global_profile.dasd_io_reqs;
         help > 9999999; help = help >> 1, shift++) ;

    len = sprintf (info->data, "%d dasd I/O requests\n",
                       dasd_global_profile.dasd_io_reqs);
    len += sprintf (info->data + len, "with %d sectors(512B each)\n",
                        dasd_global_profile.dasd_io_sects);

    len += sprintf (info->data + len,
                        "   __<4    ___8    __16    __32    __64 "
                        "   _128    _256    _512    __1k    __2k "
                        "   __4k    __8k    _16k    _32k    _64k "
                        "   128k\n");

    len += sprintf (info->data + len,
                        "   _256    _512    __1M    __2M    __4M "
                        "   __8M    _16M    _32M    _64M    128M "
                        "   256M    512M    __1G    __2G    __4G "
                        "   _>4G\n");

    len += sprintf (info->data + len, "Histogram of sizes (512B secs)\n");
    for (i = 0; i < 16; i++) {
        len += sprintf (info->data + len, "%7d ",
                                dasd_global_profile.dasd_io_secs[i] >> shift);
    }
    len += sprintf (info->data + len, "\n");

    len += sprintf (info->data + len, "Histogram of I/O times (microseconds)\n");
    for (i = 0; i < 16; i++) {
        len += sprintf (info->data + len, "%7d ",
                                dasd_global_profile.dasd_io_times[i] >> shift);
    }
    len += sprintf (info->data + len, "\n");
    for (; i < 32; i++) {
        len += sprintf (info->data + len, "%7d ",
                                dasd_global_profile.dasd_io_times[i] >> shift);
    }
    len += sprintf (info->data + len, "\n");

    len += sprintf (info->data + len, "Histogram of I/O times per sector\n");
    for (i = 0; i < 16; i++) {
        len += sprintf (info->data + len, "%7d ",
                                dasd_global_profile.dasd_io_timps[i] >> shift);
    }
    len += sprintf (info->data + len, "\n");
    for (; i < 32; i++) {
        len += sprintf (info->data + len, "%7d ",
                                dasd_global_profile.dasd_io_timps[i] >> shift);
    }
    len += sprintf (info->data + len, "\n");

    len += sprintf (info->data + len, "Histogram of I/O time till ssch\n");
    for (i = 0; i < 16; i++) {
        len += sprintf (info->data + len, "%7d ",
                                dasd_global_profile.dasd_io_time1[i] >> shift);
    }
    len += sprintf (info->data + len, "\n");
    for (; i < 32; i++) {
        len += sprintf (info->data + len, "%7d ",
                                dasd_global_profile.dasd_io_time1[i] >> shift);
    }
    len += sprintf (info->data + len, "\n");

    len += sprintf (info->data + len,
                        "Histogram of I/O time between ssch and irq\n");
    for (i = 0; i < 16; i++) {
        len += sprintf (info->data + len, "%7d ",
                                dasd_global_profile.dasd_io_time2[i] >> shift);
    }
    len += sprintf (info->data + len, "\n");
    for (; i < 32; i++) {
        len += sprintf (info->data + len, "%7d ",
                                dasd_global_profile.dasd_io_time2[i] >> shift);
    }
    len += sprintf (info->data + len, "\n");

    len += sprintf (info->data + len,
                        "Histogram of I/O time between ssch and irq per sector\n");
    for (i = 0; i < 16; i++) {
        len += sprintf (info->data + len, "%7d ",
                                dasd_global_profile.dasd_io_time2ps[i] >> shift);
    }
    len += sprintf (info->data + len, "\n");
    for (; i < 32; i++) {
        len += sprintf (info->data + len, "%7d ",
                                dasd_global_profile.dasd_io_time2ps[i] >> shift);
    }
    len += sprintf (info->data + len, "\n");

    len += sprintf (info->data + len,
                        "Histogram of I/O time between irq and end\n");
    for (i = 0; i < 16; i++) {
        len +=
            sprintf (info->data + len, "%7d ",
                 dasd_global_profile.dasd_io_time3[i] >> shift);
    }
    len += sprintf (info->data + len, "\n");
    for (; i < 32; i++) {
        len += sprintf (info->data + len, "%7d ",
                                dasd_global_profile.dasd_io_time3[i] >> shift);
    }
    len += sprintf (info->data + len, "\n");

    len += sprintf (info->data + len,
                        "# of req in chanq at enqueuing (1..32) \n");
    for (i = 0; i < 16; i++) {
        len += sprintf (info->data + len, "%7d ",
                                dasd_global_profile.dasd_io_nr_req[i] >> shift);
    }
    len += sprintf (info->data + len, "\n");
    for (; i < 32; i++) {
        len += sprintf (info->data + len, "%7d ",
                                dasd_global_profile.dasd_io_nr_req[i] >> shift);
    }
    len += sprintf (info->data + len, "\n");

    info->len = len;
    return rc;
}

static ssize_t
dasd_statistics_write (struct file *file, const char *user_buf,
               size_t user_len, loff_t * offset)
{
    char *buffer = vmalloc (user_len);

    if (buffer == NULL)
        return -ENOMEM;
    if (copy_from_user (buffer, user_buf, user_len)) {
        vfree (buffer);
        return -EFAULT;
    }
    buffer[user_len] = 0;
    printk (KERN_INFO PRINTK_HEADER "/proc/dasd/statictics: '%s'\n",
        buffer);
    if (strncmp (buffer, "reset", 4)) {
        memset (&dasd_global_profile, 0, sizeof (dasd_profile_info_t));
    }
    return user_len;
}

static struct file_operations dasd_statistics_file_ops = {
    read:dasd_generic_read,    /* read */
    open:dasd_statistics_open,    /* open */
    write:dasd_statistics_write,    /* write */
    release:dasd_devices_close,    /* close */
};

static struct inode_operations dasd_statistics_inode_ops = {
};

int
dasd_proc_init (void)
{
    int rc = 0;
    dasd_proc_root_entry = proc_mkdir ("dasd", &proc_root);
    dasd_devices_entry = create_proc_entry ("devices",
                        S_IFREG | S_IRUGO | S_IWUSR,
                        dasd_proc_root_entry);
    dasd_devices_entry->proc_fops = &dasd_devices_file_ops;
    dasd_devices_entry->proc_iops = &dasd_devices_inode_ops;
    dasd_statistics_entry = create_proc_entry ("statistics",
                           S_IFREG | S_IRUGO | S_IWUSR,
                           dasd_proc_root_entry);
    dasd_statistics_entry->proc_fops = &dasd_statistics_file_ops;
    dasd_statistics_entry->proc_iops = &dasd_statistics_inode_ops;
    return rc;
}

void
dasd_proc_cleanup (void)
{
    remove_proc_entry ("devices", dasd_proc_root_entry);
    remove_proc_entry ("statistics", dasd_proc_root_entry);
    remove_proc_entry ("dasd", &proc_root);
}

int
dasd_request_module ( void *name ) {
    int rc = -ERESTARTSYS;
        strcpy(current->comm, name);
       daemonize();
       while ( current->fs->root == NULL ) { /* wait for root-FS */
            DECLARE_WAIT_QUEUE_HEAD(wait);
            sleep_on_timeout(&wait,HZ); /* wait in steps of one second */
    } 
    while ( (rc=request_module(name)) != 0 ) {
            DECLARE_WAIT_QUEUE_HEAD(wait);
        printk ( KERN_INFO "request_module returned %d for %s\n",
                         rc,
                         (char*)name);
            sleep_on_timeout(&wait,5* HZ); /* wait in steps of 5 seconds */
        }
        return rc;
}


/* SECTION: Initializing the driver */
int __init
dasd_init (void)
{
    int rc = 0;
    int irq;
    major_info_t *major_info = NULL;
    struct list_head *l;

    printk (KERN_INFO PRINTK_HEADER "initializing...\n");
    dasd_debug_area = debug_register (DASD_NAME, 0, 2, 5 * sizeof (long));
    debug_register_view (dasd_debug_area, &debug_sprintf_view);
    debug_register_view (dasd_debug_area, &debug_hex_ascii_view);

    init_waitqueue_head (&dasd_init_waitq);

    if (dasd_debug_area == NULL) {
        goto failed;
    }
    DASD_DRIVER_DEBUG_EVENT (0, dasd_init, "%s", 
                                 "ENTRY");
    dasd_devfs_handle = devfs_mk_dir (NULL, DASD_NAME, NULL);
    if (dasd_devfs_handle < 0) {
        DASD_DRIVER_DEBUG_EVENT (1, dasd_init, "%s", 
                                         "no devfs");
        goto failed;
    }
    list_for_each (l, &dasd_major_info[0].list) {
        major_info = list_entry (l, major_info_t, list);
        if ((rc = dasd_register_major (major_info)) > 0) {
            DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
                         "major %d: success",
                         major_info->gendisk.major);
            printk (KERN_INFO PRINTK_HEADER
                "Registered successfully to major no %u\n",
                major_info->gendisk.major);
        } else {
            DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
                         "major %d: failed",
                         major_info->gendisk.major);
            printk (KERN_WARNING PRINTK_HEADER
                "Couldn't register successfully to major no %d\n",
                major_info->gendisk.major);
            /* revert registration of major infos */
            goto failed;
        }
    }
#ifndef MODULE
    dasd_split_parm_string (dasd_parm_string);
#endif                /* ! MODULE */
    rc = dasd_parse (dasd);
    if (rc) {
        DASD_DRIVER_DEBUG_EVENT (1, dasd_init, "%s",
                                         "invalid range found");
        goto failed;
    }

    rc = dasd_proc_init ();
    if (rc) {
        DASD_DRIVER_DEBUG_EVENT (1, dasd_init, "%s", "no proc-FS");
        goto failed;
    }
    genhd_dasd_name = dasd_device_name;

    if (dasd_autodetect) {    /* update device range to all devices */
        for (irq = get_irq_first (); irq != -ENODEV;
             irq = get_irq_next (irq)) {
            int devno = get_devno_by_irq (irq);
            int index = dasd_devindex_from_devno (devno);
            if (index == -ENODEV) {    /* not included in ranges */
                DASD_DRIVER_DEBUG_EVENT (2, dasd_init,
                             "add %04x to range",
                             devno);
                dasd_add_range (devno, devno, DASD_DEFAULT_FEATURES);
            }
        }
    }

    if (MACHINE_IS_VM) {
#ifdef CONFIG_DASD_DIAG
        rc = dasd_diag_init ();
        if (rc == 0) {
            DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
                         "DIAG discipline %s",
                         "success");
            printk (KERN_INFO PRINTK_HEADER
                "Registered DIAG discipline successfully\n");
        } else {
            DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
                         "DIAG discipline %s",
                         "failed");
            goto failed;
        }
#endif /* CONFIG_DASD_DIAG */
#if defined(CONFIG_DASD_DIAG_MODULE) && defined(CONFIG_DASD_AUTO_DIAG)
                kernel_thread(dasd_request_module,"dasd_diag_mod",SIGCHLD);
#endif /* CONFIG_DASD_AUTO_DIAG */
    }
#ifdef CONFIG_DASD_ECKD
    rc = dasd_eckd_init ();
    if (rc == 0) {
        DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
                     "ECKD discipline %s", "success");
        printk (KERN_INFO PRINTK_HEADER
            "Registered ECKD discipline successfully\n");
    } else {
        DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
                     "ECKD discipline %s", "failed");
        goto failed;
    }
#endif /* CONFIG_DASD_ECKD */
#if defined(CONFIG_DASD_ECKD_MODULE) && defined(CONFIG_DASD_AUTO_ECKD)
        kernel_thread(dasd_request_module,"dasd_eckd_mod",SIGCHLD);
#endif /* CONFIG_DASD_AUTO_ECKD */
#ifdef CONFIG_DASD_FBA
    rc = dasd_fba_init ();
    if (rc == 0) {
        DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
                     "FBA discipline %s", "success");

        printk (KERN_INFO PRINTK_HEADER
            "Registered FBA discipline successfully\n");
    } else {
        DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
                     "FBA discipline %s", "failed");
        goto failed;
    }
#endif /* CONFIG_DASD_FBA */
#if defined(CONFIG_DASD_FBA_MODULE) && defined(CONFIG_DASD_AUTO_FBA)
        kernel_thread(dasd_request_module,"dasd_fba_mod",SIGCHLD);
#endif /* CONFIG_DASD_AUTO_FBA */
        {
                char **disc=dasd_disciplines;
                while (*disc) {
                        kernel_thread(dasd_request_module,*disc,SIGCHLD);
                        disc++;
                }
        }

    rc = 0;
    goto out;
      failed:
    printk (KERN_INFO PRINTK_HEADER
        "initialization not performed due to errors\n");
    cleanup_dasd ();
      out:
    DASD_DRIVER_DEBUG_EVENT (0, dasd_init, "%s", "LEAVE");
    printk (KERN_INFO PRINTK_HEADER "initialization finished\n");
    return rc;
}

static void
cleanup_dasd (void)
{
    int i,rc=0;
    major_info_t *major_info = NULL;
    struct list_head *l,*n;
    dasd_range_t *range;

    printk (KERN_INFO PRINTK_HEADER "shutting down\n");
        DASD_DRIVER_DEBUG_EVENT(0,"cleanup_dasd","%s","ENTRY");
    dasd_disable_ranges (&dasd_range_head, NULL, 1, 1);
        if (MACHINE_IS_VM) {
#ifdef CONFIG_DASD_DIAG
                dasd_diag_cleanup ();
                DASD_DRIVER_DEBUG_EVENT (1, "cleanup_dasd",
                                         "DIAG discipline %s", "success");
                printk (KERN_INFO PRINTK_HEADER
            "De-Registered DIAG discipline successfully\n");
#endif /* CONFIG_DASD_ECKD_BUILTIN */
    }
#ifdef CONFIG_DASD_FBA
    dasd_fba_cleanup ();
    DASD_DRIVER_DEBUG_EVENT (1, "cleanup_dasd",
                 "FBA discipline %s", "success");
    printk (KERN_INFO PRINTK_HEADER
        "De-Registered FBA discipline successfully\n");
#endif /* CONFIG_DASD_ECKD_BUILTIN */
#ifdef CONFIG_DASD_ECKD
    dasd_eckd_cleanup ();
    DASD_DRIVER_DEBUG_EVENT (1, "cleanup_dasd",
                 "ECKD discipline %s", "success");
    printk (KERN_INFO PRINTK_HEADER
        "De-Registered ECKD discipline successfully\n");
#endif /* CONFIG_DASD_ECKD_BUILTIN */
        
    dasd_proc_cleanup ();
        
    list_for_each_safe (l, n, &dasd_major_info[0].list) {
        major_info = list_entry (l, major_info_t, list);
        for (i = 0; i < DASD_PER_MAJOR; i++) {
            kfree (major_info->dasd_device[i]);
        }
        if ((major_info->flags & DASD_MAJOR_INFO_REGISTERED) &&
            (rc = dasd_unregister_major (major_info)) == 0) {
            DASD_DRIVER_DEBUG_EVENT (1, "cleanup_dasd",
                         "major %d: success",
                         major_info->gendisk.major);
            printk (KERN_INFO PRINTK_HEADER
                "Unregistered successfully from major no %u\n",
                major_info->gendisk.major);
        } else {
            DASD_DRIVER_DEBUG_EVENT (1, "cleanup_dasd",
                         "major %d: failed",
                         major_info->gendisk.major);
            printk (KERN_WARNING PRINTK_HEADER
                "Couldn't unregister successfully from major no %d rc = %d\n",
                major_info->gendisk.major, rc);
          }
      }
    list_for_each_safe (l, n, &dasd_range_head.list) {
        range = list_entry (l, dasd_range_t, list);
                dasd_remove_range(range);
        }

#ifndef MODULE
        for( i = 0; i < 256; i++ )
                if ( dasd[i] ) {
                        kfree(dasd[i]);
                        dasd[i] = NULL;
                }
#endif /* MODULE */
        if (dasd_devfs_handle) 
                devfs_unregister(dasd_devfs_handle);
        if (dasd_debug_area != NULL )
                debug_unregister(dasd_debug_area);
    printk (KERN_INFO PRINTK_HEADER "shutdown completed\n");
        DASD_DRIVER_DEBUG_EVENT(0,"cleanup_dasd","%s","LEAVE");
}

#ifdef MODULE
int
init_module (void)
{
    int rc = 0;
    rc = dasd_init ();
    return rc;
}

void
cleanup_module (void)
{
    cleanup_dasd ();
    return;
}
#endif

/*
 * Overrides for Emacs so that we follow Linus's tabbing style.
 * Emacs will notice this stuff at the end of the file and automatically
 * adjust the settings for this buffer only.  This must remain at the end
 * of the file.
 * ---------------------------------------------------------------------------
 * Local variables:
 * c-indent-level: 4
 * c-brace-imaginary-offset: 0
 * c-brace-offset: -4
 * c-argdecl-indent: 4
 * c-label-offset: -4
 * c-continued-statement-offset: 4
 * c-continued-brace-offset: 0
 * indent-tabs-mode: nil
 * tab-width: 8
 * End:
 */

:: Command execute ::

Enter:
 
Select:
 

:: Search ::
  - regexp 

:: Upload ::
 
[ Read-Only ]

:: Make Dir ::
 
[ Read-Only ]
:: Make File ::
 
[ Read-Only ]

:: Go Dir ::
 
:: Go File ::
 

--[ c99shell v. 1.0 pre-release build #13 powered by Captain Crunch Security Team | http://ccteam.ru | Generation time: 0.023 ]--