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
......
......@@ -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;
......
......@@ -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 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
......
......@@ -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