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_fba.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_fba.c')
-rw-r--r-- | drivers/s390/block/dasd_fba.c | 607 |
1 files changed, 607 insertions, 0 deletions
diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c new file mode 100644 index 000000000000..7963ae343eef --- /dev/null +++ b/drivers/s390/block/dasd_fba.c | |||
@@ -0,0 +1,607 @@ | |||
1 | /* | ||
2 | * File...........: linux/drivers/s390/block/dasd_fba.c | ||
3 | * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> | ||
4 | * Bugreports.to..: <Linux390@de.ibm.com> | ||
5 | * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 | ||
6 | * | ||
7 | * $Revision: 1.39 $ | ||
8 | */ | ||
9 | |||
10 | #include <linux/config.h> | ||
11 | #include <linux/stddef.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <asm/debug.h> | ||
14 | |||
15 | #include <linux/slab.h> | ||
16 | #include <linux/hdreg.h> /* HDIO_GETGEO */ | ||
17 | #include <linux/bio.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/init.h> | ||
20 | |||
21 | #include <asm/idals.h> | ||
22 | #include <asm/ebcdic.h> | ||
23 | #include <asm/io.h> | ||
24 | #include <asm/todclk.h> | ||
25 | #include <asm/ccwdev.h> | ||
26 | |||
27 | #include "dasd_int.h" | ||
28 | #include "dasd_fba.h" | ||
29 | |||
30 | #ifdef PRINTK_HEADER | ||
31 | #undef PRINTK_HEADER | ||
32 | #endif /* PRINTK_HEADER */ | ||
33 | #define PRINTK_HEADER "dasd(fba):" | ||
34 | |||
35 | #define DASD_FBA_CCW_WRITE 0x41 | ||
36 | #define DASD_FBA_CCW_READ 0x42 | ||
37 | #define DASD_FBA_CCW_LOCATE 0x43 | ||
38 | #define DASD_FBA_CCW_DEFINE_EXTENT 0x63 | ||
39 | |||
40 | MODULE_LICENSE("GPL"); | ||
41 | |||
42 | static struct dasd_discipline dasd_fba_discipline; | ||
43 | |||
44 | struct dasd_fba_private { | ||
45 | struct dasd_fba_characteristics rdc_data; | ||
46 | }; | ||
47 | |||
48 | static struct ccw_device_id dasd_fba_ids[] = { | ||
49 | { CCW_DEVICE_DEVTYPE (0x6310, 0, 0x9336, 0), driver_info: 0x1}, | ||
50 | { CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3370, 0), driver_info: 0x2}, | ||
51 | { /* end of list */ }, | ||
52 | }; | ||
53 | |||
54 | MODULE_DEVICE_TABLE(ccw, dasd_fba_ids); | ||
55 | |||
56 | static struct ccw_driver dasd_fba_driver; /* see below */ | ||
57 | static int | ||
58 | dasd_fba_probe(struct ccw_device *cdev) | ||
59 | { | ||
60 | int ret; | ||
61 | |||
62 | ret = dasd_generic_probe (cdev, &dasd_fba_discipline); | ||
63 | if (ret) | ||
64 | return ret; | ||
65 | ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP); | ||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | static int | ||
70 | dasd_fba_set_online(struct ccw_device *cdev) | ||
71 | { | ||
72 | return dasd_generic_set_online (cdev, &dasd_fba_discipline); | ||
73 | } | ||
74 | |||
75 | static struct ccw_driver dasd_fba_driver = { | ||
76 | .name = "dasd-fba", | ||
77 | .owner = THIS_MODULE, | ||
78 | .ids = dasd_fba_ids, | ||
79 | .probe = dasd_fba_probe, | ||
80 | .remove = dasd_generic_remove, | ||
81 | .set_offline = dasd_generic_set_offline, | ||
82 | .set_online = dasd_fba_set_online, | ||
83 | .notify = dasd_generic_notify, | ||
84 | }; | ||
85 | |||
86 | static inline void | ||
87 | define_extent(struct ccw1 * ccw, struct DE_fba_data *data, int rw, | ||
88 | int blksize, int beg, int nr) | ||
89 | { | ||
90 | ccw->cmd_code = DASD_FBA_CCW_DEFINE_EXTENT; | ||
91 | ccw->flags = 0; | ||
92 | ccw->count = 16; | ||
93 | ccw->cda = (__u32) __pa(data); | ||
94 | memset(data, 0, sizeof (struct DE_fba_data)); | ||
95 | if (rw == WRITE) | ||
96 | (data->mask).perm = 0x0; | ||
97 | else if (rw == READ) | ||
98 | (data->mask).perm = 0x1; | ||
99 | else | ||
100 | data->mask.perm = 0x2; | ||
101 | data->blk_size = blksize; | ||
102 | data->ext_loc = beg; | ||
103 | data->ext_end = nr - 1; | ||
104 | } | ||
105 | |||
106 | static inline void | ||
107 | locate_record(struct ccw1 * ccw, struct LO_fba_data *data, int rw, | ||
108 | int block_nr, int block_ct) | ||
109 | { | ||
110 | ccw->cmd_code = DASD_FBA_CCW_LOCATE; | ||
111 | ccw->flags = 0; | ||
112 | ccw->count = 8; | ||
113 | ccw->cda = (__u32) __pa(data); | ||
114 | memset(data, 0, sizeof (struct LO_fba_data)); | ||
115 | if (rw == WRITE) | ||
116 | data->operation.cmd = 0x5; | ||
117 | else if (rw == READ) | ||
118 | data->operation.cmd = 0x6; | ||
119 | else | ||
120 | data->operation.cmd = 0x8; | ||
121 | data->blk_nr = block_nr; | ||
122 | data->blk_ct = block_ct; | ||
123 | } | ||
124 | |||
125 | static int | ||
126 | dasd_fba_check_characteristics(struct dasd_device *device) | ||
127 | { | ||
128 | struct dasd_fba_private *private; | ||
129 | struct ccw_device *cdev = device->cdev; | ||
130 | void *rdc_data; | ||
131 | int rc; | ||
132 | |||
133 | private = (struct dasd_fba_private *) device->private; | ||
134 | if (private == NULL) { | ||
135 | private = kmalloc(sizeof(struct dasd_fba_private), GFP_KERNEL); | ||
136 | if (private == NULL) { | ||
137 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
138 | "memory allocation failed for private " | ||
139 | "data"); | ||
140 | return -ENOMEM; | ||
141 | } | ||
142 | device->private = (void *) private; | ||
143 | } | ||
144 | /* Read Device Characteristics */ | ||
145 | rdc_data = (void *) &(private->rdc_data); | ||
146 | rc = read_dev_chars(device->cdev, &rdc_data, 32); | ||
147 | if (rc) { | ||
148 | DEV_MESSAGE(KERN_WARNING, device, | ||
149 | "Read device characteristics returned error %d", | ||
150 | rc); | ||
151 | return rc; | ||
152 | } | ||
153 | |||
154 | DEV_MESSAGE(KERN_INFO, device, | ||
155 | "%04X/%02X(CU:%04X/%02X) %dMB at(%d B/blk)", | ||
156 | cdev->id.dev_type, | ||
157 | cdev->id.dev_model, | ||
158 | cdev->id.cu_type, | ||
159 | cdev->id.cu_model, | ||
160 | ((private->rdc_data.blk_bdsa * | ||
161 | (private->rdc_data.blk_size >> 9)) >> 11), | ||
162 | private->rdc_data.blk_size); | ||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | static int | ||
167 | dasd_fba_do_analysis(struct dasd_device *device) | ||
168 | { | ||
169 | struct dasd_fba_private *private; | ||
170 | int sb, rc; | ||
171 | |||
172 | private = (struct dasd_fba_private *) device->private; | ||
173 | rc = dasd_check_blocksize(private->rdc_data.blk_size); | ||
174 | if (rc) { | ||
175 | DEV_MESSAGE(KERN_INFO, device, "unknown blocksize %d", | ||
176 | private->rdc_data.blk_size); | ||
177 | return rc; | ||
178 | } | ||
179 | device->blocks = private->rdc_data.blk_bdsa; | ||
180 | device->bp_block = private->rdc_data.blk_size; | ||
181 | device->s2b_shift = 0; /* bits to shift 512 to get a block */ | ||
182 | for (sb = 512; sb < private->rdc_data.blk_size; sb = sb << 1) | ||
183 | device->s2b_shift++; | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static int | ||
188 | dasd_fba_fill_geometry(struct dasd_device *device, struct hd_geometry *geo) | ||
189 | { | ||
190 | if (dasd_check_blocksize(device->bp_block) != 0) | ||
191 | return -EINVAL; | ||
192 | geo->cylinders = (device->blocks << device->s2b_shift) >> 10; | ||
193 | geo->heads = 16; | ||
194 | geo->sectors = 128 >> device->s2b_shift; | ||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | static dasd_era_t | ||
199 | dasd_fba_examine_error(struct dasd_ccw_req * cqr, struct irb * irb) | ||
200 | { | ||
201 | struct dasd_device *device; | ||
202 | struct ccw_device *cdev; | ||
203 | |||
204 | device = (struct dasd_device *) cqr->device; | ||
205 | if (irb->scsw.cstat == 0x00 && | ||
206 | irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) | ||
207 | return dasd_era_none; | ||
208 | |||
209 | cdev = device->cdev; | ||
210 | switch (cdev->id.dev_type) { | ||
211 | case 0x3370: | ||
212 | return dasd_3370_erp_examine(cqr, irb); | ||
213 | case 0x9336: | ||
214 | return dasd_9336_erp_examine(cqr, irb); | ||
215 | default: | ||
216 | return dasd_era_recover; | ||
217 | } | ||
218 | } | ||
219 | |||
220 | static dasd_erp_fn_t | ||
221 | dasd_fba_erp_action(struct dasd_ccw_req * cqr) | ||
222 | { | ||
223 | return dasd_default_erp_action; | ||
224 | } | ||
225 | |||
226 | static dasd_erp_fn_t | ||
227 | dasd_fba_erp_postaction(struct dasd_ccw_req * cqr) | ||
228 | { | ||
229 | if (cqr->function == dasd_default_erp_action) | ||
230 | return dasd_default_erp_postaction; | ||
231 | |||
232 | DEV_MESSAGE(KERN_WARNING, cqr->device, "unknown ERP action %p", | ||
233 | cqr->function); | ||
234 | return NULL; | ||
235 | } | ||
236 | |||
237 | static struct dasd_ccw_req * | ||
238 | dasd_fba_build_cp(struct dasd_device * device, struct request *req) | ||
239 | { | ||
240 | struct dasd_fba_private *private; | ||
241 | unsigned long *idaws; | ||
242 | struct LO_fba_data *LO_data; | ||
243 | struct dasd_ccw_req *cqr; | ||
244 | struct ccw1 *ccw; | ||
245 | struct bio *bio; | ||
246 | struct bio_vec *bv; | ||
247 | char *dst; | ||
248 | int count, cidaw, cplength, datasize; | ||
249 | sector_t recid, first_rec, last_rec; | ||
250 | unsigned int blksize, off; | ||
251 | unsigned char cmd; | ||
252 | int i; | ||
253 | |||
254 | private = (struct dasd_fba_private *) device->private; | ||
255 | if (rq_data_dir(req) == READ) { | ||
256 | cmd = DASD_FBA_CCW_READ; | ||
257 | } else if (rq_data_dir(req) == WRITE) { | ||
258 | cmd = DASD_FBA_CCW_WRITE; | ||
259 | } else | ||
260 | return ERR_PTR(-EINVAL); | ||
261 | blksize = device->bp_block; | ||
262 | /* Calculate record id of first and last block. */ | ||
263 | first_rec = req->sector >> device->s2b_shift; | ||
264 | last_rec = (req->sector + req->nr_sectors - 1) >> device->s2b_shift; | ||
265 | /* Check struct bio and count the number of blocks for the request. */ | ||
266 | count = 0; | ||
267 | cidaw = 0; | ||
268 | rq_for_each_bio(bio, req) { | ||
269 | bio_for_each_segment(bv, bio, i) { | ||
270 | if (bv->bv_len & (blksize - 1)) | ||
271 | /* Fba can only do full blocks. */ | ||
272 | return ERR_PTR(-EINVAL); | ||
273 | count += bv->bv_len >> (device->s2b_shift + 9); | ||
274 | #if defined(CONFIG_ARCH_S390X) | ||
275 | if (idal_is_needed (page_address(bv->bv_page), | ||
276 | bv->bv_len)) | ||
277 | cidaw += bv->bv_len / blksize; | ||
278 | #endif | ||
279 | } | ||
280 | } | ||
281 | /* Paranoia. */ | ||
282 | if (count != last_rec - first_rec + 1) | ||
283 | return ERR_PTR(-EINVAL); | ||
284 | /* 1x define extent + 1x locate record + number of blocks */ | ||
285 | cplength = 2 + count; | ||
286 | /* 1x define extent + 1x locate record */ | ||
287 | datasize = sizeof(struct DE_fba_data) + sizeof(struct LO_fba_data) + | ||
288 | cidaw * sizeof(unsigned long); | ||
289 | /* | ||
290 | * Find out number of additional locate record ccws if the device | ||
291 | * can't do data chaining. | ||
292 | */ | ||
293 | if (private->rdc_data.mode.bits.data_chain == 0) { | ||
294 | cplength += count - 1; | ||
295 | datasize += (count - 1)*sizeof(struct LO_fba_data); | ||
296 | } | ||
297 | /* Allocate the ccw request. */ | ||
298 | cqr = dasd_smalloc_request(dasd_fba_discipline.name, | ||
299 | cplength, datasize, device); | ||
300 | if (IS_ERR(cqr)) | ||
301 | return cqr; | ||
302 | ccw = cqr->cpaddr; | ||
303 | /* First ccw is define extent. */ | ||
304 | define_extent(ccw++, cqr->data, rq_data_dir(req), | ||
305 | device->bp_block, req->sector, req->nr_sectors); | ||
306 | /* Build locate_record + read/write ccws. */ | ||
307 | idaws = (unsigned long *) (cqr->data + sizeof(struct DE_fba_data)); | ||
308 | LO_data = (struct LO_fba_data *) (idaws + cidaw); | ||
309 | /* Locate record for all blocks for smart devices. */ | ||
310 | if (private->rdc_data.mode.bits.data_chain != 0) { | ||
311 | ccw[-1].flags |= CCW_FLAG_CC; | ||
312 | locate_record(ccw++, LO_data++, rq_data_dir(req), 0, count); | ||
313 | } | ||
314 | recid = first_rec; | ||
315 | rq_for_each_bio(bio, req) bio_for_each_segment(bv, bio, i) { | ||
316 | dst = page_address(bv->bv_page) + bv->bv_offset; | ||
317 | if (dasd_page_cache) { | ||
318 | char *copy = kmem_cache_alloc(dasd_page_cache, | ||
319 | SLAB_DMA | __GFP_NOWARN); | ||
320 | if (copy && rq_data_dir(req) == WRITE) | ||
321 | memcpy(copy + bv->bv_offset, dst, bv->bv_len); | ||
322 | if (copy) | ||
323 | dst = copy + bv->bv_offset; | ||
324 | } | ||
325 | for (off = 0; off < bv->bv_len; off += blksize) { | ||
326 | /* Locate record for stupid devices. */ | ||
327 | if (private->rdc_data.mode.bits.data_chain == 0) { | ||
328 | ccw[-1].flags |= CCW_FLAG_CC; | ||
329 | locate_record(ccw, LO_data++, | ||
330 | rq_data_dir(req), | ||
331 | recid - first_rec, 1); | ||
332 | ccw->flags = CCW_FLAG_CC; | ||
333 | ccw++; | ||
334 | } else { | ||
335 | if (recid > first_rec) | ||
336 | ccw[-1].flags |= CCW_FLAG_DC; | ||
337 | else | ||
338 | ccw[-1].flags |= CCW_FLAG_CC; | ||
339 | } | ||
340 | ccw->cmd_code = cmd; | ||
341 | ccw->count = device->bp_block; | ||
342 | if (idal_is_needed(dst, blksize)) { | ||
343 | ccw->cda = (__u32)(addr_t) idaws; | ||
344 | ccw->flags = CCW_FLAG_IDA; | ||
345 | idaws = idal_create_words(idaws, dst, blksize); | ||
346 | } else { | ||
347 | ccw->cda = (__u32)(addr_t) dst; | ||
348 | ccw->flags = 0; | ||
349 | } | ||
350 | ccw++; | ||
351 | dst += blksize; | ||
352 | recid++; | ||
353 | } | ||
354 | } | ||
355 | cqr->device = device; | ||
356 | cqr->expires = 5 * 60 * HZ; /* 5 minutes */ | ||
357 | cqr->status = DASD_CQR_FILLED; | ||
358 | return cqr; | ||
359 | } | ||
360 | |||
361 | static int | ||
362 | dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req) | ||
363 | { | ||
364 | struct dasd_fba_private *private; | ||
365 | struct ccw1 *ccw; | ||
366 | struct bio *bio; | ||
367 | struct bio_vec *bv; | ||
368 | char *dst, *cda; | ||
369 | unsigned int blksize, off; | ||
370 | int i, status; | ||
371 | |||
372 | if (!dasd_page_cache) | ||
373 | goto out; | ||
374 | private = (struct dasd_fba_private *) cqr->device->private; | ||
375 | blksize = cqr->device->bp_block; | ||
376 | ccw = cqr->cpaddr; | ||
377 | /* Skip over define extent & locate record. */ | ||
378 | ccw++; | ||
379 | if (private->rdc_data.mode.bits.data_chain != 0) | ||
380 | ccw++; | ||
381 | rq_for_each_bio(bio, req) bio_for_each_segment(bv, bio, i) { | ||
382 | dst = page_address(bv->bv_page) + bv->bv_offset; | ||
383 | for (off = 0; off < bv->bv_len; off += blksize) { | ||
384 | /* Skip locate record. */ | ||
385 | if (private->rdc_data.mode.bits.data_chain == 0) | ||
386 | ccw++; | ||
387 | if (dst) { | ||
388 | if (ccw->flags & CCW_FLAG_IDA) | ||
389 | cda = *((char **)((addr_t) ccw->cda)); | ||
390 | else | ||
391 | cda = (char *)((addr_t) ccw->cda); | ||
392 | if (dst != cda) { | ||
393 | if (rq_data_dir(req) == READ) | ||
394 | memcpy(dst, cda, bv->bv_len); | ||
395 | kmem_cache_free(dasd_page_cache, | ||
396 | (void *)((addr_t)cda & PAGE_MASK)); | ||
397 | } | ||
398 | dst = NULL; | ||
399 | } | ||
400 | ccw++; | ||
401 | } | ||
402 | } | ||
403 | out: | ||
404 | status = cqr->status == DASD_CQR_DONE; | ||
405 | dasd_sfree_request(cqr, cqr->device); | ||
406 | return status; | ||
407 | } | ||
408 | |||
409 | static int | ||
410 | dasd_fba_fill_info(struct dasd_device * device, | ||
411 | struct dasd_information2_t * info) | ||
412 | { | ||
413 | info->label_block = 1; | ||
414 | info->FBA_layout = 1; | ||
415 | info->format = DASD_FORMAT_LDL; | ||
416 | info->characteristics_size = sizeof(struct dasd_fba_characteristics); | ||
417 | memcpy(info->characteristics, | ||
418 | &((struct dasd_fba_private *) device->private)->rdc_data, | ||
419 | sizeof (struct dasd_fba_characteristics)); | ||
420 | info->confdata_size = 0; | ||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | static void | ||
425 | dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, | ||
426 | struct irb *irb) | ||
427 | { | ||
428 | char *page; | ||
429 | struct ccw1 *act, *end, *last; | ||
430 | int len, sl, sct, count; | ||
431 | |||
432 | page = (char *) get_zeroed_page(GFP_ATOMIC); | ||
433 | if (page == NULL) { | ||
434 | DEV_MESSAGE(KERN_ERR, device, " %s", | ||
435 | "No memory to dump sense data"); | ||
436 | return; | ||
437 | } | ||
438 | len = sprintf(page, KERN_ERR PRINTK_HEADER | ||
439 | " I/O status report for device %s:\n", | ||
440 | device->cdev->dev.bus_id); | ||
441 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | ||
442 | " in req: %p CS: 0x%02X DS: 0x%02X\n", req, | ||
443 | irb->scsw.cstat, irb->scsw.dstat); | ||
444 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | ||
445 | " device %s: Failing CCW: %p\n", | ||
446 | device->cdev->dev.bus_id, | ||
447 | (void *) (addr_t) irb->scsw.cpa); | ||
448 | if (irb->esw.esw0.erw.cons) { | ||
449 | for (sl = 0; sl < 4; sl++) { | ||
450 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | ||
451 | " Sense(hex) %2d-%2d:", | ||
452 | (8 * sl), ((8 * sl) + 7)); | ||
453 | |||
454 | for (sct = 0; sct < 8; sct++) { | ||
455 | len += sprintf(page + len, " %02x", | ||
456 | irb->ecw[8 * sl + sct]); | ||
457 | } | ||
458 | len += sprintf(page + len, "\n"); | ||
459 | } | ||
460 | } else { | ||
461 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | ||
462 | " SORRY - NO VALID SENSE AVAILABLE\n"); | ||
463 | } | ||
464 | MESSAGE_LOG(KERN_ERR, "%s", | ||
465 | page + sizeof(KERN_ERR PRINTK_HEADER)); | ||
466 | |||
467 | /* dump the Channel Program */ | ||
468 | /* print first CCWs (maximum 8) */ | ||
469 | act = req->cpaddr; | ||
470 | for (last = act; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++); | ||
471 | end = min(act + 8, last); | ||
472 | len = sprintf(page, KERN_ERR PRINTK_HEADER | ||
473 | " Related CP in req: %p\n", req); | ||
474 | while (act <= end) { | ||
475 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | ||
476 | " CCW %p: %08X %08X DAT:", | ||
477 | act, ((int *) act)[0], ((int *) act)[1]); | ||
478 | for (count = 0; count < 32 && count < act->count; | ||
479 | count += sizeof(int)) | ||
480 | len += sprintf(page + len, " %08X", | ||
481 | ((int *) (addr_t) act->cda) | ||
482 | [(count>>2)]); | ||
483 | len += sprintf(page + len, "\n"); | ||
484 | act++; | ||
485 | } | ||
486 | MESSAGE_LOG(KERN_ERR, "%s", | ||
487 | page + sizeof(KERN_ERR PRINTK_HEADER)); | ||
488 | |||
489 | |||
490 | /* print failing CCW area */ | ||
491 | len = 0; | ||
492 | if (act < ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2) { | ||
493 | act = ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2; | ||
494 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); | ||
495 | } | ||
496 | end = min((struct ccw1 *)(addr_t) irb->scsw.cpa + 2, last); | ||
497 | while (act <= end) { | ||
498 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | ||
499 | " CCW %p: %08X %08X DAT:", | ||
500 | act, ((int *) act)[0], ((int *) act)[1]); | ||
501 | for (count = 0; count < 32 && count < act->count; | ||
502 | count += sizeof(int)) | ||
503 | len += sprintf(page + len, " %08X", | ||
504 | ((int *) (addr_t) act->cda) | ||
505 | [(count>>2)]); | ||
506 | len += sprintf(page + len, "\n"); | ||
507 | act++; | ||
508 | } | ||
509 | |||
510 | /* print last CCWs */ | ||
511 | if (act < last - 2) { | ||
512 | act = last - 2; | ||
513 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); | ||
514 | } | ||
515 | while (act <= last) { | ||
516 | len += sprintf(page + len, KERN_ERR PRINTK_HEADER | ||
517 | " CCW %p: %08X %08X DAT:", | ||
518 | act, ((int *) act)[0], ((int *) act)[1]); | ||
519 | for (count = 0; count < 32 && count < act->count; | ||
520 | count += sizeof(int)) | ||
521 | len += sprintf(page + len, " %08X", | ||
522 | ((int *) (addr_t) act->cda) | ||
523 | [(count>>2)]); | ||
524 | len += sprintf(page + len, "\n"); | ||
525 | act++; | ||
526 | } | ||
527 | if (len > 0) | ||
528 | MESSAGE_LOG(KERN_ERR, "%s", | ||
529 | page + sizeof(KERN_ERR PRINTK_HEADER)); | ||
530 | free_page((unsigned long) page); | ||
531 | } | ||
532 | |||
533 | /* | ||
534 | * max_blocks is dependent on the amount of storage that is available | ||
535 | * in the static io buffer for each device. Currently each device has | ||
536 | * 8192 bytes (=2 pages). For 64 bit one dasd_mchunkt_t structure has | ||
537 | * 24 bytes, the struct dasd_ccw_req has 136 bytes and each block can use | ||
538 | * up to 16 bytes (8 for the ccw and 8 for the idal pointer). In | ||
539 | * addition we have one define extent ccw + 16 bytes of data and a | ||
540 | * locate record ccw for each block (stupid devices!) + 16 bytes of data. | ||
541 | * That makes: | ||
542 | * (8192 - 24 - 136 - 8 - 16) / 40 = 200.2 blocks at maximum. | ||
543 | * We want to fit two into the available memory so that we can immediately | ||
544 | * start the next request if one finishes off. That makes 100.1 blocks | ||
545 | * for one request. Give a little safety and the result is 96. | ||
546 | */ | ||
547 | static struct dasd_discipline dasd_fba_discipline = { | ||
548 | .owner = THIS_MODULE, | ||
549 | .name = "FBA ", | ||
550 | .ebcname = "FBA ", | ||
551 | .max_blocks = 96, | ||
552 | .check_device = dasd_fba_check_characteristics, | ||
553 | .do_analysis = dasd_fba_do_analysis, | ||
554 | .fill_geometry = dasd_fba_fill_geometry, | ||
555 | .start_IO = dasd_start_IO, | ||
556 | .term_IO = dasd_term_IO, | ||
557 | .examine_error = dasd_fba_examine_error, | ||
558 | .erp_action = dasd_fba_erp_action, | ||
559 | .erp_postaction = dasd_fba_erp_postaction, | ||
560 | .build_cp = dasd_fba_build_cp, | ||
561 | .free_cp = dasd_fba_free_cp, | ||
562 | .dump_sense = dasd_fba_dump_sense, | ||
563 | .fill_info = dasd_fba_fill_info, | ||
564 | }; | ||
565 | |||
566 | static int __init | ||
567 | dasd_fba_init(void) | ||
568 | { | ||
569 | int ret; | ||
570 | |||
571 | ASCEBC(dasd_fba_discipline.ebcname, 4); | ||
572 | |||
573 | ret = ccw_driver_register(&dasd_fba_driver); | ||
574 | if (ret) | ||
575 | return ret; | ||
576 | |||
577 | dasd_generic_auto_online(&dasd_fba_driver); | ||
578 | return 0; | ||
579 | } | ||
580 | |||
581 | static void __exit | ||
582 | dasd_fba_cleanup(void) | ||
583 | { | ||
584 | ccw_driver_unregister(&dasd_fba_driver); | ||
585 | } | ||
586 | |||
587 | module_init(dasd_fba_init); | ||
588 | module_exit(dasd_fba_cleanup); | ||
589 | |||
590 | /* | ||
591 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
592 | * Emacs will notice this stuff at the end of the file and automatically | ||
593 | * adjust the settings for this buffer only. This must remain at the end | ||
594 | * of the file. | ||
595 | * --------------------------------------------------------------------------- | ||
596 | * Local variables: | ||
597 | * c-indent-level: 4 | ||
598 | * c-brace-imaginary-offset: 0 | ||
599 | * c-brace-offset: -4 | ||
600 | * c-argdecl-indent: 4 | ||
601 | * c-label-offset: -4 | ||
602 | * c-continued-statement-offset: 4 | ||
603 | * c-continued-brace-offset: 0 | ||
604 | * indent-tabs-mode: 1 | ||
605 | * tab-width: 8 | ||
606 | * End: | ||
607 | */ | ||