File: /usr/src/linux/drivers/s390/block/dasd_diag.c

1     /* 
2      * File...........: linux/drivers/s390/block/dasd_diag.c
3      * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
4      * Based on.......: linux/drivers/s390/block/mdisk.c
5      * ...............: by Hartmunt Penner <hpenner@de.ibm.com>
6      * Bugreports.to..: <Linux390@de.ibm.com>
7      * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
8     
9      * History of changes
10      * 07/13/00 Added fixup sections for diagnoses ans saved some registers
11      * 07/14/00 fixed constraints in newly generated inline asm
12      * 10/05/00 adapted to 'new' DASD driver
13      *          fixed return codes of dia250()
14      *          fixed partition handling and HDIO_GETGEO
15      */
16     
17     #include <linux/config.h>
18     #include <linux/stddef.h>
19     #include <linux/kernel.h>
20     #include <asm/debug.h>
21     
22     #include <linux/slab.h>
23     #include <linux/hdreg.h>	/* HDIO_GETGEO                      */
24     #include <linux/blk.h>
25     #include <asm/ccwcache.h>
26     #include <asm/dasd.h>
27     
28     #include <asm/ebcdic.h>
29     #include <asm/io.h>
30     #include <asm/irq.h>
31     #include <asm/s390dyn.h>
32     
33     #include "dasd_int.h"
34     #include "dasd_diag.h"
35     
36     #ifdef PRINTK_HEADER
37     #undef PRINTK_HEADER
38     #endif				/* PRINTK_HEADER */
39     #define PRINTK_HEADER DASD_NAME"(diag):"
40     
41     dasd_discipline_t dasd_diag_discipline;
42     
43     typedef struct
44         dasd_diag_private_t {
45     	dasd_diag_characteristics_t rdc_data;
46     	diag_rw_io_t iob;
47     	diag_init_io_t iib;
48     	unsigned long *label;
49     } dasd_diag_private_t;
50     
51     static __inline__ int
52     dia210 (void *devchar)
53     {
54     	int rc;
55     
56     	__asm__ __volatile__ ("    diag  %1,0,0x210\n"
57     			      "0:  ipm   %0\n"
58     			      "    srl   %0,28\n"
59     			      "1:\n"
60     			      ".section .fixup,\"ax\"\n"
61     			      "2:  lhi   %0,3\n"
62     			      "    bras  1,3f\n"
63     			      "    .long 1b\n"
64     			      "3:  l     1,0(1)\n"
65     			      "    br    1\n"
66     			      ".previous\n"
67     			      ".section __ex_table,\"a\"\n"
68     			      "    .align 4\n"
69     			      "    .long 0b,2b\n" ".previous\n":"=d" (rc)
70     			      :"d" ((void *) __pa (devchar))
71     			      :"1");
72     	return rc;
73     }
74     
75     static __inline__ int
76     dia250 (void *iob, int cmd)
77     {
78     	__asm__ __volatile__ ("    lr    0,%1\n"
79     			      "    diag  0,%0,0x250\n"
80     			      "0:  ipm   %0\n"
81     			      "    srl   %0,28\n"
82     			      "    or    %0,1\n"
83     			      "1:\n"
84     			      ".section .fixup,\"ax\"\n"
85     			      "2:  lhi   %0,3\n"
86     			      "    bras  1,3f\n"
87     			      "    .long 1b\n"
88     			      "3:  l     1,0(1)\n"
89     			      "    br    1\n"
90     			      ".previous\n"
91     			      ".section __ex_table,\"a\"\n"
92     			      "    .align 4\n"
93     			      "    .long 0b,2b\n" ".previous\n":"+d" (cmd)
94     			      :"d" ((void *) __pa (iob))
95     			      :"0", "1", "cc");
96     	return cmd;
97     }
98     
99     static __inline__ int
100     mdsk_init_io (dasd_device_t * device, int blocksize, int offset, int size)
101     {
102     	dasd_diag_private_t *private = (dasd_diag_private_t *) device->private;
103     	diag_init_io_t *iib = &private->iib;
104     	int rc;
105     
106     	memset (iib, 0, sizeof (diag_init_io_t));
107     
108     	iib->dev_nr = device->devinfo.devno;
109     	iib->block_size = blocksize;
110     	iib->offset = offset;
111     	iib->start_block = 0;
112     	iib->end_block = size;
113     
114     	rc = dia250 (iib, INIT_BIO);
115     
116     	return rc & 3;
117     }
118     
119     static __inline__ int
120     mdsk_term_io (dasd_device_t * device)
121     {
122     	dasd_diag_private_t *private = (dasd_diag_private_t *) device->private;
123     	diag_init_io_t *iib = &private->iib;
124     	int rc;
125     
126     	memset (iib, 0, sizeof (diag_init_io_t));
127     	iib->dev_nr = device->devinfo.devno;
128     	rc = dia250 (iib, TERM_BIO);
129     	return rc & 3;
130     }
131     
132     int
133     dasd_start_diag (ccw_req_t * cqr)
134     {
135     	int rc;
136     	dasd_device_t *device = cqr->device;
137     	dasd_diag_private_t *private;
138     	diag_rw_io_t *iob;
139     
140     	private = (dasd_diag_private_t *) device->private;
141     	iob = &private->iob;
142     
143     	iob->dev_nr = device->devinfo.devno;
144     	iob->key = 0;
145     	iob->flags = 2;
146     	iob->block_count = cqr->cplength >> 1;
147     	iob->interrupt_params = (u32) cqr;
148     	iob->bio_list = __pa (cqr->cpaddr);
149     
150     	asm volatile ("STCK %0":"=m" (cqr->startclk));
151     	rc = dia250 (iob, RW_BIO);
152     	if (rc > 8) {
153     		PRINT_WARN ("dia250 returned CC %d\n", rc);
154     		check_then_set (&cqr->status,
155     				CQR_STATUS_QUEUED, CQR_STATUS_ERROR);
156     	} else if (rc == 0) {
157     		check_then_set (&cqr->status,
158     				CQR_STATUS_QUEUED, CQR_STATUS_DONE);
159     		dasd_schedule_bh (device);
160     	} else {
161     		if (cqr->expires) {
162     			cqr->expires += cqr->startclk;
163     		}
164     		check_then_set (&cqr->status,
165     				CQR_STATUS_QUEUED, CQR_STATUS_IN_IO);
166     		rc = 0;
167     	}
168     	return rc;
169     }
170     
171     void
172     dasd_ext_handler (struct pt_regs *regs, __u16 code)
173     {
174         	int cpu = smp_processor_id();
175     	ccw_req_t *cqr;
176     	int ip = S390_lowcore.ext_params;
177     	char status = *((char *) &S390_lowcore.ext_params + 5);
178     	dasd_device_t *device;
179     	int done_fast_io = 0;
180     	int devno;
181     
182     	irq_enter(cpu, -1);
183     
184     	if (!ip) {		/* no intparm: unsolicited interrupt */
185     		printk (KERN_WARNING PRINTK_HEADER
186     			"caught unsolicited interrupt\n");
187     		irq_exit(cpu, -1);
188     		return;
189     	}
190     	if (ip & 0x80000001) {
191     		printk (KERN_WARNING PRINTK_HEADER
192     			"caught spurious interrupt with parm %08x\n", ip);
193     		irq_exit(cpu, -1);
194     		return;
195     	}
196     	cqr = (ccw_req_t *) ip;
197     	device = (dasd_device_t *) cqr->device;
198     	devno = device->devinfo.devno;
199     	if (device == NULL) {
200     		printk (KERN_WARNING PRINTK_HEADER
201     			" INT on devno 0x%04X  = /dev/%s (%d:%d)"
202     			" belongs to NULL device\n",
203     			devno, device->name, MAJOR (device->kdev),
204     			MINOR (device->kdev));
205     	}
206     	if (strncmp (device->discipline->ebcname, (char *) &cqr->magic, 4)) {
207     		printk (KERN_WARNING PRINTK_HEADER
208     			"0x%04X : /dev/%s (%d:%d)"
209     			" magic number of ccw_req_t 0x%08X doesn't match"
210     			" discipline 0x%08X\n",
211     			devno, device->name,
212     			MAJOR (device->kdev), MINOR (device->kdev),
213     			cqr->magic, *(int *) (&device->discipline->name));
214     		irq_exit(cpu, -1);
215     		return;
216     	}
217     	asm volatile ("STCK %0":"=m" (cqr->stopclk));
218     	switch (status) {
219     	case 0x00:
220     		check_then_set (&cqr->status,
221     				CQR_STATUS_IN_IO, CQR_STATUS_DONE);
222     		if (cqr->next && (cqr->next->status == CQR_STATUS_QUEUED)) {
223     			if (dasd_start_diag (cqr->next) == 0) {
224     				done_fast_io = 1;
225     			}
226     		}
227     		break;
228     	case 0x01:
229     	case 0x02:
230     	case 0x03:
231     	default:
232     		check_then_set (&cqr->status,
233     				CQR_STATUS_IN_IO, CQR_STATUS_FAILED);
234     		break;
235     	}
236     	wake_up (&device->wait_q);
237     	dasd_schedule_bh (device);
238     	irq_exit(cpu, -1);
239     }
240     
241     static int
242     dasd_diag_check_characteristics (struct dasd_device_t *device)
243     {
244     	int rc = 0;
245     	int bsize;
246     	int label_block;
247     	dasd_diag_private_t *private;
248     	dasd_diag_characteristics_t *rdc_data;
249     	ccw_req_t *cqr;
250     	long *label;
251     	int sb;
252     
253     	if (device == NULL) {
254     		printk (KERN_WARNING PRINTK_HEADER
255     			"Null device pointer passed to characteristics checker\n");
256     		return -ENODEV;
257     	}
258     	device->private = kmalloc (sizeof (dasd_diag_private_t), GFP_KERNEL);
259     	if (device->private == NULL) {
260     		printk (KERN_WARNING PRINTK_HEADER
261     			"memory allocation failed for private data\n");
262     		rc = -ENOMEM;
263                     goto fail;
264     	}
265     	private = (dasd_diag_private_t *) device->private;
266     	rdc_data = (void *) &(private->rdc_data);
267     
268     	rdc_data->dev_nr = device->devinfo.devno;
269     	rdc_data->rdc_len = sizeof (dasd_diag_characteristics_t);
270     
271             rc = dia210 (rdc_data);
272     	if ( rc != 0) {
273     		goto fail;
274     	}
275     	if (rdc_data->vdev_class != DEV_CLASS_FBA &&
276     	    rdc_data->vdev_class != DEV_CLASS_ECKD &&
277     	    rdc_data->vdev_class != DEV_CLASS_CKD) {
278                     rc = -ENOTSUPP;
279     		goto fail;
280     	}
281     #if 0
282     	printk (KERN_INFO PRINTK_HEADER
283     		"%04X: %04X on real %04X/%02X\n",
284     		rdc_data->dev_nr,
285     		rdc_data->vdev_type, rdc_data->rdev_type, rdc_data->rdev_model);
286     #endif
287     	/* Figure out position of label block */
288     	if (private->rdc_data.vdev_class == DEV_CLASS_FBA) {
289     		device->sizes.pt_block = 1;
290     	} else if (private->rdc_data.vdev_class == DEV_CLASS_ECKD ||
291     		   private->rdc_data.vdev_class == DEV_CLASS_CKD) {
292     		device->sizes.pt_block = 2;
293     	} else {
294                     BUG();
295     	}
296     	private->label = (long *) get_free_page (GFP_KERNEL);
297     	label = private->label;
298     	mdsk_term_io (device);	/* first terminate all outstanding operations */
299     	/* figure out blocksize of device */
300     	for (bsize = 512; bsize <= PAGE_SIZE; bsize <<= 1) {
301     		diag_bio_t *bio;
302     		diag_rw_io_t *iob = &private->iob;
303     
304     		rc = mdsk_init_io (device, bsize, 0, 64);
305     		if (rc > 4) {
306     			continue;
307     		}
308     		cqr = dasd_alloc_request (dasd_diag_discipline.name,
309                                              sizeof (diag_bio_t) / sizeof (ccw1_t),
310                                              0, device);
311     		if (cqr == NULL) {
312     			printk (KERN_WARNING PRINTK_HEADER
313     				"No memory to allocate initialization request\n");
314     			free_page ((long) private->label);
315                             rc = -ENOMEM;
316                             goto fail;
317     		}
318     		bio = (diag_bio_t *) (cqr->cpaddr);
319     		memset (bio, 0, sizeof (diag_bio_t));
320     		bio->type = MDSK_READ_REQ;
321     		bio->block_number = device->sizes.pt_block + 1;
322     		bio->buffer = __pa (private->label);
323     		cqr->device = device;
324     		cqr->status = CQR_STATUS_FILLED;
325     		memset (iob, 0, sizeof (diag_rw_io_t));
326     		iob->dev_nr = rdc_data->dev_nr;
327     		iob->block_count = 1;
328     		iob->interrupt_params = (u32) cqr;
329     		iob->bio_list = __pa (bio);
330     		rc = dia250 (iob, RW_BIO);
331     		if (rc == 0) {
332     			if (label[3] != bsize ||
333                                 label[0] != 0xc3d4e2f1 ||	/* != CMS1 */
334                                 label[13] == 0 ){
335                                     rc = -EMEDIUMTYPE;
336     				goto fail;
337     			}
338     			break;
339     		}
340     		mdsk_term_io (device);
341     	}
342             if (bsize > PAGE_SIZE) {
343                     rc = -EMEDIUMTYPE;
344     		goto fail;
345             }
346     	device->sizes.blocks = label[7];
347     	device->sizes.bp_block = bsize;
348     	device->sizes.s2b_shift = 0;	/* bits to shift 512 to get a block */
349     	for (sb = 512; sb < bsize; sb = sb << 1)
350     		device->sizes.s2b_shift++;
351     	printk (KERN_INFO PRINTK_HEADER
352     		"/dev/%s (%04X): capacity (%dkB blks): %ldkB\n",
353     		device->name, device->devinfo.devno,
354     		(device->sizes.bp_block >> 10),
355     		(device->sizes.blocks << device->sizes.s2b_shift) >> 1);
356     	free_page ((long) private->label);
357             rc = 0;
358     	goto out;
359      fail:
360             if ( rc ) {
361                     kfree (device->private);
362                     device->private = NULL;
363             }
364      out:
365     	return rc;
366     }
367     
368     static int
369     dasd_diag_fill_geometry (struct dasd_device_t *device, struct hd_geometry *geo)
370     {
371     	int rc = 0;
372     	dasd_diag_private_t *private = (dasd_diag_private_t *) device->private;
373     	unsigned long sectors = device->sizes.blocks << device->sizes.s2b_shift;
374     	unsigned long tracks = sectors >> 6;
375     	unsigned long trk_rem = sectors & ((1 << 6) - 1);	/* 64k per track */
376     	unsigned long cyls = tracks >> 4;
377     	unsigned long cyls_rem = tracks & ((1 << 4) - 1);	/* 16 head per cyl */
378     
379     	switch (device->sizes.bp_block) {
380     	case 512:
381     	case 1024:
382     	case 2048:
383     	case 4096:
384     		break;
385     	default:
386     		return -EINVAL;
387     	}
388     	geo->cylinders = cyls;
389     	geo->heads = 16;
390     	geo->sectors = 128 >> device->sizes.s2b_shift;
391     	return rc;
392     }
393     
394     static dasd_era_t
395     dasd_diag_examine_error (ccw_req_t * cqr, devstat_t * stat)
396     {
397     	return dasd_era_fatal;
398     }
399     
400     static ccw_req_t *
401     dasd_diag_build_cp_from_req (dasd_device_t * device, struct request *req)
402     {
403     	ccw_req_t *rw_cp = NULL;
404     	struct buffer_head *bh;
405     	int rw_cmd;
406     	int noblk = req->nr_sectors >> device->sizes.s2b_shift;
407     	int byt_per_blk = device->sizes.bp_block;
408     	int block;
409     	diag_bio_t *bio;
410     	int bhct;
411     	long size;
412     
413     	if (!noblk) {
414     		PRINT_ERR ("No blocks to read/write...returning\n");
415     		return NULL;
416     	}
417     	if (req->cmd == READ) {
418     		rw_cmd = MDSK_READ_REQ;
419     	} else if (req->cmd == WRITE) {
420     		rw_cmd = MDSK_WRITE_REQ;
421     	}
422     	bhct = 0;
423     	for (bh = req->bh; bh; bh = bh->b_reqnext) {
424     		if (bh->b_size < byt_per_blk)
425                             BUG();
426                     bhct += bh->b_size >> (device->sizes.s2b_shift+9);
427     	}
428     	/* Build the request */
429     	rw_cp = dasd_alloc_request (dasd_diag_discipline.name, bhct << 1, 0, device);
430     	if (!rw_cp) {
431     		return NULL;
432     	}
433     	bio = (diag_bio_t *) (rw_cp->cpaddr);
434     
435     	block = req->sector >> device->sizes.s2b_shift;
436     	for (bh = req->bh; bh; bh = bh->b_reqnext) {
437                     memset (bio, 0, sizeof (diag_bio_t));
438                     for (size = 0; size < bh->b_size; size += byt_per_blk) {
439                             bio->type = rw_cmd;
440                             bio->block_number = block + 1;
441                             bio->buffer = __pa (bh->b_data + size);
442                             bio++;
443                             block++;
444                     }
445     	}
446     	asm volatile ("STCK %0":"=m" (rw_cp->buildclk));
447     	rw_cp->device = device;
448     	rw_cp->expires = 50 * TOD_SEC;	/* 50 seconds */
449     	rw_cp->req = req;
450     	check_then_set (&rw_cp->status, CQR_STATUS_EMPTY, CQR_STATUS_FILLED);
451     	return rw_cp;
452     }
453     
454     static int
455     dasd_diag_fill_info (dasd_device_t * device, dasd_information_t * info)
456     {
457     	int rc = 0;
458     	info->FBA_layout = 1;
459     	info->characteristics_size = sizeof (dasd_diag_characteristics_t);
460     	memcpy (info->characteristics,
461     		&((dasd_diag_private_t *) device->private)->rdc_data,
462     		sizeof (dasd_diag_characteristics_t));
463     	info->confdata_size = 0;
464     	return rc;
465     }
466     
467     static char *
468     dasd_diag_dump_sense (struct dasd_device_t *device, ccw_req_t * req)
469     {
470     	char *page = (char *) get_free_page (GFP_KERNEL);
471     	int len;
472     	if (page == NULL) {
473     		return NULL;
474     	}
475     	len = sprintf (page, KERN_WARNING PRINTK_HEADER
476     		       "device %04X on irq %d: I/O status report:\n",
477     		       device->devinfo.devno, device->devinfo.irq);
478     
479     	return page;
480     }
481     
482     dasd_discipline_t dasd_diag_discipline = {
483             owner: THIS_MODULE,
484     	name:"DIAG",
485     	ebcname:"DIAG",
486     	max_blocks:PAGE_SIZE / sizeof (diag_bio_t),
487     	check_characteristics:dasd_diag_check_characteristics,
488     	fill_geometry:dasd_diag_fill_geometry,
489     	start_IO:dasd_start_diag,
490     	examine_error:dasd_diag_examine_error,
491     	build_cp_from_req:dasd_diag_build_cp_from_req,
492     	dump_sense:dasd_diag_dump_sense,
493     	int_handler:dasd_ext_handler,
494     	fill_info:dasd_diag_fill_info,
495     };
496     
497     int
498     dasd_diag_init (void)
499     {
500     	int rc = 0;
501     	if (MACHINE_IS_VM) {
502     		printk (KERN_INFO PRINTK_HEADER
503     			"%s discipline initializing\n",
504     			dasd_diag_discipline.name);
505     		ASCEBC (dasd_diag_discipline.ebcname, 4);
506     		ctl_set_bit (0, 9);
507     		register_external_interrupt (0x2603, dasd_ext_handler);
508     		dasd_discipline_add (&dasd_diag_discipline);
509     	} else {
510     		printk (KERN_INFO PRINTK_HEADER
511     			"Machine is not VM: %s discipline not initializing\n",
512     			dasd_diag_discipline.name);
513     		rc = -EINVAL;
514     	}
515     	return rc;
516     }
517     
518     void
519     dasd_diag_cleanup (void)
520     {
521     	if (MACHINE_IS_VM) {
522     		printk (KERN_INFO PRINTK_HEADER
523     			"%s discipline cleaning up\n",
524     			dasd_diag_discipline.name);
525     		dasd_discipline_del (&dasd_diag_discipline);
526     		unregister_external_interrupt (0x2603, dasd_ext_handler);
527     		ctl_clear_bit (0, 9);
528     	} else {
529     		printk (KERN_INFO PRINTK_HEADER
530     			"Machine is not VM: %s discipline not initializing\n",
531     			dasd_diag_discipline.name);
532     	}
533     }
534     
535     #ifdef MODULE
536     int
537     init_module (void)
538     {
539     	int rc = 0;
540     	rc = dasd_diag_init ();
541     	return rc;
542     }
543     
544     void
545     cleanup_module (void)
546     {
547     	dasd_diag_cleanup ();
548     	return;
549     }
550     #endif
551     
552     /*
553      * Overrides for Emacs so that we follow Linus's tabbing style.
554      * Emacs will notice this stuff at the end of the file and automatically
555      * adjust the settings for this buffer only.  This must remain at the end
556      * of the file.
557      * ---------------------------------------------------------------------------
558      * Local variables:
559      * c-indent-level: 4 
560      * c-brace-imaginary-offset: 0
561      * c-brace-offset: -4
562      * c-argdecl-indent: 4
563      * c-label-offset: -4
564      * c-continued-statement-offset: 4
565      * c-continued-brace-offset: 0
566      * indent-tabs-mode: nil
567      * tab-width: 8
568      * End:
569      */
570