📄 dasd.c
字号:
struct dasd_device * device){ 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); cqr = kmalloc(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), 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); if (cqr->data == NULL) { if (cqr->cpaddr != NULL) 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); set_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); dasd_get_device(device); return cqr;}struct dasd_ccw_req *dasd_smalloc_request(char *magic, int cplength, int datasize, struct dasd_device * device){ unsigned long flags; struct dasd_ccw_req *cqr; char *data; 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); size = (sizeof(struct dasd_ccw_req) + 7L) & -8L; if (cplength > 0) size += cplength * sizeof(struct ccw1); if (datasize > 0) size += datasize; spin_lock_irqsave(&device->mem_lock, flags); cqr = (struct dasd_ccw_req *) dasd_alloc_chunk(&device->ccw_chunks, size); spin_unlock_irqrestore(&device->mem_lock, flags); if (cqr == NULL) return ERR_PTR(-ENOMEM); memset(cqr, 0, sizeof(struct dasd_ccw_req)); data = (char *) cqr + ((sizeof(struct dasd_ccw_req) + 7L) & -8L); cqr->cpaddr = NULL; if (cplength > 0) { cqr->cpaddr = (struct ccw1 *) data; data += cplength*sizeof(struct ccw1); memset(cqr->cpaddr, 0, cplength*sizeof(struct ccw1)); } cqr->data = NULL; if (datasize > 0) { cqr->data = data; memset(cqr->data, 0, datasize); } strncpy((char *) &cqr->magic, magic, 4); ASCEBC((char *) &cqr->magic, 4); set_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); dasd_get_device(device); return cqr;}/* * Free memory of a channel program. This function needs to free all the * idal lists that might have been created by dasd_set_cda and the * struct dasd_ccw_req itself. */voiddasd_kfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device){#ifdef CONFIG_ARCH_S390X struct ccw1 *ccw; /* Clear any idals used for the request. */ ccw = cqr->cpaddr; do { clear_normalized_cda(ccw); } while (ccw++->flags & (CCW_FLAG_CC | CCW_FLAG_DC));#endif 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); dasd_put_device(device);}voiddasd_sfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device){ unsigned long flags; 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); dasd_put_device(device);}/* * Check discipline magic in cqr. */static inline intdasd_check_cqr(struct dasd_ccw_req *cqr){ struct dasd_device *device; if (cqr == NULL) return -EINVAL; device = cqr->device; if (strncmp((char *) &cqr->magic, device->discipline->ebcname, 4)) { DEV_MESSAGE(KERN_WARNING, device, " dasd_ccw_req 0x%08x magic doesn't match" " discipline 0x%08x", cqr->magic, *(unsigned int *) device->discipline->name); return -EINVAL; } return 0;}/* * 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. */intdasd_term_IO(struct dasd_ccw_req * cqr){ struct dasd_device *device; int retries, rc; /* Check the cqr */ rc = dasd_check_cqr(cqr); if (rc) return rc; retries = 0; device = (struct dasd_device *) cqr->device; while ((retries < 5) && (cqr->status == DASD_CQR_IN_IO)) { rc = ccw_device_clear(device->cdev, (long) cqr); switch (rc) { case 0: /* termination successful */ if (cqr->retries > 0) { cqr->retries--; cqr->status = DASD_CQR_CLEAR; } else cqr->status = DASD_CQR_FAILED; cqr->stopclk = get_clock(); break; case -ENODEV: DBF_DEV_EVENT(DBF_ERR, device, "%s", "device gone, retry"); break; case -EIO: DBF_DEV_EVENT(DBF_ERR, device, "%s", "I/O error, retry"); break; case -EINVAL: case -EBUSY: DBF_DEV_EVENT(DBF_ERR, device, "%s", "device busy, retry later"); break; default: DEV_MESSAGE(KERN_ERR, device, "line %d unknown RC=%d, please " "report to linux390@de.ibm.com", __LINE__, rc); BUG(); break; } retries++; } dasd_schedule_bh(device); return rc;}/* * Start the i/o. This start_IO can fail if the channel is really busy. * In that case set up a timer to start the request later. */intdasd_start_IO(struct dasd_ccw_req * cqr){ struct dasd_device *device; int rc; /* Check the cqr */ rc = dasd_check_cqr(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; break; case -EBUSY: DBF_DEV_EVENT(DBF_ERR, device, "%s", "start_IO: device busy, retry later"); break; case -ETIMEDOUT: DBF_DEV_EVENT(DBF_ERR, device, "%s", "start_IO: request timeout, retry later"); break; case -ENODEV: case -EIO: DBF_DEV_EVENT(DBF_ERR, device, "%s", "start_IO: device gone, retry"); break; default: DEV_MESSAGE(KERN_ERR, device, "line %d unknown RC=%d, please report" " to linux390@de.ibm.com", __LINE__, rc); BUG(); break; } return rc;}/* * Timeout function for dasd devices. This is used for different purposes * 1) missing interrupt handler for normal operation * 2) delayed start of request where start_IO failed with -EBUSY * 3) timeout for missing state change interrupts * The head of the ccw queue will have status DASD_CQR_IN_IO for 1), * DASD_CQR_QUEUED for 2) and 3). */static voiddasd_timeout_device(unsigned long ptr){ unsigned long flags; struct dasd_device *device; device = (struct dasd_device *) ptr; spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); /* re-activate request queue */ device->stopped &= ~DASD_STOPPED_PENDING; spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); dasd_schedule_bh(device);}/* * Setup timeout for a device in jiffies. */voiddasd_set_timer(struct dasd_device *device, int expires){ 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);}/* * Clear timeout for a device. */voiddasd_clear_timer(struct dasd_device *device){ if (timer_pending(&device->timer)) del_timer(&device->timer);}static voiddasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm){ struct dasd_ccw_req *cqr; struct dasd_device *device; cqr = (struct dasd_ccw_req *) intparm; if (cqr->status != DASD_CQR_IN_IO) { MESSAGE(KERN_DEBUG, "invalid status in handle_killed_request: " "bus_id %s, status %02x", cdev->dev.bus_id, cqr->status); return; } device = (struct dasd_device *) cqr->device; if (device == NULL || device != dasd_device_from_cdev(cdev) || strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s", cdev->dev.bus_id); return; } /* Schedule request to be retried. */ cqr->status = DASD_CQR_QUEUED; dasd_clear_timer(device); dasd_schedule_bh(device); dasd_put_device(device);}static voiddasd_handle_state_change_pending(struct dasd_device *device){ struct dasd_ccw_req *cqr; struct list_head *l, *n; 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);}/* * Interrupt handler for "normal" ssch-io based dasd devices. */voiddasd_int_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb){ struct dasd_ccw_req *cqr, *next; struct dasd_device *device; unsigned long long now; int expires; dasd_era_t era; char mask; if (IS_ERR(irb)) { switch (PTR_ERR(irb)) { case -EIO: dasd_handle_killed_request(cdev, intparm); break; case -ETIMEDOUT: printk(KERN_WARNING"%s(%s): request timed out\n", __FUNCTION__, cdev->dev.bus_id); //FIXME - dasd uses own timeout interface... break; default: printk(KERN_WARNING"%s(%s): unknown error %ld\n", __FUNCTION__, cdev->dev.bus_id, PTR_ERR(irb)); } return; } now = get_clock(); 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; if ((irb->scsw.dstat & mask) == mask) { device = dasd_device_from_cdev(cdev); if (!IS_ERR(device)) { dasd_handle_state_change_pending(device); dasd_put_device(device); } return; } cqr = (struct dasd_ccw_req *) intparm; /* check for unsolicited interrupts */ if (cqr == NULL) { MESSAGE(KERN_DEBUG, "unsolicited interrupt received: bus_id %s", cdev->dev.bus_id); return; } device = (struct dasd_device *) cqr->device; if (device == NULL || strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s", cdev->dev.bus_id); 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)); /* Find out the appropriate era_action. */ if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) era = dasd_era_fatal; else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) && irb->scsw.cstat == 0 && !irb->esw.esw0.erw.cons) era = dasd_era_none; else if (!test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags)) era = dasd_era_fatal; /* don't recover this request */ else if (irb->esw.esw0.erw.cons) era = device->discipline->examine_error(cqr, irb); else era = dasd_era_recover; DBF_DEV_EVENT(DBF_DEBUG, device, "era_code %d", era); expires = 0; if (era == dasd_era_none) { cqr->status = DASD_CQR_DONE; cqr->stopclk = now; /* Start first request on queue if possible -> fast_io. */ if (cqr->list.next != &device->ccw_queue) { next = list_entry(cqr->list.next, struct dasd_ccw_req, list); if ((next->status == DASD_CQR_QUEUED) && (!device->stopped)) { if (device->discipline->start_IO(next) == 0) expires = next->expires; else DEV_MESSAGE(KERN_DEBUG, device, "%s", "Interrupt fastpath " "failed!"); } } } else { /* error */ memcpy(&cqr->irb, irb, sizeof (struct irb));#ifdef ERP_DEBUG /* dump sense data */ dasd_log_sense(cqr, irb);#endif switch (era) { case dasd_era_fatal: cqr->status = DASD_CQR_FAILED; cqr->stopclk = now; break; case dasd_era_recover: cqr->status = DASD_CQR_ERROR; break; default: BUG(); } } if (expires != 0) dasd_set_timer(device, expires); else dasd_clear_timer(device); dasd_schedule_bh(device);}/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -