/* * File...........: linux/drivers/s390/block/dasd.c * Author(s)......: Holger Smolinski * Horst Hummel * Carsten Otte * Martin Schwidefsky * Bugreports.to..: * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001 * */ #include #include #include #include #include #include /* This is ugly... */ #define PRINTK_HEADER "dasd_erp:" #include "dasd_int.h" struct dasd_ccw_req * dasd_alloc_erp_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 */ BUG_ON( magic == NULL || datasize > PAGE_SIZE || (cplength*sizeof(struct ccw1)) > PAGE_SIZE); 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->erp_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; } void dasd_free_erp_request(struct dasd_ccw_req * cqr, struct dasd_device * device) { unsigned long flags; spin_lock_irqsave(&device->mem_lock, flags); dasd_free_chunk(&device->erp_chunks, cqr); spin_unlock_irqrestore(&device->mem_lock, flags); atomic_dec(&device->ref_count); } /* * dasd_default_erp_action just retries the current cqr */ struct dasd_ccw_req * dasd_default_erp_action(struct dasd_ccw_req * cqr) { struct dasd_device *device; device = cqr->device; /* just retry - there is nothing to save ... I got no sense data.... */ if (cqr->retries > 0) { DEV_MESSAGE (KERN_DEBUG, device, "default ERP called (%i retries left)", cqr->retries); cqr->lpm = LPM_ANYPATH; cqr->status = DASD_CQR_QUEUED; } else { DEV_MESSAGE (KERN_WARNING, device, "%s", "default ERP called (NO retry left)"); cqr->status = DASD_CQR_FAILED; cqr->stopclk = get_clock (); } return cqr; } /* end dasd_default_erp_action */ /* * DESCRIPTION * Frees all ERPs of the current ERP Chain and set the status * of the original CQR either to DASD_CQR_DONE if ERP was successful * or to DASD_CQR_FAILED if ERP was NOT successful. * NOTE: This function is only called if no discipline postaction * is available * * PARAMETER * erp current erp_head * * RETURN VALUES * cqr pointer to the original CQR */ struct dasd_ccw_req * dasd_default_erp_postaction(struct dasd_ccw_req * cqr) { struct dasd_device *device; int success; BUG_ON(cqr->refers == NULL || cqr->function == NULL); device = cqr->device; success = cqr->status == DASD_CQR_DONE; /* free all ERPs - but NOT the original cqr */ while (cqr->refers != NULL) { struct dasd_ccw_req *refers; refers = cqr->refers; /* remove the request from the device queue */ list_del(&cqr->list); /* free the finished erp request */ dasd_free_erp_request(cqr, device); cqr = refers; } /* set corresponding status to original cqr */ if (success) cqr->status = DASD_CQR_DONE; else { cqr->status = DASD_CQR_FAILED; cqr->stopclk = get_clock(); } return cqr; } /* end default_erp_postaction */ /* * Print the hex dump of the memory used by a request. This includes * all error recovery ccws that have been chained in from of the * real request. */ static inline void hex_dump_memory(struct dasd_device *device, void *data, int len) { int *pint; pint = (int *) data; while (len > 0) { DEV_MESSAGE(KERN_ERR, device, "%p: %08x %08x %08x %08x", pint, pint[0], pint[1], pint[2], pint[3]); pint += 4; len -= 16; } } void dasd_log_sense(struct dasd_ccw_req *cqr, struct irb *irb) { struct dasd_device *device; device = cqr->device; /* dump sense data */ if (device->discipline && device->discipline->dump_sense) device->discipline->dump_sense(device, cqr, irb); } void dasd_log_ccw(struct dasd_ccw_req * cqr, int caller, __u32 cpa) { struct dasd_device *device; struct dasd_ccw_req *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, "(%s) ERP chain report for req: %p", caller == 0 ? "EXAMINE" : "ACTION", lcqr); hex_dump_memory(device, lcqr, sizeof(struct dasd_ccw_req)); cplength = 1; ccw = lcqr->cpaddr; while (ccw++->flags & (CCW_FLAG_DC | CCW_FLAG_CC)) cplength++; if (cplength > 40) { /* log only parts of the CP */ DEV_MESSAGE(KERN_ERR, device, "%s", "Start of channel program:"); hex_dump_memory(device, lcqr->cpaddr, 40*sizeof(struct ccw1)); DEV_MESSAGE(KERN_ERR, device, "%s", "End of channel program:"); hex_dump_memory(device, lcqr->cpaddr + cplength - 10, 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(struct ccw1)); } if (lcqr != cqr) continue; /* * Log bytes arround failed CCW but only if we did * not log the whole CP of the CCW is outside the * logged CP. */ if (cplength > 40 || ((addr_t) cpa < (addr_t) lcqr->cpaddr && (addr_t) cpa > (addr_t) (lcqr->cpaddr + cplength + 4))) { DEV_MESSAGE(KERN_ERR, device, "Failed CCW (%p) (area):", (void *) (long) cpa); hex_dump_memory(device, cqr->cpaddr - 10, 20*sizeof(struct ccw1)); } } } /* end log_erp_chain */ 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);