1 /* 2 * File...........: linux/drivers/s390/block/dasd.c 3 * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> 4 * Horst Hummel <Horst.Hummel@de.ibm.com> 5 * Carsten Otte <Cotte@de.ibm.com> 6 * Martin Schwidefsky <schwidefsky@de.ibm.com> 7 * Bugreports.to..: <Linux390@de.ibm.com> 8 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001 9 * 10 * $Revision: 1.14 $ 11 */ 12 13 #include <linux/config.h> 14 #include <linux/ctype.h> 15 #include <linux/init.h> 16 17 #include <asm/debug.h> 18 #include <asm/ebcdic.h> 19 #include <asm/uaccess.h> 20 21 /* This is ugly... */ 22 #define PRINTK_HEADER "dasd_erp:" 23 24 #include "dasd_int.h" 25 26 struct dasd_ccw_req * 27 dasd_alloc_erp_request(char *magic, int cplength, int datasize, 28 struct dasd_device * device) 29 { 30 unsigned long flags; 31 struct dasd_ccw_req *cqr; 32 char *data; 33 int size; 34 35 /* Sanity checks */ 36 if ( magic == NULL || datasize > PAGE_SIZE || 37 (cplength*sizeof(struct ccw1)) > PAGE_SIZE) 38 BUG(); 39 40 size = (sizeof(struct dasd_ccw_req) + 7L) & -8L; 41 if (cplength > 0) 42 size += cplength * sizeof(struct ccw1); 43 if (datasize > 0) 44 size += datasize; 45 spin_lock_irqsave(&device->mem_lock, flags); 46 cqr = (struct dasd_ccw_req *) 47 dasd_alloc_chunk(&device->erp_chunks, size); 48 spin_unlock_irqrestore(&device->mem_lock, flags); 49 if (cqr == NULL) 50 return ERR_PTR(-ENOMEM); 51 memset(cqr, 0, sizeof(struct dasd_ccw_req)); 52 data = (char *) cqr + ((sizeof(struct dasd_ccw_req) + 7L) & -8L); 53 cqr->cpaddr = NULL; 54 if (cplength > 0) { 55 cqr->cpaddr = (struct ccw1 *) data; 56 data += cplength*sizeof(struct ccw1); 57 memset(cqr->cpaddr, 0, cplength*sizeof(struct ccw1)); 58 } 59 cqr->data = NULL; 60 if (datasize > 0) { 61 cqr->data = data; 62 memset(cqr->data, 0, datasize); 63 } 64 strncpy((char *) &cqr->magic, magic, 4); 65 ASCEBC((char *) &cqr->magic, 4); 66 set_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); 67 dasd_get_device(device); 68 return cqr; 69 } 70 71 void 72 dasd_free_erp_request(struct dasd_ccw_req * cqr, struct dasd_device * device) 73 { 74 unsigned long flags; 75 76 spin_lock_irqsave(&device->mem_lock, flags); 77 dasd_free_chunk(&device->erp_chunks, cqr); 78 spin_unlock_irqrestore(&device->mem_lock, flags); 79 atomic_dec(&device->ref_count); 80 } 81 82 83 /* 84 * dasd_default_erp_action just retries the current cqr 85 */ 86 struct dasd_ccw_req * 87 dasd_default_erp_action(struct dasd_ccw_req * cqr) 88 { 89 struct dasd_device *device; 90 91 device = cqr->device; 92 93 /* just retry - there is nothing to save ... I got no sense data.... */ 94 if (cqr->retries > 0) { 95 DEV_MESSAGE (KERN_DEBUG, device, 96 "default ERP called (%i retries left)", 97 cqr->retries); 98 cqr->lpm = LPM_ANYPATH; 99 cqr->status = DASD_CQR_QUEUED; 100 } else { 101 DEV_MESSAGE (KERN_WARNING, device, "%s", 102 "default ERP called (NO retry left)"); 103 cqr->status = DASD_CQR_FAILED; 104 cqr->stopclk = get_clock (); 105 } 106 return cqr; 107 } /* end dasd_default_erp_action */ 108 109 /* 110 * DESCRIPTION 111 * Frees all ERPs of the current ERP Chain and set the status 112 * of the original CQR either to DASD_CQR_DONE if ERP was successful 113 * or to DASD_CQR_FAILED if ERP was NOT successful. 114 * NOTE: This function is only called if no discipline postaction 115 * is available 116 * 117 * PARAMETER 118 * erp current erp_head 119 * 120 * RETURN VALUES 121 * cqr pointer to the original CQR 122 */ 123 struct dasd_ccw_req * 124 dasd_default_erp_postaction(struct dasd_ccw_req * cqr) 125 { 126 struct dasd_device *device; 127 int success; 128 129 if (cqr->refers == NULL || cqr->function == NULL) 130 BUG(); 131 132 device = cqr->device; 133 success = cqr->status == DASD_CQR_DONE; 134 135 /* free all ERPs - but NOT the original cqr */ 136 while (cqr->refers != NULL) { 137 struct dasd_ccw_req *refers; 138 139 refers = cqr->refers; 140 /* remove the request from the device queue */ 141 list_del(&cqr->list); 142 /* free the finished erp request */ 143 dasd_free_erp_request(cqr, device); 144 cqr = refers; 145 } 146 147 /* set corresponding status to original cqr */ 148 if (success) 149 cqr->status = DASD_CQR_DONE; 150 else { 151 cqr->status = DASD_CQR_FAILED; 152 cqr->stopclk = get_clock(); 153 } 154 155 return cqr; 156 157 } /* end default_erp_postaction */ 158 159 /* 160 * Print the hex dump of the memory used by a request. This includes 161 * all error recovery ccws that have been chained in from of the 162 * real request. 163 */ 164 static inline void 165 hex_dump_memory(struct dasd_device *device, void *data, int len) 166 { 167 int *pint; 168 169 pint = (int *) data; 170 while (len > 0) { 171 DEV_MESSAGE(KERN_ERR, device, "%p: %08x %08x %08x %08x", 172 pint, pint[0], pint[1], pint[2], pint[3]); 173 pint += 4; 174 len -= 16; 175 } 176 } 177 178 void 179 dasd_log_sense(struct dasd_ccw_req *cqr, struct irb *irb) 180 { 181 struct dasd_device *device; 182 183 device = cqr->device; 184 /* dump sense data */ 185 if (device->discipline && device->discipline->dump_sense) 186 device->discipline->dump_sense(device, cqr, irb); 187 } 188 189 void 190 dasd_log_ccw(struct dasd_ccw_req * cqr, int caller, __u32 cpa) 191 { 192 struct dasd_device *device; 193 struct dasd_ccw_req *lcqr; 194 struct ccw1 *ccw; 195 int cplength; 196 197 device = cqr->device; 198 /* log the channel program */ 199 for (lcqr = cqr; lcqr != NULL; lcqr = lcqr->refers) { 200 DEV_MESSAGE(KERN_ERR, device, 201 "(%s) ERP chain report for req: %p", 202 caller == 0 ? "EXAMINE" : "ACTION", lcqr); 203 hex_dump_memory(device, lcqr, sizeof(struct dasd_ccw_req)); 204 205 cplength = 1; 206 ccw = lcqr->cpaddr; 207 while (ccw++->flags & (CCW_FLAG_DC | CCW_FLAG_CC)) 208 cplength++; 209 210 if (cplength > 40) { /* log only parts of the CP */ 211 DEV_MESSAGE(KERN_ERR, device, "%s", 212 "Start of channel program:"); 213 hex_dump_memory(device, lcqr->cpaddr, 214 40*sizeof(struct ccw1)); 215 216 DEV_MESSAGE(KERN_ERR, device, "%s", 217 "End of channel program:"); 218 hex_dump_memory(device, lcqr->cpaddr + cplength - 10, 219 10*sizeof(struct ccw1)); 220 } else { /* log the whole CP */ 221 DEV_MESSAGE(KERN_ERR, device, "%s", 222 "Channel program (complete):"); 223 hex_dump_memory(device, lcqr->cpaddr, 224 cplength*sizeof(struct ccw1)); 225 } 226 227 if (lcqr != cqr) 228 continue; 229 230 /* 231 * Log bytes arround failed CCW but only if we did 232 * not log the whole CP of the CCW is outside the 233 * logged CP. 234 */ 235 if (cplength > 40 || 236 ((addr_t) cpa < (addr_t) lcqr->cpaddr && 237 (addr_t) cpa > (addr_t) (lcqr->cpaddr + cplength + 4))) { 238 239 DEV_MESSAGE(KERN_ERR, device, 240 "Failed CCW (%p) (area):", 241 (void *) (long) cpa); 242 hex_dump_memory(device, cqr->cpaddr - 10, 243 20*sizeof(struct ccw1)); 244 } 245 } 246 247 } /* end log_erp_chain */ 248 249 EXPORT_SYMBOL(dasd_default_erp_action); 250 EXPORT_SYMBOL(dasd_default_erp_postaction); 251 EXPORT_SYMBOL(dasd_alloc_erp_request); 252 EXPORT_SYMBOL(dasd_free_erp_request); 253 EXPORT_SYMBOL(dasd_log_sense); 254 EXPORT_SYMBOL(dasd_log_ccw); 255