* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
*
- * $Revision: 1.139 $
+ * $Revision: 1.156 $
*/
#include <linux/config.h>
* SECTION: exported variables of dasd.c
*/
debug_info_t *dasd_debug_area;
+struct dasd_discipline *dasd_diag_discipline_pointer;
MODULE_AUTHOR("Holger Smolinski <Holger.Smolinski@de.ibm.com>");
MODULE_DESCRIPTION("Linux on S/390 DASD device driver,"
device->debug_area = debug_register(device->cdev->dev.bus_id, 0, 2,
8 * sizeof (long));
debug_register_view(device->debug_area, &debug_sprintf_view);
- debug_set_level(device->debug_area, DBF_ERR);
+ debug_set_level(device->debug_area, DBF_EMERG);
DBF_DEV_EVENT(DBF_EMERG, device, "%s", "debug area created");
device->state = DASD_STATE_BASIC;
if ( magic == NULL || datasize > 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);
- debug_int_event ( dasd_debug_area, 1, cplength);
- debug_int_event ( dasd_debug_area, 1, datasize);
cqr = kmalloc(sizeof(struct dasd_ccw_req), GFP_ATOMIC);
if (cqr == NULL)
if ( magic == NULL || datasize > 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);
- debug_int_event ( dasd_debug_area, 1, cplength);
- debug_int_event ( dasd_debug_area, 1, datasize);
size = (sizeof(struct dasd_ccw_req) + 7L) & -8L;
if (cplength > 0)
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)
kfree(cqr->cpaddr);
if (cqr->data != NULL)
{
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);
dasd_free_chunk(&device->ccw_chunks, cqr);
spin_unlock_irqrestore(&device->mem_lock, flags);
}
/*
- * 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.
*/
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();
+ DBF_DEV_EVENT(DBF_DEBUG, device,
+ "terminate cqr %p successful",
+ cqr);
break;
case -ENODEV:
DBF_DEV_EVENT(DBF_ERR, device, "%s",
if (rc)
return rc;
device = (struct dasd_device *) cqr->device;
+ if (cqr->retries < 0) {
+ DEV_MESSAGE(KERN_DEBUG, device,
+ "start_IO: request %p (%02x/%i) - no retry left.",
+ cqr, cqr->status, cqr->retries);
+ cqr->status = DASD_CQR_FAILED;
+ return -EIO;
+ }
cqr->startclk = get_clock();
cqr->starttime = jiffies;
+ cqr->retries--;
rc = ccw_device_start(device->cdev, cqr->cpaddr, (long) cqr,
cqr->lpm, 0);
switch (rc) {
case 0:
cqr->status = DASD_CQR_IN_IO;
+ DBF_DEV_EVENT(DBF_DEBUG, device,
+ "start_IO: request %p started successful",
+ cqr);
break;
case -EBUSY:
DBF_DEV_EVENT(DBF_ERR, device, "%s",
- "device busy, retry later");
+ "start_IO: device busy, retry later");
break;
case -ETIMEDOUT:
DBF_DEV_EVENT(DBF_ERR, device, "%s",
- "request timeout - terminated");
+ "start_IO: request timeout, retry later");
+ break;
case -ENODEV:
case -EIO:
- cqr->status = DASD_CQR_FAILED;
- cqr->stopclk = cqr->startclk;
- dasd_schedule_bh(device);
+ DBF_DEV_EVENT(DBF_ERR, device, "%s",
+ "start_IO: device gone, retry");
break;
default:
DEV_MESSAGE(KERN_ERR, 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)
{
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);
}
/*
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;
}
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;
}
return;
}
- DBF_DEV_EVENT(DBF_DEBUG, device, "Int: CS/DS 0x%04x",
- ((irb->scsw.cstat << 8) | irb->scsw.dstat));
+ /* 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 for cqr %p",
+ ((irb->scsw.cstat << 8) | irb->scsw.dstat), cqr);
/* Find out the appropriate era_action. */
if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC)
if (device->discipline->start_IO(next) == 0)
expires = next->expires;
else
- MESSAGE(KERN_WARNING, "%s",
- "Interrupt fastpath failed!");
+ DEV_MESSAGE(KERN_DEBUG, device, "%s",
+ "Interrupt fastpath "
+ "failed!");
}
}
} 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
- MESSAGE(KERN_ERR, "%s",
- "no memory for dstat...ignoring");
+ memcpy(&cqr->irb, irb, sizeof (struct irb));
#ifdef ERP_DEBUG
/* dump sense data */
dasd_log_sense(cqr, irb);
break;
/* 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) {
- erp_fn = device->discipline->erp_action(cqr);
+ if (cqr->irb.esw.esw0.erw.cons) {
+ erp_fn = device->discipline->
+ erp_action(cqr);
erp_fn(cqr);
} else
dasd_default_erp_action(cqr);
dasd_end_request_cb(struct dasd_ccw_req * cqr, void *data)
{
struct request *req;
+ struct dasd_device *device;
+ int status;
req = (struct request *) data;
- dasd_profile_end(cqr->device, cqr, req);
- spin_lock_irq(&cqr->device->request_queue_lock);
- dasd_end_request(req, (cqr->status == DASD_CQR_DONE));
- spin_unlock_irq(&cqr->device->request_queue_lock);
- dasd_sfree_request(cqr, cqr->device);
+ device = cqr->device;
+ dasd_profile_end(device, cqr, req);
+ status = cqr->device->discipline->free_cp(cqr,req);
+ spin_lock_irq(&device->request_queue_lock);
+ dasd_end_request(req, status);
+ spin_unlock_irq(&device->request_queue_lock);
}
req = elv_next_request(queue);
if (test_bit(DASD_FLAG_RO, &device->flags) &&
rq_data_dir(req) == WRITE) {
- DBF_EVENT(DBF_ERR,
- "(%s) Rejecting write request %p",
- device->cdev->dev.bus_id,
- req);
+ DBF_DEV_EVENT(DBF_ERR, device,
+ "Rejecting write request %p",
+ req);
blkdev_dequeue_request(req);
dasd_end_request(req, 0);
continue;
if (IS_ERR(cqr)) {
if (PTR_ERR(cqr) == -ENOMEM)
break; /* terminate request queue loop */
- DBF_EVENT(DBF_ERR,
- "(%s) CCW creation failed on request %p",
- device->cdev->dev.bus_id,
- req);
+ DBF_DEV_EVENT(DBF_ERR, device,
+ "CCW creation failed (rc=%ld) "
+ "on request %p",
+ PTR_ERR(cqr), req);
blkdev_dequeue_request(req);
dasd_end_request(req, 0);
continue;
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);
}
}
}
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);
}
}
}
/*
- * Allocate and initialize request queue.
+ * Allocate and initialize request queue and default I/O scheduler.
*/
static int
dasd_alloc_queue(struct dasd_device * device)
{
+ int rc;
+
device->request_queue = blk_init_queue(do_dasd_request,
&device->request_queue_lock);
if (device->request_queue == NULL)
return -ENOMEM;
device->request_queue->queuedata = device;
-#if 0
- elevator_exit(device->request_queue);
- rc = elevator_init(device->request_queue, &elevator_noop);
+
+ elevator_exit(device->request_queue->elevator);
+ rc = elevator_init(device->request_queue, "deadline");
if (rc) {
blk_cleanup_queue(device->request_queue);
return rc;
}
-#endif
return 0;
}
}
if (dasd_probeonly) {
- MESSAGE(KERN_INFO,
- "No access to device %s due to probeonly mode",
- disk->disk_name);
+ DEV_MESSAGE(KERN_INFO, device, "%s",
+ "No access to device due to probeonly mode");
rc = -EPERM;
goto out;
}
dasd_schedule_bh(device);
} else {
list_for_each_entry(cqr, &device->ccw_queue, list)
- if (cqr->status == DASD_CQR_IN_IO)
+ if (cqr->status == DASD_CQR_IN_IO) {
cqr->status = DASD_CQR_QUEUED;
+ cqr->retries++;
+ }
device->stopped |= DASD_STOPPED_DC_WAIT;
dasd_set_timer(device, 0);
}
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;
goto failed;
}
- debug_register_view(dasd_debug_area, &debug_hex_ascii_view);
- debug_set_level(dasd_debug_area, DBF_ERR);
+ debug_register_view(dasd_debug_area, &debug_sprintf_view);
+ debug_set_level(dasd_debug_area, DBF_EMERG);
DBF_EVENT(DBF_EMERG, "%s", "debug area created");
+ dasd_diag_discipline_pointer = NULL;
+
rc = devfs_mk_dir("dasd");
if (rc)
goto failed;
module_exit(dasd_exit);
EXPORT_SYMBOL(dasd_debug_area);
+EXPORT_SYMBOL(dasd_diag_discipline_pointer);
EXPORT_SYMBOL(dasd_add_request_head);
EXPORT_SYMBOL(dasd_add_request_tail);