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_eckd.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_eckd.c')
-rw-r--r-- | drivers/s390/block/dasd_eckd.c | 1722 |
1 files changed, 1722 insertions, 0 deletions
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c new file mode 100644 index 000000000000..838aedf78a56 --- /dev/null +++ b/drivers/s390/block/dasd_eckd.c | |||
@@ -0,0 +1,1722 @@ | |||
1 | /* | ||
2 | * File...........: linux/drivers/s390/block/dasd_eckd.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,2000 | ||
9 | * | ||
10 | * $Revision: 1.69 $ | ||
11 | */ | ||
12 | |||
13 | #include <linux/config.h> | ||
14 | #include <linux/stddef.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/hdreg.h> /* HDIO_GETGEO */ | ||
18 | #include <linux/bio.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/init.h> | ||
21 | |||
22 | #include <asm/debug.h> | ||
23 | #include <asm/idals.h> | ||
24 | #include <asm/ebcdic.h> | ||
25 | #include <asm/io.h> | ||
26 | #include <asm/todclk.h> | ||
27 | #include <asm/uaccess.h> | ||
28 | #include <asm/ccwdev.h> | ||
29 | |||
30 | #include "dasd_int.h" | ||
31 | #include "dasd_eckd.h" | ||
32 | |||
33 | #ifdef PRINTK_HEADER | ||
34 | #undef PRINTK_HEADER | ||
35 | #endif /* PRINTK_HEADER */ | ||
36 | #define PRINTK_HEADER "dasd(eckd):" | ||
37 | |||
38 | #define ECKD_C0(i) (i->home_bytes) | ||
39 | #define ECKD_F(i) (i->formula) | ||
40 | #define ECKD_F1(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f1):\ | ||
41 | (i->factors.f_0x02.f1)) | ||
42 | #define ECKD_F2(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f2):\ | ||
43 | (i->factors.f_0x02.f2)) | ||
44 | #define ECKD_F3(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f3):\ | ||
45 | (i->factors.f_0x02.f3)) | ||
46 | #define ECKD_F4(i) (ECKD_F(i)==0x02?(i->factors.f_0x02.f4):0) | ||
47 | #define ECKD_F5(i) (ECKD_F(i)==0x02?(i->factors.f_0x02.f5):0) | ||
48 | #define ECKD_F6(i) (i->factor6) | ||
49 | #define ECKD_F7(i) (i->factor7) | ||
50 | #define ECKD_F8(i) (i->factor8) | ||
51 | |||
52 | MODULE_LICENSE("GPL"); | ||
53 | |||
54 | static struct dasd_discipline dasd_eckd_discipline; | ||
55 | |||
56 | struct dasd_eckd_private { | ||
57 | struct dasd_eckd_characteristics rdc_data; | ||
58 | struct dasd_eckd_confdata conf_data; | ||
59 | struct dasd_eckd_path path_data; | ||
60 | struct eckd_count count_area[5]; | ||
61 | int init_cqr_status; | ||
62 | int uses_cdl; | ||
63 | struct attrib_data_t attrib; /* e.g. cache operations */ | ||
64 | }; | ||
65 | |||
66 | /* The ccw bus type uses this table to find devices that it sends to | ||
67 | * dasd_eckd_probe */ | ||
68 | static struct ccw_device_id dasd_eckd_ids[] = { | ||
69 | { CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3390, 0), driver_info: 0x1}, | ||
70 | { CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3390, 0), driver_info: 0x2}, | ||
71 | { CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3390, 0), driver_info: 0x3}, | ||
72 | { CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3380, 0), driver_info: 0x4}, | ||
73 | { CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3380, 0), driver_info: 0x5}, | ||
74 | { CCW_DEVICE_DEVTYPE (0x9343, 0, 0x9345, 0), driver_info: 0x6}, | ||
75 | { CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3390, 0), driver_info: 0x7}, | ||
76 | { CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3380, 0), driver_info: 0x8}, | ||
77 | { CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3390, 0), driver_info: 0x9}, | ||
78 | { CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3380, 0), driver_info: 0xa}, | ||
79 | { /* end of list */ }, | ||
80 | }; | ||
81 | |||
82 | MODULE_DEVICE_TABLE(ccw, dasd_eckd_ids); | ||
83 | |||
84 | static struct ccw_driver dasd_eckd_driver; /* see below */ | ||
85 | |||
86 | /* initial attempt at a probe function. this can be simplified once | ||
87 | * the other detection code is gone */ | ||
88 | static int | ||
89 | dasd_eckd_probe (struct ccw_device *cdev) | ||
90 | { | ||
91 | int ret; | ||
92 | |||
93 | ret = dasd_generic_probe (cdev, &dasd_eckd_discipline); | ||
94 | if (ret) | ||
95 | return ret; | ||
96 | ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP | CCWDEV_ALLOW_FORCE); | ||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | static int | ||
101 | dasd_eckd_set_online(struct ccw_device *cdev) | ||
102 | { | ||
103 | return dasd_generic_set_online (cdev, &dasd_eckd_discipline); | ||
104 | } | ||
105 | |||
106 | static struct ccw_driver dasd_eckd_driver = { | ||
107 | .name = "dasd-eckd", | ||
108 | .owner = THIS_MODULE, | ||
109 | .ids = dasd_eckd_ids, | ||
110 | .probe = dasd_eckd_probe, | ||
111 | .remove = dasd_generic_remove, | ||
112 | .set_offline = dasd_generic_set_offline, | ||
113 | .set_online = dasd_eckd_set_online, | ||
114 | .notify = dasd_generic_notify, | ||
115 | }; | ||
116 | |||
117 | static const int sizes_trk0[] = { 28, 148, 84 }; | ||
118 | #define LABEL_SIZE 140 | ||
119 | |||
120 | static inline unsigned int | ||
121 | round_up_multiple(unsigned int no, unsigned int mult) | ||
122 | { | ||
123 | int rem = no % mult; | ||
124 | return (rem ? no - rem + mult : no); | ||
125 | } | ||
126 | |||
127 | static inline unsigned int | ||
128 | ceil_quot(unsigned int d1, unsigned int d2) | ||
129 | { | ||
130 | return (d1 + (d2 - 1)) / d2; | ||
131 | } | ||
132 | |||
133 | static inline int | ||
134 | bytes_per_record(struct dasd_eckd_characteristics *rdc, int kl, int dl) | ||
135 | { | ||
136 | unsigned int fl1, fl2, int1, int2; | ||
137 | int bpr; | ||
138 | |||
139 | switch (rdc->formula) { | ||
140 | case 0x01: | ||
141 | fl1 = round_up_multiple(ECKD_F2(rdc) + dl, ECKD_F1(rdc)); | ||
142 | fl2 = round_up_multiple(kl ? ECKD_F2(rdc) + kl : 0, | ||
143 | ECKD_F1(rdc)); | ||
144 | bpr = fl1 + fl2; | ||
145 | break; | ||
146 | case 0x02: | ||
147 | int1 = ceil_quot(dl + ECKD_F6(rdc), ECKD_F5(rdc) << 1); | ||
148 | int2 = ceil_quot(kl + ECKD_F6(rdc), ECKD_F5(rdc) << 1); | ||
149 | fl1 = round_up_multiple(ECKD_F1(rdc) * ECKD_F2(rdc) + dl + | ||
150 | ECKD_F6(rdc) + ECKD_F4(rdc) * int1, | ||
151 | ECKD_F1(rdc)); | ||
152 | fl2 = round_up_multiple(ECKD_F1(rdc) * ECKD_F3(rdc) + kl + | ||
153 | ECKD_F6(rdc) + ECKD_F4(rdc) * int2, | ||
154 | ECKD_F1(rdc)); | ||
155 | bpr = fl1 + fl2; | ||
156 | break; | ||
157 | default: | ||
158 | bpr = 0; | ||
159 | break; | ||
160 | } | ||
161 | return bpr; | ||
162 | } | ||
163 | |||
164 | static inline unsigned int | ||
165 | bytes_per_track(struct dasd_eckd_characteristics *rdc) | ||
166 | { | ||
167 | return *(unsigned int *) (rdc->byte_per_track) >> 8; | ||
168 | } | ||
169 | |||
170 | static inline unsigned int | ||
171 | recs_per_track(struct dasd_eckd_characteristics * rdc, | ||
172 | unsigned int kl, unsigned int dl) | ||
173 | { | ||
174 | int dn, kn; | ||
175 | |||
176 | switch (rdc->dev_type) { | ||
177 | case 0x3380: | ||
178 | if (kl) | ||
179 | return 1499 / (15 + 7 + ceil_quot(kl + 12, 32) + | ||
180 | ceil_quot(dl + 12, 32)); | ||
181 | else | ||
182 | return 1499 / (15 + ceil_quot(dl + 12, 32)); | ||
183 | case 0x3390: | ||
184 | dn = ceil_quot(dl + 6, 232) + 1; | ||
185 | if (kl) { | ||
186 | kn = ceil_quot(kl + 6, 232) + 1; | ||
187 | return 1729 / (10 + 9 + ceil_quot(kl + 6 * kn, 34) + | ||
188 | 9 + ceil_quot(dl + 6 * dn, 34)); | ||
189 | } else | ||
190 | return 1729 / (10 + 9 + ceil_quot(dl + 6 * dn, 34)); | ||
191 | case 0x9345: | ||
192 | dn = ceil_quot(dl + 6, 232) + 1; | ||
193 | if (kl) { | ||
194 | kn = ceil_quot(kl + 6, 232) + 1; | ||
195 | return 1420 / (18 + 7 + ceil_quot(kl + 6 * kn, 34) + | ||
196 | ceil_quot(dl + 6 * dn, 34)); | ||
197 | } else | ||
198 | return 1420 / (18 + 7 + ceil_quot(dl + 6 * dn, 34)); | ||
199 | } | ||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | static inline void | ||
204 | check_XRC (struct ccw1 *de_ccw, | ||
205 | struct DE_eckd_data *data, | ||
206 | struct dasd_device *device) | ||
207 | { | ||
208 | struct dasd_eckd_private *private; | ||
209 | |||
210 | private = (struct dasd_eckd_private *) device->private; | ||
211 | |||
212 | /* switch on System Time Stamp - needed for XRC Support */ | ||
213 | if (private->rdc_data.facilities.XRC_supported) { | ||
214 | |||
215 | data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */ | ||
216 | data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */ | ||
217 | |||
218 | data->ep_sys_time = get_clock (); | ||
219 | |||
220 | de_ccw->count = sizeof (struct DE_eckd_data); | ||
221 | de_ccw->flags |= CCW_FLAG_SLI; | ||
222 | } | ||
223 | |||
224 | return; | ||
225 | |||
226 | } /* end check_XRC */ | ||
227 | |||
228 | static inline void | ||
229 | define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk, | ||
230 | int totrk, int cmd, struct dasd_device * device) | ||
231 | { | ||
232 | struct dasd_eckd_private *private; | ||
233 | struct ch_t geo, beg, end; | ||
234 | |||
235 | private = (struct dasd_eckd_private *) device->private; | ||
236 | |||
237 | ccw->cmd_code = DASD_ECKD_CCW_DEFINE_EXTENT; | ||
238 | ccw->flags = 0; | ||
239 | ccw->count = 16; | ||
240 | ccw->cda = (__u32) __pa(data); | ||
241 | |||
242 | memset(data, 0, sizeof (struct DE_eckd_data)); | ||
243 | switch (cmd) { | ||
244 | case DASD_ECKD_CCW_READ_HOME_ADDRESS: | ||
245 | case DASD_ECKD_CCW_READ_RECORD_ZERO: | ||
246 | case DASD_ECKD_CCW_READ: | ||
247 | case DASD_ECKD_CCW_READ_MT: | ||
248 | case DASD_ECKD_CCW_READ_CKD: | ||
249 | case DASD_ECKD_CCW_READ_CKD_MT: | ||
250 | case DASD_ECKD_CCW_READ_KD: | ||
251 | case DASD_ECKD_CCW_READ_KD_MT: | ||
252 | case DASD_ECKD_CCW_READ_COUNT: | ||
253 | data->mask.perm = 0x1; | ||
254 | data->attributes.operation = private->attrib.operation; | ||
255 | break; | ||
256 | case DASD_ECKD_CCW_WRITE: | ||
257 | case DASD_ECKD_CCW_WRITE_MT: | ||
258 | case DASD_ECKD_CCW_WRITE_KD: | ||
259 | case DASD_ECKD_CCW_WRITE_KD_MT: | ||
260 | data->mask.perm = 0x02; | ||
261 | data->attributes.operation = private->attrib.operation; | ||
262 | check_XRC (ccw, data, device); | ||
263 | break; | ||
264 | case DASD_ECKD_CCW_WRITE_CKD: | ||
265 | case DASD_ECKD_CCW_WRITE_CKD_MT: | ||
266 | data->attributes.operation = DASD_BYPASS_CACHE; | ||
267 | check_XRC (ccw, data, device); | ||
268 | break; | ||
269 | case DASD_ECKD_CCW_ERASE: | ||
270 | case DASD_ECKD_CCW_WRITE_HOME_ADDRESS: | ||
271 | case DASD_ECKD_CCW_WRITE_RECORD_ZERO: | ||
272 | data->mask.perm = 0x3; | ||
273 | data->mask.auth = 0x1; | ||
274 | data->attributes.operation = DASD_BYPASS_CACHE; | ||
275 | check_XRC (ccw, data, device); | ||
276 | break; | ||
277 | default: | ||
278 | DEV_MESSAGE(KERN_ERR, device, "unknown opcode 0x%x", cmd); | ||
279 | break; | ||
280 | } | ||
281 | |||
282 | data->attributes.mode = 0x3; /* ECKD */ | ||
283 | |||
284 | if ((private->rdc_data.cu_type == 0x2105 || | ||
285 | private->rdc_data.cu_type == 0x2107 || | ||
286 | private->rdc_data.cu_type == 0x1750) | ||
287 | && !(private->uses_cdl && trk < 2)) | ||
288 | data->ga_extended |= 0x40; /* Regular Data Format Mode */ | ||
289 | |||
290 | geo.cyl = private->rdc_data.no_cyl; | ||
291 | geo.head = private->rdc_data.trk_per_cyl; | ||
292 | beg.cyl = trk / geo.head; | ||
293 | beg.head = trk % geo.head; | ||
294 | end.cyl = totrk / geo.head; | ||
295 | end.head = totrk % geo.head; | ||
296 | |||
297 | /* check for sequential prestage - enhance cylinder range */ | ||
298 | if (data->attributes.operation == DASD_SEQ_PRESTAGE || | ||
299 | data->attributes.operation == DASD_SEQ_ACCESS) { | ||
300 | |||
301 | if (end.cyl + private->attrib.nr_cyl < geo.cyl) | ||
302 | end.cyl += private->attrib.nr_cyl; | ||
303 | else | ||
304 | end.cyl = (geo.cyl - 1); | ||
305 | } | ||
306 | |||
307 | data->beg_ext.cyl = beg.cyl; | ||
308 | data->beg_ext.head = beg.head; | ||
309 | data->end_ext.cyl = end.cyl; | ||
310 | data->end_ext.head = end.head; | ||
311 | } | ||
312 | |||
313 | static inline void | ||
314 | locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk, | ||
315 | int rec_on_trk, int no_rec, int cmd, | ||
316 | struct dasd_device * device, int reclen) | ||
317 | { | ||
318 | struct dasd_eckd_private *private; | ||
319 | int sector; | ||
320 | int dn, d; | ||
321 | |||
322 | private = (struct dasd_eckd_private *) device->private; | ||
323 | |||
324 | DBF_DEV_EVENT(DBF_INFO, device, | ||
325 | "Locate: trk %d, rec %d, no_rec %d, cmd %d, reclen %d", | ||
326 | trk, rec_on_trk, no_rec, cmd, reclen); | ||
327 | |||
328 | ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD; | ||
329 | ccw->flags = 0; | ||
330 | ccw->count = 16; | ||
331 | ccw->cda = (__u32) __pa(data); | ||
332 | |||
333 | memset(data, 0, sizeof (struct LO_eckd_data)); | ||
334 | sector = 0; | ||
335 | if (rec_on_trk) { | ||
336 | switch (private->rdc_data.dev_type) { | ||
337 | case 0x3390: | ||
338 | dn = ceil_quot(reclen + 6, 232); | ||
339 | d = 9 + ceil_quot(reclen + 6 * (dn + 1), 34); | ||
340 | sector = (49 + (rec_on_trk - 1) * (10 + d)) / 8; | ||
341 | break; | ||
342 | case 0x3380: | ||
343 | d = 7 + ceil_quot(reclen + 12, 32); | ||
344 | sector = (39 + (rec_on_trk - 1) * (8 + d)) / 7; | ||
345 | break; | ||
346 | } | ||
347 | } | ||
348 | data->sector = sector; | ||
349 | data->count = no_rec; | ||
350 | switch (cmd) { | ||
351 | case DASD_ECKD_CCW_WRITE_HOME_ADDRESS: | ||
352 | data->operation.orientation = 0x3; | ||
353 | data->operation.operation = 0x03; | ||
354 | break; | ||
355 | case DASD_ECKD_CCW_READ_HOME_ADDRESS: | ||
356 | data->operation.orientation = 0x3; | ||
357 | data->operation.operation = 0x16; | ||
358 | break; | ||
359 | case DASD_ECKD_CCW_WRITE_RECORD_ZERO: | ||
360 | data->operation.orientation = 0x1; | ||
361 | data->operation.operation = 0x03; | ||
362 | data->count++; | ||
363 | break; | ||
364 | case DASD_ECKD_CCW_READ_RECORD_ZERO: | ||
365 | data->operation.orientation = 0x3; | ||
366 | data->operation.operation = 0x16; | ||
367 | data->count++; | ||
368 | break; | ||
369 | case DASD_ECKD_CCW_WRITE: | ||
370 | case DASD_ECKD_CCW_WRITE_MT: | ||
371 | case DASD_ECKD_CCW_WRITE_KD: | ||
372 | case DASD_ECKD_CCW_WRITE_KD_MT: | ||
373 | data->auxiliary.last_bytes_used = 0x1; | ||
374 | data->length = reclen; | ||
375 | data->operation.operation = 0x01; | ||
376 | break; | ||
377 | case DASD_ECKD_CCW_WRITE_CKD: | ||
378 | case DASD_ECKD_CCW_WRITE_CKD_MT: | ||
379 | data->auxiliary.last_bytes_used = 0x1; | ||
380 | data->length = reclen; | ||
381 | data->operation.operation = 0x03; | ||
382 | break; | ||
383 | case DASD_ECKD_CCW_READ: | ||
384 | case DASD_ECKD_CCW_READ_MT: | ||
385 | case DASD_ECKD_CCW_READ_KD: | ||
386 | case DASD_ECKD_CCW_READ_KD_MT: | ||
387 | data->auxiliary.last_bytes_used = 0x1; | ||
388 | data->length = reclen; | ||
389 | data->operation.operation = 0x06; | ||
390 | break; | ||
391 | case DASD_ECKD_CCW_READ_CKD: | ||
392 | case DASD_ECKD_CCW_READ_CKD_MT: | ||
393 | data->auxiliary.last_bytes_used = 0x1; | ||
394 | data->length = reclen; | ||
395 | data->operation.operation = 0x16; | ||
396 | break; | ||
397 | case DASD_ECKD_CCW_READ_COUNT: | ||
398 | data->operation.operation = 0x06; | ||
399 | break; | ||
400 | case DASD_ECKD_CCW_ERASE: | ||
401 | data->length = reclen; | ||
402 | data->auxiliary.last_bytes_used = 0x1; | ||
403 | data->operation.operation = 0x0b; | ||
404 | break; | ||
405 | default: | ||
406 | DEV_MESSAGE(KERN_ERR, device, "unknown opcode 0x%x", cmd); | ||
407 | } | ||
408 | data->seek_addr.cyl = data->search_arg.cyl = | ||
409 | trk / private->rdc_data.trk_per_cyl; | ||
410 | data->seek_addr.head = data->search_arg.head = | ||
411 | trk % private->rdc_data.trk_per_cyl; | ||
412 | data->search_arg.record = rec_on_trk; | ||
413 | } | ||
414 | |||
415 | /* | ||
416 | * Returns 1 if the block is one of the special blocks that needs | ||
417 | * to get read/written with the KD variant of the command. | ||
418 | * That is DASD_ECKD_READ_KD_MT instead of DASD_ECKD_READ_MT and | ||
419 | * DASD_ECKD_WRITE_KD_MT instead of DASD_ECKD_WRITE_MT. | ||
420 | * Luckily the KD variants differ only by one bit (0x08) from the | ||
421 | * normal variant. So don't wonder about code like: | ||
422 | * if (dasd_eckd_cdl_special(blk_per_trk, recid)) | ||
423 | * ccw->cmd_code |= 0x8; | ||
424 | */ | ||
425 | static inline int | ||
426 | dasd_eckd_cdl_special(int blk_per_trk, int recid) | ||
427 | { | ||
428 | if (recid < 3) | ||
429 | return 1; | ||
430 | if (recid < blk_per_trk) | ||
431 | return 0; | ||
432 | if (recid < 2 * blk_per_trk) | ||
433 | return 1; | ||
434 | return 0; | ||
435 | } | ||
436 | |||
437 | /* | ||
438 | * Returns the record size for the special blocks of the cdl format. | ||
439 | * Only returns something useful if dasd_eckd_cdl_special is true | ||
440 | * for the recid. | ||
441 | */ | ||
442 | static inline int | ||
443 | dasd_eckd_cdl_reclen(int recid) | ||
444 | { | ||
445 | if (recid < 3) | ||
446 | return sizes_trk0[recid]; | ||
447 | return LABEL_SIZE; | ||
448 | } | ||
449 | |||
450 | static int | ||
451 | dasd_eckd_read_conf(struct dasd_device *device) | ||
452 | { | ||
453 | void *conf_data; | ||
454 | int conf_len, conf_data_saved; | ||
455 | int rc; | ||
456 | __u8 lpm; | ||
457 | struct dasd_eckd_private *private; | ||
458 | struct dasd_eckd_path *path_data; | ||
459 | |||
460 | private = (struct dasd_eckd_private *) device->private; | ||
461 | path_data = (struct dasd_eckd_path *) &private->path_data; | ||
462 | path_data->opm = ccw_device_get_path_mask(device->cdev); | ||
463 | lpm = 0x80; | ||
464 | conf_data_saved = 0; | ||
465 | |||
466 | /* get configuration data per operational path */ | ||
467 | for (lpm = 0x80; lpm; lpm>>= 1) { | ||
468 | if (lpm & path_data->opm){ | ||
469 | rc = read_conf_data_lpm(device->cdev, &conf_data, | ||
470 | &conf_len, lpm); | ||
471 | if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */ | ||
472 | MESSAGE(KERN_WARNING, | ||
473 | "Read configuration data returned " | ||
474 | "error %d", rc); | ||
475 | return rc; | ||
476 | } | ||
477 | if (conf_data == NULL) { | ||
478 | MESSAGE(KERN_WARNING, "%s", "No configuration " | ||
479 | "data retrieved"); | ||
480 | continue; /* no errror */ | ||
481 | } | ||
482 | if (conf_len != sizeof (struct dasd_eckd_confdata)) { | ||
483 | MESSAGE(KERN_WARNING, | ||
484 | "sizes of configuration data mismatch" | ||
485 | "%d (read) vs %ld (expected)", | ||
486 | conf_len, | ||
487 | sizeof (struct dasd_eckd_confdata)); | ||
488 | kfree(conf_data); | ||
489 | continue; /* no errror */ | ||
490 | } | ||
491 | /* save first valid configuration data */ | ||
492 | if (!conf_data_saved){ | ||
493 | memcpy(&private->conf_data, conf_data, | ||
494 | sizeof (struct dasd_eckd_confdata)); | ||
495 | conf_data_saved++; | ||
496 | } | ||
497 | switch (((char *)conf_data)[242] & 0x07){ | ||
498 | case 0x02: | ||
499 | path_data->npm |= lpm; | ||
500 | break; | ||
501 | case 0x03: | ||
502 | path_data->ppm |= lpm; | ||
503 | break; | ||
504 | } | ||
505 | kfree(conf_data); | ||
506 | } | ||
507 | } | ||
508 | return 0; | ||
509 | } | ||
510 | |||
511 | |||
512 | static int | ||
513 | dasd_eckd_check_characteristics(struct dasd_device *device) | ||
514 | { | ||
515 | struct dasd_eckd_private *private; | ||
516 | void *rdc_data; | ||
517 | int rc; | ||
518 | |||
519 | private = (struct dasd_eckd_private *) device->private; | ||
520 | if (private == NULL) { | ||
521 | private = kmalloc(sizeof(struct dasd_eckd_private), | ||
522 | GFP_KERNEL | GFP_DMA); | ||
523 | if (private == NULL) { | ||
524 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
525 | "memory allocation failed for private " | ||
526 | "data"); | ||
527 | return -ENOMEM; | ||
528 | } | ||
529 | memset(private, 0, sizeof(struct dasd_eckd_private)); | ||
530 | device->private = (void *) private; | ||
531 | } | ||
532 | /* Invalidate status of initial analysis. */ | ||
533 | private->init_cqr_status = -1; | ||
534 | /* Set default cache operations. */ | ||
535 | private->attrib.operation = DASD_NORMAL_CACHE; | ||
536 | private->attrib.nr_cyl = 0; | ||
537 | |||
538 | /* Read Device Characteristics */ | ||
539 | rdc_data = (void *) &(private->rdc_data); | ||
540 | rc = read_dev_chars(device->cdev, &rdc_data, 64); | ||
541 | if (rc) { | ||
542 | DEV_MESSAGE(KERN_WARNING, device, | ||
543 | "Read device characteristics returned error %d", | ||
544 | rc); | ||
545 | return rc; | ||
546 | } | ||
547 | |||
548 | DEV_MESSAGE(KERN_INFO, device, | ||
549 | "%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d", | ||
550 | private->rdc_data.dev_type, | ||
551 | private->rdc_data.dev_model, | ||
552 | private->rdc_data.cu_type, | ||
553 | private->rdc_data.cu_model.model, | ||
554 | private->rdc_data.no_cyl, | ||
555 | private->rdc_data.trk_per_cyl, | ||
556 | private->rdc_data.sec_per_trk); | ||
557 | |||
558 | /* Read Configuration Data */ | ||
559 | rc = dasd_eckd_read_conf (device); | ||
560 | return rc; | ||
561 | |||
562 | } | ||
563 | |||
564 | static struct dasd_ccw_req * | ||
565 | dasd_eckd_analysis_ccw(struct dasd_device *device) | ||
566 | { | ||
567 | struct dasd_eckd_private *private; | ||
568 | struct eckd_count *count_data; | ||
569 | struct LO_eckd_data *LO_data; | ||
570 | struct dasd_ccw_req *cqr; | ||
571 | struct ccw1 *ccw; | ||
572 | int cplength, datasize; | ||
573 | int i; | ||
574 | |||
575 | private = (struct dasd_eckd_private *) device->private; | ||
576 | |||
577 | cplength = 8; | ||
578 | datasize = sizeof(struct DE_eckd_data) + 2*sizeof(struct LO_eckd_data); | ||
579 | cqr = dasd_smalloc_request(dasd_eckd_discipline.name, | ||
580 | cplength, datasize, device); | ||
581 | if (IS_ERR(cqr)) | ||
582 | return cqr; | ||
583 | ccw = cqr->cpaddr; | ||
584 | /* Define extent for the first 3 tracks. */ | ||
585 | define_extent(ccw++, cqr->data, 0, 2, | ||
586 | DASD_ECKD_CCW_READ_COUNT, device); | ||
587 | LO_data = cqr->data + sizeof (struct DE_eckd_data); | ||
588 | /* Locate record for the first 4 records on track 0. */ | ||
589 | ccw[-1].flags |= CCW_FLAG_CC; | ||
590 | locate_record(ccw++, LO_data++, 0, 0, 4, | ||
591 | DASD_ECKD_CCW_READ_COUNT, device, 0); | ||
592 | |||
593 | count_data = private->count_area; | ||
594 | for (i = 0; i < 4; i++) { | ||
595 | ccw[-1].flags |= CCW_FLAG_CC; | ||
596 | ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT; | ||
597 | ccw->flags = 0; | ||
598 | ccw->count = 8; | ||
599 | ccw->cda = (__u32)(addr_t) count_data; | ||
600 | ccw++; | ||
601 | count_data++; | ||
602 | } | ||
603 | |||
604 | /* Locate record for the first record on track 2. */ | ||
605 | ccw[-1].flags |= CCW_FLAG_CC; | ||
606 | locate_record(ccw++, LO_data++, 2, 0, 1, | ||
607 | DASD_ECKD_CCW_READ_COUNT, device, 0); | ||
608 | /* Read count ccw. */ | ||
609 | ccw[-1].flags |= CCW_FLAG_CC; | ||
610 | ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT; | ||
611 | ccw->flags = 0; | ||
612 | ccw->count = 8; | ||
613 | ccw->cda = (__u32)(addr_t) count_data; | ||
614 | |||
615 | cqr->device = device; | ||
616 | cqr->retries = 0; | ||
617 | cqr->buildclk = get_clock(); | ||
618 | cqr->status = DASD_CQR_FILLED; | ||
619 | return cqr; | ||
620 | } | ||
621 | |||
622 | /* | ||
623 | * This is the callback function for the init_analysis cqr. It saves | ||
624 | * the status of the initial analysis ccw before it frees it and kicks | ||
625 | * the device to continue the startup sequence. This will call | ||
626 | * dasd_eckd_do_analysis again (if the devices has not been marked | ||
627 | * for deletion in the meantime). | ||
628 | */ | ||
629 | static void | ||
630 | dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data) | ||
631 | { | ||
632 | struct dasd_eckd_private *private; | ||
633 | struct dasd_device *device; | ||
634 | |||
635 | device = init_cqr->device; | ||
636 | private = (struct dasd_eckd_private *) device->private; | ||
637 | private->init_cqr_status = init_cqr->status; | ||
638 | dasd_sfree_request(init_cqr, device); | ||
639 | dasd_kick_device(device); | ||
640 | } | ||
641 | |||
642 | static int | ||
643 | dasd_eckd_start_analysis(struct dasd_device *device) | ||
644 | { | ||
645 | struct dasd_eckd_private *private; | ||
646 | struct dasd_ccw_req *init_cqr; | ||
647 | |||
648 | private = (struct dasd_eckd_private *) device->private; | ||
649 | init_cqr = dasd_eckd_analysis_ccw(device); | ||
650 | if (IS_ERR(init_cqr)) | ||
651 | return PTR_ERR(init_cqr); | ||
652 | init_cqr->callback = dasd_eckd_analysis_callback; | ||
653 | init_cqr->callback_data = NULL; | ||
654 | init_cqr->expires = 5*HZ; | ||
655 | dasd_add_request_head(init_cqr); | ||
656 | return -EAGAIN; | ||
657 | } | ||
658 | |||
659 | static int | ||
660 | dasd_eckd_end_analysis(struct dasd_device *device) | ||
661 | { | ||
662 | struct dasd_eckd_private *private; | ||
663 | struct eckd_count *count_area; | ||
664 | unsigned int sb, blk_per_trk; | ||
665 | int status, i; | ||
666 | |||
667 | private = (struct dasd_eckd_private *) device->private; | ||
668 | status = private->init_cqr_status; | ||
669 | private->init_cqr_status = -1; | ||
670 | if (status != DASD_CQR_DONE) { | ||
671 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
672 | "volume analysis returned unformatted disk"); | ||
673 | return -EMEDIUMTYPE; | ||
674 | } | ||
675 | |||
676 | private->uses_cdl = 1; | ||
677 | /* Calculate number of blocks/records per track. */ | ||
678 | blk_per_trk = recs_per_track(&private->rdc_data, 0, device->bp_block); | ||
679 | /* Check Track 0 for Compatible Disk Layout */ | ||
680 | count_area = NULL; | ||
681 | for (i = 0; i < 3; i++) { | ||
682 | if (private->count_area[i].kl != 4 || | ||
683 | private->count_area[i].dl != dasd_eckd_cdl_reclen(i) - 4) { | ||
684 | private->uses_cdl = 0; | ||
685 | break; | ||
686 | } | ||
687 | } | ||
688 | if (i == 3) | ||
689 | count_area = &private->count_area[4]; | ||
690 | |||
691 | if (private->uses_cdl == 0) { | ||
692 | for (i = 0; i < 5; i++) { | ||
693 | if ((private->count_area[i].kl != 0) || | ||
694 | (private->count_area[i].dl != | ||
695 | private->count_area[0].dl)) | ||
696 | break; | ||
697 | } | ||
698 | if (i == 5) | ||
699 | count_area = &private->count_area[0]; | ||
700 | } else { | ||
701 | if (private->count_area[3].record == 1) | ||
702 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
703 | "Trk 0: no records after VTOC!"); | ||
704 | } | ||
705 | if (count_area != NULL && count_area->kl == 0) { | ||
706 | /* we found notthing violating our disk layout */ | ||
707 | if (dasd_check_blocksize(count_area->dl) == 0) | ||
708 | device->bp_block = count_area->dl; | ||
709 | } | ||
710 | if (device->bp_block == 0) { | ||
711 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
712 | "Volume has incompatible disk layout"); | ||
713 | return -EMEDIUMTYPE; | ||
714 | } | ||
715 | device->s2b_shift = 0; /* bits to shift 512 to get a block */ | ||
716 | for (sb = 512; sb < device->bp_block; sb = sb << 1) | ||
717 | device->s2b_shift++; | ||
718 | |||
719 | blk_per_trk = recs_per_track(&private->rdc_data, 0, device->bp_block); | ||
720 | device->blocks = (private->rdc_data.no_cyl * | ||
721 | private->rdc_data.trk_per_cyl * | ||
722 | blk_per_trk); | ||
723 | |||
724 | DEV_MESSAGE(KERN_INFO, device, | ||
725 | "(%dkB blks): %dkB at %dkB/trk %s", | ||
726 | (device->bp_block >> 10), | ||
727 | ((private->rdc_data.no_cyl * | ||
728 | private->rdc_data.trk_per_cyl * | ||
729 | blk_per_trk * (device->bp_block >> 9)) >> 1), | ||
730 | ((blk_per_trk * device->bp_block) >> 10), | ||
731 | private->uses_cdl ? | ||
732 | "compatible disk layout" : "linux disk layout"); | ||
733 | |||
734 | return 0; | ||
735 | } | ||
736 | |||
737 | static int | ||
738 | dasd_eckd_do_analysis(struct dasd_device *device) | ||
739 | { | ||
740 | struct dasd_eckd_private *private; | ||
741 | |||
742 | private = (struct dasd_eckd_private *) device->private; | ||
743 | if (private->init_cqr_status < 0) | ||
744 | return dasd_eckd_start_analysis(device); | ||
745 | else | ||
746 | return dasd_eckd_end_analysis(device); | ||
747 | } | ||
748 | |||
749 | static int | ||
750 | dasd_eckd_fill_geometry(struct dasd_device *device, struct hd_geometry *geo) | ||
751 | { | ||
752 | struct dasd_eckd_private *private; | ||
753 | |||
754 | private = (struct dasd_eckd_private *) device->private; | ||
755 | if (dasd_check_blocksize(device->bp_block) == 0) { | ||
756 | geo->sectors = recs_per_track(&private->rdc_data, | ||
757 | 0, device->bp_block); | ||
758 | } | ||
759 | geo->cylinders = private->rdc_data.no_cyl; | ||
760 | geo->heads = private->rdc_data.trk_per_cyl; | ||
761 | return 0; | ||
762 | } | ||
763 | |||
764 | static struct dasd_ccw_req * | ||
765 | dasd_eckd_format_device(struct dasd_device * device, | ||
766 | struct format_data_t * fdata) | ||
767 | { | ||
768 | struct dasd_eckd_private *private; | ||
769 | struct dasd_ccw_req *fcp; | ||
770 | struct eckd_count *ect; | ||
771 | struct ccw1 *ccw; | ||
772 | void *data; | ||
773 | int rpt, cyl, head; | ||
774 | int cplength, datasize; | ||
775 | int i; | ||
776 | |||
777 | private = (struct dasd_eckd_private *) device->private; | ||
778 | rpt = recs_per_track(&private->rdc_data, 0, fdata->blksize); | ||
779 | cyl = fdata->start_unit / private->rdc_data.trk_per_cyl; | ||
780 | head = fdata->start_unit % private->rdc_data.trk_per_cyl; | ||
781 | |||
782 | /* Sanity checks. */ | ||
783 | if (fdata->start_unit >= | ||
784 | (private->rdc_data.no_cyl * private->rdc_data.trk_per_cyl)) { | ||
785 | DEV_MESSAGE(KERN_INFO, device, "Track no %d too big!", | ||
786 | fdata->start_unit); | ||
787 | return ERR_PTR(-EINVAL); | ||
788 | } | ||
789 | if (fdata->start_unit > fdata->stop_unit) { | ||
790 | DEV_MESSAGE(KERN_INFO, device, "Track %d reached! ending.", | ||
791 | fdata->start_unit); | ||
792 | return ERR_PTR(-EINVAL); | ||
793 | } | ||
794 | if (dasd_check_blocksize(fdata->blksize) != 0) { | ||
795 | DEV_MESSAGE(KERN_WARNING, device, | ||
796 | "Invalid blocksize %d...terminating!", | ||
797 | fdata->blksize); | ||
798 | return ERR_PTR(-EINVAL); | ||
799 | } | ||
800 | |||
801 | /* | ||
802 | * fdata->intensity is a bit string that tells us what to do: | ||
803 | * Bit 0: write record zero | ||
804 | * Bit 1: write home address, currently not supported | ||
805 | * Bit 2: invalidate tracks | ||
806 | * Bit 3: use OS/390 compatible disk layout (cdl) | ||
807 | * Only some bit combinations do make sense. | ||
808 | */ | ||
809 | switch (fdata->intensity) { | ||
810 | case 0x00: /* Normal format */ | ||
811 | case 0x08: /* Normal format, use cdl. */ | ||
812 | cplength = 2 + rpt; | ||
813 | datasize = sizeof(struct DE_eckd_data) + | ||
814 | sizeof(struct LO_eckd_data) + | ||
815 | rpt * sizeof(struct eckd_count); | ||
816 | break; | ||
817 | case 0x01: /* Write record zero and format track. */ | ||
818 | case 0x09: /* Write record zero and format track, use cdl. */ | ||
819 | cplength = 3 + rpt; | ||
820 | datasize = sizeof(struct DE_eckd_data) + | ||
821 | sizeof(struct LO_eckd_data) + | ||
822 | sizeof(struct eckd_count) + | ||
823 | rpt * sizeof(struct eckd_count); | ||
824 | break; | ||
825 | case 0x04: /* Invalidate track. */ | ||
826 | case 0x0c: /* Invalidate track, use cdl. */ | ||
827 | cplength = 3; | ||
828 | datasize = sizeof(struct DE_eckd_data) + | ||
829 | sizeof(struct LO_eckd_data) + | ||
830 | sizeof(struct eckd_count); | ||
831 | break; | ||
832 | default: | ||
833 | DEV_MESSAGE(KERN_WARNING, device, "Invalid flags 0x%x.", | ||
834 | fdata->intensity); | ||
835 | return ERR_PTR(-EINVAL); | ||
836 | } | ||
837 | /* Allocate the format ccw request. */ | ||
838 | fcp = dasd_smalloc_request(dasd_eckd_discipline.name, | ||
839 | cplength, datasize, device); | ||
840 | if (IS_ERR(fcp)) | ||
841 | return fcp; | ||
842 | |||
843 | data = fcp->data; | ||
844 | ccw = fcp->cpaddr; | ||
845 | |||
846 | switch (fdata->intensity & ~0x08) { | ||
847 | case 0x00: /* Normal format. */ | ||
848 | define_extent(ccw++, (struct DE_eckd_data *) data, | ||
849 | fdata->start_unit, fdata->start_unit, | ||
850 | DASD_ECKD_CCW_WRITE_CKD, device); | ||
851 | data += sizeof(struct DE_eckd_data); | ||
852 | ccw[-1].flags |= CCW_FLAG_CC; | ||
853 | locate_record(ccw++, (struct LO_eckd_data *) data, | ||
854 | fdata->start_unit, 0, rpt, | ||
855 | DASD_ECKD_CCW_WRITE_CKD, device, | ||
856 | fdata->blksize); | ||
857 | data += sizeof(struct LO_eckd_data); | ||
858 | break; | ||
859 | case 0x01: /* Write record zero + format track. */ | ||
860 | define_extent(ccw++, (struct DE_eckd_data *) data, | ||
861 | fdata->start_unit, fdata->start_unit, | ||
862 | DASD_ECKD_CCW_WRITE_RECORD_ZERO, | ||
863 | device); | ||
864 | data += sizeof(struct DE_eckd_data); | ||
865 | ccw[-1].flags |= CCW_FLAG_CC; | ||
866 | locate_record(ccw++, (struct LO_eckd_data *) data, | ||
867 | fdata->start_unit, 0, rpt + 1, | ||
868 | DASD_ECKD_CCW_WRITE_RECORD_ZERO, device, | ||
869 | device->bp_block); | ||
870 | data += sizeof(struct LO_eckd_data); | ||
871 | break; | ||
872 | case 0x04: /* Invalidate track. */ | ||
873 | define_extent(ccw++, (struct DE_eckd_data *) data, | ||
874 | fdata->start_unit, fdata->start_unit, | ||
875 | DASD_ECKD_CCW_WRITE_CKD, device); | ||
876 | data += sizeof(struct DE_eckd_data); | ||
877 | ccw[-1].flags |= CCW_FLAG_CC; | ||
878 | locate_record(ccw++, (struct LO_eckd_data *) data, | ||
879 | fdata->start_unit, 0, 1, | ||
880 | DASD_ECKD_CCW_WRITE_CKD, device, 8); | ||
881 | data += sizeof(struct LO_eckd_data); | ||
882 | break; | ||
883 | } | ||
884 | if (fdata->intensity & 0x01) { /* write record zero */ | ||
885 | ect = (struct eckd_count *) data; | ||
886 | data += sizeof(struct eckd_count); | ||
887 | ect->cyl = cyl; | ||
888 | ect->head = head; | ||
889 | ect->record = 0; | ||
890 | ect->kl = 0; | ||
891 | ect->dl = 8; | ||
892 | ccw[-1].flags |= CCW_FLAG_CC; | ||
893 | ccw->cmd_code = DASD_ECKD_CCW_WRITE_RECORD_ZERO; | ||
894 | ccw->flags = CCW_FLAG_SLI; | ||
895 | ccw->count = 8; | ||
896 | ccw->cda = (__u32)(addr_t) ect; | ||
897 | ccw++; | ||
898 | } | ||
899 | if ((fdata->intensity & ~0x08) & 0x04) { /* erase track */ | ||
900 | ect = (struct eckd_count *) data; | ||
901 | data += sizeof(struct eckd_count); | ||
902 | ect->cyl = cyl; | ||
903 | ect->head = head; | ||
904 | ect->record = 1; | ||
905 | ect->kl = 0; | ||
906 | ect->dl = 0; | ||
907 | ccw[-1].flags |= CCW_FLAG_CC; | ||
908 | ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD; | ||
909 | ccw->flags = CCW_FLAG_SLI; | ||
910 | ccw->count = 8; | ||
911 | ccw->cda = (__u32)(addr_t) ect; | ||
912 | } else { /* write remaining records */ | ||
913 | for (i = 0; i < rpt; i++) { | ||
914 | ect = (struct eckd_count *) data; | ||
915 | data += sizeof(struct eckd_count); | ||
916 | ect->cyl = cyl; | ||
917 | ect->head = head; | ||
918 | ect->record = i + 1; | ||
919 | ect->kl = 0; | ||
920 | ect->dl = fdata->blksize; | ||
921 | /* Check for special tracks 0-1 when formatting CDL */ | ||
922 | if ((fdata->intensity & 0x08) && | ||
923 | fdata->start_unit == 0) { | ||
924 | if (i < 3) { | ||
925 | ect->kl = 4; | ||
926 | ect->dl = sizes_trk0[i] - 4; | ||
927 | } | ||
928 | } | ||
929 | if ((fdata->intensity & 0x08) && | ||
930 | fdata->start_unit == 1) { | ||
931 | ect->kl = 44; | ||
932 | ect->dl = LABEL_SIZE - 44; | ||
933 | } | ||
934 | ccw[-1].flags |= CCW_FLAG_CC; | ||
935 | ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD; | ||
936 | ccw->flags = CCW_FLAG_SLI; | ||
937 | ccw->count = 8; | ||
938 | ccw->cda = (__u32)(addr_t) ect; | ||
939 | ccw++; | ||
940 | } | ||
941 | } | ||
942 | fcp->device = device; | ||
943 | fcp->retries = 2; /* set retry counter to enable ERP */ | ||
944 | fcp->buildclk = get_clock(); | ||
945 | fcp->status = DASD_CQR_FILLED; | ||
946 | return fcp; | ||
947 | } | ||
948 | |||
949 | static dasd_era_t | ||
950 | dasd_eckd_examine_error(struct dasd_ccw_req * cqr, struct irb * irb) | ||
951 | { | ||
952 | struct dasd_device *device = (struct dasd_device *) cqr->device; | ||
953 | struct ccw_device *cdev = device->cdev; | ||
954 | |||
955 | if (irb->scsw.cstat == 0x00 && | ||
956 | irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) | ||
957 | return dasd_era_none; | ||
958 | |||
959 | switch (cdev->id.cu_type) { | ||
960 | case 0x3990: | ||
961 | case 0x2105: | ||
962 | case 0x2107: | ||
963 | case 0x1750: | ||
964 | return dasd_3990_erp_examine(cqr, irb); | ||
965 | case 0x9343: | ||
966 | return dasd_9343_erp_examine(cqr, irb); | ||
967 | case 0x3880: | ||
968 | default: | ||
969 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
970 | "default (unknown CU type) - RECOVERABLE return"); | ||
971 | return dasd_era_recover; | ||
972 | } | ||
973 | } | ||
974 | |||
975 | static dasd_erp_fn_t | ||
976 | dasd_eckd_erp_action(struct dasd_ccw_req * cqr) | ||
977 | { | ||
978 | struct dasd_device *device = (struct dasd_device *) cqr->device; | ||
979 | struct ccw_device *cdev = device->cdev; | ||
980 | |||
981 | switch (cdev->id.cu_type) { | ||
982 | case 0x3990: | ||
983 | case 0x2105: | ||
984 | case 0x2107: | ||
985 | case 0x1750: | ||
986 | return dasd_3990_erp_action; | ||
987 | case 0x9343: | ||
988 | case 0x3880: | ||
989 | default: | ||
990 | return dasd_default_erp_action; | ||
991 | } | ||
992 | } | ||
993 | |||
994 | static dasd_erp_fn_t | ||
995 | dasd_eckd_erp_postaction(struct dasd_ccw_req * cqr) | ||
996 | { | ||
997 | return dasd_default_erp_postaction; | ||
998 | } | ||
999 | |||
1000 | static struct dasd_ccw_req * | ||
1001 | dasd_eckd_build_cp(struct dasd_device * device, struct request *req) | ||
1002 | { | ||
1003 | struct dasd_eckd_private *private; | ||
1004 | unsigned long *idaws; | ||
1005 | struct LO_eckd_data *LO_data; | ||
1006 | struct dasd_ccw_req *cqr; | ||
1007 | struct ccw1 *ccw; | ||
1008 | struct bio *bio; | ||
1009 | struct bio_vec *bv; | ||
1010 | char *dst; | ||
1011 | unsigned int blksize, blk_per_trk, off; | ||
1012 | int count, cidaw, cplength, datasize; | ||
1013 | sector_t recid, first_rec, last_rec; | ||
1014 | sector_t first_trk, last_trk; | ||
1015 | unsigned int first_offs, last_offs; | ||
1016 | unsigned char cmd, rcmd; | ||
1017 | int i; | ||
1018 | |||
1019 | private = (struct dasd_eckd_private *) device->private; | ||
1020 | if (rq_data_dir(req) == READ) | ||
1021 | cmd = DASD_ECKD_CCW_READ_MT; | ||
1022 | else if (rq_data_dir(req) == WRITE) | ||
1023 | cmd = DASD_ECKD_CCW_WRITE_MT; | ||
1024 | else | ||
1025 | return ERR_PTR(-EINVAL); | ||
1026 | /* Calculate number of blocks/records per track. */ | ||
1027 | blksize = device->bp_block; | ||
1028 | blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize); | ||
1029 | /* Calculate record id of first and last block. */ | ||
1030 | first_rec = first_trk = req->sector >> device->s2b_shift; | ||
1031 | first_offs = sector_div(first_trk, blk_per_trk); | ||
1032 | last_rec = last_trk = | ||
1033 | (req->sector + req->nr_sectors - 1) >> device->s2b_shift; | ||
1034 | last_offs = sector_div(last_trk, blk_per_trk); | ||
1035 | /* Check struct bio and count the number of blocks for the request. */ | ||
1036 | count = 0; | ||
1037 | cidaw = 0; | ||
1038 | rq_for_each_bio(bio, req) { | ||
1039 | bio_for_each_segment(bv, bio, i) { | ||
1040 | if (bv->bv_len & (blksize - 1)) | ||
1041 | /* Eckd can only do full blocks. */ | ||
1042 | return ERR_PTR(-EINVAL); | ||
1043 | count += bv->bv_len >> (device->s2b_shift + 9); | ||
1044 | #if defined(CONFIG_ARCH_S390X) | ||
1045 | if (idal_is_needed (page_address(bv->bv_page), | ||
1046 | bv->bv_len)) | ||
1047 | cidaw += bv->bv_len >> (device->s2b_shift + 9); | ||
1048 | #endif | ||
1049 | } | ||
1050 | } | ||
1051 | /* Paranoia. */ | ||
1052 | if (count != last_rec - first_rec + 1) | ||
1053 | return ERR_PTR(-EINVAL); | ||
1054 | /* 1x define extent + 1x locate record + number of blocks */ | ||
1055 | cplength = 2 + count; | ||
1056 | /* 1x define extent + 1x locate record + cidaws*sizeof(long) */ | ||
1057 | datasize = sizeof(struct DE_eckd_data) + sizeof(struct LO_eckd_data) + | ||
1058 | cidaw * sizeof(unsigned long); | ||
1059 | /* Find out the number of additional locate record ccws for cdl. */ | ||
1060 | if (private->uses_cdl && first_rec < 2*blk_per_trk) { | ||
1061 | if (last_rec >= 2*blk_per_trk) | ||
1062 | count = 2*blk_per_trk - first_rec; | ||
1063 | cplength += count; | ||
1064 | datasize += count*sizeof(struct LO_eckd_data); | ||
1065 | } | ||
1066 | /* Allocate the ccw request. */ | ||
1067 | cqr = dasd_smalloc_request(dasd_eckd_discipline.name, | ||
1068 | cplength, datasize, device); | ||
1069 | if (IS_ERR(cqr)) | ||
1070 | return cqr; | ||
1071 | ccw = cqr->cpaddr; | ||
1072 | /* First ccw is define extent. */ | ||
1073 | define_extent(ccw++, cqr->data, first_trk, last_trk, cmd, device); | ||
1074 | /* Build locate_record+read/write/ccws. */ | ||
1075 | idaws = (unsigned long *) (cqr->data + sizeof(struct DE_eckd_data)); | ||
1076 | LO_data = (struct LO_eckd_data *) (idaws + cidaw); | ||
1077 | recid = first_rec; | ||
1078 | if (private->uses_cdl == 0 || recid > 2*blk_per_trk) { | ||
1079 | /* Only standard blocks so there is just one locate record. */ | ||
1080 | ccw[-1].flags |= CCW_FLAG_CC; | ||
1081 | locate_record(ccw++, LO_data++, first_trk, first_offs + 1, | ||
1082 | last_rec - recid + 1, cmd, device, blksize); | ||
1083 | } | ||
1084 | rq_for_each_bio(bio, req) bio_for_each_segment(bv, bio, i) { | ||
1085 | dst = page_address(bv->bv_page) + bv->bv_offset; | ||
1086 | if (dasd_page_cache) { | ||
1087 | char *copy = kmem_cache_alloc(dasd_page_cache, | ||
1088 | SLAB_DMA | __GFP_NOWARN); | ||
1089 | if (copy && rq_data_dir(req) == WRITE) | ||
1090 | memcpy(copy + bv->bv_offset, dst, bv->bv_len); | ||
1091 | if (copy) | ||
1092 | dst = copy + bv->bv_offset; | ||
1093 | } | ||
1094 | for (off = 0; off < bv->bv_len; off += blksize) { | ||
1095 | sector_t trkid = recid; | ||
1096 | unsigned int recoffs = sector_div(trkid, blk_per_trk); | ||
1097 | rcmd = cmd; | ||
1098 | count = blksize; | ||
1099 | /* Locate record for cdl special block ? */ | ||
1100 | if (private->uses_cdl && recid < 2*blk_per_trk) { | ||
1101 | if (dasd_eckd_cdl_special(blk_per_trk, recid)){ | ||
1102 | rcmd |= 0x8; | ||
1103 | count = dasd_eckd_cdl_reclen(recid); | ||
1104 | if (count < blksize) | ||
1105 | memset(dst + count, 0xe5, | ||
1106 | blksize - count); | ||
1107 | } | ||
1108 | ccw[-1].flags |= CCW_FLAG_CC; | ||
1109 | locate_record(ccw++, LO_data++, | ||
1110 | trkid, recoffs + 1, | ||
1111 | 1, rcmd, device, count); | ||
1112 | } | ||
1113 | /* Locate record for standard blocks ? */ | ||
1114 | if (private->uses_cdl && recid == 2*blk_per_trk) { | ||
1115 | ccw[-1].flags |= CCW_FLAG_CC; | ||
1116 | locate_record(ccw++, LO_data++, | ||
1117 | trkid, recoffs + 1, | ||
1118 | last_rec - recid + 1, | ||
1119 | cmd, device, count); | ||
1120 | } | ||
1121 | /* Read/write ccw. */ | ||
1122 | ccw[-1].flags |= CCW_FLAG_CC; | ||
1123 | ccw->cmd_code = rcmd; | ||
1124 | ccw->count = count; | ||
1125 | if (idal_is_needed(dst, blksize)) { | ||
1126 | ccw->cda = (__u32)(addr_t) idaws; | ||
1127 | ccw->flags = CCW_FLAG_IDA; | ||
1128 | idaws = idal_create_words(idaws, dst, blksize); | ||
1129 | } else { | ||
1130 | ccw->cda = (__u32)(addr_t) dst; | ||
1131 | ccw->flags = 0; | ||
1132 | } | ||
1133 | ccw++; | ||
1134 | dst += blksize; | ||
1135 | recid++; | ||
1136 | } | ||
1137 | } | ||
1138 | cqr->device = device; | ||
1139 | cqr->expires = 5 * 60 * HZ; /* 5 minutes */ | ||
1140 | cqr->lpm = private->path_data.ppm; | ||
1141 | cqr->retries = 256; | ||
1142 | cqr->buildclk = get_clock(); | ||
1143 | cqr->status = DASD_CQR_FILLED; | ||
1144 | return cqr; | ||
1145 | } | ||
1146 | |||
1147 | static int | ||
1148 | dasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req) | ||
1149 | { | ||
1150 | struct dasd_eckd_private *private; | ||
1151 | struct ccw1 *ccw; | ||
1152 | struct bio *bio; | ||
1153 | struct bio_vec *bv; | ||
1154 | char *dst, *cda; | ||
1155 | unsigned int blksize, blk_per_trk, off; | ||
1156 | sector_t recid; | ||
1157 | int i, status; | ||
1158 | |||
1159 | if (!dasd_page_cache) | ||
1160 | goto out; | ||
1161 | private = (struct dasd_eckd_private *) cqr->device->private; | ||
1162 | blksize = cqr->device->bp_block; | ||
1163 | blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize); | ||
1164 | recid = req->sector >> cqr->device->s2b_shift; | ||
1165 | ccw = cqr->cpaddr; | ||
1166 | /* Skip over define extent & locate record. */ | ||
1167 | ccw++; | ||
1168 | if (private->uses_cdl == 0 || recid > 2*blk_per_trk) | ||
1169 | ccw++; | ||
1170 | rq_for_each_bio(bio, req) bio_for_each_segment(bv, bio, i) { | ||
1171 | dst = page_address(bv->bv_page) + bv->bv_offset; | ||
1172 | for (off = 0; off < bv->bv_len; off += blksize) { | ||
1173 | /* Skip locate record. */ | ||
1174 | if (private->uses_cdl && recid <= 2*blk_per_trk) | ||
1175 | ccw++; | ||
1176 | if (dst) { | ||
1177 | if (ccw->flags & CCW_FLAG_IDA) | ||
1178 | cda = *((char **)((addr_t) ccw->cda)); | ||
1179 | else | ||
1180 | cda = (char *)((addr_t) ccw->cda); | ||
1181 | if (dst != cda) { | ||
1182 | if (rq_data_dir(req) == READ) | ||
1183 | memcpy(dst, cda, bv->bv_len); | ||
1184 | kmem_cache_free(dasd_page_cache, | ||
1185 | (void *)((addr_t)cda & PAGE_MASK)); | ||
1186 | } | ||
1187 | dst = NULL; | ||
1188 | } | ||
1189 | ccw++; | ||
1190 | recid++; | ||
1191 | } | ||
1192 | } | ||
1193 | out: | ||
1194 | status = cqr->status == DASD_CQR_DONE; | ||
1195 | dasd_sfree_request(cqr, cqr->device); | ||
1196 | return status; | ||
1197 | } | ||
1198 | |||
1199 | static int | ||
1200 | dasd_eckd_fill_info(struct dasd_device * device, | ||
1201 | struct dasd_information2_t * info) | ||
1202 | { | ||
1203 | struct dasd_eckd_private *private; | ||
1204 | |||
1205 | private = (struct dasd_eckd_private *) device->private; | ||
1206 | info->label_block = 2; | ||
1207 | info->FBA_layout = private->uses_cdl ? 0 : 1; | ||
1208 | info->format = private->uses_cdl ? DASD_FORMAT_CDL : DASD_FORMAT_LDL; | ||
1209 | info->characteristics_size = sizeof(struct dasd_eckd_characteristics); | ||
1210 | memcpy(info->characteristics, &private->rdc_data, | ||
1211 | sizeof(struct dasd_eckd_characteristics)); | ||
1212 | info->confdata_size = sizeof (struct dasd_eckd_confdata); | ||
1213 | memcpy(info->configuration_data, &private->conf_data, | ||
1214 | sizeof (struct dasd_eckd_confdata)); | ||
1215 | return 0; | ||
1216 | } | ||
1217 | |||
1218 | /* | ||
1219 | * SECTION: ioctl functions for eckd devices. | ||
1220 | */ | ||
1221 | |||
1222 | /* | ||
1223 | * Release device ioctl. | ||
1224 | * Buils a channel programm to releases a prior reserved | ||
1225 | * (see dasd_eckd_reserve) device. | ||
1226 | */ | ||
1227 | static int | ||
1228 | dasd_eckd_release(struct block_device *bdev, int no, long args) | ||
1229 | { | ||
1230 | struct dasd_device *device; | ||
1231 | struct dasd_ccw_req *cqr; | ||
1232 | int rc; | ||
1233 | |||
1234 | if (!capable(CAP_SYS_ADMIN)) | ||
1235 | return -EACCES; | ||
1236 | |||
1237 | device = bdev->bd_disk->private_data; | ||
1238 | if (device == NULL) | ||
1239 | return -ENODEV; | ||
1240 | |||
1241 | cqr = dasd_smalloc_request(dasd_eckd_discipline.name, | ||
1242 | 1, 32, device); | ||
1243 | if (IS_ERR(cqr)) { | ||
1244 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1245 | "Could not allocate initialization request"); | ||
1246 | return PTR_ERR(cqr); | ||
1247 | } | ||
1248 | cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RELEASE; | ||
1249 | cqr->cpaddr->flags |= CCW_FLAG_SLI; | ||
1250 | cqr->cpaddr->count = 32; | ||
1251 | cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; | ||
1252 | cqr->device = device; | ||
1253 | clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); | ||
1254 | cqr->retries = 0; | ||
1255 | cqr->expires = 2 * HZ; | ||
1256 | cqr->buildclk = get_clock(); | ||
1257 | cqr->status = DASD_CQR_FILLED; | ||
1258 | |||
1259 | rc = dasd_sleep_on_immediatly(cqr); | ||
1260 | |||
1261 | dasd_sfree_request(cqr, cqr->device); | ||
1262 | return rc; | ||
1263 | } | ||
1264 | |||
1265 | /* | ||
1266 | * Reserve device ioctl. | ||
1267 | * Options are set to 'synchronous wait for interrupt' and | ||
1268 | * 'timeout the request'. This leads to a terminate IO if | ||
1269 | * the interrupt is outstanding for a certain time. | ||
1270 | */ | ||
1271 | static int | ||
1272 | dasd_eckd_reserve(struct block_device *bdev, int no, long args) | ||
1273 | { | ||
1274 | struct dasd_device *device; | ||
1275 | struct dasd_ccw_req *cqr; | ||
1276 | int rc; | ||
1277 | |||
1278 | if (!capable(CAP_SYS_ADMIN)) | ||
1279 | return -EACCES; | ||
1280 | |||
1281 | device = bdev->bd_disk->private_data; | ||
1282 | if (device == NULL) | ||
1283 | return -ENODEV; | ||
1284 | |||
1285 | cqr = dasd_smalloc_request(dasd_eckd_discipline.name, | ||
1286 | 1, 32, device); | ||
1287 | if (IS_ERR(cqr)) { | ||
1288 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1289 | "Could not allocate initialization request"); | ||
1290 | return PTR_ERR(cqr); | ||
1291 | } | ||
1292 | cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RESERVE; | ||
1293 | cqr->cpaddr->flags |= CCW_FLAG_SLI; | ||
1294 | cqr->cpaddr->count = 32; | ||
1295 | cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; | ||
1296 | cqr->device = device; | ||
1297 | clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); | ||
1298 | cqr->retries = 0; | ||
1299 | cqr->expires = 2 * HZ; | ||
1300 | cqr->buildclk = get_clock(); | ||
1301 | cqr->status = DASD_CQR_FILLED; | ||
1302 | |||
1303 | rc = dasd_sleep_on_immediatly(cqr); | ||
1304 | |||
1305 | dasd_sfree_request(cqr, cqr->device); | ||
1306 | return rc; | ||
1307 | } | ||
1308 | |||
1309 | /* | ||
1310 | * Steal lock ioctl - unconditional reserve device. | ||
1311 | * Buils a channel programm to break a device's reservation. | ||
1312 | * (unconditional reserve) | ||
1313 | */ | ||
1314 | static int | ||
1315 | dasd_eckd_steal_lock(struct block_device *bdev, int no, long args) | ||
1316 | { | ||
1317 | struct dasd_device *device; | ||
1318 | struct dasd_ccw_req *cqr; | ||
1319 | int rc; | ||
1320 | |||
1321 | if (!capable(CAP_SYS_ADMIN)) | ||
1322 | return -EACCES; | ||
1323 | |||
1324 | device = bdev->bd_disk->private_data; | ||
1325 | if (device == NULL) | ||
1326 | return -ENODEV; | ||
1327 | |||
1328 | cqr = dasd_smalloc_request(dasd_eckd_discipline.name, | ||
1329 | 1, 32, device); | ||
1330 | if (IS_ERR(cqr)) { | ||
1331 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1332 | "Could not allocate initialization request"); | ||
1333 | return PTR_ERR(cqr); | ||
1334 | } | ||
1335 | cqr->cpaddr->cmd_code = DASD_ECKD_CCW_SLCK; | ||
1336 | cqr->cpaddr->flags |= CCW_FLAG_SLI; | ||
1337 | cqr->cpaddr->count = 32; | ||
1338 | cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; | ||
1339 | cqr->device = device; | ||
1340 | clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); | ||
1341 | cqr->retries = 0; | ||
1342 | cqr->expires = 2 * HZ; | ||
1343 | cqr->buildclk = get_clock(); | ||
1344 | cqr->status = DASD_CQR_FILLED; | ||
1345 | |||
1346 | rc = dasd_sleep_on_immediatly(cqr); | ||
1347 | |||
1348 | dasd_sfree_request(cqr, cqr->device); | ||
1349 | return rc; | ||
1350 | } | ||
1351 | |||
1352 | /* | ||
1353 | * Read performance statistics | ||
1354 | */ | ||
1355 | static int | ||
1356 | dasd_eckd_performance(struct block_device *bdev, int no, long args) | ||
1357 | { | ||
1358 | struct dasd_device *device; | ||
1359 | struct dasd_psf_prssd_data *prssdp; | ||
1360 | struct dasd_rssd_perf_stats_t *stats; | ||
1361 | struct dasd_ccw_req *cqr; | ||
1362 | struct ccw1 *ccw; | ||
1363 | int rc; | ||
1364 | |||
1365 | device = bdev->bd_disk->private_data; | ||
1366 | if (device == NULL) | ||
1367 | return -ENODEV; | ||
1368 | |||
1369 | cqr = dasd_smalloc_request(dasd_eckd_discipline.name, | ||
1370 | 1 /* PSF */ + 1 /* RSSD */ , | ||
1371 | (sizeof (struct dasd_psf_prssd_data) + | ||
1372 | sizeof (struct dasd_rssd_perf_stats_t)), | ||
1373 | device); | ||
1374 | if (IS_ERR(cqr)) { | ||
1375 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1376 | "Could not allocate initialization request"); | ||
1377 | return PTR_ERR(cqr); | ||
1378 | } | ||
1379 | cqr->device = device; | ||
1380 | cqr->retries = 0; | ||
1381 | cqr->expires = 10 * HZ; | ||
1382 | |||
1383 | /* Prepare for Read Subsystem Data */ | ||
1384 | prssdp = (struct dasd_psf_prssd_data *) cqr->data; | ||
1385 | memset(prssdp, 0, sizeof (struct dasd_psf_prssd_data)); | ||
1386 | prssdp->order = PSF_ORDER_PRSSD; | ||
1387 | prssdp->suborder = 0x01; /* Perfomance Statistics */ | ||
1388 | prssdp->varies[1] = 0x01; /* Perf Statistics for the Subsystem */ | ||
1389 | |||
1390 | ccw = cqr->cpaddr; | ||
1391 | ccw->cmd_code = DASD_ECKD_CCW_PSF; | ||
1392 | ccw->count = sizeof (struct dasd_psf_prssd_data); | ||
1393 | ccw->flags |= CCW_FLAG_CC; | ||
1394 | ccw->cda = (__u32)(addr_t) prssdp; | ||
1395 | |||
1396 | /* Read Subsystem Data - Performance Statistics */ | ||
1397 | stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1); | ||
1398 | memset(stats, 0, sizeof (struct dasd_rssd_perf_stats_t)); | ||
1399 | |||
1400 | ccw++; | ||
1401 | ccw->cmd_code = DASD_ECKD_CCW_RSSD; | ||
1402 | ccw->count = sizeof (struct dasd_rssd_perf_stats_t); | ||
1403 | ccw->cda = (__u32)(addr_t) stats; | ||
1404 | |||
1405 | cqr->buildclk = get_clock(); | ||
1406 | cqr->status = DASD_CQR_FILLED; | ||
1407 | rc = dasd_sleep_on(cqr); | ||
1408 | if (rc == 0) { | ||
1409 | /* Prepare for Read Subsystem Data */ | ||
1410 | prssdp = (struct dasd_psf_prssd_data *) cqr->data; | ||
1411 | stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1); | ||
1412 | rc = copy_to_user((long __user *) args, (long *) stats, | ||
1413 | sizeof(struct dasd_rssd_perf_stats_t)); | ||
1414 | } | ||
1415 | dasd_sfree_request(cqr, cqr->device); | ||
1416 | return rc; | ||
1417 | } | ||
1418 | |||
1419 | /* | ||
1420 | * Get attributes (cache operations) | ||
1421 | * Returnes the cache attributes used in Define Extend (DE). | ||
1422 | */ | ||
1423 | static int | ||
1424 | dasd_eckd_get_attrib (struct block_device *bdev, int no, long args) | ||
1425 | { | ||
1426 | struct dasd_device *device; | ||
1427 | struct dasd_eckd_private *private; | ||
1428 | struct attrib_data_t attrib; | ||
1429 | int rc; | ||
1430 | |||
1431 | if (!capable(CAP_SYS_ADMIN)) | ||
1432 | return -EACCES; | ||
1433 | if (!args) | ||
1434 | return -EINVAL; | ||
1435 | |||
1436 | device = bdev->bd_disk->private_data; | ||
1437 | if (device == NULL) | ||
1438 | return -ENODEV; | ||
1439 | |||
1440 | private = (struct dasd_eckd_private *) device->private; | ||
1441 | attrib = private->attrib; | ||
1442 | |||
1443 | rc = copy_to_user((long __user *) args, (long *) &attrib, | ||
1444 | sizeof (struct attrib_data_t)); | ||
1445 | |||
1446 | return rc; | ||
1447 | } | ||
1448 | |||
1449 | /* | ||
1450 | * Set attributes (cache operations) | ||
1451 | * Stores the attributes for cache operation to be used in Define Extend (DE). | ||
1452 | */ | ||
1453 | static int | ||
1454 | dasd_eckd_set_attrib(struct block_device *bdev, int no, long args) | ||
1455 | { | ||
1456 | struct dasd_device *device; | ||
1457 | struct dasd_eckd_private *private; | ||
1458 | struct attrib_data_t attrib; | ||
1459 | |||
1460 | if (!capable(CAP_SYS_ADMIN)) | ||
1461 | return -EACCES; | ||
1462 | if (!args) | ||
1463 | return -EINVAL; | ||
1464 | |||
1465 | device = bdev->bd_disk->private_data; | ||
1466 | if (device == NULL) | ||
1467 | return -ENODEV; | ||
1468 | |||
1469 | if (copy_from_user(&attrib, (void __user *) args, | ||
1470 | sizeof (struct attrib_data_t))) { | ||
1471 | return -EFAULT; | ||
1472 | } | ||
1473 | private = (struct dasd_eckd_private *) device->private; | ||
1474 | private->attrib = attrib; | ||
1475 | |||
1476 | DEV_MESSAGE(KERN_INFO, device, | ||
1477 | "cache operation mode set to %x (%i cylinder prestage)", | ||
1478 | private->attrib.operation, private->attrib.nr_cyl); | ||
1479 | return 0; | ||
1480 | } | ||
1481 | |||
1482 | /* | ||
1483 | * Print sense data and related channel program. | ||
1484 | * Parts are printed because printk buffer is only 1024 bytes. | ||
1485 | */ | ||
1486 | static void | ||
1487 | dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, | ||
1488 | struct irb *irb) | ||
1489 | { | ||
1490 | char *page; | ||
1491 | struct ccw1 *act, *end, *last; | ||
1492 | int len, sl, sct, count; | ||
1493 | |||
1494 | page = (char *) get_zeroed_page(GFP_ATOMIC); | ||
1495 | if (page == NULL) { | ||
1496 | DEV_MESSAGE(KERN_ERR, device, " %s", | ||
1497 | "No memory to dump sense data"); | ||
1498 | return; | ||
1499 | } | ||
1500 | len = sprintf(page, KERN_ERR PRINTK_HEADER | ||
1501 | " I/O status report for device %s:\n", | ||
1502 | device->cdev->dev.bus_id); | ||
1503 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | ||
1504 | " in req: %p CS: 0x%02X DS: 0x%02X\n", req, | ||
1505 | irb->scsw.cstat, irb->scsw.dstat); | ||
1506 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | ||
1507 | " device %s: Failing CCW: %p\n", | ||
1508 | device->cdev->dev.bus_id, | ||
1509 | (void *) (addr_t) irb->scsw.cpa); | ||
1510 | if (irb->esw.esw0.erw.cons) { | ||
1511 | for (sl = 0; sl < 4; sl++) { | ||
1512 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | ||
1513 | " Sense(hex) %2d-%2d:", | ||
1514 | (8 * sl), ((8 * sl) + 7)); | ||
1515 | |||
1516 | for (sct = 0; sct < 8; sct++) { | ||
1517 | len += sprintf(page + len, " %02x", | ||
1518 | irb->ecw[8 * sl + sct]); | ||
1519 | } | ||
1520 | len += sprintf(page + len, "\n"); | ||
1521 | } | ||
1522 | |||
1523 | if (irb->ecw[27] & DASD_SENSE_BIT_0) { | ||
1524 | /* 24 Byte Sense Data */ | ||
1525 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | ||
1526 | " 24 Byte: %x MSG %x, " | ||
1527 | "%s MSGb to SYSOP\n", | ||
1528 | irb->ecw[7] >> 4, irb->ecw[7] & 0x0f, | ||
1529 | irb->ecw[1] & 0x10 ? "" : "no"); | ||
1530 | } else { | ||
1531 | /* 32 Byte Sense Data */ | ||
1532 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | ||
1533 | " 32 Byte: Format: %x " | ||
1534 | "Exception class %x\n", | ||
1535 | irb->ecw[6] & 0x0f, irb->ecw[22] >> 4); | ||
1536 | } | ||
1537 | } else { | ||
1538 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | ||
1539 | " SORRY - NO VALID SENSE AVAILABLE\n"); | ||
1540 | } | ||
1541 | MESSAGE_LOG(KERN_ERR, "%s", | ||
1542 | page + sizeof(KERN_ERR PRINTK_HEADER)); | ||
1543 | |||
1544 | /* dump the Channel Program */ | ||
1545 | /* print first CCWs (maximum 8) */ | ||
1546 | act = req->cpaddr; | ||
1547 | for (last = act; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++); | ||
1548 | end = min(act + 8, last); | ||
1549 | len = sprintf(page, KERN_ERR PRINTK_HEADER | ||
1550 | " Related CP in req: %p\n", req); | ||
1551 | while (act <= end) { | ||
1552 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | ||
1553 | " CCW %p: %08X %08X DAT:", | ||
1554 | act, ((int *) act)[0], ((int *) act)[1]); | ||
1555 | for (count = 0; count < 32 && count < act->count; | ||
1556 | count += sizeof(int)) | ||
1557 | len += sprintf(page + len, " %08X", | ||
1558 | ((int *) (addr_t) act->cda) | ||
1559 | [(count>>2)]); | ||
1560 | len += sprintf(page + len, "\n"); | ||
1561 | act++; | ||
1562 | } | ||
1563 | MESSAGE_LOG(KERN_ERR, "%s", | ||
1564 | page + sizeof(KERN_ERR PRINTK_HEADER)); | ||
1565 | |||
1566 | /* print failing CCW area */ | ||
1567 | len = 0; | ||
1568 | if (act < ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2) { | ||
1569 | act = ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2; | ||
1570 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); | ||
1571 | } | ||
1572 | end = min((struct ccw1 *)(addr_t) irb->scsw.cpa + 2, last); | ||
1573 | while (act <= end) { | ||
1574 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | ||
1575 | " CCW %p: %08X %08X DAT:", | ||
1576 | act, ((int *) act)[0], ((int *) act)[1]); | ||
1577 | for (count = 0; count < 32 && count < act->count; | ||
1578 | count += sizeof(int)) | ||
1579 | len += sprintf(page + len, " %08X", | ||
1580 | ((int *) (addr_t) act->cda) | ||
1581 | [(count>>2)]); | ||
1582 | len += sprintf(page + len, "\n"); | ||
1583 | act++; | ||
1584 | } | ||
1585 | |||
1586 | /* print last CCWs */ | ||
1587 | if (act < last - 2) { | ||
1588 | act = last - 2; | ||
1589 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); | ||
1590 | } | ||
1591 | while (act <= last) { | ||
1592 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | ||
1593 | " CCW %p: %08X %08X DAT:", | ||
1594 | act, ((int *) act)[0], ((int *) act)[1]); | ||
1595 | for (count = 0; count < 32 && count < act->count; | ||
1596 | count += sizeof(int)) | ||
1597 | len += sprintf(page + len, " %08X", | ||
1598 | ((int *) (addr_t) act->cda) | ||
1599 | [(count>>2)]); | ||
1600 | len += sprintf(page + len, "\n"); | ||
1601 | act++; | ||
1602 | } | ||
1603 | if (len > 0) | ||
1604 | MESSAGE_LOG(KERN_ERR, "%s", | ||
1605 | page + sizeof(KERN_ERR PRINTK_HEADER)); | ||
1606 | free_page((unsigned long) page); | ||
1607 | } | ||
1608 | |||
1609 | /* | ||
1610 | * max_blocks is dependent on the amount of storage that is available | ||
1611 | * in the static io buffer for each device. Currently each device has | ||
1612 | * 8192 bytes (=2 pages). For 64 bit one dasd_mchunkt_t structure has | ||
1613 | * 24 bytes, the struct dasd_ccw_req has 136 bytes and each block can use | ||
1614 | * up to 16 bytes (8 for the ccw and 8 for the idal pointer). In | ||
1615 | * addition we have one define extent ccw + 16 bytes of data and one | ||
1616 | * locate record ccw + 16 bytes of data. That makes: | ||
1617 | * (8192 - 24 - 136 - 8 - 16 - 8 - 16) / 16 = 499 blocks at maximum. | ||
1618 | * We want to fit two into the available memory so that we can immediately | ||
1619 | * start the next request if one finishes off. That makes 249.5 blocks | ||
1620 | * for one request. Give a little safety and the result is 240. | ||
1621 | */ | ||
1622 | static struct dasd_discipline dasd_eckd_discipline = { | ||
1623 | .owner = THIS_MODULE, | ||
1624 | .name = "ECKD", | ||
1625 | .ebcname = "ECKD", | ||
1626 | .max_blocks = 240, | ||
1627 | .check_device = dasd_eckd_check_characteristics, | ||
1628 | .do_analysis = dasd_eckd_do_analysis, | ||
1629 | .fill_geometry = dasd_eckd_fill_geometry, | ||
1630 | .start_IO = dasd_start_IO, | ||
1631 | .term_IO = dasd_term_IO, | ||
1632 | .format_device = dasd_eckd_format_device, | ||
1633 | .examine_error = dasd_eckd_examine_error, | ||
1634 | .erp_action = dasd_eckd_erp_action, | ||
1635 | .erp_postaction = dasd_eckd_erp_postaction, | ||
1636 | .build_cp = dasd_eckd_build_cp, | ||
1637 | .free_cp = dasd_eckd_free_cp, | ||
1638 | .dump_sense = dasd_eckd_dump_sense, | ||
1639 | .fill_info = dasd_eckd_fill_info, | ||
1640 | }; | ||
1641 | |||
1642 | static int __init | ||
1643 | dasd_eckd_init(void) | ||
1644 | { | ||
1645 | int ret; | ||
1646 | |||
1647 | dasd_ioctl_no_register(THIS_MODULE, BIODASDGATTR, | ||
1648 | dasd_eckd_get_attrib); | ||
1649 | dasd_ioctl_no_register(THIS_MODULE, BIODASDSATTR, | ||
1650 | dasd_eckd_set_attrib); | ||
1651 | dasd_ioctl_no_register(THIS_MODULE, BIODASDPSRD, | ||
1652 | dasd_eckd_performance); | ||
1653 | dasd_ioctl_no_register(THIS_MODULE, BIODASDRLSE, | ||
1654 | dasd_eckd_release); | ||
1655 | dasd_ioctl_no_register(THIS_MODULE, BIODASDRSRV, | ||
1656 | dasd_eckd_reserve); | ||
1657 | dasd_ioctl_no_register(THIS_MODULE, BIODASDSLCK, | ||
1658 | dasd_eckd_steal_lock); | ||
1659 | |||
1660 | ASCEBC(dasd_eckd_discipline.ebcname, 4); | ||
1661 | |||
1662 | ret = ccw_driver_register(&dasd_eckd_driver); | ||
1663 | if (ret) { | ||
1664 | dasd_ioctl_no_unregister(THIS_MODULE, BIODASDGATTR, | ||
1665 | dasd_eckd_get_attrib); | ||
1666 | dasd_ioctl_no_unregister(THIS_MODULE, BIODASDSATTR, | ||
1667 | dasd_eckd_set_attrib); | ||
1668 | dasd_ioctl_no_unregister(THIS_MODULE, BIODASDPSRD, | ||
1669 | dasd_eckd_performance); | ||
1670 | dasd_ioctl_no_unregister(THIS_MODULE, BIODASDRLSE, | ||
1671 | dasd_eckd_release); | ||
1672 | dasd_ioctl_no_unregister(THIS_MODULE, BIODASDRSRV, | ||
1673 | dasd_eckd_reserve); | ||
1674 | dasd_ioctl_no_unregister(THIS_MODULE, BIODASDSLCK, | ||
1675 | dasd_eckd_steal_lock); | ||
1676 | return ret; | ||
1677 | } | ||
1678 | |||
1679 | dasd_generic_auto_online(&dasd_eckd_driver); | ||
1680 | return 0; | ||
1681 | } | ||
1682 | |||
1683 | static void __exit | ||
1684 | dasd_eckd_cleanup(void) | ||
1685 | { | ||
1686 | ccw_driver_unregister(&dasd_eckd_driver); | ||
1687 | |||
1688 | dasd_ioctl_no_unregister(THIS_MODULE, BIODASDGATTR, | ||
1689 | dasd_eckd_get_attrib); | ||
1690 | dasd_ioctl_no_unregister(THIS_MODULE, BIODASDSATTR, | ||
1691 | dasd_eckd_set_attrib); | ||
1692 | dasd_ioctl_no_unregister(THIS_MODULE, BIODASDPSRD, | ||
1693 | dasd_eckd_performance); | ||
1694 | dasd_ioctl_no_unregister(THIS_MODULE, BIODASDRLSE, | ||
1695 | dasd_eckd_release); | ||
1696 | dasd_ioctl_no_unregister(THIS_MODULE, BIODASDRSRV, | ||
1697 | dasd_eckd_reserve); | ||
1698 | dasd_ioctl_no_unregister(THIS_MODULE, BIODASDSLCK, | ||
1699 | dasd_eckd_steal_lock); | ||
1700 | } | ||
1701 | |||
1702 | module_init(dasd_eckd_init); | ||
1703 | module_exit(dasd_eckd_cleanup); | ||
1704 | |||
1705 | /* | ||
1706 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
1707 | * Emacs will notice this stuff at the end of the file and automatically | ||
1708 | * adjust the settings for this buffer only. This must remain at the end | ||
1709 | * of the file. | ||
1710 | * --------------------------------------------------------------------------- | ||
1711 | * Local variables: | ||
1712 | * c-indent-level: 4 | ||
1713 | * c-brace-imaginary-offset: 0 | ||
1714 | * c-brace-offset: -4 | ||
1715 | * c-argdecl-indent: 4 | ||
1716 | * c-label-offset: -4 | ||
1717 | * c-continued-statement-offset: 4 | ||
1718 | * c-continued-brace-offset: 0 | ||
1719 | * indent-tabs-mode: 1 | ||
1720 | * tab-width: 8 | ||
1721 | * End: | ||
1722 | */ | ||