Commit 21df060f authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] s390: dasd driver

From: Martin Schwidefsky <schwidefsky@de.ibm.com>

 - Fix interrupt status examination.
 - Make dasd device attributes dependent on the devmap structure instead of
   the device structure to make them persistent and to be able to modify
   them in the offline state.
 - Allow changing the readonly attribute while dasd is online.
 - Add (diag) option to dasd= paramter.
 - Add missing spin_lock_init call.
 - Increase ref_count in dasd_device_from_cdev and add matching
   dasd_put_device pairs.
 - Adapt to notify api change in cio.
 - Fix bug in 3990 error recovery for cable pulls on ESS.
 - Replace kmap by page_address (no highmem on s/390).
 - Set correct default cache mode on ESS for eckd devices.
 - Change dasd names from "dasdx" to "dasd_<busid>_".
parent b2b9268a
......@@ -7,7 +7,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
*
* $Revision: 1.114 $
* $Revision: 1.123 $
*/
#include <linux/config.h>
......@@ -43,7 +43,6 @@ MODULE_DESCRIPTION("Linux on S/390 DASD device driver,"
" Copyright 2000 IBM Corporation");
MODULE_SUPPORTED_DEVICE("dasd");
MODULE_PARM(dasd, "1-" __MODULE_STRING(256) "s");
MODULE_PARM(dasd_disciplines, "1-" __MODULE_STRING(8) "s");
MODULE_LICENSE("GPL");
/*
......@@ -57,7 +56,6 @@ static void dasd_int_handler(struct ccw_device *, unsigned long, struct irb *);
static void dasd_flush_ccw_queue(struct dasd_device *, int);
static void dasd_tasklet(struct dasd_device *);
static void do_kick_device(void *data);
static int dasd_add_sysfs_files(struct ccw_device *cdev);
/*
* SECTION: Operations on the device structure.
......@@ -93,6 +91,7 @@ dasd_alloc_device(void)
dasd_init_chunklist(&device->ccw_chunks, device->ccw_mem, PAGE_SIZE*2);
dasd_init_chunklist(&device->erp_chunks, device->erp_mem, PAGE_SIZE);
spin_lock_init(&device->mem_lock);
spin_lock_init(&device->request_queue_lock);
atomic_set (&device->tasklet_scheduled, 0);
tasklet_init(&device->tasklet,
......@@ -860,7 +859,7 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm)
device = (struct dasd_device *) cqr->device;
if (device == NULL ||
device != cdev->dev.driver_data ||
device != dasd_device_from_cdev(cdev) ||
strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s",
cdev->dev.bus_id);
......@@ -872,6 +871,7 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm)
dasd_clear_timer(device);
dasd_schedule_bh(device);
dasd_put_device(device);
}
static void
......@@ -931,7 +931,11 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
/* first of all check for state change pending interrupt */
mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
if ((irb->scsw.dstat & mask) == mask) {
dasd_handle_state_change_pending(cdev->dev.driver_data);
device = dasd_device_from_cdev(cdev);
if (!IS_ERR(device)) {
dasd_handle_state_change_pending(device);
dasd_put_device(device);
}
return;
}
......@@ -949,7 +953,6 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
device = (struct dasd_device *) 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);
......@@ -960,16 +963,18 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
((irb->scsw.cstat << 8) | irb->scsw.dstat));
/* Find out the appropriate era_action. */
era = dasd_era_none;
if (irb->scsw.dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END) ||
irb->esw.esw0.erw.cons) {
/* The request did end abnormally. */
if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC)
era = dasd_era_fatal;
else
else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) &&
irb->scsw.cstat == 0 &&
!irb->esw.esw0.erw.cons)
era = dasd_era_none;
else if (irb->esw.esw0.erw.cons)
era = device->discipline->examine_error(cqr, irb);
DBF_EVENT(DBF_NOTICE, "era_code %d", era);
}
else
era = dasd_era_recover;
DBF_DEV_EVENT(DBF_DEBUG, device, "era_code %d", era);
expires = 0;
if (era == dasd_era_none) {
cqr->status = DASD_CQR_DONE;
......@@ -1149,6 +1154,11 @@ __dasd_process_blk_queue(struct dasd_device * device)
dasd_end_request(req, 0);
continue;
}
if (device->stopped & DASD_STOPPED_DC_EIO) {
blkdev_dequeue_request(req);
dasd_end_request(req, 0);
continue;
}
cqr = device->discipline->build_cp(device, req);
if (IS_ERR(cqr)) {
if (PTR_ERR(cqr) == -ENOMEM)
......@@ -1728,17 +1738,10 @@ int
dasd_generic_probe (struct ccw_device *cdev,
struct dasd_discipline *discipline)
{
int ret = 0;
if (dasd_autodetect &&
(ret = dasd_add_busid(cdev->dev.bus_id, DASD_FEATURE_DEFAULT))) {
printk (KERN_WARNING
"dasd_generic_probe: cannot autodetect %s\n",
cdev->dev.bus_id);
return ret;
}
int ret;
if (!ret && (ret = dasd_add_sysfs_files(cdev))) {
ret = dasd_add_sysfs_files(cdev);
if (ret) {
printk(KERN_WARNING
"dasd_generic_probe: could not add driverfs entries"
"for %s\n", cdev->dev.bus_id);
......@@ -1751,16 +1754,17 @@ dasd_generic_probe (struct ccw_device *cdev,
/* this will one day be called from a global not_oper handler.
* It is also used by driver_unregister during module unload */
int
void
dasd_generic_remove (struct ccw_device *cdev)
{
struct dasd_device *device;
device = cdev->dev.driver_data;
cdev->dev.driver_data = NULL;
if (device)
kfree(device);
return 0;
device = dasd_device_from_cdev(cdev);
if (!IS_ERR(device)) {
dasd_set_target_state(device, DASD_STATE_NEW);
/* dasd_delete_device destroys the device reference. */
dasd_delete_device(device);
}
}
/* activate a device. This is called from dasd_{eckd,fba}_probe() when either
......@@ -1779,24 +1783,14 @@ dasd_generic_set_online (struct ccw_device *cdev,
return PTR_ERR(device);
if (device->use_diag_flag)
device->discipline = dasd_diag_discipline_pointer;
rc = 0;
if (!device->discipline ||
(rc = device->discipline->check_device(device))) {
pr_debug("device %s is not diag (%d)\n",
cdev->dev.bus_id, rc);
if (device->private != NULL) {
kfree(device->private);
device->private = NULL;
}
discipline = dasd_diag_discipline_pointer;
device->discipline = discipline;
rc = discipline->check_device(device);
}
if (rc) {
printk (KERN_WARNING "dasd_generic found a bad device %s\n",
cdev->dev.bus_id);
printk (KERN_WARNING
"dasd_generic couldn't online device %s "
"with discipline %s\n",
cdev->dev.bus_id, discipline->name);
dasd_delete_device(device);
return rc;
}
......@@ -1809,16 +1803,16 @@ dasd_generic_set_online (struct ccw_device *cdev,
rc = -ENODEV;
dasd_set_target_state(device, DASD_STATE_NEW);
dasd_delete_device(device);
} else {
} else
pr_debug("dasd_generic device %s found\n",
cdev->dev.bus_id);
cdev->dev.driver_data = device;
}
/* 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));
dasd_put_device(device);
return rc;
}
......@@ -1827,18 +1821,68 @@ dasd_generic_set_offline (struct ccw_device *cdev)
{
struct dasd_device *device;
device = cdev->dev.driver_data;
dasd_set_target_state(device, DASD_STATE_NEW);
dasd_delete_device(device);
device = dasd_device_from_cdev(cdev);
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));
dasd_put_device(device);
return -EBUSY;
}
dasd_put_device(device);
dasd_generic_remove (cdev);
return 0;
}
int
dasd_generic_notify(struct ccw_device *cdev, int event)
{
struct dasd_device *device;
struct dasd_ccw_req *cqr;
unsigned long flags;
int ret;
device = dasd_device_from_cdev(cdev);
if (IS_ERR(device))
return 0;
spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
ret = 0;
switch (event) {
case CIO_GONE:
case CIO_NO_PATH:
if (device->state < DASD_STATE_BASIC)
break;
/* Device is active. We want to keep it. */
if (device->disconnect_error_flag) {
list_for_each_entry(cqr, &device->ccw_queue, list)
if (cqr->status == DASD_CQR_IN_IO)
cqr->status = DASD_CQR_FAILED;
device->stopped |= DASD_STOPPED_DC_EIO;
dasd_schedule_bh(device);
} else {
list_for_each_entry(cqr, &device->ccw_queue, list)
if (cqr->status == DASD_CQR_IN_IO)
cqr->status = DASD_CQR_QUEUED;
device->stopped |= DASD_STOPPED_DC_WAIT;
dasd_set_timer(device, 0);
}
ret = 1;
break;
case CIO_OPER:
/* FIXME: add a sanity check. */
device->stopped &= ~(DASD_STOPPED_DC_WAIT|DASD_STOPPED_DC_EIO);
dasd_schedule_bh(device);
ret = 1;
break;
}
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
dasd_put_device(device);
return ret;
}
/*
* 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.
* all devices specified with dasd= parameters.
*/
void
dasd_generic_auto_online (struct ccw_driver *dasd_discipline_driver)
......@@ -1863,97 +1907,6 @@ dasd_generic_auto_online (struct ccw_driver *dasd_discipline_driver)
put_driver(drv);
}
/*
* SECTION: files in sysfs
*/
/*
* readonly controls the readonly status of a dasd
*/
static ssize_t
dasd_ro_show(struct device *dev, char *buf)
{
struct dasd_device *device;
device = dev->driver_data;
if (!device)
return snprintf(buf, PAGE_SIZE, "n/a\n");
return snprintf(buf, PAGE_SIZE, device->ro_flag ? "1\n" : "0\n");
}
static ssize_t
dasd_ro_store(struct device *dev, const char *buf, size_t count)
{
struct dasd_device *device = dev->driver_data;
if (device)
device->ro_flag = (buf[0] == '1') ? 1 : 0;
return count;
}
static DEVICE_ATTR(readonly, 0644, dasd_ro_show, dasd_ro_store);
/*
* use_diag controls whether the driver should use diag rather than ssch
* to talk to the device
*/
/* TODO: Implement */
static ssize_t
dasd_use_diag_show(struct device *dev, char *buf)
{
struct dasd_device *device;
device = dev->driver_data;
if (!device)
return sprintf(buf, "n/a\n");
return sprintf(buf, device->use_diag_flag ? "1\n" : "0\n");
}
static ssize_t
dasd_use_diag_store(struct device *dev, const char *buf, size_t count)
{
struct dasd_device *device = dev->driver_data;
if (device)
device->use_diag_flag = (buf[0] == '1') ? 1 : 0;
return count;
}
static
DEVICE_ATTR(use_diag, 0644, dasd_use_diag_show, dasd_use_diag_store);
static ssize_t
dasd_discipline_show(struct device *dev, char *buf)
{
struct dasd_device *device;
device = dev->driver_data;
if (!device || !device->discipline)
return sprintf(buf, "none\n");
return snprintf(buf, PAGE_SIZE, "%s\n", device->discipline->name);
}
static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL);
static struct attribute * dasd_attrs[] = {
&dev_attr_readonly.attr,
&dev_attr_discipline.attr,
&dev_attr_use_diag.attr,
NULL,
};
static struct attribute_group dasd_attr_group = {
.attrs = dasd_attrs,
};
static int
dasd_add_sysfs_files(struct ccw_device *cdev)
{
return sysfs_create_group(&cdev->dev.kobj, &dasd_attr_group);
}
static int __init
dasd_init(void)
{
......
......@@ -5,7 +5,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000, 2001
*
* $Revision: 1.25 $
* $Revision: 1.26 $
*/
#include <linux/timer.h>
......@@ -312,6 +312,7 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp)
erp->lpm, erp->dstat->esw.esw0.sublog.lpum, opm);
/* reset status to queued to handle the request again... */
if (erp->status > DASD_CQR_QUEUED)
erp->status = DASD_CQR_QUEUED;
erp->retries = 1;
} else {
......@@ -321,6 +322,7 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp)
erp->dstat->esw.esw0.sublog.lpum, opm);
/* post request with permanent error */
if (erp->status > DASD_CQR_QUEUED)
erp->status = DASD_CQR_FAILED;
}
} /* end dasd_3990_erp_alternate_path */
......
......@@ -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.19 $
* $Revision: 1.25 $
*/
#include <linux/config.h>
......@@ -79,6 +79,8 @@ static spinlock_t dasd_devmap_lock = SPIN_LOCK_UNLOCKED;
static struct list_head dasd_hashlists[256];
int dasd_max_devindex;
static struct dasd_devmap *dasd_add_busid(char *, int);
static inline int
dasd_hash_busid(char *bus_id)
{
......@@ -168,12 +170,16 @@ dasd_feature_list(char *str, char **endp)
*endp = str;
return DASD_FEATURE_DEFAULT;
}
str++;
features = 0;
while (1) {
for (len = 0;
str[len] && str[len] != ':' && str[len] != ')'; len++);
if (len == 2 && !strncmp(str, "ro", 2))
features |= DASD_FEATURE_READONLY;
else if (len == 4 && !strncmp(str, "diag", 4))
features |= DASD_FEATURE_USEDIAG;
else {
MESSAGE(KERN_WARNING,
"unsupported feature: %*s, "
......@@ -203,6 +209,7 @@ dasd_feature_list(char *str, char **endp)
static inline int
dasd_ranges_list(char *str)
{
struct dasd_devmap *devmap;
int from, from_id0, from_id1;
int to, to_id0, to_id1;
int features, rc;
......@@ -233,9 +240,9 @@ dasd_ranges_list(char *str)
while (from <= to) {
sprintf(bus_id, "%01x.%01x.%04x",
from_id0, from_id1, from++);
rc = dasd_add_busid(bus_id, features);
if (rc)
return rc;
devmap = dasd_add_busid(bus_id, features);
if (IS_ERR(devmap))
return PTR_ERR(devmap);
}
if (*str != ',')
break;
......@@ -299,7 +306,7 @@ dasd_parse(void)
* added through this function will define the kdevs for the individual
* devices.
*/
int
static struct dasd_devmap *
dasd_add_busid(char *bus_id, int features)
{
struct dasd_devmap *devmap, *new, *tmp;
......@@ -308,7 +315,7 @@ dasd_add_busid(char *bus_id, int features)
new = (struct dasd_devmap *)
kmalloc(sizeof(struct dasd_devmap), GFP_KERNEL);
if (!new)
return -ENOMEM;
return ERR_PTR(-ENOMEM);
spin_lock(&dasd_devmap_lock);
devmap = 0;
hash = dasd_hash_busid(bus_id);
......@@ -330,7 +337,7 @@ dasd_add_busid(char *bus_id, int features)
spin_unlock(&dasd_devmap_lock);
if (new)
kfree(new);
return 0;
return devmap;
}
/*
......@@ -343,7 +350,7 @@ dasd_find_busid(char *bus_id)
int hash;
spin_lock(&dasd_devmap_lock);
devmap = 0;
devmap = ERR_PTR(-ENODEV);
hash = dasd_hash_busid(bus_id);
list_for_each_entry(tmp, &dasd_hashlists[hash], list) {
if (strncmp(tmp->bus_id, bus_id, BUS_ID_SIZE) == 0) {
......@@ -361,7 +368,7 @@ dasd_find_busid(char *bus_id)
int
dasd_busid_known(char *bus_id)
{
return dasd_find_busid(bus_id) ? 0 : -ENOENT;
return IS_ERR(dasd_find_busid(bus_id)) ? -ENOENT : 0;
}
/*
......@@ -414,6 +421,28 @@ dasd_device_from_devindex(int devindex)
return device;
}
/*
* Return devmap for cdev. If no devmap exists yet, create one and
* connect it to the cdev.
*/
static struct dasd_devmap *
dasd_devmap_from_cdev(struct ccw_device *cdev)
{
struct dasd_devmap *devmap;
if (cdev->dev.driver_data)
return (struct dasd_devmap *) cdev->dev.driver_data;
devmap = dasd_find_busid(cdev->dev.bus_id);
if (!IS_ERR(devmap)) {
cdev->dev.driver_data = devmap;
return devmap;
}
devmap = dasd_add_busid(cdev->dev.bus_id, DASD_FEATURE_DEFAULT);
if (!IS_ERR(devmap))
cdev->dev.driver_data = devmap;
return devmap;
}
/*
* Create a dasd device structure for cdev.
*/
......@@ -424,41 +453,35 @@ dasd_create_device(struct ccw_device *cdev)
struct dasd_device *device;
int rc;
rc = dasd_add_busid(cdev->dev.bus_id, DASD_FEATURE_DEFAULT);
if (rc)
return ERR_PTR(rc);
devmap = dasd_find_busid(cdev->dev.bus_id);
devmap = dasd_devmap_from_cdev(cdev);
if (IS_ERR(devmap))
return (void *) devmap;
device = dasd_alloc_device();
if (IS_ERR(device))
return device;
atomic_set(&device->ref_count, 1);
device->devindex = devmap->devindex;
device->ro_flag = (devmap->features & DASD_FEATURE_READONLY) ? 1 : 0;
device->use_diag_flag = 1;
atomic_set(&device->ref_count, 2);
spin_lock_irq(get_ccwdev_lock(cdev));
if (cdev->dev.driver_data == NULL) {
spin_lock(&dasd_devmap_lock);
if (!devmap->device) {
devmap->device = device;
device->devindex = devmap->devindex;
device->ro_flag =
(devmap->features & DASD_FEATURE_READONLY) != 0;
device->use_diag_flag =
(devmap->features & DASD_FEATURE_USEDIAG) != 0;
get_device(&cdev->dev);
cdev->dev.driver_data = device;
device->cdev = cdev;
rc = 0;
} else
/* Someone else was faster. */
rc = -EBUSY;
spin_unlock_irq(get_ccwdev_lock(cdev));
spin_unlock(&dasd_devmap_lock);
if (rc) {
dasd_free_device(device);
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;
}
......@@ -468,7 +491,8 @@ dasd_create_device(struct ccw_device *cdev)
static DECLARE_WAIT_QUEUE_HEAD(dasd_delete_wq);
/*
* Remove a dasd device structure.
* Remove a dasd device structure. The passed referenced
* is destroyed.
*/
void
dasd_delete_device(struct dasd_device *device)
......@@ -479,17 +503,24 @@ dasd_delete_device(struct dasd_device *device)
/* First remove device pointer from devmap. */
devmap = dasd_find_busid(device->cdev->dev.bus_id);
spin_lock(&dasd_devmap_lock);
if (devmap->device != device) {
spin_unlock(&dasd_devmap_lock);
dasd_put_device(device);
return;
}
devmap->device = NULL;
spin_unlock(&dasd_devmap_lock);
/* Drop ref_count by 2, one for the devmap reference and
* one for the passed reference. */
atomic_sub(2, &device->ref_count);
/* 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;
cdev->dev.driver_data = NULL;
/* Put ccw_device structure. */
put_device(&cdev->dev);
......@@ -508,6 +539,148 @@ dasd_put_device_wake(struct dasd_device *device)
wake_up(&dasd_delete_wq);
}
/*
* Return dasd_device structure associated with cdev.
*/
struct dasd_device *
dasd_device_from_cdev(struct ccw_device *cdev)
{
struct dasd_devmap *devmap;
struct dasd_device *device;
device = ERR_PTR(-ENODEV);
spin_lock(&dasd_devmap_lock);
devmap = cdev->dev.driver_data;
if (devmap && devmap->device) {
device = devmap->device;
dasd_get_device(device);
}
spin_unlock(&dasd_devmap_lock);
return device;
}
/*
* SECTION: files in sysfs
*/
/*
* readonly controls the readonly status of a dasd
*/
static ssize_t
dasd_ro_show(struct device *dev, char *buf)
{
struct dasd_devmap *devmap;
int ro_flag;
devmap = dev->driver_data;
if (devmap)
ro_flag = (devmap->features & DASD_FEATURE_READONLY) != 0;
else
ro_flag = (DASD_FEATURE_DEFAULT & DASD_FEATURE_READONLY) != 0;
return snprintf(buf, PAGE_SIZE, ro_flag ? "1\n" : "0\n");
}
static ssize_t
dasd_ro_store(struct device *dev, const char *buf, size_t count)
{
struct dasd_devmap *devmap;
int ro_flag;
devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
ro_flag = buf[0] == '1';
spin_lock(&dasd_devmap_lock);
if (ro_flag)
devmap->features |= DASD_FEATURE_READONLY;
else
devmap->features &= ~DASD_FEATURE_READONLY;
if (devmap->device) {
if (devmap->device->gdp)
set_disk_ro(devmap->device->gdp, ro_flag);
devmap->device->ro_flag = ro_flag;
}
spin_unlock(&dasd_devmap_lock);
return count;
}
static DEVICE_ATTR(readonly, 0644, dasd_ro_show, dasd_ro_store);
/*
* use_diag controls whether the driver should use diag rather than ssch
* to talk to the device
*/
/* TODO: Implement */
static ssize_t
dasd_use_diag_show(struct device *dev, char *buf)
{
struct dasd_devmap *devmap;
int use_diag;
devmap = dev->driver_data;
if (devmap)
use_diag = (devmap->features & DASD_FEATURE_USEDIAG) != 0;
else
use_diag = (DASD_FEATURE_DEFAULT & DASD_FEATURE_USEDIAG) != 0;
return sprintf(buf, use_diag ? "1\n" : "0\n");
}
static ssize_t
dasd_use_diag_store(struct device *dev, const char *buf, size_t count)
{
struct dasd_devmap *devmap;
int use_diag;
devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
use_diag = buf[0] == '1';
spin_lock(&dasd_devmap_lock);
/* Changing diag discipline flag is only allowed in offline state. */
if (!devmap->device) {
if (use_diag)
devmap->features |= DASD_FEATURE_USEDIAG;
else
devmap->features &= ~DASD_FEATURE_USEDIAG;
} else
count = -EPERM;
spin_unlock(&dasd_devmap_lock);
return count;
}
static
DEVICE_ATTR(use_diag, 0644, dasd_use_diag_show, dasd_use_diag_store);
static ssize_t
dasd_discipline_show(struct device *dev, char *buf)
{
struct dasd_devmap *devmap;
char *dname;
spin_lock(&dasd_devmap_lock);
dname = "none";
devmap = dev->driver_data;
if (devmap && devmap->device && devmap->device->discipline)
dname = devmap->device->discipline->name;
spin_unlock(&dasd_devmap_lock);
return snprintf(buf, PAGE_SIZE, "%s\n", dname);
}
static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL);
static struct attribute * dasd_attrs[] = {
&dev_attr_readonly.attr,
&dev_attr_discipline.attr,
&dev_attr_use_diag.attr,
NULL,
};
static struct attribute_group dasd_attr_group = {
.attrs = dasd_attrs,
};
int
dasd_add_sysfs_files(struct ccw_device *cdev)
{
return sysfs_create_group(&cdev->dev.kobj, &dasd_attr_group);
}
int
dasd_devmap_init(void)
{
......
......@@ -6,7 +6,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
* $Revision: 1.32 $
* $Revision: 1.33 $
*/
#include <linux/config.h>
......@@ -393,7 +393,7 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req)
recid = first_rec;
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) {
memset(dbio, 0, sizeof (struct dasd_diag_bio));
dbio->type = rw_cmd;
......
......@@ -7,7 +7,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
* $Revision: 1.47 $
* $Revision: 1.49 $
*/
#include <linux/config.h>
......@@ -103,6 +103,7 @@ static struct ccw_driver dasd_eckd_driver = {
.remove = dasd_generic_remove,
.set_offline = dasd_generic_set_offline,
.set_online = dasd_eckd_set_online,
.notify = dasd_generic_notify,
};
static const int sizes_trk0[] = { 28, 148, 84 };
......@@ -460,8 +461,8 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
/* Invalidate status of initial analysis. */
private->init_cqr_status = -1;
/* Set default cache operations. */
private->attrib.operation = DASD_SEQ_ACCESS;
private->attrib.nr_cyl = 0x02;
private->attrib.operation = DASD_NORMAL_CACHE;
private->attrib.nr_cyl = 0;
/* Read Device Characteristics */
rdc_data = (void *) &(private->rdc_data);
......@@ -1291,6 +1292,36 @@ dasd_eckd_performance(struct block_device *bdev, int no, long args)
return rc;
}
/*
* Get attributes (cache operations)
* Returnes the cache attributes used in Define Extend (DE).
*/
static int
dasd_eckd_get_attrib (struct block_device *bdev, int no, long args)
{
struct dasd_device *device;
struct dasd_eckd_private *private;
struct attrib_data_t attrib;
int rc;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (!args)
return -EINVAL;
device = bdev->bd_disk->private_data;
if (device == NULL)
return -ENODEV;
private = (struct dasd_eckd_private *) device->private;
attrib = private->attrib;
rc = copy_to_user((long *) args, (long *) &attrib,
sizeof (struct attrib_data_t));
return rc;
}
/*
* Set attributes (cache operations)
* Stores the attributes for cache operation to be used in Define Extend (DE).
......@@ -1433,6 +1464,8 @@ dasd_eckd_init(void)
{
int ret;
dasd_ioctl_no_register(THIS_MODULE, BIODASDGATTR,
dasd_eckd_get_attrib);
dasd_ioctl_no_register(THIS_MODULE, BIODASDSATTR,
dasd_eckd_set_attrib);
dasd_ioctl_no_register(THIS_MODULE, BIODASDPSRD,
......@@ -1448,6 +1481,8 @@ dasd_eckd_init(void)
ret = ccw_driver_register(&dasd_eckd_driver);
if (ret) {
dasd_ioctl_no_unregister(THIS_MODULE, BIODASDGATTR,
dasd_eckd_get_attrib);
dasd_ioctl_no_unregister(THIS_MODULE, BIODASDSATTR,
dasd_eckd_set_attrib);
dasd_ioctl_no_unregister(THIS_MODULE, BIODASDPSRD,
......@@ -1470,6 +1505,8 @@ dasd_eckd_cleanup(void)
{
ccw_driver_unregister(&dasd_eckd_driver);
dasd_ioctl_no_unregister(THIS_MODULE, BIODASDGATTR,
dasd_eckd_get_attrib);
dasd_ioctl_no_unregister(THIS_MODULE, BIODASDSATTR,
dasd_eckd_set_attrib);
dasd_ioctl_no_unregister(THIS_MODULE, BIODASDPSRD,
......
......@@ -4,7 +4,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
* $Revision: 1.30 $
* $Revision: 1.32 $
*/
#include <linux/config.h>
......@@ -80,6 +80,7 @@ static struct ccw_driver dasd_fba_driver = {
.remove = dasd_generic_remove,
.set_offline = dasd_generic_set_offline,
.set_online = dasd_fba_set_online,
.notify = dasd_generic_notify,
};
static inline void
......@@ -269,7 +270,7 @@ dasd_fba_build_cp(struct dasd_device * 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
}
......@@ -309,7 +310,7 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req)
}
recid = first_rec;
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) {
/* Locate record for stupid devices. */
if (private->rdc_data.mode.bits.data_chain == 0) {
......
......@@ -9,7 +9,7 @@
*
* gendisk related functions for the dasd driver.
*
* $Revision: 1.41 $
* $Revision: 1.42 $
*/
#include <linux/config.h>
......@@ -31,7 +31,6 @@ int
dasd_gendisk_alloc(struct dasd_device *device)
{
struct gendisk *gdp;
int len;
/* Make sure the minor for this device exists. */
if (device->devindex >= DASD_PER_MAJOR)
......@@ -47,22 +46,8 @@ dasd_gendisk_alloc(struct dasd_device *device)
gdp->fops = &dasd_device_operations;
gdp->driverfs_dev = &device->cdev->dev;
/*
* Set device name.
* dasda - dasdz : 26 devices
* dasdaa - dasdzz : 676 devices, added up = 702
* dasdaaa - dasdzzz : 17576 devices, added up = 18278
*/
len = sprintf(gdp->disk_name, "dasd");
if (device->devindex > 25) {
if (device->devindex > 701)
len += sprintf(gdp->disk_name + len, "%c",
'a'+(((device->devindex-702)/676)%26));
len += sprintf(gdp->disk_name + len, "%c",
'a'+(((device->devindex-26)/26)%26));
}
len += sprintf(gdp->disk_name + len, "%c", 'a'+(device->devindex%26));
/* Set device name */
sprintf(gdp->disk_name, "dasd_%s_", device->cdev->dev.bus_id);
sprintf(gdp->devfs_name, "dasd/%s", device->cdev->dev.bus_id);
if (device->ro_flag)
......
......@@ -6,7 +6,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
* $Revision: 1.48 $
* $Revision: 1.52 $
*/
#ifndef DASD_INT_H
......@@ -268,6 +268,7 @@ struct dasd_device {
unsigned int s2b_shift; /* log2 (bp_block/512) */
int ro_flag; /* read-only flag */
int use_diag_flag; /* diag allowed flag */
int disconnect_error_flag; /* return -EIO when disconnected */
/* Device discipline stuff. */
......@@ -308,6 +309,8 @@ struct dasd_device {
#define DASD_STOPPED_NOT_ACC 1 /* not accessible */
#define DASD_STOPPED_QUIESCE 2 /* Quiesced */
#define DASD_STOPPED_PENDING 4 /* long busy */
#define DASD_STOPPED_DC_WAIT 8 /* disconnected, wait */
#define DASD_STOPPED_DC_EIO 16 /* disconnected, return -EIO */
void dasd_put_device_wake(struct dasd_device *);
......@@ -458,9 +461,10 @@ void dasd_set_timer(struct dasd_device *, int);
void dasd_clear_timer(struct dasd_device *);
int dasd_cancel_req(struct dasd_ccw_req *);
int dasd_generic_probe (struct ccw_device *, struct dasd_discipline *);
int dasd_generic_remove (struct ccw_device *cdev);
void dasd_generic_remove (struct ccw_device *cdev);
int dasd_generic_set_online(struct ccw_device *, struct dasd_discipline *);
int dasd_generic_set_offline (struct ccw_device *cdev);
int dasd_generic_notify(struct ccw_device *, int);
void dasd_generic_auto_online (struct ccw_driver *);
/* externals in dasd_devmap.c */
......@@ -474,10 +478,11 @@ void dasd_devmap_exit(void);
struct dasd_device *dasd_create_device(struct ccw_device *);
void dasd_delete_device(struct dasd_device *);
int dasd_add_sysfs_files(struct ccw_device *);
struct dasd_device *dasd_device_from_cdev(struct ccw_device *);
struct dasd_device *dasd_device_from_devindex(int);
int dasd_parse(void);
int dasd_add_busid(char *, int);
int dasd_busid_known(char *);
/* externals in dasd_gendisk.c */
......
......@@ -9,7 +9,7 @@
*
* /proc interface for the dasd driver.
*
* $Revision: 1.23 $
* $Revision: 1.24 $
*/
#include <linux/config.h>
......@@ -67,15 +67,10 @@ dasd_devices_show(struct seq_file *m, void *v)
seq_printf(m, "(none)");
/* Print kdev. */
if (device->gdp)
seq_printf(m, " at (%3d:%3d)",
seq_printf(m, " at (%3d:%7d)",
device->gdp->major, device->gdp->first_minor);
else
seq_printf(m, " at (???:???)");
/* Print device name. */
if (device->gdp)
seq_printf(m, " is %-7s", device->gdp->disk_name);
else
seq_printf(m, " is ???????");
seq_printf(m, " at (???:???????)");
/* Print devices features. */
substr = device->ro_flag ? "(ro)" : " ";
seq_printf(m, "%4s: ", substr);
......
......@@ -8,7 +8,7 @@
* any future changes wrt the API will result in a change of the APIVERSION reported
* to userspace by the DASDAPIVER-ioctl
*
* $Revision: 1.4 $
* $Revision: 1.6 $
*
*/
......@@ -69,9 +69,11 @@ typedef struct dasd_information2_t {
* values to be used for dasd_information_t.features
* 0x00: default features
* 0x01: readonly (ro)
* 0x02: use diag discipline (diag)
*/
#define DASD_FEATURE_DEFAULT 0
#define DASD_FEATURE_READONLY 1
#define DASD_FEATURE_USEDIAG 2
#define DASD_PARTN_BITS 2
......@@ -234,6 +236,8 @@ typedef struct attrib_data_t {
#define BIODASDINFO2 _IOR(DASD_IOCTL_LETTER,3,dasd_information2_t)
/* Performance Statistics Read */
#define BIODASDPSRD _IOR(DASD_IOCTL_LETTER,4,dasd_rssd_perf_stats_t)
/* Get Attributes (cache operations) */
#define BIODASDGATTR _IOR(DASD_IOCTL_LETTER,5,attrib_data_t)
/* #define BIODASDFORMAT _IOW(IOCTL_LETTER,0,format_data_t) , deprecated */
......
......@@ -82,7 +82,7 @@ struct gendisk {
int major; /* major number of driver */
int first_minor;
int minors;
char disk_name[16]; /* name of major driver */
char disk_name[32]; /* name of major driver */
struct hd_struct **part; /* [indexed by minor] */
struct block_device_operations *fops;
struct request_queue *queue;
......
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