Commit 04d81fff authored by Martin Schwidefsky's avatar Martin Schwidefsky Committed by Linus Torvalds

[PATCH] s390: dasd driver changes

From: Horst Hummel <horst.hummel@de.ibm.com>
From: Carsten Otte <cotte@de.ibm.com>
From: Stefan Weinhuber <wein@de.ibm.com>

dasd device driver changes:
 - Fix calculation of number of idal words needed for a channel program.
 - Fix race in i/o termination after request timeout.
 - Fix race in state change interrupt handling.
 - Fix call to BLKPG ioctl in dasd_destroy_partitions.
 - Integrate irb into dasd request to avoid kmalloc in the interrupt handler.
 - Store build clock for error recovery requests.
 - Remove unused cpu variable from dasd_ext_handler.
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 17db3da8
......@@ -7,7 +7,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
*
* $Revision: 1.142 $
* $Revision: 1.146 $
*/
#include <linux/config.h>
......@@ -623,8 +623,6 @@ dasd_kfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device)
clear_normalized_cda(ccw);
} while (ccw++->flags & (CCW_FLAG_CC | CCW_FLAG_DC));
#endif
if (cqr->dstat != NULL)
kfree(cqr->dstat);
debug_text_event ( dasd_debug_area, 1, "FREE");
debug_int_event ( dasd_debug_area, 1, (long) cqr);
if (cqr->cpaddr != NULL)
......@@ -640,8 +638,6 @@ dasd_sfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device)
{
unsigned long flags;
if (cqr->dstat != NULL)
kfree(cqr->dstat);
debug_text_event(dasd_debug_area, 1, "FREE");
debug_int_event(dasd_debug_area, 1, (long) cqr);
spin_lock_irqsave(&device->mem_lock, flags);
......@@ -673,7 +669,8 @@ dasd_check_cqr(struct dasd_ccw_req *cqr)
}
/*
* Terminate the current i/o and set the request to failed.
* Terminate the current i/o and set the request to clear_pending.
* Timer keeps device runnig.
* ccw_device_clear can fail if the i/o subsystem
* is in a bad mood.
*/
......@@ -695,7 +692,7 @@ dasd_term_IO(struct dasd_ccw_req * cqr)
case 0: /* termination successful */
if (cqr->retries > 0) {
cqr->retries--;
cqr->status = DASD_CQR_QUEUED;
cqr->status = DASD_CQR_CLEAR;
} else
cqr->status = DASD_CQR_FAILED;
cqr->stopclk = get_clock();
......@@ -826,41 +823,6 @@ dasd_clear_timer(struct dasd_device *device)
del_timer(&device->timer);
}
/*
* Handles the state change pending interrupt.
*/
static void
do_state_change_pending(void *data)
{
struct {
struct work_struct work;
struct dasd_device *device;
} *p;
struct dasd_device *device;
struct dasd_ccw_req *cqr;
struct list_head *l, *n;
unsigned long flags;
p = data;
device = p->device;
DBF_EVENT(DBF_NOTICE, "State change Interrupt for bus_id %s",
device->cdev->dev.bus_id);
device->stopped &= ~DASD_STOPPED_PENDING;
/* restart all 'running' IO on queue */
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
list_for_each_safe(l, n, &device->ccw_queue) {
cqr = list_entry(l, struct dasd_ccw_req, list);
if (cqr->status == DASD_CQR_IN_IO)
cqr->status = DASD_CQR_QUEUED;
}
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
dasd_set_timer (device, 0);
dasd_schedule_bh(device);
dasd_put_device(device);
kfree(p);
}
static void
dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm)
{
......@@ -896,19 +858,20 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm)
static void
dasd_handle_state_change_pending(struct dasd_device *device)
{
struct {
struct work_struct work;
struct dasd_device *device;
} *p;
struct dasd_ccw_req *cqr;
struct list_head *l, *n;
p = kmalloc(sizeof(*p), GFP_ATOMIC);
if (p == NULL)
/* No memory, let the timeout do the reactivation. */
return;
INIT_WORK(&p->work, (void *) do_state_change_pending, p);
p->device = device;
dasd_get_device(device);
schedule_work(&p->work);
device->stopped &= ~DASD_STOPPED_PENDING;
/* restart all 'running' IO on queue */
list_for_each_safe(l, n, &device->ccw_queue) {
cqr = list_entry(l, struct dasd_ccw_req, list);
if (cqr->status == DASD_CQR_IN_IO) {
cqr->status = DASD_CQR_QUEUED;
}
}
dasd_clear_timer(device);
dasd_schedule_bh(device);
}
/*
......@@ -944,8 +907,9 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
now = get_clock();
DBF_EVENT(DBF_DEBUG, "Interrupt: stat %02x, bus_id %s",
irb->scsw.dstat, cdev->dev.bus_id);
DBF_EVENT(DBF_ERR, "Interrupt: bus_id %s CS/DS %04x ip %08x",
cdev->dev.bus_id, ((irb->scsw.cstat<<8)|irb->scsw.dstat),
(unsigned int) intparm);
/* first of all check for state change pending interrupt */
mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
......@@ -959,14 +923,12 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
}
cqr = (struct dasd_ccw_req *) intparm;
/*
* check status - the request might have been killed
* because of dyn detach
*/
if (cqr->status != DASD_CQR_IN_IO) {
/* check for unsolicited interrupts */
if (cqr == NULL) {
MESSAGE(KERN_DEBUG,
"invalid status: bus_id %s, status %02x",
cdev->dev.bus_id, cqr->status);
"unsolicited interrupt received: bus_id %s",
cdev->dev.bus_id);
return;
}
......@@ -978,6 +940,22 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
return;
}
/* Check for clear pending */
if (cqr->status == DASD_CQR_CLEAR &&
irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) {
cqr->status = DASD_CQR_QUEUED;
dasd_clear_timer(device);
dasd_schedule_bh(device);
return;
}
/* check status - the request might have been killed by dyn detach */
if (cqr->status != DASD_CQR_IN_IO) {
MESSAGE(KERN_DEBUG,
"invalid status: bus_id %s, status %02x",
cdev->dev.bus_id, cqr->status);
return;
}
DBF_DEV_EVENT(DBF_DEBUG, device, "Int: CS/DS 0x%04x",
((irb->scsw.cstat << 8) | irb->scsw.dstat));
......@@ -1015,13 +993,7 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
}
}
} else { /* error */
if (cqr->dstat == NULL)
cqr->dstat = kmalloc(sizeof(struct irb), GFP_ATOMIC);
if (cqr->dstat)
memcpy(cqr->dstat, irb, sizeof (struct irb));
else
DEV_MESSAGE(KERN_ERR, device, "%s",
"no memory for dstat...ignoring");
memcpy(&cqr->irb, irb, sizeof (struct irb));
#ifdef ERP_DEBUG
/* dump sense data */
dasd_log_sense(cqr, irb);
......@@ -1096,11 +1068,11 @@ __dasd_process_ccw_queue(struct dasd_device * device,
/* Process requests with DASD_CQR_ERROR */
if (cqr->status == DASD_CQR_ERROR) {
cqr->retries--;
if (cqr->dstat->scsw.fctl & SCSW_FCTL_HALT_FUNC) {
if (cqr->irb.scsw.fctl & SCSW_FCTL_HALT_FUNC) {
cqr->status = DASD_CQR_FAILED;
cqr->stopclk = get_clock();
} else {
if (cqr->dstat->esw.esw0.erw.cons) {
if (cqr->irb.esw.esw0.erw.cons) {
erp_fn = device->discipline->erp_action(cqr);
erp_fn(cqr);
} else
......@@ -1221,8 +1193,8 @@ __dasd_check_expire(struct dasd_device * device)
if (cqr->status == DASD_CQR_IN_IO && cqr->expires != 0) {
if (time_after_eq(jiffies, cqr->expires + cqr->starttime)) {
if (device->discipline->term_IO(cqr) != 0)
/* Hmpf, try again in 1/100 sec */
dasd_set_timer(device, 1);
/* Hmpf, try again in 1/10 sec */
dasd_set_timer(device, 10);
}
}
}
......@@ -1246,9 +1218,9 @@ __dasd_start_head(struct dasd_device * device)
rc = device->discipline->start_IO(cqr);
if (rc == 0)
dasd_set_timer(device, cqr->expires);
else if (rc == -EBUSY)
/* Hmpf, try again in 1/100 sec */
dasd_set_timer(device, 1);
else
/* Hmpf, try again in 1/2 sec */
dasd_set_timer(device, 50);
}
}
......@@ -1980,7 +1952,7 @@ dasd_init(void)
init_waitqueue_head(&dasd_init_waitq);
/* register 'common' DASD debug area, used faor all DBF_XXX calls */
/* register 'common' DASD debug area, used for all DBF_XXX calls */
dasd_debug_area = debug_register("dasd", 0, 2, 8 * sizeof (long));
if (dasd_debug_area == NULL) {
rc = -ENOMEM;
......
......@@ -5,7 +5,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000, 2001
*
* $Revision: 1.30 $
* $Revision: 1.33 $
*/
#include <linux/timer.h>
......@@ -301,15 +301,15 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp)
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);
erp->lpm = LPM_ANYPATH & ~(erp->irb.esw.esw0.sublog.lpum);
else
erp->lpm &= ~(erp->dstat->esw.esw0.sublog.lpum);
erp->lpm &= ~(erp->irb.esw.esw0.sublog.lpum);
if ((erp->lpm & opm) != 0x00) {
DEV_MESSAGE(KERN_DEBUG, device,
"try alternate lpm=%x (lpum=%x / opm=%x)",
erp->lpm, erp->dstat->esw.esw0.sublog.lpum, opm);
erp->lpm, erp->irb.esw.esw0.sublog.lpum, opm);
/* reset status to queued to handle the request again... */
if (erp->status > DASD_CQR_QUEUED)
......@@ -319,7 +319,7 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp)
DEV_MESSAGE(KERN_ERR, device,
"No alternate channel path left (lpum=%x / "
"opm=%x) -> permanent error",
erp->dstat->esw.esw0.sublog.lpum, opm);
erp->irb.esw.esw0.sublog.lpum, opm);
/* post request with permanent error */
if (erp->status > DASD_CQR_QUEUED)
......@@ -443,6 +443,10 @@ dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense)
/* interrupt (this enables easier enqueing of the cqr) */
if (erp->function != dasd_3990_erp_action_4) {
DEV_MESSAGE(KERN_INFO, device,
"dasd_3990_erp_action_4: first time retry"
"%s", " ");
erp->retries = 256;
erp->function = dasd_3990_erp_action_4;
......@@ -1677,7 +1681,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * 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->scsw.cpa;
cpa = default_erp->refers->irb.scsw.cpa;
if (cpa == 0) {
......@@ -1763,7 +1767,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
erp->magic = default_erp->magic;
erp->expires = 0;
erp->retries = 256;
cqr->buildclk = get_clock();
erp->buildclk = get_clock();
erp->status = DASD_CQR_FILLED;
/* remove the default erp */
......@@ -1823,7 +1827,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * 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->scsw.cpa;
cpa = previous_erp->irb.scsw.cpa;
if (cpa == 0) {
......@@ -2233,7 +2237,7 @@ dasd_3990_erp_inspect(struct dasd_ccw_req * erp)
struct dasd_ccw_req *erp_new = NULL;
/* sense data are located in the refers record of the */
/* already set up new ERP ! */
char *sense = erp->refers->dstat->ecw;
char *sense = erp->refers->irb.ecw;
/* distinguish between 24 and 32 byte sense data */
if (sense[27] & DASD_SENSE_BIT_0) {
......@@ -2306,6 +2310,7 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr)
erp->magic = cqr->magic;
erp->expires = 0;
erp->retries = 256;
erp->buildclk = get_clock();
erp->status = DASD_CQR_FILLED;
......@@ -2369,14 +2374,14 @@ dasd_3990_erp_error_match(struct dasd_ccw_req *cqr1, struct dasd_ccw_req *cqr2)
{
/* check failed CCW */
if (cqr1->dstat->scsw.cpa != cqr2->dstat->scsw.cpa) {
if (cqr1->irb.scsw.cpa != cqr2->irb.scsw.cpa) {
// return 0; /* CCW doesn't match */
}
/* check sense data; byte 0-2,25,27 */
if (!((memcmp (cqr1->dstat->ecw, cqr2->dstat->ecw, 3) == 0) &&
(cqr1->dstat->ecw[27] == cqr2->dstat->ecw[27]) &&
(cqr1->dstat->ecw[25] == cqr2->dstat->ecw[25]))) {
if (!((memcmp (cqr1->irb.ecw, cqr2->irb.ecw, 3) == 0) &&
(cqr1->irb.ecw[27] == cqr2->irb.ecw[27]) &&
(cqr1->irb.ecw[25] == cqr2->irb.ecw[25]))) {
return 0; /* sense doesn't match */
}
......@@ -2449,7 +2454,7 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp)
{
struct dasd_device *device = erp->device;
char *sense = erp->dstat->ecw;
char *sense = erp->irb.ecw;
/* check for 24 byte sense ERP */
if ((erp->function == dasd_3990_erp_bus_out) ||
......@@ -2562,7 +2567,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head,
if (erp->retries > 0) {
char *sense = erp->refers->dstat->ecw;
char *sense = erp->refers->irb.ecw;
/* check for special retries */
if (erp->function == dasd_3990_erp_action_4) {
......@@ -2620,7 +2625,7 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr)
struct dasd_ccw_req *erp = NULL;
struct dasd_device *device = cqr->device;
__u32 cpa = cqr->dstat->scsw.cpa;
__u32 cpa = cqr->irb.scsw.cpa;
#ifdef ERP_DEBUG
/* print current erp_chain */
......@@ -2641,8 +2646,8 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr)
#endif /* ERP_DEBUG */
/* double-check if current erp/cqr was successfull */
if ((cqr->dstat->scsw.cstat == 0x00) &&
(cqr->dstat->scsw.dstat == (DEV_STAT_CHN_END|DEV_STAT_DEV_END))) {
if ((cqr->irb.scsw.cstat == 0x00) &&
(cqr->irb.scsw.dstat == (DEV_STAT_CHN_END|DEV_STAT_DEV_END))) {
DEV_MESSAGE(KERN_DEBUG, device,
"ERP called for successful request %p"
......@@ -2653,7 +2658,7 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr)
return cqr;
}
/* check if sense data are available */
if (!cqr->dstat->ecw) {
if (!cqr->irb.ecw) {
DEV_MESSAGE(KERN_DEBUG, device,
"ERP called witout sense data avail ..."
"request %p - NO ERP possible", cqr);
......
......@@ -11,7 +11,7 @@
* functions may not be called from interrupt context. In particular
* dasd_get_device is a no-no from interrupt context.
*
* $Revision: 1.28 $
* $Revision: 1.30 $
*/
#include <linux/config.h>
......@@ -430,16 +430,9 @@ dasd_devmap_from_cdev(struct ccw_device *cdev)
{
struct dasd_devmap *devmap;
if (cdev->dev.driver_data)
return (struct dasd_devmap *) cdev->dev.driver_data;
devmap = dasd_find_busid(cdev->dev.bus_id);
if (!IS_ERR(devmap)) {
cdev->dev.driver_data = devmap;
return devmap;
}
devmap = dasd_add_busid(cdev->dev.bus_id, DASD_FEATURE_DEFAULT);
if (!IS_ERR(devmap))
cdev->dev.driver_data = devmap;
if (IS_ERR(devmap))
devmap = dasd_add_busid(cdev->dev.bus_id, DASD_FEATURE_DEFAULT);
return devmap;
}
......@@ -456,6 +449,7 @@ dasd_create_device(struct ccw_device *cdev)
devmap = dasd_devmap_from_cdev(cdev);
if (IS_ERR(devmap))
return (void *) devmap;
cdev->dev.driver_data = devmap;
device = dasd_alloc_device();
if (IS_ERR(device))
......
......@@ -6,7 +6,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
* $Revision: 1.36 $
* $Revision: 1.37 $
*/
#include <linux/config.h>
......@@ -158,7 +158,7 @@ dasd_ext_handler(struct pt_regs *regs, __u16 code)
unsigned long long expires;
unsigned long flags;
char status;
int ip, cpu;
int ip;
/*
* Get the external interruption subcode. VM stores
......@@ -171,8 +171,6 @@ dasd_ext_handler(struct pt_regs *regs, __u16 code)
status = *((char *) &S390_lowcore.ext_params + 5);
ip = S390_lowcore.ext_params;
cpu = smp_processor_id();
if (!ip) { /* no intparm: unsolicited interrupt */
MESSAGE(KERN_DEBUG, "%s", "caught unsolicited interrupt");
return;
......
......@@ -7,7 +7,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
* $Revision: 1.55 $
* $Revision: 1.57 $
*/
#include <linux/config.h>
......@@ -983,8 +983,8 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
return ERR_PTR(-EINVAL);
count += bv->bv_len >> (device->s2b_shift + 9);
#if defined(CONFIG_ARCH_S390X)
cidaw += idal_nr_words(page_address(bv->bv_page) +
bv->bv_offset, bv->bv_len);
if (idal_is_needed (page_address(bv->bv_page), bv->bv_len))
cidaw += bv->bv_len >> (device->s2b_shift + 9);
#endif
}
}
......
......@@ -7,7 +7,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
*
* $Revision: 1.11 $
* $Revision: 1.12 $
*/
#include <linux/config.h>
......@@ -77,8 +77,6 @@ dasd_free_erp_request(struct dasd_ccw_req * cqr, struct dasd_device * device)
{
unsigned long flags;
if (cqr->dstat != NULL)
kfree(cqr->dstat);
debug_text_event(dasd_debug_area, 1, "FREE");
debug_int_event(dasd_debug_area, 1, (long) cqr);
spin_lock_irqsave(&device->mem_lock, flags);
......
......@@ -4,7 +4,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
* $Revision: 1.33 $
* $Revision: 1.34 $
*/
#include <linux/config.h>
......@@ -270,8 +270,8 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req)
return ERR_PTR(-EINVAL);
count += bv->bv_len >> (device->s2b_shift + 9);
#if defined(CONFIG_ARCH_S390X)
cidaw += idal_nr_words(page_address(bv->bv_page) +
bv->bv_offset, bv->bv_len);
if (idal_is_needed (page_address(bv->bv_page), bv->bv_len))
cidaw += bv->bv_len / blksize;
#endif
}
}
......
......@@ -9,7 +9,7 @@
*
* gendisk related functions for the dasd driver.
*
* $Revision: 1.46 $
* $Revision: 1.48 $
*/
#include <linux/config.h>
......@@ -152,8 +152,9 @@ dasd_destroy_partitions(struct dasd_device * device)
memset(&bpart, sizeof(struct blkpg_partition), 0);
memset(&barg, sizeof(struct blkpg_ioctl_arg), 0);
barg.data = &bpart;
barg.op = BLKPG_DEL_PARTITION;
for (bpart.pno = device->gdp->minors - 1; bpart.pno > 0; bpart.pno--)
ioctl_by_bdev(bdev, BLKPG_DEL_PARTITION, (unsigned long) &barg);
ioctl_by_bdev(bdev, BLKPG, (unsigned long) &barg);
invalidate_partition(device->gdp, 0);
/* Matching blkdev_put to the blkdev_get in dasd_scan_partitions. */
......
......@@ -6,7 +6,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
* $Revision: 1.58 $
* $Revision: 1.60 $
*/
#ifndef DASD_INT_H
......@@ -168,7 +168,7 @@ struct dasd_ccw_req {
void *data; /* pointer to data area */
/* these are important for recovering erroneous requests */
struct irb *dstat; /* device status in case of an error */
struct irb irb; /* device status in case of an error */
struct dasd_ccw_req *refers; /* ERP-chain queueing. */
void *function; /* originating ERP action */
......@@ -192,6 +192,7 @@ struct dasd_ccw_req {
#define DASD_CQR_DONE 0x03 /* request is completed successfully */
#define DASD_CQR_ERROR 0x04 /* request is completed with error */
#define DASD_CQR_FAILED 0x05 /* request is finally failed */
#define DASD_CQR_CLEAR 0x06 /* request is clear pending */
/* per dasd_ccw_req flags */
#define DASD_CQR_FLAGS_USE_ERP 0 /* use ERP for this request */
......
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