patch-2.2.14 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: 1491
- Date:
Tue Jan 4 10:12:18 2000
- Orig file:
v2.2.13/linux/drivers/s390/block/dasd.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.2.13/linux/drivers/s390/block/dasd.c linux/drivers/s390/block/dasd.c
@@ -0,0 +1,1490 @@
+/*
+ * 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
+ */
+
+#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/genhd.h>
+#include <linux/hdreg.h>
+#include <linux/interrupt.h>
+#include <linux/ctype.h>
+#include <asm/io.h>
+#include <asm/spinlock.h>
+#include <asm/semaphore.h>
+#include <asm/ebcdic.h>
+#include <asm/uaccess.h>
+
+#include "../../../arch/s390/kernel/irq.h"
+
+#include "dasd.h"
+#include <linux/blk.h>
+
+#include "dasd_types.h"
+#include "dasd_ccwstuff.h"
+
+#define PRINTK_HEADER DASD_NAME":"
+
+#define CCW_READ_DEVICE_CHARACTERISTICS 0x64
+
+#define DASD_SSCH_RETRIES 5
+
+/* 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 ) )
+
+/* 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_debug (unsigned long tag);
+void dasd_profile_add (cqr_t *cqr);
+void dasd_proc_init (void);
+
+static struct file_operations dasd_file_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;
+
+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;
+}
+
+/* sets the string pointer after the next comma */
+static void
+dasd_scan_for_next_comma (char **strptr)
+{
+ while (((**strptr) != ',') && ((**strptr)++))
+ (*strptr)++;
+
+ /* set the position AFTER the comma */
+ if (**strptr == ',')
+ (*strptr)++;
+}
+
+/*sets the string pointer after the next comma, if a parse error occured */
+static int
+dasd_get_next_int (char **strptr)
+{
+ 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)
+{
+ 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;
+}
+
+/* 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);
+}
+
+/* dasd_insert_range skips ranges, if the start or the end is -1 */
+static void
+dasd_insert_range (int start, int end)
+{
+ 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");
+ 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");
+ 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)
+{
+ if (sem != NULL) {
+ up (sem);
+ }
+}
+
+void
+sleep (int timeout)
+{
+ struct semaphore sem = MUTEX_LOCKED;
+ struct timer_list timer;
+
+ init_timer (&timer);
+ timer.data = (unsigned long) &sem;
+ timer.expires = jiffies + timeout;
+ timer.function = (void (*)(unsigned long)) sleep_done;
+ printk (KERN_DEBUG PRINTK_HEADER
+ "Sleeping for timer tics %d\n", timeout);
+ add_timer (&timer);
+ down (&sem);
+ del_timer (&timer);
+}
+
+#ifdef CONFIG_DASD_ECKD
+extern dasd_operations_t dasd_eckd_operations;
+#endif /* CONFIG_DASD_ECKD */
+
+dasd_operations_t *dasd_disciplines[] =
+{
+#ifdef CONFIG_DASD_ECKD
+ &dasd_eckd_operations
+#endif /* CONFIG_DASD_ECKD */
+};
+
+char *dasd_name[] =
+{
+#ifdef CONFIG_DASD_ECKD
+ "ECKD"
+#endif /* CONFIG_DASD_ECKD */
+};
+
+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] =
+ ((PAGE_SIZE/sizeof(ccw1_t))-4)<<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] =
+ ((PAGE_SIZE/sizeof(ccw1_t))-4)<<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)
+{
+ 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);
+
+ 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;
+ }
+ RO_IOCTLS (inp->i_rdev, data);
+
+ case BIODASDRSID:{
+ rc = copy_to_user ((void *) data,
+ &(dev->info.sid_data),
+ sizeof (senseid_t));
+ break;
+ }
+ case BIODASDRWTB:{
+ int offset = 0;
+ int xlt;
+ rc = copy_from_user (&xlt, (void *) data,
+ sizeof (int));
+ 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));
+ 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);
+ }
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static void
+dasd_end_request (struct request *req, int uptodate)
+{
+ 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);
+ }
+ FUNCTION_EXIT ("dasd_end_request");
+ return;
+}
+
+void
+dasd_wakeup (void)
+{
+ wake_up (&dasd_waitq);
+}
+
+int
+dasd_unregister_dasd (int irq, dasd_type_t dt, dev_info_t * 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;
+}
+
+/* Below you find the functions already cleaned up */
+static dasd_type_t
+check_type (dev_info_t * info)
+{
+ dasd_type_t type = dasd_none;
+
+ FUNCTION_ENTRY ("check_type");
+#ifdef CONFIG_DASD_ECKD
+ if (MATCH (info, == 0x3990, ||1, == 0x3390, ||1) ||
+ MATCH (info, == 0x3990, ||1, == 0x3380, ||1)) {
+ type = dasd_eckd;
+ } else
+#endif /* CONFIG_DASD_ECKD */
+ {
+ type = dasd_none;
+ }
+ FUNCTION_EXIT ("check_type");
+ return type;
+}
+
+static int
+dasd_read_characteristics (dasd_information_t * info)
+{
+ int rc;
+ int ct = 0;
+ dev_info_t *di;
+ dasd_type_t dt;
+
+ FUNCTION_ENTRY ("read_characteristics");
+ if (info == NULL) {
+ return -ENODEV;
+ }
+ di = &(info->info);
+ if (di == NULL) {
+ return -ENODEV;
+ }
+ dt = check_type (di);
+ /* Some cross-checks, if the cu supports RDC */
+ if (MATCH (di, == 0x2835, ||1, ||1, ||1) ||
+ MATCH (di, == 0x3830, ||1, ||1, ||1) ||
+ MATCH (di, == 0x3830, ||1, ||1, ||1) ||
+ MATCH (di, == 0x3990, <=0x03, == 0x3380, <=0x0d)) {
+ PRINT_WARN ("Device %d (%x/%x at %x/%x) supports no RDC\n",
+ info->info.irq,
+ di->sid_data.dev_type,
+ di->sid_data.dev_model,
+ di->sid_data.cu_type,
+ di->sid_data.cu_model);
+ return -EINVAL;
+ }
+ switch (dt) {
+#ifdef CONFIG_DASD_ECKD
+ case dasd_eckd:
+ ct = 64;
+ break;
+#endif /* CONFIG_DASD_ECKD */
+ 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);
+ }
+ FUNCTION_EXIT ("read_characteristics");
+ return rc;
+}
+
+/* How many sectors must be in a request to dequeue it ? */
+#define QUEUE_BLOCKS 100
+#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)
+{
+ cqr_t *cqr = NULL;
+ int di;
+ dasd_information_t *info;
+
+ if (!req) {
+ PRINT_ERR ("No request passed!");
+ return NULL;
+ }
+ di = DEVICE_NR (req->rq_dev);
+ info = dasd_info[di];
+ if (!info)
+ return NULL;
+ /* if applicable relocate block */
+ if (MINOR (req->rq_dev) & 0x1) {
+ req->sector += info->sizes.first_sector;
+ }
+ /* Now check for consistency */
+ if (!req->nr_sectors) {
+ PRINT_WARN ("req: %p dev: %08x sector: %ld nr_sectors: %ld bh: %p\n",
+ req, req->rq_dev, req->sector, req->nr_sectors, req->bh);
+ return NULL;
+ }
+ if (((req->sector + req->nr_sectors) >> 1) > info->sizes.kbytes) {
+ printk (KERN_ERR PRINTK_HEADER
+ "Requesting I/O past end of device %d\n",
+ di);
+ return NULL;
+ }
+ cqr = dasd_disciplines[info->type]->get_req_ccw (di, req);
+ if (!cqr) {
+ PRINT_WARN ("empty CQR generated\n");
+ } else {
+ cqr->req = req;
+ cqr->int4cqr = cqr;
+ cqr->devindex = di;
+#ifdef DASD_PROFILE
+ asm volatile ("STCK %0":"=m" (cqr->buildclk));
+#endif /* DASD_PROFILE */
+ 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));
+ }
+ }
+ return cqr;
+}
+
+int
+dasd_start_IO (cqr_t * cqr)
+{
+ int rc = 0;
+ int retries = DASD_SSCH_RETRIES;
+ int di, irq;
+
+ 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);
+ 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);
+ }
+ }
+ return rc;
+}
+
+static inline
+void
+dasd_end_cqr (cqr_t * cqr, int uptodate)
+{
+ 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);
+ }
+}
+
+void
+dasd_dump_sense (devstat_t * stat)
+{
+ int sl, sct;
+ printk (KERN_INFO PRINTK_HEADER
+ "-------------------I/O Error-----------------------------\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");
+ }
+}
+
+void
+dasd_do_chanq (void)
+{
+ dasd_chanq_t *qp = NULL;
+ cqr_t *cqr;
+ long flags;
+ int irq,di;
+ int tasks;
+ atomic_set (&bh_scheduled, 0);
+ dasd_debug (0x44445f69); /* DD_i */
+ while ((tasks = atomic_read(&chanq_tasks)) != 0) {
+/* initialization and wraparound */
+ if (qp == NULL) {
+ dasd_debug (0x44445f68); /* DD_h */
+ qp = cq_head;
+ if (!qp) {
+ dasd_debug (0x44445f45); /* DD_E */
+ 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 the irq number: Ouch, FIXME!!!!! */
+#if 0
+ di =(((unsigned long)qp)-
+ ((unsigned long)&(dasd_info[0]->queue)))
+ / sizeof(dasd_information_t);
+ dasd_debug(di);
+ irq = dasd_info[di]->info.irq;
+ dasd_debug(irq);
+#endif
+/* Get first request */
+ cqr = (cqr_t *) (qp->head);
+ dasd_debug ((unsigned long) cqr); /* cqr */
+/* 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 (0x44445f64); /* DD_d */
+ dasd_debug ((unsigned long) cqr); /* cqr */
+ if (cqr->magic != DASD_MAGIC) {
+ dasd_debug (0x44445f65); /* DD_e */
+ PRINT_WARN ("do_cq:magic mismatch %p\n", cqr);
+ 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 (0x44445f48); /* DD_I */
+ cqr = NULL;
+ break;
+ case CQR_STATUS_QUEUED:
+ dasd_debug (0x44445f53); /* DD_S */
+ if (dasd_start_IO (cqr) == 0) {
+ atomic_dec (&chanq_tasks);
+ cqr = NULL;
+ }
+ break;
+ case CQR_STATUS_ERROR:
+ dasd_debug (0x44445f54); /* DD_E */
+ dasd_dump_sense (cqr->dstat);
+ if (cqr->retries++ < 5) {
+ atomic_set (&cqr->status,
+ CQR_STATUS_QUEUED);
+ if (dasd_start_IO (cqr) == 0) {
+ atomic_dec (&chanq_tasks);
+ cqr = NULL;
+ }
+ } else {
+ atomic_set (&cqr->status,
+ CQR_STATUS_FAILED);
+ }
+ break;
+ case CQR_STATUS_DONE:
+ next = cqr->next;
+ dasd_debug (0x44445f44); /* DD_D */
+ dasd_end_cqr (cqr, 1);
+ atomic_dec (&chanq_tasks);
+ cqr = next;
+ break;
+ case CQR_STATUS_FAILED:
+ next = cqr->next;
+ dasd_debug (0x44445f45); /* DD_F */
+ dasd_end_cqr (cqr, 0);
+ atomic_dec (&chanq_tasks);
+ cqr = next;
+ break;
+ default:
+ PRINT_WARN ("unknown cqrstatus\n");
+ cqr = NULL;
+ }
+ s390irq_spin_unlock_irqrestore (irq, flags);
+ } while (cqr);
+ qp = qp->next_q;
+ }
+ spin_lock (&io_request_lock);
+ do_dasd_request ();
+ spin_unlock (&io_request_lock);
+ dasd_debug (0x44445f6f); /* DD_o */
+}
+
+/*
+ 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.
+ */
+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;
+ cqr_t *cqr;
+ long caller;
+ dasd_chanq_t *q;
+ long flags;
+ int irq;
+
+ __asm__ ("lr %0,14":"=d" (caller));
+ dasd_debug (0x44525f69); /* DR_i */
+ dasd_debug ((unsigned long) caller); /* calleraddres */
+ prev = NULL;
+ for (req = CURRENT; req != NULL; req = next) {
+ next = req->next;
+ di = DEVICE_NR (req->rq_dev);
+ dasd_debug ((unsigned long) req); /* req */
+ dasd_debug (0x44520000 + /* DR## */
+ ((((di/16)<9?(di/16)+'0':(di/16)+'a'))<<8) +
+ (((di%16)<9?(di%16)+'0':(di%16)+'a')));
+ 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 (prev) {
+ prev->next = next;
+ } else {
+ CURRENT = next;
+ }
+ req->next = NULL;
+ if (req == &blk_dev[MAJOR_NR].plug) {
+ dasd_debug (0x44525f75); /* DR_u */
+ goto cont;
+ }
+ cqr = dasd_cqr_from_req (req);
+ if (!cqr) {
+ dasd_debug (0x44525f65); /* DR_e */
+ dasd_end_request (req, 0);
+ goto cont;
+ }
+ dasd_debug (0x44525f71); /* DR_q */
+ 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[di]) {
+ if (dasd_start_IO (cqr) == 0)
+ dasd_debug (0x44525f73); /* DR_s */
+ else {
+ atomic_inc (&chanq_tasks);
+ schedule_bh (dasd_do_chanq);
+ busy[di] = 1;
+ }
+ }
+ } else {
+ dasd_debug (0x44525f62); /* DR_b */
+ broken[di] = 1;
+ prev = req;
+ }
+ cont:
+ s390irq_spin_unlock_irqrestore (irq, flags);
+ }
+ dasd_debug (0x44525f6f); /* DR_o */
+}
+
+void
+dasd_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 (0x44485f69); /* DH_i */
+ if (!stat)
+ PRINT_ERR ("handler called without devstat");
+ ip = stat->intparm;
+ dasd_debug (ip); /* intparm */
+ switch (ip) { /* filter special intparms... */
+ case 0x00000000: /* no intparm: unsolicited interrupt */
+ dasd_debug (0x44485f30); /* DH_0 */
+ PRINT_INFO ("Unsolicited interrupt on device %04X\n",
+ stat->devno);
+ dasd_dump_sense (stat);
+ return;
+ default:
+ if (ip & 0x80000001) {
+ PRINT_INFO ("Spurious interrupt %08x on device %04X\n",
+ ip, stat->devno);
+ return;
+ }
+ cqr = (cqr_t *) ip;
+ if (cqr->magic != DASD_MAGIC) {
+ dasd_debug (0x44485f65); /* DH_e */
+ PRINT_WARN ("handler:magic mismatch on %p %08x\n",
+ cqr, cqr->magic);
+ return;
+ }
+ asm volatile ("STCK %0":"=m" (cqr->stopclk));
+ if (stat->cstat == 0x00 && stat->dstat == 0x0c) {
+ dasd_debug (0x44486f6b); /* 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 (cqr->next) {
+ dasd_debug (0x44485f6e); /* DH_n */
+ if (dasd_start_IO (cqr->next) == 0)
+ done_fast_io = 1;
+ }
+ break;
+ }
+ dasd_debug (0x44482121); /* DH!! */
+ if (!cqr->dstat)
+ cqr->dstat = kmalloc (sizeof (devstat_t),
+ GFP_ATOMIC);
+ if (cqr->dstat) {
+ memcpy (cqr->dstat, stat, sizeof (devstat_t));
+ dasd_dump_sense (cqr->dstat);
+ } else {
+ PRINT_ERR ("no memory for dtstat\n");
+ }
+ /* errorprocessing */
+ if (cqr->retries < DASD_MAX_RETRIES) {
+ dasd_debug (0x44485f72); /* DH_r */
+ atomic_set (&cqr->status, CQR_STATUS_ERROR);
+ } else {
+ dasd_debug (0x44485f6e); /* DH_f */
+ atomic_set (&cqr->status, CQR_STATUS_FAILED);
+ }
+
+ }
+ 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 (0x44485f77); /* DH_w */
+ dasd_wakeup ();
+ } else if (! (cqr->options & DOIO_WAIT_FOR_INTERRUPT) ){
+ dasd_debug (0x44485f73); /* DH_s */
+ atomic_inc (&chanq_tasks);
+ schedule_bh (dasd_do_chanq);
+ } else {
+ dasd_debug (cqr->flags); /* DH_g */
+ dasd_debug (0x44485f6f); /* DH_g */
+ }
+}
+
+static int
+register_dasd (int irq, dasd_type_t dt, dev_info_t * info)
+{
+ int rc = 0;
+ int di;
+ 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);
+ return -ENODEV;
+ }
+ if (dasd_info[di]) { /* devindex is not free */
+ INTERNAL_CHECK ("reusing allocated deviceindex %d\n", di);
+ return -ENODEV;
+ }
+ dasd_info[di] = (dasd_information_t *)
+ kmalloc (sizeof (dasd_information_t), GFP_ATOMIC);
+ if (dasd_info[di] == NULL) {
+ PRINT_WARN ("No memory for dasd_info_t on irq %d\n", irq);
+ return -ENOMEM;
+ }
+ memset (dasd_info[di], 0, sizeof (dasd_information_t));
+ memcpy (&(dasd_info[di]->info), info, sizeof (dev_info_t));
+ spin_lock_init (&dasd_info[di]->queue.f_lock);
+ spin_lock_init (&dasd_info[di]->queue.q_lock);
+ atomic_set (&dasd_info[di]->queue.flags, 0);
+ dasd_info[di]->type = dt;
+ dasd_info[di]->irq = irq;
+ dasd_info[di]->sem = MUTEX;
+ 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;
+ }
+ 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_DEBUG */
+ rc = -ENODEV;
+ goto unalloc;
+ }
+#if DASD_PARANOIA > 1
+ if (!dasd_disciplines[dt]->fill_sizes) {
+ INTERNAL_CHECK ("No fill_sizes for dt=%d\n", dt);
+ goto unregister;
+ }
+#endif /* DASD_PARANOIA */
+ fill_sizes (di);
+ goto exit;
+
+ unregister:
+ free_irq (irq, &(dasd_info[di]->dev_status));
+ unalloc:
+ kfree (dasd_info[di]);
+ exit:
+ spin_unlock (®ister_lock);
+ FUNCTION_EXIT ("register_dasd");
+ return rc;
+}
+
+static int
+probe_for_dasd (int irq)
+{
+ int rc;
+ 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;
+ }
+#if DASD_PARANOIA > 2
+ if (rc) {
+ INTERNAL_CHECK ("unknown rc %d of get_dev_info", rc);
+ return rc;
+ }
+#endif /* DASD_PARANOIA */
+ if ((info.status & DEVSTAT_NOT_OPER)) {
+ 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 {
+ 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]);
+ }
+ case dasd_none:
+ break;
+ default:
+ PRINT_DEBUG ("unknown device type\n");
+ break;
+ }
+ FUNCTION_EXIT ("probe_for_dasd");
+ return rc;
+}
+
+static int
+register_major (int major)
+{
+ int rc = 0;
+
+ FUNCTION_ENTRY ("register_major");
+ rc = register_blkdev (major, DASD_NAME, &dasd_file_operations);
+#if DASD_PARANOIA > 1
+ if (rc) {
+ PRINT_WARN ("registering major -> rc=%d aborting... \n", rc);
+ return rc;
+ }
+#endif /* DASD_PARANOIA */
+ blk_dev[major].request_fn = do_dasd_request;
+ FUNCTION_CONTROL ("successfully registered major: %d\n", major);
+ FUNCTION_EXIT ("register_major");
+ return rc;
+}
+
+/*
+ Below you find functions which are called from outside. Some of them may be
+ static, because they are called by their function pointers only. Thus static
+ modifier is to make sure, that they are only called via the kernel's methods
+ */
+
+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)
+{
+ int rc = 0;
+ FUNCTION_ENTRY ("dasd_ioctl");
+ if ((!inp) || !(inp->i_rdev)) {
+ return -EINVAL;
+ }
+ rc = do_dasd_ioctl (inp, no, data);
+ FUNCTION_EXIT ("dasd_ioctl");
+ return rc;
+}
+
+static int
+dasd_open (struct inode *inp, struct file *filp)
+{
+ int rc = 0;
+ dasd_information_t *dev;
+ FUNCTION_ENTRY ("dasd_open");
+ if ((!inp) || !(inp->i_rdev)) {
+ return -EINVAL;
+ }
+ dev = dasd_info[DEVICE_NR (inp->i_rdev)];
+ if (!dev) {
+ PRINT_DEBUG ("No device registered as %d (%d)\n",
+ inp->i_rdev, DEVICE_NR (inp->i_rdev));
+ return -EINVAL;
+ }
+ down (&dev->sem);
+ up (&dev->sem);
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif /* MODULE */
+#if DASD_PARANOIA > 2
+ if (dev->open_count < 0) {
+ INTERNAL_ERROR ("open count cannot be less than 0: %d",
+ dev->open_count);
+ return -EINVAL;
+ }
+#endif /* DASD_PARANOIA */
+ dev->open_count++;
+ FUNCTION_EXIT ("dasd_open");
+ return rc;
+}
+
+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;
+ 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;
+ }
+#endif /* DASD_PARANOIA */
+ FUNCTION_EXIT ("dasd_release");
+ return rc;
+}
+
+static struct
+file_operations dasd_file_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 *); */
+};
+
+int
+dasd_init (void)
+{
+ 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);
+ /* 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;
+ 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;
+ }
+ }
+ FUNCTION_CONTROL ("detection loop completed%s\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);
+ }
+#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");
+ 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 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");
+}
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)