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  * Bugreports.to..: <Linux390@de.ibm.com>
7  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
8  *
9  * $Revision $
10  *
11  * History of changes (starts July 2000)
12  * 07/11/00 Enabled rotational position sensing
13  * 07/14/00 Reorganized the format process for better ERP
14  * 07/20/00 added experimental support for 2105 control unit (ESS)
15  * 07/24/00 increased expiration time and added the missing zero
16  * 08/07/00 added some bits to define_extent for ESS support
17  * 09/20/00 added reserve and release ioctls
18  * 10/04/00 changed RW-CCWS to R/W Key and Data
19  * 10/10/00 reverted last change according to ESS exploitation
20  * 10/10/00 now dequeuing init_cqr before freeing *ouch*
21  * 26/10/00 fixed ITPM20144ASC (problems when accesing a device formatted by VIF)
22  * 01/23/01 fixed kmalloc statement in dump_sense to be GFP_ATOMIC
23  *          fixed partition handling and HDIO_GETGEO
24  */
25 
26 #include <linux/config.h>
27 #include <linux/stddef.h>
28 #include <linux/kernel.h>
29 #include <linux/slab.h>
30 #include <linux/hdreg.h>	/* HDIO_GETGEO                      */
31 #include <linux/blk.h>
32 
33 #include <asm/debug.h>
34 #include <asm/ccwcache.h>
35 #include <asm/idals.h>
36 #include <asm/ebcdic.h>
37 #include <asm/io.h>
38 #include <asm/irq.h>
39 #include <asm/s390dyn.h>
40 
41 #include "dasd_int.h"
42 #include "dasd_eckd.h"
43 
44 #ifdef PRINTK_HEADER
45 #undef PRINTK_HEADER
46 #endif				/* PRINTK_HEADER */
47 #define PRINTK_HEADER DASD_NAME"(eckd):"
48 
49 #define ECKD_C0(i) (i->home_bytes)
50 #define ECKD_F(i) (i->formula)
51 #define ECKD_F1(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f1):(i->factors.f_0x02.f1))
52 #define ECKD_F2(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f2):(i->factors.f_0x02.f2))
53 #define ECKD_F3(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f3):(i->factors.f_0x02.f3))
54 #define ECKD_F4(i) (ECKD_F(i)==0x02?(i->factors.f_0x02.f4):0)
55 #define ECKD_F5(i) (ECKD_F(i)==0x02?(i->factors.f_0x02.f5):0)
56 #define ECKD_F6(i) (i->factor6)
57 #define ECKD_F7(i) (i->factor7)
58 #define ECKD_F8(i) (i->factor8)
59 
60 #ifdef MODULE
61 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12))
62 MODULE_LICENSE("GPL");
63 #endif
64 #endif
65 
66 dasd_discipline_t dasd_eckd_discipline;
67 
68 typedef struct dasd_eckd_private_t {
69 	dasd_eckd_characteristics_t rdc_data;
70 	dasd_eckd_confdata_t        conf_data;
71 	eckd_count_t                count_area[5];
72 	int                         uses_cdl;
73         attrib_data_t               attrib;     /* e.g. cache operations */
74 } dasd_eckd_private_t;
75 
76 #ifdef CONFIG_DASD_DYNAMIC
77 static
78 devreg_t dasd_eckd_known_devices[] = {
79         {
80                 ci: { hc: {ctype:0x3880, dtype: 0x3390}},
81                 flag:(DEVREG_MATCH_CU_TYPE | DEVREG_MATCH_DEV_TYPE |
82                       DEVREG_TYPE_DEVCHARS),
83                 oper_func:dasd_oper_handler
84         },
85         {
86                 ci: { hc: {ctype:0x3990}},
87                 flag:(DEVREG_MATCH_CU_TYPE | DEVREG_TYPE_DEVCHARS),
88                 oper_func:dasd_oper_handler
89         },
90 	{
91                 ci: { hc: {ctype:0x2105}},
92                 flag:(DEVREG_MATCH_CU_TYPE | DEVREG_TYPE_DEVCHARS),
93                 oper_func:dasd_oper_handler
94         },
95 	{
96                 ci: { hc: {ctype:0x9343}},
97                 flag:(DEVREG_MATCH_CU_TYPE | DEVREG_TYPE_DEVCHARS),
98                 oper_func:dasd_oper_handler
99         }
100 };
101 #endif
102 
103 int sizes_trk0[] = { 28, 148, 84 };
104 #define LABEL_SIZE 140
105 
106 static inline unsigned int
round_up_multiple(unsigned int no,unsigned int mult)107 round_up_multiple (unsigned int no, unsigned int mult)
108 {
109 	int rem = no % mult;
110 	return (rem ? no - rem + mult : no);
111 }
112 
113 static inline unsigned int
ceil_quot(unsigned int d1,unsigned int d2)114 ceil_quot (unsigned int d1, unsigned int d2)
115 {
116 	return (d1 + (d2 - 1)) / d2;
117 }
118 
119 static inline int
bytes_per_record(dasd_eckd_characteristics_t * rdc,int kl,int dl)120 bytes_per_record (dasd_eckd_characteristics_t * rdc, int kl,	/* key length */
121 		  int dl /* data length */ )
122 {
123 	int bpr = 0;
124 	switch (rdc->formula) {
125 	case 0x01:{
126 			unsigned int fl1, fl2;
127 			fl1 = round_up_multiple (ECKD_F2 (rdc) + dl,
128 						 ECKD_F1 (rdc));
129 			fl2 = round_up_multiple (kl ? ECKD_F2 (rdc) + kl : 0,
130 						 ECKD_F1 (rdc));
131 			bpr = fl1 + fl2;
132 			break;
133 		}
134 	case 0x02:{
135 			unsigned int fl1, fl2, int1, int2;
136 			int1 = ceil_quot (dl + ECKD_F6 (rdc),
137 					  ECKD_F5 (rdc) << 1);
138 			int2 = ceil_quot (kl + ECKD_F6 (rdc),
139 					  ECKD_F5 (rdc) << 1);
140 			fl1 = round_up_multiple (ECKD_F1 (rdc) *
141 						 ECKD_F2 (rdc) +
142 						 (dl + ECKD_F6 (rdc) +
143 						  ECKD_F4 (rdc) * int1),
144 						 ECKD_F1 (rdc));
145 			fl2 = round_up_multiple (ECKD_F1 (rdc) *
146 						 ECKD_F3 (rdc) +
147 						 (kl + ECKD_F6 (rdc) +
148 						  ECKD_F4 (rdc) * int2),
149 						 ECKD_F1 (rdc));
150 			bpr = fl1 + fl2;
151 			break;
152 		}
153 	default:
154 
155 		MESSAGE (KERN_ERR,
156                          "unknown formula%d",
157                          rdc->formula);
158 	}
159 	return bpr;
160 }
161 
162 static inline unsigned int
bytes_per_track(dasd_eckd_characteristics_t * rdc)163 bytes_per_track (dasd_eckd_characteristics_t * rdc)
164 {
165 	return *(unsigned int *) (rdc->byte_per_track) >> 8;
166 }
167 
168 static inline unsigned int
recs_per_track(dasd_eckd_characteristics_t * rdc,unsigned int kl,unsigned int dl)169 recs_per_track (dasd_eckd_characteristics_t * rdc,
170 		unsigned int kl, unsigned int dl)
171 {
172 	int rpt = 0;
173 	int dn;
174 	switch (rdc->dev_type) {
175 	case 0x3380:
176 		if (kl)
177 			return 1499 / (15 +
178 				       7 + ceil_quot (kl + 12, 32) +
179 				       ceil_quot (dl + 12, 32));
180 		else
181 			return 1499 / (15 + ceil_quot (dl + 12, 32));
182 	case 0x3390:
183 		dn = ceil_quot (dl + 6, 232) + 1;
184 		if (kl) {
185 			int kn = ceil_quot (kl + 6, 232) + 1;
186 			return 1729 / (10 +
187 				       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 			int kn = ceil_quot (kl + 6, 232) + 1;
195 			return 1420 / (18 +
196 				       7 + ceil_quot (kl + 6 * kn, 34) +
197 				       ceil_quot (dl + 6 * dn, 34));
198 		} else
199 			return 1420 / (18 + 7 + ceil_quot (dl + 6 * dn, 34));
200 	}
201 	return rpt;
202 }
203 
204 static inline void
check_XRC(ccw1_t * de_ccw,DE_eckd_data_t * data,dasd_device_t * device)205 check_XRC (ccw1_t         *de_ccw,
206            DE_eckd_data_t *data,
207            dasd_device_t  *device)
208 {
209 
210 	dasd_eckd_private_t *private = (dasd_eckd_private_t *) 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 (DE_eckd_data_t);
221                 de_ccw->flags |= CCW_FLAG_SLI;
222         }
223 
224         return;
225 
226 } /* end check_XRC */
227 
228 static inline int
define_extent(ccw1_t * de_ccw,DE_eckd_data_t * data,int trk,int totrk,int cmd,dasd_device_t * device,ccw_req_t * cqr)229 define_extent (ccw1_t * de_ccw,
230 	       DE_eckd_data_t * data,
231 	       int trk, int totrk,
232                int cmd, dasd_device_t * device, ccw_req_t* cqr)
233 {
234         int rc=0;
235 	ch_t geo, beg, end;
236 	dasd_eckd_private_t *private = (dasd_eckd_private_t *) device->private;
237 
238 	geo.cyl = private->rdc_data.no_cyl;
239 	geo.head = private->rdc_data.trk_per_cyl;
240 	beg.cyl = trk / geo.head;
241 	beg.head = trk % geo.head;
242 	end.cyl = totrk / geo.head;
243 	end.head = totrk % geo.head;
244 
245 	memset (de_ccw, 0, sizeof (ccw1_t));
246 
247 	de_ccw->cmd_code = DASD_ECKD_CCW_DEFINE_EXTENT;
248 	de_ccw->count = 16;
249 
250 	if ((rc=dasd_set_normalized_cda (de_ccw, __pa (data), cqr, device)))
251                 return rc;
252 
253 	memset (data, 0, sizeof (DE_eckd_data_t));
254 	switch (cmd) {
255 	case DASD_ECKD_CCW_READ_HOME_ADDRESS:
256 	case DASD_ECKD_CCW_READ_RECORD_ZERO:
257 	case DASD_ECKD_CCW_READ:
258 	case DASD_ECKD_CCW_READ_MT:
259 	case DASD_ECKD_CCW_READ_CKD:	/* Fallthrough */
260 	case DASD_ECKD_CCW_READ_CKD_MT:
261 	case DASD_ECKD_CCW_READ_KD:
262 	case DASD_ECKD_CCW_READ_KD_MT:
263 	case DASD_ECKD_CCW_READ_COUNT:
264 		data->mask.perm = 0x1;
265 		data->attributes.operation = private->attrib.operation;
266 		break;
267 	case DASD_ECKD_CCW_WRITE:
268 	case DASD_ECKD_CCW_WRITE_MT:
269 	case DASD_ECKD_CCW_WRITE_KD:
270 	case DASD_ECKD_CCW_WRITE_KD_MT:
271 		data->mask.perm = 0x02;
272 		data->attributes.operation = private->attrib.operation;
273 
274                 check_XRC (de_ccw,
275                            data,
276                            device);
277 		break;
278 	case DASD_ECKD_CCW_WRITE_CKD:
279 	case DASD_ECKD_CCW_WRITE_CKD_MT:
280 		data->attributes.operation = DASD_BYPASS_CACHE;
281 
282                 check_XRC (de_ccw,
283                            data,
284                            device);
285 		break;
286 	case DASD_ECKD_CCW_ERASE:
287 	case DASD_ECKD_CCW_WRITE_HOME_ADDRESS:
288 	case DASD_ECKD_CCW_WRITE_RECORD_ZERO:
289 		data->mask.perm = 0x3;
290 		data->mask.auth = 0x1;
291 		data->attributes.operation = DASD_BYPASS_CACHE;
292 
293                 check_XRC (de_ccw,
294                            data,
295                            device);
296 		break;
297 	default:
298 
299 		MESSAGE (KERN_ERR,
300                          "unknown opcode 0x%x",
301                          cmd);
302 
303 		break;
304 	}
305 
306 	data->attributes.mode = 0x3; /* ECKD */
307 
308 	if (private->rdc_data.cu_type == 0x2105
309 	    && !(private->uses_cdl && trk < 2) ) {
310 
311 		data->ga_extended |= 0x40;
312 	}
313 
314         /* check for sequential prestage - enhance cylinder range */
315         if (data->attributes.operation == DASD_SEQ_PRESTAGE ||
316             data->attributes.operation == DASD_SEQ_ACCESS     ) {
317 
318                 if (end.cyl + private->attrib.nr_cyl < geo.cyl) {
319 
320                         end.cyl +=  private->attrib.nr_cyl;
321 
322                         DBF_DEV_EVENT (DBF_NOTICE, device,
323                                        "Enhanced DE Cylinder from  %x to %x",
324                                        (totrk / geo.head),
325                                        end.cyl);
326 
327 
328                 } else {
329                         end.cyl = (geo.cyl -1);
330 
331                         DBF_DEV_EVENT (DBF_NOTICE, device,
332                                        "Enhanced DE Cylinder from  %x to "
333                                        "End of device %x",
334                                        (totrk / geo.head),
335                                        end.cyl);
336 
337                 }
338         }
339 
340 	data->beg_ext.cyl  = beg.cyl;
341 	data->beg_ext.head = beg.head;
342 	data->end_ext.cyl  = end.cyl;
343 	data->end_ext.head = end.head;
344 
345         return rc;
346 }
347 
348 static inline int
locate_record(ccw1_t * lo_ccw,LO_eckd_data_t * data,int trk,int rec_on_trk,int no_rec,int cmd,dasd_device_t * device,int reclen,ccw_req_t * cqr)349 locate_record (ccw1_t * lo_ccw,
350 	       LO_eckd_data_t * data,
351 	       int trk,
352 	       int rec_on_trk,
353 	       int no_rec,
354                int cmd,
355                dasd_device_t * device,
356                int reclen,
357                ccw_req_t* cqr)
358 {
359         int rc=0;
360 	dasd_eckd_private_t *private = (dasd_eckd_private_t *) device->private;
361 	ch_t geo = { private->rdc_data.no_cyl,
362 		private->rdc_data.trk_per_cyl
363 	};
364 	ch_t seek = { trk / (geo.head), trk % (geo.head) };
365 	int sector = 0;
366 
367         DBF_EVENT (DBF_INFO,
368                    "Locate: trk %d, rec %d, no_rec %d, cmd %d, reclen %d",
369                    trk,
370                    rec_on_trk,
371                    no_rec,
372                    cmd,
373                    reclen);
374 
375 	memset (lo_ccw, 0, sizeof (ccw1_t));
376 	lo_ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD;
377 	lo_ccw->count = 16;
378 	if ((rc=dasd_set_normalized_cda (lo_ccw, __pa (data), cqr, device)))
379                 return rc;
380 
381 	memset (data, 0, sizeof (LO_eckd_data_t));
382         if (rec_on_trk) {
383                 switch (private->rdc_data.dev_type) {
384                 case 0x3390:{
385                         int dn, d;
386                         dn = ceil_quot (reclen + 6, 232);
387                         d = 9 + ceil_quot (reclen + 6 * (dn + 1), 34);
388                         sector = (49 + (rec_on_trk - 1) * (10 + d)) / 8;
389                         break;
390                 }
391                 case 0x3380:{
392                         int d;
393                         d = 7 + ceil_quot (reclen + 12, 32);
394                         sector = (39 + (rec_on_trk - 1) * (8 + d)) / 7;
395                         break;
396                 }
397                 case 0x9345:
398                 default:
399                         sector = 0;
400                 }
401         }
402         data->sector = sector;
403 	data->count = no_rec;
404 	switch (cmd) {
405 	case DASD_ECKD_CCW_WRITE_HOME_ADDRESS:
406 		data->operation.orientation = 0x3;
407 		data->operation.operation = 0x03;
408 		break;
409 	case DASD_ECKD_CCW_READ_HOME_ADDRESS:
410 		data->operation.orientation = 0x3;
411 		data->operation.operation = 0x16;
412 		break;
413 	case DASD_ECKD_CCW_WRITE_RECORD_ZERO:
414 		data->operation.orientation = 0x1;
415 		data->operation.operation = 0x03;
416 		data->count++;
417 		break;
418 	case DASD_ECKD_CCW_READ_RECORD_ZERO:
419 		data->operation.orientation = 0x3;
420 		data->operation.operation = 0x16;
421 		data->count++;
422 		break;
423 	case DASD_ECKD_CCW_WRITE:
424 	case DASD_ECKD_CCW_WRITE_MT:
425 	case DASD_ECKD_CCW_WRITE_KD:
426 	case DASD_ECKD_CCW_WRITE_KD_MT:
427 		data->auxiliary.last_bytes_used = 0x1;
428 		data->length = reclen;
429 		data->operation.operation = 0x01;
430 		break;
431 	case DASD_ECKD_CCW_WRITE_CKD:
432 	case DASD_ECKD_CCW_WRITE_CKD_MT:
433 		data->auxiliary.last_bytes_used = 0x1;
434 		data->length = reclen;
435 		data->operation.operation = 0x03;
436 		break;
437 	case DASD_ECKD_CCW_READ:
438 	case DASD_ECKD_CCW_READ_MT:
439 	case DASD_ECKD_CCW_READ_KD:
440 	case DASD_ECKD_CCW_READ_KD_MT:
441 		data->auxiliary.last_bytes_used = 0x1;
442 		data->length = reclen;
443 		data->operation.operation = 0x06;
444 		break;
445 	case DASD_ECKD_CCW_READ_CKD:
446 	case DASD_ECKD_CCW_READ_CKD_MT:
447 		data->auxiliary.last_bytes_used = 0x1;
448 		data->length = reclen;
449 		data->operation.operation = 0x16;
450 		break;
451 	case DASD_ECKD_CCW_READ_COUNT:
452 		data->operation.operation = 0x06;
453 		break;
454 	case DASD_ECKD_CCW_ERASE:
455                 data->length = reclen;
456                 data->auxiliary.last_bytes_used = 0x1;
457 		data->operation.operation = 0x0b;
458 		break;
459 	default:
460 
461                 MESSAGE (KERN_ERR,
462                          "unknown opcode 0x%x",
463                          cmd);
464 	}
465 	memcpy (&(data->seek_addr), &seek, sizeof (ch_t));
466 	memcpy (&(data->search_arg), &seek, sizeof (ch_t));
467 	data->search_arg.record = rec_on_trk;
468         return rc;
469 }
470 
471 static int
dasd_eckd_id_check(s390_dev_info_t * info)472 dasd_eckd_id_check (s390_dev_info_t * info)
473 {
474 	if (info->sid_data.cu_type == 0x3990 ||
475 	    info->sid_data.cu_type == 0x2105)
476 		    if (info->sid_data.dev_type == 0x3390) return 0;
477 	if (info->sid_data.cu_type == 0x3990 ||
478 	    info->sid_data.cu_type == 0x2105)
479 		    if (info->sid_data.dev_type == 0x3380) return 0;
480 	if (info->sid_data.cu_type == 0x9343)
481 		if (info->sid_data.dev_type == 0x9345)
482 			return 0;
483 	return -ENODEV;
484 }
485 
486 static int
dasd_eckd_check_characteristics(struct dasd_device_t * device)487 dasd_eckd_check_characteristics (struct dasd_device_t *device)
488 {
489 	int   rc = 0;
490 	void *conf_data;
491         void *rdc_data;
492 	int   conf_len;
493 	dasd_eckd_private_t *private;
494 
495 	if (device == NULL) {
496 
497 		MESSAGE (KERN_WARNING, "%s",
498                          "Null device pointer passed to characteristics "
499                          "checker");
500 
501                 return -ENODEV;
502 	}
503 	device->private = kmalloc (sizeof (dasd_eckd_private_t),
504                                    GFP_KERNEL);
505 
506 	if (device->private == NULL) {
507 
508 		MESSAGE (KERN_WARNING, "%s",
509 			"memory allocation failed for private data");
510 
511 		rc = -ENOMEM;
512                 goto fail;
513 	}
514 
515 	private  = (dasd_eckd_private_t *) device->private;
516 	rdc_data = (void *) &(private->rdc_data);
517 
518         /* Read Device Characteristics */
519 	rc = read_dev_chars (device->devinfo.irq,
520                              &rdc_data,
521                              64);
522 	if (rc) {
523 
524 		MESSAGE (KERN_WARNING,
525 			"Read device characteristics returned error %d",
526                          rc);
527 
528                 goto fail;
529         }
530 
531         DEV_MESSAGE (KERN_INFO, device,
532                      "%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d",
533                      private->rdc_data.dev_type,
534                      private->rdc_data.dev_model,
535                      private->rdc_data.cu_type,
536                      private->rdc_data.cu_model.model,
537                      private->rdc_data.no_cyl,
538                      private->rdc_data.trk_per_cyl,
539                      private->rdc_data.sec_per_trk);
540 
541         /* set default cache operations */
542         private->attrib.operation = DASD_NORMAL_CACHE;
543         private->attrib.nr_cyl    = 0;
544 
545         /* Read Configuration Data */
546         rc = read_conf_data (device->devinfo.irq,
547                              &conf_data,
548                              &conf_len,
549                              LPM_ANYPATH);
550 
551         if (rc == -EOPNOTSUPP) {
552                 rc = 0; /* this one is ok */
553         }
554 	if (rc) {
555 
556 		MESSAGE (KERN_WARNING,
557                          "Read configuration data returned error %d",
558                          rc);
559 
560                 goto fail;
561 	}
562         if (conf_data == NULL) {
563 
564 		MESSAGE (KERN_WARNING, "%s",
565                          "No configuration data retrieved");
566 
567                 goto out; /* no errror */
568 	}
569         if (conf_len != sizeof (dasd_eckd_confdata_t)) {
570 
571 		MESSAGE (KERN_WARNING,
572                          "sizes of configuration data mismatch"
573                          "%d (read) vs %ld (expected)",
574                          conf_len,
575                          sizeof (dasd_eckd_confdata_t));
576 
577                 goto out; /* no errror */
578 	}
579         memcpy (&private->conf_data, conf_data,
580                 sizeof (dasd_eckd_confdata_t));
581 
582         DEV_MESSAGE (KERN_INFO, device,
583                 "%04X/%02X(CU:%04X/%02X): Configuration data read",
584                 private->rdc_data.dev_type,
585                 private->rdc_data.dev_model,
586                 private->rdc_data.cu_type,
587                 private->rdc_data.cu_model.model);
588         goto out;
589 
590  fail:
591         if (device->private) {
592                 kfree (device->private);
593                 device->private = NULL;
594         }
595 
596  out:
597 	return rc;
598 }
599 
600 static inline int
dasd_eckd_cdl_reclen(dasd_device_t * device,int recid)601 dasd_eckd_cdl_reclen (dasd_device_t * device, int recid)
602 {
603 	dasd_eckd_private_t *private = (dasd_eckd_private_t *) device->private;
604 	int byt_per_blk = device->sizes.bp_block;
605 	int blk_per_trk = recs_per_track (&(private->rdc_data), 0, byt_per_blk);
606 	if (recid < 3)
607 		return sizes_trk0[recid];
608 	if (recid < blk_per_trk)
609 		return byt_per_blk;
610 	if (recid < 2 * blk_per_trk)
611 		return LABEL_SIZE;
612 	return byt_per_blk;
613 }
614 
615 static ccw_req_t *
dasd_eckd_init_analysis(struct dasd_device_t * device)616 dasd_eckd_init_analysis (struct dasd_device_t *device)
617 {
618 	ccw_req_t *cqr = NULL;
619 	ccw1_t *ccw;
620 	DE_eckd_data_t *DE_data;
621 	LO_eckd_data_t *LO_data;
622 	dasd_eckd_private_t *private = (dasd_eckd_private_t *)device->private;
623 	eckd_count_t *count_data = private->count_area;
624 
625 	cqr = dasd_alloc_request (dasd_eckd_discipline.name, 8 + 1,
626 				 sizeof (DE_eckd_data_t) +
627 				 2 * sizeof (LO_eckd_data_t),
628                                  device);
629 	if (cqr == NULL) {
630 
631                 MESSAGE (KERN_WARNING, "%s",
632                         "No memory to allocate initialization request");
633                 goto out;
634 	}
635 	DE_data = cqr->data;
636 	LO_data = cqr->data + sizeof (DE_eckd_data_t);
637 	ccw = cqr->cpaddr;
638 	if (define_extent (ccw, DE_data, 0, 2, DASD_ECKD_CCW_READ_COUNT, device, cqr)) {
639                 goto clear_cqr;
640         }
641 	ccw->flags |= CCW_FLAG_CC;
642 	ccw++;
643 	if (locate_record (ccw, LO_data++, 0, 0, 4, DASD_ECKD_CCW_READ_COUNT,
644                            device, 0, cqr)) {
645                 goto clear_cqr;
646         }
647 	ccw->flags |= CCW_FLAG_CC;
648 	ccw++;
649 	ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT;
650 	ccw->count = 8;
651 	if (dasd_set_normalized_cda (ccw, __pa (count_data++), cqr, device)) {
652                 goto clear_cqr;
653         }
654 	ccw->flags |= CCW_FLAG_CC;
655 	ccw++;
656 	ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT;
657 	ccw->count = 8;
658 	if (dasd_set_normalized_cda (ccw, __pa (count_data++), cqr, device)) {
659                 goto clear_cqr;
660         }
661 	ccw->flags |= CCW_FLAG_CC;
662 	ccw++;
663 	ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT;
664 	ccw->count = 8;
665 	if (dasd_set_normalized_cda (ccw, __pa (count_data++), cqr, device)) {
666                 goto clear_cqr;
667         }
668 	ccw->flags |= CCW_FLAG_CC;
669 	ccw++;
670 	ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT;
671 	ccw->count = 8;
672 	if (dasd_set_normalized_cda (ccw, __pa (count_data++), cqr, device)) {
673                 goto clear_cqr;
674         }
675 	ccw->flags |= CCW_FLAG_CC;
676 	ccw++;
677 	if (locate_record (ccw, LO_data++, 2, 0, 1, DASD_ECKD_CCW_READ_COUNT,
678                            device, 0, cqr)) {
679                 goto clear_cqr;
680         }
681 	ccw->flags |= CCW_FLAG_CC;
682 	ccw++;
683 	ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT;
684 	ccw->count = 8;
685 	if (dasd_set_normalized_cda (ccw, __pa (count_data), cqr, device)) {
686                 goto clear_cqr;
687         }
688 	cqr->device = device;
689 	cqr->retries = 0;
690         cqr->buildclk = get_clock ();
691 	cqr->status = CQR_STATUS_FILLED;
692         dasd_chanq_enq (&device->queue, cqr);
693         goto out;
694 
695  clear_cqr:
696         dasd_free_request (cqr,device);
697 
698         MESSAGE (KERN_WARNING, "%s",
699                 "No memory to allocate initialization request");
700 
701         cqr=NULL;
702  out:
703 	return cqr;
704 }
705 
706 static int
dasd_eckd_do_analysis(struct dasd_device_t * device)707 dasd_eckd_do_analysis (struct dasd_device_t *device)
708 {
709 	int sb, rpt;
710 	dasd_eckd_private_t *private = (dasd_eckd_private_t *) device->private;
711 	eckd_count_t *count_area = NULL;
712 	char *cdl_msg;
713         int status;
714 	int i;
715 
716 	private->uses_cdl = 1;
717         status = device->init_cqr->status;
718 	dasd_chanq_deq (&device->queue, device->init_cqr);
719 	dasd_free_request (device->init_cqr, device);
720 	/* Free the cqr and cleanup device->sizes */
721         if ( status != CQR_STATUS_DONE ) {
722 
723                 DEV_MESSAGE (KERN_WARNING, device, "%s",
724                              "volume analysis returned unformatted disk");
725 
726                 return -EMEDIUMTYPE;
727         }
728 	/* Check Track 0 for Compatible Disk Layout */
729 	for (i = 0; i < 3; i++) {
730 		if ((i < 3) &&
731 		    ((private->count_area[i].kl != 4) ||
732 		     (private->count_area[i].dl !=
733 		      dasd_eckd_cdl_reclen (device, i) - 4))) {
734 			private->uses_cdl = 0;
735 			break;
736 		}
737 	}
738 	if (i == 3) {
739 		count_area = &private->count_area[4];
740 	}
741 	if (private->uses_cdl == 0) {
742 		for (i = 0; i < 5; i++) {
743 			if ((private->count_area[i].kl != 0) ||
744 			    (private->count_area[i].dl !=
745 			     private->count_area[0].dl)) {
746 				break;
747 			}
748 		}
749 		if (i == 5) {
750 			count_area = &private->count_area[0];
751 		}
752 	} else {
753 		if (private->count_area[3].record == 1) {
754 
755 			DEV_MESSAGE (KERN_WARNING, device, "%s",
756                                      "Trk 0: no records after VTOC!");
757 		}
758 	}
759 	if (count_area != NULL &&	/* we found notthing violating our disk layout */
760 	    count_area->kl == 0) {
761                 /* find out blocksize */
762                 switch (count_area->dl) {
763                 case 512:
764 		case 1024:
765 		case 2048:
766 		case 4096:
767 			device->sizes.bp_block = count_area->dl;
768 			break;
769 		}
770 	}
771 	if (device->sizes.bp_block == 0) {
772 
773 		DEV_MESSAGE (KERN_WARNING, device, "%s",
774                              "Volume has incompatible disk layout");
775 
776 		return -EMEDIUMTYPE;
777 	}
778 	device->sizes.s2b_shift = 0;	/* bits to shift 512 to get a block */
779 	device->sizes.pt_block = 2;
780 	for (sb = 512; sb < device->sizes.bp_block; sb = sb << 1)
781 		device->sizes.s2b_shift++;
782 
783 	rpt = recs_per_track (&private->rdc_data, 0, device->sizes.bp_block);
784 	device->sizes.blocks = (private->rdc_data.no_cyl *
785 				private->rdc_data.trk_per_cyl *
786 				recs_per_track (&private->rdc_data, 0,
787 						device->sizes.bp_block));
788 	cdl_msg =
789 	    private->
790 	    uses_cdl ? "compatible disk layout" : "linux disk layout";
791 
792 	DEV_MESSAGE (KERN_INFO, device,
793                      "(%dkB blks): %dkB at %dkB/trk %s",
794                      (device->sizes.bp_block >> 10),
795                      ((private->rdc_data.no_cyl *
796                        private->rdc_data.trk_per_cyl *
797                        recs_per_track (&private->rdc_data, 0,
798                                        device->sizes.bp_block) *
799                        (device->sizes.bp_block >> 9)) >> 1),
800                      ((recs_per_track (&private->rdc_data, 0,
801                                        device->sizes.bp_block) *
802                        device->sizes.bp_block) >> 10),
803                      cdl_msg);
804 
805 	return 0;
806 }
807 
808 static int
dasd_eckd_fill_geometry(struct dasd_device_t * device,struct hd_geometry * geo)809 dasd_eckd_fill_geometry (struct dasd_device_t *device, struct hd_geometry *geo)
810 {
811 	int rc = 0;
812 	dasd_eckd_private_t *private = (dasd_eckd_private_t *) device->private;
813 	switch (device->sizes.bp_block) {
814 	case 512:
815 	case 1024:
816 	case 2048:
817 	case 4096:
818             geo->sectors = recs_per_track (&(private->rdc_data),
819                                            0, device->sizes.bp_block);
820             break;
821 	default:
822             break;
823 	}
824 	geo->cylinders = private->rdc_data.no_cyl;
825 	geo->heads = private->rdc_data.trk_per_cyl;
826 	return rc;
827 }
828 
829 static ccw_req_t *
dasd_eckd_format_device(dasd_device_t * device,format_data_t * fdata)830 dasd_eckd_format_device (dasd_device_t * device, format_data_t * fdata)
831 {
832 	int i;
833 	ccw_req_t *fcp = NULL;
834 	DE_eckd_data_t *DE_data = NULL;
835 	LO_eckd_data_t *LO_data = NULL;
836 	eckd_count_t *ct_data = NULL;
837 	eckd_count_t *r0_data = NULL;
838 	eckd_home_t *ha_data = NULL;
839 	ccw1_t *last_ccw = NULL;
840 	void *last_data = NULL;
841 	dasd_eckd_private_t *private = (dasd_eckd_private_t *) device->private;
842 
843 	int rpt = recs_per_track (&(private->rdc_data), 0, fdata->blksize);
844 	int cyl = fdata->start_unit / private->rdc_data.trk_per_cyl;
845 	int head = fdata->start_unit % private->rdc_data.trk_per_cyl;
846 	int wrccws = rpt;
847 	int datasize = sizeof (DE_eckd_data_t) + sizeof (LO_eckd_data_t);
848 
849 	if (fdata->start_unit >=
850             (private->rdc_data.no_cyl * private->rdc_data.trk_per_cyl)){
851 
852                 DEV_MESSAGE (KERN_INFO, device,
853                              "Track no %d too big!",
854                              fdata->start_unit);
855 
856                 return NULL;
857         }
858         if ( fdata->start_unit > fdata->stop_unit) {
859 
860                 DEV_MESSAGE (KERN_INFO, device,
861                              "Track %d reached! ending.",
862                              fdata->start_unit);
863 
864 		return NULL;
865 	}
866 	switch (fdata->blksize) {
867 	case 512:
868 	case 1024:
869 	case 2048:
870 	case 4096:
871 		break;
872 	default:
873 
874 		MESSAGE (KERN_WARNING,
875                          "Invalid blocksize %d...terminating!",
876                          fdata->blksize);
877 
878 		return NULL;
879 	}
880 	switch (fdata->intensity) {
881 	case 0x00:
882 	case 0x01:
883 	case 0x03:
884 	case 0x04:		/* make track invalid */
885 	case 0x08:
886 	case 0x09:
887 	case 0x0b:
888 	case 0x0c:
889 		break;
890 	default:
891 
892 		MESSAGE (KERN_WARNING,
893                          "Invalid flags 0x%x...terminating!",
894                          fdata->intensity);
895 
896 		return NULL;
897 	}
898 
899 	/* print status line */
900 	if ((private->rdc_data.no_cyl < 20) ?
901 	    (fdata->start_unit % private->rdc_data.no_cyl == 0) :
902 	    (fdata->start_unit % private->rdc_data.no_cyl == 0 &&
903 	     (fdata->start_unit / private->rdc_data.no_cyl) %
904 	     (private->rdc_data.no_cyl / 20))) {
905 
906 		DBF_DEV_EVENT (DBF_NOTICE, device,
907                                "Format Cylinder: %d Flags: %d",
908                                fdata->start_unit / private->rdc_data.trk_per_cyl,
909                                fdata->intensity);
910 
911 	}
912 	if ((fdata->intensity & ~0x8) & 0x04) {
913 		wrccws = 1;
914 		rpt = 1;
915 	} else {
916 		if (fdata->intensity & 0x1) {
917 			wrccws++;
918 			datasize += sizeof (eckd_count_t);
919 		}
920 		if (fdata->intensity & 0x2) {
921 			wrccws++;
922 			datasize += sizeof (eckd_home_t);
923 		}
924 	}
925 	fcp = dasd_alloc_request (dasd_eckd_discipline.name,
926 				 wrccws + 2 + 1,
927 				 datasize + rpt * sizeof (eckd_count_t),
928                                  device );
929 	if (fcp != NULL) {
930 		fcp->device = device;
931 		fcp->retries = 2;	/* set retry counter to enable ERP */
932 		last_data = fcp->data;
933 		DE_data = (DE_eckd_data_t *) last_data;
934 		last_data = (void *) (DE_data + 1);
935 		LO_data = (LO_eckd_data_t *) last_data;
936 		last_data = (void *) (LO_data + 1);
937 		if (fdata->intensity & 0x2) {
938 			ha_data = (eckd_home_t *) last_data;
939 			last_data = (void *) (ha_data + 1);
940 		}
941 		if (fdata->intensity & 0x1) {
942 			r0_data = (eckd_count_t *) last_data;
943 			last_data = (void *) (r0_data + 1);
944 		}
945 		ct_data = (eckd_count_t *) last_data;
946 
947 		last_ccw = fcp->cpaddr;
948 
949 		switch (fdata->intensity & ~0x08) {
950 		case 0x03:
951 			if (define_extent (last_ccw, DE_data, fdata->start_unit, fdata->start_unit,
952                                            DASD_ECKD_CCW_WRITE_HOME_ADDRESS,
953                                            device, fcp)) {
954                                 goto clear_fcp;
955                         }
956 			last_ccw->flags |= CCW_FLAG_CC;
957 			last_ccw++;
958 			if (locate_record (last_ccw, LO_data, fdata->start_unit, 0, wrccws,
959                                            DASD_ECKD_CCW_WRITE_HOME_ADDRESS, device,
960                                            device->sizes.bp_block, fcp)) {
961                                 goto clear_fcp;
962                         }
963 			last_ccw->flags |= CCW_FLAG_CC;
964 			last_ccw++;
965 			break;
966 		case 0x01:
967 			if (define_extent (last_ccw, DE_data, fdata->start_unit, fdata->start_unit,
968                                            DASD_ECKD_CCW_WRITE_RECORD_ZERO, device, fcp)) {
969                                 goto clear_fcp;
970                         }
971 			last_ccw->flags |= CCW_FLAG_CC;
972 			last_ccw++;
973 			if (locate_record (last_ccw, LO_data, fdata->start_unit, 0, wrccws,
974                                            DASD_ECKD_CCW_WRITE_RECORD_ZERO, device,
975                                            device->sizes.bp_block, fcp)) {
976                                 goto clear_fcp;
977                         }
978 			last_ccw->flags |= CCW_FLAG_CC;
979 			last_ccw++;
980 			memset (r0_data, 0, sizeof (eckd_count_t));
981 			break;
982 		case 0x04:
983                         fdata->blksize = 8;
984 		case 0x00:
985 			if (define_extent (last_ccw, DE_data, fdata->start_unit, fdata->start_unit,
986                                            DASD_ECKD_CCW_WRITE_CKD, device, fcp)) {
987                                 dasd_free_request (fcp, device);
988                                 return NULL;
989                         }
990 			last_ccw->flags |= CCW_FLAG_CC;
991 			last_ccw++;
992 			if (locate_record (last_ccw, LO_data, fdata->start_unit, 0, wrccws,
993                                            DASD_ECKD_CCW_WRITE_CKD, device, fdata->blksize, fcp)) {
994                                 goto clear_fcp;
995                         }
996 			last_ccw->flags |= CCW_FLAG_CC;
997 			last_ccw++;
998 			break;
999 		default:
1000 
1001 			MESSAGE (KERN_WARNING,
1002                                  "Unknown format flags...%d",
1003                                  fdata->intensity);
1004 
1005 			return NULL;
1006 		}
1007 		if (fdata->intensity & 0x02) {
1008 
1009 			MESSAGE (KERN_WARNING,
1010                                  "Unsupported format flag...%d",
1011                                  fdata->intensity);
1012 
1013 			return NULL;
1014 		}
1015 		if (fdata->intensity & 0x01) {	/* write record zero */
1016 			r0_data->cyl = cyl;
1017 			r0_data->head = head;
1018 			r0_data->record = 0;
1019 			r0_data->kl = 0;
1020 			r0_data->dl = 8;
1021 			last_ccw->cmd_code = DASD_ECKD_CCW_WRITE_RECORD_ZERO;
1022 			last_ccw->count = 8;
1023 			last_ccw->flags |= CCW_FLAG_CC | CCW_FLAG_SLI;
1024 			if (dasd_set_normalized_cda (last_ccw, __pa (r0_data), fcp, device)) {
1025                                 goto clear_fcp;
1026                         }
1027 			last_ccw++;
1028 		}
1029 		if ((fdata->intensity & ~0x08) & 0x04) {	/* erase track */
1030 			memset (ct_data, 0, sizeof (eckd_count_t));
1031 			ct_data->cyl = cyl;
1032 			ct_data->head = head;
1033 			ct_data->record = 1;
1034 			ct_data->kl = 0;
1035 			ct_data->dl = 0;
1036 			last_ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD;
1037 			last_ccw->count = 8;
1038 			last_ccw->flags |= CCW_FLAG_CC | CCW_FLAG_SLI;
1039 			if (dasd_set_normalized_cda (last_ccw, __pa (ct_data), fcp, device)) {
1040                                 goto clear_fcp;
1041                         }
1042 			last_ccw++;
1043 		} else {	/* write remaining records */
1044 			for (i = 0; i < rpt; i++) {
1045 				memset (ct_data + i, 0, sizeof (eckd_count_t));
1046 				(ct_data + i)->cyl = cyl;
1047 				(ct_data + i)->head = head;
1048 				(ct_data + i)->record = i + 1;
1049 				(ct_data + i)->kl = 0;
1050 				if (fdata->intensity & 0x08) {
1051 					// special handling when formatting CDL
1052 					switch (fdata->start_unit) {
1053 					case 0:
1054 						if (i < 3) {
1055 							(ct_data + i)->kl = 4;
1056 
1057 							    (ct_data + i)->dl =
1058 							    sizes_trk0[i] - 4;
1059 						} else
1060 							(ct_data + i)->dl = fdata->blksize;
1061 						break;
1062 					case 1:
1063 						(ct_data + i)->kl = 44;
1064 						(ct_data + i)->dl = LABEL_SIZE - 44;
1065 						break;
1066 					default:
1067 						(ct_data + i)->dl = fdata->blksize;
1068 						break;
1069 					}
1070 				} else
1071 					(ct_data + i)->dl = fdata->blksize;
1072 				last_ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD;
1073 				last_ccw->flags |= CCW_FLAG_CC | CCW_FLAG_SLI;
1074 				last_ccw->count = 8;
1075 				if (dasd_set_normalized_cda (last_ccw,
1076                                                              __pa (ct_data + i), fcp, device)) {
1077                                 goto clear_fcp;
1078                                 }
1079 				last_ccw++;
1080 			}
1081 		}
1082 		(last_ccw - 1)->flags &= ~(CCW_FLAG_CC | CCW_FLAG_DC);
1083 		fcp->device = device;
1084                 fcp->buildclk = get_clock ();
1085 		fcp->status = CQR_STATUS_FILLED;
1086 	}
1087         goto out;
1088  clear_fcp:
1089         dasd_free_request (fcp, device);
1090         fcp=NULL;
1091  out:
1092 	return fcp;
1093 }
1094 
1095 static dasd_era_t
dasd_eckd_examine_error(ccw_req_t * cqr,devstat_t * stat)1096 dasd_eckd_examine_error (ccw_req_t * cqr, devstat_t * stat)
1097 {
1098 	dasd_device_t *device = (dasd_device_t *) cqr->device;
1099 
1100 	if (stat->cstat == 0x00 &&
1101 	    stat->dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
1102 		    return dasd_era_none;
1103 
1104 	switch (device->devinfo.sid_data.cu_type) {
1105 	case 0x3990:
1106 	case 0x2105:
1107 		return dasd_3990_erp_examine (cqr, stat);
1108 	case 0x9343:
1109 		return dasd_9343_erp_examine (cqr, stat);
1110 	default:
1111 
1112 		MESSAGE (KERN_WARNING, "%s",
1113                          "default (unknown CU type) - RECOVERABLE return");
1114 
1115 		return dasd_era_recover;
1116 	}
1117 }
1118 
1119 static dasd_erp_action_fn_t
dasd_eckd_erp_action(ccw_req_t * cqr)1120 dasd_eckd_erp_action (ccw_req_t * cqr)
1121 {
1122 	dasd_device_t *device = (dasd_device_t *) cqr->device;
1123 
1124 	switch (device->devinfo.sid_data.cu_type) {
1125 	case 0x3990:
1126 	case 0x2105:
1127 		return dasd_3990_erp_action;
1128 	case 0x9343:
1129 		/* Return dasd_9343_erp_action; */
1130 	default:
1131 		return dasd_default_erp_action;
1132 	}
1133 }
1134 
1135 static dasd_erp_postaction_fn_t
dasd_eckd_erp_postaction(ccw_req_t * cqr)1136 dasd_eckd_erp_postaction (ccw_req_t * cqr)
1137 {
1138 	return dasd_default_erp_postaction;
1139 }
1140 
1141 
1142 inline unsigned char
dasd_eckd_cdl_cmd(dasd_device_t * device,int recid,int cmd)1143 dasd_eckd_cdl_cmd (dasd_device_t * device, int recid, int cmd)
1144 {
1145 	dasd_eckd_private_t *private = (dasd_eckd_private_t *) device->private;
1146 	int byt_per_blk = device->sizes.bp_block;
1147 	int blk_per_trk = recs_per_track (&(private->rdc_data), 0, byt_per_blk);
1148 	switch (cmd) {
1149 	case READ:
1150 		if (recid < 3)
1151 			return DASD_ECKD_CCW_READ_KD_MT;
1152 		if (recid < blk_per_trk)
1153 			return DASD_ECKD_CCW_READ_MT;
1154 		if (recid < 2 * blk_per_trk)
1155 			return DASD_ECKD_CCW_READ_KD_MT;
1156 		return DASD_ECKD_CCW_READ_MT;
1157 		break;
1158 	case WRITE:
1159 		if (recid < 3)
1160 			return DASD_ECKD_CCW_WRITE_KD_MT;
1161 		if (recid < blk_per_trk)
1162 			return DASD_ECKD_CCW_WRITE_MT;
1163 		if (recid < 2 * blk_per_trk)
1164 			return DASD_ECKD_CCW_WRITE_KD_MT;
1165 		return DASD_ECKD_CCW_WRITE_MT;
1166 		break;
1167 	default:
1168 		BUG ();
1169 	}
1170 	return 0;		// never executed
1171 }
1172 
1173 
1174 static ccw_req_t *
dasd_eckd_build_cp_from_req(dasd_device_t * device,struct request * req)1175 dasd_eckd_build_cp_from_req (dasd_device_t * device, struct request *req)
1176 {
1177 	ccw_req_t *rw_cp = NULL;
1178 	int rw_cmd;
1179 	int bhct;
1180 	long size;
1181 	ccw1_t *ccw;
1182 	DE_eckd_data_t *DE_data;
1183 	LO_eckd_data_t *LO_data;
1184 	struct buffer_head *bh;
1185 	dasd_eckd_private_t *private = (dasd_eckd_private_t *) device->private;
1186 	int byt_per_blk = device->sizes.bp_block;
1187 	int shift = device->sizes.s2b_shift;
1188 	int blk_per_trk = recs_per_track (&(private->rdc_data), 0, byt_per_blk);
1189         unsigned long reloc_sector = req->sector +
1190                 device->major_info->gendisk.part[MINOR(req->rq_dev)].start_sect;
1191         int btrk = (reloc_sector >> shift) / blk_per_trk;
1192 	int etrk = ((reloc_sector + req->nr_sectors - 1) >> shift) / blk_per_trk;
1193 	int recid = reloc_sector >> shift;
1194 	int locate4k_set = 0;
1195 	int nlocs = 0;
1196 	int errcode;
1197 
1198 	if (req->cmd == READ) {
1199 		rw_cmd = DASD_ECKD_CCW_READ_MT;
1200 	} else if (req->cmd == WRITE) {
1201 		rw_cmd = DASD_ECKD_CCW_WRITE_MT;
1202 	} else {
1203 
1204 		MESSAGE (KERN_ERR,
1205                          "Unknown command %d",
1206                          req->cmd);
1207 
1208 		return ERR_PTR(-EINVAL);
1209 	}
1210 	/* Build the request */
1211 	/* count bhs to prevent errors, when bh smaller than block */
1212 	bhct = 0;
1213 	for (bh = req->bh; bh; bh = bh->b_reqnext) {
1214 		if (bh->b_size < byt_per_blk) {
1215 			MESSAGE(KERN_ERR, "ignoring bogus sized request: %d<%d",
1216 					  bh->b_size, byt_per_blk);
1217                         return ERR_PTR(-EINVAL);
1218 		}
1219                 bhct+= bh->b_size >> (device->sizes.s2b_shift+9);
1220 	}
1221 	if (btrk < 2 && private->uses_cdl) {
1222 		if (etrk < 2)
1223                         nlocs = bhct;
1224                 else
1225                         nlocs = 2 * blk_per_trk - recid;
1226 	}
1227 	rw_cp = dasd_alloc_request (dasd_eckd_discipline.name,
1228                                     2 + nlocs + bhct + 1,
1229                                     sizeof (DE_eckd_data_t) + (1 +
1230                                                                nlocs) *
1231                                     sizeof (LO_eckd_data_t),
1232                                     device);
1233 	if (!rw_cp) {
1234 		return ERR_PTR(-ENOMEM);
1235 	}
1236 	DE_data = rw_cp->data;
1237 	LO_data = rw_cp->data + sizeof (DE_eckd_data_t);
1238 	ccw = rw_cp->cpaddr;
1239 	if ((errcode = define_extent (ccw, DE_data, btrk, etrk,
1240                                       rw_cmd, device, rw_cp))) {
1241                 goto clear_rw_cp;
1242         }
1243 	ccw->flags |= CCW_FLAG_CC;
1244 	for (bh = req->bh; bh != NULL;) {
1245                 for (size = 0; size < bh->b_size; size += byt_per_blk) {
1246                         if (!locate4k_set) {
1247                                 // we need to chain a locate record before our rw-ccw
1248                                 ccw++;
1249                                 if ((recid / blk_per_trk) < 2
1250                                     && private->uses_cdl) {
1251                                         /* Do a locate record for our special blocks */
1252                                         int cmd = dasd_eckd_cdl_cmd (device,recid, req->cmd);
1253                                         if ((errcode = locate_record (ccw,
1254                                                        LO_data++,
1255                                                        recid / blk_per_trk,
1256                                                        recid % blk_per_trk + 1,
1257                                                        1, cmd, device,
1258                                                        dasd_eckd_cdl_reclen(device, recid), rw_cp))) {
1259                                                 goto clear_rw_cp;
1260                                         }
1261                                 } else {
1262                                         // Do a locate record for standard blocks */
1263                                         if ((errcode = locate_record (ccw,
1264                                                        LO_data++,
1265                                                        recid /blk_per_trk,
1266                                                        recid %blk_per_trk + 1,
1267                                                        (((reloc_sector +
1268                                                           req->nr_sectors) >>
1269                                                          shift) - recid),
1270                                                        rw_cmd, device,
1271                                                        device->sizes.bp_block, rw_cp))) {
1272                                                 goto clear_rw_cp;
1273                                         }
1274                                         locate4k_set = 1;
1275                                 }
1276                                 ccw->flags |= CCW_FLAG_CC;
1277                         }
1278                         ccw++;
1279                         ccw->flags |= CCW_FLAG_CC;
1280                         ccw->cmd_code = locate4k_set ? rw_cmd :
1281                                 dasd_eckd_cdl_cmd (device, recid, req->cmd);
1282                         ccw->count = byt_per_blk;
1283                         if (!locate4k_set) {
1284                                 ccw->count = dasd_eckd_cdl_reclen (device,recid);
1285                                 if (ccw->count < byt_per_blk) {
1286                                     memset (bh->b_data + size + ccw->count,
1287                                             0xE5, byt_per_blk - ccw->count);
1288                                 }
1289                         }
1290                         if ((errcode = dasd_set_normalized_cda (ccw, __pa (bh->b_data+size),
1291                                                                 rw_cp, device))) {
1292                                 goto clear_rw_cp;
1293                         }
1294                         recid++;
1295                 }
1296                 bh = bh->b_reqnext;
1297 	}
1298 	ccw->flags    &= ~(CCW_FLAG_DC | CCW_FLAG_CC);
1299 	rw_cp->device  = device;
1300 	rw_cp->expires = 5 * TOD_MIN;	/* 5 minutes */
1301 	rw_cp->req     = req;
1302 	rw_cp->lpm     = LPM_ANYPATH;
1303 	rw_cp->retries = 256;
1304 
1305 	rw_cp->buildclk = get_clock ();
1306 
1307 	check_then_set (&rw_cp->status,
1308                         CQR_STATUS_EMPTY,
1309                         CQR_STATUS_FILLED);
1310 
1311         goto out;
1312  clear_rw_cp:
1313         dasd_free_request (rw_cp,
1314                            device);
1315         rw_cp=ERR_PTR(errcode);
1316  out:
1317 	return rw_cp;
1318 }
1319 
1320 #if 0
1321 int
1322 dasd_eckd_cleanup_request (ccw_req_t * cqr)
1323 {
1324 	int ret = 0;
1325 	struct request *req = cqr->req;
1326 	dasd_device_t *device = cqr->device;
1327 	int byt_per_blk = device->sizes.bp_block;
1328 
1329 	for (bh = req->bh; bh != NULL;) {
1330 		if (bh->b_size > byt_per_blk) {
1331 			for (size = 0; size < bh->b_size; size += byt_per_blk) {
1332 				ccw++;
1333 				ccw->flags |= CCW_FLAG_CC;
1334 				ccw->cmd_code = rw_cmd;
1335 				ccw->count = byt_per_blk;
1336 				set_normalized_cda (ccw,
1337 						    __pa (bh->b_data + size));
1338 			}
1339 			bh = bh->b_reqnext;
1340 		} else {	/* group N bhs to fit into byt_per_blk */
1341 			for (size = 0; bh != NULL && size < byt_per_blk;) {
1342 				ccw++;
1343 				ccw->flags |= CCW_FLAG_DC;
1344 				ccw->cmd_code = rw_cmd;
1345 				ccw->count = bh->b_size;
1346 				set_normalized_cda (ccw, __pa (bh->b_data));
1347 				size += bh->b_size;
1348 				bh = bh->b_reqnext;
1349 			}
1350 		}
1351 	}
1352 	return ret;
1353 }
1354 #endif
1355 
1356 /*
1357  * DASD_ECKD_RESERVE
1358  *
1359  * DESCRIPTION
1360  *    Buils a channel programm to reserve a device.
1361  *    Options are set to 'synchronous wait for interrupt' and
1362  *    'timeout the request'. This leads to an terminate IO if
1363  *    the interrupt is outstanding for a certain time.
1364  */
1365 ccw_req_t *
dasd_eckd_reserve(struct dasd_device_t * device)1366 dasd_eckd_reserve (struct dasd_device_t * device)
1367 {
1368 	ccw_req_t *cqr;
1369 
1370         cqr = dasd_alloc_request (dasd_eckd_discipline.name,
1371                                   1 + 1, 32, device);
1372 	if (cqr == NULL) {
1373 
1374 		MESSAGE (KERN_WARNING, "%s",
1375                          "No memory to allocate initialization request");
1376 
1377 		return NULL;
1378 	}
1379 	cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RESERVE;
1380         cqr->cpaddr->flags |= CCW_FLAG_SLI;
1381         cqr->cpaddr->count = 32;
1382 
1383 	if (dasd_set_normalized_cda (cqr->cpaddr, __pa (cqr->data),
1384                                      cqr, device)) {
1385                 dasd_free_request (cqr, device);
1386                 return NULL;
1387         }
1388 
1389 	cqr->device  = device;
1390 	cqr->retries = 0;
1391 	cqr->expires = 10 * TOD_SEC;
1392 	cqr->buildclk = get_clock ();
1393 	cqr->status  = CQR_STATUS_FILLED;
1394 	return cqr;
1395 }
1396 
1397 /*
1398  * DASD_ECKD_RELEASE
1399  *
1400  * DESCRIPTION
1401  *    Buils a channel programm to releases a prior reserved
1402  *    (see dasd_eckd_reserve) device.
1403  */
1404 ccw_req_t *
dasd_eckd_release(struct dasd_device_t * device)1405 dasd_eckd_release (struct dasd_device_t * device)
1406 {
1407 	ccw_req_t *cqr;
1408 
1409         cqr = dasd_alloc_request (dasd_eckd_discipline.name,
1410                                   1 + 1, 32, device);
1411 	if (cqr == NULL) {
1412 
1413 		MESSAGE (KERN_WARNING, "%s",
1414                          "No memory to allocate initialization request");
1415 
1416 		return NULL;
1417 	}
1418 	cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RELEASE;
1419         cqr->cpaddr->flags |= CCW_FLAG_SLI;
1420         cqr->cpaddr->count = 32;
1421 
1422 	if (dasd_set_normalized_cda (cqr->cpaddr, __pa (cqr->data),
1423                                      cqr, device)) {
1424                 dasd_free_request (cqr, device);
1425                 return NULL;
1426         }
1427 
1428 	cqr->device  = device;
1429 	cqr->retries = 0;
1430 	cqr->expires = 10 * TOD_SEC;
1431 	cqr->buildclk = get_clock ();
1432 	cqr->status  = CQR_STATUS_FILLED;
1433 	return cqr;
1434 
1435 }
1436 
1437 /*
1438  * DASD_ECKD_STEAL_LOCK
1439  *
1440  * DESCRIPTION
1441  *    Buils a channel programm to break a device's reservation.
1442  *    (unconditional reserve)
1443  */
1444 ccw_req_t *
dasd_eckd_steal_lock(struct dasd_device_t * device)1445 dasd_eckd_steal_lock (struct dasd_device_t * device)
1446 {
1447 	ccw_req_t *cqr;
1448 
1449         cqr = dasd_alloc_request (dasd_eckd_discipline.name,
1450                                   1 + 1, 32, device);
1451 	if (cqr == NULL) {
1452 
1453 		MESSAGE (KERN_WARNING, "%s",
1454 			"No memory to allocate initialization request");
1455 
1456 		return NULL;
1457 	}
1458 	cqr->cpaddr->cmd_code = DASD_ECKD_CCW_SLCK;
1459         cqr->cpaddr->flags |= CCW_FLAG_SLI;
1460         cqr->cpaddr->count = 32;
1461 
1462 	if (dasd_set_normalized_cda (cqr->cpaddr, __pa (cqr->data),
1463                                      cqr, device)) {
1464                 dasd_free_request (cqr, device);
1465                 return NULL;
1466         }
1467 
1468 	cqr->device  = device;
1469 	cqr->retries = 0;
1470 	cqr->expires = 10 * TOD_SEC;
1471 	cqr->buildclk = get_clock ();
1472 	cqr->status  = CQR_STATUS_FILLED;
1473 	return cqr;
1474 }
1475 
1476 static inline ccw1_t *
dasd_eckd_find_cmd(ccw_req_t * cqr,int cmd)1477 dasd_eckd_find_cmd (ccw_req_t * cqr, int cmd)
1478 {
1479 	ccw1_t *cp;
1480 
1481 	cp = cqr->cpaddr;
1482 	do {
1483 		if (cp->cmd_code == cmd)
1484 			return cp;
1485 		if (cp->cmd_code == CCW_CMD_TIC) {
1486 			cp = (ccw1_t *) (long) cp->cda;
1487 			continue;
1488 		}
1489 		if (cp->flags & (CCW_FLAG_DC | CCW_FLAG_CC)) {
1490 			cp++;
1491 			continue;
1492 		}
1493 		break;
1494 	} while (1);
1495 	return NULL;
1496 }
1497 
1498 static ccw_req_t *
dasd_eckd_merge_cp(dasd_device_t * device)1499 dasd_eckd_merge_cp (dasd_device_t * device)
1500 {
1501 	return NULL;
1502 }
1503 
1504 static int
dasd_eckd_fill_info(dasd_device_t * device,dasd_information2_t * info)1505 dasd_eckd_fill_info (dasd_device_t * device, dasd_information2_t * info)
1506 {
1507 	int rc = 0;
1508 	info->label_block = 2;
1509 	if (((dasd_eckd_private_t *) device->private)->uses_cdl) {
1510 		info->FBA_layout = 0;
1511                 info->format = DASD_FORMAT_CDL;
1512         } else {
1513                 info->FBA_layout = 1;
1514                 info->format = DASD_FORMAT_LDL;
1515         }
1516 	info->characteristics_size = sizeof (dasd_eckd_characteristics_t);
1517 	memcpy (info->characteristics,
1518 		&((dasd_eckd_private_t *) device->private)->rdc_data,
1519 		sizeof (dasd_eckd_characteristics_t));
1520 	info->confdata_size = sizeof (dasd_eckd_confdata_t);
1521 	memcpy (info->configuration_data,
1522 		&((dasd_eckd_private_t *) device->private)->conf_data,
1523 		sizeof (dasd_eckd_confdata_t));
1524 	return rc;
1525 }
1526 
1527 /*
1528  * DASD_ECKD_READ_STATS
1529  *
1530  * DESCRIPTION
1531  *   build the channel program to read the performance statistics
1532  *   of the attached subsystem
1533  */
1534 ccw_req_t *
dasd_eckd_read_stats(struct dasd_device_t * device)1535 dasd_eckd_read_stats (struct dasd_device_t * device)
1536 {
1537 
1538         int                    rc;
1539 	ccw1_t                 *ccw;
1540 	ccw_req_t              *cqr;
1541 	dasd_psf_prssd_data_t  *prssdp;
1542 	dasd_rssd_perf_stats_t *statsp;
1543 
1544         cqr = dasd_alloc_request (dasd_eckd_discipline.name,
1545                                   1 /* PSF */ + 1 /* RSSD */,
1546                                   (sizeof (dasd_psf_prssd_data_t) +
1547                                    sizeof (dasd_rssd_perf_stats_t) ),
1548                                   device);
1549 
1550 	if (cqr == NULL) {
1551 
1552                 MESSAGE (KERN_WARNING, "%s",
1553                          "No memory to allocate initialization request");
1554 
1555 		return NULL;
1556 	}
1557 
1558 	cqr->device  = device;
1559 	cqr->retries = 0;
1560 	cqr->expires = 10 * TOD_SEC;
1561 
1562         /* Prepare for Read Subsystem Data */
1563 	prssdp = (dasd_psf_prssd_data_t *) cqr->data;
1564 
1565 	memset (prssdp, 0, sizeof (dasd_psf_prssd_data_t));
1566 
1567 	prssdp->order     = PSF_ORDER_PRSSD;
1568 	prssdp->suborder  = 0x01; /* Perfomance Statistics */
1569 	prssdp->varies[1] = 0x01; /* Perf Statistics for the Subsystem */
1570 
1571 	ccw = cqr->cpaddr;
1572 
1573 	ccw->cmd_code = DASD_ECKD_CCW_PSF;
1574 	ccw->count    = sizeof (dasd_psf_prssd_data_t);
1575 	ccw->flags   |= CCW_FLAG_CC;
1576 
1577 	if ((rc = dasd_set_normalized_cda (ccw,__pa (prssdp), cqr, device))) {
1578 
1579                 dasd_free_request (cqr,
1580                                    device);
1581                 return NULL;
1582         }
1583 
1584 	ccw++;
1585 
1586         /* Read Subsystem Data - Performance Statistics */
1587 	statsp = (dasd_rssd_perf_stats_t *) (prssdp + 1);
1588 	memset (statsp, 0, sizeof (dasd_rssd_perf_stats_t));
1589 
1590 	ccw->cmd_code = DASD_ECKD_CCW_RSSD;
1591 	ccw->count    = sizeof (dasd_rssd_perf_stats_t);
1592 
1593 	if ((rc = dasd_set_normalized_cda (ccw,__pa (statsp), cqr, device))) {
1594 
1595                 dasd_free_request (cqr,
1596                                    device);
1597                 return NULL;
1598         }
1599         cqr->buildclk = get_clock ();
1600 	cqr->status   = CQR_STATUS_FILLED;
1601 
1602 	return cqr;
1603 } /* end  dasd_eckd_rstat */
1604 
1605 /*
1606  * DASD_ECKD_RET_STATS
1607  *
1608  * DESCRIPTION
1609  *   returns pointer to Performance Statistics Data.
1610  */
1611 dasd_rssd_perf_stats_t *
dasd_eckd_ret_stats(ccw_req_t * cqr)1612 dasd_eckd_ret_stats (ccw_req_t *cqr)
1613 {
1614 
1615         dasd_psf_prssd_data_t  *prssdp;
1616 	dasd_rssd_perf_stats_t *statsp;
1617 
1618 	if (cqr == NULL) {
1619 
1620 		return NULL;
1621 	}
1622 
1623         /* Prepare for Read Subsystem Data */
1624 	prssdp = (dasd_psf_prssd_data_t *)  cqr->data;
1625 	statsp = (dasd_rssd_perf_stats_t *) (prssdp + 1);
1626 
1627 	return statsp;
1628 
1629 } /* end  dasd_eckd_rstat */
1630 
1631 
1632 /*
1633  * DASD_ECKD_GET_ATTRIB
1634  *
1635  * DESCRIPTION
1636  *   returnes the cache attributes used in Define Extend (DE).
1637  */
1638 int
dasd_eckd_get_attrib(dasd_device_t * device,attrib_data_t * attrib)1639 dasd_eckd_get_attrib (dasd_device_t *device,
1640                       attrib_data_t *attrib)
1641 {
1642 	dasd_eckd_private_t *private;
1643 
1644         private = (dasd_eckd_private_t *) device->private;
1645         *attrib = private->attrib;
1646 
1647         return 0;
1648 
1649 } /* end dasd_eckd_get_attrib */
1650 
1651 /*
1652  * DASD_ECKD_SET_ATTRIB
1653  *
1654  * DESCRIPTION
1655  *   stores the attributes for cache operation to be used in Define Extend (DE).
1656  */
1657 int
dasd_eckd_set_attrib(dasd_device_t * device,attrib_data_t * attrib)1658 dasd_eckd_set_attrib (dasd_device_t *device,
1659                       attrib_data_t *attrib)
1660 {
1661 	dasd_eckd_private_t *private;
1662 
1663         private = (dasd_eckd_private_t *) device->private;
1664         private->attrib = *attrib;
1665 
1666         DBF_DEV_EVENT (DBF_ERR, device,
1667                      "cache operation mode set to "
1668                      "%x (%i cylinder prestage)",
1669                      private->attrib.operation,
1670                      private->attrib.nr_cyl);
1671 
1672         return 0;
1673 
1674 } /* end dasd_eckd_set_attrib */
1675 
1676 static void
dasd_eckd_dump_sense(struct dasd_device_t * device,ccw_req_t * req)1677 dasd_eckd_dump_sense (struct dasd_device_t *device,
1678                       ccw_req_t            *req)
1679 {
1680 
1681 	char *page = (char *) get_free_page (GFP_ATOMIC);
1682 	devstat_t *stat = &device->dev_status;
1683 	char *sense = stat->ii.sense.data;
1684 	int len, sl, sct;
1685 
1686 	if (page == NULL) {
1687 
1688                 MESSAGE (KERN_ERR, "%s",
1689                         "No memory to dump sense data");
1690 
1691 		return;
1692 	}
1693 
1694 	len = sprintf (page, KERN_ERR PRINTK_HEADER
1695 		       "device %04X on irq %d: I/O status report:\n",
1696 		       device->devinfo.devno, device->devinfo.irq);
1697 	len += sprintf (page + len, KERN_ERR PRINTK_HEADER
1698 			"in req: %p CS: 0x%02X DS: 0x%02X\n",
1699 			req, stat->cstat, stat->dstat);
1700 	len += sprintf (page + len, KERN_ERR PRINTK_HEADER
1701 			"Failing CCW: %p\n", (void *) (long) stat->cpa);
1702 	{
1703 
1704 		ccw1_t *act = req->cpaddr;
1705 		int i = req->cplength;
1706 
1707 		do {
1708 
1709 			DBF_EVENT (DBF_INFO,
1710                                    "CCW %p: %08X %08X",
1711                                    act,
1712                                    ((int *) act)[0],
1713                                    ((int *) act)[1]);
1714 
1715 			DBF_EVENT (DBF_INFO,
1716                                    "DAT: %08X %08X %08X %08X",
1717                                    ((int *) (addr_t) act->cda)[0],
1718                                    ((int *) (addr_t) act->cda)[1],
1719                                    ((int *) (addr_t) act->cda)[2],
1720                                    ((int *) (addr_t) act->cda)[3]);
1721 
1722 			act++;
1723 
1724 		} while (--i);
1725 	}
1726 	if (stat->flag & DEVSTAT_FLAG_SENSE_AVAIL) {
1727 		for (sl = 0; sl < 4; sl++) {
1728 			len += sprintf (page + len, KERN_ERR PRINTK_HEADER
1729 					"Sense(hex) %2d-%2d:",
1730 					(8 * sl), ((8 * sl) + 7));
1731 
1732 			for (sct = 0; sct < 8; sct++) {
1733 				len += sprintf (page + len, " %02x",
1734 						sense[8 * sl + sct]);
1735 			}
1736 			len += sprintf (page + len, "\n");
1737 		}
1738 
1739 		if (sense[27] & DASD_SENSE_BIT_0) {
1740 			/* 24 Byte Sense Data */
1741 			len += sprintf (page + len, KERN_ERR PRINTK_HEADER
1742 					"24 Byte: %x MSG %x, %s MSGb to SYSOP\n",
1743 					sense[7] >> 4, sense[7] & 0x0f,
1744 					sense[1] & 0x10 ? "" : "no");
1745 		} else {
1746 			/* 32 Byte Sense Data */
1747 			len += sprintf (page + len, KERN_ERR PRINTK_HEADER
1748 					"32 Byte: Format: %x "
1749                                         "Exception class %x\n",
1750 					sense[6] & 0x0f, sense[22] >> 4);
1751 		}
1752 	}
1753 
1754         MESSAGE_LOG (KERN_ERR,
1755                      "Sense data:\n%s",
1756                      page);
1757 
1758         free_page ((unsigned long) page);
1759 }
1760 
1761 
1762 dasd_discipline_t dasd_eckd_discipline = {
1763         owner: THIS_MODULE,
1764 	name:"ECKD",
1765 	ebcname:"ECKD",
1766 	max_blocks:240,
1767 	id_check:dasd_eckd_id_check,
1768 	check_characteristics:dasd_eckd_check_characteristics,
1769 	init_analysis:dasd_eckd_init_analysis,
1770 	do_analysis:dasd_eckd_do_analysis,
1771 	fill_geometry:dasd_eckd_fill_geometry,
1772 	start_IO:dasd_start_IO,
1773 	term_IO:dasd_term_IO,
1774 	format_device:dasd_eckd_format_device,
1775 	examine_error:dasd_eckd_examine_error,
1776 	erp_action:dasd_eckd_erp_action,
1777 	erp_postaction:dasd_eckd_erp_postaction,
1778 	build_cp_from_req:dasd_eckd_build_cp_from_req,
1779 	dump_sense:dasd_eckd_dump_sense,
1780 	int_handler:dasd_int_handler,
1781 	reserve:dasd_eckd_reserve,
1782 	release:dasd_eckd_release,
1783         steal_lock:dasd_eckd_steal_lock,
1784 	merge_cp:dasd_eckd_merge_cp,
1785 	fill_info:dasd_eckd_fill_info,
1786         read_stats:dasd_eckd_read_stats,
1787         ret_stats:dasd_eckd_ret_stats,
1788         get_attrib:dasd_eckd_get_attrib,
1789         set_attrib:dasd_eckd_set_attrib,
1790 	list:LIST_HEAD_INIT(dasd_eckd_discipline.list),
1791 };
1792 
1793 int
dasd_eckd_init(void)1794 dasd_eckd_init (void)
1795 {
1796 	int rc = 0;
1797 
1798 	MESSAGE (KERN_INFO,
1799 		"%s discipline initializing",
1800                  dasd_eckd_discipline.name);
1801 
1802 	ASCEBC (dasd_eckd_discipline.ebcname, 4);
1803 	dasd_discipline_add (&dasd_eckd_discipline);
1804 #ifdef CONFIG_DASD_DYNAMIC
1805 	{
1806 		int i;
1807 		for (i = 0;
1808 		     i < sizeof (dasd_eckd_known_devices) / sizeof (devreg_t);
1809 		     i++) {
1810 
1811 			MESSAGE (KERN_INFO,
1812                                  "We are interested in: CU %04X/%02x",
1813                                  dasd_eckd_known_devices[i].ci.hc.ctype,
1814                                  dasd_eckd_known_devices[i].ci.hc.cmode);
1815 
1816 			s390_device_register (&dasd_eckd_known_devices[i]);
1817 		}
1818 	}
1819 #endif				/* CONFIG_DASD_DYNAMIC */
1820 	return rc;
1821 }
1822 
1823 void
dasd_eckd_cleanup(void)1824 dasd_eckd_cleanup (void)
1825 {
1826 
1827 	MESSAGE (KERN_INFO,
1828 		"%s discipline cleaning up",
1829                  dasd_eckd_discipline.name);
1830 
1831 #ifdef CONFIG_DASD_DYNAMIC
1832 	{
1833 		int i;
1834 		for (i = 0;
1835 		     i < sizeof (dasd_eckd_known_devices) / sizeof (devreg_t);
1836 		     i++) {
1837 
1838 			MESSAGE (KERN_INFO,
1839                                  "We were interested in: CU %04X/%02x",
1840                                  dasd_eckd_known_devices[i].ci.hc.ctype,
1841                                  dasd_eckd_known_devices[i].ci.hc.cmode);
1842 
1843 			s390_device_unregister (&dasd_eckd_known_devices[i]);
1844 		}
1845 	}
1846 #endif				/* CONFIG_DASD_DYNAMIC */
1847 	dasd_discipline_del (&dasd_eckd_discipline);
1848 }
1849 
1850 #ifdef MODULE
1851 int
init_module(void)1852 init_module (void)
1853 {
1854 	int rc = 0;
1855 	rc = dasd_eckd_init ();
1856 	return rc;
1857 }
1858 
1859 void
cleanup_module(void)1860 cleanup_module (void)
1861 {
1862 	dasd_eckd_cleanup ();
1863 	return;
1864 }
1865 #endif
1866 
1867 /*
1868  * Overrides for Emacs so that we follow Linus's tabbing style.
1869  * Emacs will notice this stuff at the end of the file and automatically
1870  * adjust the settings for this buffer only.  This must remain at the end
1871  * of the file.
1872  * ---------------------------------------------------------------------------
1873  * Local variables:
1874  * c-indent-level: 4
1875  * c-brace-imaginary-offset: 0
1876  * c-brace-offset: -4
1877  * c-argdecl-indent: 4
1878  * c-label-offset: -4
1879  * c-continued-statement-offset: 4
1880  * c-continued-brace-offset: 0
1881  * indent-tabs-mode: nil
1882  * tab-width: 8
1883  * End:
1884  */
1885