dasd.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:131k
- /*
- * 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 (®->devreg);
- list_add (®->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(®->list);
- s390_device_unregister (®->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 != ' ') {
- char *end;
- int len;
- end = strchr (tmp, ',');
- if (end == NULL) {
- len = strlen (tmp) + 1;
- } else {
- len = (long) end - (long) tmp + 1;
- *end = ' ';
- end++;
- }
- dasd[count] = kmalloc (len * sizeof (char), GFP_ATOMIC);
- if (dasd[count] == NULL) {
- printk (KERN_WARNING PRINTK_HEADER
- "can't store dasd= parameter no %dn",
- 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 memoryn",
- 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]!=' ' && temp[i]!='(' &&
- temp[i]!='-' && temp[i]!=' '; i++){
- if (isxdigit(temp[i])) {
- buffer[i]=temp[i];
- } else {
- return -EINVAL;
- }
- }
- buffer[i]=' ';
- val = simple_strtoul (buffer, &buffer, 16);
- /* check for features - e.g. (ro) ; the ' ', ')' and '-' stops check */
- *features = DASD_DEFAULT_FEATURES;
- if (temp[i]=='(') {
- while (temp[i]!=' ' && temp[i]!=')'&&temp[i]!='-') {
- start=++i;
-
- /* move next feature to buffer */
- for (;temp[i]!=' '&&temp[i]!=':'&&temp[i]!=')'&&temp[i]!='-';i++)
- buffer[i-start]=temp[i];
- buffer[i-start]=' ';
- 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 moden");
- break;
- } else if (strcmp ("probeonly", *str) == 0) {
- dasd_probeonly = 1;
- printk (KERN_INFO "turning to probeonly moden");
- 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 numbern");
- 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 = %dn",
- 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 = %dn",
- 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 = %dn",
- 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%08xn",
- 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, retryn");
- break;
- case -EIO:
- DASD_MESSAGE (KERN_WARNING, device, "%s",
- "I/O error, retryn");
- break;
- case -EBUSY:
- DASD_MESSAGE (KERN_WARNING, device, "%s",
- "device busy, retry latern");
- break;
- default:
- DASD_MESSAGE (KERN_ERR, device,
- "line %d unknown RC=%d, please report"
- " to linux390@de.ibm.comn",
- __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%08xn",
- 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 latern");
- break;
- case -ETIMEDOUT:
- DASD_MESSAGE (KERN_WARNING, device, "%s",
- "request timeout - terminatedn");
- 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.comn", __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 %pn",
- (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 %pn",
- 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 %pn",
- 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%04xn",
- 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 interruptn");
-
- 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 %04xn",
- 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 %08xn",
- 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 %02xn",
- 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...ignoringn");
- }
- #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 = %dn",