patch-2.2.16 linux/drivers/s390/block/dasd.c
Next file: linux/drivers/s390/block/dasd.h
Previous file: linux/drivers/s390/block/Makefile
Back to the patch index
Back to the overall index
- Lines: 1763
- Date:
Wed Jun 7 14:26:43 2000
- Orig file:
v2.2.15/linux/drivers/s390/block/dasd.c
- Orig date:
Wed May 3 17:16:43 2000
diff -urN v2.2.15/linux/drivers/s390/block/dasd.c linux/drivers/s390/block/dasd.c
@@ -1,7 +1,6 @@
/*
* File...........: linux/drivers/s390/block/dasd.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
- * : Utz Bacher <utz.bacher@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*/
@@ -9,10 +8,6 @@
#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>
@@ -27,16 +22,18 @@
#include <asm/uaccess.h>
#include <asm/irq.h>
+#include <asm/s390_ext.h>
-#include "dasd.h"
+#include <linux/dasd.h>
#include <linux/blk.h>
+#include "dasd_erp.h"
#include "dasd_types.h"
#include "dasd_ccwstuff.h"
-#define PRINTK_HEADER DASD_NAME":"
+#include "../../../arch/s390/kernel/debug.h"
-#define CCW_READ_DEVICE_CHARACTERISTICS 0x64
+#define PRINTK_HEADER DASD_NAME":"
#define DASD_SSCH_RETRIES 2
@@ -45,220 +42,107 @@
(( info -> sid_data.cu_type ct ) && ( info -> sid_data.cu_model cm )) && \
(( info -> sid_data.dev_type dt ) && ( info -> sid_data.dev_model dm )) )
+#ifdef MODULE
+#include <linux/module.h>
+
+char *dasd[DASD_MAX_DEVICES] =
+{NULL,};
+#ifdef CONFIG_DASD_MDSK
+char *dasd_force_mdsk[DASD_MAX_DEVICES] =
+{NULL,};
+#endif
+
+kdev_t ROOT_DEV;
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR ("Holger Smolinski <Holger.Smolinski@de.ibm.com>");
+MODULE_DESCRIPTION ("Linux on S/390 DASD device driver, Copyright 2000 IBM Corporation");
+MODULE_PARM (dasd, "1-" __MODULE_STRING (DASD_MAX_DEVICES) "s");
+#ifdef CONFIG_DASD_MDSK
+MODULE_PARM (dasd_force_mdsk, "1-" __MODULE_STRING (DASD_MAX_DEVICES) "s");
+#endif
+#endif
+
/* Prototypes for the functions called from external */
-static ssize_t dasd_read (struct file *, char *, size_t, loff_t *);
-static ssize_t dasd_write (struct file *, const char *, size_t, loff_t *);
-static int dasd_ioctl (struct inode *, struct file *, unsigned int, unsigned long);
-static int dasd_open (struct inode *, struct file *);
-static int dasd_fsync (struct file *, struct dentry *);
-static int dasd_release (struct inode *, struct file *);
+void dasd_partn_detect (int di);
+int devindex_from_devno (int devno);
+int dasd_is_accessible (int devno);
+void dasd_add_devno_to_ranges (int devno);
+
+#ifdef CONFIG_DASD_MDSK
+extern int dasd_force_mdsk_flag[DASD_MAX_DEVICES];
+extern void do_dasd_mdsk_interrupt (struct pt_regs *regs, __u16 code);
+extern int dasd_parse_module_params (void);
+extern void (**ext_mdisk_int) (void);
+#endif
+
void dasd_debug (unsigned long tag);
void dasd_profile_add (cqr_t *cqr);
void dasd_proc_init (void);
-static struct file_operations dasd_file_operations;
+static int dasd_format (int dev, format_data_t * fdata);
+
+static struct file_operations dasd_device_operations;
spinlock_t dasd_lock; /* general purpose lock for the dasd driver */
/* All asynchronous I/O should waint on this wait_queue */
struct wait_queue *dasd_waitq = NULL;
-static int dasd_autodetect = 1;
-static int dasd_devno[DASD_MAX_DEVICES] =
-{0,};
-static int dasd_count = 0;
-
extern dasd_chanq_t *cq_head;
+extern int dasd_probeonly;
-static int
-dasd_get_hexdigit (char c)
-{
- 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;
-}
+debug_info_t *dasd_debug_info;
-/* sets the string pointer after the next comma */
-static void
-dasd_scan_for_next_comma (char **strptr)
-{
- while (((**strptr) != ',') && ((**strptr)++))
- (*strptr)++;
+extern dasd_information_t **dasd_information;
- /* set the position AFTER the comma */
- if (**strptr == ',')
- (*strptr)++;
-}
+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,};
-/*sets the string pointer after the next comma, if a parse error occured */
-static int
-dasd_get_next_int (char **strptr)
+void
+dasd_geninit (struct gendisk *dd)
{
- 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;
- }
- }
- }
- return i;
}
-static inline int
-devindex_from_devno (int devno)
+struct gendisk dd_gendisk =
{
- 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;
-}
+ major:MAJOR_NR, /* Major number */
+ major_name:"dasd", /* Major name */
+ minor_shift:PARTN_BITS, /* Bits to shift to get real from partn */
+ max_p:1 << PARTN_BITS, /* Number of partitions per real */
+ max_nr:0, /* number */
+ init:dasd_geninit,
+ part:dd_hdstruct, /* hd struct */
+ sizes:dasd_blks, /* sizes in blocks */
+ nr_real:0,
+ real_devices:NULL, /* internal */
+ next:NULL /* next */
+};
-/* returns 1, if dasd_no is in the specified ranges, otherwise 0 */
-static inline int
-dasd_is_accessible (int devno)
-{
- return (devindex_from_devno (devno) >= 0);
-}
+static atomic_t bh_scheduled = ATOMIC_INIT (0);
-/* dasd_insert_range skips ranges, if the start or the end is -1 */
-static void
-dasd_insert_range (int start, int end)
+void
+dasd_schedule_bh (void (*func) (void))
{
- 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;
- }
- if ((start == -1) || (end == -1)) {
- PRINT_ERR ("invalid format of parameter, skipping range\n");
- FUNCTION_EXIT ("dasd_insert_range");
+ static struct tq_struct dasd_tq =
+ {0,};
+ /* Protect against rescheduling, when already running */
+ if (atomic_compare_and_swap (0, 1, &bh_scheduled))
return;
- }
- 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");
+ dasd_tq.routine = (void *) (void *) func;
+ queue_task (&dasd_tq, &tq_immediate);
+ mark_bh (IMMEDIATE_BH);
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;
- }
- }
- PRINT_INFO (" added dasd range from %x to %x.\n",
- start, dasd_devno[dasd_count - 1]);
-
- FUNCTION_EXIT ("dasd_insert_range");
-}
-
-void
-dasd_setup (char *str, int *ints)
-{
- 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.");
- } else {
- dasd_insert_range (devno, devno2);
- }
- dasd_scan_for_next_comma (&str);
- continue;
- }
- if (*str == 0) {
- dasd_insert_range (devno, devno);
- break;
- }
- PRINT_ERR (" unexpected character in kernel parameter, " \
- "skipping range.\n");
- }
- FUNCTION_EXIT ("dasd_setup");
-}
-
-static void
-dd_geninit (struct gendisk *ignored)
-{
- FUNCTION_ENTRY ("dd_geninit");
- FUNCTION_EXIT ("dd_geninit");
-}
-
-static struct hd_struct dd_hdstruct[DASD_MAX_DEVICES << PARTN_BITS];
-static int dd_blocksizes[DASD_MAX_DEVICES << PARTN_BITS];
-
-struct gendisk dd_gendisk =
-{
- MAJOR_NR, /* Major number */
- "dd", /* Major name */
- PARTN_BITS, /* Bits to shift to get real from partn */
- 1 << PARTN_BITS, /* Number of partitions per real */
- DASD_MAX_DEVICES, /* maximum number of real */
- dd_geninit, /* init function */
- dd_hdstruct, /* hd struct */
- dd_blocksizes, /* block sizes */
- 0, /* number */
- NULL, /* internal */
- NULL /* next */
-
-};
void
sleep_done (struct semaphore *sem)
@@ -288,104 +172,49 @@
#ifdef CONFIG_DASD_ECKD
extern dasd_operations_t dasd_eckd_operations;
#endif /* CONFIG_DASD_ECKD */
+#ifdef CONFIG_DASD_FBA
+extern dasd_operations_t dasd_fba_operations;
+#endif /* CONFIG_DASD_FBA */
+#ifdef CONFIG_DASD_MDSK
+extern dasd_operations_t dasd_mdsk_operations;
+#endif /* CONFIG_DASD_MDSK */
dasd_operations_t *dasd_disciplines[] =
{
#ifdef CONFIG_DASD_ECKD
- &dasd_eckd_operations
+ &dasd_eckd_operations,
#endif /* CONFIG_DASD_ECKD */
+#ifdef CONFIG_DASD_FBA
+ &dasd_fba_operations,
+#endif /* CONFIG_DASD_FBA */
+#ifdef CONFIG_DASD_MDSK
+ &dasd_mdsk_operations,
+#endif /* CONFIG_DASD_MDSK */
+#ifdef CONFIG_DASD_CKD
+ &dasd_ckd_operations,
+#endif /* CONFIG_DASD_CKD */
+ NULL
};
char *dasd_name[] =
{
#ifdef CONFIG_DASD_ECKD
- "ECKD"
+ "ECKD",
#endif /* CONFIG_DASD_ECKD */
+#ifdef CONFIG_DASD_FBA
+ "FBA",
+#endif /* CONFIG_DASD_FBA */
+#ifdef CONFIG_DASD_MDSK
+ "MDSK",
+#endif /* CONFIG_DASD_MDSK */
+#ifdef CONFIG_DASD_CKD
+ "CKD",
+#endif /* CONFIG_DASD_CKD */
+ "END"
};
-dasd_information_t *dasd_info[DASD_MAX_DEVICES] =
-{NULL,};
-
-static int dasd_blks[256] =
-{0,};
-static int dasd_secsize[256] =
-{0,};
-static int dasd_blksize[256] =
-{0,};
-static int dasd_maxsecs[256] =
-{0,};
-
-void
-fill_sizes (int di)
-{
- int rc;
- int minor;
- rc = dasd_disciplines[dasd_info[di]->type]->fill_sizes (di);
- switch (rc) {
- case -EMEDIUMTYPE:
- dasd_info[di]->flags |= DASD_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.first_sector =
- 3 << dasd_info[di]->sizes.s2b_shift;
- break;
-#endif /* CONFIG_DASD_ECKD */
- default:
- INTERNAL_CHECK ("Unknown dasd type %d\n", dasd_info[di]->type);
- }
- 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_blks[minor + 1] = dasd_info[di]->sizes.kbytes -
- (dasd_info[di]->sizes.first_sector >> 1);
- 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;
-}
-
-int
-dasd_format (int dev, format_data_t * fdata)
-{
- int rc;
- int devindex = DEVICE_NR (dev);
- 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);
- }
- } else {
- PRINT_WARN ("device is open! %d\n", dasd_info[devindex]->open_count);
- rc = -EINVAL;
- }
- if (!rc) {
- fill_sizes (devindex);
- dasd_info[devindex]->flags &= ~DASD_NOT_FORMATTED;
- } else {
- dasd_info[devindex]->flags |= DASD_NOT_FORMATTED;
- }
- up (&dasd_info[devindex]->sem);
- return rc;
-}
-
-static inline int
-do_dasd_ioctl (struct inode *inp, unsigned int no, unsigned long data)
+static int
+do_dasd_ioctl (struct inode *inp, /* unsigned */ int no, unsigned long data)
{
int rc;
int di;
@@ -415,14 +244,10 @@
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));
+ int blocks = dasd_blks[MINOR (inp->i_rdev)] << 1;
+ rc = copy_to_user ((long *) data,
+ &blocks,
+ sizeof (long));
break;
}
case BLKFLSBUF:{
@@ -440,17 +265,30 @@
break;
}
case BLKRRPART:{
- INTERNAL_CHECK ("BLKRPART not implemented%s", "");
- rc = -EINVAL;
+ dasd_partn_detect (di);
+ rc = 0;
+ break;
+ }
+ case BIODASDRLB:{
+ rc = copy_to_user ((int *) data,
+ &dasd_info[di]->sizes.label_block,
+ sizeof (int));
+ break;
+ }
+ case BLKGETBSZ:{
+ rc = copy_to_user ((int *) data,
+ &dasd_info[di]->sizes.bp_block,
+ sizeof (int));
break;
}
case HDIO_GETGEO:{
- INTERNAL_CHECK ("HDIO_GETGEO not implemented%s", "");
- rc = -EINVAL;
+ struct hd_geometry geo;
+ dasd_disciplines[dev->type]->fill_geometry (di, &geo);
+ rc = copy_to_user ((struct hd_geometry *) data, &geo,
+ sizeof (struct hd_geometry));
break;
}
RO_IOCTLS (inp->i_rdev, data);
-
case BIODASDRSID:{
rc = copy_to_user ((void *) data,
&(dev->info.sid_data),
@@ -462,13 +300,17 @@
int xlt;
rc = copy_from_user (&xlt, (void *) data,
sizeof (int));
+#if 0
PRINT_INFO("Xlating %d to",xlt);
+#endif
if (rc)
break;
- if (MINOR (inp->i_rdev) & 1)
- offset = 3;
+ offset = dd_gendisk.part[MINOR (inp->i_rdev)].start_sect >>
+ dev->sizes.s2b_shift;
xlt += offset;
+#if 0
printk(" %d \n",xlt);
+#endif
rc = copy_to_user ((void *) data, &xlt,
sizeof (int));
break;
@@ -476,6 +318,7 @@
case BIODASDFORMAT:{
/* fdata == NULL is a valid arg to dasd_format ! */
format_data_t *fdata = NULL;
+ PRINT_WARN ("called format ioctl\n");
if (data) {
fdata = kmalloc (sizeof (format_data_t),
GFP_ATOMIC);
@@ -495,6 +338,7 @@
break;
}
default:
+ PRINT_WARN ("unknown ioctl number %08x %08lx\n", no, BIODASDFORMAT);
rc = -EINVAL;
break;
}
@@ -533,13 +377,58 @@
wake_up (&dasd_waitq);
}
+
+int
+dasd_watch_volume (int di)
+{
+ int rc = 0;
+
+ return rc;
+}
+
+void
+dasd_watcher (void)
+{
+ int i = 0;
+ int rc;
+ do {
+ for ( i = 0; i < DASD_MAX_DEVICES; i++ ) {
+ if ( dasd_info [i] ) {
+ rc = dasd_watch_volume ( i );
+ }
+ }
+ interruptible_sleep_on(&dasd_waitq);
+ } while(1);
+}
+
int
-dasd_unregister_dasd (int irq, dasd_type_t dt, dev_info_t * info)
+dasd_unregister_dasd (int di)
{
int rc = 0;
- FUNCTION_ENTRY ("dasd_unregister_dasd");
- INTERNAL_CHECK ("dasd_unregister_dasd not implemented%s\n", "");
- FUNCTION_EXIT ("dasd_unregister_dasd");
+ int minor;
+ int i;
+
+ minor = di << PARTN_BITS;
+ if (!dasd_info[di]) { /* devindex is not free */
+ INTERNAL_CHECK ("trying to free unallocated device %d\n", di);
+ return -ENODEV;
+ }
+ /* delete all that partition stuff */
+ for (i = 0; i < (1 << PARTN_BITS); i++) {
+ dasd_blks[minor] = 0;
+ dasd_secsize[minor + i] = 0;
+ dasd_blksize[minor + i] = 0;
+ dasd_maxsecs[minor + i] = 0;
+ }
+ /* reset DASD to unknown statuss */
+ atomic_set (&dasd_info[di]->status, DASD_INFO_STATUS_UNKNOWN);
+
+ free_irq (dasd_info[di]->info.irq, &(dasd_info[di]->dev_status));
+ if (dasd_info[di]->rdc_data)
+ kfree (dasd_info[di]->rdc_data);
+ kfree (dasd_info[di]);
+ PRINT_INFO ("%04d deleted from list of valid DASDs\n",
+ dasd_info[di]->info.devno);
return rc;
}
@@ -548,8 +437,17 @@
check_type (dev_info_t * info)
{
dasd_type_t type = dasd_none;
+ int di;
FUNCTION_ENTRY ("check_type");
+ di = devindex_from_devno (info->devno);
+
+#ifdef CONFIG_DASD_MDSK
+ if (MACHINE_IS_VM && dasd_force_mdsk_flag[di] == 1) {
+ type = dasd_mdsk;
+ } else
+#endif /* CONFIG_DASD_MDSK */
+
#ifdef CONFIG_DASD_ECKD
if (MATCH (info, == 0x3990, ||1, == 0x3390, ||1) ||
MATCH (info, == 0x9343, ||1, == 0x9345, ||1) ||
@@ -557,9 +455,20 @@
type = dasd_eckd;
} else
#endif /* CONFIG_DASD_ECKD */
+#ifdef CONFIG_DASD_FBA
+ if (MATCH (info, == 0x6310, ||1, == 0x9336, ||1)) {
+ type = dasd_fba;
+ } else
+#endif /* CONFIG_DASD_FBA */
+#ifdef CONFIG_DASD_MDSK
+ if (MACHINE_IS_VM) {
+ type = dasd_mdsk;
+ } else
+#endif /* CONFIG_DASD_MDSK */
{
type = dasd_none;
}
+
FUNCTION_EXIT ("check_type");
return type;
}
@@ -567,7 +476,7 @@
static int
dasd_read_characteristics (dasd_information_t * info)
{
- int rc;
+ int rc = 0;
int ct = 0;
dev_info_t *di;
dasd_type_t dt;
@@ -598,13 +507,25 @@
#ifdef CONFIG_DASD_ECKD
case dasd_eckd:
ct = 64;
+ rc = read_dev_chars (info->info.irq,
+ (void *) &(info->rdc_data), ct);
break;
#endif /* CONFIG_DASD_ECKD */
+#ifdef CONFIG_DASD_FBA
+ case dasd_fba:
+ ct = 32;
+ rc = read_dev_chars (info->info.irq,
+ (void *) &(info->rdc_data), ct);
+ break;
+#endif /* CONFIG_DASD_FBA */
+#ifdef CONFIG_DASD_MDSK
+ case dasd_mdsk:
+ ct = 0;
+ break;
+#endif /* CONFIG_DASD_FBA */
default:
INTERNAL_ERROR ("don't know dasd type %d\n", dt);
}
- rc = read_dev_chars (info->info.irq,
- (void *) &(info->rdc_data), ct);
if (rc) {
PRINT_WARN ("RDC resulted in rc=%d\n", rc);
}
@@ -613,28 +534,12 @@
}
/* How many sectors must be in a request to dequeue it ? */
-#define QUEUE_BLOCKS 100
+#define QUEUE_BLOCKS 25
#define QUEUE_SECTORS (QUEUE_BLOCKS << dasd_info[di]->sizes.s2b_shift)
/* How often to retry an I/O before raising an error */
#define DASD_MAX_RETRIES 5
-static atomic_t bh_scheduled = ATOMIC_INIT (0);
-
-static inline void
-schedule_bh (void (*func) (void))
-{
- 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;
-}
-
static inline
cqr_t *
dasd_cqr_from_req (struct request *req)
@@ -652,8 +557,9 @@
if (!info)
return NULL;
/* if applicable relocate block */
- if (MINOR (req->rq_dev) & 0x1) {
- req->sector += info->sizes.first_sector;
+ 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) {
@@ -677,12 +583,7 @@
#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));
- }
+ ACS (cqr->status, CQR_STATUS_EMPTY, CQR_STATUS_FILLED);
}
return cqr;
}
@@ -694,24 +595,22 @@
int retries = DASD_SSCH_RETRIES;
int di, irq;
- dasd_debug (cqr); /* cqr */
+ dasd_debug ((unsigned long) cqr); /* cqr */
if (!cqr) {
PRINT_WARN ("(start_IO) no cqr passed\n");
return -EINVAL;
}
- if (cqr->magic != DASD_MAGIC) {
- PRINT_WARN ("(start_IO) magic number mismatch\n");
- 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);
+#ifdef CONFIG_DASD_MDSK
+ if (cqr->magic == MDSK_MAGIC) {
+ return dasd_mdsk_start_IO (cqr);
+ }
+#endif /* CONFIG_DASD_MDSK */
+ if (cqr->magic != DASD_MAGIC && cqr->magic != ERP_MAGIC) {
+ PRINT_ERR ("(start_IO) magic number mismatch\n");
return -EINVAL;
}
+ ACS (cqr->status, CQR_STATUS_QUEUED, CQR_STATUS_IN_IO);
di = cqr->devindex;
irq = dasd_info[di]->info.irq;
do {
@@ -732,10 +631,12 @@
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);
@@ -743,13 +644,7 @@
}
} 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);
- }
+ ACS (cqr->status, CQR_STATUS_IN_IO, CQR_STATUS_ERROR);
}
return rc;
}
@@ -773,8 +668,11 @@
dasd_dump_sense (devstat_t * stat)
{
int sl, sct;
+ if (!stat->flag | DEVSTAT_FLAG_SENSE_AVAIL) {
+ PRINT_INFO ("I/O status w/o sense data\n");
+ } else {
printk (KERN_INFO PRINTK_HEADER
- "-------------------I/O resulted in unit check:-----------\n");
+ "-------------------I/O result:-----------\n");
for (sl = 0; sl < 4; sl++) {
printk (KERN_INFO PRINTK_HEADER "Sense:");
for (sct = 0; sct < 8; sct++) {
@@ -784,35 +682,65 @@
printk ("\n");
}
}
+}
+
+int
+register_dasd_last (int di)
+{
+ int rc = 0;
+ int minor;
+ int i;
+
+ rc = dasd_disciplines[dasd_info[di]->type]->fill_sizes_last (di);
+ if (!rc) {
+ ACS (dasd_info[di]->status,
+ DASD_INFO_STATUS_DETECTED, DASD_INFO_STATUS_FORMATTED);
+ } else { /* -EMEDIUMTYPE: */
+ ACS (dasd_info[di]->status,
+ DASD_INFO_STATUS_DETECTED, DASD_INFO_STATUS_ANALYSED);
+ }
+ PRINT_INFO ("%04X (dasd%c):%ld kB <- block: %d on sector %d B\n",
+ dasd_info[di]->info.devno,
+ 'a' + di,
+ dasd_info[di]->sizes.kbytes,
+ dasd_info[di]->sizes.bp_block,
+ dasd_info[di]->sizes.bp_sector);
+ minor = di << PARTN_BITS;
+ dasd_blks[minor] = dasd_info[di]->sizes.kbytes;
+ for (i = 0; i < (1 << PARTN_BITS); i++) {
+ dasd_secsize[minor + i] = dasd_info[di]->sizes.bp_sector;
+ dasd_blksize[minor + i] = dasd_info[di]->sizes.bp_block;
+ dasd_maxsecs[minor + i] = 252 << dasd_info[di]->sizes.s2b_shift;
+ }
+ return rc;
+}
+
+void
+dasd_partn_detect (int di)
+{
+ int minor = di << PARTN_BITS;
+ while (atomic_read (&dasd_info[di]->status) !=
+ DASD_INFO_STATUS_FORMATTED) {
+ interruptible_sleep_on(&dasd_info[di]->wait_q);
+ }
+ dd_gendisk.part[minor].nr_sects = dasd_info[di]->sizes.kbytes << 1;
+ resetup_one_dev (&dd_gendisk, di);
+}
void
dasd_do_chanq (void)
{
dasd_chanq_t *qp = NULL;
- cqr_t *cqr;
+ cqr_t *cqr, *next;
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;
- }
- }
+ for (qp = cq_head; qp != NULL;) {
/* Get first request */
- dasd_debug (qp); /* qp */
+ dasd_debug ((unsigned long) qp);
cqr = (cqr_t *) (qp->head);
/* empty queue -> dequeue and proceed */
if (!cqr) {
@@ -823,9 +751,11 @@
}
/* process all requests on that queue */
do {
- cqr_t *next;
+ next = NULL;
dasd_debug ((unsigned long) cqr); /* cqr */
- if (cqr->magic != DASD_MAGIC) {
+ if (cqr->magic != DASD_MAGIC &&
+ cqr->magic != MDSK_MAGIC &&
+ cqr->magic != ERP_MAGIC) {
dasd_debug (0xc4c46ff2); /* DD?2 */
panic ( PRINTK_HEADER "do_cq:"
"magic mismatch %p -> %x\n",
@@ -837,51 +767,106 @@
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;
+ if (dasd_start_IO (cqr) != 0) {
+ PRINT_WARN("start_io failed\n");
}
break;
- case CQR_STATUS_ERROR:
+ 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 */
+ dasd_debug (0xc4c4e2e3);
if (dasd_start_IO (cqr) == 0) {
- atomic_dec (&chanq_tasks);
- cqr = NULL;
+ atomic_dec (&qp->
+ dirty_requests);
+ break;
}
+ }
+ ACS (cqr->status,
+ CQR_STATUS_ERROR,
+ CQR_STATUS_FAILED);
+ break;
+ }
+ case CQR_STATUS_ERP_PEND:{
+ /* This case is entered, when an interrupt
+ ended with a condittion */
+ dasd_erp_action_t erp_action;
+ erp_t *erp = NULL;
+
+ if ( cqr -> magic != ERP_MAGIC ) {
+ erp = request_er ();
+ if (erp == NULL) {
+ PRINT_WARN ("No memory for ERP%s\n", "");
+ break;
+ }
+ memset (erp, 0, sizeof (erp_t));
+ erp->cqr.magic = ERP_MAGIC;
+ erp->cqr.int4cqr = cqr;
+ erp->cqr.devindex= cqr->devindex;
+ erp_action = dasd_erp_action (cqr);
+ if (erp_action) {
+ PRINT_WARN ("Taking ERP action %p\n", erp_action);
+ erp_action (erp);
+ }
+ dasd_chanq_enq_head(qp, (cqr_t *) erp);
+ next = (cqr_t *) erp;
} else {
- atomic_set (&cqr->status,
+ PRINT_WARN("ERP_ACTION failed\n");
+ ACS (cqr->status,
+ CQR_STATUS_ERP_PEND,
CQR_STATUS_FAILED);
}
break;
- case CQR_STATUS_DONE:
+ }
+ case CQR_STATUS_ERP_ACTIVE:
+ break;
+ case CQR_STATUS_DONE:{
next = cqr->next;
- dasd_debug (0xc4c49692); /* DDok */
+ if (cqr->magic == DASD_MAGIC) {
+ dasd_debug (0xc4c49692);
+ } else if (cqr->magic == ERP_MAGIC) {
+ dasd_erp_action_t erp_postaction;
+ erp_t *erp = (erp_t *) cqr;
+ erp_postaction =
+ dasd_erp_postaction (erp);
+ if (erp_postaction)
+ erp_postaction (erp);
+ atomic_dec (&qp->dirty_requests);
+ } else if (cqr->magic == MDSK_MAGIC) {
+ } else {
+ PRINT_WARN ("unknown magic%s\n", "");
+ }
dasd_end_cqr (cqr, 1);
- atomic_dec (&chanq_tasks);
- cqr = next;
break;
- case CQR_STATUS_FAILED:
+ }
+ case CQR_STATUS_FAILED: {
next = cqr->next;
- dasd_debug (0xc4c47a7a); /* DD:: */
+ if (cqr->magic == DASD_MAGIC) {
+ dasd_debug (0xc4c49692);
+ } else if (cqr->magic == ERP_MAGIC) {
+ dasd_erp_action_t erp_postaction;
+ erp_t *erp = (erp_t *) cqr;
+ erp_postaction =
+ dasd_erp_postaction (erp);
+ if (erp_postaction)
+ erp_postaction (erp);
+ } else if (cqr->magic == MDSK_MAGIC) {
+ } else {
+ PRINT_WARN ("unknown magic%s\n", "");
+ }
dasd_end_cqr (cqr, 0);
- atomic_dec (&chanq_tasks);
- cqr = next;
+ atomic_dec (&qp->dirty_requests);
break;
+ }
default:
PRINT_WARN ("unknown cqrstatus\n");
- cqr = NULL;
}
s390irq_spin_unlock_irqrestore (irq, flags);
- } while (cqr);
+ } while ((cqr = next) != NULL);
qp = qp->next_q;
}
spin_lock (&io_request_lock);
@@ -901,20 +886,15 @@
void
do_dasd_request (void)
{
- struct request *req;
- struct request *prev;
- struct request *next;
- char broken[DASD_MAX_DEVICES] = {0,};
- char busy[DASD_MAX_DEVICES] = {0,};
- int di;
+ struct request *req, *next, *prev;
cqr_t *cqr;
- long caller;
dasd_chanq_t *q;
long flags;
- int irq;
+ int di, irq;
+ int broken, busy;
dasd_debug (0xc4d90000); /* DR */
- dasd_debug (__builtin_return_address(0)); /* calleraddres */
+ dasd_debug ((unsigned long) __builtin_return_address (0));
prev = NULL;
for (req = CURRENT; req != NULL; req = next) {
next = req->next;
@@ -926,12 +906,11 @@
irq = dasd_info[di]->info.irq;
s390irq_spin_lock_irqsave (irq, flags);
q = &dasd_info[di]->queue;
- busy[di] = busy[di]||(atomic_read(&q->flags)&DASD_CHANQ_BUSY);
- if ( !busy[di] ||
- ((!broken[di]) && (req->nr_sectors >= QUEUE_SECTORS))) {
-#if 0
- if ( q -> queued_requests < QUEUE_THRESHOLD ) {
-#endif
+ busy = atomic_read (&q->flags) & DASD_CHANQ_BUSY;
+ broken = atomic_read (&q->flags) & DASD_REQUEST_Q_BROKEN;
+ if (!busy ||
+ (!broken &&
+ (req->nr_sectors >= QUEUE_SECTORS))) {
if (prev) {
prev->next = next;
} else {
@@ -950,24 +929,22 @@
}
dasd_debug ((unsigned long) cqr); /* cqr */
dasd_chanq_enq (q, cqr);
- if (!(atomic_read (&q->flags) &
- DASD_CHANQ_ACTIVE)) {
+ if (!(atomic_read (&q->flags) & DASD_CHANQ_ACTIVE)) {
cql_enq_head (q);
}
- if (!busy[di]) {
- dasd_debug (0xc4d9e2e3); /* DRST */
- if (dasd_start_IO (cqr) != 0) {
- atomic_inc (&chanq_tasks);
- schedule_bh (dasd_do_chanq);
- busy[di] = 1;
+ if (!busy) {
+ atomic_clear_mask (DASD_REQUEST_Q_BROKEN,
+ &q->flags);
+ if ( atomic_read (&q->dirty_requests) == 0 ) {
+ if (dasd_start_IO (cqr) == 0) {
+ } else {
+ dasd_schedule_bh (dasd_do_chanq);
}
}
-#if 0
}
-#endif
} else {
dasd_debug (0xc4d9c2d9); /* DRBR */
- broken[di] = 1;
+ atomic_set_mask (DASD_REQUEST_Q_BROKEN, &q->flags);
prev = req;
}
cont:
@@ -983,60 +960,88 @@
int ip;
cqr_t *cqr;
int done_fast_io = 0;
+ dasd_era_t era;
+ static int counter = 0;
dasd_debug (0xc4c80000); /* DH */
- if (!stat)
+ if (!stat) {
PRINT_ERR ("handler called without devstat");
+ return;
+ }
ip = stat->intparm;
dasd_debug (ip); /* intparm */
- switch (ip) { /* filter special intparms... */
- case 0x00000000: /* no intparm: unsolicited interrupt */
+ if (!ip) { /* no intparm: unsolicited interrupt */
dasd_debug (0xc4c8a489); /* DHui */
- PRINT_INFO ("Unsolicited interrupt on device %04X\n",
+ PRINT_INFO ("%04X caught unsolicited interrupt\n",
stat->devno);
- dasd_dump_sense (stat);
return;
- default:
+ }
if (ip & 0x80000001) {
dasd_debug (0xc4c8a489); /* DHui */
- PRINT_INFO ("Spurious interrupt %08x on device %04X\n",
- ip, stat->devno);
+ PRINT_INFO ("%04X caught spurious interrupt with parm %08x\n",
+ stat->devno, ip);
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;
- }
+ if (cqr->magic == DASD_MAGIC || cqr->magic == ERP_MAGIC) {
asm volatile ("STCK %0":"=m" (cqr->stopclk));
- if (stat->cstat == 0x00 && stat->dstat == 0x0c) {
+ if ((stat->cstat == 0x00 &&
+ stat->dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) ||
+ ((era = dasd_erp_examine (cqr, stat)) == dasd_era_none)) {
dasd_debug (0xc4c89692); /* DHok */
- if (atomic_compare_and_swap (CQR_STATUS_IN_IO,
- CQR_STATUS_DONE,
- &cqr->status)) {
- PRINT_WARN ("handler: cqrstat changed%d\n",
- atomic_read (&cqr->status));
+#if 0
+ if ( counter < 20 || cqr -> magic == ERP_MAGIC) {
+ counter ++;
+#endif
+ ACS (cqr->status, CQR_STATUS_IN_IO, CQR_STATUS_DONE);
+#if 0
+ } else {
+ counter=0;
+ PRINT_WARN ("Faking I/O error\n");
+ ACS (cqr->status, CQR_STATUS_IN_IO, CQR_STATUS_ERP_PEND);
+ atomic_inc (&dasd_info[cqr->devindex]->
+ queue.dirty_requests);
+ }
+#endif
+ if (atomic_read (&dasd_info[cqr->devindex]->status) ==
+ DASD_INFO_STATUS_DETECTED) {
+ register_dasd_last (cqr->devindex);
+ if ( dasd_info[cqr->devindex]->wait_q ) {
+ wake_up( &dasd_info[cqr->devindex]->
+ wait_q);
+ }
}
- if (cqr->next) {
+ if (cqr->next &&
+ (atomic_read (&cqr->next->status) ==
+ CQR_STATUS_QUEUED)) {
dasd_debug (0xc4c8e2e3); /* DHST */
- if (dasd_start_IO (cqr->next) == 0)
+ if (dasd_start_IO (cqr->next) == 0) {
done_fast_io = 1;
+ } else {
}
- break;
}
+ } else { /* only visited in case of error ! */
dasd_debug (0xc4c8c5d9); /* DHER */
+ dasd_dump_sense (stat);
if (!cqr->dstat)
cqr->dstat = kmalloc (sizeof (devstat_t),
GFP_ATOMIC);
if (cqr->dstat) {
memcpy (cqr->dstat, stat, sizeof (devstat_t));
} else {
- PRINT_ERR ("no memory for dtstat\n");
+ PRINT_ERR ("no memory for dstat\n");
}
+ atomic_inc (&dasd_info[cqr->devindex]->
+ queue.dirty_requests);
/* errorprocessing */
- atomic_set (&cqr->status, CQR_STATUS_ERROR);
+ if (era == dasd_era_fatal) {
+ PRINT_WARN ("ERP returned fatal error\n");
+ ACS (cqr->status,
+ CQR_STATUS_IN_IO, CQR_STATUS_FAILED);
+ } else {
+ ACS (cqr->status,
+ CQR_STATUS_IN_IO, CQR_STATUS_ERP_PEND);
+ }
}
if (done_fast_io == 0)
atomic_clear_mask (DASD_CHANQ_BUSY,
@@ -1048,20 +1053,86 @@
dasd_wakeup ();
} else if (! (cqr->options & DOIO_WAIT_FOR_INTERRUPT) ){
dasd_debug (0xc4c8a293); /* DHsl */
- atomic_inc (&chanq_tasks);
- schedule_bh (dasd_do_chanq);
+ dasd_schedule_bh (dasd_do_chanq);
} else {
dasd_debug (0x64686f6f); /* DH_g */
dasd_debug (cqr->flags); /* DH_g */
}
+ } else {
+ dasd_debug (0xc4c86ff1); /* DH?1 */
+ PRINT_ERR ("handler:magic mismatch on %p %08x\n",
+ cqr, cqr->magic);
+ return;
+ }
dasd_debug (0xc4c86d6d); /* DHwu */
}
static int
+dasd_format (int dev, format_data_t * fdata)
+{
+ int rc;
+ int devindex = DEVICE_NR (dev);
+ dasd_chanq_t *q;
+ cqr_t *cqr;
+ int irq;
+ long flags;
+ PRINT_INFO ("%04X called format on %x\n",
+ dasd_info[devindex]->info.devno, dev);
+ if (MINOR (dev) & (0xff >> (8 - PARTN_BITS))) {
+ PRINT_WARN ("Can't format partition! minor %x %x\n",
+ MINOR (dev), 0xff >> (8 - PARTN_BITS));
+ return -EINVAL;
+ }
+ down (&dasd_info[devindex]->sem);
+ atomic_set (&dasd_info[devindex]->status,
+ DASD_INFO_STATUS_UNKNOWN);
+ if (dasd_info[devindex]->open_count == 1) {
+ rc = dasd_disciplines[dasd_info[devindex]->type]->
+ dasd_format (devindex, fdata);
+ if (rc) {
+ PRINT_WARN ("Formatting failed rc=%d\n", rc);
+ up (&dasd_info[devindex]->sem);
+ return rc;
+ }
+ } else {
+ PRINT_WARN ("device is open! %d\n", dasd_info[devindex]->open_count);
+ up (&dasd_info[devindex]->sem);
+ return -EINVAL;
+ }
+#if DASD_PARANOIA > 1
+ if (!dasd_disciplines[dasd_info[devindex]->type]->fill_sizes_first) {
+ INTERNAL_CHECK ("No fill_sizes for dt=%d\n", dasd_info[devindex]->type);
+ } else
+#endif /* DASD_PARANOIA */
+ {
+ ACS (dasd_info[devindex]->status,
+ DASD_INFO_STATUS_UNKNOWN, DASD_INFO_STATUS_DETECTED);
+ irq = dasd_info[devindex]->info.irq;
+ PRINT_INFO ("%04X reacessing, irq %x, index %d\n",
+ get_devno_by_irq (irq), irq, devindex);
+ s390irq_spin_lock_irqsave (irq, flags);
+ q = &dasd_info[devindex]->queue;
+ cqr = dasd_disciplines[dasd_info[devindex]->type]->
+ fill_sizes_first (devindex);
+ dasd_chanq_enq (q, cqr);
+ if (!(atomic_read (&q->flags) & DASD_CHANQ_ACTIVE)) {
+ cql_enq_head (q);
+ }
+ dasd_schedule_bh (dasd_do_chanq);
+ s390irq_spin_unlock_irqrestore (irq, flags);
+ }
+ up (&dasd_info[devindex]->sem);
+ return rc;
+}
+
+static int
register_dasd (int irq, dasd_type_t dt, dev_info_t * info)
{
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");
@@ -1084,10 +1155,23 @@
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);
- atomic_set (&dasd_info[di]->queue.flags, 0);
dasd_info[di]->type = dt;
dasd_info[di]->irq = irq;
dasd_info[di]->sem = MUTEX;
+#ifdef CONFIG_DASD_MDSK
+ if (dt == dasd_mdsk) {
+ dasd_info[di]->rdc_data = kmalloc (
+ sizeof (dasd_characteristics_t), GFP_ATOMIC);
+ if (!dasd_info[di]->rdc_data) {
+ PRINT_WARN ("No memory for char on irq %d\n", irq);
+ goto unalloc;
+ }
+ dasd_info[di]->rdc_data->mdsk.dev_nr = dasd_info[di]->
+ info.devno;
+ dasd_info[di]->rdc_data->mdsk.rdc_len =
+ sizeof (dasd_mdsk_characteristics_t);
+ } else
+#endif /* CONFIG_DASD_MDSK */
rc = dasd_read_characteristics (dasd_info[di]);
if (rc) {
PRINT_WARN ("RDC returned error %d\n", rc);
@@ -1106,30 +1190,52 @@
rc = -ENODEV;
goto unalloc;
}
+#ifdef CONFIG_DASD_MDSK
+ if (dt == dasd_mdsk) {
+
+ } else
+#endif /* CONFIG_DASD_MDSK */
rc = request_irq (irq, dasd_handler, 0, "dasd",
&(dasd_info[di]->dev_status));
+ ACS (dasd_info[di]->status,
+ DASD_INFO_STATUS_UNKNOWN, DASD_INFO_STATUS_DETECTED);
if (rc) {
#if DASD_PARANOIA > 0
printk (KERN_WARNING PRINTK_HEADER
"Cannot register irq %d, rc=%d\n",
irq, rc);
-#endif /* DASD_DEBUG */
+#endif /* DASD_PARANOIA */
rc = -ENODEV;
goto unalloc;
}
#if DASD_PARANOIA > 1
- if (!dasd_disciplines[dt]->fill_sizes) {
+ if (!dasd_disciplines[dt]->fill_sizes_first) {
INTERNAL_CHECK ("No fill_sizes for dt=%d\n", dt);
goto unregister;
}
#endif /* DASD_PARANOIA */
- fill_sizes (di);
+ irq = dasd_info[di]->info.irq;
+ PRINT_INFO ("%04X trying to access, irq %x, index %d\n",
+ get_devno_by_irq (irq), irq, di);
+ s390irq_spin_lock_irqsave (irq, flags);
+ q = &dasd_info[di]->queue;
+ cqr = dasd_disciplines[dt]->fill_sizes_first (di);
+ dasd_chanq_enq (q, cqr);
+ if (!(atomic_read (&q->flags) & DASD_CHANQ_ACTIVE)) {
+ cql_enq_head (q);
+ }
+ if (dasd_start_IO (cqr) != 0) {
+ dasd_schedule_bh (dasd_do_chanq);
+ }
+ s390irq_spin_unlock_irqrestore (irq, flags);
+
goto exit;
unregister:
free_irq (irq, &(dasd_info[di]->dev_status));
unalloc:
kfree (dasd_info[di]);
+ dasd_info[di] = NULL;
exit:
spin_unlock (®ister_lock);
FUNCTION_EXIT ("register_dasd");
@@ -1143,68 +1249,57 @@
dev_info_t info;
dasd_type_t dt;
- FUNCTION_ENTRY ("probe_for_dasd");
-
rc = get_dev_info_by_irq (irq, &info);
+
if (rc == -ENODEV) { /* end of device list */
return rc;
+ } else if ((info.status & DEVSTAT_DEVICE_OWNED)) {
+ return -EBUSY;
+ } else if ((info.status & DEVSTAT_NOT_OPER)) {
+ return -ENODEV;
}
#if DASD_PARANOIA > 2
- if (rc) {
+ else {
INTERNAL_CHECK ("unknown rc %d of get_dev_info", rc);
return rc;
}
#endif /* DASD_PARANOIA */
- if ((info.status & DEVSTAT_NOT_OPER)) {
+
+ dt = check_type (&info); /* make a first guess */
+
+ if (dt == dasd_none) {
return -ENODEV;
}
- 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 {
+ if (!dasd_disciplines[dt]->ck_devinfo) {
INTERNAL_ERROR ("no ck_devinfo function%s\n", "");
return -ENODEV;
}
-#endif /* DASD_PARANOIA */
- if (rc == -ENODEV) {
+ rc = dasd_disciplines[dt]->ck_devinfo (&info);
+ if (rc) {
return rc;
}
-#if DASD_PARANOIA > 2
- if (rc) {
- INTERNAL_CHECK ("unknown error rc=%d\n", rc);
+ if (dasd_probeonly) {
+ PRINT_INFO ("%04X not enabled due to probeonly mode\n",
+ info.devno);
+ dasd_add_devno_to_ranges (info.devno);
return -ENODEV;
- }
-#endif /* DASD_PARANOIA */
+ } else {
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);
+ PRINT_WARN ("%04X not enabled due to errors\n",
+ info.devno);
} else {
- PRINT_INFO ("devno %x added as minor %d (%s)\n",
+ PRINT_INFO ("%04X is (dasd%c) minor %d (%s)\n",
info.devno,
+ 'a' + devindex_from_devno (info.devno),
devindex_from_devno (info.devno) << PARTN_BITS,
dasd_name[dt]);
}
- case dasd_none:
- break;
- default:
- PRINT_DEBUG ("unknown device type\n");
- break;
- }
- FUNCTION_EXIT ("probe_for_dasd");
+
return rc;
}
@@ -1214,7 +1309,7 @@
int rc = 0;
FUNCTION_ENTRY ("register_major");
- rc = register_blkdev (major, DASD_NAME, &dasd_file_operations);
+ rc = register_blkdev (major, DASD_NAME, &dasd_device_operations);
#if DASD_PARANOIA > 1
if (rc) {
PRINT_WARN ("registering major -> rc=%d aborting... \n", rc);
@@ -1233,24 +1328,6 @@
modifier is to make sure, that they are only called via the kernel's methods
*/
-static ssize_t
-dasd_read (struct file *filp, char *b, size_t s, loff_t * o)
-{
- ssize_t rc;
- FUNCTION_ENTRY ("dasd_read");
- rc = block_read (filp, b, s, o);
- FUNCTION_EXIT ("dasd_read");
- return rc;
-}
-static ssize_t
-dasd_write (struct file *filp, const char *b, size_t s, loff_t * o)
-{
- ssize_t rc;
- FUNCTION_ENTRY ("dasd_write");
- rc = block_write (filp, b, s, o);
- FUNCTION_EXIT ("dasd_write");
- return rc;
-}
static int
dasd_ioctl (struct inode *inp, struct file *filp,
unsigned int no, unsigned long data)
@@ -1298,19 +1375,6 @@
}
static int
-dasd_fsync (struct file *filp, struct dentry *d)
-{
- int rc = 0;
- FUNCTION_ENTRY ("dasd_fsync");
- if (!filp) {
- return -EINVAL;
- }
- rc = block_fsync (filp, d);
- FUNCTION_EXIT ("dasd_fsync");
- return rc;
-}
-
-static int
dasd_release (struct inode *inp, struct file *filp)
{
int rc = 0;
@@ -1346,23 +1410,14 @@
}
static struct
-file_operations dasd_file_operations =
+file_operations dasd_device_operations =
{
- NULL, /* loff_t(*llseek)(struct file *,loff_t,int); */
- dasd_read,
- dasd_write,
- NULL, /* int(*readdir)(struct file *,void *,filldir_t); */
- NULL, /* u int(*poll)(struct file *,struct poll_table_struct *); */
- dasd_ioctl,
- NULL, /* int (*mmap) (struct file *, struct vm_area_struct *); */
- dasd_open,
- NULL, /* int (*flush) (struct file *) */
- dasd_release,
- dasd_fsync,
- NULL, /* int (*fasync) (int, struct file *, int);
- int (*check_media_change) (kdev_t dev);
- int (*revalidate) (kdev_t dev);
- int (*lock) (struct file *, int, struct file_lock *); */
+ read:block_read,
+ write:block_write,
+ fsync:block_fsync,
+ ioctl:dasd_ioctl,
+ open:dasd_open,
+ release:dasd_release,
};
int
@@ -1371,20 +1426,29 @@
int rc = 0;
int i;
- FUNCTION_ENTRY ("dasd_init");
PRINT_INFO ("initializing...\n");
- atomic_set (&chanq_tasks, 0);
atomic_set (&bh_scheduled, 0);
spin_lock_init (&dasd_lock);
+#ifdef CONFIG_DASD_MDSK
+ /*
+ * enable service-signal external interruptions,
+ * Control Register 0 bit 22 := 1
+ * (besides PSW bit 7 must be set to 1 somewhere for external
+ * interruptions)
+ */
+ ctl_set_bit (0, 9);
+ register_external_interrupt (0x2603, do_dasd_mdsk_interrupt);
+#endif
+ dasd_debug_info = debug_register ("dasd", 1, 4);
/* First register to the major number */
+
rc = register_major (MAJOR_NR);
#if DASD_PARANOIA > 1
if (rc) {
PRINT_WARN ("registering major_nr returned rc=%d\n", rc);
return rc;
}
-#endif /* DASD_PARANOIA */
- read_ahead[MAJOR_NR] = 8;
+#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;
@@ -1393,17 +1457,14 @@
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", "");
+ case -EBUSY:
break;
case -EMEDIUMTYPE:
PRINT_WARN ("DASD not formatted%s\n", "");
@@ -1413,33 +1474,64 @@
break;
}
}
- FUNCTION_CONTROL ("detection loop completed%s\n", "");
+ FUNCTION_CONTROL ("detection loop completed %s partn check...\n", "");
/* Finally do the genhd stuff */
-#if 0 /* 2 b done */
dd_gendisk.next = gendisk_head;
gendisk_head = &dd_gendisk;
- for (i = 0; i < DASD_MAXDEVICES; i++) {
- LOOP_CONTROL ("Setting partitions of DASD %d\n", i);
- resetup_one_dev (&dd_gendisk, i);
+ dasd_information = dasd_info; /* to enable genhd to know about DASD */
+ tod_wait (1000000);
+
+ /* wait on root filesystem before detecting partitions */
+ if (MAJOR (ROOT_DEV) == DASD_MAJOR) {
+ int count = 10;
+ i = DEVICE_NR (ROOT_DEV);
+ if (dasd_info[i] == NULL) {
+ panic ("root device not accessible\n");
+ }
+ while ((atomic_read (&dasd_info[i]->status) !=
+ DASD_INFO_STATUS_FORMATTED) &&
+ count ) {
+ PRINT_INFO ("Waiting on root volume...%d seconds left\n", count);
+ tod_wait (1000000);
+ count--;
+ }
+ if (count == 0) {
+ panic ("Waiting on root volume...giving up!\n");
+ }
+ }
+ for (i = 0; i < DASD_MAX_DEVICES; i++) {
+ if (dasd_info[i]) {
+ if (atomic_read (&dasd_info[i]->status) ==
+ DASD_INFO_STATUS_FORMATTED) {
+ dasd_partn_detect (i);
+ } else { /* start kernel thread for devices not ready now */
+ kernel_thread (dasd_partn_detect, (void *) i,
+ CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ }
+ }
}
-#endif /* 0 */
- FUNCTION_EXIT ("dasd_init");
return rc;
}
#ifdef MODULE
+
int
init_module (void)
{
int rc = 0;
- FUNCTION_ENTRY ("init_module");
- PRINT_INFO ("trying to load module\n");
+ PRINT_INFO ("Initializing module\n");
+ rc = dasd_parse_module_params ();
+ if (rc == 0) {
+ PRINT_INFO ("module parameters parsed successfully\n");
+ } else {
+ PRINT_WARN ("parsing parameters returned rc=%d\n", rc);
+ }
rc = dasd_init ();
if (rc == 0) {
- PRINT_INFO ("module loaded successfully\n");
+ PRINT_INFO ("module initialized successfully\n");
} else {
- PRINT_WARN ("warning: Module load returned rc=%d\n", rc);
+ PRINT_WARN ("initializing module returned rc=%d\n", rc);
}
FUNCTION_EXIT ("init_module");
return rc;
@@ -1449,19 +1541,32 @@
cleanup_module (void)
{
int rc = 0;
+ struct gendisk *genhd = gendisk_head, *prev = NULL;
- FUNCTION_ENTRY ("cleanup_module");
PRINT_INFO ("trying to unload module \n");
- /* FIXME: replace by proper unload functionality */
- INTERNAL_ERROR ("Modules not yet implemented %s", "");
+ /* unregister gendisk stuff */
+ for (genhd = gendisk_head; genhd; prev = genhd, genhd = genhd->next) {
+ if (genhd == dd_gendisk) {
+ if (prev)
+ prev->next = genhd->next;
+ else {
+ gendisk_head = genhd->next;
+ }
+ break;
+ }
+ }
+ /* unregister devices */
+ for (i = 0; i = DASD_MAX_DEVICES; i++) {
+ if (dasd_info[i])
+ dasd_unregister_dasd (i);
+ }
if (rc == 0) {
PRINT_INFO ("module unloaded successfully\n");
} else {
PRINT_WARN ("module unloaded with errors\n");
}
- FUNCTION_EXIT ("cleanup_module");
}
#endif /* MODULE */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)