1
2
3
4
5
6
7
8
9
10
11
12
13#include <linux/config.h>
14#include <linux/stddef.h>
15#include <linux/kernel.h>
16#include <asm/debug.h>
17
18#include <linux/slab.h>
19#include <linux/hdreg.h>
20#include <linux/blk.h>
21
22#include <asm/ccwcache.h>
23#include <asm/idals.h>
24#include <asm/ebcdic.h>
25#include <asm/io.h>
26#include <asm/irq.h>
27#include <asm/s390dyn.h>
28
29#include "dasd_int.h"
30#include "dasd_fba.h"
31#include "dasd_3370_erp.h"
32#include "dasd_9336_erp.h"
33
34#ifdef PRINTK_HEADER
35#undef PRINTK_HEADER
36#endif
37#define PRINTK_HEADER DASD_NAME"(fba):"
38
39#define DASD_FBA_CCW_WRITE 0x41
40#define DASD_FBA_CCW_READ 0x42
41#define DASD_FBA_CCW_LOCATE 0x43
42#define DASD_FBA_CCW_DEFINE_EXTENT 0x63
43
44#ifdef MODULE
45#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12))
46MODULE_LICENSE("GPL");
47#endif
48#endif
49
50dasd_discipline_t dasd_fba_discipline;
51
52typedef struct
53 dasd_fba_private_t {
54 dasd_fba_characteristics_t rdc_data;
55} dasd_fba_private_t;
56
57#ifdef CONFIG_DASD_DYNAMIC
58static
59devreg_t dasd_fba_known_devices[] = {
60 {
61 ci: { hc: {ctype:0x6310, dtype:0x9336}},
62 flag:(DEVREG_MATCH_CU_TYPE |
63 DEVREG_MATCH_DEV_TYPE | DEVREG_TYPE_DEVCHARS),
64 oper_func:dasd_oper_handler
65 },
66 {
67 ci: { hc: {ctype:0x3880, dtype:0x3370}},
68 flag:(DEVREG_MATCH_CU_TYPE |
69 DEVREG_MATCH_DEV_TYPE | DEVREG_TYPE_DEVCHARS),
70 oper_func:dasd_oper_handler
71 }
72};
73#endif
74static inline int
75define_extent (ccw1_t * ccw, DE_fba_data_t * DE_data, int rw,
76 int blksize, int beg, int nr, ccw_req_t* cqr,
77 dasd_device_t* device)
78{
79 int rc=0;
80 memset (DE_data, 0, sizeof (DE_fba_data_t));
81 ccw->cmd_code = DASD_FBA_CCW_DEFINE_EXTENT;
82 ccw->count = 16;
83 if ((rc=dasd_set_normalized_cda (ccw, __pa (DE_data), cqr, device)))
84 return rc;
85 if (rw == WRITE)
86 (DE_data->mask).perm = 0x0;
87 else if (rw == READ)
88 (DE_data->mask).perm = 0x1;
89 else
90 DE_data->mask.perm = 0x2;
91 DE_data->blk_size = blksize;
92 DE_data->ext_loc = beg;
93 DE_data->ext_end = nr - 1;
94 return rc;
95}
96
97static inline void
98locate_record (ccw1_t * ccw, LO_fba_data_t * LO_data, int rw, int block_nr,
99 int block_ct, ccw_req_t* cqr, dasd_device_t* device)
100{
101 memset (LO_data, 0, sizeof (LO_fba_data_t));
102 ccw->cmd_code = DASD_FBA_CCW_LOCATE;
103 ccw->count = 8;
104 dasd_set_normalized_cda (ccw, __pa (LO_data), cqr, device);
105 if (rw == WRITE)
106 LO_data->operation.cmd = 0x5;
107 else if (rw == READ)
108 LO_data->operation.cmd = 0x6;
109 else
110 LO_data->operation.cmd = 0x8;
111 LO_data->blk_nr = block_nr;
112 LO_data->blk_ct = block_ct;
113}
114
115static int
116dasd_fba_id_check (s390_dev_info_t * info)
117{
118 if (info->sid_data.cu_type == 0x3880)
119 if (info->sid_data.dev_type == 0x3370)
120 return 0;
121 if (info->sid_data.cu_type == 0x6310)
122 if (info->sid_data.dev_type == 0x9336)
123 return 0;
124 return -ENODEV;
125}
126
127static int
128dasd_fba_check_characteristics (struct dasd_device_t *device)
129{
130 int rc = -ENODEV;
131 void *rdc_data;
132 dasd_fba_private_t *private;
133
134 if (device == NULL) {
135
136 MESSAGE (KERN_WARNING, "%s",
137 "Null device pointer passed to characteristics "
138 "checker");
139
140 return -ENODEV;
141 }
142 device->private = kmalloc (sizeof (dasd_fba_private_t), GFP_KERNEL);
143
144 if (device->private == NULL) {
145
146 MESSAGE (KERN_WARNING, "%s",
147 "memory allocation failed for private data");
148
149 rc = -ENOMEM;
150 goto fail;
151 }
152 private = (dasd_fba_private_t *) device->private;
153 rdc_data = (void *) &(private->rdc_data);
154 rc = read_dev_chars (device->devinfo.irq, &rdc_data, 32);
155
156 if (rc) {
157
158 MESSAGE (KERN_WARNING,
159 "Read device characteristics returned error %d",
160 rc);
161
162 goto fail;
163 }
164
165 DEV_MESSAGE (KERN_INFO, device,
166 "%04X/%02X(CU:%04X/%02X) %dMB at(%d B/blk)",
167 device->devinfo.sid_data.dev_type,
168 device->devinfo.sid_data.dev_model,
169 device->devinfo.sid_data.cu_type,
170 device->devinfo.sid_data.cu_model,
171 ((private->rdc_data.blk_bdsa *
172 (private->rdc_data.blk_size >> 9)) >> 11),
173 private->rdc_data.blk_size);
174
175 goto out;
176 fail:
177 if ( rc ) {
178 kfree(device->private);
179 device->private = NULL;
180 }
181
182 out:
183 return rc;
184}
185
186static int
187dasd_fba_do_analysis (struct dasd_device_t *device)
188{
189 int rc = 0;
190 int sb;
191 dasd_fba_private_t *private = (dasd_fba_private_t *) device->private;
192 int bs = private->rdc_data.blk_size;
193
194 memset (&(device->sizes), 0, sizeof (dasd_sizes_t));
195 switch (bs) {
196 case 512:
197 case 1024:
198 case 2048:
199 case 4096:
200 device->sizes.bp_block = bs;
201 break;
202 default:
203
204 DEV_MESSAGE (KERN_INFO, device,
205 "unknown blocksize %d",
206 bs);
207
208 return -EMEDIUMTYPE;
209 }
210 device->sizes.s2b_shift = 0;
211 for (sb = 512; sb < bs; sb = sb << 1)
212 device->sizes.s2b_shift++;
213
214 device->sizes.blocks = (private->rdc_data.blk_bdsa);
215 device->sizes.pt_block = 1;
216
217 return rc;
218}
219
220static int
221dasd_fba_fill_geometry (struct dasd_device_t *device, struct hd_geometry *geo)
222{
223 int rc = 0;
224 unsigned long sectors = device->sizes.blocks << device->sizes.s2b_shift;
225 unsigned long tracks = sectors >> 6;
226 unsigned long cyls = tracks >> 4;
227
228 switch (device->sizes.bp_block) {
229 case 512:
230 case 1024:
231 case 2048:
232 case 4096:
233 break;
234 default:
235 return -EINVAL;
236 }
237 geo->cylinders = cyls;
238 geo->heads = 16;
239 geo->sectors = 128 >> device->sizes.s2b_shift;
240 return rc;
241}
242
243static dasd_era_t
244dasd_fba_examine_error (ccw_req_t * cqr, devstat_t * stat)
245{
246 dasd_device_t *device = (dasd_device_t *) cqr->device;
247 if (stat->cstat == 0x00 &&
248 stat->dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
249 return dasd_era_none;
250
251 switch (device->devinfo.sid_data.dev_model) {
252 case 0x3370:
253 return dasd_3370_erp_examine (cqr, stat);
254 case 0x9336:
255 return dasd_9336_erp_examine (cqr, stat);
256 default:
257 return dasd_era_recover;
258 }
259}
260
261static dasd_erp_action_fn_t
262dasd_fba_erp_action (ccw_req_t * cqr)
263{
264 return dasd_default_erp_action;
265}
266
267static dasd_erp_postaction_fn_t
268dasd_fba_erp_postaction (ccw_req_t * cqr)
269{
270 if (cqr->function == dasd_default_erp_action)
271 return dasd_default_erp_postaction;
272
273 MESSAGE (KERN_WARNING,
274 "unknown ERP action %p",
275 cqr->function);
276
277 return NULL;
278}
279
280static ccw_req_t *
281dasd_fba_build_cp_from_req (dasd_device_t * device, struct request *req)
282{
283 ccw_req_t *rw_cp = NULL;
284 int rw_cmd;
285 int bhct, i = 0;
286 long size;
287 ccw1_t *ccw;
288 DE_fba_data_t *DE_data;
289 LO_fba_data_t *LO_data;
290 struct buffer_head *bh;
291 dasd_fba_private_t *private = (dasd_fba_private_t *) device->private;
292 int byt_per_blk = device->sizes.bp_block;
293 unsigned long reloc_sector = req->sector +
294 device->major_info->gendisk.part[MINOR (req->rq_dev)].start_sect;
295
296 if (req->cmd == READ) {
297 rw_cmd = DASD_FBA_CCW_READ;
298 } else if (req->cmd == WRITE) {
299 rw_cmd = DASD_FBA_CCW_WRITE;
300 } else {
301
302 MESSAGE (KERN_ERR,
303 "Unknown command %d\n",
304 req->cmd);
305
306 return ERR_PTR(-EINVAL);
307 }
308
309
310 bh = req -> bh;
311 bhct = 0;
312 while ( bh != NULL ) {
313 if (bh->b_size < byt_per_blk) {
314 BUG();
315 }
316 bhct += bh->b_size >> (device->sizes.s2b_shift+9);
317 bh = bh->b_reqnext;
318 }
319
320 if (private->rdc_data.mode.bits.data_chain) {
321 rw_cp = dasd_alloc_request (dasd_fba_discipline.name,
322 2 + bhct,
323 sizeof (DE_fba_data_t) +
324 sizeof (LO_fba_data_t),
325 device);
326 } else {
327 rw_cp = dasd_alloc_request (dasd_fba_discipline.name,
328 1 + 2 * bhct,
329 sizeof (DE_fba_data_t) +
330 bhct * sizeof (LO_fba_data_t),
331 device);
332 }
333 if (!rw_cp) {
334 return ERR_PTR(-ENOMEM);
335 }
336 DE_data = rw_cp->data;
337 LO_data = rw_cp->data + sizeof (DE_fba_data_t);
338 ccw = rw_cp->cpaddr;
339
340 if (define_extent (ccw, DE_data, req->cmd, byt_per_blk,
341 reloc_sector, req->nr_sectors, rw_cp, device)) {
342 goto clear_rw_cp;
343 }
344 ccw->flags |= CCW_FLAG_CC;
345 ccw ++;
346 locate_record (ccw, LO_data, req->cmd, 0,
347 private->rdc_data.mode.bits.data_chain ? bhct : 1, rw_cp, device);
348 if (ccw->cda == 0) {
349 goto clear_rw_cp;
350 }
351 ccw->flags |= CCW_FLAG_CC;
352
353 bh = req -> bh;
354 i = 0;
355 while ( bh != NULL ) {
356 for (size = 0; size < bh->b_size; size += byt_per_blk) {
357 ccw ++;
358 ccw->cmd_code = rw_cmd;
359 ccw->count = byt_per_blk;
360 if (dasd_set_normalized_cda (ccw,__pa (bh->b_data + size), rw_cp, device)) {
361 goto clear_rw_cp;
362 }
363 if (private->rdc_data.mode.bits.data_chain) {
364 ccw->flags |= CCW_FLAG_DC;
365 } else {
366 ccw->flags |= CCW_FLAG_CC;
367 }
368 }
369 bh = bh->b_reqnext;
370 if ( bh != NULL &&
371 !(private->rdc_data.mode.bits.data_chain)) {
372 ccw++;
373 i++;
374 LO_data++;
375 locate_record (ccw, LO_data, req->cmd, i, 1, rw_cp, device);
376 if (ccw->cda == 0) {
377 goto clear_rw_cp;
378 }
379 ccw->flags |= CCW_FLAG_CC;
380 }
381 }
382 ccw->flags &= ~(CCW_FLAG_DC | CCW_FLAG_CC);
383
384 rw_cp->device = device;
385 rw_cp->expires = 5 * TOD_MIN;
386 rw_cp->req = req;
387 check_then_set (&rw_cp->status, CQR_STATUS_EMPTY, CQR_STATUS_FILLED);
388 goto out;
389 clear_rw_cp:
390 dasd_free_request (rw_cp, device);
391 rw_cp = NULL;
392 out:
393 return rw_cp;
394}
395
396static int
397dasd_fba_fill_info (dasd_device_t * device, dasd_information2_t * info)
398{
399 int rc = 0;
400 info->label_block = 1;
401 info->FBA_layout = 1;
402 info->format = DASD_FORMAT_LDL;
403 info->characteristics_size = sizeof (dasd_fba_characteristics_t);
404 memcpy (info->characteristics,
405 &((dasd_fba_private_t *) device->private)->rdc_data,
406 sizeof (dasd_fba_characteristics_t));
407 info->confdata_size = 0;
408 return rc;
409}
410
411
412dasd_discipline_t dasd_fba_discipline = {
413 owner: THIS_MODULE,
414 name:"FBA ",
415 ebcname:"FBA ",
416 max_blocks:((PAGE_SIZE >> 1) / sizeof (ccw1_t) - 1),
417 id_check:dasd_fba_id_check,
418 check_characteristics:dasd_fba_check_characteristics,
419 do_analysis:dasd_fba_do_analysis,
420 fill_geometry:dasd_fba_fill_geometry,
421 start_IO:dasd_start_IO,
422 term_IO:dasd_term_IO,
423 examine_error:dasd_fba_examine_error,
424 erp_action:dasd_fba_erp_action,
425 erp_postaction:dasd_fba_erp_postaction,
426 build_cp_from_req:dasd_fba_build_cp_from_req,
427 int_handler:dasd_int_handler,
428 fill_info:dasd_fba_fill_info,
429 list:LIST_HEAD_INIT(dasd_fba_discipline.list),
430};
431
432int
433dasd_fba_init (void)
434{
435 int rc = 0;
436
437 MESSAGE (KERN_INFO,
438 "%s discipline initializing",
439 dasd_fba_discipline.name);
440
441 ASCEBC (dasd_fba_discipline.ebcname, 4);
442 dasd_discipline_add (&dasd_fba_discipline);
443#ifdef CONFIG_DASD_DYNAMIC
444 {
445 int i;
446 for (i = 0;
447 i < sizeof (dasd_fba_known_devices) / sizeof (devreg_t);
448 i++) {
449
450 MESSAGE (KERN_INFO,
451 "We are interested in: "
452 "Dev %04X/%02X @ CU %04X/%02x",
453 dasd_fba_known_devices[i].ci.hc.dtype,
454 dasd_fba_known_devices[i].ci.hc.dmode,
455 dasd_fba_known_devices[i].ci.hc.ctype,
456 dasd_fba_known_devices[i].ci.hc.cmode);
457
458 s390_device_register (&dasd_fba_known_devices[i]);
459 }
460 }
461#endif
462 return rc;
463}
464
465void
466dasd_fba_cleanup( void ) {
467
468 MESSAGE (KERN_INFO,
469 "%s discipline cleaning up",
470 dasd_fba_discipline.name);
471
472#ifdef CONFIG_DASD_DYNAMIC
473 {
474 int i;
475 for ( i=0; i<sizeof(dasd_fba_known_devices)/sizeof(devreg_t); i++) {
476 s390_device_unregister(&dasd_fba_known_devices[i]);
477 }
478 }
479#endif
480 dasd_discipline_del(&dasd_fba_discipline);
481}
482
483#ifdef MODULE
484int
485init_module (void)
486{
487 int rc = 0;
488 rc = dasd_fba_init ();
489 return rc;
490}
491
492void
493cleanup_module (void)
494{
495 dasd_fba_cleanup ();
496 return;
497}
498#endif
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519