Commit f7a9fa93 authored by Martin Schwidefsky's avatar Martin Schwidefsky Committed by Linus Torvalds

[PATCH] s390: dasd driver.

Convert dasd driver to new channel subsystem driver

This makes the dasd driver work again after the changes to
the channel subsystem driver:
- handle device detection with standard driver model functions
- reduce use of dasd_devmap (devmap will die as soon as
  dasd configuration is handled from initramfs)
- some cleanups

Authors: Arnd Bergmann <arndb@de.ibm.com>
	 Martin Schwidefsky <schwidefsky@de.ibm.com>
parent 0286402a
......@@ -79,6 +79,7 @@ CONFIG_BLK_DEV_XPRAM=m
# S/390 block device drivers
#
CONFIG_DASD=y
# CONFIG_DASD_PROFILE is not set
CONFIG_DASD_ECKD=y
CONFIG_DASD_FBA=y
CONFIG_DASD_DIAG=y
......
......@@ -137,6 +137,7 @@ CONFIG_BLK_DEV_XPRAM=m
# S/390 block device drivers
#
CONFIG_DASD=y
# CONFIG_DASD_PROFILE is not set
CONFIG_DASD_ECKD=y
CONFIG_DASD_FBA=y
# CONFIG_DASD_DIAG is not set
......
......@@ -138,49 +138,34 @@ config DASD
S/390s channel subsystem commands. This is necessary for running
natively on a single image or an LPAR.
config DASD_PROFILE
bool "Profiling support for dasd devices"
help
Enable this option if you want to see profiling information
in /proc/dasd/statistics.
config DASD_ECKD
tristate "Support for ECKD Disks"
depends on DASD
help
ECKD devices are the most commonly used devices. you should enable
ECKD devices are the most commonly used devices. You should enable
this option unless you are very sure to have no ECKD device.
config DASD_AUTO_ECKD
bool "Automatic activation of ECKD module"
depends on DASD && DASD_ECKD=m
help
Enable this option if you want your ECKD discipline module loaded
on DASD driver startup.
config DASD_FBA
tristate "Support for FBA Disks"
depends on DASD
help
FBA devices are currently unsupported.
Select this option to be able to access FBA devices. It is safe to
say "Y".
config DASD_AUTO_FBA
bool "Automatic activation of FBA module"
depends on DASD && DASD_FBA=m
help
Enable this option if you want your FBA discipline module loaded
on DASD driver startup.
# dep_tristate ' Support for CKD Disks' CONFIG_DASD_CKD $CONFIG_DASD
config DASD_DIAG
tristate "Support for DIAG access to CMS reserved Disks"
depends on !ARCH_S390X && DASD
depends on DASD
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
unsure what it is, say "N".
config DASD_AUTO_DIAG
bool "Automatic activation of DIAG module"
depends on DASD && !ARCH_S390X && DASD_DIAG=m
help
Enable this option if you want your DIAG discipline module loaded
on DASD driver startup.
endmenu
source "drivers/md/Kconfig"
......
......@@ -7,6 +7,8 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
*
* $Revision: 1.71 $
*
* History of changes (starts July 2000)
* 11/09/00 complete redesign after code review
* 02/01/01 added dynamic registration of ioctls
......@@ -51,7 +53,7 @@
#include <linux/slab.h>
#include <linux/buffer_head.h>
#include <asm/div64.h>
#include <asm/ccwdev.h>
#include <asm/ebcdic.h>
#include <asm/idals.h>
#include <asm/todclk.h>
......@@ -60,10 +62,6 @@
#define PRINTK_HEADER "dasd:"
#include "dasd_int.h"
#include "dasd_eckd.h"
#include "dasd_fba.h"
#include "dasd_diag.h"
/*
* SECTION: Constant definitions to be used within this file
*/
......@@ -80,116 +78,19 @@ MODULE_DESCRIPTION("Linux on S/390 DASD device driver,"
MODULE_SUPPORTED_DEVICE("dasd");
MODULE_PARM(dasd, "1-" __MODULE_STRING(256) "s");
MODULE_PARM(dasd_disciplines, "1-" __MODULE_STRING(8) "s");
MODULE_LICENSE("GPL");
/*
* SECTION: prototypes for static functions of dasd.c
*/
static void dasd_enable_discipline(dasd_discipline_t *);
static void dasd_disable_discipline(dasd_discipline_t *);
static void dasd_not_oper_handler(int irq, int status);
static int dasd_setup_blkdev(dasd_device_t * device);
static void dasd_disable_blkdev(dasd_device_t * device);
static void dasd_flush_request_queue(dasd_device_t *);
static void dasd_int_handler(int, void *, struct pt_regs *);
static void dasd_int_handler(struct ccw_device *, unsigned long, struct irb *);
static void dasd_flush_ccw_queue(dasd_device_t *, int);
static void dasd_tasklet(dasd_device_t *);
static void do_kick_device(void *data);
/*
* Parameter parsing functions. There are two for the dasd driver:
* <dasd_disciplines> : <dasd_module>(,<dasd_module>)*
*/
static char *dasd_disciplines[8];
#ifndef MODULE
/*
* The parameter parsing functions for builtin-drivers are called
* before kmalloc works. Store the pointers to the parameters strings
* into dasd_disciplines[] for later processing.
*/
static int __init
dasd_disciplines_setup(char *str)
{
static int count = 0;
if (count < 8)
dasd_disciplines[count++] = str;
return 1;
}
__setup ("dasd_disciplines=", dasd_disciplines_setup);
#endif /* #ifndef MODULE */
/*
* SECTION: managing dasd disciplines
*/
static struct list_head dasd_disc_head = LIST_HEAD_INIT(dasd_disc_head);
static spinlock_t discipline_lock = SPIN_LOCK_UNLOCKED;
/*
* Add a discipline to the head of the discipline chain. The last added
* discipline that matches a device is used. In particular disciplines
* added by a module will have precedence over statically linked
* disciplines.
*/
void
dasd_discipline_add(dasd_discipline_t * discipline)
{
MOD_INC_USE_COUNT;
spin_lock(&discipline_lock);
list_add(&discipline->list, &dasd_disc_head);
spin_unlock(&discipline_lock);
/* Setup devices for discipline. */
dasd_enable_discipline(discipline);
}
/*
* Remove a discipline from the discipline list and disable all devices
* that rely on that discipline.
*/
void
dasd_discipline_del(dasd_discipline_t * discipline)
{
struct list_head *l;
spin_lock(&discipline_lock);
/* Check if the discipline was added. */
list_for_each(l, &dasd_disc_head) {
if (list_entry(l, dasd_discipline_t, list) == discipline) {
list_del(&discipline->list);
break;
}
}
spin_unlock(&discipline_lock);
/* Disable devices for discipline. */
dasd_disable_discipline(discipline);
MOD_DEC_USE_COUNT;
}
/*
* Find the discipline for a device.
*/
static inline int
dasd_find_disc(dasd_device_t * device)
{
struct list_head *l;
dasd_discipline_t *discipline, *tmp;
discipline = NULL;
spin_lock(&discipline_lock);
list_for_each(l, &dasd_disc_head) {
tmp = list_entry(l, dasd_discipline_t, list);
if (tmp->check_device(device) == 0) {
/* Found a matching discipline. */
discipline = tmp;
break;
}
}
device->discipline = discipline;
spin_unlock(&discipline_lock);
return (discipline != NULL) ? 0 : -ENODEV;
}
static int dasd_add_sysfs_files(struct ccw_device *cdev);
/*
* SECTION: Operations on the device structure.
......@@ -204,22 +105,13 @@ dasd_alloc_device(dasd_devmap_t *devmap)
{
dasd_device_t *device;
struct gendisk *gdp;
int rc;
device = kmalloc(sizeof (dasd_device_t), GFP_ATOMIC);
if (device == NULL)
return ERR_PTR(-ENOMEM);
memset(device, 0, sizeof (dasd_device_t));
/* Get devinfo from the common io layer. */
rc = get_dev_info_by_devno(devmap->devno, &device->devinfo);
if (rc) {
kfree(device);
return ERR_PTR(rc);
}
DBF_EVENT(DBF_NOTICE, "got devinfo CU-type %04x and dev-type %04x",
device->devinfo.sid_data.cu_type,
device->devinfo.sid_data.dev_type);
device->devno = devmap->devno;
/* Get two pages for normal block device operations. */
device->ccw_mem = (void *) __get_free_pages(GFP_ATOMIC | GFP_DMA, 1);
......@@ -255,7 +147,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->devinfo.devno);
INIT_WORK(&device->kick_work, do_kick_device, (void *) (addr_t) device->devno);
device->state = DASD_STATE_NEW;
device->target = DASD_STATE_NEW;
......@@ -286,9 +178,13 @@ dasd_state_new_to_known(dasd_device_t *device)
dasd_devmap_t *devmap;
umode_t devfs_perm;
devfs_handle_t dir;
int major, minor, rc;
int major, minor;
/* Increase reference count of bdev. */
if (bdget(MKDEV(device->gdp->major, device->gdp->first_minor)) == NULL)
return -ENODEV;
devmap = dasd_devmap_from_devno(device->devinfo.devno);
devmap = dasd_devmap_from_devno(device->devno);
if (devmap == NULL)
return -ENODEV;
major = dasd_gendisk_index_major(devmap->devindex);
......@@ -296,16 +192,11 @@ dasd_state_new_to_known(dasd_device_t *device)
return -ENODEV;
minor = devmap->devindex % DASD_PER_MAJOR;
/* Find a discipline for the device. */
rc = dasd_find_disc(device);
if (rc)
return rc;
/* Add a proc directory and the dasd device entry to devfs. */
sprintf(buffer, "dasd/%04x", device->devinfo.devno);
dir = devfs_mk_dir(NULL, buffer, NULL);
sprintf(buffer, "dasd/%04x", device->devno);
dir = devfs_mk_dir(NULL, buffer, NULL);
device->gdp->de = dir;
if (devmap->features & DASD_FEATURE_READONLY)
if (device->ro_flag)
devfs_perm = S_IFBLK | S_IRUSR;
else
devfs_perm = S_IFBLK | S_IRUSR | S_IWUSR;
......@@ -323,12 +214,7 @@ dasd_state_new_to_known(dasd_device_t *device)
static inline void
dasd_state_known_to_new(dasd_device_t * device)
{
dasd_devmap_t *devmap;
struct block_device *bdev;
int minor;
devmap = dasd_devmap_from_devno(device->devinfo.devno);
minor = devmap->devindex % DASD_PER_MAJOR;
/* Remove device entry and devfs directory. */
devfs_unregister(device->devfs_entry);
......@@ -337,6 +223,11 @@ dasd_state_known_to_new(dasd_device_t * device)
/* Forget the discipline information. */
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);
}
/*
......@@ -345,8 +236,6 @@ dasd_state_known_to_new(dasd_device_t * device)
static inline int
dasd_state_known_to_basic(dasd_device_t * device)
{
int rc;
/* register 'device' debug area, used for all DBF_DEV_XXX calls */
device->debug_area = debug_register(device->gdp->disk_name, 0, 2,
8 * sizeof (long));
......@@ -354,19 +243,6 @@ dasd_state_known_to_basic(dasd_device_t * device)
debug_set_level(device->debug_area, DBF_ERR);
DBF_DEV_EVENT(DBF_EMERG, device, "%s", "debug area created");
/*
* We request the "official" irq line even for dasd diag device.
* The interrupts for dasd diag will arrive at dasd_ext_handler
* instead of dasd_int_handler so this is just a placeholder.
*/
rc = s390_request_irq_special(device->devinfo.irq,
dasd_int_handler,
dasd_not_oper_handler,
0, "dasd", &device->dev_status);
if (rc) {
MESSAGE(KERN_ERR, "%s", "No request IRQ");
return rc;
}
device->state = DASD_STATE_BASIC;
return 0;
}
......@@ -378,7 +254,6 @@ static inline void
dasd_state_basic_to_known(dasd_device_t * device)
{
dasd_flush_ccw_queue(device, 1);
free_irq(device->devinfo.irq, &device->dev_status);
DBF_DEV_EVENT(DBF_EMERG, device, "%p debug area deleted", device);
if (device->debug_area != NULL) {
debug_unregister(device->debug_area);
......@@ -428,15 +303,8 @@ dasd_state_accept_to_basic(dasd_device_t * device)
static inline int
dasd_state_accept_to_ready(dasd_device_t * device)
{
dasd_devmap_t *devmap;
int rc, i;
int rc;
devmap = dasd_devmap_from_devno(device->devinfo.devno);
if (devmap->features & DASD_FEATURE_READONLY) {
device->ro_flag = 1;
DEV_MESSAGE (KERN_WARNING, device, "%s",
"setting read-only mode ");
}
rc = dasd_setup_blkdev(device);
if (rc == 0) {
dasd_setup_partitions(device);
......@@ -559,7 +427,7 @@ 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->devinfo.devno);
"devno %04x", device->devno);
device->target = device->state;
}
......@@ -622,255 +490,25 @@ dasd_set_target_state(dasd_device_t *device, int target)
* Enable devices with device numbers in [from..to].
*/
static inline int
_wait_for_devices(int from, int to)
_wait_for_device(dasd_device_t *device)
{
dasd_devmap_t *devmap;
dasd_device_t *device;
int devno, rc;
rc = 0;
for (devno = from; devno <= to && rc == 0; devno++) {
devmap = dasd_devmap_from_devno(devno);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device))
continue;
if (device->state != device->target)
rc = 1;
dasd_put_device(devmap);
}
return rc;
return (device->state == device->target);
}
// FIXME: if called from dasd_devices_write discpline is not set -> oops.
void
dasd_enable_devices(int from, int to)
dasd_enable_device(dasd_device_t *device)
{
dasd_devmap_t *devmap;
dasd_device_t *device;
int devno;
for (devno = from; devno <= to; devno++) {
devmap = dasd_devmap_from_devno(devno);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device))
continue;
dasd_set_target_state(device, DASD_STATE_ONLINE);
if (device->state <= DASD_STATE_KNOWN)
/* No discipline for device found. */
dasd_set_target_state(device, DASD_STATE_NEW);
dasd_put_device(devmap);
}
/* Now wait for the devices to come up. */
wait_event(dasd_init_waitq, _wait_for_devices(from, to) == 0);
}
/*
* Disable devices with device numbers in [from..to].
*/
void
dasd_disable_devices(int from, int to)
{
dasd_devmap_t *devmap;
dasd_device_t *device;
int devno;
for (devno = from; devno <= to; devno++) {
devmap = dasd_devmap_from_devno(devno);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device))
continue;
dasd_set_target_state(device, DASD_STATE_ONLINE);
if (device->state <= DASD_STATE_KNOWN)
/* No discipline for device found. */
dasd_set_target_state(device, DASD_STATE_NEW);
dasd_put_device(devmap);
}
}
/*
* Enable devices that use a specific discipline.
*/
static inline int
_wait_for_disc_devices(dasd_discipline_t *discipline)
{
dasd_devmap_t *devmap;
dasd_device_t *device;
int devindex, rc;
rc = 0;
for (devindex = 0; devindex < dasd_max_devindex; devindex++) {
devmap = dasd_devmap_from_devindex(devindex);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device))
continue;
if (device->discipline == discipline &&
device->state != device->target)
rc = 1;
dasd_put_device(devmap);
}
return rc;
}
static void
dasd_enable_discipline(dasd_discipline_t *discipline)
{
dasd_devmap_t *devmap;
dasd_device_t *device;
int devindex;
for (devindex = 0; devindex < dasd_max_devindex; devindex++) {
devmap = dasd_devmap_from_devindex(devindex);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device))
continue;
if (device->discipline != NULL &&
device->discipline != discipline)
continue;
dasd_set_target_state(device, DASD_STATE_ONLINE);
if (device->discipline == NULL)
/* No discipline for device found. */
dasd_set_target_state(device, DASD_STATE_NEW);
dasd_put_device(devmap);
}
/* Now wait for the devices to come up. */
wait_event(dasd_init_waitq, _wait_for_disc_devices(discipline) == 0);
}
/*
* Disable devices that use a specific discipline.
*/
static void
dasd_disable_discipline(dasd_discipline_t *discipline)
{
dasd_devmap_t *devmap;
dasd_device_t *device;
int devindex;
/* Shutdown device that use the discipline. */
for (devindex = 0; devindex < dasd_max_devindex; devindex++) {
devmap = dasd_devmap_from_devindex(devindex);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device))
continue;
if (device->discipline == discipline)
dasd_set_target_state(device, DASD_STATE_NEW);
dasd_put_device(devmap);
}
wait_event(dasd_init_waitq, _wait_for_device(device));
}
/*
* Deactivate a device that has vaninshed.
*/
static void
do_not_oper_handler(void *data)
{
struct {
struct work_struct work;
int irq;
} *p;
dasd_device_t *device;
dasd_devmap_t *devmap;
/*
* find out devno of leaving device: CIO has already deleted
* this information so we need to find it by irq!
*/
p = data;
devmap = dasd_devmap_from_irq(p->irq);
if (devmap == NULL) {
MESSAGE(KERN_WARNING,
"not_oper_handler called on irq 0x%04x no devno!",
p->irq);
return;
} else
DBF_EVENT(DBF_NOTICE, "called for devno %04x", devmap->devno);
device = dasd_get_device(devmap);
if (IS_ERR(device))
return;
dasd_set_target_state(device, DASD_STATE_NEW);
dasd_put_device(devmap);
kfree(p);
}
void
dasd_not_oper_handler(int irq, int status)
{
struct {
struct work_struct work;
int irq;
} *p;
p = kmalloc(sizeof(*p), GFP_ATOMIC);
if (p == NULL)
/* FIXME: No memory, we loose. */
return;
INIT_WORK(&p->work, (void *) do_not_oper_handler, p);
p->irq = irq;
/* queue call to do_not_oper_handler to the kernel event daemon. */
schedule_work(&p->work);
}
/*
* Activate a device that appeared out of nowhere.
*/
static void
do_oper_handler(void *data)
{
struct {
struct work_struct work;
int devno;
} *p;
dasd_devmap_t *devmap;
dasd_device_t *device;
p = data;
DBF_EVENT(DBF_NOTICE, "called for devno %04x", p->devno);
if (dasd_autodetect &&
dasd_add_range(p->devno, p->devno, DASD_FEATURE_DEFAULT) != 0)
return;
/* Get/create the device structure for devno. */
devmap = dasd_devmap_from_devno(p->devno);
if (devmap == NULL) {
DBF_EXC(DBF_ALERT, "no dasd: devno %04x", p->devno);
return;
}
device = dasd_get_device(devmap);
if (IS_ERR(device))
return;
dasd_enable_devices(p->devno, p->devno);
dasd_put_device(devmap);
kfree(p);
}
int
dasd_oper_handler(int irq, devreg_t * devreg)
{
struct {
struct work_struct work;
int devno;
} *p;
p = kmalloc(sizeof(*p), GFP_ATOMIC);
if (p == NULL)
/* FIXME: No memory, we loose. */
return -ENOMEM;
p->devno = get_devno_by_irq(irq);
if (p->devno == -ENODEV)
return -ENODEV;
INIT_WORK(&p->work, (void *) do_oper_handler, p);
/* queue call to do_oper_handler to the kernel event daemon. */
schedule_work(&p->work);
return 0;
}
/*
* SECTION: device operation (interrupt handler, start_IO, term_IO ...)
* SECTION: device operation (interrupt handler, start i/o, term i/o ...)
*/
#ifdef CONFIG_DASD_PROFILE
......@@ -973,7 +611,7 @@ dasd_kmalloc_request(char *magic, int cplength, int datasize,
/* Sanity checks */
if ( magic == NULL || datasize > PAGE_SIZE ||
(cplength*sizeof(ccw1_t)) > PAGE_SIZE)
(cplength*sizeof(struct ccw1)) > PAGE_SIZE)
BUG();
debug_text_event ( dasd_debug_area, 1, "ALLC");
debug_text_event ( dasd_debug_area, 1, magic);
......@@ -986,13 +624,13 @@ dasd_kmalloc_request(char *magic, int cplength, int datasize,
memset(cqr, 0, sizeof(dasd_ccw_req_t));
cqr->cpaddr = NULL;
if (cplength > 0) {
cqr->cpaddr = kmalloc(cplength*sizeof(ccw1_t),
cqr->cpaddr = kmalloc(cplength*sizeof(struct ccw1),
GFP_ATOMIC | GFP_DMA);
if (cqr->cpaddr == NULL) {
kfree(cqr);
return ERR_PTR(-ENOMEM);
}
memset(cqr->cpaddr, 0, cplength*sizeof(ccw1_t));
memset(cqr->cpaddr, 0, cplength*sizeof(struct ccw1));
}
cqr->data = NULL;
if (datasize > 0) {
......@@ -1022,7 +660,7 @@ dasd_smalloc_request(char *magic, int cplength, int datasize,
/* Sanity checks */
if ( magic == NULL || datasize > PAGE_SIZE ||
(cplength*sizeof(ccw1_t)) > PAGE_SIZE)
(cplength*sizeof(struct ccw1)) > PAGE_SIZE)
BUG();
debug_text_event ( dasd_debug_area, 1, "ALLC");
debug_text_event ( dasd_debug_area, 1, magic);
......@@ -1031,7 +669,7 @@ dasd_smalloc_request(char *magic, int cplength, int datasize,
size = (sizeof(dasd_ccw_req_t) + 7L) & -8L;
if (cplength > 0)
size += cplength * sizeof(ccw1_t);
size += cplength * sizeof(struct ccw1);
if (datasize > 0)
size += datasize;
spin_lock_irqsave(&device->mem_lock, flags);
......@@ -1043,9 +681,9 @@ dasd_smalloc_request(char *magic, int cplength, int datasize,
data = (char *) cqr + ((sizeof(dasd_ccw_req_t) + 7L) & -8L);
cqr->cpaddr = NULL;
if (cplength > 0) {
cqr->cpaddr = (ccw1_t *) data;
data += cplength*sizeof(ccw1_t);
memset(cqr->cpaddr, 0, cplength*sizeof(ccw1_t));
cqr->cpaddr = (struct ccw1 *) data;
data += cplength*sizeof(struct ccw1);
memset(cqr->cpaddr, 0, cplength*sizeof(struct ccw1));
}
cqr->data = NULL;
if (datasize > 0) {
......@@ -1067,7 +705,7 @@ void
dasd_kfree_request(dasd_ccw_req_t * cqr, dasd_device_t * device)
{
#ifdef CONFIG_ARCH_S390X
ccw1_t *ccw;
struct ccw1 *ccw;
/* Clear any idals used for the request. */
ccw = cqr->cpaddr;
......@@ -1126,7 +764,8 @@ dasd_check_cqr(dasd_ccw_req_t *cqr)
/*
* Terminate the current i/o and set the request to failed.
* halt_IO/clear_IO can fail if the i/o subsystem is in a bad mood.
* ccw_device_halt/ccw_device_clear can fail if the i/o subsystem
* is in a bad mood.
*/
int
dasd_term_IO(dasd_ccw_req_t * cqr)
......@@ -1142,11 +781,9 @@ dasd_term_IO(dasd_ccw_req_t * cqr)
device = (dasd_device_t *) cqr->device;
while ((retries < 5) && (cqr->status == DASD_CQR_IN_IO)) {
if (retries < 2)
rc = halt_IO(device->devinfo.irq,
(long) cqr, cqr->options);
rc = ccw_device_halt(device->cdev, (long) cqr);
else
rc = clear_IO(device->devinfo.irq,
(long) cqr, cqr->options);
rc = ccw_device_clear(device->cdev, (long) cqr);
switch (rc) {
case 0: /* termination successful */
cqr->status = DASD_CQR_FAILED;
......@@ -1172,6 +809,7 @@ dasd_term_IO(dasd_ccw_req_t * cqr)
BUG();
break;
}
retries++;
}
dasd_schedule_bh(device);
return rc;
......@@ -1193,17 +831,11 @@ dasd_start_IO(dasd_ccw_req_t * cqr)
return rc;
device = (dasd_device_t *) cqr->device;
cqr->startclk = get_clock();
rc = do_IO(device->devinfo.irq,
cqr->cpaddr, (long) cqr, cqr->lpm, cqr->options);
rc = ccw_device_start(device->cdev, cqr->cpaddr, (long) cqr,
cqr->lpm, 0);
switch (rc) {
case 0:
if (cqr->options & DOIO_WAIT_FOR_INTERRUPT) {
/* request already finished (synchronous IO) */
cqr->status = DASD_CQR_DONE;
cqr->stopclk = cqr->startclk;
dasd_schedule_bh(device);
} else
cqr->status = DASD_CQR_IN_IO;
cqr->status = DASD_CQR_IN_IO;
break;
case -EBUSY:
DBF_DEV_EVENT(DBF_ERR, device, "%s",
......@@ -1244,14 +876,14 @@ dasd_timeout_device(unsigned long ptr)
dasd_ccw_req_t *cqr;
device = (dasd_device_t *) ptr;
spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
/* re-activate first request in queue */
if (!list_empty(&device->ccw_queue)) {
cqr = list_entry(device->ccw_queue.next, dasd_ccw_req_t, list);
if (cqr->status == DASD_CQR_PENDING)
cqr->status = DASD_CQR_QUEUED;
}
spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
dasd_schedule_bh(device);
}
......@@ -1264,18 +896,19 @@ dasd_set_timer(dasd_device_t *device, int expires)
/* FIXME: timeouts are based on jiffies but the timeout
* comparision in __dasd_check_expire is based on the
* TOD clock. */
if (expires != 0) {
if (!timer_pending(&device->timer)) {
device->timer.function = dasd_timeout_device;
device->timer.data = (unsigned long) device;
device->timer.expires = jiffies + expires;
add_timer(&device->timer);
} else
mod_timer(&device->timer, jiffies + expires);
} else {
if (expires == 0) {
if (timer_pending(&device->timer))
del_timer(&device->timer);
return;
}
if (timer_pending(&device->timer)) {
if (mod_timer(&device->timer, jiffies + expires))
return;
}
device->timer.function = dasd_timeout_device;
device->timer.data = (unsigned long) device;
device->timer.expires = jiffies + expires;
add_timer(&device->timer);
}
/*
......@@ -1300,33 +933,36 @@ do_state_change_pending(void *data)
{
struct {
struct work_struct work;
unsigned short devno;
dasd_device_t *device;
} *p;
dasd_devmap_t *devmap;
dasd_device_t *device;
dasd_ccw_req_t *cqr;
int devno;
p = data;
DBF_EVENT(DBF_NOTICE, "State change Interrupt: %04x", p->devno);
device = p->device;
DBF_EVENT(DBF_NOTICE, "State change Interrupt for bus_id %s",
device->cdev->dev.bus_id);
devmap = dasd_devmap_from_devno(p->devno);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device)) {
MESSAGE(KERN_DEBUG,
"unable to find device for state change pending "
"interrupt: devno%04x", p->devno);
return;
}
spin_lock_irq(get_irq_lock(device->devinfo.irq));
// 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)) {
cqr = list_entry(device->ccw_queue.next, dasd_ccw_req_t, list);
if (cqr == NULL) {
MESSAGE (KERN_DEBUG,
"got state change pending interrupt on"
"an idle device: bus_id %s",
device->cdev->dev.bus_id);
return;
}
if (cqr->status == DASD_CQR_PENDING)
cqr->status = DASD_CQR_QUEUED;
}
spin_unlock_irq(get_irq_lock(device->devinfo.irq));
spin_unlock_irq(get_ccwdev_lock(device->cdev));
dasd_schedule_bh(device);
dasd_put_device(devmap);
kfree(p);
......@@ -1334,11 +970,11 @@ do_state_change_pending(void *data)
static void
dasd_handle_state_change_pending(devstat_t * stat)
dasd_handle_state_change_pending(dasd_device_t *device)
{
struct {
struct work_struct work;
unsigned short devno;
dasd_device_t *device;
} *p;
p = kmalloc(sizeof(*p), GFP_ATOMIC);
......@@ -1346,7 +982,8 @@ dasd_handle_state_change_pending(devstat_t * stat)
/* No memory, let the timeout do the reactivation. */
return;
INIT_WORK(&p->work, (void *) do_state_change_pending, p);
p->devno = stat->devno;
p->device = device;
atomic_inc(&device->ref_count);
/* queue call to do_state_change_pending to the kernel event daemon. */
schedule_work(&p->work);
}
......@@ -1355,71 +992,61 @@ dasd_handle_state_change_pending(devstat_t * stat)
* Interrupt handler for "normal" ssch-io based dasd devices.
*/
void
dasd_int_handler(int irq, void *ds, struct pt_regs *regs)
dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
struct irb *irb)
{
dasd_ccw_req_t *cqr, *next;
dasd_device_t *device;
unsigned long long now;
int expires;
dasd_era_t era;
devstat_t *stat;
char mask;
now = get_clock();
stat = (devstat_t *) ds;
DBF_EVENT(DBF_DEBUG, "Interrupt: IRQ %02x, stat %02x, devno %04x",
irq, stat->dstat, stat->devno);
DBF_EVENT(DBF_DEBUG, "Interrupt: stat %02x, bus_id %s",
irb->scsw.dstat, cdev->dev.bus_id);
/* first of all check for state change pending interrupt */
mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
if ((stat->dstat & mask) == mask) {
dasd_handle_state_change_pending(stat);
return;
}
if (stat->intparm == 0) { /* no intparm: unsolicited interrupt */
MESSAGE(KERN_DEBUG,
"unsolicited interrupt: irq 0x%x devno %04x",
irq, stat->devno);
if ((irb->scsw.dstat & mask) == mask) {
dasd_handle_state_change_pending(cdev->dev.driver_data);
return;
}
cqr = (dasd_ccw_req_t *) (unsigned long) stat->intparm;
cqr = (dasd_ccw_req_t *) intparm;
/*
* check status - the request might have been killed
* because of dyn detach
*/
if (cqr->status != DASD_CQR_IN_IO) {
MESSAGE(KERN_DEBUG,
"invalid status: irq 0x%x devno %04x, status %02x",
irq, stat->devno, cqr->status);
"invalid status: bus_id %s, status %02x",
cdev->dev.bus_id, cqr->status);
return;
}
device = (dasd_device_t *) cqr->device;
if (device == NULL ||
device != ds - offsetof(dasd_device_t, dev_status) ||
device->devinfo.irq != irq ||
device != cdev->dev.driver_data ||
strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
MESSAGE(KERN_DEBUG,
"invalid device in request: irq 0x%x devno %04x",
irq, stat->devno);
MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s",
cdev->dev.bus_id);
return;
}
DBF_DEV_EVENT(DBF_DEBUG, device,
"Int: CS/DS 0x%04x", ((stat->cstat << 8) | stat->dstat));
DBF_DEV_EVENT(DBF_DEBUG, device, "Int: CS/DS 0x%04x",
((irb->scsw.cstat << 8) | irb->scsw.dstat));
/* Find out the appropriate era_action. */
era = dasd_era_none;
if (stat->flag & DEVSTAT_FLAG_SENSE_AVAIL ||
stat->dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) {
if (irb->scsw.dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END) ||
irb->esw.esw0.erw.cons) {
/* The request did end abnormally. */
if (stat->flag & DEVSTAT_HALT_FUNCTION)
if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC)
era = dasd_era_fatal;
else
era = device->discipline->examine_error(cqr, stat);
era = device->discipline->examine_error(cqr, irb);
DBF_EVENT(DBF_NOTICE, "era_code %d", era);
}
expires = 0;
......@@ -1440,9 +1067,9 @@ dasd_int_handler(int irq, void *ds, struct pt_regs *regs)
}
} else { /* error */
if (cqr->dstat == NULL)
cqr->dstat = kmalloc(sizeof(devstat_t), GFP_ATOMIC);
cqr->dstat = kmalloc(sizeof(struct irb), GFP_ATOMIC);
if (cqr->dstat)
memcpy(cqr->dstat, stat, sizeof (devstat_t));
memcpy(cqr->dstat, irb, sizeof (struct irb));
else
MESSAGE(KERN_ERR, "%s",
"no memory for dstat...ignoring");
......@@ -1521,7 +1148,7 @@ __dasd_process_ccw_queue(dasd_device_t * device, struct list_head *final_queue)
/* Process requests with DASD_CQR_ERROR */
if (cqr->status == DASD_CQR_ERROR) {
cqr->retries--;
if (cqr->dstat->flag & DEVSTAT_HALT_FUNCTION) {
if (cqr->dstat->scsw.fctl & SCSW_FCTL_HALT_FUNC) {
cqr->status = DASD_CQR_FAILED;
cqr->stopclk = get_clock();
} else {
......@@ -1536,7 +1163,7 @@ __dasd_process_ccw_queue(dasd_device_t * device, struct list_head *final_queue)
goto restart;
}
/* Rechain request on device device request queue */
/* Rechain finished requests to final queue */
cqr->endclk = get_clock();
list_move_tail(&cqr->list, final_queue);
}
......@@ -1595,7 +1222,7 @@ __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->devinfo.devno, req);
device->devno, req);
blkdev_dequeue_request(req);
dasd_end_request(req, 0);
continue;
......@@ -1606,7 +1233,7 @@ __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->devinfo.devno, req);
device->devno, req);
blkdev_dequeue_request(req);
dasd_end_request(req, 0);
continue;
......@@ -1623,7 +1250,7 @@ __dasd_process_blk_queue(dasd_device_t * device)
/*
* Take a look at the first request on the ccw queue and check
* if it reached its expire time.
* if it reached its expire time. If so, terminate the IO.
*/
static inline void
__dasd_check_expire(dasd_device_t * device)
......@@ -1679,7 +1306,7 @@ dasd_flush_ccw_queue(dasd_device_t * device, int all)
dasd_ccw_req_t *cqr;
INIT_LIST_HEAD(&flush_queue);
spin_lock_irq(get_irq_lock(device->devinfo.irq));
spin_lock_irq(get_ccwdev_lock(device->cdev));
list_for_each_safe(l, n, &device->ccw_queue) {
cqr = list_entry(l, dasd_ccw_req_t, list);
/* Flush all request or only block device requests? */
......@@ -1701,7 +1328,7 @@ dasd_flush_ccw_queue(dasd_device_t * device, int all)
cqr->endclk = get_clock();
list_move_tail(&cqr->list, &flush_queue);
}
spin_unlock_irq(get_irq_lock(device->devinfo.irq));
spin_unlock_irq(get_ccwdev_lock(device->cdev));
/* Now call the callback function of flushed requests */
list_for_each_safe(l, n, &flush_queue) {
cqr = list_entry(l, dasd_ccw_req_t, list);
......@@ -1722,12 +1349,12 @@ dasd_tasklet(dasd_device_t * device)
atomic_set (&device->tasklet_scheduled, 0);
INIT_LIST_HEAD(&final_queue);
spin_lock_irq(get_irq_lock(device->devinfo.irq));
spin_lock_irq(get_ccwdev_lock(device->cdev));
/* Check expire time of first request on the ccw queue. */
__dasd_check_expire(device);
/* Finish off requests on ccw queue */
__dasd_process_ccw_queue(device, &final_queue);
spin_unlock_irq(get_irq_lock(device->devinfo.irq));
spin_unlock_irq(get_ccwdev_lock(device->cdev));
/* Now call the callback function of requests with final status */
list_for_each_safe(l, n, &final_queue) {
cqr = list_entry(l, dasd_ccw_req_t, list);
......@@ -1736,19 +1363,19 @@ dasd_tasklet(dasd_device_t * device)
(cqr->callback)(cqr, cqr->callback_data);
}
spin_lock_irq(&device->request_queue_lock);
spin_lock(get_irq_lock(device->devinfo.irq));
spin_lock(get_ccwdev_lock(device->cdev));
/* Get new request from the block device request queue */
__dasd_process_blk_queue(device);
/* Now check if the head of the ccw queue needs to be started. */
__dasd_start_head(device);
spin_unlock(get_irq_lock(device->devinfo.irq));
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);
}
/*
* Schedules a call to dasd_process_queues over the device tasklet.
* Schedules a call to dasd_tasklet over the device tasklet.
*/
void
dasd_schedule_bh(dasd_device_t * device)
......@@ -1771,13 +1398,13 @@ dasd_add_request_head(dasd_ccw_req_t *req)
unsigned long flags;
device = req->device;
spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
req->status = DASD_CQR_QUEUED;
req->device = device;
list_add(&req->list, &device->ccw_queue);
/* let the bh start the request to keep them in order */
dasd_schedule_bh(device);
spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
}
/*
......@@ -1791,13 +1418,13 @@ dasd_add_request_tail(dasd_ccw_req_t *req)
unsigned long flags;
device = req->device;
spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
req->status = DASD_CQR_QUEUED;
req->device = device;
list_add_tail(&req->list, &device->ccw_queue);
/* let the bh start the request to keep them in order */
dasd_schedule_bh(device);
spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
}
/*
......@@ -1816,9 +1443,9 @@ _wait_for_wakeup(dasd_ccw_req_t *cqr)
int rc;
device = cqr->device;
spin_lock_irq(get_irq_lock(device->devinfo.irq));
spin_lock_irq(get_ccwdev_lock(device->cdev));
rc = cqr->status == DASD_CQR_DONE || cqr->status == DASD_CQR_FAILED;
spin_unlock_irq(get_irq_lock(device->devinfo.irq));
spin_unlock_irq(get_ccwdev_lock(device->cdev));
return rc;
}
......@@ -1833,7 +1460,7 @@ dasd_sleep_on(dasd_ccw_req_t * cqr)
int rc;
device = cqr->device;
spin_lock_irq(get_irq_lock(device->devinfo.irq));
spin_lock_irq(get_ccwdev_lock(device->cdev));
init_waitqueue_head (&wait_q);
cqr->callback = dasd_wakeup_cb;
......@@ -1844,7 +1471,7 @@ dasd_sleep_on(dasd_ccw_req_t * cqr)
/* let the bh start the request to keep them in order */
dasd_schedule_bh(device);
spin_unlock_irq(get_irq_lock(device->devinfo.irq));
spin_unlock_irq(get_ccwdev_lock(device->cdev));
wait_event(wait_q, _wait_for_wakeup(cqr));
......@@ -1865,7 +1492,7 @@ dasd_sleep_on_interruptible(dasd_ccw_req_t * cqr)
int rc, finished;
device = cqr->device;
spin_lock_irq(get_irq_lock(device->devinfo.irq));
spin_lock_irq(get_ccwdev_lock(device->cdev));
init_waitqueue_head (&wait_q);
cqr->callback = dasd_wakeup_cb;
......@@ -1875,7 +1502,7 @@ dasd_sleep_on_interruptible(dasd_ccw_req_t * cqr)
/* let the bh start the request to keep them in order */
dasd_schedule_bh(device);
spin_unlock_irq(get_irq_lock(device->devinfo.irq));
spin_unlock_irq(get_ccwdev_lock(device->cdev));
finished = 0;
while (!finished) {
......@@ -1885,13 +1512,13 @@ dasd_sleep_on_interruptible(dasd_ccw_req_t * cqr)
rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0;
break;
}
spin_lock_irq(get_irq_lock(device->devinfo.irq));
spin_lock_irq(get_ccwdev_lock(device->cdev));
if (cqr->status == DASD_CQR_IN_IO &&
device->discipline->term_IO(cqr) == 0) {
list_del(&cqr->list);
finished = 1;
}
spin_unlock_irq(get_irq_lock(device->devinfo.irq));
spin_unlock_irq(get_ccwdev_lock(device->cdev));
}
return rc;
}
......@@ -1928,10 +1555,10 @@ dasd_sleep_on_immediatly(dasd_ccw_req_t * cqr)
int rc;
device = cqr->device;
spin_lock_irq(get_irq_lock(device->devinfo.irq));
spin_lock_irq(get_ccwdev_lock(device->cdev));
rc = _dasd_term_running_cqr(device);
if (rc) {
spin_unlock_irq(get_irq_lock(device->devinfo.irq));
spin_unlock_irq(get_ccwdev_lock(device->cdev));
return rc;
}
......@@ -1944,7 +1571,7 @@ dasd_sleep_on_immediatly(dasd_ccw_req_t * cqr)
/* let the bh start the request to keep them in order */
dasd_schedule_bh(device);
spin_unlock_irq(get_irq_lock(device->devinfo.irq));
spin_unlock_irq(get_ccwdev_lock(device->cdev));
wait_event(wait_q, _wait_for_wakeup(cqr));
......@@ -1966,7 +1593,7 @@ int dasd_cancel_req(dasd_ccw_req_t *cqr)
int rc;
rc = 0;
spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
switch (cqr->status) {
case DASD_CQR_QUEUED:
/* request was not started - just set to failed */
......@@ -1992,7 +1619,7 @@ int dasd_cancel_req(dasd_ccw_req_t *cqr)
BUG();
}
spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
dasd_schedule_bh(device);
return rc;
}
......@@ -2010,12 +1637,12 @@ do_dasd_request(request_queue_t * queue)
dasd_device_t *device;
device = (dasd_device_t *) queue->queuedata;
spin_lock(get_irq_lock(device->devinfo.irq));
spin_lock(get_ccwdev_lock(device->cdev));
/* Get new request from the block device request queue */
__dasd_process_blk_queue(device);
/* Now check if the head of the ccw queue needs to be started. */
__dasd_start_head(device);
spin_unlock(get_irq_lock(device->devinfo.irq));
spin_unlock(get_ccwdev_lock(device->cdev));
}
/*
......@@ -2088,7 +1715,6 @@ dasd_flush_request_queue(dasd_device_t * device)
static int
dasd_open(struct inode *inp, struct file *filp)
{
dasd_devmap_t *devmap;
dasd_device_t *device;
int rc;
......@@ -2098,55 +1724,35 @@ dasd_open(struct inode *inp, struct file *filp)
major(inp->i_rdev), minor(inp->i_rdev));
return -EPERM;
}
devmap = dasd_devmap_from_bdev(inp->i_bdev);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device)) {
MESSAGE(KERN_WARNING,
"No device registered as (%d:%d)",
major(inp->i_rdev), minor(inp->i_rdev));
return PTR_ERR(device);
}
device = inp->i_bdev->bd_disk->private_data;
if (device->state < DASD_STATE_BASIC) {
DBF_DEV_EVENT(DBF_ERR, device, " %s",
" Cannot open unrecognized device");
dasd_put_device(devmap);
return -ENODEV;
}
rc = 0;
spin_lock(&discipline_lock);
if (atomic_inc_return(&device->open_count) == 1 &&
device->discipline->owner != NULL) {
if (atomic_inc_return(&device->open_count) == 1) {
if (!try_inc_mod_count(device->discipline->owner)) {
/* Discipline is currently unloaded! */
atomic_dec(&device->open_count);
rc = -ENODEV;
}
}
spin_unlock(&discipline_lock);
dasd_put_device(devmap);
return rc;
}
static int
dasd_release(struct inode *inp, struct file *filp)
{
dasd_devmap_t *devmap;
dasd_device_t *device;
devmap = dasd_devmap_from_bdev(inp->i_bdev);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device)) {
MESSAGE(KERN_WARNING,
"No device registered as %d:%d",
major(inp->i_rdev), minor(inp->i_rdev));
return -EINVAL;
}
device = inp->i_bdev->bd_disk->private_data;
if (device->state < DASD_STATE_ACCEPT) {
DBF_DEV_EVENT(DBF_ERR, device, " %s",
" Cannot release unrecognized device");
dasd_put_device(devmap);
return -EINVAL;
}
if (atomic_dec_return(&device->open_count) == 0) {
......@@ -2154,7 +1760,6 @@ dasd_release(struct inode *inp, struct file *filp)
if (device->discipline->owner)
__MOD_DEC_USE_COUNT(device->discipline->owner);
}
dasd_put_device(devmap);
return 0;
}
......@@ -2170,18 +1775,6 @@ block_device_operations dasd_device_operations = {
static void
dasd_exit(void)
{
dasd_disable_devices(0, dasd_max_devindex);
#ifdef CONFIG_DASD_DIAG
if (MACHINE_IS_VM)
dasd_diag_cleanup();
#endif
#ifdef CONFIG_DASD_FBA
dasd_fba_cleanup();
#endif
#ifdef CONFIG_DASD_ECKD
dasd_eckd_cleanup();
#endif
#ifdef CONFIG_PROC_FS
dasd_proc_exit();
#endif
......@@ -2196,30 +1789,295 @@ dasd_exit(void)
}
/*
* SECTION: Initializing the driver
* SECTION: common functions for ccw_driver use
*/
static int
dasd_request_module(void *name)
{
int rc = -ERESTARTSYS;
strcpy(current->comm, name);
daemonize();
while (current->fs->root == NULL) /* wait for root-FS */
schedule_timeout(HZ); /* wait a second */
while ((rc = request_module(name)) != 0) {
MESSAGE(KERN_INFO, "request_module returned %d for %s",
rc, (char *) name);
schedule_timeout(5 * HZ); /* wait 5 seconds */
/* initial attempt at a probe function. this can be simplified once
* the other detection code is gone */
int
dasd_generic_probe (struct ccw_device *cdev, dasd_discipline_t *discipline)
{
int devno;
int ret = 0;
snprintf(cdev->dev.name, DEVICE_NAME_SIZE,
"Direct Access Storage Device");
devno = _ccw_device_get_device_number(cdev);
if (dasd_autodetect
&& (ret = dasd_add_range(devno, devno, DASD_FEATURE_DEFAULT))) {
printk (KERN_WARNING
"dasd_generic_probe: cannot autodetect %s\n",
cdev->dev.bus_id);
return ret;
}
if (!ret && (ret = dasd_add_sysfs_files(cdev))) {
printk(KERN_WARNING
"dasd_generic_probe: could not add driverfs entries"
"for %s\n", cdev->dev.bus_id);
}
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;
}
/* this will one day be called from a global not_oper handler.
* It is also used by driver_unregister during module unload */
int
dasd_generic_remove (struct ccw_device *cdev)
{
struct dasd_device_t *device;
device = cdev->dev.driver_data;
cdev->dev.driver_data = NULL;
if (device)
kfree(device);
return 0;
}
/* activate a device. This is called from dasd_{eckd,fba}_probe() when either
* the device is detected for the first time and is supposed to be used
* or the user has started activation through sysfs */
int
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);
return PTR_ERR(device);
}
device->gdp->driverfs_dev = &cdev->dev;
device->cdev = cdev;
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;
}
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);
dasd_put_device(devmap);
return rc;
}
dasd_set_target_state(device, DASD_STATE_ONLINE);
if (device->state <= DASD_STATE_KNOWN) {
printk (KERN_WARNING
"dasd_generic discipline not found for %s\n",
cdev->dev.bus_id);
rc = -ENODEV;
dasd_set_target_state(device, DASD_STATE_NEW);
} 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;
}
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);
dasd_set_target_state(device, DASD_STATE_NEW);
dasd_put_device(devmap);
return 0;
}
/*
* SECTION: files in sysfs
*/
/*
* readonly controls the readonly status of a dasd
*/
static ssize_t
dasd_ro_show(struct device *dev, char *buf, size_t count, loff_t off)
{
dasd_device_t *device;
if (off)
return 0;
device = dev->driver_data;
if (!device)
return snprintf(buf, count, "n/a\n");
return snprintf(buf, count, device->ro_flag ? "1\n" : "0\n");
}
static ssize_t
dasd_ro_store(struct device *dev, const char *buf, size_t count, loff_t off)
{
dasd_device_t *device = dev->driver_data;
if (off)
return 0;
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, size_t count, loff_t off)
{
dasd_device_t *device;
if (off)
return 0;
device = dev->driver_data;
if (!device)
return snprintf(buf, count, "n/a\n");
return snprintf(buf, count, device->use_diag_flag ? "1\n" : "0\n");
}
static ssize_t
dasd_use_diag_store(struct device *dev, const char *buf,
size_t count, loff_t off)
{
dasd_device_t *device = dev->driver_data;
if (off)
return 0;
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);
#if 0
/* this file shows the same information as /proc/dasd/devices using
* an inaccaptable interface */
/* TODO: Split this up into smaller files! */
static ssize_t
dasd_devices_show(struct device *dev, char *buf, size_t count, loff_t off)
{
dasd_device_t *device;
dasd_devmap_t *devmap;
if (off) /* ignore partial write */
return 0;
devmap = NULL;
device = dev->driver_data;
if (device)
devmap = dasd_devmap_from_devno(device->devno);
if (!devmap)
return snprintf(buf, count, "unused\n");
return min ((size_t) dasd_devices_print(devmap, buf), count);
}
static DEVICE_ATTR(dasd, 0444, dasd_devices_show, 0);
#endif
static ssize_t
dasd_discipline_show(struct device *dev, char *buf, size_t count, loff_t off)
{
dasd_device_t *device;
if (off)
return 0;
device = dev->driver_data;
if (!device || !device->discipline)
return snprintf(buf, count, "none\n");
return snprintf(buf, count, "%s\n", device->discipline->name);
}
static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, 0);
static int
dasd_add_sysfs_files(struct ccw_device *cdev)
{
int ret;
if (/* (ret = device_create_file(&cdev->dev, &dev_attr_dasd)) || */
(ret = device_create_file(&cdev->dev, &dev_attr_readonly)) ||
(ret = device_create_file(&cdev->dev, &dev_attr_discipline)) ||
(ret = device_create_file(&cdev->dev, &dev_attr_use_diag))) {
device_remove_file(&cdev->dev, &dev_attr_discipline);
device_remove_file(&cdev->dev, &dev_attr_readonly);
/* device_remove_file(&cdev->dev, &dev_attr_dasd); */
}
return ret;
}
static int __init
dasd_init(void)
{
char **disc;
int irq, devno;
int rc;
init_waitqueue_head(&dasd_init_waitq);
......@@ -2235,7 +2093,7 @@ dasd_init(void)
DBF_EVENT(DBF_EMERG, "%s", "debug area created");
if (!devfs_mk_dir(NULL, "dasd", NULL)) {
if (devfs_mk_dir(NULL, "dasd", NULL)) {
DBF_EVENT(DBF_ALERT, "%s", "no devfs");
rc = -ENOSYS;
goto failed;
......@@ -2258,60 +2116,6 @@ dasd_init(void)
goto failed;
#endif
if (dasd_autodetect) {
/* update device range to all devices */
irq = get_irq_first();
while (irq != -ENODEV) {
devno = get_devno_by_irq(irq);
if (dasd_devmap_from_devno(devno) == NULL) {
/* devno not included yet */
DBF_EVENT(DBF_NOTICE, "add %04x to range",
devno);
dasd_add_range(devno, devno,
DASD_FEATURE_DEFAULT);
}
irq = get_irq_next(irq);
}
}
if (MACHINE_IS_VM) {
#ifdef CONFIG_DASD_DIAG
rc = dasd_diag_init();
if (rc != 0) {
DBF_EVENT(DBF_ALERT, "%s",
"Register DIAG discipline failed");
goto failed;
}
#endif /* CONFIG_DASD_DIAG */
#if defined(CONFIG_DASD_DIAG_MODULE) && defined(CONFIG_DASD_AUTO_DIAG)
kernel_thread(dasd_request_module, "dasd_diag_mod", SIGCHLD);
#endif
}
#ifdef CONFIG_DASD_ECKD
rc = dasd_eckd_init();
if (rc != 0) {
DBF_EVENT(DBF_ALERT, "%s", "Register ECKD discipline failed");
goto failed;
}
#endif /* CONFIG_DASD_ECKD */
#if defined(CONFIG_DASD_ECKD_MODULE) && defined(CONFIG_DASD_AUTO_ECKD)
kernel_thread(dasd_request_module, "dasd_eckd_mod", SIGCHLD);
#endif
#ifdef CONFIG_DASD_FBA
rc = dasd_fba_init();
if (rc != 0) {
DBF_EVENT(DBF_ALERT, "%s", "Register FBA discipline failed");
goto failed;
}
#endif
#if defined(CONFIG_DASD_FBA_MODULE) && defined(CONFIG_DASD_AUTO_FBA)
kernel_thread(dasd_request_module, "dasd_fba_mod", SIGCHLD);
#endif /* CONFIG_DASD_AUTO_FBA */
disc = dasd_disciplines;
while (*disc) {
kernel_thread(dasd_request_module, *disc, SIGCHLD);
disc++;
}
return 0;
failed:
MESSAGE(KERN_INFO, "%s", "initialization not performed due to errors");
......@@ -2328,16 +2132,11 @@ EXPORT_SYMBOL(dasd_add_request_head);
EXPORT_SYMBOL(dasd_add_request_tail);
EXPORT_SYMBOL(dasd_cancel_req);
EXPORT_SYMBOL(dasd_clear_timer);
EXPORT_SYMBOL(dasd_disable_devices);
EXPORT_SYMBOL(dasd_discipline_add);
EXPORT_SYMBOL(dasd_discipline_del);
EXPORT_SYMBOL(dasd_enable_devices);
EXPORT_SYMBOL(dasd_enable_device);
EXPORT_SYMBOL(dasd_int_handler);
EXPORT_SYMBOL(dasd_kfree_request);
EXPORT_SYMBOL(dasd_kick_device);
EXPORT_SYMBOL(dasd_kmalloc_request);
EXPORT_SYMBOL(dasd_not_oper_handler);
EXPORT_SYMBOL(dasd_oper_handler);
EXPORT_SYMBOL(dasd_schedule_bh);
EXPORT_SYMBOL(dasd_set_target_state);
EXPORT_SYMBOL(dasd_set_timer);
......
......@@ -3,6 +3,11 @@
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
*
* $Revision: 1.7 $
*
* History of changes
*
*/
#define PRINTK_HEADER "dasd_erp(3370)"
......@@ -27,13 +32,13 @@
* dasd_era_recover for all others.
*/
dasd_era_t
dasd_3370_erp_examine(dasd_ccw_req_t * cqr, devstat_t * stat)
dasd_3370_erp_examine(dasd_ccw_req_t * cqr, struct irb * irb)
{
char *sense = stat->ii.sense.data;
char *sense = irb->ecw;
/* check for successful execution first */
if (stat->cstat == 0x00 &&
stat->dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
if (irb->scsw.cstat == 0x00 &&
irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
return dasd_era_none;
if (sense[0] & 0x80) { /* CMD reject */
return dasd_era_fatal;
......
/*
* File...........: linux/drivers/s390/block/dasd_3370_erp.h
* Author(s)......: Horst Hummel <Horst Hummel@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
*
* History of changes (starts July 2000)
*/
#ifndef DASD_3370_ERP_H
#define DASD_3370_ERP_H
dasd_era_t dasd_3370_erp_examine(dasd_ccw_req_t *, devstat_t *);
#endif /* DASD_3990_ERP_H */
......@@ -5,6 +5,8 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000, 2001
*
* $Revision: 1.19 $
*
* History of changes:
* 05/14/01 fixed PL030160GTO (BUG() in erp_action_5)
* 05/04/02 code restructuring.
......@@ -13,7 +15,6 @@
#include <linux/timer.h>
#include <linux/slab.h>
#include <asm/idals.h>
#include <asm/s390io.h>
#include <asm/todclk.h>
#define PRINTK_HEADER "dasd_erp(3990): "
......@@ -148,16 +149,16 @@ dasd_3990_erp_examine_32(dasd_ccw_req_t * cqr, char *sense)
* dasd_era_recover for all others.
*/
dasd_era_t
dasd_3990_erp_examine(dasd_ccw_req_t * cqr, devstat_t * stat)
dasd_3990_erp_examine(dasd_ccw_req_t * cqr, struct irb * irb)
{
char *sense = stat->ii.sense.data;
char *sense = irb->ecw;
dasd_era_t era = dasd_era_recover;
dasd_device_t *device = cqr->device;
/* check for successful execution first */
if (stat->cstat == 0x00 &&
stat->dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
if (irb->scsw.cstat == 0x00 &&
irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
return dasd_era_none;
/* distinguish between 24 and 32 byte sense data */
......@@ -172,8 +173,10 @@ dasd_3990_erp_examine(dasd_ccw_req_t * cqr, devstat_t * stat)
}
/* log the erp chain if fatal error occurred */
if ((era == dasd_era_fatal) && (device->state >= DASD_STATE_READY))
dasd_log_ccw(cqr, 0, stat->cpa);
if ((era == dasd_era_fatal) && (device->state >= DASD_STATE_READY)) {
dasd_log_sense(cqr, irb);
dasd_log_ccw(cqr, 0, irb->scsw.cpa);
}
return era;
......@@ -296,42 +299,39 @@ dasd_3990_erp_int_req(dasd_ccw_req_t * erp)
*
* RETURN VALUES
* erp modified pointer to the ERP
*
*/
static void
dasd_3990_erp_alternate_path(dasd_ccw_req_t * erp)
{
dasd_device_t *device = erp->device;
int irq = device->devinfo.irq;
__u8 opm;
/* try alternate valid path */
erp->lpm &= ~(erp->dstat->lpum);
erp->options |= DOIO_VALID_LPM; /* use LPM for DO_IO */
opm = ccw_device_get_path_mask(device->cdev);
//FIXME: start with get_opm ?
if (erp->lpm == 0)
erp->lpm = LPM_ANYPATH & ~(erp->dstat->esw.esw0.sublog.lpum);
else
erp->lpm &= ~(erp->dstat->esw.esw0.sublog.lpum);
if ((erp->lpm & ioinfo[irq]->opm) != 0x00) {
if ((erp->lpm & opm) != 0x00) {
DEV_MESSAGE(KERN_DEBUG, device,
"try alternate lpm=%x (lpum=%x / opm=%x)",
erp->lpm, erp->dstat->lpum, ioinfo[irq]->opm);
erp->lpm, erp->dstat->esw.esw0.sublog.lpum, opm);
/* reset status to queued to handle the request again... */
erp->status = DASD_CQR_QUEUED;
erp->retries = 1;
} else {
DEV_MESSAGE(KERN_ERR, device,
"No alternate channel path left (lpum=%x / "
"opm=%x) -> permanent error",
erp->dstat->lpum, ioinfo[irq]->opm);
erp->dstat->esw.esw0.sublog.lpum, opm);
/* post request with permanent error */
erp->status = DASD_CQR_FAILED;
}
} /* end dasd_3990_erp_alternate_path */
/*
......@@ -355,7 +355,7 @@ dasd_3990_erp_DCTL(dasd_ccw_req_t * erp, char modifier)
dasd_device_t *device = erp->device;
DCTL_data_t *DCTL_data;
ccw1_t *ccw;
struct ccw1 *ccw;
dasd_ccw_req_t *dctl_cqr;
dctl_cqr = dasd_alloc_erp_request((char *) &erp->magic, 1,
......@@ -373,7 +373,7 @@ dasd_3990_erp_DCTL(dasd_ccw_req_t * erp, char modifier)
DCTL_data->modifier = modifier;
ccw = dctl_cqr->cpaddr;
memset(ccw, 0, sizeof (ccw1_t));
memset(ccw, 0, sizeof (struct ccw1));
ccw->cmd_code = CCW_CMD_DCTL;
ccw->count = 4;
ccw->cda = (__u32)(addr_t) DCTL_data;
......@@ -381,7 +381,6 @@ dasd_3990_erp_DCTL(dasd_ccw_req_t * erp, char modifier)
dctl_cqr->refers = erp;
dctl_cqr->device = erp->device;
dctl_cqr->magic = erp->magic;
dctl_cqr->lpm = LPM_ANYPATH;
dctl_cqr->expires = 5 * 60 * HZ;
dctl_cqr->retries = 2;
......@@ -1122,6 +1121,9 @@ dasd_3990_handle_env_data(dasd_ccw_req_t * erp, char *sense)
break;
default: /* unknown message format - should not happen */
DEV_MESSAGE (KERN_WARNING, device,
"unknown message format %02x",
msg_format);
break;
} /* end switch message format */
......@@ -1650,7 +1652,7 @@ dasd_3990_erp_action_1B_32(dasd_ccw_req_t * default_erp, char *sense)
dasd_ccw_req_t *erp;
DE_eckd_data_t *DE_data;
char *LO_data; /* LO_eckd_data_t */
ccw1_t *ccw;
struct ccw1 *ccw;
DEV_MESSAGE(KERN_DEBUG, device, "%s",
"Write not finished because of unexpected condition");
......@@ -1675,7 +1677,7 @@ dasd_3990_erp_action_1B_32(dasd_ccw_req_t * default_erp, char *sense)
/* determine the address of the CCW to be restarted */
/* Imprecise ending is not set -> addr from IRB-SCSW */
cpa = default_erp->refers->dstat->cpa;
cpa = default_erp->refers->dstat->scsw.cpa;
if (cpa == 0) {
......@@ -1735,7 +1737,7 @@ dasd_3990_erp_action_1B_32(dasd_ccw_req_t * default_erp, char *sense)
/* create DE ccw */
ccw = erp->cpaddr;
memset(ccw, 0, sizeof (ccw1_t));
memset(ccw, 0, sizeof (struct ccw1));
ccw->cmd_code = DASD_ECKD_CCW_DEFINE_EXTENT;
ccw->flags = CCW_FLAG_CC;
ccw->count = 16;
......@@ -1743,7 +1745,7 @@ dasd_3990_erp_action_1B_32(dasd_ccw_req_t * default_erp, char *sense)
/* create LO ccw */
ccw++;
memset(ccw, 0, sizeof (ccw1_t));
memset(ccw, 0, sizeof (struct ccw1));
ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD;
ccw->flags = CCW_FLAG_CC;
ccw->count = 16;
......@@ -1759,7 +1761,6 @@ dasd_3990_erp_action_1B_32(dasd_ccw_req_t * default_erp, char *sense)
erp->refers = default_erp->refers;
erp->device = device;
erp->magic = default_erp->magic;
erp->lpm = 0xFF;
erp->expires = 0;
erp->retries = 256;
erp->status = DASD_CQR_FILLED;
......@@ -1795,7 +1796,7 @@ dasd_3990_update_1B(dasd_ccw_req_t * previous_erp, char *sense)
dasd_ccw_req_t *cqr;
dasd_ccw_req_t *erp;
char *LO_data; /* LO_eckd_data_t */
ccw1_t *ccw;
struct ccw1 *ccw;
DEV_MESSAGE(KERN_DEBUG, device, "%s",
"Write not finished because of unexpected condition"
......@@ -1821,7 +1822,7 @@ dasd_3990_update_1B(dasd_ccw_req_t * previous_erp, char *sense)
/* determine the address of the CCW to be restarted */
/* Imprecise ending is not set -> addr from IRB-SCSW */
cpa = previous_erp->dstat->cpa;
cpa = previous_erp->dstat->scsw.cpa;
if (cpa == 0) {
......@@ -1954,7 +1955,7 @@ dasd_3990_erp_compound_path(dasd_ccw_req_t * erp, char *sense)
/* reset the lpm and the status to be able to
* try further actions. */
erp->lpm = LPM_ANYPATH;
erp->lpm = 0;
erp->status = DASD_CQR_ERROR;
......@@ -2234,7 +2235,7 @@ dasd_3990_erp_inspect(dasd_ccw_req_t * erp)
dasd_ccw_req_t *erp_new = NULL;
/* sense data are located in the refers record of the */
/* already set up new ERP ! */
char *sense = erp->refers->dstat->ii.sense.data;
char *sense = erp->refers->dstat->ecw;
/* distinguish between 24 and 32 byte sense data */
if (sense[27] & DASD_SENSE_BIT_0) {
......@@ -2291,7 +2292,6 @@ dasd_3990_erp_add_erp(dasd_ccw_req_t * cqr)
erp->refers = cqr;
erp->device = cqr->device;
erp->magic = cqr->magic;
erp->lpm = 0xFF;
erp->expires = 0;
erp->retries = 256;
......@@ -2357,18 +2357,14 @@ dasd_3990_erp_error_match(dasd_ccw_req_t * cqr1, dasd_ccw_req_t * cqr2)
{
/* check failed CCW */
if (cqr1->dstat->cpa != cqr2->dstat->cpa) {
if (cqr1->dstat->scsw.cpa != cqr2->dstat->scsw.cpa) {
// return 0; /* CCW doesn't match */
}
/* check sense data; byte 0-2,25,27 */
if (!((strncmp(cqr1->dstat->ii.sense.data,
cqr2->dstat->ii.sense.data,
3) == 0) &&
(cqr1->dstat->ii.sense.data[27] ==
cqr2->dstat->ii.sense.data[27]) &&
(cqr1->dstat->ii.sense.data[25] ==
cqr2->dstat->ii.sense.data[25]))) {
if (!((strncmp(cqr1->dstat->ecw, cqr2->dstat->ecw, 3) == 0) &&
(cqr1->dstat->ecw[27] == cqr2->dstat->ecw[27]) &&
(cqr1->dstat->ecw[25] == cqr2->dstat->ecw[25]))) {
return 0; /* sense doesn't match */
}
......@@ -2441,7 +2437,7 @@ dasd_3990_erp_further_erp(dasd_ccw_req_t * erp)
{
dasd_device_t *device = erp->device;
char *sense = erp->dstat->ii.sense.data;
char *sense = erp->dstat->ecw;
/* check for 24 byte sense ERP */
if ((erp->function == dasd_3990_erp_bus_out) ||
......@@ -2553,7 +2549,7 @@ dasd_3990_erp_handle_match_erp(dasd_ccw_req_t * erp_head, dasd_ccw_req_t * erp)
if (erp->retries > 0) {
char *sense = erp->dstat->ii.sense.data;
char *sense = erp->dstat->ecw;
/* check for special retries */
if (erp->function == dasd_3990_erp_action_4) {
......@@ -2611,7 +2607,7 @@ dasd_3990_erp_action(dasd_ccw_req_t * cqr)
dasd_ccw_req_t *erp = NULL;
dasd_device_t *device = cqr->device;
__u32 cpa = cqr->dstat->cpa;
__u32 cpa = cqr->dstat->scsw.cpa;
#ifdef ERP_DEBUG
/* print current erp_chain */
......@@ -2631,8 +2627,8 @@ dasd_3990_erp_action(dasd_ccw_req_t * cqr)
#endif /* ERP_DEBUG */
/* double-check if current erp/cqr was successfull */
if ((cqr->dstat->cstat == 0x00) &&
(cqr->dstat->dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))) {
if ((cqr->dstat->scsw.cstat == 0x00) &&
(cqr->dstat->scsw.dstat == (DEV_STAT_CHN_END|DEV_STAT_DEV_END))) {
DEV_MESSAGE(KERN_DEBUG, device,
"ERP called for successful request %p"
......@@ -2643,7 +2639,7 @@ dasd_3990_erp_action(dasd_ccw_req_t * cqr)
return cqr;
}
/* check if sense data are available */
if (!cqr->dstat->ii.sense.data) {
if (!cqr->dstat->ecw) {
DEV_MESSAGE(KERN_DEBUG, device,
"ERP called witout sense data avail ..."
"request %p - NO ERP possible", cqr);
......
/*
* File...........: linux/drivers/s390/block/dasd_3990_erp.h
* Author(s)......: Horst Hummel <Horst Hummel@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
*
* History of changes (starts July 2000)
*/
#ifndef DASD_3990_ERP_H
#define DASD_3990_ERP_H
dasd_era_t dasd_3990_erp_examine(dasd_ccw_req_t *, devstat_t *);
dasd_ccw_req_t *dasd_3990_erp_action(dasd_ccw_req_t *);
dasd_ccw_req_t *dasd_2105_erp_action(ccw_req_t *);
void dasd_3990_erp_restart_queue(unsigned long);
typedef struct DCTL_data_t {
unsigned char subcommand; /* e.g Inhibit Write, Enable Write,... */
unsigned char modifier; /* Subcommand modifier */
unsigned short res; /* reserved */
} __attribute__ ((packed)) DCTL_data_t;
#endif /* DASD_3990_ERP_H */
......@@ -3,6 +3,11 @@
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
*
* $Revision: 1.6 $
*
* History of changes
*
*/
#define PRINTK_HEADER "dasd_erp(9336)"
......@@ -27,11 +32,11 @@
* dasd_era_recover for all others.
*/
dasd_era_t
dasd_9336_erp_examine(dasd_ccw_req_t * cqr, devstat_t * stat)
dasd_9336_erp_examine(dasd_ccw_req_t * cqr, struct irb * irb)
{
/* check for successful execution first */
if (stat->cstat == 0x00 &&
stat->dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
if (irb->scsw.cstat == 0x00 &&
irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
return dasd_era_none;
/* examine the 24 byte sense data */
......
/*
* File...........: linux/drivers/s390/block/dasd_9336_erp.h
* Author(s)......: Horst Hummel <Horst Hummel@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
*
* History of changes (starts July 2000)
*/
#ifndef DASD_9336_ERP_H
#define DASD_9336_ERP_H
dasd_era_t dasd_9336_erp_examine(dasd_ccw_req_t *, devstat_t *);
#endif /* DASD_3990_ERP_H */
/*
* File...........: linux/drivers/s390/block/dasd_9345_erp.h
* File...........: linux/drivers/s390/block/dasd_9345_erp.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
*
* $Revision: 1.11 $
*
* History of changes
*
*/
#define PRINTK_HEADER "dasd_erp(9343)"
......@@ -10,10 +15,11 @@
#include "dasd_int.h"
dasd_era_t
dasd_9343_erp_examine(dasd_ccw_req_t * cqr, devstat_t * stat)
dasd_9343_erp_examine(dasd_ccw_req_t * cqr, struct irb * irb)
{
if (stat->cstat == 0x00 &&
stat->dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
if (irb->scsw.cstat == 0x00 &&
irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
return dasd_era_none;
return dasd_era_recover;
}
/*
* File...........: linux/drivers/s390/block/dasd_9343_erp.h
* Author(s)......: Horst Hummel <Horst Hummel@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
*
* History of changes (starts July 2000)
*/
#ifndef DASD_9343_ERP_H
#define DASD_9343_ERP_H
dasd_era_t dasd_9343_erp_examine(dasd_ccw_req_t *, devstat_t *);
dasd_ccw_req_t *dasd_9343_erp_action(dasd_ccw_req_t *);
#endif /* DASD_9343_ERP_H */
......@@ -11,6 +11,9 @@
* functions may not be called from interrupt context. In particular
* dasd_get_device is a no-no from interrupt context.
*
* $Revision: 1.11 $
*
* History of changes
* 05/04/02 split from dasd.c, code restructuring.
*/
......@@ -20,7 +23,6 @@
#include <linux/init.h>
#include <asm/debug.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
/* This is ugly... */
......@@ -84,9 +86,11 @@ __setup ("dasd=", dasd_call_setup);
* Read a device number from a string. The number is always in hex,
* a leading 0x is accepted.
*/
int
static inline int
dasd_devno(char *str, char **endp)
{
int val;
/* remove leading '0x' */
if (*str == '0') {
str++;
......@@ -96,7 +100,10 @@ dasd_devno(char *str, char **endp)
/* We require at least one hex digit */
if (!isxdigit(*str))
return -EINVAL;
return simple_strtoul(str, endp, 16);
val = simple_strtoul(str, endp, 16);
if ((val > 0xFFFF) || (val < 0))
return -EINVAL;
return val;
}
/*
......@@ -104,7 +111,7 @@ dasd_devno(char *str, char **endp)
* only one: "ro" for read-only devices. The default feature set
* is empty (value 0).
*/
int
static inline int
dasd_feature_list(char *str, char **endp)
{
int features, len, rc;
......@@ -260,64 +267,12 @@ dasd_add_range(int from, int to, int features)
devmap->devindex = devindex;
devmap->devno = devno;
devmap->features = features;
devmap->devreg = NULL;
devmap->device = NULL;
list_add(&devmap->devindex_list,
&dasd_devindex_hashlists[devindex & 255]);
list_add(&devmap->devno_list,
&dasd_devno_hashlists[devno & 255]);
}
if (devmap->devreg == NULL) {
/* The devreg is missing. */
devmap->devreg = (devreg_t *)
kmalloc(sizeof(devreg_t), GFP_KERNEL);
if (devmap->devreg == NULL)
return -ENOMEM;
memset(devmap->devreg, sizeof(devreg_t), 0);
devmap->devreg->ci.devno = devno;
devmap->devreg->flag = DEVREG_TYPE_DEVNO;
devmap->devreg->oper_func = dasd_oper_handler;
s390_device_register(devmap->devreg);
}
}
spin_unlock(&dasd_devmap_lock);
return 0;
}
/*
* Removes the devreg_t structures for a range of devices. This does
* NOT remove the range itself. The mapping between devno and kdevs
* for the devices is remembered until dasd_forget_ranges() is called.
*/
static int
dasd_clear_range(int from, int to)
{
int devno;
if (from > to) {
MESSAGE(KERN_ERR,
"Invalid device range %04x-%04x", from, to);
return -EINVAL;
}
spin_lock(&dasd_devmap_lock);
for (devno = from; devno <= to; devno++) {
struct list_head *l;
dasd_devmap_t *devmap = NULL;
/* Find previous devmap for device number i */
list_for_each(l, &dasd_devno_hashlists[devno & 255]) {
devmap = list_entry(l, dasd_devmap_t, devno_list);
if (devmap->devno == devno)
break;
}
if (devmap == NULL)
continue;
if (devmap->device != NULL)
BUG();
if (devmap->devreg == NULL)
continue;
s390_device_unregister(devmap->devreg);
kfree(devmap->devreg);
devmap->devreg = NULL;
}
spin_unlock(&dasd_devmap_lock);
return 0;
......@@ -340,11 +295,6 @@ dasd_forget_ranges(void)
devmap = list_entry(l, dasd_devmap_t, devno_list);
if (devmap->device != NULL)
BUG();
if (devmap->devreg != NULL) {
s390_device_unregister(devmap->devreg);
kfree(devmap->devreg);
devmap->devreg = NULL;
}
list_del(&devmap->devindex_list);
list_del(&devmap->devno_list);
kfree(devmap);
......@@ -403,52 +353,6 @@ dasd_devmap_from_devindex(int devindex)
return devmap;
}
/*
* Find the devmap for a device by its irq line.
*/
dasd_devmap_t *
dasd_devmap_from_irq(int irq)
{
struct list_head *l;
dasd_devmap_t *devmap, *tmp;
int i;
devmap = NULL;
spin_lock(&dasd_devmap_lock);
for (i = 0; (i < 256) && (devmap == NULL); i++) {
list_for_each(l, &dasd_devno_hashlists[i & 255]) {
tmp = list_entry(l, dasd_devmap_t, devno_list);
if (tmp->device != NULL &&
tmp->device->devinfo.irq == irq) {
devmap = tmp;
break;
}
}
}
spin_unlock(&dasd_devmap_lock);
return devmap;
}
/*
* Find the devmap for a device corresponding to a block_device.
*/
dasd_devmap_t *
dasd_devmap_from_bdev(struct block_device *bdev)
{
kdev_t kdev = to_kdev_t(bdev->bd_dev);
int devindex;
/* Find the devindex for kdev. */
devindex = dasd_gendisk_major_index(major(kdev));
if (devindex < 0)
/* No such major -> no devmap */
return NULL;
devindex += minor(kdev) >> DASD_PARTN_BITS;
/* Now find the devmap by the devindex. */
return dasd_devmap_from_devindex(devindex);
}
/*
* Find the device structure for device number devno. If it does not
* exists yet, allocate it. Increase the reference counter in the device
......@@ -479,6 +383,8 @@ dasd_get_device(dasd_devmap_t *devmap)
} else
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;
}
......@@ -523,9 +429,3 @@ dasd_devmap_exit(void)
{
dasd_forget_ranges();
}
EXPORT_SYMBOL(dasd_devmap_from_devno);
EXPORT_SYMBOL(dasd_devmap_from_devindex);
EXPORT_SYMBOL(dasd_devmap_from_irq);
EXPORT_SYMBOL(dasd_get_device);
EXPORT_SYMBOL(dasd_put_device);
......@@ -5,7 +5,9 @@
* ...............: by Hartmunt Penner <hpenner@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
* $Revision: 1.27 $
*
* History of changes
* 07/13/00 Added fixup sections for diagnoses ans saved some registers
* 07/14/00 fixed constraints in newly generated inline asm
......@@ -22,13 +24,13 @@
#include <linux/slab.h>
#include <linux/hdreg.h> /* HDIO_GETGEO */
#include <linux/bio.h>
#include <linux/module.h>
#include <linux/init.h>
#include <asm/dasd.h>
#include <asm/debug.h>
#include <asm/ebcdic.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/s390dyn.h>
#include <asm/s390_ext.h>
#include <asm/todclk.h>
......@@ -40,7 +42,7 @@
#endif /* PRINTK_HEADER */
#define PRINTK_HEADER "dasd(diag):"
static dasd_discipline_t dasd_diag_discipline;
MODULE_LICENSE("GPL");
typedef struct dasd_diag_private_t {
dasd_diag_characteristics_t rdc_data;
......@@ -57,25 +59,30 @@ typedef struct dasd_diag_req_t {
static __inline__ int
dia250(void *iob, int cmd)
{
__asm__ __volatile__(" lr 0,%1\n"
" diag 0,%0,0x250\n"
int rc;
__asm__ __volatile__(" lhi %0,3\n"
" lr 0,%2\n"
" diag 0,%1,0x250\n"
"0: ipm %0\n"
" srl %0,28\n"
" or %0,1\n"
"1:\n"
".section .fixup,\"ax\"\n"
"2: lhi %0,3\n"
" bras 1,3f\n"
" .long 1b\n"
"3: l 1,0(1)\n"
" br 1\n"
".previous\n"
#ifndef CONFIG_ARCH_S390X
".section __ex_table,\"a\"\n"
" .align 4\n"
" .long 0b,2b\n" ".previous\n":"+d"(cmd)
:"d"((void *) __pa(iob))
:"0", "1", "cc");
return cmd;
" .long 0b,1b\n"
".previous\n"
#else
".section __ex_table,\"a\"\n"
" .align 8\n"
" .quad 0b,1b\n"
".previous\n"
#endif
: "=&d" (rc)
: "d" (cmd), "d" ((void *) __pa(iob))
: "0", "1", "cc");
return rc;
}
static __inline__ int
......@@ -89,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->devinfo.devno;
iib->dev_nr = device->devno;
iib->block_size = blocksize;
iib->offset = offset;
iib->start_block = 0;
......@@ -110,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->devinfo.devno;
iib->dev_nr = device->devno;
rc = dia250(iib, TERM_BIO);
return rc & 3;
}
......@@ -127,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->devinfo.devno;
private->iob.dev_nr = device->devno;
private->iob.key = 0;
private->iob.flags = 2; /* do asynchronous io */
private->iob.block_count = dreq->block_count;
......@@ -191,7 +198,7 @@ dasd_ext_handler(struct pt_regs *regs, __u16 code)
}
/* get irq lock to modify request queue */
spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
cqr->stopclk = get_clock();
......@@ -219,7 +226,7 @@ dasd_ext_handler(struct pt_regs *regs, __u16 code)
dasd_clear_timer(device);
dasd_schedule_bh(device);
spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
irq_exit();
}
......@@ -245,10 +252,10 @@ dasd_diag_check_device(dasd_device_t *device)
}
/* Read Device Characteristics */
rdc_data = (void *) &(private->rdc_data);
rdc_data->dev_nr = device->devinfo.devno;
rdc_data->dev_nr = device->devno;
rdc_data->rdc_len = sizeof (dasd_diag_characteristics_t);
rc = diag210((diag210_t *) rdc_data);
rc = diag210((struct diag210 *) rdc_data);
if (rc)
return -ENOTSUPP;
......@@ -328,7 +335,7 @@ dasd_diag_fill_geometry(dasd_device_t *device, struct hd_geometry *geo)
}
static dasd_era_t
dasd_diag_examine_error(dasd_ccw_req_t * cqr, devstat_t * stat)
dasd_diag_examine_error(dasd_ccw_req_t * cqr, struct irb * stat)
{
return dasd_era_fatal;
}
......@@ -433,7 +440,8 @@ dasd_diag_fill_info(dasd_device_t * device, dasd_information2_t * info)
}
static void
dasd_diag_dump_sense(dasd_device_t *device, dasd_ccw_req_t * req)
dasd_diag_dump_sense(dasd_device_t *device, dasd_ccw_req_t * req,
struct irb *stat)
{
char *page;
......@@ -443,8 +451,8 @@ dasd_diag_dump_sense(dasd_device_t *device, dasd_ccw_req_t * req)
return;
}
sprintf(page, KERN_WARNING PRINTK_HEADER
"device %04X on irq %d: I/O status report:\n",
device->devinfo.devno, device->devinfo.irq);
"device %s: I/O status report:\n",
device->cdev->dev.bus_id);
MESSAGE(KERN_ERR, "Sense data:\n%s", page);
......@@ -463,7 +471,7 @@ dasd_diag_dump_sense(dasd_device_t *device, dasd_ccw_req_t * req)
* start the next request if one finishes off. That makes 252.75 blocks
* for one request. Give a little safety and the result is 240.
*/
static dasd_discipline_t dasd_diag_discipline = {
dasd_discipline_t dasd_diag_discipline = {
.owner = THIS_MODULE,
.name = "DIAG",
.ebcname = "DIAG",
......@@ -479,7 +487,7 @@ static dasd_discipline_t dasd_diag_discipline = {
.fill_info = dasd_diag_fill_info,
};
int
static int __init
dasd_diag_init(void)
{
if (!MACHINE_IS_VM) {
......@@ -489,13 +497,13 @@ dasd_diag_init(void)
return -EINVAL;
}
ASCEBC(dasd_diag_discipline.ebcname, 4);
ctl_set_bit(0, 9);
register_external_interrupt(0x2603, dasd_ext_handler);
dasd_discipline_add(&dasd_diag_discipline);
return 0;
}
void
static void __exit
dasd_diag_cleanup(void)
{
if (!MACHINE_IS_VM) {
......@@ -504,24 +512,12 @@ dasd_diag_cleanup(void)
dasd_diag_discipline.name);
return;
}
dasd_discipline_del(&dasd_diag_discipline);
unregister_external_interrupt(0x2603, dasd_ext_handler);
ctl_clear_bit(0, 9);
}
#ifdef MODULE
int
init_module(void)
{
return dasd_diag_init();
}
void
cleanup_module(void)
{
dasd_diag_cleanup();
}
#endif
module_init(dasd_diag_init);
module_exit(dasd_diag_cleanup);
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
......
......@@ -5,6 +5,11 @@
* ...............: by Hartmunt Penner <hpenner@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
* $Revision: 1.4 $
*
* History of changes
*
*/
#define MDSK_WRITE_REQ 0x01
......@@ -70,5 +75,3 @@ typedef struct diag_rw_io_t {
diag_rw_io_t;
int dasd_diag_init(void);
void dasd_diag_cleanup(void);
......@@ -7,6 +7,8 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
* $Revision: 1.35 $
*
* History of changes (starts July 2000)
* 07/11/00 Enabled rotational position sensing
* 07/14/00 Reorganized the format process for better ERP
......@@ -30,15 +32,16 @@
#include <linux/slab.h>
#include <linux/hdreg.h> /* HDIO_GETGEO */
#include <linux/bio.h>
#include <linux/module.h>
#include <linux/init.h>
#include <asm/debug.h>
#include <asm/idals.h>
#include <asm/ebcdic.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/s390dyn.h>
#include <asm/todclk.h>
#include <asm/uaccess.h>
#include <asm/ccwdev.h>
#include "dasd_int.h"
#include "dasd_eckd.h"
......@@ -59,6 +62,8 @@
#define ECKD_F7(i) (i->factor7)
#define ECKD_F8(i) (i->factor8)
MODULE_LICENSE("GPL");
static dasd_discipline_t dasd_eckd_discipline;
typedef struct dasd_eckd_private_t {
......@@ -70,29 +75,44 @@ typedef struct dasd_eckd_private_t {
attrib_data_t attrib; /* e.g. cache operations */
} dasd_eckd_private_t;
static
devreg_t dasd_eckd_known_devices[] = {
{
.ci = { .hc = { .ctype = 0x3880, .dtype = 3390 } },
.flag = (DEVREG_MATCH_CU_TYPE | DEVREG_MATCH_DEV_TYPE |
DEVREG_TYPE_DEVCHARS),
.oper_func = dasd_oper_handler
},
{
.ci = { .hc = { .ctype = 0x3990 } },
.flag = (DEVREG_MATCH_CU_TYPE | DEVREG_TYPE_DEVCHARS),
.oper_func = dasd_oper_handler
},
{
.ci = { .hc = { .ctype = 0x2105 } },
.flag = (DEVREG_MATCH_CU_TYPE | DEVREG_TYPE_DEVCHARS),
.oper_func = dasd_oper_handler
},
{
.ci = { .hc = { .ctype = 0x9343 } },
.flag = (DEVREG_MATCH_CU_TYPE | DEVREG_TYPE_DEVCHARS),
.oper_func = dasd_oper_handler
}
/* The ccw bus type uses this table to find devices that it sends to
* dasd_eckd_probe */
static struct ccw_device_id dasd_eckd_ids[] = {
{ CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3390, 0), driver_info: 0x1},
{ CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3390, 0), driver_info: 0x2},
{ CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3390, 0), driver_info: 0x3},
{ CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3380, 0), driver_info: 0x4},
{ CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3380, 0), driver_info: 0x5},
{ CCW_DEVICE_DEVTYPE (0x9343, 0, 0x9345, 0), driver_info: 0x6},
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(ccw, dasd_eckd_ids);
static struct ccw_driver dasd_eckd_driver; /* see below */
/* initial attempt at a probe function. this can be simplified once
* the other detection code is gone */
static int
dasd_eckd_probe (struct ccw_device *cdev)
{
return dasd_generic_probe (cdev, &dasd_eckd_discipline);
}
static int
dasd_eckd_set_online(struct ccw_device *cdev)
{
return dasd_generic_set_online (cdev, &dasd_eckd_discipline);
}
static struct ccw_driver dasd_eckd_driver = {
.name = "dasd-eckd",
.owner = THIS_MODULE,
.ids = dasd_eckd_ids,
.probe = dasd_eckd_probe,
.remove = dasd_generic_remove,
.set_offline = dasd_generic_set_offline,
.set_online = dasd_eckd_set_online,
};
static const int sizes_trk0[] = { 28, 148, 84 };
......@@ -183,7 +203,32 @@ recs_per_track(dasd_eckd_characteristics_t * rdc,
}
static inline void
define_extent(ccw1_t * ccw, DE_eckd_data_t * data, int trk, int totrk,
check_XRC (struct ccw1 *de_ccw,
DE_eckd_data_t *data,
dasd_device_t *device)
{
dasd_eckd_private_t *private;
private = (dasd_eckd_private_t *) device->private;
/* switch on System Time Stamp - needed for XRC Support */
if (private->rdc_data.facilities.XRC_supported) {
data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */
data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */
data->ep_sys_time = get_clock ();
de_ccw->count = sizeof (DE_eckd_data_t);
de_ccw->flags = CCW_FLAG_SLI;
}
return;
} /* end check_XRC */
static inline void
define_extent(struct ccw1 * ccw, DE_eckd_data_t * data, int trk, int totrk,
int cmd, dasd_device_t * device)
{
dasd_eckd_private_t *private;
......@@ -216,10 +261,12 @@ define_extent(ccw1_t * ccw, DE_eckd_data_t * data, int trk, int totrk,
case DASD_ECKD_CCW_WRITE_KD_MT:
data->mask.perm = 0x02;
data->attributes.operation = private->attrib.operation;
check_XRC (ccw, data, device);
break;
case DASD_ECKD_CCW_WRITE_CKD:
case DASD_ECKD_CCW_WRITE_CKD_MT:
data->attributes.operation = DASD_BYPASS_CACHE;
check_XRC (ccw, data, device);
break;
case DASD_ECKD_CCW_ERASE:
case DASD_ECKD_CCW_WRITE_HOME_ADDRESS:
......@@ -227,6 +274,7 @@ define_extent(ccw1_t * ccw, DE_eckd_data_t * data, int trk, int totrk,
data->mask.perm = 0x3;
data->mask.auth = 0x1;
data->attributes.operation = DASD_BYPASS_CACHE;
check_XRC (ccw, data, device);
break;
default:
MESSAGE(KERN_ERR, "unknown opcode 0x%x", cmd);
......@@ -271,7 +319,7 @@ define_extent(ccw1_t * ccw, DE_eckd_data_t * data, int trk, int totrk,
}
static inline void
locate_record(ccw1_t *ccw, LO_eckd_data_t *data, int trk,
locate_record(struct ccw1 *ccw, LO_eckd_data_t *data, int trk,
int rec_on_trk, int no_rec, int cmd,
dasd_device_t * device, int reclen)
{
......@@ -407,24 +455,7 @@ dasd_eckd_cdl_reclen(int recid)
return LABEL_SIZE;
}
static inline int
dasd_eckd_id_check(s390_dev_info_t * info)
{
if (info->sid_data.cu_type == 0x3990 ||
info->sid_data.cu_type == 0x2105)
if (info->sid_data.dev_type == 0x3390)
return 0;
if (info->sid_data.cu_type == 0x3990 ||
info->sid_data.cu_type == 0x2105)
if (info->sid_data.dev_type == 0x3380)
return 0;
if (info->sid_data.cu_type == 0x9343)
if (info->sid_data.dev_type == 0x9345)
return 0;
return -ENODEV;
}
static inline int
static int
dasd_eckd_check_characteristics(dasd_device_t *device)
{
dasd_eckd_private_t *private;
......@@ -452,7 +483,7 @@ dasd_eckd_check_characteristics(dasd_device_t *device)
/* Read Device Characteristics */
rdc_data = (void *) &(private->rdc_data);
rc = read_dev_chars(device->devinfo.irq, &rdc_data, 64);
rc = read_dev_chars(device->cdev, &rdc_data, 64);
if (rc) {
MESSAGE(KERN_WARNING,
"Read device characteristics returned error %d", rc);
......@@ -470,8 +501,7 @@ dasd_eckd_check_characteristics(dasd_device_t *device)
private->rdc_data.sec_per_trk);
/* Read Configuration Data */
rc = read_conf_data(device->devinfo.irq,
&conf_data, &conf_len, LPM_ANYPATH);
rc = read_conf_data(device->cdev, &conf_data, &conf_len);
if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */
MESSAGE(KERN_WARNING,
"Read configuration data returned error %d", rc);
......@@ -497,17 +527,11 @@ dasd_eckd_check_characteristics(dasd_device_t *device)
private->rdc_data.cu_type,
private->rdc_data.cu_model.model);
return 0;
}
static int
dasd_eckd_check_device(struct dasd_device_t *device)
{
int rc;
rc = dasd_eckd_id_check(&device->devinfo);
if (rc)
return rc;
return dasd_eckd_check_characteristics(device);
/* get characteristis via diag to determine the kind of minidisk under VM */
/* needed beacause XRC is not support by VM (jet) */
/* Can be removed as soon as VM supports XRC */
// TBD ??? HUM
}
static dasd_ccw_req_t *
......@@ -517,7 +541,7 @@ dasd_eckd_analysis_ccw(struct dasd_device_t *device)
eckd_count_t *count_data;
LO_eckd_data_t *LO_data;
dasd_ccw_req_t *cqr;
ccw1_t *ccw;
struct ccw1 *ccw;
int cplength, datasize;
int i;
......@@ -563,6 +587,7 @@ dasd_eckd_analysis_ccw(struct dasd_device_t *device)
cqr->device = device;
cqr->retries = 0;
cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED;
return cqr;
}
......@@ -716,7 +741,7 @@ dasd_eckd_format_device(dasd_device_t * device, format_data_t * fdata)
dasd_eckd_private_t *private;
dasd_ccw_req_t *fcp;
eckd_count_t *ect;
ccw1_t *ccw;
struct ccw1 *ccw;
void *data;
int rpt, cyl, head;
int cplength, datasize;
......@@ -881,25 +906,28 @@ dasd_eckd_format_device(dasd_device_t * device, format_data_t * fdata)
}
fcp->device = device;
fcp->retries = 2; /* set retry counter to enable ERP */
fcp->buildclk = get_clock();
fcp->status = DASD_CQR_FILLED;
return fcp;
}
static dasd_era_t
dasd_eckd_examine_error(dasd_ccw_req_t * cqr, devstat_t * stat)
dasd_eckd_examine_error(dasd_ccw_req_t * cqr, struct irb * irb)
{
dasd_device_t *device = (dasd_device_t *) cqr->device;
struct ccw_device *cdev = device->cdev;
if (stat->cstat == 0x00 &&
stat->dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
if (irb->scsw.cstat == 0x00 &&
irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
return dasd_era_none;
switch (device->devinfo.sid_data.cu_type) {
switch (cdev->id.cu_type) {
case 0x3990:
case 0x2105:
return dasd_3990_erp_examine(cqr, stat);
return dasd_3990_erp_examine(cqr, irb);
case 0x9343:
return dasd_9343_erp_examine(cqr, stat);
return dasd_9343_erp_examine(cqr, irb);
case 0x3880:
default:
MESSAGE(KERN_WARNING, "%s",
"default (unknown CU type) - RECOVERABLE return");
......@@ -911,13 +939,15 @@ static dasd_erp_fn_t
dasd_eckd_erp_action(dasd_ccw_req_t * cqr)
{
dasd_device_t *device = (dasd_device_t *) cqr->device;
struct ccw_device *cdev = device->cdev;
switch (device->devinfo.sid_data.cu_type) {
switch (cdev->id.cu_type) {
case 0x3990:
case 0x2105:
return dasd_3990_erp_action;
case 0x9343:
/* Return dasd_9343_erp_action; */
case 0x3880:
default:
return dasd_default_erp_action;
}
......@@ -936,13 +966,15 @@ dasd_eckd_build_cp(dasd_device_t * device, struct request *req)
unsigned long *idaws;
LO_eckd_data_t *LO_data;
dasd_ccw_req_t *cqr;
ccw1_t *ccw;
struct ccw1 *ccw;
struct bio *bio;
struct bio_vec *bv;
char *dst;
unsigned int blksize, blk_per_trk, off;
int count, cidaw, cplength, datasize;
sector_t recid, first_rec, last_rec;
sector_t first_trk, last_trk;
unsigned int first_offs, last_offs;
unsigned char cmd, rcmd;
int i;
......@@ -957,8 +989,10 @@ dasd_eckd_build_cp(dasd_device_t * device, struct request *req)
blksize = device->bp_block;
blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize);
/* Calculate record id of first and last block. */
first_rec = req->sector >> device->s2b_shift;
last_rec = (req->sector + req->nr_sectors - 1) >> device->s2b_shift;
first_rec = first_trk = req->sector >> device->s2b_shift;
first_offs = sector_div(first_trk, blk_per_trk);
last_rec = last_trk = (req->sector + req->nr_sectors - 1) >> device->s2b_shift;
last_offs = sector_div(last_trk, blk_per_trk);
/* Check struct bio and count the number of blocks for the request. */
count = 0;
cidaw = 0;
......@@ -996,8 +1030,7 @@ dasd_eckd_build_cp(dasd_device_t * device, struct request *req)
return cqr;
ccw = cqr->cpaddr;
/* First ccw is define extent. */
define_extent(ccw++, cqr->data, first_rec / blk_per_trk,
last_rec / blk_per_trk, cmd, device);
define_extent(ccw++, cqr->data, first_trk, last_trk, cmd, device);
/* Build locate_record+read/write/ccws. */
idaws = (unsigned long *) (cqr->data + sizeof(DE_eckd_data_t));
LO_data = (LO_eckd_data_t *) (idaws + cidaw);
......@@ -1005,13 +1038,14 @@ dasd_eckd_build_cp(dasd_device_t * device, struct request *req)
if (private->uses_cdl == 0 || recid > 2*blk_per_trk) {
/* Only standard blocks so there is just one locate record. */
ccw[-1].flags |= CCW_FLAG_CC;
locate_record(ccw++, LO_data++,
recid / blk_per_trk, recid % blk_per_trk + 1,
locate_record(ccw++, LO_data++, first_trk, first_offs + 1,
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;
for (off = 0; off < bv->bv_len; off += blksize) {
sector_t trkid = recid;
unsigned int recoffs = sector_div(trkid, blk_per_trk);
rcmd = cmd;
count = blksize;
/* Locate record for cdl special block ? */
......@@ -1025,16 +1059,14 @@ dasd_eckd_build_cp(dasd_device_t * device, struct request *req)
}
ccw[-1].flags |= CCW_FLAG_CC;
locate_record(ccw++, LO_data++,
recid / blk_per_trk,
recid % blk_per_trk + 1,
trkid, recoffs + 1,
1, rcmd, device, count);
}
/* Locate record for standard blocks ? */
if (private->uses_cdl && recid == 2*blk_per_trk) {
ccw[-1].flags |= CCW_FLAG_CC;
locate_record(ccw++, LO_data++,
recid / blk_per_trk,
recid % blk_per_trk + 1,
trkid, recoffs + 1,
last_rec - recid + 1,
cmd, device, count);
}
......@@ -1094,37 +1126,34 @@ dasd_eckd_fill_info(dasd_device_t * device, dasd_information2_t * info)
static int
dasd_eckd_release(struct block_device *bdev, int no, long args)
{
dasd_devmap_t *devmap;
dasd_device_t *device;
dasd_ccw_req_t *cqr;
int rc;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
devmap = dasd_devmap_from_bdev(bdev);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device))
return PTR_ERR(device);
device = bdev->bd_disk->private_data;
if (device == NULL)
return -ENODEV;
cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
1, 0, device);
if (cqr == NULL) {
MESSAGE(KERN_WARNING, "%s",
"No memory to allocate initialization request");
dasd_put_device(devmap);
return -ENOMEM;
}
cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RELEASE;
cqr->device = device;
cqr->retries = 0;
cqr->expires = 10 * HZ;
cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED;
rc = dasd_sleep_on_immediatly(cqr);
dasd_kfree_request(cqr, cqr->device);
dasd_put_device(devmap);
return rc;
}
......@@ -1137,18 +1166,16 @@ dasd_eckd_release(struct block_device *bdev, int no, long args)
static int
dasd_eckd_reserve(struct block_device *bdev, int no, long args)
{
dasd_devmap_t *devmap;
dasd_device_t *device;
dasd_ccw_req_t *cqr;
int rc;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
devmap = dasd_devmap_from_bdev(bdev);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device))
return PTR_ERR(device);
device = bdev->bd_disk->private_data;
if (device == NULL)
return -ENODEV;
cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
1, 0, device);
......@@ -1156,13 +1183,13 @@ dasd_eckd_reserve(struct block_device *bdev, int no, long args)
if (cqr == NULL) {
MESSAGE(KERN_WARNING, "%s",
"No memory to allocate initialization request");
dasd_put_device(devmap);
return -ENOMEM;
}
cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RESERVE;
cqr->device = device;
cqr->retries = 0;
cqr->expires = 10 * HZ;
cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED;
rc = dasd_sleep_on_immediatly(cqr);
......@@ -1172,7 +1199,6 @@ dasd_eckd_reserve(struct block_device *bdev, int no, long args)
dasd_eckd_release(bdev, no, args);
}
dasd_kfree_request(cqr, cqr->device);
dasd_put_device(devmap);
return rc;
}
......@@ -1184,31 +1210,29 @@ dasd_eckd_reserve(struct block_device *bdev, int no, long args)
static int
dasd_eckd_steal_lock(struct block_device *bdev, int no, long args)
{
dasd_devmap_t *devmap;
dasd_device_t *device;
dasd_ccw_req_t *cqr;
int rc;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
devmap = dasd_devmap_from_bdev(bdev);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device))
return PTR_ERR(device);
device = bdev->bd_disk->private_data;
if (device == NULL)
return -ENODEV;
cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
1, 0, device);
if (cqr == NULL) {
MESSAGE(KERN_WARNING, "%s",
"No memory to allocate initialization request");
dasd_put_device(devmap);
return -ENOMEM;
}
cqr->cpaddr->cmd_code = DASD_ECKD_CCW_SLCK;
cqr->device = device;
cqr->retries = 0;
cqr->expires = 10 * HZ;
cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED;
rc = dasd_sleep_on_immediatly(cqr);
......@@ -1218,7 +1242,6 @@ dasd_eckd_steal_lock(struct block_device *bdev, int no, long args)
dasd_eckd_release(bdev, no, args);
}
dasd_kfree_request(cqr, cqr->device);
dasd_put_device(devmap);
return rc;
}
......@@ -1228,19 +1251,16 @@ dasd_eckd_steal_lock(struct block_device *bdev, int no, long args)
static int
dasd_eckd_performance(struct block_device *bdev, int no, long args)
{
dasd_devmap_t *devmap;
dasd_device_t *device;
dasd_psf_prssd_data_t *prssdp;
dasd_rssd_perf_stats_t *stats;
dasd_ccw_req_t *cqr;
ccw1_t *ccw;
struct ccw1 *ccw;
int rc;
devmap = dasd_devmap_from_bdev(bdev);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device))
return PTR_ERR(device);
device = bdev->bd_disk->private_data;
if (device == NULL)
return -ENODEV;
cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
1 /* PSF */ + 1 /* RSSD */ ,
......@@ -1249,7 +1269,6 @@ dasd_eckd_performance(struct block_device *bdev, int no, long args)
if (cqr == NULL) {
MESSAGE(KERN_WARNING, "%s",
"No memory to allocate initialization request");
dasd_put_device(devmap);
return -ENOMEM;
}
cqr->device = device;
......@@ -1277,6 +1296,7 @@ dasd_eckd_performance(struct block_device *bdev, int no, long args)
ccw->count = sizeof (dasd_rssd_perf_stats_t);
ccw->cda = (__u32)(addr_t) stats;
cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED;
rc = dasd_sleep_on(cqr);
if (rc == 0) {
......@@ -1287,7 +1307,6 @@ dasd_eckd_performance(struct block_device *bdev, int no, long args)
sizeof(dasd_rssd_perf_stats_t));
}
dasd_sfree_request(cqr, cqr->device);
dasd_put_device(devmap);
return rc;
}
......@@ -1298,7 +1317,6 @@ dasd_eckd_performance(struct block_device *bdev, int no, long args)
static int
dasd_eckd_set_attrib(struct block_device *bdev, int no, long args)
{
dasd_devmap_t *devmap;
dasd_device_t *device;
dasd_eckd_private_t *private;
attrib_data_t attrib;
......@@ -1308,14 +1326,11 @@ dasd_eckd_set_attrib(struct block_device *bdev, int no, long args)
if (!args)
return -EINVAL;
devmap = dasd_devmap_from_bdev(bdev);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device))
return PTR_ERR(device);
device = bdev->bd_disk->private_data;
if (device == NULL)
return -ENODEV;
if (copy_from_user(&attrib, (void *) args, sizeof (attrib_data_t))) {
dasd_put_device(devmap);
return -EFAULT;
}
......@@ -1332,16 +1347,16 @@ dasd_eckd_set_attrib(struct block_device *bdev, int no, long args)
"cache operation mode set to "
"%x (%i cylinder prestage)",
private->attrib.operation, private->attrib.nr_cyl);
dasd_put_device(devmap);
return 0;
}
static void
dasd_eckd_dump_sense(struct dasd_device_t *device, dasd_ccw_req_t * req)
dasd_eckd_dump_sense(struct dasd_device_t *device, dasd_ccw_req_t * req,
struct irb *irb)
{
char *page, *sense;
ccw1_t *act;
char *page;
struct ccw1 *act;
int len, sl, sct;
page = (char *) get_zeroed_page(GFP_ATOMIC);
......@@ -1350,14 +1365,14 @@ dasd_eckd_dump_sense(struct dasd_device_t *device, dasd_ccw_req_t * req)
return;
}
len = sprintf(page, KERN_ERR PRINTK_HEADER
"device %04X on irq %d: I/O status report:\n",
device->devinfo.devno, device->devinfo.irq);
"device %s: I/O status report:\n",
device->cdev->dev.bus_id);
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
"in req: %p CS: 0x%02X DS: 0x%02X\n", req,
device->dev_status.cstat, device->dev_status.dstat);
irb->scsw.cstat, irb->scsw.dstat);
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
"Failing CCW: %p\n",
(void *) (addr_t) device->dev_status.cpa);
(void *) (addr_t) irb->scsw.cpa);
act = req->cpaddr;
do {
DBF_EVENT(DBF_INFO,
......@@ -1370,8 +1385,7 @@ dasd_eckd_dump_sense(struct dasd_device_t *device, dasd_ccw_req_t * req)
((int *) (addr_t) act->cda)[2],
((int *) (addr_t) act->cda)[3]);
} while (act++->flags & (CCW_FLAG_CC | CCW_FLAG_DC));
sense = device->dev_status.ii.sense.data;
if (device->dev_status.flag & DEVSTAT_FLAG_SENSE_AVAIL) {
if (irb->esw.esw0.erw.cons) {
for (sl = 0; sl < 4; sl++) {
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
"Sense(hex) %2d-%2d:",
......@@ -1379,23 +1393,23 @@ dasd_eckd_dump_sense(struct dasd_device_t *device, dasd_ccw_req_t * req)
for (sct = 0; sct < 8; sct++) {
len += sprintf(page + len, " %02x",
sense[8 * sl + sct]);
irb->ecw[8 * sl + sct]);
}
len += sprintf(page + len, "\n");
}
if (sense[27] & DASD_SENSE_BIT_0) {
if (irb->ecw[27] & DASD_SENSE_BIT_0) {
/* 24 Byte Sense Data */
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
"24 Byte: %x MSG %x, %s MSGb to SYSOP\n",
sense[7] >> 4, sense[7] & 0x0f,
sense[1] & 0x10 ? "" : "no");
irb->ecw[7] >> 4, irb->ecw[7] & 0x0f,
irb->ecw[1] & 0x10 ? "" : "no");
} else {
/* 32 Byte Sense Data */
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
"32 Byte: Format: %x "
"Exception class %x\n",
sense[6] & 0x0f, sense[22] >> 4);
irb->ecw[6] & 0x0f, irb->ecw[22] >> 4);
}
}
......@@ -1422,7 +1436,7 @@ static dasd_discipline_t dasd_eckd_discipline = {
.name = "ECKD",
.ebcname = "ECKD",
.max_blocks = 240,
.check_device = dasd_eckd_check_device,
.check_device = dasd_eckd_check_characteristics,
.do_analysis = dasd_eckd_do_analysis,
.fill_geometry = dasd_eckd_fill_geometry,
.start_IO = dasd_start_IO,
......@@ -1436,12 +1450,9 @@ static dasd_discipline_t dasd_eckd_discipline = {
.fill_info = dasd_eckd_fill_info,
};
int
static int __init
dasd_eckd_init(void)
{
int i;
printk("sizeof(dasd_ccw_req_t) = %li\n",
sizeof(dasd_ccw_req_t));
dasd_ioctl_no_register(THIS_MODULE, BIODASDSATTR,
dasd_eckd_set_attrib);
dasd_ioctl_no_register(THIS_MODULE, BIODASDPSRD,
......@@ -1454,22 +1465,15 @@ dasd_eckd_init(void)
dasd_eckd_steal_lock);
ASCEBC(dasd_eckd_discipline.ebcname, 4);
dasd_discipline_add(&dasd_eckd_discipline);
for (i = 0; i < sizeof(dasd_eckd_known_devices)/sizeof(devreg_t); i++)
s390_device_register(&dasd_eckd_known_devices[i]);
ccw_driver_register(&dasd_eckd_driver);
return 0;
}
void
static void __exit
dasd_eckd_cleanup(void)
{
int i;
for (i = 0; i < sizeof(dasd_eckd_known_devices)/sizeof(devreg_t); i++)
s390_device_unregister(&dasd_eckd_known_devices[i]);
dasd_discipline_del(&dasd_eckd_discipline);
ccw_driver_unregister(&dasd_eckd_driver);
dasd_ioctl_no_unregister(THIS_MODULE, BIODASDSATTR,
dasd_eckd_set_attrib);
......@@ -1483,19 +1487,8 @@ dasd_eckd_cleanup(void)
dasd_eckd_steal_lock);
}
#ifdef MODULE
int
init_module(void)
{
return dasd_eckd_init();
}
void
cleanup_module(void)
{
dasd_eckd_cleanup();
}
#endif
module_init(dasd_eckd_init);
module_exit(dasd_eckd_cleanup);
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
......
/*
* File...........: linux/drivers/s390/block/dasd_eckd.h
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Horst Hummel <Horst.Hummel@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
* $Revision: 1.5 $
*
* History of changes
*
*/
#ifndef DASD_ECKD_H
#define DASD_ECKD_H
......@@ -102,6 +115,10 @@ typedef struct DE_eckd_data_t {
__u8 ga_extended; /* Global Attributes Extended */
ch_t beg_ext;
ch_t end_ext;
unsigned long long ep_sys_time; /* Extended Parameter - System Time Stamp */
__u8 ep_format; /* Extended Parameter format byte */
__u8 ep_prio; /* Extended Parameter priority I/O byte */
__u8 ep_reserved[6]; /* Extended Parameter Reserved */
} __attribute__ ((packed)) DE_eckd_data_t;
typedef struct LO_eckd_data_t {
......@@ -141,7 +158,8 @@ typedef struct dasd_eckd_characteristics_t {
unsigned char reserved2:4;
unsigned char reserved3:8;
unsigned char defect_wr:1;
unsigned char reserved4:2;
unsigned char XRC_supported:1;
unsigned char reserved4:1;
unsigned char striping:1;
unsigned char reserved5:4;
unsigned char cfw:1;
......@@ -322,6 +340,4 @@ typedef struct dasd_psf_prssd_data_t {
unsigned char varies[9];
} __attribute__ ((packed)) dasd_psf_prssd_data_t;
int dasd_eckd_init(void);
void dasd_eckd_cleanup(void);
#endif /* DASD_ECKD_H */
......@@ -7,6 +7,9 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
*
* $Revision: 1.6 $
*
* History of changes
* 05/04/02 split from dasd.c, code restructuring.
*/
......@@ -17,7 +20,6 @@
#include <asm/debug.h>
#include <asm/ebcdic.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
/* This is ugly... */
......@@ -36,7 +38,7 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize,
/* Sanity checks */
if ( magic == NULL || datasize > PAGE_SIZE ||
(cplength*sizeof(ccw1_t)) > PAGE_SIZE)
(cplength*sizeof(struct ccw1)) > PAGE_SIZE)
BUG();
debug_text_event ( dasd_debug_area, 1, "ALLC");
debug_text_event ( dasd_debug_area, 1, magic);
......@@ -45,7 +47,7 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize,
size = (sizeof(dasd_ccw_req_t) + 7L) & -8L;
if (cplength > 0)
size += cplength * sizeof(ccw1_t);
size += cplength * sizeof(struct ccw1);
if (datasize > 0)
size += datasize;
spin_lock_irqsave(&device->mem_lock, flags);
......@@ -57,9 +59,9 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize,
data = (char *) cqr + ((sizeof(dasd_ccw_req_t) + 7L) & -8L);
cqr->cpaddr = NULL;
if (cplength > 0) {
cqr->cpaddr = (ccw1_t *) data;
data += cplength*sizeof(ccw1_t);
memset(cqr->cpaddr, 0, cplength*sizeof(ccw1_t));
cqr->cpaddr = (struct ccw1 *) data;
data += cplength*sizeof(struct ccw1);
memset(cqr->cpaddr, 0, cplength*sizeof(struct ccw1));
}
cqr->data = NULL;
if (datasize > 0) {
......@@ -201,18 +203,25 @@ hex_dump_memory(dasd_device_t *device, void *data, int len)
}
void
dasd_log_ccw(dasd_ccw_req_t * cqr, int caller, __u32 cpa)
dasd_log_sense(dasd_ccw_req_t *cqr, struct irb *irb)
{
dasd_device_t *device;
dasd_ccw_req_t *lcqr;
ccw1_t *ccw;
int cplength;
device = cqr->device;
/* dump sense data */
if (device->discipline && device->discipline->dump_sense)
device->discipline->dump_sense(device, cqr);
device->discipline->dump_sense(device, cqr, irb);
}
void
dasd_log_ccw(dasd_ccw_req_t * cqr, int caller, __u32 cpa)
{
dasd_device_t *device;
dasd_ccw_req_t *lcqr;
struct ccw1 *ccw;
int cplength;
device = cqr->device;
/* log the channel program */
for (lcqr = cqr; lcqr != NULL; lcqr = lcqr->refers) {
DEV_MESSAGE(KERN_ERR, device,
......@@ -229,17 +238,17 @@ dasd_log_ccw(dasd_ccw_req_t * cqr, int caller, __u32 cpa)
DEV_MESSAGE(KERN_ERR, device, "%s",
"Start of channel program:");
hex_dump_memory(device, lcqr->cpaddr,
40*sizeof(ccw1_t));
40*sizeof(struct ccw1));
DEV_MESSAGE(KERN_ERR, device, "%s",
"End of channel program:");
hex_dump_memory(device, lcqr->cpaddr + cplength - 10,
10*sizeof(ccw1_t));
10*sizeof(struct ccw1));
} else { /* log the whole CP */
DEV_MESSAGE(KERN_ERR, device, "%s",
"Channel program (complete):");
hex_dump_memory(device, lcqr->cpaddr,
cplength*sizeof(ccw1_t));
cplength*sizeof(struct ccw1));
}
if (lcqr != cqr)
......@@ -258,7 +267,7 @@ dasd_log_ccw(dasd_ccw_req_t * cqr, int caller, __u32 cpa)
"Failed CCW (%p) (area):",
(void *) (long) cpa);
hex_dump_memory(device, cqr->cpaddr - 10,
20*sizeof(ccw1_t));
20*sizeof(struct ccw1));
}
}
......@@ -268,4 +277,5 @@ EXPORT_SYMBOL(dasd_default_erp_action);
EXPORT_SYMBOL(dasd_default_erp_postaction);
EXPORT_SYMBOL(dasd_alloc_erp_request);
EXPORT_SYMBOL(dasd_free_erp_request);
EXPORT_SYMBOL(dasd_log_sense);
EXPORT_SYMBOL(dasd_log_ccw);
/*
* File...........: linux/drivers/s390/block/dasd_fba.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
* $Revision: 1.25 $
*
* History of changes
* fixed partition handling and HDIO_GETGEO
* 2002/01/04 Created 2.4-2.5 compatibility mode
* 05/04/02 code restructuring.
......@@ -17,13 +20,14 @@
#include <linux/slab.h>
#include <linux/hdreg.h> /* HDIO_GETGEO */
#include <linux/bio.h>
#include <linux/module.h>
#include <linux/init.h>
#include <asm/idals.h>
#include <asm/ebcdic.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/s390dyn.h>
#include <asm/todclk.h>
#include <asm/ccwdev.h>
#include "dasd_int.h"
#include "dasd_fba.h"
......@@ -38,30 +42,47 @@
#define DASD_FBA_CCW_LOCATE 0x43
#define DASD_FBA_CCW_DEFINE_EXTENT 0x63
MODULE_LICENSE("GPL");
static dasd_discipline_t dasd_fba_discipline;
typedef struct dasd_fba_private_t {
dasd_fba_characteristics_t rdc_data;
} dasd_fba_private_t;
static
devreg_t dasd_fba_known_devices[] = {
{
.ci = {.hc = {.ctype = 0x6310, .dtype = 0x9336}},
.flag = (DEVREG_MATCH_CU_TYPE |
DEVREG_MATCH_DEV_TYPE | DEVREG_TYPE_DEVCHARS),
.oper_func = dasd_oper_handler
},
{
.ci = {.hc = {.ctype = 0x3880, .dtype = 0x3370}},
.flag = (DEVREG_MATCH_CU_TYPE |
DEVREG_MATCH_DEV_TYPE | DEVREG_TYPE_DEVCHARS),
.oper_func = dasd_oper_handler
}
static struct ccw_device_id dasd_fba_ids[] = {
{ CCW_DEVICE_DEVTYPE (0x6310, 0, 0x9336, 0), driver_info: 0x1},
{ CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3370, 0), driver_info: 0x2},
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(ccw, dasd_fba_ids);
static struct ccw_driver dasd_fba_driver; /* see below */
static int
dasd_fba_probe(struct ccw_device *cdev)
{
return dasd_generic_probe (cdev, &dasd_fba_discipline);
}
static int
dasd_fba_set_online(struct ccw_device *cdev)
{
return dasd_generic_set_online (cdev, &dasd_fba_discipline);
}
static struct ccw_driver dasd_fba_driver = {
.name = "dasd-fba",
.owner = THIS_MODULE,
.ids = dasd_fba_ids,
.probe = dasd_fba_probe,
.remove = dasd_generic_remove,
.set_offline = dasd_generic_set_offline,
.set_online = dasd_fba_set_online,
};
static inline void
define_extent(ccw1_t * ccw, DE_fba_data_t *data, int rw,
define_extent(struct ccw1 * ccw, DE_fba_data_t *data, int rw,
int blksize, int beg, int nr)
{
ccw->cmd_code = DASD_FBA_CCW_DEFINE_EXTENT;
......@@ -81,7 +102,7 @@ define_extent(ccw1_t * ccw, DE_fba_data_t *data, int rw,
}
static inline void
locate_record(ccw1_t * ccw, LO_fba_data_t *data, int rw,
locate_record(struct ccw1 * ccw, LO_fba_data_t *data, int rw,
int block_nr, int block_ct)
{
ccw->cmd_code = DASD_FBA_CCW_LOCATE;
......@@ -99,22 +120,11 @@ locate_record(ccw1_t * ccw, LO_fba_data_t *data, int rw,
data->blk_ct = block_ct;
}
static inline int
dasd_fba_id_check(s390_dev_info_t * info)
{
if (info->sid_data.cu_type == 0x3880)
if (info->sid_data.dev_type == 0x3370)
return 0;
if (info->sid_data.cu_type == 0x6310)
if (info->sid_data.dev_type == 0x9336)
return 0;
return -ENODEV;
}
static inline int
static int
dasd_fba_check_characteristics(struct dasd_device_t *device)
{
dasd_fba_private_t *private;
struct ccw_device *cdev = device->cdev;
void *rdc_data;
int rc;
......@@ -130,7 +140,7 @@ dasd_fba_check_characteristics(struct dasd_device_t *device)
}
/* Read Device Characteristics */
rdc_data = (void *) &(private->rdc_data);
rc = read_dev_chars(device->devinfo.irq, &rdc_data, 32);
rc = read_dev_chars(device->cdev, &rdc_data, 32);
if (rc) {
MESSAGE(KERN_WARNING,
"Read device characteristics returned error %d", rc);
......@@ -139,27 +149,16 @@ dasd_fba_check_characteristics(struct dasd_device_t *device)
DEV_MESSAGE(KERN_INFO, device,
"%04X/%02X(CU:%04X/%02X) %dMB at(%d B/blk)",
device->devinfo.sid_data.dev_type,
device->devinfo.sid_data.dev_model,
device->devinfo.sid_data.cu_type,
device->devinfo.sid_data.cu_model,
cdev->id.dev_type,
cdev->id.dev_model,
cdev->id.cu_type,
cdev->id.cu_model,
((private->rdc_data.blk_bdsa *
(private->rdc_data.blk_size >> 9)) >> 11),
private->rdc_data.blk_size);
return 0;
}
static int
dasd_fba_check_device(struct dasd_device_t *device)
{
int rc;
rc = dasd_fba_id_check(&device->devinfo);
if (rc)
return rc;
return dasd_fba_check_characteristics(device);
}
static int
dasd_fba_do_analysis(struct dasd_device_t *device)
{
......@@ -193,20 +192,22 @@ dasd_fba_fill_geometry(struct dasd_device_t *device, struct hd_geometry *geo)
}
static dasd_era_t
dasd_fba_examine_error(dasd_ccw_req_t * cqr, devstat_t * stat)
dasd_fba_examine_error(dasd_ccw_req_t * cqr, struct irb * irb)
{
dasd_device_t *device;
struct ccw_device *cdev;
device = (dasd_device_t *) cqr->device;
if (stat->cstat == 0x00 &&
stat->dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
if (irb->scsw.cstat == 0x00 &&
irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
return dasd_era_none;
switch (device->devinfo.sid_data.dev_model) {
cdev = device->cdev;
switch (cdev->id.dev_model) {
case 0x3370:
return dasd_3370_erp_examine(cqr, stat);
return dasd_3370_erp_examine(cqr, irb);
case 0x9336:
return dasd_9336_erp_examine(cqr, stat);
return dasd_9336_erp_examine(cqr, irb);
default:
return dasd_era_recover;
}
......@@ -236,7 +237,7 @@ dasd_fba_build_cp(dasd_device_t * device, struct request *req)
unsigned long *idaws;
LO_fba_data_t *LO_data;
dasd_ccw_req_t *cqr;
ccw1_t *ccw;
struct ccw1 *ccw;
struct bio *bio;
struct bio_vec *bv;
char *dst;
......@@ -359,7 +360,8 @@ dasd_fba_fill_info(dasd_device_t * device, dasd_information2_t * info)
}
static void
dasd_fba_dump_sense(struct dasd_device_t *device, dasd_ccw_req_t * req)
dasd_fba_dump_sense(struct dasd_device_t *device, dasd_ccw_req_t * req,
struct irb *irb)
{
char *page;
......@@ -369,8 +371,8 @@ dasd_fba_dump_sense(struct dasd_device_t *device, dasd_ccw_req_t * req)
return;
}
sprintf(page, KERN_WARNING PRINTK_HEADER
"device %04X on irq %d: I/O status report:\n",
device->devinfo.devno, device->devinfo.irq);
"device %s: I/O status report:\n",
device->cdev->dev.bus_id);
MESSAGE(KERN_ERR, "Sense data:\n%s", page);
......@@ -396,7 +398,7 @@ static dasd_discipline_t dasd_fba_discipline = {
.name = "FBA ",
.ebcname = "FBA ",
.max_blocks = 96,
.check_device = dasd_fba_check_device,
.check_device = dasd_fba_check_characteristics,
.do_analysis = dasd_fba_do_analysis,
.fill_geometry = dasd_fba_fill_geometry,
.start_IO = dasd_start_IO,
......@@ -409,41 +411,22 @@ static dasd_discipline_t dasd_fba_discipline = {
.fill_info = dasd_fba_fill_info,
};
int
static int __init
dasd_fba_init(void)
{
int i;
ASCEBC(dasd_fba_discipline.ebcname, 4);
dasd_discipline_add(&dasd_fba_discipline);
for (i = 0; i < sizeof(dasd_fba_known_devices) / sizeof(devreg_t); i++)
s390_device_register(&dasd_fba_known_devices[i]);
return 0;
}
void
dasd_fba_cleanup(void)
{
int i;
for (i = 0; i < sizeof(dasd_fba_known_devices) / sizeof(devreg_t); i++)
s390_device_unregister(&dasd_fba_known_devices[i]);
dasd_discipline_del(&dasd_fba_discipline);
return ccw_driver_register(&dasd_fba_driver);
}
#ifdef MODULE
int
init_module(void)
static void __exit
dasd_fba_cleanup(void)
{
return dasd_fba_init();
ccw_driver_unregister(&dasd_fba_driver);
}
void
cleanup_module(void)
{
dasd_fba_cleanup();
}
#endif
module_init(dasd_fba_init);
module_exit(dasd_fba_cleanup);
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
......
/*
* File...........: linux/drivers/s390/block/dasd_fba.h
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
* $Revision: 1.4 $
*
* History of changes
*
*/
#ifndef DASD_FBA_H
#define DASD_FBA_H
......@@ -71,6 +82,4 @@ typedef
dasd_fba_characteristics_t;
int dasd_fba_init(void);
void dasd_fba_cleanup(void);
#endif /* DASD_FBA_H */
/*
* File...........: linux/drivers/s390/block/dasd_ioctl.c
* File...........: linux/drivers/s390/block/dasd_genhd.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Horst Hummel <Horst.Hummel@de.ibm.com>
* Carsten Otte <Cotte@de.ibm.com>
......@@ -9,6 +9,9 @@
*
* Dealing with devices registered to multiple major numbers.
*
* $Revision: 1.23 $
*
* History of changes
* 05/04/02 split from dasd.c, code restructuring.
*/
......@@ -19,7 +22,6 @@
#include <linux/blkpg.h>
#include <linux/blk.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
/* This is ugly... */
......@@ -72,7 +74,6 @@ dasd_register_major(int major)
/* Initialize major info structure. */
mi->major = new_major;
/* Setup block device pointers for the new major. */
/* Insert the new major info structure into dasd_major_info list. */
spin_lock(&dasd_major_lock);
list_add_tail(&mi->list, &dasd_major_info);
......@@ -104,35 +105,6 @@ dasd_unregister_major(struct major_info * mi)
kfree(mi);
}
/*
* This one is needed for naming 18000+ possible dasd devices.
* dasda - dasdz : 26 devices
* dasdaa - dasdzz : 676 devices, added up = 702
* dasdaaa - dasdzzz : 17576 devices, added up = 18278
*/
int
dasd_device_name(char *str, int index, int partition)
{
int len;
if (partition > DASD_PARTN_MASK)
return -EINVAL;
len = sprintf(str, "dasd");
if (index > 25) {
if (index > 701)
len += sprintf(str + len, "%c",
'a' + (((index - 702) / 676) % 26));
len += sprintf(str + len, "%c",
'a' + (((index - 26) / 26) % 26));
}
len += sprintf(str + len, "%c", 'a' + (index % 26));
if (partition)
len += sprintf(str + len, "%d", partition);
return 0;
}
/*
* Allocate gendisk structure for devindex.
*/
......@@ -164,7 +136,7 @@ dasd_gendisk_alloc(int devindex)
return ERR_PTR(rc);
}
}
gdp = alloc_disk(1 << DASD_PARTN_BITS);
if (!gdp)
return ERR_PTR(-ENOMEM);
......@@ -196,7 +168,7 @@ dasd_gendisk_alloc(int devindex)
/*
* Return devindex of first device using a specific major number.
*/
int dasd_gendisk_major_index(int major)
static int dasd_gendisk_major_index(int major)
{
struct list_head *l;
struct major_info *mi;
......
/*
* File...........: linux/drivers/s390/block/dasd.c
* File...........: linux/drivers/s390/block/dasd_int.h
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Horst Hummel <Horst.Hummel@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
* $Revision: 1.36 $
*
* History of changes (starts July 2000)
* 02/01/01 added dynamic registration of ioctls
* 2002/01/04 Created 2.4-2.5 compatibility mode
......@@ -58,12 +61,11 @@
#include <linux/genhd.h>
#include <linux/hdreg.h>
#include <linux/interrupt.h>
#include <asm/ccwdev.h>
#include <linux/workqueue.h>
#include <asm/debug.h>
#include <asm/dasd.h>
#include <asm/idals.h>
#include <asm/irq.h>
#include <asm/s390dyn.h>
/*
* SECTION: Type definitions
......@@ -140,10 +142,9 @@ do { \
/* messages to be written via klogd and dbf */
#define DEV_MESSAGE(d_loglevel,d_device,d_string,d_args...)\
do { \
printk(d_loglevel PRINTK_HEADER " %s,%04x@%02x: " \
printk(d_loglevel PRINTK_HEADER " %s,%s: " \
d_string "\n", d_device->gdp->disk_name, \
d_device->devinfo.devno, d_device->devinfo.irq, \
d_args); \
d_device->cdev->dev.bus_id, d_args); \
DBF_DEV_EVENT(DBF_ALERT, d_device, d_string, d_args); \
} while(0)
......@@ -159,18 +160,17 @@ typedef struct dasd_ccw_req_t {
/* Where to execute what... */
struct dasd_device_t *device; /* device the request is for */
ccw1_t *cpaddr; /* address of channel program */
struct ccw1 *cpaddr; /* address of channel program */
char status; /* status of this request */
short retries; /* A retry counter */
/* ... and how */
int options; /* options for execution */
int expires; /* expiration period in jiffies */
char lpm; /* logical path mask */
void *data; /* pointer to data area */
/* these are important for recovering erroneous requests */
devstat_t *dstat; /* device status in case of an error */
struct irb *dstat; /* device status in case of an error */
struct dasd_ccw_req_t *refers; /* ERP-chain queueing. */
void *function; /* originating ERP action */
......@@ -247,16 +247,24 @@ typedef struct dasd_discipline_t {
* is called for every error condition to print the sense data
* to the console.
*/
dasd_era_t(*examine_error) (dasd_ccw_req_t *, devstat_t *);
dasd_era_t(*examine_error) (dasd_ccw_req_t *, struct irb *);
dasd_erp_fn_t(*erp_action) (dasd_ccw_req_t *);
dasd_erp_fn_t(*erp_postaction) (dasd_ccw_req_t *);
void (*dump_sense) (struct dasd_device_t *, dasd_ccw_req_t *);
void (*dump_sense) (struct dasd_device_t *, dasd_ccw_req_t *,
struct irb *);
/* i/o control functions. */
int (*fill_geometry) (struct dasd_device_t *, struct hd_geometry *);
int (*fill_info) (struct dasd_device_t *, dasd_information2_t *);
} dasd_discipline_t;
extern dasd_discipline_t dasd_diag_discipline;
#ifdef CONFIG_DASD_DIAG
#define dasd_diag_discipline_pointer (&dasd_diag_discipline)
#else
#define dasd_diag_discipline_pointer (0)
#endif
typedef struct dasd_device_t {
/* Block device stuff. */
struct gendisk *gdp;
......@@ -267,6 +275,8 @@ typedef struct dasd_device_t {
unsigned int bp_block; /* bytes per block */
unsigned int s2b_shift; /* log2 (bp_block/512) */
int ro_flag; /* read-only flag */
int use_diag_flag; /* diag allowed flag */
/* Device discipline stuff. */
dasd_discipline_t *discipline;
......@@ -288,8 +298,8 @@ typedef struct dasd_device_t {
struct list_head erp_chunks;
/* Common i/o stuff. */
s390_dev_info_t devinfo;
devstat_t dev_status;
/* FIXME: remove the next */
int devno;
atomic_t tasklet_scheduled;
struct tasklet_struct tasklet;
......@@ -297,6 +307,9 @@ typedef struct dasd_device_t {
struct timer_list timer;
debug_info_t *debug_area;
struct ccw_device *cdev;
#ifdef CONFIG_DASD_PROFILE
dasd_profile_info_t profile;
#endif
......@@ -318,7 +331,6 @@ typedef struct {
unsigned int devindex;
unsigned short devno;
unsigned short features;
devreg_t *devreg;
dasd_device_t *device;
} dasd_devmap_t;
......@@ -427,17 +439,14 @@ void dasd_kfree_request(dasd_ccw_req_t *, dasd_device_t *);
void dasd_sfree_request(dasd_ccw_req_t *, dasd_device_t *);
static inline int
dasd_kmalloc_set_cda(ccw1_t *ccw, void *cda, dasd_device_t *device)
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 *);
void dasd_free_device(dasd_device_t *);
void dasd_enable_devices(int, int);
void dasd_disable_devices(int, int);
void dasd_discipline_add(dasd_discipline_t *);
void dasd_discipline_del(dasd_discipline_t *);
void dasd_enable_device(dasd_device_t *);
void dasd_set_target_state(dasd_device_t *, int);
void dasd_kick_device(dasd_device_t *);
......@@ -445,7 +454,6 @@ void dasd_add_request_head(dasd_ccw_req_t *);
void dasd_add_request_tail(dasd_ccw_req_t *); /* unused */
int dasd_start_IO(dasd_ccw_req_t *);
int dasd_term_IO(dasd_ccw_req_t *);
int dasd_oper_handler(int, devreg_t *);
void dasd_schedule_bh(dasd_device_t *);
int dasd_sleep_on(dasd_ccw_req_t *);
int dasd_sleep_on_immediatly(dasd_ccw_req_t *);
......@@ -453,6 +461,12 @@ int dasd_sleep_on_interruptible(dasd_ccw_req_t *);
void dasd_set_timer(dasd_device_t *, int);
void dasd_clear_timer(dasd_device_t *);
int dasd_cancel_req(dasd_ccw_req_t *); /* unused */
int dasd_generic_probe (struct ccw_device *cdev,
dasd_discipline_t *discipline);
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);
/* externals in dasd_devmap.c */
extern int dasd_max_devindex;
......@@ -463,20 +477,15 @@ 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_devmap_t *dasd_devmap_from_irq(int);
dasd_devmap_t *dasd_devmap_from_bdev(struct block_device *bdev);
dasd_device_t *dasd_get_device(dasd_devmap_t *);
void dasd_put_device(dasd_devmap_t *);
int dasd_devno(char *, char **);
int dasd_feature_list(char *, char **);
int dasd_parse(void);
int dasd_add_range(int, int, int);
/* externals in dasd_gendisk.c */
int dasd_gendisk_init(void);
void dasd_gendisk_exit(void);
int dasd_gendisk_major_index(int);
int dasd_gendisk_index_major(int);
struct gendisk *dasd_gendisk_alloc(int);
void dasd_setup_partitions(dasd_device_t *);
......@@ -498,22 +507,21 @@ dasd_ccw_req_t *dasd_default_erp_action(dasd_ccw_req_t *);
dasd_ccw_req_t *dasd_default_erp_postaction(dasd_ccw_req_t *);
dasd_ccw_req_t *dasd_alloc_erp_request(char *, int, int, dasd_device_t *);
void dasd_free_erp_request(dasd_ccw_req_t *, dasd_device_t *);
void dasd_log_sense(dasd_ccw_req_t *, struct irb *);
void dasd_log_ccw(dasd_ccw_req_t *, int, __u32);
/* externals in dasd_3370_erp.c */
dasd_era_t dasd_3370_erp_examine(dasd_ccw_req_t *, devstat_t *);
dasd_era_t dasd_3370_erp_examine(dasd_ccw_req_t *, struct irb *);
/* externals in dasd_3990_erp.c */
dasd_era_t dasd_3990_erp_examine(dasd_ccw_req_t *, devstat_t *);
dasd_era_t dasd_3990_erp_examine(dasd_ccw_req_t *, struct irb *);
dasd_ccw_req_t *dasd_3990_erp_action(dasd_ccw_req_t *);
dasd_ccw_req_t *dasd_2105_erp_action(dasd_ccw_req_t *);
void dasd_3990_erp_restart_queue(unsigned long);
/* externals in dasd_9336_erp.c */
dasd_era_t dasd_9336_erp_examine(dasd_ccw_req_t *, devstat_t *);
dasd_era_t dasd_9336_erp_examine(dasd_ccw_req_t *, struct irb *);
/* externals in dasd_9336_erp.c */
dasd_era_t dasd_9343_erp_examine(dasd_ccw_req_t *, devstat_t *);
dasd_era_t dasd_9343_erp_examine(dasd_ccw_req_t *, struct irb *);
dasd_ccw_req_t *dasd_9343_erp_action(dasd_ccw_req_t *);
#endif /* __KERNEL__ */
......
......@@ -19,7 +19,7 @@
#include <linux/blkpg.h>
#include <linux/blk.h>
#include <asm/irq.h>
#include <asm/ccwdev.h>
#include <asm/uaccess.h>
/* This is ugly... */
......@@ -125,7 +125,7 @@ dasd_ioctl(struct inode *inp, struct file *filp,
DBF_DEV_EVENT(DBF_INFO, device,
"unknown ioctl 0x%08x=%s'0x%x'%d(%d) data %8lx", no,
dir, _IOC_TYPE(no), _IOC_NR(no), _IOC_SIZE(no), data);
return -ENOTTY;
return -EINVAL;
}
static int
......@@ -137,24 +137,20 @@ dasd_ioctl_api_version(struct block_device *bdev, int no, long args)
/*
* Enable device.
* FIXME: how can we get here if the device is not already enabled?
* -arnd
*/
static int
dasd_ioctl_enable(struct block_device *bdev, int no, long args)
{
dasd_devmap_t *devmap;
dasd_device_t *device;
int devno;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
devmap = dasd_devmap_from_bdev(bdev);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device))
return PTR_ERR(device);
devno = device->devinfo.devno;
dasd_enable_devices(devno, devno);
dasd_put_device(devmap);
device = bdev->bd_disk->private_data;
if (device == NULL)
return -ENODEV;
dasd_enable_device(device);
return 0;
}
......@@ -164,16 +160,13 @@ dasd_ioctl_enable(struct block_device *bdev, int no, long args)
static int
dasd_ioctl_disable(struct block_device *bdev, int no, long args)
{
dasd_devmap_t *devmap;
dasd_device_t *device;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
devmap = dasd_devmap_from_bdev(bdev);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device))
return PTR_ERR(device);
device = bdev->bd_disk->private_data;
if (device == NULL)
return -ENODEV;
/*
* Man this is sick. We don't do a real disable but only downgrade
* the device to DASD_STATE_BASIC. The reason is that dasdfmt uses
......@@ -183,7 +176,6 @@ dasd_ioctl_disable(struct block_device *bdev, int no, long args)
* device is DASD_STATE_BASIC that allows to do basic i/o.
*/
dasd_set_target_state(device, DASD_STATE_BASIC);
dasd_put_device(devmap);
return 0;
}
......@@ -238,35 +230,28 @@ dasd_format(dasd_device_t * device, format_data_t * fdata)
static int
dasd_ioctl_format(struct block_device *bdev, int no, long args)
{
dasd_devmap_t *devmap;
dasd_device_t *device;
format_data_t fdata;
int rc;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (!args)
return -EINVAL;
/* fdata == NULL is no longer a valid arg to dasd_format ! */
devmap = dasd_devmap_from_bdev(bdev);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device))
return PTR_ERR(device);
device = bdev->bd_disk->private_data;
rc = 0;
if (devmap->features & DASD_FEATURE_READONLY)
rc = -EROFS;
else if (copy_from_user(&fdata, (void *) args, sizeof (format_data_t)))
rc = -EFAULT;
else if (bdev != bdev->bd_contains) {
if (device == NULL)
return -ENODEV;
if (device->ro_flag)
return -EROFS;
if (copy_from_user(&fdata, (void *) args, sizeof (format_data_t)))
return -EFAULT;
if (bdev != bdev->bd_contains) {
DEV_MESSAGE(KERN_WARNING, device, "%s",
"Cannot low-level format a partition");
rc = -EINVAL;
} else
rc = dasd_format(device, &fdata);
dasd_put_device(devmap);
return rc;
return -EINVAL;
}
return dasd_format(device, &fdata);
}
#ifdef CONFIG_DASD_PROFILE
......@@ -276,18 +261,16 @@ dasd_ioctl_format(struct block_device *bdev, int no, long args)
static int
dasd_ioctl_reset_profile(struct block_device *bdev, int no, long args)
{
dasd_devmap_t *devmap;
dasd_device_t *device;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
devmap = dasd_devmap_from_bdev(bdev);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device))
return PTR_ERR(device);
device = = bdev->bd_disk->private_data;
if (device == NULL)
return -ENODEV;
memset(&device->profile, 0, sizeof (dasd_profile_info_t));
dasd_put_device(devmap);
return 0;
}
......@@ -297,21 +280,16 @@ dasd_ioctl_reset_profile(struct block_device *bdev, int no, long args)
static int
dasd_ioctl_read_profile(struct block_device *bdev, int no, long args)
{
dasd_devmap_t *devmap;
dasd_device_t *device;
int rc;
devmap = dasd_devmap_from_bdev(bdev);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device))
return PTR_ERR(device);
rc = 0;
device = = bdev->bd_disk->private_data;
if (device == NULL)
return -ENODEV;
if (copy_to_user((long *) args, (long *) &device->profile,
sizeof (dasd_profile_info_t)))
rc = -EFAULT;
dasd_put_device(devmap);
return rc;
return -EFAULT;
return 0;
}
#else
static int
......@@ -333,40 +311,37 @@ dasd_ioctl_read_profile(struct block_device *bdev, int no, long args)
static int
dasd_ioctl_information(struct block_device *bdev, int no, long args)
{
dasd_devmap_t *devmap;
dasd_device_t *device;
dasd_information2_t *dasd_info;
unsigned long flags;
int rc;
struct ccw_device *cdev;
device = bdev->bd_disk->private_data;
if (device == NULL)
return -ENODEV;
devmap = dasd_devmap_from_bdev(bdev);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device))
return PTR_ERR(device);
if (!device->discipline->fill_info) {
dasd_put_device(devmap);
if (!device->discipline->fill_info)
return -EINVAL;
}
dasd_info = kmalloc(sizeof(dasd_information2_t), GFP_KERNEL);
if (dasd_info == NULL) {
dasd_put_device(devmap);
if (dasd_info == NULL)
return -ENOMEM;
}
rc = device->discipline->fill_info(device, dasd_info);
if (rc) {
dasd_put_device(devmap);
kfree(dasd_info);
return rc;
}
dasd_info->devno = device->devinfo.devno;
dasd_info->schid = device->devinfo.irq;
dasd_info->cu_type = device->devinfo.sid_data.cu_type;
dasd_info->cu_model = device->devinfo.sid_data.cu_model;
dasd_info->dev_type = device->devinfo.sid_data.dev_type;
dasd_info->dev_model = device->devinfo.sid_data.dev_model;
cdev = device->cdev;
dasd_info->devno = device->devno;
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;
dasd_info->dev_type = cdev->id.dev_type;
dasd_info->dev_model = cdev->id.dev_model;
dasd_info->open_count = atomic_read(&device->open_count);
dasd_info->status = device->state;
......@@ -378,8 +353,9 @@ dasd_ioctl_information(struct block_device *bdev, int no, long args)
(dasd_check_blocksize(device->bp_block)))
dasd_info->format = DASD_FORMAT_NONE;
dasd_info->features = devmap->features;
dasd_info->features |= device->ro_flag ? DASD_FEATURE_READONLY
: DASD_FEATURE_DEFAULT;
if (device->discipline)
memcpy(dasd_info->type, device->discipline->name, 4);
else
......@@ -397,10 +373,10 @@ dasd_ioctl_information(struct block_device *bdev, int no, long args)
spin_unlock_irqrestore(&device->lock, flags);
}
#endif /* DASD_EXTENDED_PROFILING */
spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
list_for_each(l, &device->ccw_queue)
dasd_info->chanq_len++;
spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq),
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev),
flags);
}
......@@ -410,7 +386,6 @@ dasd_ioctl_information(struct block_device *bdev, int no, long args)
sizeof (dasd_information2_t) :
sizeof (dasd_information_t))))
rc = -EFAULT;
dasd_put_device(devmap);
kfree(dasd_info);
return rc;
}
......@@ -421,9 +396,8 @@ dasd_ioctl_information(struct block_device *bdev, int no, long args)
static int
dasd_ioctl_set_ro(struct block_device *bdev, int no, long args)
{
dasd_devmap_t *devmap;
dasd_device_t *device;
int intval, i;
int intval;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
......@@ -432,18 +406,11 @@ dasd_ioctl_set_ro(struct block_device *bdev, int no, long args)
return -EINVAL;
if (get_user(intval, (int *) args))
return -EFAULT;
devmap = dasd_devmap_from_bdev(bdev);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device))
return PTR_ERR(device);
if (intval)
devmap->features |= DASD_FEATURE_READONLY;
else
devmap->features &= ~DASD_FEATURE_READONLY;
device = bdev->bd_disk->private_data;
if (device == NULL)
return -ENODEV;
set_disk_ro(bdev->bd_disk, intval);
device->ro_flag = intval;
dasd_put_device(devmap);
return 0;
}
......@@ -454,27 +421,24 @@ static int
dasd_ioctl_getgeo(struct block_device *bdev, int no, long args)
{
struct hd_geometry geo = { 0, };
dasd_devmap_t *devmap;
dasd_device_t *device;
int rc;
devmap = dasd_devmap_from_bdev(bdev);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device))
return PTR_ERR(device);
rc = 0;
if (device != NULL && device->discipline != NULL &&
device->discipline->fill_geometry != NULL) {
device->discipline->fill_geometry(device, &geo);
geo.start = get_start_sect(bdev);
if (copy_to_user((struct hd_geometry *) args, &geo,
sizeof (struct hd_geometry)))
rc = -EFAULT;
} else
rc = -EINVAL;
dasd_put_device(devmap);
return rc;
device = bdev->bd_disk->private_data;
if (device == NULL)
return -ENODEV;
if (device == NULL || device->discipline == NULL ||
device->discipline->fill_geometry == NULL)
return -EINVAL;
geo = (struct hd_geometry) {};
device->discipline->fill_geometry(device, &geo);
geo.start = get_start_sect(bdev) >> device->s2b_shift;
if (copy_to_user((struct hd_geometry *) args, &geo,
sizeof (struct hd_geometry)))
return -EFAULT;
return 0;
}
/*
......
......@@ -9,16 +9,19 @@
*
* /proc interface for the dasd driver.
*
* $Revision: 1.17 $
*
* History of changes
* 05/04/02 split from dasd.c, code restructuring.
*/
#include <linux/config.h>
#include <linux/version.h>
#include <linux/ctype.h>
#include <linux/seq_file.h>
#include <linux/vmalloc.h>
#include <asm/debug.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
/* This is ugly... */
......@@ -26,47 +29,9 @@
#include "dasd_int.h"
typedef struct {
char *data;
int len;
} tempinfo_t;
static struct proc_dir_entry *dasd_proc_root_entry = NULL;
static struct proc_dir_entry *dasd_devices_entry;
static struct proc_dir_entry *dasd_statistics_entry;
static ssize_t
dasd_generic_read(struct file *file, char *user_buf, size_t user_len,
loff_t * offset)
{
loff_t len;
tempinfo_t *p_info = (tempinfo_t *) file->private_data;
if (*offset >= p_info->len)
return 0; /* EOF */
len = p_info->len - *offset;
if (user_len < len)
len = user_len;
if (copy_to_user(user_buf, &(p_info->data[*offset]), len))
return -EFAULT;
(*offset) += len;
return len; /* number of bytes "read" */
}
static int
dasd_generic_close (struct inode *inode, struct file *file)
{
tempinfo_t *info;
info = (tempinfo_t *) file->private_data;
file->private_data = NULL;
if (info) {
if (info->data)
vfree (info->data);
kfree (info);
}
return 0;
}
static struct proc_dir_entry *dasd_devices_entry = NULL;
static struct proc_dir_entry *dasd_statistics_entry = NULL;
static inline char *
dasd_get_user_string(const char *user_buf, size_t user_len)
......@@ -88,189 +53,121 @@ dasd_get_user_string(const char *user_buf, size_t user_len)
return buffer;
}
static ssize_t
dasd_devices_write(struct file *file, const char *user_buf,
size_t user_len, loff_t * offset)
{
char *buffer, *str;
int add_or_set;
int from, to, features;
buffer = dasd_get_user_string(user_buf, user_len);
MESSAGE(KERN_INFO, "/proc/dasd/devices: '%s'", buffer);
/* Scan for "add " or "set ". */
for (str = buffer; isspace(*str); str++);
if (strncmp(str, "add", 3) == 0 && isspace(str[3]))
add_or_set = 0;
else if (strncmp(str, "set", 3) == 0 && isspace(str[3]))
add_or_set = 1;
else
goto out_error;
for (str = str + 4; isspace(*str); str++);
/* Scan for "device " and "range=" and ignore it. This is sick. */
if (strncmp(str, "device", 6) == 0 && isspace(str[6]))
for (str = str + 6; isspace(*str); str++);
if (strncmp(str, "range=", 6) == 0)
for (str = str + 6; isspace(*str); str++);
/* Scan device number range and feature string. */
to = from = dasd_devno(str, &str);
if (*str == '-') {
str++;
to = dasd_devno(str, &str);
}
features = dasd_feature_list(str, &str);
/* Negative numbers in from/to/features indicate errors */
if (from < 0 || to < 0 || from > 65546 || to > 65536 || features < 0)
goto out_error;
if (add_or_set == 0) {
dasd_add_range(from, to, features);
dasd_enable_devices(from, to);
} else {
for (; isspace(*str); str++);
if (strcmp(str, "on") == 0)
dasd_enable_devices(from, to);
else if (strcmp(str, "off") == 0)
dasd_disable_devices(from, to);
else
goto out_error;
}
kfree(buffer);
return user_len;
out_error:
MESSAGE(KERN_WARNING,
"/proc/dasd/devices: range parse error in '%s'",
buffer);
kfree(buffer);
return -EINVAL;
}
static inline int
dasd_devices_print(dasd_devmap_t *devmap, char *str)
static int
dasd_devices_show(struct seq_file *m, void *v)
{
dasd_devmap_t *devmap;
dasd_device_t *device;
char *substr;
int len;
device = dasd_get_device(devmap);
devmap = dasd_devmap_from_devindex((unsigned long) v - 1);
device = (devmap != NULL) ?
dasd_get_device(devmap) : ERR_PTR(-ENODEV);
if (IS_ERR(device))
return 0;
/* Print device number. */
len = sprintf(str, "%04x", devmap->devno);
seq_printf(m, "%04x", devmap->devno);
/* Print discipline string. */
if (device != NULL && device->discipline != NULL)
len += sprintf(str + len, "(%s)", device->discipline->name);
seq_printf(m, "(%s)", device->discipline->name);
else
len += sprintf(str + len, "(none)");
seq_printf(m, "(none)");
/* Print kdev. */
len += sprintf(str + len, " at (%3d:%3d)",
device->gdp->major, device->gdp->first_minor);
seq_printf(m, " at (%3d:%3d)",
device->gdp->major, device->gdp->first_minor);
/* Print device name. */
len += sprintf(str + len, " is %-7s", device->gdp->disk_name);
seq_printf(m, " is %-7s", device->gdp->disk_name);
/* Print devices features. */
substr = (devmap->features & DASD_FEATURE_READONLY) ? "(ro)" : " ";
len += sprintf(str + len, "%4s: ", substr);
substr = device->ro_flag ? "(ro)" : " ";
seq_printf(m, "%4s: ", substr);
/* Print device status information. */
if (device == NULL) {
len += sprintf(str + len, "unknown");
dasd_put_device(devmap);
return len;
}
switch (device->state) {
switch ((device != NULL) ? device->state : -1) {
case -1:
seq_printf(m, "unknown");
break;
case DASD_STATE_NEW:
len += sprintf(str + len, "new");
seq_printf(m, "new");
break;
case DASD_STATE_KNOWN:
len += sprintf(str + len, "detected");
seq_printf(m, "detected");
break;
case DASD_STATE_BASIC:
len += sprintf(str + len, "basic");
seq_printf(m, "basic");
break;
case DASD_STATE_ACCEPT:
len += sprintf(str + len, "accepted");
seq_printf(m, "accepted");
break;
case DASD_STATE_READY:
case DASD_STATE_ONLINE:
if (device->state < DASD_STATE_ONLINE)
len += sprintf(str + len, "fenced ");
else
len += sprintf(str + len, "active ");
seq_printf(m, "active ");
if (dasd_check_blocksize(device->bp_block))
len += sprintf(str + len, "n/f ");
seq_printf(m, "n/f ");
else
len += sprintf(str + len,
"at blocksize: %d, %ld blocks, %ld MB",
device->bp_block, device->blocks,
((device->bp_block >> 9) *
device->blocks) >> 11);
seq_printf(m,
"at blocksize: %d, %ld blocks, %ld MB",
device->bp_block, device->blocks,
((device->bp_block >> 9) *
device->blocks) >> 11);
break;
default:
len += sprintf(str + len, "no stat");
seq_printf(m, "no stat");
break;
}
dasd_put_device(devmap);
return len;
if (dasd_probeonly)
seq_printf(m, "(probeonly)");
seq_printf(m, "\n");
return 0;
}
static int
dasd_devices_open(struct inode *inode, struct file *file)
static void *dasd_devices_start(struct seq_file *m, loff_t *pos)
{
tempinfo_t *info;
int size, len;
int devindex;
info = (tempinfo_t *) kmalloc(sizeof (tempinfo_t), GFP_KERNEL);
if (info == NULL) {
MESSAGE(KERN_WARNING, "%s",
"No memory available for data (tempinfo)");
return -ENOMEM;
}
if (*pos >= dasd_max_devindex)
return NULL;
return (void *)((unsigned long) *pos + 1);
}
size = dasd_max_devindex * 128;
info->data = (char *) vmalloc (size);
if (size && info->data == NULL) {
MESSAGE(KERN_WARNING, "%s",
"No memory available for data (info->data)");
kfree(info);
return -ENOMEM;
}
file->private_data = (void *) info;
static void *dasd_devices_next(struct seq_file *m, void *v, loff_t *pos)
{
++*pos;
return dasd_devices_start(m, pos);
}
DBF_EVENT(DBF_NOTICE,
"procfs-area: %p, size 0x%x allocated", info->data, size);
static void dasd_devices_stop(struct seq_file *m, void *v)
{
}
len = 0;
for (devindex = 0; devindex < dasd_max_devindex; devindex++) {
dasd_devmap_t *devmap;
static struct seq_operations dasd_devices_seq_ops = {
.start = dasd_devices_start,
.next = dasd_devices_next,
.stop = dasd_devices_stop,
.show = dasd_devices_show,
};
devmap = dasd_devmap_from_devindex(devindex);
len += dasd_devices_print(devmap, info->data + len);
if (dasd_probeonly)
len += sprintf(info->data + len, "(probeonly)");
len += sprintf(info->data + len, "\n");
}
info->len = len;
if (len > size) {
printk("len = %i, size = %i\n", len, size);
BUG();
}
return 0;
static int dasd_devices_open(struct inode *inode, struct file *file)
{
return seq_open(file, &dasd_devices_seq_ops);
}
static struct file_operations dasd_devices_file_ops = {
.owner = THIS_MODULE,
.read = dasd_generic_read, /* read */
.write = dasd_devices_write, /* write */
.open = dasd_devices_open, /* open */
.release = dasd_generic_close, /* close */
.open = dasd_devices_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
static struct inode_operations dasd_devices_inode_ops = {
};
static inline int
dasd_calc_metrics(char *page, char **start, off_t off,
int count, int *eof, int len)
{
len = (len > off) ? len - off : 0;
if (len > count)
len = count;
if (len < count)
*eof = 1;
*start = page + off;
return len;
}
static inline char *
dasd_statistics_array(char *str, int *array, int shift)
......@@ -287,48 +184,28 @@ dasd_statistics_array(char *str, int *array, int shift)
}
static int
dasd_statistics_open(struct inode *inode, struct file *file)
dasd_statistics_read(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
tempinfo_t *info;
unsigned long len;
#ifdef CONFIG_DASD_PROFILE
dasd_profile_info_t *prof;
char *str;
int shift;
#endif
info = (tempinfo_t *) kmalloc(sizeof (tempinfo_t), GFP_KERNEL);
if (info == NULL) {
MESSAGE(KERN_WARNING, "%s",
"No memory available for data (tempinfo)");
return -ENOMEM;
}
file->private_data = (void *) info;
/* FIXME! determine space needed in a better way */
info->data = (char *) vmalloc (PAGE_SIZE);
if (info->data == NULL) {
MESSAGE(KERN_WARNING, "%s",
"No memory available for data (info->data)");
kfree(info);
file->private_data = NULL;
return -ENOMEM;
}
#ifdef CONFIG_DASD_PROFILE
/* check for active profiling */
if (dasd_profile_level == DASD_PROFILE_OFF) {
info->len = sprintf(info->data,
"Statistics are off - they might be "
len = sprintf(page, "Statistics are off - they might be "
"switched on using 'echo set on > "
"/proc/dasd/statistics'\n");
return 0;
return dasd_calc_metrics(page, start, off, count, eof, len);
}
prof = &dasd_global_profile;
/* prevent couter 'overflow' on output */
for (shift = 0; (prof->dasd_io_reqs >> shift) > 9999999; shift++);
str = info->data;
str = page;
str += sprintf(str, "%d dasd I/O requests\n", prof->dasd_io_reqs);
str += sprintf(str, "with %d sectors(512B each)\n",
prof->dasd_io_sects);
......@@ -358,22 +235,22 @@ dasd_statistics_open(struct inode *inode, struct file *file)
str = dasd_statistics_array(str, prof->dasd_io_time3, shift);
str += sprintf(str, "# of req in chanq at enqueuing (1..32) \n");
str = dasd_statistics_array(str, prof->dasd_io_nr_req, shift);
info->len = str - info->data;
len = str - page;
#else
info->len = sprintf(info->data,
"Statistics are not activated in this kernel\n");
len = sprintf(page, "Statistics are not activated in this kernel\n");
#endif
return 0;
return dasd_calc_metrics(page, start, off, count, eof, len);
}
static ssize_t
static int
dasd_statistics_write(struct file *file, const char *user_buf,
size_t user_len, loff_t * offset)
unsigned long user_len, void *data)
{
#ifdef CONFIG_DASD_PROFILE
char *buffer, *str;
if (user_len > 65536)
user_len = 65536;
buffer = dasd_get_user_string(user_buf, user_len);
MESSAGE(KERN_INFO, "/proc/dasd/statictics: '%s'", buffer);
......@@ -415,31 +292,22 @@ dasd_statistics_write(struct file *file, const char *user_buf,
#endif /* CONFIG_DASD_PROFILE */
}
static struct file_operations dasd_statistics_file_ops = {
.owner = THIS_MODULE,
.read = dasd_generic_read, /* read */
.write = dasd_statistics_write, /* write */
.open = dasd_statistics_open, /* open */
.release = dasd_generic_close, /* close */
};
static struct inode_operations dasd_statistics_inode_ops = {
};
int
dasd_proc_init(void)
{
dasd_proc_root_entry = proc_mkdir("dasd", &proc_root);
dasd_proc_root_entry->owner = THIS_MODULE;
dasd_devices_entry = create_proc_entry("devices",
S_IFREG | S_IRUGO | S_IWUSR,
dasd_proc_root_entry);
dasd_devices_entry->proc_fops = &dasd_devices_file_ops;
dasd_devices_entry->proc_iops = &dasd_devices_inode_ops;
dasd_devices_entry->owner = THIS_MODULE;
dasd_statistics_entry = create_proc_entry("statistics",
S_IFREG | S_IRUGO | S_IWUSR,
dasd_proc_root_entry);
dasd_statistics_entry->proc_fops = &dasd_statistics_file_ops;
dasd_statistics_entry->proc_iops = &dasd_statistics_inode_ops;
dasd_statistics_entry->read_proc = dasd_statistics_read;
dasd_statistics_entry->write_proc = dasd_statistics_write;
dasd_statistics_entry->owner = THIS_MODULE;
return 0;
}
......
......@@ -8,6 +8,8 @@
* any future changes wrt the API will result in a change of the APIVERSION reported
* to userspace by the DASDAPIVER-ioctl
*
* $Revision: 1.3 $
*
* History of changes (starts July 2000)
* 05/04/01 created by moving the kernel interface to drivers/s390/block/dasd_int.h
* 12/06/01 DASD_API_VERSION 2 - binary compatible to 0 (new BIODASDINFO2)
......@@ -224,8 +226,6 @@ typedef struct attrib_data_t {
#define BIODASDSLCK _IO(DASD_IOCTL_LETTER,4) /* steal lock */
/* reset profiling information of a device */
#define BIODASDPRRST _IO(DASD_IOCTL_LETTER,5)
/* enable PAV */
#define BIODASDENAPAV _IO(DASD_IOCTL_LETTER,6)
/* retrieve API version number */
......
......@@ -8,6 +8,8 @@
* any future changes wrt the API will result in a change of the APIVERSION reported
* to userspace by the DASDAPIVER-ioctl
*
* $Revision: 1.3 $
*
* History of changes (starts July 2000)
* 05/04/01 created by moving the kernel interface to drivers/s390/block/dasd_int.h
* 12/06/01 DASD_API_VERSION 2 - binary compatible to 0 (new BIODASDINFO2)
......@@ -224,8 +226,6 @@ typedef struct attrib_data_t {
#define BIODASDSLCK _IO(DASD_IOCTL_LETTER,4) /* steal lock */
/* reset profiling information of a device */
#define BIODASDPRRST _IO(DASD_IOCTL_LETTER,5)
/* enable PAV */
#define BIODASDENAPAV _IO(DASD_IOCTL_LETTER,6)
/* retrieve API version number */
......
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