[PATCH] s390 (7/16): dasd driver fixes.

From: Martin Schwidefsky (schwidefsky@de.ibm.com)
Date: Mon Apr 14 2003 - 12:50:13 EST


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.

diffstat:
 Kconfig | 2
 block/dasd.c | 241 +++++++++++++++++++++++++++++-----------------------
 block/dasd_devmap.c | 188 +++++++++++++++++++++++++++++++++-------
 block/dasd_diag.c | 10 +-
 block/dasd_eckd.c | 25 ++++-
 block/dasd_fba.c | 11 +-
 block/dasd_genhd.c | 12 +-
 block/dasd_int.h | 52 +++++------
 block/dasd_ioctl.c | 3
 block/dasd_proc.c | 11 --
 10 files changed, 363 insertions(+), 192 deletions(-)

diff -urN linux-2.5.67/drivers/s390/Kconfig linux-2.5.67-s390/drivers/s390/Kconfig
--- linux-2.5.67/drivers/s390/Kconfig Mon Apr 7 19:32:28 2003
+++ linux-2.5.67-s390/drivers/s390/Kconfig Mon Apr 14 19:11:52 2003
@@ -164,7 +164,7 @@
 
 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
diff -urN linux-2.5.67/drivers/s390/block/dasd.c linux-2.5.67-s390/drivers/s390/block/dasd.c
--- linux-2.5.67/drivers/s390/block/dasd.c Mon Apr 7 19:31:14 2003
+++ linux-2.5.67-s390/drivers/s390/block/dasd.c Mon Apr 14 19:11:52 2003
@@ -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 @@
  * 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 @@
                 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 @@
         }
 
         /* 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 @@
                      (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 @@
 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 @@
 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 @@
         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 @@
         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 @@
 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 @@
         }
         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 @@
         }
         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 @@
         if (cqr->data != NULL)
                 kfree(cqr->data);
         kfree(cqr);
- atomic_dec(&device->ref_count);
+ dasd_put_device(device);
 }
 
 void
@@ -738,7 +725,7 @@
         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 @@
                 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 @@
         }
         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 @@
                 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_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 @@
                 BUG();
         add_disk_randomness(req->rq_disk);
         end_that_request_last(req);
- return;
 }
 
 /*
@@ -1177,7 +1203,9 @@
 
         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 @@
 __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 @@
                 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 @@
                 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 @@
                                 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_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 @@
         /* 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 @@
         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 @@
 
         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_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 @@
         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 @@
                         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,26 +1929,52 @@
 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 @@
 
         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;
diff -urN linux-2.5.67/drivers/s390/block/dasd_devmap.c linux-2.5.67-s390/drivers/s390/block/dasd_devmap.c
--- linux-2.5.67/drivers/s390/block/dasd_devmap.c Mon Apr 7 19:31:23 2003
+++ linux-2.5.67-s390/drivers/s390/block/dasd_devmap.c Mon Apr 14 19:11:52 2003
@@ -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.
@@ -31,6 +31,25 @@
 #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]+
  * <feature> : ro
@@ -279,6 +298,29 @@
 }
 
 /*
+ * 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 @@
  * 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 @@
  * 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 @@
         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;
+}
 
- device = dasd_alloc_device(devmap);
+/*
+ * 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);
+
+ 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
diff -urN linux-2.5.67/drivers/s390/block/dasd_diag.c linux-2.5.67-s390/drivers/s390/block/dasd_diag.c
--- linux-2.5.67/drivers/s390/block/dasd_diag.c Mon Apr 7 19:30:35 2003
+++ linux-2.5.67-s390/drivers/s390/block/dasd_diag.c Mon Apr 14 19:11:52 2003
@@ -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 @@
         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 @@
         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 @@
         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 @@
         }
         /* 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);
diff -urN linux-2.5.67/drivers/s390/block/dasd_eckd.c linux-2.5.67-s390/drivers/s390/block/dasd_eckd.c
--- linux-2.5.67/drivers/s390/block/dasd_eckd.c Mon Apr 7 19:30:34 2003
+++ linux-2.5.67-s390/drivers/s390/block/dasd_eckd.c Mon Apr 14 19:11:52 2003
@@ -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 @@
                                 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 @@
                               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 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 @@
 
         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;
 }
 
diff -urN linux-2.5.67/drivers/s390/block/dasd_fba.c linux-2.5.67-s390/drivers/s390/block/dasd_fba.c
--- linux-2.5.67/drivers/s390/block/dasd_fba.c Mon Apr 7 19:31:56 2003
+++ linux-2.5.67-s390/drivers/s390/block/dasd_fba.c Mon Apr 14 19:11:52 2003
@@ -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 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
diff -urN linux-2.5.67/drivers/s390/block/dasd_genhd.c linux-2.5.67-s390/drivers/s390/block/dasd_genhd.c
--- linux-2.5.67/drivers/s390/block/dasd_genhd.c Mon Apr 7 19:32:18 2003
+++ linux-2.5.67-s390/drivers/s390/block/dasd_genhd.c Mon Apr 14 19:11:52 2003
@@ -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 @@
 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 @@
         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 @@
         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 @@
  */
 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 @@
 dasd_destroy_partitions(dasd_device_t * device)
 {
         del_gendisk(device->gdp);
+ put_disk(device->gdp);
 }
 
 int
diff -urN linux-2.5.67/drivers/s390/block/dasd_int.h linux-2.5.67-s390/drivers/s390/block/dasd_int.h
--- linux-2.5.67/drivers/s390/block/dasd_int.h Mon Apr 7 19:32:16 2003
+++ linux-2.5.67-s390/drivers/s390/block/dasd_int.h Mon Apr 14 19:11:52 2003
@@ -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 @@
         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 @@
 #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 @@
         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_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 @@
 
 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);
diff -urN linux-2.5.67/drivers/s390/block/dasd_ioctl.c linux-2.5.67-s390/drivers/s390/block/dasd_ioctl.c
--- linux-2.5.67/drivers/s390/block/dasd_ioctl.c Mon Apr 7 19:31:46 2003
+++ linux-2.5.67-s390/drivers/s390/block/dasd_ioctl.c Mon Apr 14 19:11:52 2003
@@ -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 @@
 
         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;
diff -urN linux-2.5.67/drivers/s390/block/dasd_proc.c linux-2.5.67-s390/drivers/s390/block/dasd_proc.c
--- linux-2.5.67/drivers/s390/block/dasd_proc.c Mon Apr 7 19:30:45 2003
+++ linux-2.5.67-s390/drivers/s390/block/dasd_proc.c Mon Apr 14 19:11:52 2003
@@ -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 @@
 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 @@
                 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");

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Tue Apr 15 2003 - 22:00:31 EST