dasd_diag.c 17.1 KB
Newer Older
1
/*
L
Linus Torvalds 已提交
2 3 4 5 6 7 8 9 10 11 12 13
 * File...........: linux/drivers/s390/block/dasd_diag.c
 * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
 * Based on.......: linux/drivers/s390/block/mdisk.c
 * ...............: by Hartmunt Penner <hpenner@de.ibm.com>
 * Bugreports.to..: <Linux390@de.ibm.com>
 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
 *
 */

#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/slab.h>
14
#include <linux/hdreg.h>
L
Linus Torvalds 已提交
15 16 17
#include <linux/bio.h>
#include <linux/module.h>
#include <linux/init.h>
18
#include <linux/jiffies.h>
L
Linus Torvalds 已提交
19 20 21 22 23 24 25

#include <asm/dasd.h>
#include <asm/debug.h>
#include <asm/ebcdic.h>
#include <asm/io.h>
#include <asm/s390_ext.h>
#include <asm/todclk.h>
26
#include <asm/vtoc.h>
L
Linus Torvalds 已提交
27 28 29 30 31 32 33 34

#include "dasd_int.h"
#include "dasd_diag.h"

#define PRINTK_HEADER "dasd(diag):"

MODULE_LICENSE("GPL");

35 36 37 38 39 40 41 42 43 44 45
/* The maximum number of blocks per request (max_blocks) is dependent on the
 * amount of storage that is available in the static I/O buffer for each
 * device. Currently each device gets 2 pages. We want to fit two requests
 * into the available memory so that we can immediately start the next if one
 * finishes. */
#define DIAG_MAX_BLOCKS	(((2 * PAGE_SIZE - sizeof(struct dasd_ccw_req) - \
			   sizeof(struct dasd_diag_req)) / \
		           sizeof(struct dasd_diag_bio)) / 2)
#define DIAG_MAX_RETRIES	32
#define DIAG_TIMEOUT		50 * HZ

L
Linus Torvalds 已提交
46 47 48 49 50 51
struct dasd_discipline dasd_diag_discipline;

struct dasd_diag_private {
	struct dasd_diag_characteristics rdc_data;
	struct dasd_diag_rw_io iob;
	struct dasd_diag_init_io iib;
52
	blocknum_t pt_block;
L
Linus Torvalds 已提交
53 54 55
};

struct dasd_diag_req {
56
	unsigned int block_count;
L
Linus Torvalds 已提交
57 58 59
	struct dasd_diag_bio bio[0];
};

60 61 62 63 64 65
static const u8 DASD_DIAG_CMS1[] = { 0xc3, 0xd4, 0xe2, 0xf1 };/* EBCDIC CMS1 */

/* Perform DIAG250 call with block I/O parameter list iob (input and output)
 * and function code cmd.
 * In case of an exception return 3. Otherwise return result of bitwise OR of
 * resulting condition code and DIAG return code. */
L
Linus Torvalds 已提交
66 67 68
static __inline__ int
dia250(void *iob, int cmd)
{
69 70 71
	typedef union {
		struct dasd_diag_init_io init_io;
		struct dasd_diag_rw_io rw_io;
72
	} addr_type;
L
Linus Torvalds 已提交
73 74
	int rc;

75
	__asm__ __volatile__(
76
#ifdef CONFIG_64BIT
77 78 79 80 81 82 83 84 85 86 87
		"	lghi	%0,3\n"
		"	lgr	0,%3\n"
		"	diag	0,%2,0x250\n"
		"0:	ipm	%0\n"
		"	srl	%0,28\n"
		"	or	%0,1\n"
		"1:\n"
		".section __ex_table,\"a\"\n"
		"	.align 8\n"
		"	.quad  0b,1b\n"
		".previous\n"
L
Linus Torvalds 已提交
88
#else
89 90 91 92 93 94 95 96 97 98 99
		"	lhi	%0,3\n"
		"	lr	0,%3\n"
		"	diag	0,%2,0x250\n"
		"0:	ipm	%0\n"
		"	srl	%0,28\n"
		"	or	%0,1\n"
		"1:\n"
		".section __ex_table,\"a\"\n"
		"	.align 4\n"
		"	.long 0b,1b\n"
		".previous\n"
L
Linus Torvalds 已提交
100
#endif
101 102 103
		: "=&d" (rc), "=m" (*(addr_type *) iob)
		: "d" (cmd), "d" (iob), "m" (*(addr_type *) iob)
		: "0", "1", "cc");
L
Linus Torvalds 已提交
104 105 106
	return rc;
}

107 108 109 110
/* Initialize block I/O to DIAG device using the specified blocksize and
 * block offset. On success, return zero and set end_block to contain the
 * number of blocks on the device minus the specified offset. Return non-zero
 * otherwise. */
L
Linus Torvalds 已提交
111
static __inline__ int
112 113
mdsk_init_io(struct dasd_device *device, unsigned int blocksize,
	     blocknum_t offset, blocknum_t *end_block)
L
Linus Torvalds 已提交
114 115 116 117 118 119 120 121 122 123 124 125
{
	struct dasd_diag_private *private;
	struct dasd_diag_init_io *iib;
	int rc;

	private = (struct dasd_diag_private *) device->private;
	iib = &private->iib;
	memset(iib, 0, sizeof (struct dasd_diag_init_io));

	iib->dev_nr = _ccw_device_get_device_number(device->cdev);
	iib->block_size = blocksize;
	iib->offset = offset;
126
	iib->flaga = DASD_DIAG_FLAGA_DEFAULT;
L
Linus Torvalds 已提交
127 128 129

	rc = dia250(iib, INIT_BIO);

130 131 132 133
	if ((rc & 3) == 0 && end_block)
		*end_block = iib->end_block;

	return rc;
L
Linus Torvalds 已提交
134 135
}

136 137
/* Remove block I/O environment for device. Return zero on success, non-zero
 * otherwise. */
L
Linus Torvalds 已提交
138 139 140 141 142 143 144 145 146 147 148 149
static __inline__ int
mdsk_term_io(struct dasd_device * device)
{
	struct dasd_diag_private *private;
	struct dasd_diag_init_io *iib;
	int rc;

	private = (struct dasd_diag_private *) device->private;
	iib = &private->iib;
	memset(iib, 0, sizeof (struct dasd_diag_init_io));
	iib->dev_nr = _ccw_device_get_device_number(device->cdev);
	rc = dia250(iib, TERM_BIO);
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
	return rc;
}

/* Error recovery for failed DIAG requests - try to reestablish the DIAG
 * environment. */
static void
dasd_diag_erp(struct dasd_device *device)
{
	int rc;

	mdsk_term_io(device);
	rc = mdsk_init_io(device, device->bp_block, 0, NULL);
	if (rc)
		DEV_MESSAGE(KERN_WARNING, device, "DIAG ERP unsuccessful, "
			    "rc=%d", rc);
L
Linus Torvalds 已提交
165 166
}

167 168
/* Start a given request at the device. Return zero on success, non-zero
 * otherwise. */
L
Linus Torvalds 已提交
169 170 171 172 173 174 175 176 177
static int
dasd_start_diag(struct dasd_ccw_req * cqr)
{
	struct dasd_device *device;
	struct dasd_diag_private *private;
	struct dasd_diag_req *dreq;
	int rc;

	device = cqr->device;
178 179 180 181 182 183
	if (cqr->retries < 0) {
		DEV_MESSAGE(KERN_WARNING, device, "DIAG start_IO: request %p "
			    "- no retry left)", cqr);
		cqr->status = DASD_CQR_FAILED;
		return -EIO;
	}
L
Linus Torvalds 已提交
184 185 186 187 188
	private = (struct dasd_diag_private *) device->private;
	dreq = (struct dasd_diag_req *) cqr->data;

	private->iob.dev_nr = _ccw_device_get_device_number(device->cdev);
	private->iob.key = 0;
189
	private->iob.flags = DASD_DIAG_RWFLAG_ASYNC;
L
Linus Torvalds 已提交
190
	private->iob.block_count = dreq->block_count;
191
	private->iob.interrupt_params = (addr_t) cqr;
192
	private->iob.bio_list = dreq->bio;
193
	private->iob.flaga = DASD_DIAG_FLAGA_DEFAULT;
L
Linus Torvalds 已提交
194 195

	cqr->startclk = get_clock();
196 197
	cqr->starttime = jiffies;
	cqr->retries--;
L
Linus Torvalds 已提交
198 199

	rc = dia250(&private->iob, RW_BIO);
200 201 202
	switch (rc) {
	case 0: /* Synchronous I/O finished successfully */
		cqr->stopclk = get_clock();
L
Linus Torvalds 已提交
203
		cqr->status = DASD_CQR_DONE;
204 205 206 207 208
		/* Indicate to calling function that only a dasd_schedule_bh()
		   and no timer is needed */
                rc = -EACCES;
		break;
	case 8: /* Asynchronous I/O was started */
L
Linus Torvalds 已提交
209 210
		cqr->status = DASD_CQR_IN_IO;
		rc = 0;
211 212 213 214 215 216 217
		break;
	default: /* Error condition */
		cqr->status = DASD_CQR_QUEUED;
		DEV_MESSAGE(KERN_WARNING, device, "dia250 returned rc=%d", rc);
		dasd_diag_erp(device);
		rc = -EIO;
		break;
L
Linus Torvalds 已提交
218 219 220 221
	}
	return rc;
}

222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
/* Terminate given request at the device. */
static int
dasd_diag_term_IO(struct dasd_ccw_req * cqr)
{
	struct dasd_device *device;

	device = cqr->device;
	mdsk_term_io(device);
	mdsk_init_io(device, device->bp_block, 0, NULL);
	cqr->status = DASD_CQR_CLEAR;
	cqr->stopclk = get_clock();
	dasd_schedule_bh(device);
	return 0;
}

/* Handle external interruption. */
L
Linus Torvalds 已提交
238 239 240 241 242 243 244
static void
dasd_ext_handler(struct pt_regs *regs, __u16 code)
{
	struct dasd_ccw_req *cqr, *next;
	struct dasd_device *device;
	unsigned long long expires;
	unsigned long flags;
245 246 247
	u8 int_code, status;
	addr_t ip;
	int rc;
L
Linus Torvalds 已提交
248

249 250 251 252 253 254 255 256 257 258 259 260
	int_code = *((u8 *) DASD_DIAG_LC_INT_CODE);
	status = *((u8 *) DASD_DIAG_LC_INT_STATUS);
	switch (int_code) {
	case DASD_DIAG_CODE_31BIT:
		ip = (addr_t) *((u32 *) DASD_DIAG_LC_INT_PARM_31BIT);
		break;
	case DASD_DIAG_CODE_64BIT:
		ip = (addr_t) *((u64 *) DASD_DIAG_LC_INT_PARM_64BIT);
		break;
	default:
		return;
	}
L
Linus Torvalds 已提交
261 262 263 264
	if (!ip) {		/* no intparm: unsolicited interrupt */
		MESSAGE(KERN_DEBUG, "%s", "caught unsolicited interrupt");
		return;
	}
265
	cqr = (struct dasd_ccw_req *) ip;
L
Linus Torvalds 已提交
266 267 268 269 270 271 272 273 274 275 276 277
	device = (struct dasd_device *) cqr->device;
	if (strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
		DEV_MESSAGE(KERN_WARNING, device,
			    " magic number of dasd_ccw_req 0x%08X doesn't"
			    " match discipline 0x%08X",
			    cqr->magic, *(int *) (&device->discipline->name));
		return;
	}

	/* get irq lock to modify request queue */
	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);

278 279 280 281 282 283 284 285 286
	/* Check for a pending clear operation */
	if (cqr->status == DASD_CQR_CLEAR) {
		cqr->status = DASD_CQR_QUEUED;
		dasd_clear_timer(device);
		dasd_schedule_bh(device);
		spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
		return;
	}

L
Linus Torvalds 已提交
287 288 289 290 291 292 293 294 295 296
	cqr->stopclk = get_clock();

	expires = 0;
	if (status == 0) {
		cqr->status = DASD_CQR_DONE;
		/* Start first request on queue if possible -> fast_io. */
		if (!list_empty(&device->ccw_queue)) {
			next = list_entry(device->ccw_queue.next,
					  struct dasd_ccw_req, list);
			if (next->status == DASD_CQR_QUEUED) {
297 298
				rc = dasd_start_diag(next);
				if (rc == 0)
L
Linus Torvalds 已提交
299
					expires = next->expires;
300
				else if (rc != -EACCES)
L
Linus Torvalds 已提交
301 302 303 304 305
					DEV_MESSAGE(KERN_WARNING, device, "%s",
						    "Interrupt fastpath "
						    "failed!");
			}
		}
306 307 308 309 310 311 312
	} else {
		cqr->status = DASD_CQR_QUEUED;
		DEV_MESSAGE(KERN_WARNING, device, "interrupt status for "
			    "request %p was %d (%d retries left)", cqr, status,
			    cqr->retries);
		dasd_diag_erp(device);
	}
L
Linus Torvalds 已提交
313 314 315 316 317 318 319 320 321 322

	if (expires != 0)
		dasd_set_timer(device, expires);
	else
		dasd_clear_timer(device);
	dasd_schedule_bh(device);

	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
}

323 324
/* Check whether device can be controlled by DIAG discipline. Return zero on
 * success, non-zero otherwise. */
L
Linus Torvalds 已提交
325 326 327 328 329 330
static int
dasd_diag_check_device(struct dasd_device *device)
{
	struct dasd_diag_private *private;
	struct dasd_diag_characteristics *rdc_data;
	struct dasd_diag_bio bio;
331
	struct vtoc_cms_label *label;
332 333
	blocknum_t end_block;
	unsigned int sb, bsize;
L
Linus Torvalds 已提交
334 335 336 337
	int rc;

	private = (struct dasd_diag_private *) device->private;
	if (private == NULL) {
338
		private = kzalloc(sizeof(struct dasd_diag_private),GFP_KERNEL);
L
Linus Torvalds 已提交
339 340 341 342 343 344 345 346 347 348 349 350 351
		if (private == NULL) {
			DEV_MESSAGE(KERN_WARNING, device, "%s",
				"memory allocation failed for private data");
			return -ENOMEM;
		}
		device->private = (void *) private;
	}
	/* Read Device Characteristics */
	rdc_data = (void *) &(private->rdc_data);
	rdc_data->dev_nr = _ccw_device_get_device_number(device->cdev);
	rdc_data->rdc_len = sizeof (struct dasd_diag_characteristics);

	rc = diag210((struct diag210 *) rdc_data);
352 353 354
	if (rc) {
		DEV_MESSAGE(KERN_WARNING, device, "failed to retrieve device "
			    "information (rc=%d)", rc);
L
Linus Torvalds 已提交
355
		return -ENOTSUPP;
356
	}
L
Linus Torvalds 已提交
357 358 359 360 361 362 363 364 365 366

	/* Figure out position of label block */
	switch (private->rdc_data.vdev_class) {
	case DEV_CLASS_FBA:
		private->pt_block = 1;
		break;
	case DEV_CLASS_ECKD:
		private->pt_block = 2;
		break;
	default:
367 368
		DEV_MESSAGE(KERN_WARNING, device, "unsupported device class "
			    "(class=%d)", private->rdc_data.vdev_class);
L
Linus Torvalds 已提交
369 370 371 372 373 374 375 376 377 378 379 380 381
		return -ENOTSUPP;
	}

	DBF_DEV_EVENT(DBF_INFO, device,
		      "%04X: %04X on real %04X/%02X",
		      rdc_data->dev_nr,
		      rdc_data->vdev_type,
		      rdc_data->rdev_type, rdc_data->rdev_model);

	/* terminate all outstanding operations */
	mdsk_term_io(device);

	/* figure out blocksize of device */
382
	label = (struct vtoc_cms_label *) get_zeroed_page(GFP_KERNEL);
L
Linus Torvalds 已提交
383 384 385 386 387
	if (label == NULL)  {
		DEV_MESSAGE(KERN_WARNING, device, "%s",
			    "No memory to allocate initialization request");
		return -ENOMEM;
	}
388 389
	rc = 0;
	end_block = 0;
L
Linus Torvalds 已提交
390 391
	/* try all sizes - needed for ECKD devices */
	for (bsize = 512; bsize <= PAGE_SIZE; bsize <<= 1) {
392
		mdsk_init_io(device, bsize, 0, &end_block);
L
Linus Torvalds 已提交
393 394 395
		memset(&bio, 0, sizeof (struct dasd_diag_bio));
		bio.type = MDSK_READ_REQ;
		bio.block_number = private->pt_block + 1;
396
		bio.buffer = label;
L
Linus Torvalds 已提交
397 398 399 400 401 402
		memset(&private->iob, 0, sizeof (struct dasd_diag_rw_io));
		private->iob.dev_nr = rdc_data->dev_nr;
		private->iob.key = 0;
		private->iob.flags = 0;	/* do synchronous io */
		private->iob.block_count = 1;
		private->iob.interrupt_params = 0;
403
		private->iob.bio_list = &bio;
404 405
		private->iob.flaga = DASD_DIAG_FLAGA_DEFAULT;
		rc = dia250(&private->iob, RW_BIO);
406 407 408 409 410 411
		if (rc == 3) {
			DEV_MESSAGE(KERN_WARNING, device, "%s",
				"DIAG call failed");
			rc = -EOPNOTSUPP;
			goto out;
		}
L
Linus Torvalds 已提交
412
		mdsk_term_io(device);
413 414
		if (rc == 0)
			break;
L
Linus Torvalds 已提交
415
	}
416
	if (bsize > PAGE_SIZE) {
417 418 419
		DEV_MESSAGE(KERN_WARNING, device, "device access failed "
			    "(rc=%d)", rc);
		rc = -EIO;
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
		goto out;
	}
	/* check for label block */
	if (memcmp(label->label_id, DASD_DIAG_CMS1,
		  sizeof(DASD_DIAG_CMS1)) == 0) {
		/* get formatted blocksize from label block */
		bsize = (unsigned int) label->block_size;
		device->blocks = (unsigned long) label->block_count;
	} else
		device->blocks = end_block;
	device->bp_block = bsize;
	device->s2b_shift = 0;	/* bits to shift 512 to get a block */
	for (sb = 512; sb < bsize; sb = sb << 1)
		device->s2b_shift++;
	rc = mdsk_init_io(device, device->bp_block, 0, NULL);
	if (rc) {
		DEV_MESSAGE(KERN_WARNING, device, "DIAG initialization "
			"failed (rc=%d)", rc);
		rc = -EIO;
439
	} else {
L
Linus Torvalds 已提交
440
		DEV_MESSAGE(KERN_INFO, device,
441 442 443 444
			    "(%ld B/blk): %ldkB",
			    (unsigned long) device->bp_block,
			    (unsigned long) (device->blocks <<
				device->s2b_shift) >> 1);
L
Linus Torvalds 已提交
445
	}
446
out:
L
Linus Torvalds 已提交
447 448 449 450
	free_page((long) label);
	return rc;
}

451 452
/* Fill in virtual disk geometry for device. Return zero on success, non-zero
 * otherwise. */
L
Linus Torvalds 已提交
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
static int
dasd_diag_fill_geometry(struct dasd_device *device, struct hd_geometry *geo)
{
	if (dasd_check_blocksize(device->bp_block) != 0)
		return -EINVAL;
	geo->cylinders = (device->blocks << device->s2b_shift) >> 10;
	geo->heads = 16;
	geo->sectors = 128 >> device->s2b_shift;
	return 0;
}

static dasd_era_t
dasd_diag_examine_error(struct dasd_ccw_req * cqr, struct irb * stat)
{
	return dasd_era_fatal;
}

static dasd_erp_fn_t
dasd_diag_erp_action(struct dasd_ccw_req * cqr)
{
	return dasd_default_erp_action;
}

static dasd_erp_fn_t
dasd_diag_erp_postaction(struct dasd_ccw_req * cqr)
{
	return dasd_default_erp_postaction;
}

482 483
/* Create DASD request from block device request. Return pointer to new
 * request on success, ERR_PTR otherwise. */
L
Linus Torvalds 已提交
484 485 486 487 488 489 490 491 492
static struct dasd_ccw_req *
dasd_diag_build_cp(struct dasd_device * device, struct request *req)
{
	struct dasd_ccw_req *cqr;
	struct dasd_diag_req *dreq;
	struct dasd_diag_bio *dbio;
	struct bio *bio;
	struct bio_vec *bv;
	char *dst;
493
	unsigned int count, datasize;
L
Linus Torvalds 已提交
494
	sector_t recid, first_rec, last_rec;
495
	unsigned int blksize, off;
L
Linus Torvalds 已提交
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
	unsigned char rw_cmd;
	int i;

	if (rq_data_dir(req) == READ)
		rw_cmd = MDSK_READ_REQ;
	else if (rq_data_dir(req) == WRITE)
		rw_cmd = MDSK_WRITE_REQ;
	else
		return ERR_PTR(-EINVAL);
	blksize = device->bp_block;
	/* Calculate record id of first and last block. */
	first_rec = req->sector >> device->s2b_shift;
	last_rec = (req->sector + req->nr_sectors - 1) >> device->s2b_shift;
	/* Check struct bio and count the number of blocks for the request. */
	count = 0;
	rq_for_each_bio(bio, req) {
		bio_for_each_segment(bv, bio, i) {
			if (bv->bv_len & (blksize - 1))
				/* Fba can only do full blocks. */
				return ERR_PTR(-EINVAL);
			count += bv->bv_len >> (device->s2b_shift + 9);
		}
	}
	/* Paranoia. */
	if (count != last_rec - first_rec + 1)
		return ERR_PTR(-EINVAL);
	/* Build the request */
	datasize = sizeof(struct dasd_diag_req) +
		count*sizeof(struct dasd_diag_bio);
	cqr = dasd_smalloc_request(dasd_diag_discipline.name, 0,
				   datasize, device);
	if (IS_ERR(cqr))
		return cqr;
529

L
Linus Torvalds 已提交
530 531 532 533 534 535 536 537 538 539 540
	dreq = (struct dasd_diag_req *) cqr->data;
	dreq->block_count = count;
	dbio = dreq->bio;
	recid = first_rec;
	rq_for_each_bio(bio, req) {
		bio_for_each_segment(bv, bio, i) {
			dst = page_address(bv->bv_page) + bv->bv_offset;
			for (off = 0; off < bv->bv_len; off += blksize) {
				memset(dbio, 0, sizeof (struct dasd_diag_bio));
				dbio->type = rw_cmd;
				dbio->block_number = recid + 1;
541
				dbio->buffer = dst;
L
Linus Torvalds 已提交
542 543 544 545 546 547
				dbio++;
				dst += blksize;
				recid++;
			}
		}
	}
548
	cqr->retries = DIAG_MAX_RETRIES;
L
Linus Torvalds 已提交
549
	cqr->buildclk = get_clock();
550 551
	if (req->flags & REQ_FAILFAST)
		set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
L
Linus Torvalds 已提交
552
	cqr->device = device;
553
	cqr->expires = DIAG_TIMEOUT;
L
Linus Torvalds 已提交
554 555 556 557
	cqr->status = DASD_CQR_FILLED;
	return cqr;
}

558 559
/* Release DASD request. Return non-zero if request was successful, zero
 * otherwise. */
L
Linus Torvalds 已提交
560 561 562 563 564 565 566 567 568 569
static int
dasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req)
{
	int status;

	status = cqr->status == DASD_CQR_DONE;
	dasd_sfree_request(cqr, cqr->device);
	return status;
}

570
/* Fill in IOCTL data for device. */
L
Linus Torvalds 已提交
571 572 573 574 575 576 577
static int
dasd_diag_fill_info(struct dasd_device * device,
		    struct dasd_information2_t * info)
{
	struct dasd_diag_private *private;

	private = (struct dasd_diag_private *) device->private;
578
	info->label_block = (unsigned int) private->pt_block;
L
Linus Torvalds 已提交
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600
	info->FBA_layout = 1;
	info->format = DASD_FORMAT_LDL;
	info->characteristics_size = sizeof (struct dasd_diag_characteristics);
	memcpy(info->characteristics,
	       &((struct dasd_diag_private *) device->private)->rdc_data,
	       sizeof (struct dasd_diag_characteristics));
	info->confdata_size = 0;
	return 0;
}

static void
dasd_diag_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
		     struct irb *stat)
{
	DEV_MESSAGE(KERN_ERR, device, "%s",
		    "dump sense not available for DIAG data");
}

struct dasd_discipline dasd_diag_discipline = {
	.owner = THIS_MODULE,
	.name = "DIAG",
	.ebcname = "DIAG",
601
	.max_blocks = DIAG_MAX_BLOCKS,
L
Linus Torvalds 已提交
602 603 604
	.check_device = dasd_diag_check_device,
	.fill_geometry = dasd_diag_fill_geometry,
	.start_IO = dasd_start_diag,
605
	.term_IO = dasd_diag_term_IO,
L
Linus Torvalds 已提交
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
	.examine_error = dasd_diag_examine_error,
	.erp_action = dasd_diag_erp_action,
	.erp_postaction = dasd_diag_erp_postaction,
	.build_cp = dasd_diag_build_cp,
	.free_cp = dasd_diag_free_cp,
	.dump_sense = dasd_diag_dump_sense,
	.fill_info = dasd_diag_fill_info,
};

static int __init
dasd_diag_init(void)
{
	if (!MACHINE_IS_VM) {
		MESSAGE_LOG(KERN_INFO,
			    "Machine is not VM: %s "
			    "discipline not initializing",
			    dasd_diag_discipline.name);
623
		return -ENODEV;
L
Linus Torvalds 已提交
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
	}
	ASCEBC(dasd_diag_discipline.ebcname, 4);

	ctl_set_bit(0, 9);
	register_external_interrupt(0x2603, dasd_ext_handler);
	dasd_diag_discipline_pointer = &dasd_diag_discipline;
	return 0;
}

static void __exit
dasd_diag_cleanup(void)
{
	unregister_external_interrupt(0x2603, dasd_ext_handler);
	ctl_clear_bit(0, 9);
	dasd_diag_discipline_pointer = NULL;
}

module_init(dasd_diag_init);
module_exit(dasd_diag_cleanup);