Commit 616dea43 authored by Martin Schwidefsky's avatar Martin Schwidefsky Committed by Linus Torvalds

[PATCH] s390: dasd driver fixes.

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.
parent db165cde
......@@ -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
......
This diff is collapsed.
......@@ -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.
......@@ -30,6 +30,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]+
......@@ -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 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);
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
return ERR_PTR(rc);
}
/* Device created successfully. Make it known via devmap. */
spin_lock(&dasd_devmap_lock);
devmap->device = device;
atomic_inc(&device->ref_count);
device->ro_flag = (devmap->features & DASD_FEATURE_READONLY) ? 1 : 0;
device->use_diag_flag = 1;
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);
}
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
......
......@@ -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);
......
......@@ -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;
}
......
......@@ -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
......
......@@ -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
......
......@@ -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);
......
......@@ -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;
......
......@@ -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");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment