patch-2.4.2 linux/drivers/s390/block/dasd.c
Next file: linux/drivers/s390/block/dasd_3370_erp.c
Previous file: linux/drivers/s390/block/Makefile
Back to the patch index
Back to the overall index
- Lines: 4261
- Date:
Fri Feb 16 16:02:36 2001
- Orig file:
v2.4.1/linux/drivers/s390/block/dasd.c
- Orig date:
Sat Feb 3 19:51:28 2001
diff -u --recursive --new-file v2.4.1/linux/drivers/s390/block/dasd.c linux/drivers/s390/block/dasd.c
@@ -1,1542 +1,2989 @@
/*
* File...........: linux/drivers/s390/block/dasd.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
- * : Utz Bacher <utz.bacher@de.ibm.com>
+ * Horst Hummel <Horst.Hummel@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
+ *
+ * History of changes (starts July 2000)
+ * 11/09/00 complete redesign after code review
*/
#include <linux/config.h>
+#include <linux/version.h>
#include <linux/init.h>
+#include <linux/blkdev.h>
#include <linux/stddef.h>
#include <linux/kernel.h>
-
-#ifdef MODULE
-#include <linux/module.h>
-#endif /* MODULE */
-
#include <linux/tqueue.h>
#include <linux/timer.h>
-#include <linux/malloc.h>
+#include <linux/slab.h>
#include <linux/genhd.h>
-#include <linux/devfs_fs_kernel.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 */
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+#include <linux/spinlock.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/blkpg.h>
+#else
+#include <asm/spinlock.h>
+#endif
+
+#include <asm/ccwcache.h>
+#include <asm/dasd.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 <linux/dasd.h>
-#include <linux/blk.h>
+#ifdef CONFIG_DASD_ECKD
+#include "dasd_eckd.h"
+#endif /* CONFIG_DASD_ECKD */
+#ifdef CONFIG_DASD_FBA
+#include "dasd_fba.h"
+#endif /* CONFIG_DASD_FBA */
+#ifdef CONFIG_DASD_MDSK
+#include "dasd_diag.h"
+#endif /* CONFIG_DASD_MDSK */
-#include "dasd_erp.h"
-#include "dasd_types.h"
-#include "dasd_ccwstuff.h"
+static struct block_device_operations dasd_device_operations;
-#define PRINTK_HEADER DASD_NAME":"
+#ifdef MODULE
+#define EXPORT_SYMTAB
+#include <linux/module.h>
-#define CCW_READ_DEVICE_CHARACTERISTICS 0x64
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR ("Holger Smolinski <Holger.Smolinski@de.ibm.com>");
+MODULE_DESCRIPTION ("Linux on S/390 DASD device driver,"
+ " Copyright 2000 IBM Corporation");
+MODULE_SUPPORTED_DEVICE ("dasd");
+MODULE_PARM (dasd, "1-" __MODULE_STRING (256) "s");
+EXPORT_SYMBOL (dasd_discipline_enq);
+EXPORT_SYMBOL (dasd_discipline_deq);
+EXPORT_SYMBOL (dasd_start_IO);
+EXPORT_SYMBOL (dasd_int_handler);
+EXPORT_SYMBOL (dasd_alloc_request);
+EXPORT_SYMBOL (dasd_free_request);
-#define DASD_SSCH_RETRIES 2
+#endif /* MODULE */
-/* This macro is a little tricky, but makes the code more easy to read... */
-#define MATCH(info,ct,cm,dt,dm) ( \
-(( info -> sid_data.cu_type ct ) && ( info -> sid_data.cu_model cm )) && \
-(( info -> sid_data.dev_type dt ) && ( info -> sid_data.dev_model dm )) )
+/* SECTION: Constant definitions to be used within this file */
+#undef ERP_DEBUG
-/* Prototypes for the functions called from external */
-static int dasd_ioctl (struct inode *, struct file *, unsigned int, unsigned long);
-static int dasd_open (struct inode *, struct file *);
-static int dasd_release (struct inode *, struct file *);
+#define PRINTK_HEADER DASD_NAME": "
-void dasd_debug (unsigned long tag);
-void dasd_profile_add (cqr_t *cqr);
-void dasd_proc_init (void);
+#define DASD_EMERGENCY_REQUESTS 16
+#define DASD_MIN_SIZE_FOR_QUEUE 32
+#undef CONFIG_DYNAMIC_QUEUE_MIN_SIZE
+#define DASD_CHANQ_MAX_SIZE 6
-static int dasd_format( int, format_data_t * );
-static struct block_device_operations dasd_device_operations;
+/* SECTION: prototypes for static functions of dasd.c */
-spinlock_t dasd_lock; /* general purpose lock for the dasd driver */
+static request_fn_proc do_dasd_request;
+void dasd_schedule_bh (dasd_device_t *);
+static int dasd_set_device_level (unsigned int, int, dasd_discipline_t *, int);
+static request_queue_t *dasd_get_queue (kdev_t kdev);
-/* All asynchronous I/O should waint on this wait_queue */
-wait_queue_head_t dasd_waitq;
+static struct block_device_operations dasd_device_operations;
-static int dasd_autodetect = 1;
-static int dasd_devno[DASD_MAX_DEVICES] =
-{0,};
-static int dasd_count = 0;
+#ifdef CONFIG_DASD_DYNAMIC
+/* SECTION: managing dynamic configuration of dasd_driver */
-extern dasd_chanq_t *cq_head;
+static dasd_devreg_t *dasd_devreg_head = NULL;
-static int
-dasd_get_hexdigit (char c)
+/*
+ * function: dasd_create_devreg
+ * creates a dasd_devreg_t related to a devno
+ */
+static inline dasd_devreg_t *
+dasd_create_devreg (int devno)
{
- if ((c >= '0') && (c <= '9'))
- return c - '0';
- if ((c >= 'a') && (c <= 'f'))
- return c + 10 - 'a';
- if ((c >= 'A') && (c <= 'F'))
- return c + 10 - 'A';
- return -1;
+ 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;
}
-/* sets the string pointer after the next comma */
-static void
-dasd_scan_for_next_comma (char **strptr)
+/*
+ * function: dasd_destroy_devreg
+ * destroys the dasd_devreg_t given as argument
+ */
+static inline void
+dasd_destroy_devreg (dasd_devreg_t * devreg)
{
- while (((**strptr) != ',') && ((**strptr)++))
- (*strptr)++;
-
- /* set the position AFTER the comma */
- if (**strptr == ',')
- (*strptr)++;
+ kfree (devreg);
}
-/*sets the string pointer after the next comma, if a parse error occured */
-static int
-dasd_get_next_int (char **strptr)
+#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 */
+
+/* dasd_range_t are used for ordering the DASD devices */
+typedef struct dasd_range_t {
+ unsigned int from; /* first DASD in range */
+ unsigned int to; /* last DASD in range */
+ char discipline[4]; /* placeholder to force discipline */
+ struct dasd_range_t *next; /* next one in linked list */
+} dasd_range_t;
+
+static dasd_range_t *dasd_range_head = NULL; /* anchor for list of ranges */
+static 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 j, i = -1; /* for cosmetic reasons first -1, then 0 */
- if (isxdigit (**strptr)) {
- for (i = 0; isxdigit (**strptr);) {
- i <<= 4;
- j = dasd_get_hexdigit (**strptr);
- if (j == -1) {
- PRINT_ERR ("no integer: skipping range.\n");
- dasd_scan_for_next_comma (strptr);
- i = -1;
- break;
- }
- i += j;
- (*strptr)++;
- if (i > 0xffff) {
- PRINT_ERR (" value too big, skipping range.\n");
- dasd_scan_for_next_comma (strptr);
- i = -1;
- break;
- }
- }
+ dasd_range_t *range = NULL;
+ range = (dasd_range_t *) kmalloc (sizeof (dasd_range_t), GFP_KERNEL);
+ if (range == NULL)
+ return NULL;
+ memset (range, 0, sizeof (dasd_range_t));
+ range->from = from;
+ if (to == 0) { /* single devno ? */
+ range->to = from;
+ } else {
+ range->to = to;
}
- return i;
+ return range;
}
-static inline int
-devindex_from_devno (int devno)
+/*
+ * 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)
{
- int i;
- for (i = 0; i < dasd_count; i++) {
- if (dasd_devno[i] == devno)
- return i;
- }
- if (dasd_autodetect) {
- if (dasd_count < DASD_MAX_DEVICES) {
- dasd_devno[dasd_count] = devno;
- return dasd_count++;
- }
- return -EOVERFLOW;
- }
- return -ENODEV;
+ kfree (range);
}
-/* returns 1, if dasd_no is in the specified ranges, otherwise 0 */
-static inline int
-dasd_is_accessible (int devno)
+/*
+ * 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)
{
- return (devindex_from_devno (devno) >= 0);
+ dasd_range_t *temp;
+ long flags;
+
+ spin_lock_irqsave (&range_lock, flags);
+ if (dasd_range_head == NULL) {
+ dasd_range_head = range;
+ } else {
+ for (temp = dasd_range_head;
+ temp && temp->next;
+ temp = temp->next) ;
+ temp->next = range;
+ }
+ spin_unlock_irqrestore (&range_lock, flags);
}
-/* dasd_insert_range skips ranges, if the start or the end is -1 */
-static void
-dasd_insert_range (int start, int end)
+/*
+ * 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)
{
- int curr;
- FUNCTION_ENTRY ("dasd_insert_range");
- if (dasd_count >= DASD_MAX_DEVICES) {
- PRINT_ERR (" too many devices specified, ignoring some.\n");
- FUNCTION_EXIT ("dasd_insert_range");
- return;
+ dasd_range_t *temp, *prev = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave (&range_lock, flags);
+ for (temp = dasd_range_head; temp != NULL; temp = temp->next) {
+ if (temp == range)
+ break;
+ prev = temp;
}
- if ((start == -1) || (end == -1)) {
- PRINT_ERR
- ("invalid format of parameter, skipping range\n");
- FUNCTION_EXIT ("dasd_insert_range");
- return;
+ if (!temp)
+ BUG ();
+ if (prev) {
+ prev->next = temp->next;
+ } else {
+ dasd_range_head = temp->next;
}
- if (end < start) {
- PRINT_ERR (" ignoring range from %x to %x - start value " \
- "must be less than end value.\n", start, end);
- FUNCTION_EXIT ("dasd_insert_range");
+ 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 void
+dasd_add_range (int from, int to)
+{
+ dasd_range_t *range;
+ range = dasd_create_range (from, to);
+ if (range)
+ dasd_append_range (range);
+ else
return;
- }
-/* concurrent execution would be critical, but will not occur here */
- for (curr = start; curr <= end; curr++) {
- if (dasd_is_accessible (curr)) {
- PRINT_WARN (" %x is already in list as device %d\n",
- curr, devindex_from_devno (curr));
- }
- dasd_devno[dasd_count] = curr;
- dasd_count++;
- if (dasd_count >= DASD_MAX_DEVICES) {
- PRINT_ERR (" too many devices specified, ignoring some.\n");
- break;
+#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);
+ reg->next = dasd_devreg_head;
+ dasd_devreg_head = reg;
}
}
- PRINT_INFO (" added dasd range from %x to %x.\n",
- start, dasd_devno[dasd_count - 1]);
-
- FUNCTION_EXIT ("dasd_insert_range");
+#endif /* CONFIG_DASD_DYNAMIC */
}
-static int __init
-dasd_setup (char *str)
+/*
+ * 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)
{
- int devno, devno2;
-
- FUNCTION_ENTRY ("dasd_setup");
- dasd_autodetect = 0;
- while (*str && *str != 1) {
- if (!isxdigit (*str)) {
- str++; /* to avoid looping on two commas */
- PRINT_ERR (" kernel parameter in invalid format.\n");
- continue;
- }
- devno = dasd_get_next_int (&str);
-
- /* range was skipped? -> scan for comma has been done */
- if (devno == -1)
- continue;
-
- if (*str == ',') {
- str++;
- dasd_insert_range (devno, devno);
- continue;
- }
- if (*str == '-') {
- str++;
- devno2 = dasd_get_next_int (&str);
- if (devno2 == -1) {
- PRINT_ERR (" invalid character in " \
- "kernel parameters.");
+#ifdef CONFIG_DASD_DYNAMIC
+ /* deallocate and dechain devreg infos for the devnos... */
+ {
+ int i;
+ for (i = range->from; i <= range->to; i++) {
+ dasd_devreg_t *reg, *prev = NULL;
+ for (reg = dasd_devreg_head; reg; reg = reg->next) {
+ if (reg->devreg.flag == DEVREG_TYPE_DEVNO &&
+ reg->devreg.devno == i &&
+ reg->devreg.oper_func == dasd_oper_handler)
+ break;
+ prev = reg;
+ }
+ if (!reg)
+ BUG ();
+ if (prev) {
+ prev->next = reg->next;
} else {
- dasd_insert_range (devno, devno2);
+ dasd_devreg_head = reg->next;
}
- dasd_scan_for_next_comma (&str);
- continue;
- }
- if (*str == 0) {
- dasd_insert_range (devno, devno);
- break;
+ s390_device_unregister (®->devreg);
+ dasd_destroy_devreg (reg);
}
- PRINT_ERR (" unexpected character in kernel parameter, " \
- "skipping range.\n");
}
- FUNCTION_EXIT ("dasd_setup");
- return 1;
+ dasd_dechain_range (range);
+ dasd_destroy_range (range);
+#endif /* CONFIG_DASD_DYNAMIC */
}
-__setup("dasd=", dasd_setup);
-
-dasd_information_t *dasd_info[DASD_MAX_DEVICES] = {NULL,};
-static struct hd_struct dd_hdstruct[DASD_MAX_DEVICES << PARTN_BITS];
-static int dasd_blks[256] = {0,};
-static int dasd_secsize[256] = {0,};
-static int dasd_blksize[256] = {0,};
-static int dasd_maxsecs[256] = {0,};
-
-struct gendisk dd_gendisk =
+/*
+ * 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)
{
- MAJOR_NR, /* Major number */
- "dasd", /* Major name */
- PARTN_BITS, /* Bits to shift to get real from partn */
- 1 << PARTN_BITS, /* Number of partitions per real */
- dd_hdstruct, /* hd struct */
- dasd_blks, /* sizes in blocks */
- DASD_MAX_DEVICES, /* number */
- NULL, /* internal */
- NULL /* next */
+ dasd_range_t *temp;
+ int devindex = 0;
+ unsigned long flags;
-};
+ spin_lock_irqsave (&range_lock, flags);
+ for (temp = dasd_range_head; temp; temp = temp->next) {
+ 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;
+}
-static atomic_t bh_scheduled = ATOMIC_INIT (0);
+/* SECTION: parsing the dasd= parameter of the parmline/insmod cmdline */
-static inline void
-schedule_bh (void (*func) (void))
+/*
+ * char *dasd[] is intended to hold the ranges supplied by the dasd= statement
+ * it is named 'dasd' to directly be filled by insmod with the comma separated
+ * strings when running as a module.
+ * a maximum of 256 ranges can be supplied, as the parmline is limited to
+ * <1024 Byte anyway.
+ */
+char *dasd[256] = {NULL,};
+
+#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)
{
- static struct tq_struct dasd_tq =
- {0,};
- /* Protect against rescheduling, when already running */
- if (atomic_compare_and_swap (0, 1, &bh_scheduled))
- return;
- dasd_tq.routine = (void *) (void *) func;
- queue_task (&dasd_tq, &tq_immediate);
- mark_bh (IMMEDIATE_BH);
- return;
+ char *tmp = str;
+ int count = 0;
+ do {
+ char *end;
+ int len;
+ end = strchr (tmp, ',');
+ if (end == NULL) {
+ len = strlen (tmp) + 1;
+ } else {
+ len = (long) end - (long) tmp + 1;
+ *end = '\0';
+ end++;
+ }
+ dasd[count] = kmalloc (len * sizeof (char), GFP_ATOMIC);
+ if (dasd == NULL) {
+ printk (KERN_WARNING PRINTK_HEADER
+ "can't store dasd= parameter no %d\n", count + 1);
+ break;
+ }
+ memset (dasd[count], 0, len * sizeof (char));
+ memcpy (dasd[count], tmp, len * sizeof (char));
+ count++;
+ tmp = end;
+ } while (tmp != NULL && *tmp != '\0');
}
-void
-sleep_done (struct semaphore *sem)
+/*
+ * 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)
{
- if (sem != NULL) {
- up (sem);
+ int len = strlen (dasd_parm_string);
+ if (len != 0) {
+ strcat (dasd_parm_string, ",");
}
+ strcat (dasd_parm_string, str);
}
-
-void
-sleep (int timeout)
+/*
+ * 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)
{
- struct semaphore sem;
- struct timer_list timer;
-
- init_MUTEX_LOCKED (&sem);
- init_timer (&timer);
- timer.data = (unsigned long) &sem;
- timer.expires = jiffies + timeout;
- timer.function = (void (*)(unsigned long)) sleep_done;
- printk (KERN_DEBUG PRINTK_HEADER
- "Sleeping for timer tics %d\n", timeout);
- add_timer (&timer);
- down (&sem);
- del_timer (&timer);
+ int dummy;
+ dasd_setup(str,&dummy);
+ return 1;
}
-#ifdef CONFIG_DASD_ECKD
-extern dasd_operations_t dasd_eckd_operations;
-#endif /* CONFIG_DASD_ECKD */
-#ifdef CONFIG_DASD_MDSK
-extern dasd_operations_t dasd_mdsk_operations;
-#endif /* CONFIG_DASD_MDSK */
-
-dasd_operations_t *dasd_disciplines[] =
-{
-#ifdef CONFIG_DASD_ECKD
- &dasd_eckd_operations,
-#endif /* CONFIG_DASD_ECKD */
-#ifdef CONFIG_DASD_MDSK
- &dasd_mdsk_operations,
-#endif /* CONFIG_DASD_MDSK */
-#ifdef CONFIG_DASD_CKD
- &dasd_ckd_operations,
-#endif /* CONFIG_DASD_CKD */
- NULL
-};
-
-char *dasd_name[] =
-{
-#ifdef CONFIG_DASD_ECKD
- "ECKD",
-#endif /* CONFIG_DASD_ECKD */
-#ifdef CONFIG_DASD_MDSK
- "MDSK",
-#endif /* CONFIG_DASD_MDSK */
-#ifdef CONFIG_DASD_CKD
- "CKD",
-#endif /* CONFIG_DASD_CKD */
- "END"
-};
+__setup ("dasd=", dasd_call_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
-do_dasd_ioctl (struct inode *inp, unsigned int no, unsigned long data)
+dasd_strtoul (char *str, char **stra)
{
- int rc;
- int di;
- dasd_information_t *dev;
-
- di = DEVICE_NR (inp->i_rdev);
- if (!dasd_info[di]) {
- PRINT_WARN ("No device registered as %d\n", inp->i_rdev);
- return -EINVAL;
- }
- if ((_IOC_DIR (no) != _IOC_NONE) && (data == 0)) {
- PRINT_DEBUG ("empty data ptr");
- return -EINVAL;
- }
- dev = dasd_info[di];
- if (!dev) {
- PRINT_WARN ("No device registered as %d\n", inp->i_rdev);
- return -EINVAL;
- }
- PRINT_INFO ("ioctl 0x%08x %s'0x%x'%d(%d) on dev %d/%d (%d) with data %8lx\n", no,
- _IOC_DIR (no) == _IOC_NONE ? "0" :
- _IOC_DIR (no) == _IOC_READ ? "r" :
- _IOC_DIR (no) == _IOC_WRITE ? "w" :
- _IOC_DIR (no) == (_IOC_READ | _IOC_WRITE) ? "rw" : "u",
- _IOC_TYPE (no), _IOC_NR (no), _IOC_SIZE (no),
- MAJOR (inp->i_rdev), MINOR (inp->i_rdev), di, data);
+ char *temp = str;
+ int val;
+ if (*temp == '0') {
+ temp++; /* strip leading zero */
+ if (*temp == 'x')
+ temp++; /* strip leading x */
+ }
+ val = simple_strtoul (temp, &temp, 16); /* interpret anything as hex */
+ *stra = temp;
+ return val;
+}
- switch (no) {
- case BLKGETSIZE:{ /* Return device size */
- unsigned long blocks;
- if (inp->i_rdev & 0x01) {
- blocks = (dev->sizes.blocks - 3) <<
- dev->sizes.s2b_shift;
- } else {
- blocks = dev->sizes.kbytes << dev->sizes.s2b_shift;
- }
- rc = copy_to_user ((long *) data, &blocks, sizeof (long));
- break;
- }
- case BLKFLSBUF:{
- rc = fsync_dev (inp->i_rdev);
- break;
- }
- case BLKRAGET:{
- rc = copy_to_user ((long *) data,
- read_ahead + MAJOR_NR, sizeof (long));
- break;
- }
- case BLKRASET:{
- rc = copy_from_user (read_ahead + MAJOR_NR,
- (long *) data, sizeof (long));
- break;
- }
- case BLKRRPART:{
- INTERNAL_CHECK ("BLKRPART not implemented%s", "");
- rc = -EINVAL;
- break;
- }
- case HDIO_GETGEO:{
- INTERNAL_CHECK ("HDIO_GETGEO not implemented%s", "");
- rc = -EINVAL;
- break;
- }
+/*
+ * function: dasd_parse
+ * examines the strings given in the string array str and
+ * creates and adds the ranges to the apropriate lists
+ */
+static inline void
+dasd_parse (char **str)
+{
+ char *temp;
+ int from, to;
- case BIODASDRSID:{
- rc = copy_to_user ((void *) data,
- &(dev->info.sid_data),
- sizeof (senseid_t));
+ 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;
- }
- case BIODASDRWTB:{
- int offset = 0;
- int xlt;
- rc = copy_from_user (&xlt, (void *) data,
- sizeof (int));
- PRINT_INFO("Xlating %d to",xlt);
- if (rc)
- break;
- if (MINOR (inp->i_rdev) & 1)
- offset = 3;
- xlt += offset;
- printk(" %d \n",xlt);
- rc = copy_to_user ((void *) data, &xlt,
- sizeof (int));
+ } else if (strcmp ("probeonly", *str) == 0) {
+ dasd_probeonly = 1;
+ printk (KERN_INFO "turning to probeonly mode\n");
break;
- }
- case BIODASDFORMAT:{
- /* fdata == NULL is a valid arg to dasd_format ! */
- format_data_t *fdata = NULL;
- if (data) {
- fdata = kmalloc (sizeof (format_data_t),
- GFP_ATOMIC);
- if (!fdata) {
- rc = -ENOMEM;
- break;
- }
- rc = copy_from_user (fdata, (void *) data,
- sizeof (format_data_t));
- if (rc)
- break;
- }
- rc = dasd_format (inp->i_rdev, fdata);
- if (fdata) {
- kfree (fdata);
+ } else {
+ /* turn off autodetect mode, if any range is present */
+ dasd_autodetect = 0;
+ from = dasd_strtoul (temp, &temp);
+ if (*temp == '-') {
+ temp++;
+ to = dasd_strtoul (temp, &temp);
}
- break;
+ dasd_add_range (from, to);
}
- default:
- rc = -EINVAL;
- break;
+ str++;
}
- return rc;
}
-static void
-dasd_end_request (struct request *req, int uptodate)
+/* 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[] =
{
- struct buffer_head *bh;
- FUNCTION_ENTRY ("dasd_end_request");
-#if DASD_PARANOIA > 2
- if (!req) {
- INTERNAL_CHECK ("end_request called with zero arg%s\n", "");
- }
-#endif /* DASD_PARANOIA */
- while ((bh = req->bh) != NULL) {
- req->bh = bh->b_reqnext;
- bh->b_reqnext = NULL;
- bh->b_end_io (bh, uptodate);
- }
- if (!end_that_request_first (req, uptodate, DEVICE_NAME)) {
-#ifndef DEVICE_NO_RANDOM
- add_blkdev_randomness (MAJOR (req->rq_dev));
-#endif
- DEVICE_OFF (req->rq_dev);
- end_that_request_last (req);
+ {
+ next:NULL,
+ gendisk: {
+ INIT_GENDISK(94,DASD_NAME,DASD_PARTN_BITS,DASD_PER_MAJOR)
+ }
}
- FUNCTION_EXIT ("dasd_end_request");
- return;
-}
+};
-void
-dasd_wakeup (void)
+static major_info_t *
+get_new_major_info (void)
{
- wake_up (&dasd_waitq);
+ major_info_t *major_info = NULL;
+ unsigned long flags;
+
+ major_info = kmalloc (sizeof (major_info_t), GFP_KERNEL);
+ if (major_info) {
+ major_info_t *temp = dasd_major_info;
+ static major_info_t temp_major_info[] =
+ {
+ {
+ next:NULL,
+ gendisk: {
+ INIT_GENDISK(0,DASD_NAME,DASD_PARTN_BITS,DASD_PER_MAJOR)
+ }
+ }
+ };
+ spin_lock_irqsave (&dasd_major_lock, flags);
+ while (temp->next)
+ temp = temp->next;
+ temp->next = major_info;
+ spin_unlock_irqrestore (&dasd_major_lock, flags);
+ memcpy (major_info, temp_major_info, sizeof (major_info_t));
+ }
+ return major_info;
}
-int
-dasd_unregister_dasd (int irq, dasd_type_t dt, dev_info_t * info)
+static int
+dasd_register_major (major_info_t * major_info)
{
int rc = 0;
- FUNCTION_ENTRY ("dasd_unregister_dasd");
- INTERNAL_CHECK ("dasd_unregister_dasd not implemented%s\n", "");
- FUNCTION_EXIT ("dasd_unregister_dasd");
- return rc;
+ int major;
+
+ if (major_info == NULL) {
+ major_info = get_new_major_info ();
+ if (!major_info) {
+ printk (KERN_WARNING PRINTK_HEADER
+ "Cannot get memory to allocate another major number\n");
+ return -ENOMEM;
+ } else {
+ printk (KERN_INFO PRINTK_HEADER
+ "Created another major number\n");
+ }
+ }
+ major = major_info->gendisk.major;
+ rc = devfs_register_blkdev (major, DASD_NAME, &dasd_device_operations);
+ if (rc < 0) {
+ printk (KERN_WARNING PRINTK_HEADER
+ "Cannot register to major no %d, rc = %d\n", major, rc);
+ return rc;
+ }
+ if (major == 0) {
+ major = rc;
+ rc = 0;
+ }
+ major_info->dasd_device = (dasd_device_t **) kmalloc (DASD_PER_MAJOR * sizeof (dasd_device_t *),
+ GFP_ATOMIC);
+ if (!major_info->dasd_device)
+ goto out_devfs;
+ memset (major_info->dasd_device, 0, DASD_PER_MAJOR * sizeof (dasd_device_t *));
+ blk_size[major] =
+ (int *) kmalloc ((1 << MINORBITS) * sizeof (int), GFP_ATOMIC);
+ if (!blk_size[major])
+ goto out_dasd_device;
+ memset (blk_size[major], 0, (1 << MINORBITS) * sizeof (int));
+ blksize_size[major] =
+ (int *) kmalloc ((1 << MINORBITS) * sizeof (int), GFP_ATOMIC);
+ if (!blksize_size[major])
+ goto out_blk_size;
+ memset (blksize_size[major], 0, (1 << MINORBITS) * sizeof (int));
+ hardsect_size[major] =
+ (int *) kmalloc ((1 << MINORBITS) * sizeof (int), GFP_ATOMIC);
+ if (!hardsect_size[major])
+ goto out_blksize_size;
+ memset (hardsect_size[major], 0, (1 << MINORBITS) * sizeof (int));
+ max_sectors[major] =
+ (int *) kmalloc ((1 << MINORBITS) * sizeof (int), GFP_ATOMIC);
+ if (!max_sectors[major])
+ goto out_hardsect_size;
+ 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_max_sectors;
+ 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.major = major;
+ major_info->gendisk.next = gendisk_head;
+ major_info->gendisk.sizes = blk_size[major];
+ gendisk_head = &major_info->gendisk;
+ return major;
+out_max_sectors:
+ kfree(max_sectors[major]);
+out_hardsect_size:
+ kfree(hardsect_size[major]);
+out_blksize_size:
+ kfree(blksize_size[major]);
+out_blk_size:
+ kfree(blk_size[major]);
+out_dasd_device:
+ kfree(major_info->dasd_device);
+out_devfs:
+ devfs_unregister_blkdev(major, DASD_NAME);
+ return -ENOMEM;
}
-/* Below you find the functions already cleaned up */
-static dasd_type_t
-check_type (dev_info_t * info)
+static int
+dasd_unregister_major (major_info_t * major_info)
{
- dasd_type_t type = dasd_none;
+ int rc = 0;
+ int major;
+ struct gendisk *dd, *prev = NULL;
- FUNCTION_ENTRY ("check_type");
-#ifdef CONFIG_DASD_ECKD
- if (MATCH (info, == 0x3990, ||1, == 0x3390, ||1) ||
- MATCH (info, == 0x9343, ||1, == 0x9345, ||1) ||
- MATCH (info, == 0x3990, ||1, == 0x3380, ||1)) {
- type = dasd_eckd;
- } else
-#endif /* CONFIG_DASD_ECKD */
-#ifdef CONFIG_DASD_MDSK
- if ( MACHINE_IS_VM ) {
- type = dasd_mdsk;
- } else
-#endif /* CONFIG_DASD_MDSK */
- {
- type = dasd_none;
- }
-
- FUNCTION_EXIT ("check_type");
- return type;
+ if (major_info == NULL) {
+ return -EINVAL;
+ }
+ major = major_info->gendisk.major;
+ INIT_BLK_DEV(major,NULL,NULL,NULL);
+ blk_size[major] = NULL;
+ blksize_size[major] = NULL;
+ hardsect_size[major] = NULL;
+ max_sectors[major] = NULL;
+
+ /* do the gendisk stuff */
+ for (dd = gendisk_head; dd; dd = dd->next) {
+ if (dd == &major_info->gendisk) {
+ if (prev)
+ prev->next = dd->next;
+ else
+ gendisk_head = dd->next;
+ break;
+ }
+ prev = dd;
+ }
+ if (dd == NULL) {
+ return -ENOENT;
+ }
+ kfree (major_info->dasd_device);
+ kfree (blk_size[major]);
+ kfree (blksize_size[major]);
+ kfree (hardsect_size[major]);
+ kfree (max_sectors[major]);
+ kfree (major_info->gendisk.part);
+
+ 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;
+ }
+ if (major_info->gendisk.major > 128)
+ kfree (major_info);
+ return rc;
}
-static int
-dasd_read_characteristics (dasd_information_t * info)
+/*
+ * 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
+ */
+static inline dasd_device_t *
+dasd_device_from_kdev (kdev_t kdev)
{
- int rc;
- int ct = 0;
- dev_info_t *di;
- dasd_type_t dt;
+ major_info_t *major_info = dasd_major_info;
+ unsigned long flags;
- FUNCTION_ENTRY ("read_characteristics");
- if (info == NULL) {
- return -ENODEV;
- }
- di = &(info->info);
- if (di == NULL) {
- return -ENODEV;
+ spin_lock_irqsave (&dasd_major_lock, flags);
+ while (major_info &&
+ major_info->gendisk.major != MAJOR (kdev)) {
+ major_info = major_info->next;
+ }
+ spin_unlock_irqrestore (&dasd_major_lock, flags);
+ if (major_info)
+ 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 = dasd_major_info;
+ int devindex = dasd_devindex_from_devno (devno);
+ unsigned long flags;
+
+ spin_lock_irqsave (&dasd_major_lock, flags);
+ for (major_info = dasd_major_info; major_info;
+ major_info = major_info->next) {
+ if (devindex < DASD_PER_MAJOR) {
+ spin_unlock_irqrestore (&dasd_major_lock, flags);
+ return &major_info->dasd_device[devindex];
+ }
+ devindex -= DASD_PER_MAJOR;
}
- dt = check_type (di);
- /* Some cross-checks, if the cu supports RDC */
- if (MATCH (di, == 0x2835, ||1, ||1, ||1) ||
- MATCH (di, == 0x3830, ||1, ||1, ||1) ||
- MATCH (di, == 0x3830, ||1, ||1, ||1) ||
- MATCH (di, == 0x3990, <=0x03, == 0x3380, <=0x0d)) {
- PRINT_WARN ("Device %d (%x/%x at %x/%x) supports no RDC\n",
- info->info.irq,
- di->sid_data.dev_type,
- di->sid_data.dev_model,
- di->sid_data.cu_type,
- di->sid_data.cu_model);
- return -EINVAL;
+ spin_unlock_irqrestore (&dasd_major_lock, flags);
+ return NULL;
+}
+
+/* SECTION: managing dasd disciplines */
+
+/* anchor and spinlock for list of disciplines */
+static dasd_discipline_t *dasd_disciplines;
+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
+ */
+void
+dasd_discipline_enq (dasd_discipline_t * d)
+{
+ spin_lock (&discipline_lock);
+ d->next = dasd_disciplines;
+ dasd_disciplines = d;
+ spin_unlock (&discipline_lock);
+}
+
+/*
+ * function dasd_discipline_deq
+ * removes the discipline given as argument from the list of disciplines
+ */
+int
+dasd_discipline_deq (dasd_discipline_t * d)
+{
+ int rc = 0;
+ spin_lock (&discipline_lock);
+ if (dasd_disciplines == d) {
+ dasd_disciplines = dasd_disciplines->next;
+ } else {
+ dasd_discipline_t *b;
+ b = dasd_disciplines;
+ while (b && b->next != d)
+ b = b->next;
+ if (b != NULL) {
+ b->next = b->next->next;
+ } else {
+ rc = -ENOENT;
+ }
}
- switch (dt) {
-#ifdef CONFIG_DASD_ECKD
- case dasd_eckd:
- ct = 64;
- rc = read_dev_chars (info->info.irq,
- (void *) &(info->rdc_data), ct);
- break;
-#endif /* CONFIG_DASD_ECKD */
-#ifdef CONFIG_DASD_MDSK
- case dasd_mdsk:
- ct = 0;
+ spin_unlock (&discipline_lock);
+ return rc;
+}
+
+static inline dasd_discipline_t *
+dasd_find_discipline (dasd_device_t * device)
+{
+ dasd_discipline_t *temp;
+ for (temp = dasd_disciplines; temp != NULL; temp = temp->next) {
+ if (temp->id_check)
+ if (temp->id_check (&device->devinfo)) {
+ continue;
+ }
+ if (temp->check_characteristics) {
+ if (temp->check_characteristics (device)) {
+ continue;
+ }
+ }
break;
-#endif /* CONFIG_DASD_MDSK */
- default:
- INTERNAL_ERROR ("don't know dasd type %d\n", dt);
}
- if (rc) {
- PRINT_WARN ("RDC resulted in rc=%d\n", rc);
+ return temp;
+}
+
+/* SECTION: profiling stuff */
+
+static dasd_profile_info_t dasd_global_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;
+ long tottimeps, sectors;
+ dasd_device_t *device = cqr->device;
+
+ if (!cqr->req) /* safeguard against abnormal cqrs */
+ return;
+ sectors = ((struct request *) (cqr->req))->nr_sectors;
+ 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_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);
+}
+
+/* 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
+ */
+static inline void
+dasd_chanq_enq (dasd_chanq_t * q, ccw_req_t * cqr)
+{
+ if (q->head != NULL) {
+ q->tail->next = cqr;
+ } else
+ q->head = cqr;
+ cqr->next = NULL;
+ q->tail = cqr;
+ check_then_set (&cqr->status, CQR_STATUS_FILLED, CQR_STATUS_QUEUED);
+}
+
+/*
+ * function dasd_chanq_enq_head
+ * chains the cqr given as argument to the queue head
+ * has to be called with the queue lock (namely the s390_irq_lock) acquired
+ */
+static inline void
+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
+ */
+int
+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 -ENOENT;
+ prev->next = cqr->next;
+ if (prev->next == NULL)
+ q->tail = prev;
}
- FUNCTION_EXIT ("read_characteristics");
- return rc;
+ cqr->next = NULL;
+ return 0;
}
-/* How many sectors must be in a request to dequeue it ? */
-#define QUEUE_BLOCKS 25
-#define QUEUE_SECTORS (QUEUE_BLOCKS << dasd_info[di]->sizes.s2b_shift)
+/* SECTION: All the gendisk stuff */
+
+/*
+ * function dasd_partn_detect
+ * calls the function in genhd, which is appropriate to setup a partitioned disk
+ */
+static void
+dasd_partn_detect (dasd_device_t * dev)
+{
+ major_info_t *major_info = dev->major_info;
+ struct gendisk *dd = &major_info->gendisk;
+ int minor = MINOR (dev->kdev);
+
+ register_disk (dd,
+ MKDEV (dd->major, minor),
+ 1 << DASD_PARTN_BITS,
+ &dasd_device_operations,
+ (dev->sizes.blocks << dev->sizes.s2b_shift));
+}
-/* How often to retry an I/O before raising an error */
-#define DASD_MAX_RETRIES 5
+/* SECTION: Managing wrappers for ccwcache */
+/* array and spinlock of emergency requests */
+static ccw_req_t *dasd_emergency_req[DASD_EMERGENCY_REQUESTS];
+static spinlock_t dasd_emergency_req_lock = SPIN_LOCK_UNLOCKED;
-static inline
- cqr_t *
-dasd_cqr_from_req (struct request *req)
+/*
+ * function dasd_init_emergeny_req
+ * allocates emergeny requests
+ */
+static inline void __init
+dasd_init_emergency_req (void)
{
- cqr_t *cqr = NULL;
- int di;
- dasd_information_t *info;
+ int i;
+ for (i = 0; i < DASD_EMERGENCY_REQUESTS; i++) {
+ dasd_emergency_req[i] = (ccw_req_t *) get_free_page (GFP_KERNEL);
+ memset (dasd_emergency_req[i], 0, PAGE_SIZE);
+ }
+}
- if (!req) {
- PRINT_ERR ("No request passed!");
- return NULL;
+/*
+ * function dasd_cleanup_emergeny_req
+ * tries to free emergeny requests skipping those, which are currently in use
+ */
+static inline void
+dasd_cleanup_emergency_req (void)
+{
+ int i;
+ for (i = 0; i < DASD_EMERGENCY_REQUESTS; i++) {
+ if (dasd_emergency_req[i])
+ free_page ((long) (dasd_emergency_req[i]));
+ else
+ printk (KERN_WARNING PRINTK_HEADER
+ "losing page for emergency request in use\n");
}
- di = DEVICE_NR (req->rq_dev);
- info = dasd_info[di];
- if (!info)
- return NULL;
- /* if applicable relocate block */
- if (MINOR (req->rq_dev) & ((1 << PARTN_BITS) - 1) ) {
- req->sector +=
- dd_gendisk.part[MINOR(req->rq_dev)].start_sect;
- }
- /* Now check for consistency */
- if (!req->nr_sectors) {
- PRINT_WARN ("req: %p dev: %08x sector: %ld nr_sectors: %ld bh: %p\n",
- req, req->rq_dev, req->sector, req->nr_sectors, req->bh);
- return NULL;
+}
+
+/*
+ * function dasd_alloc_request
+ * tries to return space for a channel program of length cplength with
+ * additional data of size datasize.
+ * If the ccwcache cannot fulfill the request it tries the emergeny requests
+ * before giving up finally
+ * FIXME: initialization of ccw_req_t should be done by function of ccwcache
+ */
+ccw_req_t *
+dasd_alloc_request (char *magic, int cplength, int datasize)
+{
+ ccw_req_t *rv = NULL;
+ int i;
+ unsigned long flags;
+
+ if ((rv = ccw_alloc_request (magic, cplength, datasize)) != NULL) {
+ return rv;
}
- if (((req->sector + req->nr_sectors) >> 1) > info->sizes.kbytes) {
- printk (KERN_ERR PRINTK_HEADER
- "Requesting I/O past end of device %d\n",
- di);
- return NULL;
+ if ((((sizeof (ccw_req_t) + 7) & -8) +
+ cplength*sizeof(ccw1_t) + datasize) > PAGE_SIZE) {
+ BUG();
+ }
+ spin_lock_irqsave (&dasd_emergency_req_lock, flags);
+ for (i = 0; i < DASD_EMERGENCY_REQUESTS; i++) {
+ if (dasd_emergency_req[i] != NULL) {
+ rv = dasd_emergency_req[i];
+ dasd_emergency_req[i] = NULL;
+ break;
+ }
}
- cqr = dasd_disciplines[info->type]->get_req_ccw (di, req);
- if (!cqr) {
- PRINT_WARN ("empty CQR generated\n");
+ spin_unlock_irqrestore (&dasd_emergency_req_lock, flags);
+ if (rv) {
+ memset (rv, 0, PAGE_SIZE);
+ rv->cache = (kmem_cache_t *) (dasd_emergency_req + i);
+ strncpy ((char *) (&rv->magic), magic, 4);
+ ASCEBC ((char *) (&rv->magic), 4);
+ rv->cplength = cplength;
+ rv->datasize = datasize;
+ rv->data = (void *) ((long) rv + PAGE_SIZE - datasize);
+ rv->cpaddr = (ccw1_t *) ((long) rv + sizeof (ccw_req_t));
} else {
- cqr->req = req;
- cqr->int4cqr = cqr;
- cqr->devindex = di;
-#ifdef DASD_PROFILE
- asm volatile ("STCK %0":"=m" (cqr->buildclk));
-#endif /* DASD_PROFILE */
- if (atomic_compare_and_swap (CQR_STATUS_EMPTY,
- CQR_STATUS_FILLED,
- &cqr->status)) {
- PRINT_WARN ("cqr from req stat changed %d\n",
- atomic_read (&cqr->status));
- }
+ panic ("No way to fulfill request for I/O request\n");
}
- return cqr;
+ 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)
+{
+ if ((request->cache >= (kmem_cache_t *) dasd_emergency_req) &&
+ (request->cache < (kmem_cache_t *) (dasd_emergency_req +
+ DASD_EMERGENCY_REQUESTS))) {
+ *((ccw_req_t **) (request->cache)) = request;
+ } else {
+ ccw_free_request (request);
+ }
+}
+
+/* SECTION: Managing the device queues etc. */
+
+
+/*
+ * function dasd_start_IO
+ * attempts to start the IO and returns an appropriate return code
+ */
int
-dasd_start_IO (cqr_t * cqr)
+dasd_start_IO (ccw_req_t * cqr)
{
int rc = 0;
- int retries = DASD_SSCH_RETRIES;
- int di, irq;
-
- dasd_debug ((unsigned long) cqr); /* cqr */
+ dasd_device_t *device = cqr->device;
+ int irq;
+ unsigned long long now;
if (!cqr) {
- PRINT_WARN ("(start_IO) no cqr passed\n");
- return -EINVAL;
+ BUG ();
}
- if (cqr->magic != DASD_MAGIC) {
- PRINT_WARN ("(start_IO) magic number mismatch\n");
+ irq = device->devinfo.irq;
+ if (strncmp ((char *) &cqr->magic, device->discipline->ebcname, 4)) {
+ DASD_MESSAGE (KERN_WARNING, device,
+ " ccw_req_t 0x%08X magic doesn't match"
+ " discipline 0x%08X\n",
+ cqr->magic,
+ *(unsigned int *) device->discipline->name);
return -EINVAL;
}
- if (atomic_compare_and_swap (CQR_STATUS_QUEUED,
- CQR_STATUS_IN_IO,
- &cqr->status)) {
- PRINT_WARN ("start_IO: status changed %d\n",
- atomic_read (&cqr->status));
- atomic_set (&cqr->status, CQR_STATUS_ERROR);
- return -EINVAL;
- }
- di = cqr->devindex;
- irq = dasd_info[di]->info.irq;
- do {
- asm volatile ("STCK %0":"=m" (cqr->startclk));
- rc = do_IO (irq, cqr->cpaddr, (long) cqr, 0x00, cqr->options);
- switch (rc) {
- case 0:
- if (!(cqr->options & DOIO_WAIT_FOR_INTERRUPT))
- atomic_set_mask (DASD_CHANQ_BUSY,
- &dasd_info[di]->queue.flags);
- break;
- case -ENODEV:
- PRINT_WARN ("cqr %p: 0x%04x error, %d retries left\n",
- cqr, dasd_info[di]->info.devno, retries);
- break;
- case -EIO:
- PRINT_WARN ("cqr %p: 0x%04x I/O, %d retries left\n",
- cqr, dasd_info[di]->info.devno, retries);
- break;
- case -EBUSY: /* set up timer, try later */
-
- PRINT_WARN ("cqr %p: 0x%04x busy, %d retries left\n",
- cqr, dasd_info[di]->info.devno, retries);
- break;
- default:
-
- PRINT_WARN ("cqr %p: 0x%04x %d, %d retries left\n",
- cqr, rc, dasd_info[di]->info.devno,
- retries);
- break;
- }
- } while (rc && --retries);
- if (rc) {
- if (atomic_compare_and_swap (CQR_STATUS_IN_IO,
- CQR_STATUS_ERROR,
- &cqr->status)) {
- PRINT_WARN ("start_IO:(done) status changed %d\n",
- atomic_read (&cqr->status));
- atomic_set (&cqr->status, CQR_STATUS_ERROR);
- }
+
+ asm volatile ("STCK %0":"=m" (now));
+ rc = do_IO (irq, cqr->cpaddr, (long) cqr, cqr->lpm, cqr->options);
+ switch (rc) {
+ case 0:
+ break;
+ case -ENODEV:
+ check_then_set (&cqr->status,
+ CQR_STATUS_QUEUED, CQR_STATUS_FAILED);
+ break;
+ case -EIO:
+ check_then_set (&cqr->status,
+ CQR_STATUS_QUEUED, CQR_STATUS_FAILED);
+ break;
+ case -EBUSY:
+ DASD_MESSAGE (KERN_WARNING, device,"%s",
+ "device busy, retry later\n");
+ break;
+ default:
+ DASD_MESSAGE (KERN_ERR, device,
+ "line %d unknown RC=%d, please report"
+ " to linux390@de.ibm.com\n",
+ __LINE__, rc);
+ BUG();
+ break;
}
+ if (rc == 0) {
+ check_then_set (&cqr->status,
+ CQR_STATUS_QUEUED, CQR_STATUS_IN_IO);
+ cqr->startclk = now;
+ }
return rc;
}
-static inline
-void
-dasd_end_cqr (cqr_t * cqr, int uptodate)
+/*
+ * function sleep_on_req
+ * attempts to start the IO and waits for completion
+ * FIXME: replace handmade sleeping by wait_event
+ */
+static int
+sleep_on_req (ccw_req_t * req)
{
- struct request *req = cqr->req;
- asm volatile ("STCK %0":"=m" (cqr->endclk));
-#ifdef DASD_PROFILE
- dasd_profile_add (cqr);
-#endif /* DASD_PROFILE */
- dasd_chanq_deq (&dasd_info[cqr->devindex]->queue, cqr);
- if (req) {
- dasd_end_request (req, uptodate);
- }
-}
+ unsigned long flags;
+ int cs;
+ int rc = 0;
+ dasd_device_t *device = (dasd_device_t *) req->device;
-void
-dasd_dump_sense (devstat_t * stat)
+ 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)));
+ s390irq_spin_lock_irqsave (device->devinfo.irq, flags);
+ cs = req->status;
+ } while (cs != CQR_STATUS_DONE && cs != CQR_STATUS_FAILED);
+
+ s390irq_spin_unlock_irqrestore (device->devinfo.irq, flags);
+ if (cs == CQR_STATUS_FAILED) {
+ rc = -EIO;
+ }
+ return rc;
+
+} /* end 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)
{
- int sl, sct;
- if ( ! stat->flag | DEVSTAT_FLAG_SENSE_AVAIL) {
- PRINT_INFO("I/O status w/o sense data");
- } else {
- printk (KERN_INFO PRINTK_HEADER
- "-------------------I/O result:-----------\n");
- for (sl = 0; sl < 4; sl++) {
- printk (KERN_INFO PRINTK_HEADER "Sense:");
- for (sct = 0; sct < 8; sct++) {
- printk (" %2d:0x%02X", 8 * sl + sct,
- stat->ii.sense.data[8 * sl + sct]);
- }
- printk ("\n");
- }
+ struct buffer_head *bh;
+ while ((bh = req->bh) != NULL) {
+ int nsect = bh->b_size >> 9;
+ blk_finished_io (nsect);
+ req->bh = bh->b_reqnext;
+ bh->b_reqnext = NULL;
+ bh->b_end_io (bh, uptodate);
}
+ if (!end_that_request_first (req, uptodate, DASD_NAME)) {
+#ifndef DEVICE_NO_RANDOM
+ add_blkdev_randomness (MAJOR (req->rq_dev));
+#endif
+ end_that_request_last (req);
+ }
+ 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);
+ return &device->request_queue;
}
-static int
-register_dasd_last (int di)
+/*
+ * function dasd_check_expire_time
+ * check the request given as argument for expiration
+ * and returns 0 if not yet expired, nonzero else
+ */
+static inline int
+dasd_check_expire_time (ccw_req_t * cqr)
{
+ unsigned long long now;
int rc = 0;
- int minor;
- struct buffer_head *bh;
- rc = dasd_disciplines[dasd_info[di]->type]->fill_sizes_last (di);
- switch (rc) {
- case -EMEDIUMTYPE:
- dasd_info[di]->flags |= DASD_INFO_FLAGS_NOT_FORMATTED;
- break;
- }
- PRINT_INFO ("%ld kB <- 'soft'-block: %d, hardsect %d Bytes\n",
- dasd_info[di]->sizes.kbytes,
- dasd_info[di]->sizes.bp_block,
- dasd_info[di]->sizes.bp_sector);
- switch (dasd_info[di]->type) {
-#ifdef CONFIG_DASD_ECKD
- case dasd_eckd:
- dasd_info[di]->sizes.label_block = 2;
- break;
-#endif /* CONFIG_DASD_ECKD */
-#ifdef CONFIG_DASD_MDSK
- case dasd_mdsk:
- dasd_info[di]->sizes.label_block = -1;
- break;
-#endif /* CONFIG_DASD_ECKD */
- default:
- INTERNAL_CHECK ("Unknown dasd type %d\n", dasd_info[di]->type);
+ asm volatile ("STCK %0":"=m" (now));
+ if ( cqr->expires + cqr->startclk < now) {
+ DASD_MESSAGE (KERN_ERR, ((dasd_device_t*)cqr->device),
+ "IO timeout 0x%08lx%08lx usecs with req %p\n",
+ (long) (cqr->expires >> 44),
+ (long) (cqr->expires >> 12), cqr);
+ cqr->expires <<=1;
}
- minor = di << PARTN_BITS;
- dasd_blks[minor] = dasd_info[di]->sizes.kbytes;
- dasd_secsize[minor] = dasd_info[di]->sizes.bp_sector;
- dasd_blksize[minor] = dasd_info[di]->sizes.bp_block;
- dasd_maxsecs[minor] = 252<<dasd_info[di]->sizes.s2b_shift;
- dasd_secsize[minor+1] = dasd_info[di]->sizes.bp_sector;
- dasd_blksize[minor+1] = dasd_info[di]->sizes.bp_block;
- dasd_maxsecs[minor+1] = 252<<dasd_info[di]->sizes.s2b_shift;
-
- {
-#define DASD_NAME_PREFIX "dasd_"
- char * name = (char *) kmalloc ( 1+strlen (DASD_NAME_PREFIX) +
- 2 /* 0x */ + 4 /* devno */,
- GFP_KERNEL);
- sprintf ( name , DASD_NAME_PREFIX "%04x%c",
- dasd_info[di]->info.devno,'\0' );
- dasd_info[di] -> devfs_entry =
- devfs_register ( NULL /* dir */,
- name,
- DEVFS_FL_DEFAULT /* flags */,
- DASD_MAJOR, minor,
- 0755 /* mode */,
- &dasd_device_operations,
- (void *)dasd_info[di]);
- }
- /* end of that stuff */
return rc;
}
-void
-dasd_partn_detect ( int di )
+/*
+ * 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)
{
- int minor = di << PARTN_BITS;
- LOOP_CONTROL ("Setting partitions of DASD %d\n", di);
- register_disk (&dd_gendisk,
- MKDEV(DASD_MAJOR,minor),
- 1 << PARTN_BITS,
- &dasd_device_operations,
- dasd_info[di]->sizes.kbytes << 1);
+ dasd_device_t *device = cqr->device;
+ dasd_discipline_t *discipline = device->discipline;
+
+ asm volatile ("STCK %0":"=m" (cqr->endclk));
+ if (cqr->req) {
+ dasd_end_request (cqr->req, (cqr->status == CQR_STATUS_DONE));
+ dasd_profile_add (cqr);
+ /* free request if nobody is waiting on it */
+ dasd_free_request (cqr);
+ } else {
+ /* during format we don't have the request structure */
+ /* notify sleeping task about finished postprocessing */
+ wake_up (&device->wait_q);
+ }
+ return;
}
-void
-dasd_do_chanq (void)
+/*
+ * function dasd_process_queues
+ * transfers the requests on the queue given as argument to the chanq
+ * if possible, the request ist started on a fastpath
+ */
+static void
+dasd_process_queues (dasd_device_t *device)
{
- dasd_chanq_t *qp = NULL;
- cqr_t *cqr;
- long flags;
- int irq;
- int tasks;
- atomic_set (&bh_scheduled, 0);
- dasd_debug (0xc4c40000); /* DD */
- while ((tasks = atomic_read(&chanq_tasks)) != 0) {
-/* initialization and wraparound */
- if (qp == NULL) {
- dasd_debug (0xc4c46df0); /* DD_0 */
- qp = cq_head;
- if (!qp) {
- dasd_debug (0xc4c46ff1); /* DD?1 */
- dasd_debug (tasks);
- PRINT_ERR("Mismatch of NULL queue pointer and "
- "still %d chanq_tasks to do!!\n"
- "Please send output of /proc/dasd/debug "
- "to Linux390@de.ibm.com\n", tasks);
- atomic_set(&chanq_tasks,0);
- break;
- }
- }
-/* Get first request */
- dasd_debug ((unsigned long) qp);
- cqr = (cqr_t *) (qp->head);
-/* empty queue -> dequeue and proceed */
- if (!cqr) {
- dasd_chanq_t *nqp = qp->next_q;
- cql_deq (qp);
- qp = nqp;
- continue;
- }
-/* process all requests on that queue */
- do {
- cqr_t *next;
- dasd_debug ((unsigned long) cqr); /* cqr */
- if (cqr->magic != DASD_MAGIC) {
- dasd_debug (0xc4c46ff2); /* DD?2 */
- panic ( PRINTK_HEADER "do_cq:"
- "magic mismatch %p -> %x\n",
- cqr, cqr -> magic);
- break;
- }
- irq = dasd_info[cqr->devindex]->info.irq;
- s390irq_spin_lock_irqsave (irq, flags);
- switch (atomic_read (&cqr->status)) {
- case CQR_STATUS_IN_IO:
- dasd_debug (0xc4c4c9d6); /* DDIO */
- cqr = NULL;
- break;
- case CQR_STATUS_QUEUED:
- dasd_debug (0xc4c4e2e3); /* DDST */
- if (dasd_start_IO (cqr) == 0) {
- atomic_dec (&chanq_tasks);
- cqr = NULL;
- }
- break;
- case CQR_STATUS_ERROR:
- dasd_debug (0xc4c4c5d9); /* DDER */
- dasd_dump_sense (cqr->dstat);
- if ( ++ cqr->retries < 2 ) {
- atomic_set (&cqr->status,
- CQR_STATUS_QUEUED);
- dasd_debug (0xc4c4e2e3); /* DDST */
- if (dasd_start_IO (cqr) == 0) {
- atomic_dec ( &qp ->
- dirty_requests);
- atomic_dec (&chanq_tasks);
- cqr = NULL;
- }
- } else {
- atomic_set (&cqr->status,
- CQR_STATUS_FAILED);
- }
- break;
- case CQR_STATUS_DONE:
- next = cqr->next;
- dasd_debug (0xc4c49692); /* DDok */
- dasd_end_cqr (cqr, 1);
- atomic_dec (&chanq_tasks);
- cqr = next;
- break;
- case CQR_STATUS_FAILED:
- next = cqr->next;
- dasd_debug (0xc4c47a7a); /* DD:: */
- if ( ! ( dasd_info[cqr->devindex]-> flags &
- DASD_INFO_FLAGS_INITIALIZED ) ) {
- dasd_info[cqr->devindex]-> flags |=
- DASD_INFO_FLAGS_INITIALIZED |
- DASD_INFO_FLAGS_NOT_FORMATTED;
- }
- dasd_end_cqr (cqr, 0);
- atomic_dec ( &qp -> dirty_requests );
- atomic_dec (&chanq_tasks);
- cqr = next;
- break;
- default:
- PRINT_WARN ("unknown cqrstatus\n");
- cqr = NULL;
+ 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) {
+ if ((qp -> head -> dstat -> flag & DEVSTAT_HALT_FUNCTION) ||
+ (qp->head->retries-- == 0 ) ||
+ (device->discipline->erp_action==NULL) ||
+ ((erp_action=device->discipline->erp_action(qp->head))==NULL)||
+ ((erp_cqr = erp_action(qp->head))== NULL)) {
+ check_then_set (&qp->head->status,
+ CQR_STATUS_ERROR,
+ CQR_STATUS_FAILED);
+ continue;
+ } else {
+ dasd_chanq_enq_head (qp, erp_cqr);
+ /* chain of completed requests is now broken */
+ break;
}
- s390irq_spin_unlock_irqrestore (irq, flags);
- } while (cqr);
- qp = qp->next_q;
- }
- spin_lock (&io_request_lock);
- do_dasd_request (&blk_dev[DASD_MAJOR].request_queue);
- spin_unlock (&io_request_lock);
- dasd_debug (0xc4c46d6d); /* DD__ */
+ } else if ( qp -> head -> refers ) { /* we deal with an ERP */
+ char *uptodatestr;
+ if ( qp -> head -> status == CQR_STATUS_DONE) {
+ uptodatestr = "ERP successful";
+ } else {
+ uptodatestr = "ERP unsuccessful";
+ }
+
+ if (device->discipline->erp_postaction == NULL ||
+ ((erp_postaction = device->discipline->erp_postaction (qp->head)) == NULL)) {
+ /*
+ * maybe we shoud set it to FAILED,
+ * because we are very paranoid ;)
+ */
+ erp_postaction = default_erp_postaction;
+ }
+ DASD_MESSAGE (KERN_INFO, device,
+ "%s: postaction [<%p>]\n",
+ uptodatestr, erp_postaction);
+ 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;
+ }
+ if ( cqr )
+ cqr -> next = NULL;
+ /* Now we try to fetch requests from the request queue */
+ for (temp = cqr; temp != NULL ;temp=temp-> next )
+ if ( temp ->status == CQR_STATUS_QUEUED)
+ chanq_max_size --;
+ while ( (! list_empty(&queue->queue_head)) &&
+ (req=dasd_next_request(queue)) != NULL) {
+ /* queue empty or certain critera fulfilled -> transfer */
+ if ( qp -> head == NULL ||
+ chanq_max_size > 0 ||
+ (req->nr_sectors >= chanq_min_size)) {
+ ccw_req_t *cqr;
+ /* relocate request according to partition table */
+ req->sector += device->major_info->gendisk.part[MINOR (req->rq_dev)].start_sect;
+ cqr = device->discipline->build_cp_from_req (device, req);
+ if (cqr == NULL) {
+ DASD_MESSAGE (KERN_WARNING, device,
+ "CCW creation failed on request %p\n", req);
+ /* revert relocation of request */
+ req->sector -= device->major_info->gendisk.part[MINOR (req->rq_dev)].start_sect;
+ break; /* terminate request queue loop */
+
+ }
+#ifdef CONFIG_DYNAMIC_QUEUE_MIN_SIZE
+ chanq_min_size = (chanq_min_size + req->nr_sectors)>>1;
+#endif /* CONFIG_DYNAMIC_QUEUE_MIN_SIZE */
+ dasd_dequeue_request(queue,req);
+ dasd_chanq_enq (qp, cqr);
+ } else { /* queue not empty OR criteria not met */
+ break; /* terminate request queue loop */
+ }
+ }
+ /* we process the requests with non-final status */
+ if ( qp -> head ) {
+ switch ( qp->head->status ) {
+ case CQR_STATUS_QUEUED:
+ /* try to start the first I/O that can be started */
+ if ( device->discipline->start_IO (qp->head) != 0)
+ BUG();
+ break;
+ case CQR_STATUS_IN_IO:
+ /* Check, if to invoke the missing interrupt handler */
+ if ( dasd_check_expire_time (qp->head) ) {
+ /* to be filled with MIH */
+ }
+ break;
+ default:
+ BUG();
+ }
+ }
+ /* Now clean the requests with final status */
+ while ( final_requests ) {
+ cqr = final_requests;
+ final_requests = cqr-> next;
+ dasd_finalize_request( cqr );
+ }
+ s390irq_spin_unlock_irqrestore (irq, flags);
}
/*
- The request_fn is called from ll_rw_blk for any new request.
- We use it to feed the chanqs.
- This implementation assumes we are serialized by the io_request_lock.
+ * 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);
+}
-#define QUEUE_THRESHOLD 5
-
+/*
+ * function dasd_schedule_bh
+ * schedules the request_fn to run with next run_bh cycle
+ */
void
-do_dasd_request (request_queue_t *queue)
+dasd_schedule_bh (dasd_device_t *device)
{
- struct request *req;
- cqr_t *cqr;
- dasd_chanq_t *q;
- long flags;
- int di, irq, go;
- int broken, busy;
-
- dasd_debug (0xc4d90000); /* DR */
- dasd_debug ((unsigned long) __builtin_return_address(0));
- go = 1;
- while (go && !list_empty(&queue->queue_head)) {
- req = blkdev_entry_next_request(&queue->queue_head);
- di = DEVICE_NR (req->rq_dev);
- dasd_debug ((unsigned long) req); /* req */
- dasd_debug (0xc4d90000 + /* DR## */
- ((((di/16)<9?(di/16)+0xf0:(di/16)+0xc1))<<8) +
- (((di%16)<9?(di%16)+0xf0:(di%16)+0xc1)));
- irq = dasd_info[di]->info.irq;
- s390irq_spin_lock_irqsave (irq, flags);
- q = &dasd_info[di]->queue;
- busy = atomic_read(&q->flags) & DASD_CHANQ_BUSY;
- broken = atomic_read(&q->flags)&DASD_REQUEST_Q_BROKEN;
- if ( ! busy ||
- ( ! broken &&
- (req->nr_sectors >= QUEUE_SECTORS))) {
- blkdev_dequeue_request(req);
- /*
- printk ( KERN_INFO "0x%04x %c %d %d\n",
- req->rq_dev,req->cmd ?'w':'r',
- req->sector,req->nr_sectors);
- */
- cqr = dasd_cqr_from_req (req);
- if (!cqr) {
- dasd_debug (0xc4d96ff1); /* DR?1 */
- dasd_end_request (req, 0);
- goto cont;
- }
- dasd_debug ((unsigned long) cqr); /* cqr */
- dasd_chanq_enq (q, cqr);
- if (!(atomic_read (&q->flags) &
- DASD_CHANQ_ACTIVE)) {
- cql_enq_head (q);
- }
- if ( ! busy ) {
- atomic_clear_mask (DASD_REQUEST_Q_BROKEN,
- &q->flags );
- if (atomic_read( &q->dirty_requests) == 0 ) {
- if ( dasd_start_IO (cqr) == 0 ) {
- } else {
- atomic_inc (&chanq_tasks);
- schedule_bh (dasd_do_chanq);
- }
- }
- }
- } else {
- dasd_debug (0xc4d9c2d9); /* DRBR */
- atomic_set_mask (DASD_REQUEST_Q_BROKEN, &q->flags );
- go = 0;
+ /* 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
+ */
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+static void
+do_dasd_request (request_queue_t * queue)
+{
+ dasd_device_t *device = (dasd_device_t *)
+ ((long)queue-(long)offsetof (dasd_device_t, request_queue));
+ dasd_process_queues (device);
+}
+#else
+static void
+do_dasd_request (void)
+{
+ major_info_t *major_info;
+ dasd_device_t *device;
+ int i;
+
+ for (major_info = dasd_major_info;
+ major_info != NULL;
+ major_info = major_info->next) {
+ for (i = 0; i < DASD_PER_MAJOR; i++) {
+ device = major_info->dasd_device[i];
+ if (!device)
+ continue; /* remove indentation level */
+ dasd_process_queues (device);
}
- cont:
- s390irq_spin_unlock_irqrestore (irq, flags);
}
- dasd_debug (0xc4d96d6d); /* DR__ */
}
+#endif /* LINUX_IS_24 */
+
+
+
+/*
+ * function dasd_int_handler
+ * is the DASD driver's default interrupt handler for SSCH-IO
+ */
void
-dasd_handler (int irq, void *ds, struct pt_regs *regs)
+dasd_int_handler (int irq, void *ds, struct pt_regs *regs)
{
- devstat_t *stat = (devstat_t *) ds;
int ip;
- cqr_t *cqr;
- int done_fast_io = 0;
-
- dasd_debug (0xc4c80000); /* DH */
- if (!stat)
- PRINT_ERR ("handler called without devstat");
+ int devno;
+ ccw_req_t *cqr;
+ dasd_device_t *device;
+ unsigned long long now;
+#ifdef ERP_DEBUG
+ static int counter;
+#endif
+ dasd_era_t era = dasd_era_none; /* default is everything is okay */
+ devstat_t *stat = (devstat_t *)ds;
+
+ asm volatile ("STCK %0":"=m" (now));
+ if (stat == NULL) {
+ BUG();
+ }
ip = stat->intparm;
- dasd_debug (ip); /* intparm */
- switch (ip) { /* filter special intparms... */
- case 0x00000000: /* no intparm: unsolicited interrupt */
- dasd_debug (0xc4c8a489); /* DHui */
- PRINT_INFO ("Unsolicited interrupt on device %04X\n",
- stat->devno);
- dasd_dump_sense (stat);
+ if (!ip) { /* no intparm: unsolicited interrupt */
+ printk (KERN_INFO PRINTK_HEADER
+ "unsolicited interrupt: irq0x%x devno%04X\n",
+ irq,stat->devno);
return;
- default:
- if (ip & 0x80000001) {
- dasd_debug (0xc4c8a489); /* DHui */
- PRINT_INFO ("Spurious interrupt %08x on device %04X\n",
- ip, stat->devno);
- return;
- }
- cqr = (cqr_t *) ip;
- if (cqr->magic != DASD_MAGIC) {
- dasd_debug (0xc4c86ff1); /* DH?1 */
- PRINT_ERR ("handler:magic mismatch on %p %08x\n",
- cqr, cqr->magic);
- return;
- }
- asm volatile ("STCK %0":"=m" (cqr->stopclk));
- if ( ( stat->cstat == 0x00 &&
- stat->dstat == (DEV_STAT_CHN_END|DEV_STAT_DEV_END) ) ||
- dasd_erp_examine ( cqr ) == dasd_era_none ) {
- dasd_debug (0xc4c89692); /* DHok */
- if (atomic_compare_and_swap (CQR_STATUS_IN_IO,
- CQR_STATUS_DONE,
- &cqr->status)) {
- PRINT_WARN ("handler: cqrstat changed%d\n",
- atomic_read (&cqr->status));
- atomic_set(&cqr->status, CQR_STATUS_DONE);
- }
- if ( ! ( dasd_info[cqr->devindex]-> flags &
- DASD_INFO_FLAGS_INITIALIZED ) ) {
- int rc = register_dasd_last ( cqr->devindex );
- dasd_info[cqr->devindex]-> flags |=
- DASD_INFO_FLAGS_INITIALIZED;
- if ( rc ) {
- dasd_info[cqr->devindex]->flags &=
- ~DASD_INFO_FLAGS_NOT_FORMATTED;
- } else {
- dasd_info[cqr->devindex]->flags |=
- DASD_INFO_FLAGS_NOT_FORMATTED;
- }
+ }
+ if (ip & 0x80000001) {
+ printk (KERN_INFO PRINTK_HEADER
+ "spurious interrupt: irq0x%x devno%04X, parm %08x\n",
+ irq,stat->devno,ip);
+ return;
+ }
+ cqr = (ccw_req_t *)(long)ip;
+ device = (dasd_device_t *) cqr->device;
+ if (device == NULL || device != ds-offsetof(dasd_device_t,dev_status)) {
+ BUG();
+ }
+ devno = device->devinfo.devno;
+ if (device->devinfo.irq != irq) {
+ BUG();
+ }
+ if (strncmp (device->discipline->ebcname, (char *) &cqr->magic, 4)) {
+ BUG();
+ }
+#ifdef ERP_DEBUG
+ counter++;
+ if ( counter % 137 == 0 ) {
+ stat->flag |= DEVSTAT_FLAG_SENSE_AVAIL;
+ stat->cstat = 0x00;
+ stat->dstat = 0x02;
+ memset(stat->ii.sense.data,0,32);
+ stat->ii.sense.data[2] = 0x06;
+ stat->ii.sense.data[4] = 0x04;
+ stat->ii.sense.data[5] = 0x60;
+ stat->ii.sense.data[6] = 0x41;
+ stat->ii.sense.data[8] = 0xff;
+ stat->ii.sense.data[9] = 0xff;
+ stat->ii.sense.data[15] = 0x05;
+ stat->ii.sense.data[16] = 0x21;
+ stat->ii.sense.data[18] = 0x60;
+ stat->ii.sense.data[19] = 0x3b;
+ stat->ii.sense.data[20] = 0x24;
+ stat->ii.sense.data[21] = 0x61;
+ stat->ii.sense.data[22] = 0x65;
+ stat->ii.sense.data[23] = 0x03;
+ stat->ii.sense.data[24] = 0x04;
+ stat->ii.sense.data[25] = 0x10;
+ stat->ii.sense.data[26] = 0x4e;
+ }
+#endif
+ /* first of all lets try to find out the appropriate era_action */
+ if ( stat->flag & DEVSTAT_FLAG_SENSE_AVAIL ||
+ stat->dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END) ) {
+ /* anything abnormal ? */
+ if ( device->discipline->examine_error == NULL ||
+ stat->flag & DEVSTAT_HALT_FUNCTION ) {
+ era = dasd_era_fatal;
+ } else {
+ era = device->discipline->examine_error (cqr, stat);
+ }
+ }
+ if ( era == dasd_era_none ) {
+ if (device->level == DASD_DEVICE_LEVEL_ANALYSIS_PENDING)
+ device->level = DASD_DEVICE_LEVEL_ANALYSIS_PREPARED;
+ check_then_set(&cqr->status,
+ CQR_STATUS_IN_IO, CQR_STATUS_DONE);
+ cqr->stopclk=now;
+ cqr=cqr->next;
+ /* start the next queued request if possible -> fast_io */
+ if (cqr->status == CQR_STATUS_QUEUED) {
+ if (device->discipline->start_IO (cqr) != 0) {
+ printk (KERN_WARNING PRINTK_HEADER
+ "Interrupt fastpath failed!\n");
+ }
+ }
+ } else { /* error */
+ if (cqr->dstat == NULL)
+ cqr->dstat = kmalloc (sizeof (devstat_t), GFP_ATOMIC);
+ if (cqr->dstat) {
+ memcpy (cqr->dstat, stat, sizeof (devstat_t));
+ } else {
+ PRINT_ERR ("no memory for dstat...ignoring\n");
+ }
+ /* dump sense data */
+ if (device->discipline &&
+ device->discipline->dump_sense) {
+ char *errmsg = device->discipline->dump_sense (device, cqr);
+ if (errmsg != NULL) {
+ printk ("Sense data:\n%s", errmsg);
+ free_page ((unsigned long) errmsg);
+ } else {
+ printk (KERN_WARNING PRINTK_HEADER
+ "No memory to dump error message\n");
+ }
+ }
+ switch(era) {
+ case dasd_era_fatal:
+ check_then_set (&cqr->status,CQR_STATUS_IN_IO,
+ CQR_STATUS_FAILED);
+ break;
+ case dasd_era_recover:
+ check_then_set (&cqr->status,CQR_STATUS_IN_IO,
+ CQR_STATUS_ERROR);
+ break;
+ default:
+ BUG();
+ }
+ }
+ dasd_schedule_bh (device);
+}
+
+/* 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 *
+default_erp_action (ccw_req_t * cqr)
+{
+ ccw_req_t *erp = ccw_alloc_request ((char *) &cqr->magic, 1, 0);
+
+ printk (KERN_WARNING PRINTK_HEADER
+ "Default ERP called... \n");
+
+ if (!erp)
+ return NULL;
+
+ erp->cpaddr->cmd_code = CCW_CMD_TIC;
+ erp->cpaddr->cda = (__u32)(void *)cqr->cpaddr;
+ erp->function = default_erp_action;
+ erp->refers = cqr;
+ erp->device = cqr->device;
+ erp->magic = cqr->magic;
+ erp->retries = 16;
+
+ erp->status = CQR_STATUS_FILLED;
+ return erp;
+}
+
+/*
+ * 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.
+ *
+ * PARAMETER
+ * erp current erp_head
+ *
+ * RETURN VALUES
+ * cqr pointer to the original CQR
+ */
+ccw_req_t *
+default_erp_postaction (ccw_req_t * erp)
+{
+ dasd_device_t *device = NULL;
+ ccw_req_t *free_erp;
+ int success;
+
+ device = (dasd_device_t *) (erp->device);
+
+ if (erp->status == CQR_STATUS_DONE)
+ success = 1;
+ else
+ success = 0;
+
+ if (erp->refers == NULL || erp->function == NULL) {
+ BUG();
+ }
+ if (erp->function != default_erp_action) {
+ printk (KERN_WARNING PRINTK_HEADER
+ "default ERP postaction called ERP action [<%p>]\n",
+ erp->function);
+ }
+ /* free all ERPs - but NOT the original cqr */
+
+ while (erp->refers != NULL) {
+ free_erp = erp;
+ erp = erp->refers;
+ dasd_chanq_deq (&device->queue, free_erp);
+ dasd_free_request (free_erp);
+ }
+
+ /* save ptr to original cqr */
+
+ /*
+ * printk (KERN_INFO PRINTK_HEADER
+ * "default_erp_postaction - left original request = %p \n",erp);
+ */
+ /* set corresponding status to original cqr */
+ if (success) {
+ check_then_set (&erp->status, CQR_STATUS_ERROR,
+ CQR_STATUS_DONE);
+ } else {
+ check_then_set (&erp->status, CQR_STATUS_ERROR,
+ CQR_STATUS_FAILED);
+ }
+
+ /* print current erp_chain */
+#if 0
+ printk (KERN_WARNING PRINTK_HEADER
+ "default ERP postaction finished with remaining chain:\n");
+ {
+ ccw_req_t *temp_erp = NULL;
+ for (temp_erp = erp; temp_erp != NULL; temp_erp = temp_erp->refers) {
+ printk (KERN_WARNING PRINTK_HEADER
+ " erp %p refers to %p \n",
+ temp_erp, temp_erp->refers);
+ }
+ }
+#endif
+ return erp;
+} /* 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;
+ ccw_req_t *req = NULL;
+ format_data_t temp =
+ {
+ fdata->start_unit,
+ fdata->stop_unit,
+ fdata->blksize,
+ fdata->intensity
+ };
+
+ if (device->open_count != 1) {
+ DASD_MESSAGE (KERN_INFO, device,
+ "device is already open %d times",
+ device->open_count);
+ return -EINVAL;
+ }
+ if (!device->discipline->format_device) {
+ return -EINVAL;
+ }
+ /* downgrade state of the device */
+ dasd_set_device_level (device->devinfo.irq,
+ DASD_DEVICE_LEVEL_RECOGNIZED,
+ device->discipline,
+ 0);
+ DASD_MESSAGE (KERN_INFO, device, "Starting format from %d to %d (%d B blocks flags %d",fdata->start_unit,fdata->stop_unit,fdata->blksize,fdata->intensity);
+ /* Invalidate first track */
+ if (fdata->start_unit == DASD_FORMAT_DEFAULT_START_UNIT &&
+ fdata->stop_unit == DASD_FORMAT_DEFAULT_STOP_UNIT &&
+ fdata->intensity == DASD_FORMAT_DEFAULT_INTENSITY ) {
+ format_data_t temp2 =
+ {0, 0, DASD_FORMAT_DEFAULT_BLOCKSIZE, 0x04};
+ DASD_MESSAGE (KERN_INFO, device, "%s", "Invalidating first track...");
+ req = device->discipline->format_device (device, &temp2);
+ if (req) {
+ rc = sleep_on_req (req);
+ dasd_free_request (req); /* request is no longer used */
+ } else {
+ rc = -EINVAL;
+ }
+ if (rc) {
+ printk (KERN_WARNING PRINTK_HEADER "Can't invalidate Track 0\n");
+ }
+ temp.start_unit++;
+ DASD_MESSAGE (KERN_INFO, device, "%s", "...Invalidation complete");
+ }
+ /* format remainnig tracks of device */
+ while (!rc &&
+ ((req = device->discipline->format_device (device, &temp)) !=
+ NULL)) {
+ if ((rc = sleep_on_req (req)) != 0) {
+ DASD_MESSAGE (KERN_WARNING, device,
+ " Formatting failed with rc = %d\n",
+ rc);
+ break;
+ }
+ dasd_free_request (req); /* request is no longer used */
+ temp.start_unit++;
+ }
+ if (!rc &&
+ req == NULL) {
+ if (fdata->start_unit == DASD_FORMAT_DEFAULT_START_UNIT &&
+ fdata->stop_unit == DASD_FORMAT_DEFAULT_STOP_UNIT &&
+ fdata->intensity == DASD_FORMAT_DEFAULT_INTENSITY ) {
+ format_data_t temp2 =
+ {0, 0, fdata->blksize, fdata->intensity};
+ DASD_MESSAGE (KERN_INFO, device, "%s", "Revalidating first track...");
+ req = device->discipline->format_device (device, &temp2);
+ if (req) {
+ rc = sleep_on_req (req);
+ dasd_free_request (req); /* request is no longer used */
+ } else {
+ rc = -EINVAL;
}
- if (cqr->next) {
- dasd_debug (0xc4c8e2e3); /* DHST */
- if (dasd_start_IO (cqr->next) == 0) {
- done_fast_io = 1;
- } else {
- atomic_inc (&chanq_tasks);
- }
+ if (rc) {
+ printk (KERN_WARNING PRINTK_HEADER
+ "Can't revalidate Track 0\n");
}
- break;
+ DASD_MESSAGE (KERN_INFO, device, "%s", "...Revalidation complete");
}
- /* only visited in case of error ! */
- dasd_debug (0xc4c8c5d9); /* DHER */
- if (!cqr->dstat)
- cqr->dstat = kmalloc (sizeof (devstat_t),
- GFP_ATOMIC);
- if (cqr->dstat) {
- memcpy (cqr->dstat, stat, sizeof (devstat_t));
- } else {
- PRINT_ERR ("no memory for dstat\n");
+ } /* end if no more requests */
+ if (rc)
+ DASD_MESSAGE (KERN_WARNING, device,
+ "%s", " Formatting finished unsuccessfully");
+ else
+ DASD_MESSAGE (KERN_INFO, device,
+ "%s", " Formatting finished successfully");
+
+ /* re-activate device even if formatting was unsuccessful */
+ /* Horst Hummel - 17/10/00 - ITPM PL020062RSC */
+ dasd_set_device_level (device->devinfo.irq,
+ DASD_DEVICE_LEVEL_ANALYSIS_PREPARED,
+ device->discipline,
+ 0);
+ udelay (1500000);
+
+ dasd_set_device_level (device->devinfo.irq,
+ DASD_DEVICE_LEVEL_ANALYSED,
+ device->discipline,
+ 0);
+
+ return rc;
+} /* end dasd_format */
+
+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 BLKGETSIZE:{ /* Return device size */
+ long blocks = blk_size[MAJOR (inp->i_rdev)][MINOR (inp->i_rdev)] << 1;
+ rc = copy_to_user ((long *) data, &blocks, sizeof (long));
+ if (rc)
+ rc = -EFAULT;
+ break;
+ }
+ case BLKRRPART:{
+ if (!capable(CAP_SYS_ADMIN)) {
+ rc = -EACCES;
+ break;
+ }
+ fsync_dev(inp->i_rdev);
+ dasd_partn_detect (device);
+ invalidate_buffers(inp->i_rdev);
+ rc = 0;
+ break;
+ }
+ case HDIO_GETGEO:{
+ struct hd_geometry geo =
+ {0,};
+ if (device->discipline->fill_geometry)
+ device->discipline->fill_geometry (device, &geo);
+ rc = copy_to_user ((struct hd_geometry *) data, &geo,
+ sizeof (struct hd_geometry));
+ if (rc)
+ rc = -EFAULT;
+ break;
}
- /* errorprocessing */
- atomic_set (&cqr->status, CQR_STATUS_ERROR);
- atomic_inc (&dasd_info[cqr->devindex]->queue.dirty_requests);
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+ case BLKSSZGET:
+ case BLKROSET:
+ case BLKROGET:
+ case BLKRASET:
+ case BLKRAGET:
+ case BLKFLSBUF:
+ case BLKPG:
+ case BLKELVGET:
+ case BLKELVSET:
+ return blk_ioctl(inp->i_rdev, no, data);
+ break;
+#else
+ case BLKRASET:
+ if(!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ if(!dev || arg > 0xff)
+ return -EINVAL;
+ read_ahead[MAJOR(dev)] = arg;
+ rc = 0;
+ break;
+ case BLKRAGET:
+ if (!arg)
+ return -EINVAL;
+ rc = put_user(read_ahead[MAJOR(dev)], (long *) arg);
+ break;
+ case BLKSSZGET: {
+ /* Block size of media */
+ rc = copy_to_user((int *)data,
+ &blksize_size[MAJOR(device->kdev)]
+ [MINOR(device->kdev)],
+ sizeof(int)) ? -EFAULT : 0;
}
- if (done_fast_io == 0)
- atomic_clear_mask (DASD_CHANQ_BUSY,
- &dasd_info[cqr->devindex]->queue.flags);
-
- if (cqr->flags & DASD_DO_IO_SLEEP) {
- dasd_debug (0xc4c8a6a4); /* DHwu */
- dasd_wakeup ();
- } else if (! (cqr->options & DOIO_WAIT_FOR_INTERRUPT) ){
- dasd_debug (0xc4c8a293); /* DHsl */
- atomic_inc (&chanq_tasks);
- schedule_bh (dasd_do_chanq);
- } else {
- dasd_debug (0x64686f6f); /* DH_g */
- dasd_debug (cqr->flags); /* DH_g */
+ RO_IOCTLS (inp->i_rdev, data);
+ case BLKFLSBUF:{
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ fsync_dev(inp->i_rdev);
+ invalidate_buffers(inp->i_rdev);
+ rc = 0;
+ break;
+ }
+#endif /* LINUX_IS_24 */
+ case BIODASDRSID:{
+ rc = copy_to_user ((void *) data,
+ &(device->devinfo.sid_data),
+ sizeof (senseid_t)) ? -EFAULT : 0;
+ break;
+ }
+ case BIODASDRWTB:{
+ int offset = 0;
+ int xlt;
+ rc = copy_from_user (&xlt, (void *) data,
+ sizeof (int)) ? -EFAULT : 0;
+ if (rc)
+ break;
+ offset = major_info->gendisk.part[MINOR (inp->i_rdev)].start_sect >>
+ device->sizes.s2b_shift;
+ xlt += offset;
+ rc = copy_to_user ((void *) data, &xlt,
+ sizeof (int)) ? -EFAULT : 0;
+ break;
+ }
+ case BIODASDFORMAT:{
+ /* fdata == NULL is a valid arg to dasd_format ! */
+ int partn;
+ format_data_t fdata =
+ {
+ DASD_FORMAT_DEFAULT_START_UNIT,
+ DASD_FORMAT_DEFAULT_STOP_UNIT,
+ DASD_FORMAT_DEFAULT_BLOCKSIZE,
+ DASD_FORMAT_DEFAULT_INTENSITY};
+
+ if (!capable(CAP_SYS_ADMIN)) {
+ rc = -EACCES;
+ break;
+ }
+ if (data) {
+ rc = copy_from_user (&fdata, (void *) data,
+ sizeof (format_data_t));
+ if (rc) {
+ rc = -EFAULT;
+ break;
+ }
+ }
+ partn = MINOR (inp->i_rdev) & ((1 << major_info->gendisk.minor_shift) - 1);
+ if (partn != 0) {
+ printk (KERN_WARNING PRINTK_HEADER
+ " devno 0x%04X on subchannel %d = /dev/%s (%d:%d)"
+ " Cannot low-level format a partition\n",
+ device->devinfo.devno, device->devinfo.irq, device->name,
+ MAJOR (inp->i_rdev), MINOR (inp->i_rdev));
+ return -EINVAL;
+ }
+ rc = dasd_format (device, &fdata);
+ break;
+ }
+ case BIODASDRSRV:{
+ ccw_req_t *req;
+ if (!capable(CAP_SYS_ADMIN)) {
+ rc = -EACCES;
+ break;
+ }
+ req = device->discipline->reserve (device);
+ rc = sleep_on_req (req);
+ dasd_free_request (req);
+ break;
+ }
+ case BIODASDRLSE:{
+ ccw_req_t *req;
+ if (!capable(CAP_SYS_ADMIN)) {
+ rc = -EACCES;
+ break;
+ }
+ req = device->discipline->release (device);
+ rc = sleep_on_req (req);
+ dasd_free_request (req);
+ break;
}
- dasd_debug (0xc4c86d6d); /* DHwu */
+ case BIODASDSLCK:{
+ printk (KERN_WARNING PRINTK_HEADER
+ "Unsupported ioctl BIODASDSLCK\n");
+ break;
+ }
+ default:{
+ 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 = -EINVAL;
+ 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;
+ dasd_device_t *device;
+
+ if ((!inp) || !(inp->i_rdev)) {
+ return -EINVAL;
+ }
+ if (dasd_probeonly) {
+ printk ("\n" KERN_INFO PRINTK_HEADER "No access to device (%d:%d) due to probeonly mode\n", MAJOR (inp->i_rdev), MINOR (inp->i_rdev));
+ return -EPERM;
+ }
+ device = dasd_device_from_kdev (inp->i_rdev);
+ if (device == NULL) {
+ printk (KERN_WARNING PRINTK_HEADER
+ "No device registered as (%d:%d)\n", MAJOR (inp->i_rdev), MINOR (inp->i_rdev));
+ return -ENODEV;
+ }
+ if (device->level < DASD_DEVICE_LEVEL_RECOGNIZED ||
+ device->discipline == NULL) {
+ DASD_MESSAGE (KERN_WARNING, device,
+ " %s", " Cannot open unrecognized device\n");
+ return -EINVAL;
+ }
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif /* MODULE */
+ device->open_count++;
+ return rc;
+}
+
+static int
+dasd_release (struct inode *inp, struct file *filp)
+{
+ int rc = 0;
+ dasd_device_t *device;
+
+ if ((!inp) || !(inp->i_rdev)) {
+ return -EINVAL;
+ }
+ device = dasd_device_from_kdev (inp->i_rdev);
+ if (device == NULL) {
+ printk (KERN_WARNING PRINTK_HEADER
+ "No device registered as %d:%d\n",
+ MAJOR (inp->i_rdev), MINOR (inp->i_rdev));
+ return -EINVAL;
+ }
+ if (device->open_count--) {
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif /* MODULE */
+ }
+ fsync_dev(inp->i_rdev); /* sync the device */
+ if (device->open_count == 0) /* finally invalidate buffers */
+ invalidate_buffers(inp->i_rdev);
+ return rc;
+}
+
+static struct
+block_device_operations dasd_device_operations =
+{
+ open:dasd_open,
+ release:dasd_release,
+ ioctl:dasd_ioctl,
+#if ! (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+ read:block_read,
+ write:block_write,
+ fsync:block_fsync,
+#endif /* LINUX_IS_24 */
+};
+
+/* SECTION: Management of device list */
+
+/* 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;
+ for (major_info = dasd_major_info; major_info; major_info = major_info->next) {
+ if (&major_info->gendisk == hd) {
+ break;
+ }
+ index += DASD_PER_MAJOR;
+ }
+ if (major_info == NULL) {
+ return -EINVAL;
+ }
+ }
+ third = index % 26;
+ second = (index / 26) % 27;
+ first = ((index / 26) / 27) % 27;
+
+ len = sprintf (str, "dasd");
+ if (first) {
+ len += sprintf (str + len, "%c", first + 'a' - 1);
+ }
+ if (second) {
+ len += sprintf (str + len, "%c", second + 'a' - 1);
+ }
+ len += sprintf (str + len, "%c", third + 'a');
+ if (partition) {
+ if (partition > 9) {
+ return -EINVAL;
+ } else {
+ len += sprintf (str + len, "%d", partition);
+ }
+ }
+ str[len] = '\0';
+ return 0;
+}
+
+#ifdef CONFIG_DASD_DYNAMIC
+static void
+dasd_not_oper_handler (int irq, int status)
+{
+ dasd_device_t *device = NULL;
+ major_info_t *major_info;
+ int i, devno = -ENODEV;
+
+ for (major_info = dasd_major_info; major_info != NULL; major_info = major_info->next) {
+ for (i = 0; i < DASD_PER_MAJOR; i++) {
+ device = major_info->dasd_device[i];
+ if (device &&
+ device->devinfo.irq == irq) {
+ devno = device->devinfo.devno;
+ break;
+ }
+ }
+ if (devno != -ENODEV)
+ break;
+ }
+ if (devno < 0) {
+ printk (KERN_WARNING PRINTK_HEADER
+ "not_oper_handler called on irq %d no devno!\n", irq);
+ return;
+ }
+ printk (KERN_INFO PRINTK_HEADER
+ "not_oper_handler called on irq %d devno %04X\n", irq, devno);
+ if (device->open_count != 0) {
+ printk (KERN_ALERT PRINTK_HEADER
+ "Device %04X detached has still been open. expect errors\n", devno);
+ }
+ dasd_set_device_level (irq, DASD_DEVICE_LEVEL_UNKNOWN, NULL, 0);
+}
+
+static int
+dasd_enable_single_volume (int irq)
+{
+ int rc = 0;
+ dasd_set_device_level (irq, DASD_DEVICE_LEVEL_ANALYSIS_PREPARED,
+ NULL, 0);
+ printk (KERN_INFO PRINTK_HEADER "waiting for response...\n");
+ {
+ static wait_queue_head_t wait_queue;
+ init_waitqueue_head (&wait_queue);
+ interruptible_sleep_on_timeout (&wait_queue, (5 * HZ) >> 1);
+ }
+ dasd_set_device_level (irq, DASD_DEVICE_LEVEL_ANALYSED,
+ NULL, 0);
+ return rc;
}
-static int
-dasd_format (int dev, format_data_t * fdata)
+int
+dasd_oper_handler (int irq, devreg_t * devreg)
{
+ int devno;
int rc;
- int devindex = DEVICE_NR (dev);
- dasd_chanq_t *q;
- cqr_t *cqr;
- int irq;
- long flags;
- PRINT_INFO ("Format called with devno %x\n", dev);
- if (MINOR (dev) & (0xff >> (8 - PARTN_BITS))) {
- PRINT_WARN ("Can't format partition! minor %x %x\n",
- MINOR (dev), 0xff >> (8 - PARTN_BITS));
- return -EINVAL;
- }
- down (&dasd_info[devindex]->sem);
- if (dasd_info[devindex]->open_count == 1) {
- rc = dasd_disciplines[dasd_info[devindex]->type]->
- dasd_format (devindex, fdata);
- if (rc) {
- PRINT_WARN ("Formatting failed rc=%d\n", rc);
- }
+ devno = get_devno_by_irq (irq);
+ if (devno == -ENODEV)
+ return -ENODEV;
+ if (dasd_autodetect) {
+ dasd_add_range (devno, 0);
} else {
- PRINT_WARN ("device is open! %d\n", dasd_info[devindex]->open_count);
- rc = -EINVAL;
- }
- if (!rc) {
-#if DASD_PARANOIA > 1
- if (!dasd_disciplines[dasd_info[devindex]->type]->fill_sizes_first) {
- INTERNAL_CHECK ("No fill_sizes for dt=%d\n", dasd_info[devindex]->type);
- } else
-#endif /* DASD_PARANOIA */
- {
- dasd_info[devindex]->flags &= ~DASD_INFO_FLAGS_INITIALIZED;
- irq = dasd_info[devindex]->info.irq;
- PRINT_INFO ("Trying to access DASD %x, irq %x, index %d\n",
- get_devno_by_irq(irq), irq, devindex);
- s390irq_spin_lock_irqsave (irq, flags);
- q = &dasd_info[devindex]->queue;
- cqr = dasd_disciplines[dasd_info[devindex]->type]->
- fill_sizes_first (devindex);
- dasd_chanq_enq (q, cqr);
- schedule_bh(dasd_do_chanq);
- s390irq_spin_unlock_irqrestore (irq, flags);
- }
+ return -ENODEV;
}
- up (&dasd_info[devindex]->sem);
+ rc = dasd_enable_single_volume (irq);
return rc;
}
+#endif /* CONFIG_DASD_DYNAMIC */
-
+/*
+ * function dasd_set_device_level
+ */
static int
-register_dasd (int irq, dasd_type_t dt, dev_info_t * info)
+dasd_set_device_level (unsigned int irq, int desired_level,
+ dasd_discipline_t * discipline, int flags)
{
int rc = 0;
- int di;
- unsigned long flags;
- dasd_chanq_t *q;
- cqr_t * cqr;
- static spinlock_t register_lock = SPIN_LOCK_UNLOCKED;
- spin_lock (®ister_lock);
- FUNCTION_ENTRY ("register_dasd");
- di = devindex_from_devno (info->devno);
- if (di < 0) {
- INTERNAL_CHECK ("Can't get index for devno %d\n", info->devno);
+ int devno;
+ dasd_device_t **device_addr, *device;
+ int current_level;
+ major_info_t *major_info = NULL;
+ int i, minor, major;
+ ccw_req_t *cqr = NULL;
+ struct gendisk *dd;
+
+ devno = get_devno_by_irq (irq);
+ if (devno < 0) {
+ printk (KERN_WARNING PRINTK_HEADER " no device appears to be connected to SCH %d\n", irq);
return -ENODEV;
}
- if (dasd_info[di]) { /* devindex is not free */
- INTERNAL_CHECK ("reusing allocated deviceindex %d\n", di);
+ if (dasd_devindex_from_devno (devno) < 0) {
return -ENODEV;
}
- dasd_info[di] = (dasd_information_t *)
- kmalloc (sizeof (dasd_information_t), GFP_ATOMIC);
- if (dasd_info[di] == NULL) {
- PRINT_WARN ("No memory for dasd_info_t on irq %d\n", irq);
- return -ENOMEM;
+ while ((device_addr = dasd_device_from_devno (devno)) == NULL) {
+ if ((rc = dasd_register_major (NULL)) > 0) {
+ printk (KERN_INFO PRINTK_HEADER
+ "Registered to major number: %u\n", rc);
+ } else {
+ printk (KERN_WARNING PRINTK_HEADER
+ "Couldn't register to another major no\n");
+ return -ERANGE;
+ }
+ }
+ device = *device_addr;
+ if (!device) { /* allocate device descriptor */
+ device = kmalloc (sizeof (dasd_device_t), GFP_ATOMIC);
+ if (!device) {
+ printk (KERN_WARNING PRINTK_HEADER " No memory for device descriptor\n");
+ goto nomem;
+ }
+ memset (device, 0, sizeof (dasd_device_t));
+ *device_addr = device;
+ }
+ for (major_info = dasd_major_info; major_info; major_info = major_info->next) {
+ int i;
+ for (i = 0; i < DASD_PER_MAJOR; i++) {
+ if (major_info->dasd_device[i] == device) {
+ device->kdev = MKDEV (major_info->gendisk.major, i << DASD_PARTN_BITS);
+ break;
+ }
+ }
+ if (i < DASD_PER_MAJOR)
+ break;
}
- memset (dasd_info[di], 0, sizeof (dasd_information_t));
- memcpy (&(dasd_info[di]->info), info, sizeof (dev_info_t));
- spin_lock_init (&dasd_info[di]->queue.f_lock);
- spin_lock_init (&dasd_info[di]->queue.q_lock);
- dasd_info[di]->type = dt;
- dasd_info[di]->irq = irq;
- init_MUTEX (&dasd_info[di]->sem);
- rc = dasd_read_characteristics (dasd_info[di]);
- if (rc) {
- PRINT_WARN ("RDC returned error %d\n", rc);
- rc = -ENODEV;
- goto unalloc;
- }
-#if DASD_PARANOIA > 1
- if (dasd_disciplines[dt]->ck_characteristics)
-#endif /* DASD_PARANOIA */
- rc = dasd_disciplines[dt]->
- ck_characteristics (dasd_info[di]->rdc_data);
-
- if (rc) {
- INTERNAL_CHECK ("Discipline returned non-zero when"
- "checking device characteristics%s\n", "");
- rc = -ENODEV;
- goto unalloc;
+ if (major_info == NULL) {
+ return -ENODEV;
}
- rc = request_irq (irq, dasd_handler, 0, "dasd",
- &(dasd_info[di]->dev_status));
- if (rc) {
-#if DASD_PARANOIA > 0
- printk (KERN_WARNING PRINTK_HEADER
- "Cannot register irq %d, rc=%d\n",
- irq, rc);
-#endif /* DASD_PARANOIA */
- rc = -ENODEV;
- goto unalloc;
- }
-#if DASD_PARANOIA > 1
- if (!dasd_disciplines[dt]->fill_sizes_first) {
- INTERNAL_CHECK ("No fill_sizes for dt=%d\n", dt);
- goto unregister;
- }
-#endif /* DASD_PARANOIA */
- irq = dasd_info[di]->info.irq;
- PRINT_INFO ("Trying to access DASD %x, irq %x, index %d\n",
- get_devno_by_irq(irq), irq, di);
- s390irq_spin_lock_irqsave (irq, flags);
- q = &dasd_info[di]->queue;
- cqr = dasd_disciplines[dt]->fill_sizes_first (di);
- dasd_chanq_enq (q, cqr);
- cql_enq_head(q);
- if (dasd_start_IO(cqr) != 0) {
- atomic_inc(&chanq_tasks);
- }
- s390irq_spin_unlock_irqrestore (irq, flags);
+ device->major_info = major_info;
+ dasd_device_name (device->name,
+ ((long) device_addr -
+ (long) device->major_info->dasd_device) /
+ sizeof (dasd_device_t *),
+ 0, &major_info->gendisk);
+ minor = MINOR (device->kdev);
+ major = MAJOR (device->kdev);
+ current_level = device->level;
+ if (desired_level > current_level) {
+ switch (current_level) {
+ case DASD_DEVICE_LEVEL_UNKNOWN: /* Find a discipline */
+ rc = get_dev_info_by_irq (irq, &device->devinfo);
+ if (rc < 0) {
+ break;
+ }
+ discipline = dasd_find_discipline (device);
+ if (discipline && !rc) {
+ DASD_MESSAGE (KERN_INFO, device,
+ "%s", " ");
+ } else {
+ break;
+ }
+ device->discipline = discipline;
+ if (device->discipline->int_handler) {
+#ifdef CONFIG_DASD_DYNAMIC
+ s390_request_irq_special (irq,
+ device->discipline->int_handler,
+ dasd_not_oper_handler,
+ 0,
+ DASD_NAME,
+ &device->dev_status);
+#else /* !defined(CONFIG_DASD_DYNAMIC) */
+ request_irq (irq,
+ device->discipline->int_handler,
+ 0,
+ DASD_NAME,
+ &device->dev_status);
+#endif /* CONFIG_DASD_DYNAMIC */
+ }
+ device->proc_dir = (struct proc_dir_entry *)
+ kmalloc (sizeof (struct proc_dir_entry), GFP_KERNEL);
+ if (device->proc_dir) {
+ memset (device->proc_dir, 0, sizeof (struct proc_dir_entry));
+ device->proc_info = (struct proc_dir_entry *)
+ kmalloc (sizeof (struct proc_dir_entry), GFP_KERNEL);
+ if (device->proc_info) {
+ memset (device->proc_info, 0,
+ sizeof (struct proc_dir_entry));
+ }
+ device->proc_stats = (struct proc_dir_entry *)
+ kmalloc (sizeof (struct proc_dir_entry), GFP_KERNEL);
+ if (device->proc_stats) {
+ memset (device->proc_stats, 0,
+ sizeof (struct proc_dir_entry));
+ }
+ }
+ init_waitqueue_head (&device->wait_q);
+ blk_init_queue (&device->request_queue, do_dasd_request);
+ blk_queue_headactive (&device->request_queue, 0);
+ check_then_set (&device->level,
+ DASD_DEVICE_LEVEL_UNKNOWN,
+ DASD_DEVICE_LEVEL_RECOGNIZED);
+ if (desired_level == DASD_DEVICE_LEVEL_RECOGNIZED)
+ break;
+ case DASD_DEVICE_LEVEL_RECOGNIZED: /* Fallthrough ?? */
+ if (device->discipline->init_analysis) {
+ cqr = device->discipline->init_analysis (device);
+ if (cqr != NULL) {
+ dasd_chanq_enq (&device->queue, cqr);
+ if (device->discipline->start_IO) {
+ long flags;
+ s390irq_spin_lock_irqsave (irq, flags);
+ device->discipline->start_IO (cqr);
+ check_then_set (&device->level,
+ DASD_DEVICE_LEVEL_RECOGNIZED,
+ DASD_DEVICE_LEVEL_ANALYSIS_PENDING);
+ s390irq_spin_unlock_irqrestore (irq, flags);
+ }
+ }
+ } else {
+ check_then_set (&device->level, DASD_DEVICE_LEVEL_RECOGNIZED,
+ DASD_DEVICE_LEVEL_ANALYSIS_PREPARED);
+ }
+ if (desired_level >= DASD_DEVICE_LEVEL_ANALYSIS_PENDING)
+ break;
+ case DASD_DEVICE_LEVEL_ANALYSIS_PENDING: /* Fallthrough ?? */
+ return -EAGAIN;
+ case DASD_DEVICE_LEVEL_ANALYSIS_PREPARED: /* Re-entering here ! */
+ if (device->discipline->do_analysis)
+ if (device->discipline->do_analysis (device))
+ return -ENODEV;
+ switch (device->sizes.bp_block) {
+ case 512:
+ case 1024:
+ case 2048:
+ case 4096:
+ break;
+ default:
+ {
+ printk (KERN_INFO PRINTK_HEADER
+ "/dev/%s (devno 0x%04X): Detected invalid blocksize of %d bytes"
+ " Did you format the drive?\n",
+ device->name, devno, device->sizes.bp_block);
+ return -EMEDIUMTYPE;
+ }
+ }
+ for (i = 0; i < (1 << DASD_PARTN_BITS); i++) {
+ if (i == 0)
+ blk_size[major][minor] = (device->sizes.blocks << device->sizes.s2b_shift) >> 1;
+ else
+ blk_size[major][minor + i] = 0;
+ hardsect_size[major][minor + i] = device->sizes.bp_block;
+ blksize_size[major][minor + i] = device->sizes.bp_block;
+ if (blksize_size[major][minor + i] < 1024)
+ blksize_size[major][minor + i] = 1024;
- goto exit;
+ max_sectors[major][minor + i] =
+ 255 << device->sizes.s2b_shift;
+ }
+ check_then_set (&device->level,
+ DASD_DEVICE_LEVEL_ANALYSIS_PREPARED,
+ DASD_DEVICE_LEVEL_ANALYSED);
+ dd = &major_info->gendisk;
+ dd->sizes[minor] = (device->sizes.blocks <<
+ device->sizes.s2b_shift) >> 1;
+#if !(LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+#ifndef MODULE
+ if (flags & 0x80)
+#endif
+#endif /* KERNEL_VERSION */
+ dasd_partn_detect (device);
+ if (desired_level == DASD_DEVICE_LEVEL_ANALYSED)
+ break;
+ case DASD_DEVICE_LEVEL_ANALYSED: /* Fallthrough ?? */
- unregister:
- free_irq (irq, &(dasd_info[di]->dev_status));
- unalloc:
- kfree (dasd_info[di]);
+ break;
+ default:
+ printk (KERN_WARNING PRINTK_HEADER
+ "Internal error in " __FILE__ " on line %d."
+ " validate_dasd called from %p with "
+ " desired_level = %d, current_level =%d"
+ " Pls send this message and your System.map to"
+ " linux390@de.ibm.com\n",
+ __LINE__, __builtin_return_address (0),
+ desired_level, current_level);
+ break;
+ }
+ } else if (desired_level < current_level) { /* donwgrade device status */
+ switch (current_level) {
+ case DASD_DEVICE_LEVEL_ANALYSED: /* Fallthrough ?? */
+ check_then_set (&device->level,
+ DASD_DEVICE_LEVEL_ANALYSED,
+ DASD_DEVICE_LEVEL_ANALYSIS_PREPARED);
+ if (desired_level == DASD_DEVICE_LEVEL_ANALYSIS_PREPARED)
+ break;
+ case DASD_DEVICE_LEVEL_ANALYSIS_PREPARED:
+ for (i = 0; i < (1 << DASD_PARTN_BITS); i++) {
+ blk_size[major][minor] = 0;
+ hardsect_size[major][minor + i] = 0;
+ blksize_size[major][minor + i] = 0;
+ max_sectors[major][minor + i] = 0;
+ }
+ memset (&device->sizes, 0, sizeof (dasd_sizes_t));
+ check_then_set (&device->level,
+ DASD_DEVICE_LEVEL_ANALYSIS_PREPARED,
+ DASD_DEVICE_LEVEL_ANALYSIS_PENDING);
+ if (desired_level == DASD_DEVICE_LEVEL_ANALYSIS_PENDING)
+ break;
+ case DASD_DEVICE_LEVEL_ANALYSIS_PENDING: /* Fallthrough ?? */
+ check_then_set (&device->level,
+ DASD_DEVICE_LEVEL_ANALYSIS_PENDING,
+ DASD_DEVICE_LEVEL_RECOGNIZED);
+ if (desired_level == DASD_DEVICE_LEVEL_RECOGNIZED)
+ break;
+ case DASD_DEVICE_LEVEL_RECOGNIZED: /* Fallthrough ?? */
+ if (device->discipline->int_handler) {
+ free_irq (irq, &device->dev_status);
+ }
+ device->discipline = NULL;
+ blk_cleanup_queue (&device->request_queue);
+ check_then_set (&device->level,
+ DASD_DEVICE_LEVEL_RECOGNIZED,
+ DASD_DEVICE_LEVEL_UNKNOWN);
+ if (desired_level == DASD_DEVICE_LEVEL_UNKNOWN)
+ break;
+ case DASD_DEVICE_LEVEL_UNKNOWN:
+ break;
+ default:
+ printk (KERN_WARNING PRINTK_HEADER
+ "Internal error in " __FILE__ " on line %d."
+ " validate_dasd called from %p with "
+ " desired_level = %d, current_level =%d"
+ " Pls send this message and your System.map to"
+ " linux390@de.ibm.com\n",
+ __LINE__, __builtin_return_address (0),
+ desired_level, current_level);
+ break;
+ }
+ }
+ if (rc) {
+ goto exit;
+ }
+ nomem:
+ rc = -ENOMEM;
exit:
- spin_unlock (®ister_lock);
- FUNCTION_EXIT ("register_dasd");
- return rc;
+ return 0;
}
+/* SECTION: Procfs stuff */
+typedef struct {
+ char *data;
+ int len;
+} tempinfo_t;
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+static struct proc_dir_entry *dasd_proc_root_entry = NULL;
+#else
+static struct proc_dir_entry dasd_proc_root_entry =
+{
+ low_ino:0,
+ namelen:4,
+ name:"dasd",
+ mode:S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR | S_IWGRP,
+ nlink:1,
+ uid:0,
+ gid:0,
+ size:0
+};
+#endif /* KERNEL_VERSION */
+static struct proc_dir_entry *dasd_devices_entry;
+static struct proc_dir_entry *dasd_statistics_entry;
+
static int
-probe_for_dasd (int irq)
+dasd_devices_open (struct inode *inode, struct file *file)
{
- int rc;
- dev_info_t info;
- dasd_type_t dt;
-
- FUNCTION_ENTRY ("probe_for_dasd");
+ int rc = 0;
+ int size = 1;
+ int len = 0;
+ major_info_t *temp = dasd_major_info;
+ tempinfo_t *info;
- rc = get_dev_info_by_irq (irq, &info);
- if (rc == -ENODEV) { /* end of device list */
- return rc;
+ info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t));
+ if (info == NULL) {
+ printk (KERN_WARNING "No memory available for data\n");
+ return -ENOMEM;
+ } else {
+ file->private_data = (void *) info;
}
-#if DASD_PARANOIA > 2
- if (rc) {
- INTERNAL_CHECK ("unknown rc %d of get_dev_info", rc);
- return rc;
+ while (temp) {
+ int i;
+ for (i = 0; i < 1 << (MINORBITS - DASD_PARTN_BITS); i++) {
+ dasd_device_t *device = temp->dasd_device[i];
+ if (device) {
+ size += 128;
+ }
+ }
+ temp = temp->next;
}
-#endif /* DASD_PARANOIA */
- if ((info.status & DEVSTAT_NOT_OPER)) {
- return -ENODEV;
+ temp = dasd_major_info;
+ info->data = (char *) vmalloc (size); /* FIXME! determine space needed in a better way */
+ if (size && info->data == NULL) {
+ printk (KERN_WARNING "No memory available for data\n");
+ vfree (info);
+ return -ENOMEM;
}
- dt = check_type (&info);
- switch (dt) {
-#ifdef CONFIG_DASD_ECKD
- case dasd_eckd:
-#endif /* CONFIG_DASD_ECKD */
- FUNCTION_CONTROL ("Probing devno %d...\n", info.devno);
- if (!dasd_is_accessible (info.devno)) {
- FUNCTION_CONTROL ("out of range...skip%s\n", "");
- return -ENODEV;
- }
- if (dasd_disciplines[dt]->ck_devinfo) {
- rc = dasd_disciplines[dt]->ck_devinfo (&info);
- }
-#if DASD_PARANOIA > 1
- else {
- INTERNAL_ERROR ("no ck_devinfo function%s\n", "");
- return -ENODEV;
- }
-#endif /* DASD_PARANOIA */
- if (rc == -ENODEV) {
- return rc;
- }
-#if DASD_PARANOIA > 2
- if (rc) {
- INTERNAL_CHECK ("unknown error rc=%d\n", rc);
- return -ENODEV;
- }
-#endif /* DASD_PARANOIA */
- rc = register_dasd (irq, dt, &info);
- if (rc) {
- PRINT_INFO ("devno %x not enabled as minor %d due to errors\n",
- info.devno,
- devindex_from_devno (info.devno) <<
- PARTN_BITS);
- } else {
- PRINT_INFO ("devno %x added as minor %d (%s)\n",
- info.devno,
- devindex_from_devno (info.devno) << PARTN_BITS,
- dasd_name[dt]);
+ while (temp) {
+ int i;
+ for (i = 0; i < 1 << (MINORBITS - DASD_PARTN_BITS); i++) {
+ dasd_device_t *device = temp->dasd_device[i];
+ if (device) {
+ len += sprintf (info->data + len,
+ "%04X(%s) at (%d:%d) is %7s:",
+ device->devinfo.devno,
+ device->discipline ? device->discipline->name : "none",
+ temp->gendisk.major, i << DASD_PARTN_BITS,
+ device->name);
+ switch (device->level) {
+ case DASD_DEVICE_LEVEL_UNKNOWN:
+ len += sprintf (info->data + len, "unknown\n");
+ break;
+ case DASD_DEVICE_LEVEL_RECOGNIZED:
+ len += sprintf (info->data + len, "passive");
+ len += sprintf (info->data + len, " at blocksize: %d, %ld blocks, %ld MB\n",
+ device->sizes.bp_block,
+ device->sizes.blocks,
+ ((device->sizes.bp_block >> 9) * device->sizes.blocks) >> 11);
+ break;
+ case DASD_DEVICE_LEVEL_ANALYSIS_PENDING:
+ len += sprintf (info->data + len, "busy \n");
+ break;
+ case DASD_DEVICE_LEVEL_ANALYSIS_PREPARED:
+ len += sprintf (info->data + len, "n/f \n");
+ break;
+ case DASD_DEVICE_LEVEL_ANALYSED:
+ len += sprintf (info->data + len, "active ");
+ len += sprintf (info->data + len, " at blocksize: %d, %ld blocks, %ld MB\n",
+ device->sizes.bp_block,
+ device->sizes.blocks,
+ ((device->sizes.bp_block >> 9) * device->sizes.blocks) >> 11);
+ break;
+ default:
+ len += sprintf (info->data + len, "no stat\n");
+ break;
+ }
+ }
}
- case dasd_none:
- break;
- default:
- PRINT_DEBUG ("unknown device type\n");
- break;
+ temp = temp->next;
}
- FUNCTION_EXIT ("probe_for_dasd");
+ info->len = len;
return rc;
}
-static int
-register_major (int major)
+#define MIN(a,b) ((a)<(b)?(a):(b))
+
+static ssize_t
+dasd_devices_read (struct file *file, char *user_buf, size_t user_len, loff_t * offset)
{
- request_queue_t *q;
- int rc = 0;
+ loff_t len;
+ tempinfo_t *p_info = (tempinfo_t *) file->private_data;
- FUNCTION_ENTRY ("register_major");
- rc = devfs_register_blkdev (major, DASD_NAME, &dasd_device_operations);
-#if DASD_PARANOIA > 1
- if (rc) {
- PRINT_WARN ("registering major -> rc=%d aborting... \n", rc);
- return rc;
+ 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" */
}
-#endif /* DASD_PARANOIA */
- q = BLK_DEFAULT_QUEUE(major);
- blk_init_queue(q, do_dasd_request);
- blk_queue_headactive(BLK_DEFAULT_QUEUE(major), 0);
- FUNCTION_CONTROL ("successfully registered major: %d\n", major);
- FUNCTION_EXIT ("register_major");
- return rc;
}
-/*
- Below you find functions which are called from outside. Some of them may be
- static, because they are called by their function pointers only. Thus static
- modifier is to make sure, that they are only called via the kernel's methods
- */
+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);
+
+ 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 "Now executing %s\n", buffer);
+ if (!strncmp (buffer, "add range", strlen ("add_range"))) {
+
+ } else if (!strncmp (buffer, "enable device", strlen ("enable device"))) {
+
+ } else if (!strncmp (buffer, "disable device", strlen ("disable device"))) {
+
+ } else {
+ printk (KERN_WARNING PRINTK_HEADER "unknown command %s",
+ buffer);
+ }
+ vfree (buffer);
+ return user_len;
+}
static int
-dasd_ioctl (struct inode *inp, struct file *filp,
- unsigned int no, unsigned long data)
+dasd_devices_close (struct inode *inode, struct file *file)
{
int rc = 0;
- FUNCTION_ENTRY ("dasd_ioctl");
- if ((!inp) || !(inp->i_rdev)) {
- return -EINVAL;
+ tempinfo_t *p_info = (tempinfo_t *) file->private_data;
+ if (p_info) {
+ if (p_info->data)
+ vfree (p_info->data);
+ vfree (p_info);
}
- rc = do_dasd_ioctl (inp, no, data);
- FUNCTION_EXIT ("dasd_ioctl");
return rc;
}
+static struct file_operations dasd_devices_file_ops =
+{
+ read:dasd_devices_read, /* read */
+ write:dasd_devices_write, /* write */
+ open:dasd_devices_open, /* open */
+ release:dasd_devices_close, /* close */
+};
+
+static struct inode_operations dasd_devices_inode_ops =
+{
+#if !(LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+ default_file_ops:&dasd_devices_file_ops /* file ops */
+#endif /* LINUX_IS_24 */
+};
+
static int
-dasd_open (struct inode *inp, struct file *filp)
+dasd_statistics_open (struct inode *inode, struct file *file)
{
int rc = 0;
- dasd_information_t *dev;
- FUNCTION_ENTRY ("dasd_open");
- if ((!inp) || !(inp->i_rdev)) {
- return -EINVAL;
+ int len = 0;
+ tempinfo_t *info;
+ int shift, i, help = 0;
+
+ info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t));
+ if (info == NULL) {
+ printk (KERN_WARNING "No memory available for data\n");
+ return -ENOMEM;
+ } else {
+ file->private_data = (void *) info;
}
- dev = dasd_info[DEVICE_NR (inp->i_rdev)];
- if (!dev) {
- PRINT_DEBUG ("No device registered as %d (%d)\n",
- inp->i_rdev, DEVICE_NR (inp->i_rdev));
- return -EINVAL;
+ 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;
+ return -ENOMEM;
}
- down (&dev->sem);
- up (&dev->sem);
-#ifdef MODULE
- MOD_INC_USE_COUNT;
-#endif /* MODULE */
-#if DASD_PARANOIA > 2
- if (dev->open_count < 0) {
- INTERNAL_ERROR ("open count cannot be less than 0: %d",
- dev->open_count);
- return -EINVAL;
+ for (shift = 0, help = dasd_global_profile.dasd_io_reqs;
+ help > 8192;
+ help = help >> 1, shift++) ;
+ len = sprintf (info->data, "%ld dasd I/O requests\n", dasd_global_profile.dasd_io_reqs);
+ len += sprintf (info->data + len, "__<4 ___8 __16 __32 __64 _128 _256 _512 __1k __2k __4k __8k _16k _32k _64k 128k\n");
+ len += sprintf (info->data + len, "_256 _512 __1M __2M __4M __8M _16M _32M _64M 128M 256M 512M __1G __2G __4G _>4G\n");
+ len += sprintf (info->data + len, "Histogram of sizes (512B secs)\n");
+ for (i = 0; i < 16; i++) {
+ len += sprintf (info->data + len, "%4ld ", dasd_global_profile.dasd_io_secs[i] >> shift);
+ }
+ len += sprintf (info->data + len, "\n");
+ len += sprintf (info->data + len, "Histogram of I/O times\n");
+ for (i = 0; i < 16; i++) {
+ len += sprintf (info->data + len, "%4ld ", dasd_global_profile.dasd_io_times[i] >> shift);
+ }
+ len += sprintf (info->data + len, "\n");
+ for (; i < 32; i++) {
+ len += sprintf (info->data + len, "%4ld ", 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, "%4ld ", dasd_global_profile.dasd_io_timps[i] >> shift);
+ }
+ len += sprintf (info->data + len, "\n");
+ for (; i < 32; i++) {
+ len += sprintf (info->data + len, "%4ld ", dasd_global_profile.dasd_io_timps[i] >> shift);
+ }
+ len += sprintf (info->data + len, "\n");
+ len += sprintf (info->data + len, "Histogram of I/O time till ssch\n");
+ for (i = 0; i < 16; i++) {
+ len += sprintf (info->data + len, "%4ld ", dasd_global_profile.dasd_io_time1[i] >> shift);
+ }
+ len += sprintf (info->data + len, "\n");
+ for (; i < 32; i++) {
+ len += sprintf (info->data + len, "%4ld ", 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, "%4ld ", dasd_global_profile.dasd_io_time2[i] >> shift);
+ }
+ len += sprintf (info->data + len, "\n");
+ for (; i < 32; i++) {
+ len += sprintf (info->data + len, "%4ld ", dasd_global_profile.dasd_io_time2[i] >> shift);
+ }
+ len += sprintf (info->data + len, "\n");
+ len += sprintf (info->data + len, "Histogram of I/O time between ssch and irq per sector\n");
+ for (i = 0; i < 16; i++) {
+ len += sprintf (info->data + len, "%4ld ", dasd_global_profile.dasd_io_time2ps[i] >> shift);
+ }
+ len += sprintf (info->data + len, "\n");
+ for (; i < 32; i++) {
+ len += sprintf (info->data + len, "%4ld ", 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, "%4ld ", dasd_global_profile.dasd_io_time3[i] >> shift);
+ }
+ len += sprintf (info->data + len, "\n");
+ for (; i < 32; i++) {
+ len += sprintf (info->data + len, "%4ld ", dasd_global_profile.dasd_io_time3[i] >> shift);
}
-#endif /* DASD_PARANOIA */
- dev->open_count++;
- FUNCTION_EXIT ("dasd_open");
+ len += sprintf (info->data + len, "\n");
+ info->len = len;
return rc;
}
-static int
-dasd_release (struct inode *inp, struct file *filp)
+static struct file_operations dasd_statistics_file_ops =
+{
+ read:dasd_devices_read, /* read */
+ open:dasd_statistics_open, /* open */
+ release:dasd_devices_close, /* close */
+};
+
+static struct inode_operations dasd_statistics_inode_ops =
+{
+#if !(LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+ default_file_ops:&dasd_statistics_file_ops /* file ops */
+#endif /* LINUX_IS_24 */
+};
+
+int
+dasd_proc_init (void)
{
int rc = 0;
- dasd_information_t *dev;
- FUNCTION_ENTRY ("dasd_release");
- if ((!inp) || !(inp->i_rdev)) {
- return -EINVAL;
- }
- dev = dasd_info[DEVICE_NR (inp->i_rdev)];
- if (!dev) {
- PRINT_WARN ("No device registered as %d\n", inp->i_rdev);
- return -EINVAL;
- }
-#ifdef MODULE
- MOD_DEC_USE_COUNT;
-#endif /* MODULE */
-#if DASD_PARANOIA > 2
- if (!dev->open_count) {
- PRINT_WARN ("device %d has not been opened before:\n",
- inp->i_rdev);
- }
-#endif /* DASD_PARANOIA */
- dev->open_count--;
-#if DASD_PARANOIA > 2
- if (dev->open_count < 0) {
- INTERNAL_ERROR ("open count cannot be less than 0: %d",
- dev->open_count);
- return -EINVAL;
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+ dasd_proc_root_entry = proc_mkdir ("dasd", &proc_root);
+ dasd_devices_entry = create_proc_entry ("devices",
+ S_IFREG | S_IRUGO | S_IWUSR,
+ 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;
+#else
+ proc_register (&proc_root, &dasd_proc_root_entry);
+ dasd_devices_entry = (struct proc_dir_entry *) kmalloc (sizeof (struct proc_dir_entry), GFP_ATOMIC);
+ if (dasd_devices_entry) {
+ memset (dasd_devices_entry, 0, sizeof (struct proc_dir_entry));
+ dasd_devices_entry->name = "devices";
+ dasd_devices_entry->namelen = strlen ("devices");
+ dasd_devices_entry->low_ino = 0;
+ dasd_devices_entry->mode = (S_IFREG | S_IRUGO | S_IWUSR);
+ dasd_devices_entry->nlink = 1;
+ dasd_devices_entry->uid = 0;
+ dasd_devices_entry->gid = 0;
+ dasd_devices_entry->size = 0;
+ dasd_devices_entry->get_info = NULL;
+ dasd_devices_entry->ops = &dasd_devices_inode_ops;
+ proc_register (&dasd_proc_root_entry, dasd_devices_entry);
+ }
+ dasd_statistics_entry = (struct proc_dir_entry *) kmalloc (sizeof (struct proc_dir_entry), GFP_ATOMIC);
+ if (dasd_statistics_entry) {
+ memset (dasd_statistics_entry, 0, sizeof (struct proc_dir_entry));
+ dasd_statistics_entry->name = "statistics";
+ dasd_statistics_entry->namelen = strlen ("statistics");
+ dasd_statistics_entry->low_ino = 0;
+ dasd_statistics_entry->mode = (S_IFREG | S_IRUGO | S_IWUSR);
+ dasd_statistics_entry->nlink = 1;
+ dasd_statistics_entry->uid = 0;
+ dasd_statistics_entry->gid = 0;
+ dasd_statistics_entry->size = 0;
+ dasd_statistics_entry->get_info = NULL;
+ dasd_statistics_entry->ops = &dasd_statistics_inode_ops;
+ proc_register (&dasd_proc_root_entry, dasd_statistics_entry);
}
-#endif /* DASD_PARANOIA */
- FUNCTION_EXIT ("dasd_release");
+#endif /* LINUX_IS_24 */
return rc;
}
-static struct
-block_device_operations dasd_device_operations =
+void
+dasd_proc_cleanup (void)
{
- ioctl: dasd_ioctl,
- open: dasd_open,
- release: dasd_release,
-};
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+ remove_proc_entry ("devices", dasd_proc_root_entry);
+ remove_proc_entry ("statistics", dasd_proc_root_entry);
+ remove_proc_entry ("dasd", &proc_root);
+#else
+ proc_unregister (&dasd_proc_root_entry, dasd_statistics_entry->low_ino);
+ kfree (dasd_statistics_entry);
+ proc_unregister (&dasd_proc_root_entry, dasd_devices_entry->low_ino);
+ kfree (dasd_devices_entry);
+ proc_unregister (&proc_root, dasd_proc_root_entry.low_ino);
+#endif /* LINUX_IS_24 */
+}
-int
+/* SECTION: Initializing the driver */
+
+int __init
dasd_init (void)
{
int rc = 0;
- int i;
+ int irq;
+ int j;
+ major_info_t *major_info;
+ dasd_range_t *range;
+
+ printk (KERN_INFO PRINTK_HEADER "initializing...\n");
+ for (major_info = dasd_major_info; major_info; major_info = major_info->next) {
+ if ((rc = dasd_register_major (major_info)) > 0) {
+ printk (KERN_INFO PRINTK_HEADER
+ "Registered successfully to major no %u\n", major_info->gendisk.major);
+ } else {
+ printk (KERN_WARNING PRINTK_HEADER
+ "Couldn't register successfully to major no %d\n", major_info->gendisk.major);
+ /* revert registration of major infos */
+ goto major_failed;
+ }
+ }
+#ifndef MODULE
+ dasd_split_parm_string (dasd_parm_string);
+#endif /* ! MODULE */
+ dasd_parse (dasd);
+ dasd_init_emergency_req ();
- FUNCTION_ENTRY ("dasd_init");
- PRINT_INFO ("initializing...\n");
- atomic_set (&chanq_tasks, 0);
- atomic_set (&bh_scheduled, 0);
- spin_lock_init (&dasd_lock);
- init_waitqueue_head(&dasd_waitq);
- /* First register to the major number */
- rc = register_major (MAJOR_NR);
-#if DASD_PARANOIA > 1
+ rc = dasd_proc_init ();
if (rc) {
- PRINT_WARN ("registering major_nr returned rc=%d\n", rc);
- return rc;
+ goto proc_failed;
}
-#endif /* DASD_PARANOIA */
- read_ahead[MAJOR_NR] = 8;
- blk_size[MAJOR_NR] = dasd_blks;
- hardsect_size[MAJOR_NR] = dasd_secsize;
- blksize_size[MAJOR_NR] = dasd_blksize;
- max_sectors[MAJOR_NR] = dasd_maxsecs;
-#ifdef CONFIG_PROC_FS
- dasd_proc_init ();
-#endif /* CONFIG_PROC_FS */
- /* Now scan the device list for DASDs */
- FUNCTION_CONTROL ("entering detection loop%s\n", "");
- for (i = 0; i < NR_IRQS; i++) {
- int irc; /* Internal return code */
- LOOP_CONTROL ("Probing irq %d...\n", i);
- irc = probe_for_dasd (i);
- switch (irc) {
- case 0:
- LOOP_CONTROL ("Added DASD%s\n", "");
- break;
- case -ENODEV:
- LOOP_CONTROL ("No DASD%s\n", "");
- break;
- case -EMEDIUMTYPE:
- PRINT_WARN ("DASD not formatted%s\n", "");
- break;
- default:
- INTERNAL_CHECK ("probe_for_dasd: unknown rc=%d", irc);
- break;
+ genhd_dasd_name = dasd_device_name;
+
+#ifdef CONFIG_DASD_ECKD
+ rc = dasd_eckd_init ();
+ if (rc == 0) {
+ printk (KERN_INFO PRINTK_HEADER
+ "Registered ECKD discipline successfully\n");
+ } else {
+ goto eckd_failed;
+ }
+#endif /* CONFIG_DASD_ECKD */
+#ifdef CONFIG_DASD_FBA
+ rc = dasd_fba_init ();
+ if (rc == 0) {
+ printk (KERN_INFO PRINTK_HEADER
+ "Registered FBA discipline successfully\n");
+ } else {
+ goto fba_failed;
+ }
+#endif /* CONFIG_DASD_FBA */
+#ifdef CONFIG_DASD_MDSK
+ if (MACHINE_IS_VM) {
+ rc = dasd_diag_init ();
+ if (rc == 0) {
+ printk (KERN_INFO PRINTK_HEADER
+ "Registered MDSK discipline successfully\n");
+ } else {
+ goto mdsk_failed;
}
}
- FUNCTION_CONTROL ("detection loop completed %s partn check...\n", "");
-/* Finally do the genhd stuff */
- dd_gendisk.next = gendisk_head;
- gendisk_head = &dd_gendisk;
- for ( i = 0; i < DASD_MAX_DEVICES; i ++ )
- if ( dasd_info[i] )
- dasd_partn_detect ( i );
-
- FUNCTION_EXIT ("dasd_init");
+#endif /* CONFIG_DASD_MDSK */
+ rc = 0;
+ for (range = dasd_range_head; range; range = range->next) {
+ for (j = range->from; j <= range->to; j++) {
+ irq = get_irq_by_devno (j);
+ if (irq >= 0)
+ dasd_set_device_level (irq, DASD_DEVICE_LEVEL_ANALYSIS_PREPARED,
+ NULL, 0);
+ }
+ }
+ if (dasd_autodetect) {
+ for (irq = get_irq_first (); irq != -ENODEV; irq = get_irq_next (irq)) {
+ int devno = get_devno_by_irq (irq);
+ int index = dasd_devindex_from_devno (devno);
+ if (index == -ENODEV) { /* not included in ranges */
+ dasd_add_range (devno, 0);
+ dasd_set_device_level (irq, DASD_DEVICE_LEVEL_ANALYSIS_PREPARED,
+ NULL, 0);
+ }
+ }
+ }
+ printk (KERN_INFO PRINTK_HEADER "waiting for responses...\n");
+ {
+ static wait_queue_head_t wait_queue;
+ init_waitqueue_head (&wait_queue);
+ interruptible_sleep_on_timeout (&wait_queue,
+ (5 * HZ) );
+ }
+ for (range = dasd_range_head; range; range = range->next) {
+ for (j = range->from; j <= range->to; j++) {
+ irq = get_irq_by_devno (j);
+ if (irq >= 0) {
+ dasd_set_device_level (irq, DASD_DEVICE_LEVEL_ANALYSED,
+ NULL, 0);
+ }
+ }
+ }
+ goto out;
+#ifdef CONFIG_DASD_MDSK
+ mdsk_failed:
+ dasd_diag_cleanup ();
+#endif /* CONFIG_DASD_MDSK */
+#ifdef CONFIG_DASD_FBA
+ fba_failed:
+ dasd_fba_cleanup ();
+#endif /* CONFIG_DASD_FBA */
+#ifdef CONFIG_DASD_ECKD
+ eckd_failed:
+ dasd_eckd_cleanup ();
+#endif /* CONFIG_DASD_ECKD */
+ proc_failed:
+ dasd_proc_cleanup ();
+ major_failed:{
+ major_info_t * temp;
+ for (temp = dasd_major_info;
+ temp && (temp != major_info);
+ temp = temp->next) {
+ dasd_unregister_major (temp);
+ }
+ }
+ dasd_cleanup_emergency_req ();
+ printk (KERN_INFO PRINTK_HEADER "initialization not performed due to errors\n");
+ out:
+ printk (KERN_INFO PRINTK_HEADER "initialization finished\n");
return rc;
}
+void
+cleanup_dasd (void)
+{
+ int j, rc = 0;
+ int irq;
+ major_info_t *major_info;
+ dasd_range_t *range, *next;
+
+ printk (KERN_INFO PRINTK_HEADER "shutting down\n");
+
+ dasd_proc_cleanup ();
+
+ for (range = dasd_range_head; range; range = range->next) {
+ for (j = range->from; j <= range->to; j++) {
+ irq = get_irq_by_devno (j);
+ if (irq >= 0) {
+ dasd_set_device_level (irq, DASD_DEVICE_LEVEL_UNKNOWN,
+ NULL, 0);
+ }
+ }
+ }
+ for (major_info = dasd_major_info; major_info; major_info = major_info->next) {
+ int i;
+ for (i = 0; i < DASD_PER_MAJOR; i++) {
+ kfree (major_info->dasd_device[i]);
+ }
+ if ((rc = dasd_unregister_major (major_info)) == 0) {
+ printk (KERN_INFO PRINTK_HEADER
+ "Unregistered successfully from major no %u\n", major_info->gendisk.major);
+ } else {
+ printk (KERN_WARNING PRINTK_HEADER
+ "Couldn't unregister successfully from major no %d rc = %d\n", major_info->gendisk.major, rc);
+ }
+ }
+ dasd_cleanup_emergency_req ();
+
+ range = dasd_range_head;
+ while (range) {
+ next = range->next;
+ kfree (range);
+ if (next == NULL)
+ break;
+ else
+ range = next;
+ }
+ dasd_range_head = NULL;
+
+#ifdef CONFIG_DASD_DYNAMIC
+ {
+ dasd_devreg_t *reg;
+ while (dasd_devreg_head) {
+ reg = dasd_devreg_head->next;
+ kfree (dasd_devreg_head);
+ dasd_devreg_head = reg;
+ }
+ }
+#endif /* CONFIG_DASD_DYNAMIC */
+ printk (KERN_INFO PRINTK_HEADER "shutdown completed\n");
+}
+
#ifdef MODULE
int
init_module (void)
{
int rc = 0;
-
- FUNCTION_ENTRY ("init_module");
- PRINT_INFO ("trying to load module\n");
- rc = dasd_init ();
- if (rc == 0) {
- PRINT_INFO ("module loaded successfully\n");
- } else {
- PRINT_WARN ("warning: Module load returned rc=%d\n", rc);
- }
- FUNCTION_EXIT ("init_module");
+ return dasd_init ();
return rc;
}
void
cleanup_module (void)
{
- int rc = 0;
-
- FUNCTION_ENTRY ("cleanup_module");
- PRINT_INFO ("trying to unload module \n");
-
- /* FIXME: replace by proper unload functionality */
- INTERNAL_ERROR ("Modules not yet implemented %s", "");
-
- if (rc == 0) {
- PRINT_INFO ("module unloaded successfully\n");
- } else {
- PRINT_WARN ("module unloaded with errors\n");
- }
- FUNCTION_EXIT ("cleanup_module");
+ cleanup_dasd ();
+ return;
}
-#endif /* MODULE */
+#endif
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)