diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/s390/block/dasd_erp.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/s390/block/dasd_erp.c')
-rw-r--r-- | drivers/s390/block/dasd_erp.c | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/drivers/s390/block/dasd_erp.c b/drivers/s390/block/dasd_erp.c new file mode 100644 index 000000000000..7cb98d25f341 --- /dev/null +++ b/drivers/s390/block/dasd_erp.c | |||
@@ -0,0 +1,254 @@ | |||
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); | ||