dasd_diag.c 17.1 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8
/* 
 * 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
 *
9
 * $Revision: 1.53 $
L
Linus Torvalds 已提交
10 11 12 13 14 15
 */

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

#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>
28
#include <asm/vtoc.h>
L
Linus Torvalds 已提交
29 30 31 32 33 34 35 36

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

#define PRINTK_HEADER "dasd(diag):"

MODULE_LICENSE("GPL");

37 38 39 40 41 42 43 44 45 46 47
/* 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 已提交
48 49 50 51 52 53
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;
54
	blocknum_t pt_block;
L
Linus Torvalds 已提交
55 56 57
};

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

62 63 64 65 66 67
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 已提交
68 69 70
static __inline__ int
dia250(void *iob, int cmd)
{
71 72 73
	typedef union {
		struct dasd_diag_init_io init_io;
		struct dasd_diag_rw_io rw_io;
74
	} addr_type;
L
Linus Torvalds 已提交
75 76
	int rc;

77 78 79 80 81 82 83 84 85 86 87 88 89
	__asm__ __volatile__(
#ifdef CONFIG_ARCH_S390X
		"	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 已提交
90
#else
91 92 93 94 95 96 97 98 99 100 101
		"	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 已提交
102
#endif
103 104 105
		: "=&d" (rc), "=m" (*(addr_type *) iob)
		: "d" (cmd), "d" (iob), "m" (*(addr_type *) iob)
		: "0", "1", "cc");
L
Linus Torvalds 已提交
106 107 108
	return rc;
}

109 110 111 112
/* 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 已提交
113
static __inline__ int
114 115
mdsk_init_io(struct dasd_device *device, unsigned int blocksize,
	     blocknum_t offset, blocknum_t *end_block)
L
Linus Torvalds 已提交
116 117 118 119 120 121 122 123 124 125 126 127
{
	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;
128
	iib->flaga = DASD_DIAG_FLAGA_DEFAULT;
L
Linus Torvalds 已提交
129 130 131

	rc = dia250(iib, INIT_BIO);

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

	return rc;
L
Linus Torvalds 已提交
136 137
}

138 139
/* Remove block I/O environment for device. Return zero on success, non-zero
 * otherwise. */
L
Linus Torvalds 已提交
140 141 142 143 144 145 146 147 148 149 150 151
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);
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
	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 已提交
167 168
}

169 170
/* Start a given request at the device. Return zero on success, non-zero
 * otherwise. */
L
Linus Torvalds 已提交
171 172 173 174 175 176 177 178 179
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;
180 181 182 183 184 185
	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 已提交
186 187 188 189 190
	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;
191
	private->iob.flags = DASD_DIAG_RWFLAG_ASYNC;
L
Linus Torvalds 已提交
192
	private->iob.block_count = dreq->block_count;
193
	private->iob.interrupt_params = (addr_t) cqr;
194
	private->iob.bio_list = dreq->bio;
195
	private->iob.flaga = DASD_DIAG_FLAGA_DEFAULT;
L
Linus Torvalds 已提交
196 197

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

	rc = dia250(&private->iob, RW_BIO);
202 203 204
	switch (rc) {
	case 0: /* Synchronous I/O finished successfully */
		cqr->stopclk = get_clock();
L
Linus Torvalds 已提交
205
		cqr->status = DASD_CQR_DONE;
206 207 208 209 210
		/* 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 已提交
211 212
		cqr->status = DASD_CQR_IN_IO;
		rc = 0;
213 214 215 216 217 218 219
		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 已提交
220 221 222 223
	}
	return rc;
}

224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
/* 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 已提交
240 241 242 243 244 245 246
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;
247 248 249
	u8 int_code, status;
	addr_t ip;
	int rc;
L
Linus Torvalds 已提交
250

251 252 253 254 255 256 257 258 259 260 261 262
	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 已提交
263 264 265 266
	if (!ip) {		/* no intparm: unsolicited interrupt */
		MESSAGE(KERN_DEBUG, "%s", "caught unsolicited interrupt");
		return;
	}
267
	cqr = (struct dasd_ccw_req *) ip;
L
Linus Torvalds 已提交
268 269 270 271 272 273 274 275 276 277 278 279
	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);

280 281 282 283 284 285 286 287 288
	/* 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 已提交
289 290 291 292 293 294 295 296 297 298
	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) {
299 300
				rc = dasd_start_diag(next);
				if (rc == 0)
L
Linus Torvalds 已提交
301
					expires = next->expires;
302
				else if (rc != -EACCES)
L
Linus Torvalds 已提交
303 304 305 306 307
					DEV_MESSAGE(KERN_WARNING, device, "%s",
						    "Interrupt fastpath "
						    "failed!");
			}
		}
308 309 310 311 312 313 314
	} 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 已提交
315 316 317 318 319 320 321 322 323 324

	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);
}

325 326
/* Check whether device can be controlled by DIAG discipline. Return zero on
 * success, non-zero otherwise. */
L
Linus Torvalds 已提交
327 328 329 330 331 332
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;
333
	struct vtoc_cms_label *label;
334 335
	blocknum_t end_block;
	unsigned int sb, bsize;
L
Linus Torvalds 已提交
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
	int rc;

	private = (struct dasd_diag_private *) device->private;
	if (private == NULL) {
		private = kmalloc(sizeof(struct dasd_diag_private),GFP_KERNEL);
		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);
354 355 356
	if (rc) {
		DEV_MESSAGE(KERN_WARNING, device, "failed to retrieve device "
			    "information (rc=%d)", rc);
L
Linus Torvalds 已提交
357
		return -ENOTSUPP;
358
	}
L
Linus Torvalds 已提交
359 360 361 362 363 364 365 366 367 368

	/* 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:
369 370
		DEV_MESSAGE(KERN_WARNING, device, "unsupported device class "
			    "(class=%d)", private->rdc_data.vdev_class);
L
Linus Torvalds 已提交
371 372 373 374 375 376 377 378 379 380 381 382 383
		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 */
384
	label = (struct vtoc_cms_label *) get_zeroed_page(GFP_KERNEL);
L
Linus Torvalds 已提交
385 386 387 388 389
	if (label == NULL)  {
		DEV_MESSAGE(KERN_WARNING, device, "%s",
			    "No memory to allocate initialization request");
		return -ENOMEM;
	}
390 391
	rc = 0;
	end_block = 0;
L
Linus Torvalds 已提交
392 393
	/* try all sizes - needed for ECKD devices */
	for (bsize = 512; bsize <= PAGE_SIZE; bsize <<= 1) {
394
		mdsk_init_io(device, bsize, 0, &end_block);
L
Linus Torvalds 已提交
395 396 397
		memset(&bio, 0, sizeof (struct dasd_diag_bio));
		bio.type = MDSK_READ_REQ;
		bio.block_number = private->pt_block + 1;
398
		bio.buffer = label;
L
Linus Torvalds 已提交
399 400 401 402 403 404
		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;
405
		private->iob.bio_list = &bio;
406 407
		private->iob.flaga = DASD_DIAG_FLAGA_DEFAULT;
		rc = dia250(&private->iob, RW_BIO);
408 409 410 411 412 413
		if (rc == 3) {
			DEV_MESSAGE(KERN_WARNING, device, "%s",
				"DIAG call failed");
			rc = -EOPNOTSUPP;
			goto out;
		}
L
Linus Torvalds 已提交
414
		mdsk_term_io(device);
415 416
		if (rc == 0)
			break;
L
Linus Torvalds 已提交
417
	}
418
	if (bsize > PAGE_SIZE) {
419 420 421
		DEV_MESSAGE(KERN_WARNING, device, "device access failed "
			    "(rc=%d)", rc);
		rc = -EIO;
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
		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;
441
	} else {
L
Linus Torvalds 已提交
442
		DEV_MESSAGE(KERN_INFO, device,
443 444 445 446
			    "(%ld B/blk): %ldkB",
			    (unsigned long) device->bp_block,
			    (unsigned long) (device->blocks <<
				device->s2b_shift) >> 1);
L
Linus Torvalds 已提交
447
	}
448
out:
L
Linus Torvalds 已提交
449 450 451 452
	free_page((long) label);
	return rc;
}

453 454
/* Fill in virtual disk geometry for device. Return zero on success, non-zero
 * otherwise. */
L
Linus Torvalds 已提交
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 482 483
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;
}

484 485
/* Create DASD request from block device request. Return pointer to new
 * request on success, ERR_PTR otherwise. */
L
Linus Torvalds 已提交
486 487 488 489 490 491 492 493 494
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;
495
	unsigned int count, datasize;
L
Linus Torvalds 已提交
496
	sector_t recid, first_rec, last_rec;
497
	unsigned int blksize, off;
L
Linus Torvalds 已提交
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 529 530 531 532 533 534 535 536 537 538 539 540 541 542
	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;
	
	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;
543
				dbio->buffer = dst;
L
Linus Torvalds 已提交
544 545 546 547 548 549
				dbio++;
				dst += blksize;
				recid++;
			}
		}
	}
550
	cqr->retries = DIAG_MAX_RETRIES;
L
Linus Torvalds 已提交
551
	cqr->buildclk = get_clock();
552 553
	if (req->flags & REQ_FAILFAST)
		set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
L
Linus Torvalds 已提交
554
	cqr->device = device;
555
	cqr->expires = DIAG_TIMEOUT;
L
Linus Torvalds 已提交
556 557 558 559
	cqr->status = DASD_CQR_FILLED;
	return cqr;
}

560 561
/* Release DASD request. Return non-zero if request was successful, zero
 * otherwise. */
L
Linus Torvalds 已提交
562 563 564 565 566 567 568 569 570 571
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;
}

572
/* Fill in IOCTL data for device. */
L
Linus Torvalds 已提交
573 574 575 576 577 578 579
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;
580
	info->label_block = (unsigned int) private->pt_block;
L
Linus Torvalds 已提交
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
	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",
603
	.max_blocks = DIAG_MAX_BLOCKS,
L
Linus Torvalds 已提交
604 605 606
	.check_device = dasd_diag_check_device,
	.fill_geometry = dasd_diag_fill_geometry,
	.start_IO = dasd_start_diag,
607
	.term_IO = dasd_diag_term_IO,
L
Linus Torvalds 已提交
608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
	.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);
625
		return -ENODEV;
L
Linus Torvalds 已提交
626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
	}
	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);