]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] s390: dasd driver fixes.
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Mon, 14 Apr 2003 04:25:52 +0000 (21:25 -0700)
committerLinus Torvalds <torvalds@home.transmeta.com>
Mon, 14 Apr 2003 04:25:52 +0000 (21:25 -0700)
s390 dasd driver fixes:
 - Take request queue lock in dasd_end_request.
 - Make it work with CONFIG_DEVFS_FS=y.
 - Properly wait for the root device.
 - Cope with requests killed due to failed channel path.
 - Improve reference counting.
 - Remove devno from struct dasd_device.
 - Remove unnecessary bdget/bdput calls.

drivers/s390/Kconfig
drivers/s390/block/dasd.c
drivers/s390/block/dasd_devmap.c
drivers/s390/block/dasd_diag.c
drivers/s390/block/dasd_eckd.c
drivers/s390/block/dasd_fba.c
drivers/s390/block/dasd_genhd.c
drivers/s390/block/dasd_int.h
drivers/s390/block/dasd_ioctl.c
drivers/s390/block/dasd_proc.c

index b56367b2deb5fd1be2e9fd9582fdb80d444e477e..874571c0ecfa1b0592aed19d23846ecdc89a9483 100644 (file)
@@ -164,7 +164,7 @@ config DASD_FBA
 
 config DASD_DIAG
        tristate "Support for DIAG access to CMS reserved Disks"
-       depends on DASD
+       depends on DASD && ARCH_S390X = 'n'
        help
          Select this option if you want to use CMS reserved Disks under VM
          with the Diagnose250 command.  If you are not running under VM or
index 204383e5b37abf838e602c4834846c920d5b9bfd..8dc53c704e3b8bf6c2b7a158c931ebf59fcd1538 100644 (file)
@@ -7,7 +7,7 @@
  * Bugreports.to..: <Linux390@de.ibm.com>
  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
  *
- * $Revision: 1.74 $
+ * $Revision: 1.82 $
  *
  * History of changes (starts July 2000)
  * 11/09/00 complete redesign after code review
@@ -101,7 +101,7 @@ static wait_queue_head_t dasd_init_waitq;
  * Allocate memory for a new device structure.
  */
 dasd_device_t *
-dasd_alloc_device(dasd_devmap_t *devmap)
+dasd_alloc_device(unsigned int devindex)
 {
        dasd_device_t *device;
        struct gendisk *gdp;
@@ -111,8 +111,6 @@ dasd_alloc_device(dasd_devmap_t *devmap)
                return ERR_PTR(-ENOMEM);
        memset(device, 0, sizeof (dasd_device_t));
 
-       device->devno = devmap->devno;
-
        /* Get two pages for normal block device operations. */
        device->ccw_mem = (void *) __get_free_pages(GFP_ATOMIC | GFP_DMA, 1);
        if (device->ccw_mem == NULL) {
@@ -128,7 +126,7 @@ dasd_alloc_device(dasd_devmap_t *devmap)
        }
 
        /* Allocate gendisk structure for device. */
-       gdp = dasd_gendisk_alloc(devmap->devindex);
+       gdp = dasd_gendisk_alloc(devindex);
        if (IS_ERR(gdp)) {
                free_page((unsigned long) device->erp_mem);
                free_pages((unsigned long) device->ccw_mem, 1);
@@ -147,7 +145,7 @@ dasd_alloc_device(dasd_devmap_t *devmap)
                     (unsigned long) device);
        INIT_LIST_HEAD(&device->ccw_queue);
        init_timer(&device->timer);
-       INIT_WORK(&device->kick_work, do_kick_device, (void *) (addr_t) device->devno);
+       INIT_WORK(&device->kick_work, do_kick_device, device);
        device->state = DASD_STATE_NEW;
        device->target = DASD_STATE_NEW;
 
@@ -174,35 +172,35 @@ dasd_free_device(dasd_device_t *device)
 static inline int
 dasd_state_new_to_known(dasd_device_t *device)
 {
-       dasd_devmap_t *devmap;
        umode_t devfs_perm;
-       devfs_handle_t dir;
-       int major, minor;
+       kdev_t kdev;
        char buf[20];
 
-       /* Increase reference count of bdev. */
-       if (bdget(MKDEV(device->gdp->major, device->gdp->first_minor)) == NULL)
+       kdev = dasd_get_kdev(device);
+       if (kdev_none(kdev))
                return -ENODEV;
 
-       devmap = dasd_devmap_from_devno(device->devno);
-       if (devmap == NULL)
-               return -ENODEV;
-       major = dasd_gendisk_index_major(devmap->devindex);
-       if (major < 0)
-               return -ENODEV;
-       minor = devmap->devindex % DASD_PER_MAJOR;
+       /*
+        * As long as the device is not in state DASD_STATE_NEW we want to 
+        * keep the reference count > 0.
+        */
+       dasd_get_device(device);
 
+#ifdef CONFIG_DEVFS_FS
        /* Add a proc directory and the dasd device entry to devfs. */
-       dir = devfs_mk_dir("dasd/%04x", device->devno);
-       device->gdp->de = dir;
+       device->gdp->de = devfs_mk_dir("dasd/%04x",
+               _ccw_device_get_device_number(device->cdev));
+#endif
        if (device->ro_flag)
                devfs_perm = S_IFBLK | S_IRUSR;
        else
                devfs_perm = S_IFBLK | S_IRUSR | S_IWUSR;
 
-       snprintf(buf, sizeof(buf), "dasd/%04x/device", device->devno);
+       snprintf(buf, sizeof(buf), "dasd/%04x/device",
+                _ccw_device_get_device_number(device->cdev));
        device->devfs_entry = devfs_register(NULL, buf, 0,
-                                            major, minor << DASD_PARTN_BITS,
+                                            major(kdev),
+                                            minor(kdev) << DASD_PARTN_BITS,
                                             devfs_perm,
                                             &dasd_device_operations, NULL);
        device->state = DASD_STATE_KNOWN;
@@ -215,8 +213,6 @@ dasd_state_new_to_known(dasd_device_t *device)
 static inline void
 dasd_state_known_to_new(dasd_device_t * device)
 {
-       struct block_device *bdev;
-
        /* Remove device entry and devfs directory. */
        devfs_unregister(device->devfs_entry);
        devfs_unregister(device->gdp->de);
@@ -225,10 +221,8 @@ dasd_state_known_to_new(dasd_device_t * device)
        device->discipline = NULL;
        device->state = DASD_STATE_NEW;
 
-       /* Decrease reference count of bdev. */
-       bdev = bdget(MKDEV(device->gdp->major, device->gdp->first_minor));
-       bdput(bdev);
-       bdput(bdev);
+       /* Give up reference we took in dasd_state_new_to_known. */
+       dasd_put_device(device);
 }
 
 /*
@@ -428,7 +422,8 @@ dasd_change_state(dasd_device_t *device)
         if (rc && rc != -EAGAIN) {
                if (rc != -ENODEV)
                        MESSAGE (KERN_INFO, "giving up on dasd device with "
-                                "devno %04x", device->devno);
+                                "devno %04x",
+                                _ccw_device_get_device_number(device->cdev));
                 device->target = device->state;
         }
 
@@ -445,26 +440,18 @@ dasd_change_state(dasd_device_t *device)
 static void
 do_kick_device(void *data)
 {
-       int devno;
-       dasd_devmap_t *devmap;
        dasd_device_t *device;
 
-       devno = (long) data;
-       devmap = dasd_devmap_from_devno(devno);
-       device = (devmap != NULL) ?
-               dasd_get_device(devmap) : ERR_PTR(-ENODEV);
-       if (IS_ERR(device))
-               return;
-       atomic_dec(&device->ref_count);
+       device = (dasd_device_t *) data;
        dasd_change_state(device);
        dasd_schedule_bh(device);
-       dasd_put_device(devmap);
+       dasd_put_device(device);
 }
 
 void
 dasd_kick_device(dasd_device_t *device)
 {
-       atomic_inc(&device->ref_count);
+       dasd_get_device(device);
        /* queue call to dasd_kick_device to the kernel event daemon. */
        schedule_work(&device->kick_work);
 }
@@ -646,7 +633,7 @@ dasd_kmalloc_request(char *magic, int cplength, int datasize,
        }
        strncpy((char *) &cqr->magic, magic, 4);
        ASCEBC((char *) &cqr->magic, 4);
-       atomic_inc(&device->ref_count);
+       dasd_get_device(device);
        return cqr;
 }
 
@@ -693,7 +680,7 @@ dasd_smalloc_request(char *magic, int cplength, int datasize,
        }
        strncpy((char *) &cqr->magic, magic, 4);
        ASCEBC((char *) &cqr->magic, 4);
-       atomic_inc(&device->ref_count);
+       dasd_get_device(device);
        return cqr;
 }
 
@@ -723,7 +710,7 @@ dasd_kfree_request(dasd_ccw_req_t * cqr, dasd_device_t * device)
        if (cqr->data != NULL)
                kfree(cqr->data);
        kfree(cqr);
-       atomic_dec(&device->ref_count);
+       dasd_put_device(device);
 }
 
 void
@@ -738,7 +725,7 @@ dasd_sfree_request(dasd_ccw_req_t * cqr, dasd_device_t * device)
        spin_lock_irqsave(&device->mem_lock, flags);
        dasd_free_chunk(&device->ccw_chunks, cqr);
        spin_unlock_irqrestore(&device->mem_lock, flags);
-       atomic_dec(&device->ref_count);
+       dasd_put_device(device);
 }
 
 /*
@@ -936,19 +923,14 @@ do_state_change_pending(void *data)
                struct work_struct work;
                dasd_device_t *device;
        } *p;
-       dasd_devmap_t *devmap;
        dasd_device_t *device;
        dasd_ccw_req_t *cqr;
-       int devno;
 
        p = data;
        device = p->device;
        DBF_EVENT(DBF_NOTICE, "State change Interrupt for bus_id %s",
                  device->cdev->dev.bus_id);
 
-       // FIXME: get rid of devmap.
-       devno = _ccw_device_get_device_number(device->cdev);
-       devmap = dasd_devmap_from_devno(devno);
        spin_lock_irq(get_ccwdev_lock(device->cdev));
        /* re-activate first request in queue */
        if (!list_empty(&device->ccw_queue)) {
@@ -965,10 +947,39 @@ do_state_change_pending(void *data)
        }
        spin_unlock_irq(get_ccwdev_lock(device->cdev));
        dasd_schedule_bh(device);
-       dasd_put_device(devmap);
+       dasd_put_device(device);
        kfree(p);
 }
 
+static void
+dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm)
+{
+       dasd_ccw_req_t *cqr;
+       dasd_device_t *device;
+
+       cqr = (dasd_ccw_req_t *) intparm;
+       if (cqr->status != DASD_CQR_IN_IO) {
+               MESSAGE(KERN_DEBUG,
+                       "invalid status: bus_id %s, status %02x",
+                       cdev->dev.bus_id, cqr->status);
+               return;
+       }
+
+       device = (dasd_device_t *) cqr->device;
+       if (device == NULL ||
+           device != cdev->dev.driver_data ||
+           strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
+               MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s",
+                       cdev->dev.bus_id);
+               return;
+       }
+
+       /* Schedule request to be retried. */
+       cqr->status = DASD_CQR_QUEUED;
+
+       dasd_clear_timer(device);
+       dasd_schedule_bh(device);
+}
 
 static void
 dasd_handle_state_change_pending(dasd_device_t *device)
@@ -984,8 +995,7 @@ dasd_handle_state_change_pending(dasd_device_t *device)
                return;
        INIT_WORK(&p->work, (void *) do_state_change_pending, p);
        p->device = device;
-       atomic_inc(&device->ref_count);
-       /* queue call to do_state_change_pending to the kernel event daemon. */
+       dasd_get_device(device);
        schedule_work(&p->work);
 }
 
@@ -1003,6 +1013,23 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
        dasd_era_t era;
        char mask;
 
+       if (IS_ERR(irb)) {
+               switch (PTR_ERR(irb)) {
+               case -EIO:
+                       dasd_handle_killed_request(cdev, intparm);
+                       break;
+               case -ETIMEDOUT:
+                       printk(KERN_WARNING"%s(%s): request timed out\n",
+                              __FUNCTION__, cdev->dev.bus_id);
+                       //FIXME - dasd uses own timeout interface...
+                       break;
+               default:
+                       printk(KERN_WARNING"%s(%s): unknown error %ld\n",
+                              __FUNCTION__, cdev->dev.bus_id, PTR_ERR(irb));
+               }
+               return;
+       }
+
        now = get_clock();
 
        DBF_EVENT(DBF_DEBUG, "Interrupt: stat %02x, bus_id %s",
@@ -1108,7 +1135,6 @@ dasd_end_request(struct request *req, int uptodate)
                BUG();
        add_disk_randomness(req->rq_disk);
        end_that_request_last(req);
-       return;
 }
 
 /*
@@ -1177,7 +1203,9 @@ dasd_end_request_cb(dasd_ccw_req_t * cqr, void *data)
 
        req = (struct request *) data;
        dasd_profile_end(cqr->device, cqr, req);
+       spin_lock_irq(&cqr->device->request_queue_lock);
        dasd_end_request(req, (cqr->status == DASD_CQR_DONE));
+       spin_unlock_irq(&cqr->device->request_queue_lock);
        dasd_sfree_request(cqr, cqr->device);
 }
 
@@ -1189,7 +1217,6 @@ static inline void
 __dasd_process_blk_queue(dasd_device_t * device)
 {
        request_queue_t *queue;
-       struct list_head *l;
        struct request *req;
        dasd_ccw_req_t *cqr;
        int nr_queued;
@@ -1211,11 +1238,9 @@ __dasd_process_blk_queue(dasd_device_t * device)
                return;
        nr_queued = 0;
        /* Now we try to fetch requests from the request queue */
-       list_for_each(l, &device->ccw_queue) {
-               cqr = list_entry(l, dasd_ccw_req_t, list);
+       list_for_each_entry(cqr, &device->ccw_queue, list)
                if (cqr->status == DASD_CQR_QUEUED)
                        nr_queued++;
-       }
        while (!blk_queue_plugged(queue) &&
               elv_next_request(queue) &&
                nr_queued < DASD_CHANQ_MAX_SIZE) {
@@ -1223,7 +1248,8 @@ __dasd_process_blk_queue(dasd_device_t * device)
                if (device->ro_flag && rq_data_dir(req) == WRITE) {
                        DBF_EVENT(DBF_ERR,
                                  "(%04x) Rejecting write request %p",
-                                 device->devno, req);
+                                 _ccw_device_get_device_number(device->cdev),
+                                 req);
                        blkdev_dequeue_request(req);
                        dasd_end_request(req, 0);
                        continue;
@@ -1234,7 +1260,8 @@ __dasd_process_blk_queue(dasd_device_t * device)
                                break;  /* terminate request queue loop */
                        DBF_EVENT(DBF_ERR,
                                  "(%04x) CCW creation failed on request %p",
-                                 device->devno, req);
+                                 _ccw_device_get_device_number(device->cdev),
+                                 req);
                        blkdev_dequeue_request(req);
                        dasd_end_request(req, 0);
                        continue;
@@ -1371,8 +1398,7 @@ dasd_tasklet(dasd_device_t * device)
        __dasd_start_head(device);
        spin_unlock(get_ccwdev_lock(device->cdev));
        spin_unlock_irq(&device->request_queue_lock);
-       /* FIXME: what if ref_count == 0 && state == DASD_STATE_NEW ?? */
-       atomic_dec(&device->ref_count);
+       dasd_put_device(device);
 }
 
 /*
@@ -1384,7 +1410,7 @@ dasd_schedule_bh(dasd_device_t * device)
        /* Protect against rescheduling. */
        if (atomic_compare_and_swap (0, 1, &device->tasklet_scheduled))
                return;
-       atomic_inc(&device->ref_count);
+       dasd_get_device(device);
        tasklet_hi_schedule(&device->tasklet);
 }
 
@@ -1766,8 +1792,8 @@ dasd_release(struct inode *inp, struct file *filp)
        return 0;
 }
 
-struct
-block_device_operations dasd_device_operations = {
+struct block_device_operations
+dasd_device_operations = {
        .owner          = THIS_MODULE,
        .open           = dasd_open,
        .release        = dasd_release,
@@ -1823,12 +1849,6 @@ dasd_generic_probe (struct ccw_device *cdev, dasd_discipline_t *discipline)
 
        cdev->handler = &dasd_int_handler;
 
-       if (dasd_autodetect ||
-           dasd_devmap_from_devno(devno) != 0) {
-               /* => device was in dasd parameter line */
-               ccw_device_set_online(cdev);
-       }
-
        return ret;
 }
 
@@ -1854,30 +1874,12 @@ dasd_generic_set_online (struct ccw_device *cdev,
                         dasd_discipline_t *discipline)
 
 {
-       int devno;
-       dasd_devmap_t *devmap;
        dasd_device_t *device;
        int rc;
 
-       if (cdev->dev.driver_data != NULL) /* already enabled */
-               return 0;
-
-       devno = _ccw_device_get_device_number(cdev);
-       rc = dasd_add_range(devno, devno, DASD_FEATURE_DEFAULT);
-       if (rc)
-               return rc;
-
-       if (!(devmap = dasd_devmap_from_devno (devno)))
-               return 0; /* device is still disabled -> ignore it */
-
-       if (IS_ERR(device = dasd_get_device(devmap))) {
-               printk (KERN_WARNING "dasd_generic could not get %s\n",
-                               cdev->dev.bus_id);
+       device = dasd_create_device(cdev);
+       if (IS_ERR(device))
                return PTR_ERR(device);
-       }
-
-       device->gdp->driverfs_dev = &cdev->dev;
-       device->cdev = cdev;
 
        if (device->use_diag_flag)
                device->discipline = dasd_diag_discipline_pointer;
@@ -1898,7 +1900,7 @@ dasd_generic_set_online (struct ccw_device *cdev,
        if (rc) {
                printk (KERN_WARNING "dasd_generic found a bad device %s\n", 
                        cdev->dev.bus_id);
-               dasd_put_device(devmap);
+               dasd_delete_device(device);
                return rc;
        }
 
@@ -1909,16 +1911,17 @@ dasd_generic_set_online (struct ccw_device *cdev,
                        cdev->dev.bus_id);
                rc = -ENODEV;
                dasd_set_target_state(device, DASD_STATE_NEW);
+               dasd_delete_device(device);
        } else {
                pr_debug("dasd_generic device %s found\n",
                                cdev->dev.bus_id);
                cdev->dev.driver_data = device;
        }
 
-       dasd_put_device(devmap);
        /* FIXME: we have to wait for the root device but we don't want
         * to wait for each single device but for all at once. */
        wait_event(dasd_init_waitq, _wait_for_device(device));
+
        return rc;
 }
 
@@ -1926,25 +1929,51 @@ int
 dasd_generic_set_offline (struct ccw_device *cdev)
 {
        dasd_device_t *device;
-       dasd_devmap_t *devmap;
-       int devno;
 
-       devno = _ccw_device_get_device_number(cdev);
        device = cdev->dev.driver_data;
-       devmap = dasd_devmap_from_devno(devno);
-       if (device == NULL || devmap == NULL)
-               return -ENODEV;
-
-       device = dasd_get_device(devmap);
-       if (IS_ERR(device))
-               return PTR_ERR(device);
-
+       if (atomic_read(&device->open_count) > 0) {
+               printk (KERN_WARNING "Can't offline dasd device with open"
+                       " count = %i.\n",
+                       atomic_read(&device->open_count));
+               return -EBUSY;
+       }
        dasd_set_target_state(device, DASD_STATE_NEW);
-       dasd_put_device(devmap);
+       dasd_delete_device(device);
        
        return 0;
 }
 
+/*
+ * Automatically online either all dasd devices (dasd_autodetect) or
+ * all devices specified with dasd= parameters. For dasd_autodetect
+ * dasd_generic_probe has added devmaps for all dasd devices. We
+ * scan all present dasd devmaps and call ccw_device_set_online.
+ */
+void
+dasd_generic_auto_online (struct ccw_driver *dasd_discipline_driver)
+{
+       struct device_driver *drv;
+       struct device *d, *dev;
+       struct ccw_device *cdev;
+       int devno;
+
+       drv = get_driver(&dasd_discipline_driver->driver);
+       down_read(&drv->bus->subsys.rwsem);
+       dev = NULL;
+       list_for_each_entry(d, &drv->devices, driver_list) {
+               dev = get_device(d);
+               if (!dev)
+                       continue;
+               cdev = to_ccwdev(dev);
+               devno = _ccw_device_get_device_number(cdev);
+               if (dasd_autodetect || dasd_devno_in_range(devno) == 0)
+                       ccw_device_set_online(cdev);
+               put_device(dev);
+       }
+       up_read(&drv->bus->subsys.rwsem);
+       put_driver(drv);
+}
+
 /*
  * SECTION: files in sysfs
  */
@@ -2078,11 +2107,13 @@ dasd_init(void)
 
        DBF_EVENT(DBF_EMERG, "%s", "debug area created");
 
-       if (devfs_mk_dir("dasd")) {
+#ifdef CONFIG_DEVFS_FS
+       if (!devfs_mk_dir("dasd")) {
                DBF_EVENT(DBF_ALERT, "%s", "no devfs");
                rc = -ENOSYS;
                goto failed;
        }
+#endif
        rc = dasd_devmap_init();
        if (rc)
                goto failed;
index 6ead754e9bbe99bb6ffccc24df822870f329d40a..09a1afacc639eb6628c5608a07f06c2e9dc4005e 100644 (file)
@@ -11,7 +11,7 @@
  * functions may not be called from interrupt context. In particular
  * dasd_get_device is a no-no from interrupt context.
  *
- * $Revision: 1.11 $
+ * $Revision: 1.12 $
  *
  * History of changes 
  * 05/04/02 split from dasd.c, code restructuring.
 
 #include "dasd_int.h"
 
+/*
+ * dasd_devmap_t is used to store the features and the relation
+ * between device number and device index. To find a dasd_devmap_t
+ * that corresponds to a device number of a device index each
+ * dasd_devmap_t is added to two linked lists, one to search by
+ * the device number and one to search by the device index. As
+ * soon as big minor numbers are available the device index list
+ * can be removed since the device number will then be identical
+ * to the device index.
+ */
+typedef struct {
+       struct list_head devindex_list;
+       struct list_head devno_list;
+        unsigned int devindex;
+        unsigned short devno;
+        unsigned short features;
+       dasd_device_t *device;
+} dasd_devmap_t;
+
 /*
  * Parameter parsing functions for dasd= parameter. The syntax is:
  *   <devno>           : (0x)?[0-9a-fA-F]+
@@ -278,6 +297,29 @@ dasd_add_range(int from, int to, int features)
        return 0;
 }
 
+/*
+ * Check if devno has been added to the list of dasd ranges.
+ */
+int
+dasd_devno_in_range(int devno)
+{
+       struct list_head *l;
+       int ret;
+               
+       ret = -ENOENT;
+       spin_lock(&dasd_devmap_lock);
+       /* Find devmap for device with device number devno */
+       list_for_each(l, &dasd_devno_hashlists[devno&255]) {
+               if (list_entry(l, dasd_devmap_t, devno_list)->devno == devno) {
+                       /* Found the device. */
+                       ret = 0;
+                       break;
+               }
+       }
+       spin_unlock(&dasd_devmap_lock);
+       return ret;
+}
+
 /*
  * Forget all about the device numbers added so far.
  * This may only be called at module unload or system shutdown.
@@ -307,7 +349,7 @@ dasd_forget_ranges(void)
  * Find the devmap structure from a devno. Can be removed as soon
  * as big minors are available.
  */
-dasd_devmap_t *
+static dasd_devmap_t *
 dasd_devmap_from_devno(int devno)
 {
        struct list_head *l;
@@ -332,7 +374,7 @@ dasd_devmap_from_devno(int devno)
  * Find the devmap for a device by its device index. Can be removed
  * as soon as big minors are available.
  */
-dasd_devmap_t *
+static dasd_devmap_t *
 dasd_devmap_from_devindex(int devindex)
 {
        struct list_head *l;
@@ -353,60 +395,140 @@ dasd_devmap_from_devindex(int devindex)
        return devmap;
 }
 
-/*
- * Find the device structure for device number devno. If it does not
- * exists yet, allocate it. Increase the reference counter in the device
- * structure and return a pointer to it.
- */
 dasd_device_t *
-dasd_get_device(dasd_devmap_t *devmap)
+dasd_device_from_devindex(int devindex)
 {
+       dasd_devmap_t *devmap;
        dasd_device_t *device;
 
+       devmap = dasd_devmap_from_devindex(devindex);
        spin_lock(&dasd_devmap_lock);
        device = devmap->device;
-       if (device != NULL)
-               atomic_inc(&device->ref_count);
+       if (device)
+               dasd_get_device(device);
+       else
+               device = ERR_PTR(-ENODEV);
        spin_unlock(&dasd_devmap_lock);
-       if (device != NULL)
-               return device;
+       return device;
+}
+
+/*
+ * Return kdev for a dasd device.
+ */
+kdev_t
+dasd_get_kdev(dasd_device_t *device)
+{
+       dasd_devmap_t *devmap;
+       int major, minor;
+       int devno;
+
+       devno = _ccw_device_get_device_number(device->cdev);
+       devmap = dasd_devmap_from_devno(devno);
+       if (devmap == NULL)
+               return NODEV;
+       major = dasd_gendisk_index_major(devmap->devindex);
+       if (major < 0)
+               return NODEV;
+       minor = devmap->devindex % DASD_PER_MAJOR;
+       return mk_kdev(major, minor);
+}
+
+/*
+ * Create a dasd device structure for cdev.
+ */
+dasd_device_t *
+dasd_create_device(struct ccw_device *cdev)
+{
+       dasd_devmap_t *devmap;
+       dasd_device_t *device;
+       int devno;
+       int rc;
+
+       devno = _ccw_device_get_device_number(cdev);
+       rc = dasd_add_range(devno, devno, DASD_FEATURE_DEFAULT);
+       if (rc)
+               return ERR_PTR(rc);
 
-       device = dasd_alloc_device(devmap);
+       if (!(devmap = dasd_devmap_from_devno (devno)))
+               return ERR_PTR(-ENODEV);
+
+       device = dasd_alloc_device(devmap->devindex);
        if (IS_ERR(device))
                return device;
+       atomic_set(&device->ref_count, 1);
+       device->ro_flag = (devmap->features & DASD_FEATURE_READONLY) ? 1 : 0;
+       device->use_diag_flag = 1;
 
-       spin_lock(&dasd_devmap_lock);
-       if (devmap->device != NULL) {
+       spin_lock_irq(get_ccwdev_lock(cdev));
+       if (cdev->dev.driver_data == NULL) {
+               get_device(&cdev->dev);
+               cdev->dev.driver_data = device;
+               device->gdp->driverfs_dev = &cdev->dev;
+               device->cdev = cdev;
+               rc = 0;
+       } else
                /* Someone else was faster. */
+               rc = -EBUSY;
+       spin_unlock_irq(get_ccwdev_lock(cdev));
+       if (rc) {
                dasd_free_device(device);
-               device = devmap->device;
-       } else
-               devmap->device = device;
-       atomic_inc(&device->ref_count);
-       device->ro_flag = (devmap->features & DASD_FEATURE_READONLY) ? 1 : 0;
-       device->use_diag_flag = 1;
+               return ERR_PTR(rc);
+       }
+       /* Device created successfully. Make it known via devmap. */
+       spin_lock(&dasd_devmap_lock);
+       devmap->device = device;
        spin_unlock(&dasd_devmap_lock);
+
        return device;
 }
 
 /*
- * Decrease the reference counter of a devices structure. If the
- * reference counter reaches zero and the device status is
- * DASD_STATE_NEW the device structure is freed. 
+ * Wait queue for dasd_delete_device waits.
+ */
+static DECLARE_WAIT_QUEUE_HEAD(dasd_delete_wq);
+
+/*
+ * Remove a dasd device structure.
  */
 void
-dasd_put_device(dasd_devmap_t *devmap)
+dasd_delete_device(dasd_device_t *device)
 {
-       dasd_device_t *device;
+       struct ccw_device *cdev;
+       dasd_devmap_t *devmap;
+       int devno;
 
+       /* First remove device pointer from devmap. */
+       devno = _ccw_device_get_device_number(device->cdev);
+       devmap = dasd_devmap_from_devno (devno);
        spin_lock(&dasd_devmap_lock);
-       device = devmap->device;
-       if (atomic_dec_return(&device->ref_count) == 0 &&
-           device->state == DASD_STATE_NEW) {
-               devmap->device = NULL;
-               dasd_free_device(device);
-       }
+       devmap->device = NULL;
        spin_unlock(&dasd_devmap_lock);
+
+       /* Wait for reference counter to drop to zero. */
+       atomic_dec(&device->ref_count);
+       wait_event(dasd_delete_wq, atomic_read(&device->ref_count) == 0);
+
+       /* Disconnect dasd_device structure from ccw_device structure. */
+       cdev = device->cdev;
+       device->cdev = NULL;
+       device->gdp->driverfs_dev = NULL;
+       cdev->dev.driver_data = NULL;
+
+       /* Put ccw_device structure. */
+       put_device(&cdev->dev);
+
+       /* Now the device structure can be freed. */
+       dasd_free_device(device);
+}
+
+/*
+ * Reference counter dropped to zero. Wake up waiter
+ * in dasd_delete_device.
+ */
+void
+dasd_put_device_wake(dasd_device_t *device)
+{
+       wake_up(&dasd_delete_wq);
 }
 
 int
index acdca8a9abefd35e77cdc7241ff7513dddf45044..8961708a46d3725efcd6336037d74a898421cc83 100644 (file)
@@ -6,7 +6,7 @@
  * Bugreports.to..: <Linux390@de.ibm.com>
  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
  *
- * $Revision: 1.27 $
+ * $Revision: 1.28 $
  *
  * History of changes
  * 07/13/00 Added fixup sections for diagnoses ans saved some registers
@@ -96,7 +96,7 @@ mdsk_init_io(dasd_device_t * device, int blocksize, int offset, int size)
        iib = &private->iib;
        memset(iib, 0, sizeof (diag_init_io_t));
 
-       iib->dev_nr = device->devno;
+       iib->dev_nr = _ccw_device_get_device_number(device->cdev);
        iib->block_size = blocksize;
        iib->offset = offset;
        iib->start_block = 0;
@@ -117,7 +117,7 @@ mdsk_term_io(dasd_device_t * device)
        private = (dasd_diag_private_t *) device->private;
        iib = &private->iib;
        memset(iib, 0, sizeof (diag_init_io_t));
-       iib->dev_nr = device->devno;
+       iib->dev_nr = _ccw_device_get_device_number(device->cdev);
        rc = dia250(iib, TERM_BIO);
        return rc & 3;
 }
@@ -134,7 +134,7 @@ dasd_start_diag(dasd_ccw_req_t * cqr)
        private = (dasd_diag_private_t *) device->private;
        dreq = (dasd_diag_req_t *) cqr->data;
 
-       private->iob.dev_nr = device->devno;
+       private->iob.dev_nr = _ccw_device_get_device_number(device->cdev);
        private->iob.key = 0;
        private->iob.flags = 2; /* do asynchronous io */
        private->iob.block_count = dreq->block_count;
@@ -252,7 +252,7 @@ dasd_diag_check_device(dasd_device_t *device)
        }
        /* Read Device Characteristics */
        rdc_data = (void *) &(private->rdc_data);
-       rdc_data->dev_nr = device->devno;
+       rdc_data->dev_nr = _ccw_device_get_device_number(device->cdev);
        rdc_data->rdc_len = sizeof (dasd_diag_characteristics_t);
 
        rc = diag210((struct diag210 *) rdc_data);
index f841ed449de793b7c3251302bdd242d7fa3224b1..80b9eeae7ba5c08a645309af3856daeaffd69a90 100644 (file)
@@ -7,7 +7,7 @@
  * Bugreports.to..: <Linux390@de.ibm.com>
  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
  *
- * $Revision: 1.36 $
+ * $Revision: 1.38 $
  *
  * History of changes (starts July 2000)
  * 07/11/00 Enabled rotational position sensing
@@ -1003,7 +1003,7 @@ dasd_eckd_build_cp(dasd_device_t * device, struct request *req)
                                return ERR_PTR(-EINVAL);
                        count += bv->bv_len >> (device->s2b_shift + 9);
 #if defined(CONFIG_ARCH_S390X)
-                       cidaw += idal_nr_words(kmap(bv->bv_page) +
+                       cidaw += idal_nr_words(page_address(bv->bv_page) +
                                               bv->bv_offset, bv->bv_len);
 #endif
                }
@@ -1042,7 +1042,7 @@ dasd_eckd_build_cp(dasd_device_t * device, struct request *req)
                              last_rec - recid + 1, cmd, device, blksize);
        }
        rq_for_each_bio(bio, req) bio_for_each_segment(bv, bio, i) {
-               dst = kmap(bv->bv_page) + bv->bv_offset;
+               dst = page_address(bv->bv_page) + bv->bv_offset;
                for (off = 0; off < bv->bv_len; off += blksize) {
                        sector_t trkid = recid;
                        unsigned int recoffs = sector_div(trkid, blk_per_trk);
@@ -1453,6 +1453,8 @@ static dasd_discipline_t dasd_eckd_discipline = {
 static int __init
 dasd_eckd_init(void)
 {
+       int ret;
+
        dasd_ioctl_no_register(THIS_MODULE, BIODASDSATTR,
                               dasd_eckd_set_attrib);
        dasd_ioctl_no_register(THIS_MODULE, BIODASDPSRD,
@@ -1466,7 +1468,22 @@ dasd_eckd_init(void)
 
        ASCEBC(dasd_eckd_discipline.ebcname, 4);
 
-       ccw_driver_register(&dasd_eckd_driver);
+       ret = ccw_driver_register(&dasd_eckd_driver);
+       if (ret) {
+               dasd_ioctl_no_unregister(THIS_MODULE, BIODASDSATTR,
+                                        dasd_eckd_set_attrib);
+               dasd_ioctl_no_unregister(THIS_MODULE, BIODASDPSRD,
+                                        dasd_eckd_performance);
+               dasd_ioctl_no_unregister(THIS_MODULE, BIODASDRLSE,
+                                        dasd_eckd_release);
+               dasd_ioctl_no_unregister(THIS_MODULE, BIODASDRSRV,
+                                        dasd_eckd_reserve);
+               dasd_ioctl_no_unregister(THIS_MODULE, BIODASDSLCK,
+                                        dasd_eckd_steal_lock);
+               return ret;
+       }
+
+       dasd_generic_auto_online(&dasd_eckd_driver);
        return 0;
 }
 
index 7756963481d0d9cc2068e6c623d8868e5d3701f5..8ca451f7bcad7359c8bd9a0bffad488929c32e08 100644 (file)
@@ -4,7 +4,7 @@
  * Bugreports.to..: <Linux390@de.ibm.com>
  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
  *
- * $Revision: 1.25 $
+ * $Revision: 1.27 $
  *
  * History of changes
  *         fixed partition handling and HDIO_GETGEO
@@ -414,9 +414,16 @@ static dasd_discipline_t dasd_fba_discipline = {
 static int __init
 dasd_fba_init(void)
 {
+       int ret;
+
        ASCEBC(dasd_fba_discipline.ebcname, 4);
 
-       return ccw_driver_register(&dasd_fba_driver);
+       ret = ccw_driver_register(&dasd_fba_driver);
+       if (ret)
+               return ret;
+
+       dasd_generic_auto_online(&dasd_fba_driver);
+       return 0;
 }
 
 static void __exit
index 441ae4bd369d168689bba06e8d516759fceb6138..b2f9bb8d2fcbcd3fe7e7d938e1160e79e764b98e 100644 (file)
@@ -9,7 +9,7 @@
  *
  * Dealing with devices registered to multiple major numbers.
  *
- * $Revision: 1.23 $
+ * $Revision: 1.24 $
  *
  * History of changes
  * 05/04/02 split from dasd.c, code restructuring.
@@ -108,7 +108,6 @@ dasd_unregister_major(struct major_info * mi)
 struct gendisk *
 dasd_gendisk_alloc(int devindex)
 {
-       struct list_head *l;
        struct major_info *mi;
        struct gendisk *gdp;
        int index, len, rc;
@@ -118,8 +117,7 @@ dasd_gendisk_alloc(int devindex)
        while (1) {
                spin_lock(&dasd_major_lock);
                index = devindex;
-               list_for_each(l, &dasd_major_info) {
-                       mi = list_entry(l, struct major_info, list);
+               list_for_each_entry(mi, &dasd_major_info, list) {
                        if (index < DASD_PER_MAJOR)
                                break;
                        index -= DASD_PER_MAJOR;
@@ -142,6 +140,7 @@ dasd_gendisk_alloc(int devindex)
        gdp->major = mi->major;
        gdp->first_minor = index << DASD_PARTN_BITS;
        gdp->fops = &dasd_device_operations;
+       gdp->flags |= GENHD_FL_DEVFS;
 
        /*
         * Set device name.
@@ -191,14 +190,12 @@ static int dasd_gendisk_major_index(int major)
  */
 int dasd_gendisk_index_major(int devindex)
 {
-       struct list_head *l;
        struct major_info *mi;
        int rc;
 
        spin_lock(&dasd_major_lock);
        rc = -ENODEV;
-       list_for_each(l, &dasd_major_info) {
-               mi = list_entry(l, struct major_info, list);
+       list_for_each_entry(mi, &dasd_major_info, list) {
                if (devindex < DASD_PER_MAJOR) {
                        rc = mi->major;
                        break;
@@ -231,6 +228,7 @@ void
 dasd_destroy_partitions(dasd_device_t * device)
 {
        del_gendisk(device->gdp);
+       put_disk(device->gdp);
 }
 
 int
index 32189d47046d80ad0e2df60fdf9da13d35a33ab0..342c32d501a5d0e4f699ff4ad45dae3b2dc7558f 100644 (file)
@@ -6,7 +6,7 @@
  * Bugreports.to..: <Linux390@de.ibm.com>
  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
  *
- * $Revision: 1.36 $
+ * $Revision: 1.38 $
  *
  * History of changes (starts July 2000)
  * 02/01/01 added dynamic registration of ioctls
@@ -297,10 +297,6 @@ typedef struct dasd_device_t {
        struct list_head ccw_chunks;
        struct list_head erp_chunks;
 
-       /* Common i/o stuff. */
-       /* FIXME: remove the next */
-       int devno;
-
        atomic_t tasklet_scheduled;
         struct tasklet_struct tasklet;
        struct work_struct kick_work;
@@ -315,24 +311,23 @@ typedef struct dasd_device_t {
 #endif
 } dasd_device_t;
 
+void dasd_put_device_wake(dasd_device_t *);
+
 /*
- * dasd_devmap_t is used to store the features and the relation
- * between device number and device index. To find a dasd_devmap_t
- * that corresponds to a device number of a device index each
- * dasd_devmap_t is added to two linked lists, one to search by
- * the device number and one to search by the device index. As
- * soon as big minor numbers are available the device index list
- * can be removed since the device number will then be identical
- * to the device index.
+ * Reference count inliners
  */
-typedef struct {
-       struct list_head devindex_list;
-       struct list_head devno_list;
-        unsigned int devindex;
-        unsigned short devno;
-        unsigned short features;
-        dasd_device_t *device;
-} dasd_devmap_t;
+static inline void
+dasd_get_device(dasd_device_t *device)
+{
+       atomic_inc(&device->ref_count);
+}
+
+static inline void
+dasd_put_device(dasd_device_t *device)
+{
+       if (atomic_dec_return(&device->ref_count) == 0)
+               dasd_put_device_wake(device);
+}
 
 /*
  * The static memory in ccw_mem and erp_mem is managed by a sorted
@@ -444,8 +439,9 @@ dasd_kmalloc_set_cda(struct ccw1 *ccw, void *cda, dasd_device_t *device)
        return set_normalized_cda(ccw, cda);
 }
 
-dasd_device_t *dasd_alloc_device(dasd_devmap_t *);
+dasd_device_t *dasd_alloc_device(unsigned int devindex);
 void dasd_free_device(dasd_device_t *);
+
 void dasd_enable_device(dasd_device_t *);
 void dasd_set_target_state(dasd_device_t *, int);
 void dasd_kick_device(dasd_device_t *);
@@ -467,6 +463,7 @@ int dasd_generic_remove (struct ccw_device *cdev);
 int dasd_generic_set_online(struct ccw_device *cdev, 
                            dasd_discipline_t *discipline);
 int dasd_generic_set_offline (struct ccw_device *cdev);
+void dasd_generic_auto_online (struct ccw_driver *);
 
 /* externals in dasd_devmap.c */
 extern int dasd_max_devindex;
@@ -475,13 +472,16 @@ extern int dasd_autodetect;
 
 int dasd_devmap_init(void);
 void dasd_devmap_exit(void);
-dasd_devmap_t *dasd_devmap_from_devno(int);
-dasd_devmap_t *dasd_devmap_from_devindex(int);
-dasd_device_t *dasd_get_device(dasd_devmap_t *);
-void dasd_put_device(dasd_devmap_t *);
+
+dasd_device_t *dasd_create_device(struct ccw_device *);
+void dasd_delete_device(dasd_device_t *);
+
+kdev_t dasd_get_kdev(dasd_device_t *);
+dasd_device_t *dasd_device_from_devindex(int);
 
 int dasd_parse(void);
 int dasd_add_range(int, int, int);
+int dasd_devno_in_range(int);
 
 /* externals in dasd_gendisk.c */
 int  dasd_gendisk_init(void);
index 3b84084989fc5d9f7de5e9d3b22f65ef24e9f0cc..bb4912082c5c583e5beeabb92d9d70651d0afb9b 100644 (file)
@@ -12,7 +12,6 @@
  * 05/04/02 split from dasd.c, code restructuring.
  */
 #include <linux/config.h>
-#include <linux/version.h>
 #include <linux/interrupt.h>
 #include <linux/major.h>
 #include <linux/fs.h>
@@ -333,7 +332,7 @@ dasd_ioctl_information(struct block_device *bdev, int no, long args)
 
        cdev = device->cdev;
 
-       dasd_info->devno = device->devno;
+       dasd_info->devno = _ccw_device_get_device_number(device->cdev);
        dasd_info->schid = _ccw_device_get_subchannel_number(device->cdev);
        dasd_info->cu_type = cdev->id.cu_type;
        dasd_info->cu_model = cdev->id.cu_model;
index c9df1587373c96aba57fe133842320edbd6a15c0..51c205740b4b21186937b26d6beeeb39580b49c3 100644 (file)
@@ -9,7 +9,7 @@
  *
  * /proc interface for the dasd driver.
  *
- * $Revision: 1.17 $
+ * $Revision: 1.18 $
  *
  * History of changes
  * 05/04/02 split from dasd.c, code restructuring.
@@ -56,17 +56,14 @@ dasd_get_user_string(const char *user_buf, size_t user_len)
 static int
 dasd_devices_show(struct seq_file *m, void *v)
 {
-       dasd_devmap_t *devmap;
        dasd_device_t *device;
        char *substr;
 
-       devmap = dasd_devmap_from_devindex((unsigned long) v - 1);
-       device = (devmap != NULL) ?
-               dasd_get_device(devmap) : ERR_PTR(-ENODEV);
+       device = dasd_device_from_devindex((unsigned long) v - 1);
        if (IS_ERR(device))
                return 0;
        /* Print device number. */
-       seq_printf(m, "%04x", devmap->devno);
+       seq_printf(m, "%04x", _ccw_device_get_device_number(device->cdev));
        /* Print discipline string. */
        if (device != NULL && device->discipline != NULL)
                seq_printf(m, "(%s)", device->discipline->name);
@@ -113,7 +110,7 @@ dasd_devices_show(struct seq_file *m, void *v)
                seq_printf(m, "no stat");
                break;
        }
-       dasd_put_device(devmap);
+       dasd_put_device(device);
        if (dasd_probeonly)
                seq_printf(m, "(probeonly)");
        seq_printf(m, "\n");