X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fs390%2Fblock%2Fdasd.c;h=cfb1fff3787caafd63fe05a2c36587bfdb8f19a5;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=ab9f28443436e469d8f852947fd8094e9897c607;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index ab9f28443..cfb1fff37 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -7,7 +7,6 @@ * Bugreports.to..: * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001 * - * $Revision: 1.139 $ */ #include @@ -18,6 +17,7 @@ #include #include #include +#include #include #include @@ -37,12 +37,12 @@ * SECTION: exported variables of dasd.c */ debug_info_t *dasd_debug_area; +struct dasd_discipline *dasd_diag_discipline_pointer; MODULE_AUTHOR("Holger Smolinski "); MODULE_DESCRIPTION("Linux on S/390 DASD device driver," " Copyright 2000 IBM Corporation"); MODULE_SUPPORTED_DEVICE("dasd"); -MODULE_PARM(dasd, "1-" __MODULE_STRING(256) "s"); MODULE_LICENSE("GPL"); /* @@ -70,10 +70,9 @@ dasd_alloc_device(void) { struct dasd_device *device; - device = kmalloc(sizeof (struct dasd_device), GFP_ATOMIC); + device = kzalloc(sizeof (struct dasd_device), GFP_ATOMIC); if (device == NULL) return ERR_PTR(-ENOMEM); - memset(device, 0, sizeof (struct dasd_device)); /* open_count = 0 means device online but not in use */ atomic_set(&device->open_count, -1); @@ -114,8 +113,7 @@ dasd_alloc_device(void) void dasd_free_device(struct dasd_device *device) { - if (device->private) - kfree(device->private); + kfree(device->private); free_page((unsigned long) device->erp_mem); free_pages((unsigned long) device->ccw_mem, 1); kfree(device); @@ -151,8 +149,15 @@ dasd_state_new_to_known(struct dasd_device *device) static inline void dasd_state_known_to_new(struct dasd_device * device) { + /* Disable extended error reporting for this device. */ + dasd_eer_disable(device); /* Forget the discipline information. */ + if (device->discipline) + module_put(device->discipline->owner); device->discipline = NULL; + if (device->base_discipline) + module_put(device->base_discipline->owner); + device->base_discipline = NULL; device->state = DASD_STATE_NEW; dasd_free_queue(device); @@ -175,10 +180,10 @@ dasd_state_known_to_basic(struct dasd_device * device) return rc; /* register 'device' debug area, used for all DBF_DEV_XXX calls */ - device->debug_area = debug_register(device->cdev->dev.bus_id, 0, 2, + device->debug_area = debug_register(device->cdev->dev.bus_id, 1, 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; @@ -210,9 +215,10 @@ dasd_state_basic_to_known(struct dasd_device * device) * interrupt for this detection ccw uses the kernel event daemon to * trigger the call to dasd_change_state. All this is done in the * discipline code, see dasd_eckd.c. - * After the analysis ccw is done (do_analysis returned 0 or error) - * the block device is setup. Either a fake disk is added to allow - * formatting or a proper device request queue is created. + * After the analysis ccw is done (do_analysis returned 0) the block + * device is setup. + * In case the analysis returns an error, the device setup is stopped + * (a fake disk was already added to allow formatting). */ static inline int dasd_state_basic_to_ready(struct dasd_device * device) @@ -222,13 +228,19 @@ dasd_state_basic_to_ready(struct dasd_device * device) rc = 0; if (device->discipline->do_analysis != NULL) rc = device->discipline->do_analysis(device); - if (rc) + if (rc) { + if (rc != -EAGAIN) + device->state = DASD_STATE_UNFMT; return rc; + } + /* make disk known with correct capacity */ dasd_setup_queue(device); + set_capacity(device->gdp, device->blocks << device->s2b_shift); device->state = DASD_STATE_READY; - if (dasd_scan_partitions(device) != 0) + rc = dasd_scan_partitions(device); + if (rc) device->state = DASD_STATE_BASIC; - return 0; + return rc; } /* @@ -248,6 +260,15 @@ dasd_state_ready_to_basic(struct dasd_device * device) device->state = DASD_STATE_BASIC; } +/* + * Back to basic. + */ +static inline void +dasd_state_unfmt_to_basic(struct dasd_device * device) +{ + device->state = DASD_STATE_BASIC; +} + /* * Make the device online and schedule the bottom half to start * the requeueing of requests from the linux request queue to the @@ -293,6 +314,11 @@ dasd_increase_state(struct dasd_device *device) device->target >= DASD_STATE_READY) rc = dasd_state_basic_to_ready(device); + if (!rc && + device->state == DASD_STATE_UNFMT && + device->target > DASD_STATE_UNFMT) + rc = -EPERM; + if (!rc && device->state == DASD_STATE_READY && device->target >= DASD_STATE_ONLINE) @@ -314,8 +340,12 @@ dasd_decrease_state(struct dasd_device *device) if (device->state == DASD_STATE_READY && device->target <= DASD_STATE_BASIC) dasd_state_ready_to_basic(device); - - if (device->state == DASD_STATE_BASIC && + + if (device->state == DASD_STATE_UNFMT && + device->target <= DASD_STATE_BASIC) + dasd_state_unfmt_to_basic(device); + + if (device->state == DASD_STATE_BASIC && device->target <= DASD_STATE_KNOWN) dasd_state_basic_to_known(device); @@ -516,38 +546,29 @@ dasd_kmalloc_request(char *magic, int cplength, int datasize, struct dasd_ccw_req *cqr; /* Sanity checks */ - 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); + BUG_ON( magic == NULL || datasize > PAGE_SIZE || + (cplength*sizeof(struct ccw1)) > PAGE_SIZE); - cqr = kmalloc(sizeof(struct dasd_ccw_req), GFP_ATOMIC); + cqr = kzalloc(sizeof(struct dasd_ccw_req), GFP_ATOMIC); if (cqr == NULL) return ERR_PTR(-ENOMEM); - memset(cqr, 0, sizeof(struct dasd_ccw_req)); cqr->cpaddr = NULL; if (cplength > 0) { - cqr->cpaddr = kmalloc(cplength*sizeof(struct ccw1), + cqr->cpaddr = kcalloc(cplength, sizeof(struct ccw1), GFP_ATOMIC | GFP_DMA); if (cqr->cpaddr == NULL) { kfree(cqr); return ERR_PTR(-ENOMEM); } - memset(cqr->cpaddr, 0, cplength*sizeof(struct ccw1)); } cqr->data = NULL; if (datasize > 0) { - cqr->data = kmalloc(datasize, GFP_ATOMIC | GFP_DMA); + cqr->data = kzalloc(datasize, GFP_ATOMIC | GFP_DMA); if (cqr->data == NULL) { - if (cqr->cpaddr != NULL) - kfree(cqr->cpaddr); + kfree(cqr->cpaddr); kfree(cqr); return ERR_PTR(-ENOMEM); } - memset(cqr->data, 0, datasize); } strncpy((char *) &cqr->magic, magic, 4); ASCEBC((char *) &cqr->magic, 4); @@ -566,13 +587,8 @@ dasd_smalloc_request(char *magic, int cplength, int datasize, int size; /* Sanity checks */ - 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); + BUG_ON( magic == NULL || datasize > PAGE_SIZE || + (cplength*sizeof(struct ccw1)) > PAGE_SIZE); size = (sizeof(struct dasd_ccw_req) + 7L) & -8L; if (cplength > 0) @@ -613,7 +629,7 @@ dasd_smalloc_request(char *magic, int cplength, int datasize, void dasd_kfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device) { -#ifdef CONFIG_ARCH_S390X +#ifdef CONFIG_64BIT struct ccw1 *ccw; /* Clear any idals used for the request. */ @@ -622,14 +638,8 @@ 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) - kfree(cqr->cpaddr); - if (cqr->data != NULL) - kfree(cqr->data); + kfree(cqr->cpaddr); + kfree(cqr->data); kfree(cqr); dasd_put_device(device); } @@ -639,10 +649,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); dasd_free_chunk(&device->ccw_chunks, cqr); spin_unlock_irqrestore(&device->mem_lock, flags); @@ -672,7 +678,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. */ @@ -692,12 +699,12 @@ dasd_term_IO(struct dasd_ccw_req * cqr) rc = ccw_device_clear(device->cdev, (long) cqr); switch (rc) { case 0: /* termination successful */ - if (cqr->retries > 0) { - cqr->retries--; - cqr->status = DASD_CQR_QUEUED; - } else - cqr->status = DASD_CQR_FAILED; + cqr->retries--; + cqr->status = DASD_CQR_CLEAR; 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", @@ -741,26 +748,48 @@ dasd_start_IO(struct dasd_ccw_req * cqr) 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 -EACCES: + /* -EACCES indicates that the request used only a + * subset of the available pathes and all these + * pathes are gone. + * Do a retry with all available pathes. + */ + cqr->lpm = LPM_ANYPATH; + DBF_DEV_EVENT(DBF_ERR, device, "%s", + "start_IO: selected pathes gone," + " retry on all pathes"); + 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, @@ -825,41 +854,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) { @@ -895,19 +889,23 @@ 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); + /* First of all start sense subsystem status request. */ + dasd_eer_snss(device); + + 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); } /* @@ -943,8 +941,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; @@ -958,14 +957,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; } @@ -977,8 +974,24 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, 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) @@ -1008,18 +1021,13 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, 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); @@ -1052,7 +1060,7 @@ dasd_end_request(struct request *req, int uptodate) if (end_that_request_first(req, uptodate, req->hard_nr_sectors)) BUG(); add_disk_randomness(req->rq_disk); - end_that_request_last(req); + end_that_request_last(req, uptodate); } /* @@ -1093,19 +1101,32 @@ restart: 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); } goto restart; } + + /* First of all call extended error reporting. */ + if (dasd_eer_enabled(device) && + cqr->status == DASD_CQR_FAILED) { + dasd_eer_write(device, cqr, DASD_EER_FATALERROR); + + /* restart request */ + cqr->status = DASD_CQR_QUEUED; + cqr->retries = 255; + device->stopped |= DASD_STOPPED_QUIESCE; + goto restart; + } + /* Process finished ERP request. */ if (cqr->refers) { __dasd_process_erp(device, cqr); @@ -1122,13 +1143,16 @@ static void 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); } @@ -1167,12 +1191,12 @@ __dasd_process_blk_queue(struct dasd_device * device) elv_next_request(queue) && nr_queued < DASD_CHANQ_MAX_SIZE) { req = elv_next_request(queue); - if (test_bit(DASD_FLAG_RO, &device->flags) && + + if (device->features & DASD_FEATURE_READONLY && 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; @@ -1186,10 +1210,10 @@ __dasd_process_blk_queue(struct dasd_device * device) 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; @@ -1219,8 +1243,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); } } } @@ -1238,16 +1262,28 @@ __dasd_start_head(struct dasd_device * device) if (list_empty(&device->ccw_queue)) return; cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list); - if ((cqr->status == DASD_CQR_QUEUED) && - (!device->stopped)) { - /* try to start the first I/O that can be started */ - 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); + if (cqr->status != DASD_CQR_QUEUED) + return; + /* Non-temporary stop condition will trigger fail fast */ + if (device->stopped & ~DASD_STOPPED_PENDING && + test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && + (!dasd_eer_enabled(device))) { + cqr->status = DASD_CQR_FAILED; + dasd_schedule_bh(device); + return; } + /* Don't try to start requests if device is stopped */ + if (device->stopped) + return; + + rc = device->discipline->start_IO(cqr); + if (rc == 0) + dasd_set_timer(device, cqr->expires); + else if (rc == -EACCES) { + dasd_schedule_bh(device); + } else + /* Hmpf, try again in 1/2 sec */ + dasd_set_timer(device, 50); } /* @@ -1313,7 +1349,7 @@ dasd_tasklet(struct dasd_device * device) /* Now call the callback function of requests with final status */ list_for_each_safe(l, n, &final_queue) { cqr = list_entry(l, struct dasd_ccw_req, list); - list_del(&cqr->list); + list_del_init(&cqr->list); if (cqr->callback != NULL) (cqr->callback)(cqr, cqr->callback_data); } @@ -1335,7 +1371,7 @@ void dasd_schedule_bh(struct dasd_device * device) { /* Protect against rescheduling. */ - if (atomic_compare_and_swap (0, 1, &device->tasklet_scheduled)) + if (atomic_cmpxchg (&device->tasklet_scheduled, 0, 1) != 0) return; dasd_get_device(device); tasklet_hi_schedule(&device->tasklet); @@ -1398,7 +1434,9 @@ _wait_for_wakeup(struct dasd_ccw_req *cqr) device = cqr->device; spin_lock_irq(get_ccwdev_lock(device->cdev)); - rc = cqr->status == DASD_CQR_DONE || cqr->status == DASD_CQR_FAILED; + rc = ((cqr->status == DASD_CQR_DONE || + cqr->status == DASD_CQR_FAILED) && + list_empty(&cqr->list)); spin_unlock_irq(get_ccwdev_lock(device->cdev)); return rc; } @@ -1462,15 +1500,37 @@ dasd_sleep_on_interruptible(struct dasd_ccw_req * cqr) while (!finished) { rc = wait_event_interruptible(wait_q, _wait_for_wakeup(cqr)); if (rc != -ERESTARTSYS) { - /* Request status is either done or failed. */ - rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0; + /* Request is final (done or failed) */ + rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO; break; } 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); + switch (cqr->status) { + case DASD_CQR_IN_IO: + /* terminate runnig cqr */ + if (device->discipline->term_IO) { + cqr->retries = -1; + device->discipline->term_IO(cqr); + /*nished = + * wait (non-interruptible) for final status + * because signal ist still pending + */ + spin_unlock_irq(get_ccwdev_lock(device->cdev)); + wait_event(wait_q, _wait_for_wakeup(cqr)); + spin_lock_irq(get_ccwdev_lock(device->cdev)); + rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO; + finished = 1; + } + break; + case DASD_CQR_QUEUED: + /* request */ + list_del_init(&cqr->list); + rc = -EIO; finished = 1; + break; + default: + /* cqr with 'non-interruptable' status - just wait */ + break; } spin_unlock_irq(get_ccwdev_lock(device->cdev)); } @@ -1602,25 +1662,26 @@ do_dasd_request(request_queue_t * queue) } /* - * 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; } @@ -1639,6 +1700,7 @@ dasd_setup_queue(struct dasd_device * device) blk_queue_max_hw_segments(device->request_queue, -1L); blk_queue_max_segment_size(device->request_queue, -1L); blk_queue_segment_boundary(device->request_queue, -1L); + blk_queue_ordered(device->request_queue, QUEUE_ORDERED_TAG, NULL); } /* @@ -1694,14 +1756,13 @@ dasd_open(struct inode *inp, struct file *filp) } 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; } - if (device->state < DASD_STATE_BASIC) { + if (device->state <= DASD_STATE_BASIC) { DBF_DEV_EVENT(DBF_ERR, device, " %s", " Cannot open unrecognized device"); rc = -ENODEV; @@ -1728,12 +1789,35 @@ dasd_release(struct inode *inp, struct file *filp) return 0; } +/* + * Return disk geometry. + */ +static int +dasd_getgeo(struct block_device *bdev, struct hd_geometry *geo) +{ + struct dasd_device *device; + + device = bdev->bd_disk->private_data; + if (!device) + return -ENODEV; + + if (!device->discipline || + !device->discipline->fill_geometry) + return -EINVAL; + + device->discipline->fill_geometry(device, geo); + geo->start = get_start_sect(bdev) >> device->s2b_shift; + return 0; +} + struct block_device_operations dasd_device_operations = { .owner = THIS_MODULE, .open = dasd_open, .release = dasd_release, .ioctl = dasd_ioctl, + .compat_ioctl = dasd_compat_ioctl, + .getgeo = dasd_getgeo, }; @@ -1743,7 +1827,11 @@ dasd_exit(void) #ifdef CONFIG_PROC_FS dasd_proc_exit(); #endif - dasd_ioctl_exit(); + dasd_eer_exit(); + if (dasd_page_cache != NULL) { + kmem_cache_destroy(dasd_page_cache); + dasd_page_cache = NULL; + } dasd_gendisk_exit(); dasd_devmap_exit(); devfs_remove("dasd"); @@ -1757,8 +1845,10 @@ dasd_exit(void) * SECTION: common functions for ccw_driver use */ -/* initial attempt at a probe function. this can be simplified once - * the other detection code is gone */ +/* + * 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, struct dasd_discipline *discipline) @@ -1770,20 +1860,24 @@ dasd_generic_probe (struct ccw_device *cdev, printk(KERN_WARNING "dasd_generic_probe: could not add sysfs entries " "for %s\n", cdev->dev.bus_id); + } else { + cdev->handler = &dasd_int_handler; } - cdev->handler = &dasd_int_handler; - return ret; } -/* this will one day be called from a global not_oper handler. - * It is also used by driver_unregister during module unload */ +/* + * This will one day be called from a global not_oper handler. + * It is also used by driver_unregister during module unload. + */ void dasd_generic_remove (struct ccw_device *cdev) { struct dasd_device *device; + cdev->handler = NULL; + dasd_remove_sysfs_files(cdev); device = dasd_device_from_cdev(cdev); if (IS_ERR(device)) @@ -1803,14 +1897,17 @@ dasd_generic_remove (struct ccw_device *cdev) dasd_delete_device(device); } -/* activate a device. This is called from dasd_{eckd,fba}_probe() when either +/* + * 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 */ + * or the user has started activation through sysfs. + */ int dasd_generic_set_online (struct ccw_device *cdev, - struct dasd_discipline *discipline) + struct dasd_discipline *base_discipline) { + struct dasd_discipline *discipline; struct dasd_device *device; int rc; @@ -1818,7 +1915,8 @@ dasd_generic_set_online (struct ccw_device *cdev, if (IS_ERR(device)) return PTR_ERR(device); - if (test_bit(DASD_FLAG_USE_DIAG, &device->flags)) { + discipline = base_discipline; + if (device->features & DASD_FEATURE_USEDIAG) { if (!dasd_diag_discipline_pointer) { printk (KERN_WARNING "dasd_generic couldn't online device %s " @@ -1829,14 +1927,26 @@ dasd_generic_set_online (struct ccw_device *cdev, } discipline = dasd_diag_discipline_pointer; } + if (!try_module_get(base_discipline->owner)) { + dasd_delete_device(device); + return -EINVAL; + } + if (!try_module_get(discipline->owner)) { + module_put(base_discipline->owner); + dasd_delete_device(device); + return -EINVAL; + } + device->base_discipline = base_discipline; device->discipline = discipline; rc = discipline->check_device(device); if (rc) { printk (KERN_WARNING "dasd_generic couldn't online device %s " - "with discipline %s\n", - cdev->dev.bus_id, discipline->name); + "with discipline %s rc=%i\n", + cdev->dev.bus_id, discipline->name, rc); + module_put(discipline->owner); + module_put(base_discipline->owner); dasd_delete_device(device); return rc; } @@ -1866,7 +1976,7 @@ int dasd_generic_set_offline (struct ccw_device *cdev) { struct dasd_device *device; - int max_count; + int max_count, open_count; device = dasd_device_from_cdev(cdev); if (IS_ERR(device)) @@ -1883,10 +1993,16 @@ dasd_generic_set_offline (struct ccw_device *cdev) * in the other openers. */ max_count = device->bdev ? 0 : -1; - if (atomic_read(&device->open_count) > max_count) { - printk (KERN_WARNING "Can't offline dasd device with open" - " count = %i.\n", - atomic_read(&device->open_count)); + open_count = (int) atomic_read(&device->open_count); + if (open_count > max_count) { + if (open_count > 0) + printk (KERN_WARNING "Can't offline dasd device with " + "open count = %i.\n", + open_count); + else + printk (KERN_WARNING "%s", + "Can't offline dasd device due to internal " + "use\n"); clear_bit(DASD_FLAG_OFFLINE, &device->flags); dasd_put_device(device); return -EBUSY; @@ -1914,6 +2030,9 @@ dasd_generic_notify(struct ccw_device *cdev, int event) switch (event) { case CIO_GONE: case CIO_NO_PATH: + /* First of all call extended error reporting. */ + dasd_eer_write(device, NULL, DASD_EER_NOPATH); + if (device->state < DASD_STATE_BASIC) break; /* Device is active. We want to keep it. */ @@ -1922,14 +2041,16 @@ dasd_generic_notify(struct ccw_device *cdev, int event) if (cqr->status == DASD_CQR_IN_IO) cqr->status = DASD_CQR_FAILED; device->stopped |= DASD_STOPPED_DC_EIO; - dasd_schedule_bh(device); } else { list_for_each_entry(cqr, &device->ccw_queue, list) - if (cqr->status == DASD_CQR_IN_IO) + 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); } + dasd_schedule_bh(device); ret = 1; break; case CIO_OPER: @@ -1948,29 +2069,28 @@ dasd_generic_notify(struct ccw_device *cdev, int event) * Automatically online either all dasd devices (dasd_autodetect) or * all devices specified with dasd= parameters. */ +static int +__dasd_auto_online(struct device *dev, void *data) +{ + struct ccw_device *cdev; + + cdev = to_ccwdev(dev); + if (dasd_autodetect || dasd_busid_known(cdev->dev.bus_id) == 0) + ccw_device_set_online(cdev); + return 0; +} + void dasd_generic_auto_online (struct ccw_driver *dasd_discipline_driver) { struct device_driver *drv; - struct device *d, *dev; - struct ccw_device *cdev; drv = get_driver(&dasd_discipline_driver->driver); - down_read(&drv->bus->subsys.rwsem); - dev = NULL; - list_for_each_entry(d, &drv->devices, driver_list) { - dev = get_device(d); - if (!dev) - continue; - cdev = to_ccwdev(dev); - if (dasd_autodetect || dasd_busid_known(cdev->dev.bus_id) == 0) - ccw_device_set_online(cdev); - put_device(dev); - } - up_read(&drv->bus->subsys.rwsem); + driver_for_each_device(drv, NULL, NULL, __dasd_auto_online); put_driver(drv); } + static int __init dasd_init(void) { @@ -1978,17 +2098,19 @@ dasd_init(void) init_waitqueue_head(&dasd_init_waitq); - /* register 'common' DASD debug area, used faor all DBF_XXX calls */ - dasd_debug_area = debug_register("dasd", 0, 2, 8 * sizeof (long)); + /* register 'common' DASD debug area, used for all DBF_XXX calls */ + dasd_debug_area = debug_register("dasd", 1, 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; @@ -2001,7 +2123,7 @@ dasd_init(void) rc = dasd_parse(); if (rc) goto failed; - rc = dasd_ioctl_init(); + rc = dasd_eer_init(); if (rc) goto failed; #ifdef CONFIG_PROC_FS @@ -2021,6 +2143,7 @@ module_init(dasd_init); 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);