[PATCH] s390: 64 bit diag250 support

Add support for diag 250 access to dasd devices for 64 bit kernels.  In
addition fix detach/attach for diag disks.  The VM control block needs to get
recreated by a call to mdsk_init_io.

Signed-off-by: Horst Hummel <horst.hummel@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
Horst Hummel 2005-09-03 15:58:00 -07:00 committed by Linus Torvalds
parent c6eb7b7703
commit fd49f41aa0
3 changed files with 306 additions and 133 deletions

View File

@ -49,7 +49,7 @@ config DASD_FBA
config DASD_DIAG config DASD_DIAG
tristate "Support for DIAG access to Disks" tristate "Support for DIAG access to Disks"
depends on DASD && ARCH_S390X = 'n' depends on DASD && ( ARCH_S390X = 'n' || EXPERIMENTAL)
help help
Select this option if you want to use Diagnose250 command to access Select this option if you want to use Diagnose250 command to access
Disks under VM. If you are not running under VM or unsure what it is, Disks under VM. If you are not running under VM or unsure what it is,

View File

@ -6,17 +6,18 @@
* Bugreports.to..: <Linux390@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
* *
* $Revision: 1.42 $ * $Revision: 1.49 $
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/stddef.h> #include <linux/stddef.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/hdreg.h> /* HDIO_GETGEO */ #include <linux/hdreg.h>
#include <linux/bio.h> #include <linux/bio.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/jiffies.h>
#include <asm/dasd.h> #include <asm/dasd.h>
#include <asm/debug.h> #include <asm/debug.h>
@ -28,58 +29,89 @@
#include "dasd_int.h" #include "dasd_int.h"
#include "dasd_diag.h" #include "dasd_diag.h"
#ifdef PRINTK_HEADER
#undef PRINTK_HEADER
#endif /* PRINTK_HEADER */
#define PRINTK_HEADER "dasd(diag):" #define PRINTK_HEADER "dasd(diag):"
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
/* 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
struct dasd_discipline dasd_diag_discipline; struct dasd_discipline dasd_diag_discipline;
struct dasd_diag_private { struct dasd_diag_private {
struct dasd_diag_characteristics rdc_data; struct dasd_diag_characteristics rdc_data;
struct dasd_diag_rw_io iob; struct dasd_diag_rw_io iob;
struct dasd_diag_init_io iib; struct dasd_diag_init_io iib;
unsigned int pt_block; blocknum_t pt_block;
}; };
struct dasd_diag_req { struct dasd_diag_req {
int block_count; unsigned int block_count;
struct dasd_diag_bio bio[0]; struct dasd_diag_bio bio[0];
}; };
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. */
static __inline__ int static __inline__ int
dia250(void *iob, int cmd) dia250(void *iob, int cmd)
{ {
typedef struct {
char _[max(sizeof (struct dasd_diag_init_io),
sizeof (struct dasd_diag_rw_io))];
} addr_type;
int rc; int rc;
__asm__ __volatile__(" lhi %0,3\n" __asm__ __volatile__(
" lr 0,%2\n" #ifdef CONFIG_ARCH_S390X
" diag 0,%1,0x250\n" " lghi %0,3\n"
"0: ipm %0\n" " lgr 0,%3\n"
" srl %0,28\n" " diag 0,%2,0x250\n"
" or %0,1\n" "0: ipm %0\n"
"1:\n" " srl %0,28\n"
#ifndef CONFIG_ARCH_S390X " or %0,1\n"
".section __ex_table,\"a\"\n" "1:\n"
" .align 4\n" ".section __ex_table,\"a\"\n"
" .long 0b,1b\n" " .align 8\n"
".previous\n" " .quad 0b,1b\n"
".previous\n"
#else #else
".section __ex_table,\"a\"\n" " lhi %0,3\n"
" .align 8\n" " lr 0,%3\n"
" .quad 0b,1b\n" " diag 0,%2,0x250\n"
".previous\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"
#endif #endif
: "=&d" (rc) : "=&d" (rc), "=m" (*(addr_type *) iob)
: "d" (cmd), "d" ((void *) __pa(iob)) : "d" (cmd), "d" (iob), "m" (*(addr_type *) iob)
: "0", "1", "cc"); : "0", "1", "cc");
return rc; return rc;
} }
/* 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. */
static __inline__ int static __inline__ int
mdsk_init_io(struct dasd_device * device, int blocksize, int offset, int size) mdsk_init_io(struct dasd_device *device, unsigned int blocksize,
blocknum_t offset, blocknum_t *end_block)
{ {
struct dasd_diag_private *private; struct dasd_diag_private *private;
struct dasd_diag_init_io *iib; struct dasd_diag_init_io *iib;
@ -92,14 +124,18 @@ mdsk_init_io(struct dasd_device * device, int blocksize, int offset, int size)
iib->dev_nr = _ccw_device_get_device_number(device->cdev); iib->dev_nr = _ccw_device_get_device_number(device->cdev);
iib->block_size = blocksize; iib->block_size = blocksize;
iib->offset = offset; iib->offset = offset;
iib->start_block = 0; iib->flaga = DASD_DIAG_FLAGA_DEFAULT;
iib->end_block = size;
rc = dia250(iib, INIT_BIO); rc = dia250(iib, INIT_BIO);
return rc & 3; if ((rc & 3) == 0 && end_block)
*end_block = iib->end_block;
return rc;
} }
/* Remove block I/O environment for device. Return zero on success, non-zero
* otherwise. */
static __inline__ int static __inline__ int
mdsk_term_io(struct dasd_device * device) mdsk_term_io(struct dasd_device * device)
{ {
@ -112,9 +148,25 @@ mdsk_term_io(struct dasd_device * device)
memset(iib, 0, sizeof (struct dasd_diag_init_io)); memset(iib, 0, sizeof (struct dasd_diag_init_io));
iib->dev_nr = _ccw_device_get_device_number(device->cdev); iib->dev_nr = _ccw_device_get_device_number(device->cdev);
rc = dia250(iib, TERM_BIO); rc = dia250(iib, TERM_BIO);
return rc & 3; 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);
}
/* Start a given request at the device. Return zero on success, non-zero
* otherwise. */
static int static int
dasd_start_diag(struct dasd_ccw_req * cqr) dasd_start_diag(struct dasd_ccw_req * cqr)
{ {
@ -124,32 +176,66 @@ dasd_start_diag(struct dasd_ccw_req * cqr)
int rc; int rc;
device = cqr->device; device = cqr->device;
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;
}
private = (struct dasd_diag_private *) device->private; private = (struct dasd_diag_private *) device->private;
dreq = (struct dasd_diag_req *) cqr->data; dreq = (struct dasd_diag_req *) cqr->data;
private->iob.dev_nr = _ccw_device_get_device_number(device->cdev); private->iob.dev_nr = _ccw_device_get_device_number(device->cdev);
private->iob.key = 0; private->iob.key = 0;
private->iob.flags = 2; /* do asynchronous io */ private->iob.flags = DASD_DIAG_RWFLAG_ASYNC;
private->iob.block_count = dreq->block_count; private->iob.block_count = dreq->block_count;
private->iob.interrupt_params = (u32)(addr_t) cqr; private->iob.interrupt_params = (addr_t) cqr;
private->iob.bio_list = __pa(dreq->bio); private->iob.bio_list = __pa(dreq->bio);
private->iob.flaga = DASD_DIAG_FLAGA_DEFAULT;
cqr->startclk = get_clock(); cqr->startclk = get_clock();
cqr->starttime = jiffies;
cqr->retries--;
rc = dia250(&private->iob, RW_BIO); rc = dia250(&private->iob, RW_BIO);
if (rc > 8) { switch (rc) {
DEV_MESSAGE(KERN_WARNING, device, "dia250 returned CC %d", rc); case 0: /* Synchronous I/O finished successfully */
cqr->status = DASD_CQR_ERROR; cqr->stopclk = get_clock();
} else if (rc == 0) {
cqr->status = DASD_CQR_DONE; cqr->status = DASD_CQR_DONE;
dasd_schedule_bh(device); /* Indicate to calling function that only a dasd_schedule_bh()
} else { and no timer is needed */
rc = -EACCES;
break;
case 8: /* Asynchronous I/O was started */
cqr->status = DASD_CQR_IN_IO; cqr->status = DASD_CQR_IN_IO;
rc = 0; rc = 0;
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;
} }
return rc; return rc;
} }
/* 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. */
static void static void
dasd_ext_handler(struct pt_regs *regs, __u16 code) dasd_ext_handler(struct pt_regs *regs, __u16 code)
{ {
@ -157,25 +243,27 @@ dasd_ext_handler(struct pt_regs *regs, __u16 code)
struct dasd_device *device; struct dasd_device *device;
unsigned long long expires; unsigned long long expires;
unsigned long flags; unsigned long flags;
char status; u8 int_code, status;
int ip; addr_t ip;
int rc;
/* int_code = *((u8 *) DASD_DIAG_LC_INT_CODE);
* Get the external interruption subcode. VM stores status = *((u8 *) DASD_DIAG_LC_INT_STATUS);
* this in the 'cpu address' field associated with switch (int_code) {
* the external interrupt. For diag 250 the subcode case DASD_DIAG_CODE_31BIT:
* needs to be 3. ip = (addr_t) *((u32 *) DASD_DIAG_LC_INT_PARM_31BIT);
*/ break;
if ((S390_lowcore.cpu_addr & 0xff00) != 0x0300) case DASD_DIAG_CODE_64BIT:
ip = (addr_t) *((u64 *) DASD_DIAG_LC_INT_PARM_64BIT);
break;
default:
return; return;
status = *((char *) &S390_lowcore.ext_params + 5); }
ip = S390_lowcore.ext_params;
if (!ip) { /* no intparm: unsolicited interrupt */ if (!ip) { /* no intparm: unsolicited interrupt */
MESSAGE(KERN_DEBUG, "%s", "caught unsolicited interrupt"); MESSAGE(KERN_DEBUG, "%s", "caught unsolicited interrupt");
return; return;
} }
cqr = (struct dasd_ccw_req *)(addr_t) ip; cqr = (struct dasd_ccw_req *) ip;
device = (struct dasd_device *) cqr->device; device = (struct dasd_device *) cqr->device;
if (strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { if (strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
DEV_MESSAGE(KERN_WARNING, device, DEV_MESSAGE(KERN_WARNING, device,
@ -188,6 +276,15 @@ dasd_ext_handler(struct pt_regs *regs, __u16 code)
/* get irq lock to modify request queue */ /* get irq lock to modify request queue */
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
/* 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;
}
cqr->stopclk = get_clock(); cqr->stopclk = get_clock();
expires = 0; expires = 0;
@ -198,16 +295,22 @@ dasd_ext_handler(struct pt_regs *regs, __u16 code)
next = list_entry(device->ccw_queue.next, next = list_entry(device->ccw_queue.next,
struct dasd_ccw_req, list); struct dasd_ccw_req, list);
if (next->status == DASD_CQR_QUEUED) { if (next->status == DASD_CQR_QUEUED) {
if (dasd_start_diag(next) == 0) rc = dasd_start_diag(next);
if (rc == 0)
expires = next->expires; expires = next->expires;
else else if (rc != -EACCES)
DEV_MESSAGE(KERN_WARNING, device, "%s", DEV_MESSAGE(KERN_WARNING, device, "%s",
"Interrupt fastpath " "Interrupt fastpath "
"failed!"); "failed!");
} }
} }
} else } else {
cqr->status = DASD_CQR_FAILED; 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);
}
if (expires != 0) if (expires != 0)
dasd_set_timer(device, expires); dasd_set_timer(device, expires);
@ -218,14 +321,17 @@ dasd_ext_handler(struct pt_regs *regs, __u16 code)
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
} }
/* Check whether device can be controlled by DIAG discipline. Return zero on
* success, non-zero otherwise. */
static int static int
dasd_diag_check_device(struct dasd_device *device) dasd_diag_check_device(struct dasd_device *device)
{ {
struct dasd_diag_private *private; struct dasd_diag_private *private;
struct dasd_diag_characteristics *rdc_data; struct dasd_diag_characteristics *rdc_data;
struct dasd_diag_bio bio; struct dasd_diag_bio bio;
long *label; struct dasd_diag_cms_label *label;
int sb, bsize; blocknum_t end_block;
unsigned int sb, bsize;
int rc; int rc;
private = (struct dasd_diag_private *) device->private; private = (struct dasd_diag_private *) device->private;
@ -244,8 +350,11 @@ dasd_diag_check_device(struct dasd_device *device)
rdc_data->rdc_len = sizeof (struct dasd_diag_characteristics); rdc_data->rdc_len = sizeof (struct dasd_diag_characteristics);
rc = diag210((struct diag210 *) rdc_data); rc = diag210((struct diag210 *) rdc_data);
if (rc) if (rc) {
DEV_MESSAGE(KERN_WARNING, device, "failed to retrieve device "
"information (rc=%d)", rc);
return -ENOTSUPP; return -ENOTSUPP;
}
/* Figure out position of label block */ /* Figure out position of label block */
switch (private->rdc_data.vdev_class) { switch (private->rdc_data.vdev_class) {
@ -256,6 +365,8 @@ dasd_diag_check_device(struct dasd_device *device)
private->pt_block = 2; private->pt_block = 2;
break; break;
default: default:
DEV_MESSAGE(KERN_WARNING, device, "unsupported device class "
"(class=%d)", private->rdc_data.vdev_class);
return -ENOTSUPP; return -ENOTSUPP;
} }
@ -269,15 +380,17 @@ dasd_diag_check_device(struct dasd_device *device)
mdsk_term_io(device); mdsk_term_io(device);
/* figure out blocksize of device */ /* figure out blocksize of device */
label = (long *) get_zeroed_page(GFP_KERNEL); label = (struct dasd_diag_cms_label *) get_zeroed_page(GFP_KERNEL);
if (label == NULL) { if (label == NULL) {
DEV_MESSAGE(KERN_WARNING, device, "%s", DEV_MESSAGE(KERN_WARNING, device, "%s",
"No memory to allocate initialization request"); "No memory to allocate initialization request");
return -ENOMEM; return -ENOMEM;
} }
rc = 0;
end_block = 0;
/* try all sizes - needed for ECKD devices */ /* try all sizes - needed for ECKD devices */
for (bsize = 512; bsize <= PAGE_SIZE; bsize <<= 1) { for (bsize = 512; bsize <= PAGE_SIZE; bsize <<= 1) {
mdsk_init_io(device, bsize, 0, 64); mdsk_init_io(device, bsize, 0, &end_block);
memset(&bio, 0, sizeof (struct dasd_diag_bio)); memset(&bio, 0, sizeof (struct dasd_diag_bio));
bio.type = MDSK_READ_REQ; bio.type = MDSK_READ_REQ;
bio.block_number = private->pt_block + 1; bio.block_number = private->pt_block + 1;
@ -289,37 +402,45 @@ dasd_diag_check_device(struct dasd_device *device)
private->iob.block_count = 1; private->iob.block_count = 1;
private->iob.interrupt_params = 0; private->iob.interrupt_params = 0;
private->iob.bio_list = __pa(&bio); private->iob.bio_list = __pa(&bio);
if (dia250(&private->iob, RW_BIO) == 0) private->iob.flaga = DASD_DIAG_FLAGA_DEFAULT;
rc = dia250(&private->iob, RW_BIO);
if (rc == 0 || rc == 3)
break; break;
mdsk_term_io(device); mdsk_term_io(device);
} }
if (bsize <= PAGE_SIZE && label[0] == 0xc3d4e2f1) { if (rc == 3) {
/* get formatted blocksize from label block */ DEV_MESSAGE(KERN_WARNING, device, "%s", "DIAG call failed");
bsize = (int) label[3]; rc = -EOPNOTSUPP;
device->blocks = label[7]; } else if (rc != 0) {
DEV_MESSAGE(KERN_WARNING, device, "device access failed "
"(rc=%d)", rc);
rc = -EIO;
} else {
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->bp_block = bsize;
device->s2b_shift = 0; /* bits to shift 512 to get a block */ device->s2b_shift = 0; /* bits to shift 512 to get a block */
for (sb = 512; sb < bsize; sb = sb << 1) for (sb = 512; sb < bsize; sb = sb << 1)
device->s2b_shift++; device->s2b_shift++;
DEV_MESSAGE(KERN_INFO, device, DEV_MESSAGE(KERN_INFO, device,
"capacity (%dkB blks): %ldkB", "(%ld B/blk): %ldkB",
(device->bp_block >> 10), (unsigned long) device->bp_block,
(device->blocks << device->s2b_shift) >> 1); (unsigned long) (device->blocks <<
device->s2b_shift) >> 1);
rc = 0; rc = 0;
} else {
if (bsize > PAGE_SIZE)
DEV_MESSAGE(KERN_WARNING, device, "%s",
"DIAG access failed");
else
DEV_MESSAGE(KERN_WARNING, device, "%s",
"volume is not CMS formatted");
rc = -EMEDIUMTYPE;
} }
free_page((long) label); free_page((long) label);
return rc; return rc;
} }
/* Fill in virtual disk geometry for device. Return zero on success, non-zero
* otherwise. */
static int static int
dasd_diag_fill_geometry(struct dasd_device *device, struct hd_geometry *geo) dasd_diag_fill_geometry(struct dasd_device *device, struct hd_geometry *geo)
{ {
@ -349,6 +470,8 @@ dasd_diag_erp_postaction(struct dasd_ccw_req * cqr)
return dasd_default_erp_postaction; return dasd_default_erp_postaction;
} }
/* Create DASD request from block device request. Return pointer to new
* request on success, ERR_PTR otherwise. */
static struct dasd_ccw_req * static struct dasd_ccw_req *
dasd_diag_build_cp(struct dasd_device * device, struct request *req) dasd_diag_build_cp(struct dasd_device * device, struct request *req)
{ {
@ -358,9 +481,9 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req)
struct bio *bio; struct bio *bio;
struct bio_vec *bv; struct bio_vec *bv;
char *dst; char *dst;
int count, datasize; unsigned int count, datasize;
sector_t recid, first_rec, last_rec; sector_t recid, first_rec, last_rec;
unsigned blksize, off; unsigned int blksize, off;
unsigned char rw_cmd; unsigned char rw_cmd;
int i; int i;
@ -413,13 +536,16 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req)
} }
} }
} }
cqr->retries = DIAG_MAX_RETRIES;
cqr->buildclk = get_clock(); cqr->buildclk = get_clock();
cqr->device = device; cqr->device = device;
cqr->expires = 50 * HZ; /* 50 seconds */ cqr->expires = DIAG_TIMEOUT;
cqr->status = DASD_CQR_FILLED; cqr->status = DASD_CQR_FILLED;
return cqr; return cqr;
} }
/* Release DASD request. Return non-zero if request was successful, zero
* otherwise. */
static int static int
dasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req) dasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req)
{ {
@ -430,6 +556,7 @@ dasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req)
return status; return status;
} }
/* Fill in IOCTL data for device. */
static int static int
dasd_diag_fill_info(struct dasd_device * device, dasd_diag_fill_info(struct dasd_device * device,
struct dasd_information2_t * info) struct dasd_information2_t * info)
@ -437,7 +564,7 @@ dasd_diag_fill_info(struct dasd_device * device,
struct dasd_diag_private *private; struct dasd_diag_private *private;
private = (struct dasd_diag_private *) device->private; private = (struct dasd_diag_private *) device->private;
info->label_block = private->pt_block; info->label_block = (unsigned int) private->pt_block;
info->FBA_layout = 1; info->FBA_layout = 1;
info->format = DASD_FORMAT_LDL; info->format = DASD_FORMAT_LDL;
info->characteristics_size = sizeof (struct dasd_diag_characteristics); info->characteristics_size = sizeof (struct dasd_diag_characteristics);
@ -456,26 +583,15 @@ dasd_diag_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
"dump sense not available for DIAG data"); "dump sense not available for DIAG data");
} }
/*
* max_blocks is dependent on the amount of storage that is available
* in the static io buffer for each device. Currently each device has
* 8192 bytes (=2 pages). dasd diag is only relevant for 31 bit.
* The struct dasd_ccw_req has 96 bytes, the struct dasd_diag_req has
* 8 bytes and the struct dasd_diag_bio for each block has 16 bytes.
* That makes:
* (8192 - 96 - 8) / 16 = 505.5 blocks at maximum.
* We want to fit two into the available memory so that we can immediately
* start the next request if one finishes off. That makes 252.75 blocks
* for one request. Give a little safety and the result is 240.
*/
struct dasd_discipline dasd_diag_discipline = { struct dasd_discipline dasd_diag_discipline = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "DIAG", .name = "DIAG",
.ebcname = "DIAG", .ebcname = "DIAG",
.max_blocks = 240, .max_blocks = DIAG_MAX_BLOCKS,
.check_device = dasd_diag_check_device, .check_device = dasd_diag_check_device,
.fill_geometry = dasd_diag_fill_geometry, .fill_geometry = dasd_diag_fill_geometry,
.start_IO = dasd_start_diag, .start_IO = dasd_start_diag,
.term_IO = dasd_diag_term_IO,
.examine_error = dasd_diag_examine_error, .examine_error = dasd_diag_examine_error,
.erp_action = dasd_diag_erp_action, .erp_action = dasd_diag_erp_action,
.erp_postaction = dasd_diag_erp_postaction, .erp_postaction = dasd_diag_erp_postaction,
@ -493,7 +609,7 @@ dasd_diag_init(void)
"Machine is not VM: %s " "Machine is not VM: %s "
"discipline not initializing", "discipline not initializing",
dasd_diag_discipline.name); dasd_diag_discipline.name);
return -EINVAL; return -ENODEV;
} }
ASCEBC(dasd_diag_discipline.ebcname, 4); ASCEBC(dasd_diag_discipline.ebcname, 4);
@ -506,13 +622,6 @@ dasd_diag_init(void)
static void __exit static void __exit
dasd_diag_cleanup(void) dasd_diag_cleanup(void)
{ {
if (!MACHINE_IS_VM) {
MESSAGE_LOG(KERN_INFO,
"Machine is not VM: %s "
"discipline not cleaned",
dasd_diag_discipline.name);
return;
}
unregister_external_interrupt(0x2603, dasd_ext_handler); unregister_external_interrupt(0x2603, dasd_ext_handler);
ctl_clear_bit(0, 9); ctl_clear_bit(0, 9);
dasd_diag_discipline_pointer = NULL; dasd_diag_discipline_pointer = NULL;
@ -520,22 +629,3 @@ dasd_diag_cleanup(void)
module_init(dasd_diag_init); module_init(dasd_diag_init);
module_exit(dasd_diag_cleanup); module_exit(dasd_diag_cleanup);
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-indent-level: 4
* c-brace-imaginary-offset: 0
* c-brace-offset: -4
* c-argdecl-indent: 4
* c-label-offset: -4
* c-continued-statement-offset: 4
* c-continued-brace-offset: 0
* indent-tabs-mode: 1
* tab-width: 8
* End:
*/

View File

@ -6,7 +6,7 @@
* Bugreports.to..: <Linux390@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
* *
* $Revision: 1.6 $ * $Revision: 1.7 $
*/ */
#define MDSK_WRITE_REQ 0x01 #define MDSK_WRITE_REQ 0x01
@ -19,6 +19,18 @@
#define DEV_CLASS_FBA 0x01 #define DEV_CLASS_FBA 0x01
#define DEV_CLASS_ECKD 0x04 #define DEV_CLASS_ECKD 0x04
#define DASD_DIAG_LC_INT_CODE 132
#define DASD_DIAG_LC_INT_STATUS 133
#define DASD_DIAG_LC_INT_PARM_31BIT 128
#define DASD_DIAG_LC_INT_PARM_64BIT 4536
#define DASD_DIAG_CODE_31BIT 0x03
#define DASD_DIAG_CODE_64BIT 0x07
#define DASD_DIAG_RWFLAG_ASYNC 0x02
#define DASD_DIAG_RWFLAG_NOCACHE 0x01
#define DASD_DIAG_FLAGA_FORMAT_64BIT 0x80
struct dasd_diag_characteristics { struct dasd_diag_characteristics {
u16 dev_nr; u16 dev_nr;
u16 rdc_len; u16 rdc_len;
@ -32,35 +44,106 @@ struct dasd_diag_characteristics {
u8 rdev_features; u8 rdev_features;
} __attribute__ ((packed, aligned(4))); } __attribute__ ((packed, aligned(4)));
struct dasd_diag_cms_label {
u8 label_id[4];
u8 vol_id[6];
u16 version_id;
u32 block_size;
u32 origin_ptr;
u32 usable_count;
u32 formatted_count;
u32 block_count;
u32 used_count;
u32 fst_size;
u32 fst_count;
u8 format_date[6];
u8 reserved1[2];
u32 disk_offset;
u32 map_block;
u32 hblk_disp;
u32 user_disp;
u8 reserved2[4];
u8 segment_name[8];
} __attribute__ ((packed));
#ifdef CONFIG_ARCH_S390X
#define DASD_DIAG_FLAGA_DEFAULT DASD_DIAG_FLAGA_FORMAT_64BIT
typedef u64 blocknum_t;
typedef s64 sblocknum_t;
struct dasd_diag_bio {
u8 type;
u8 status;
u8 spare1[2];
u32 alet;
blocknum_t block_number;
u64 buffer;
} __attribute__ ((packed, aligned(8)));
struct dasd_diag_init_io {
u16 dev_nr;
u8 flaga;
u8 spare1[21];
u32 block_size;
u8 spare2[4];
blocknum_t offset;
sblocknum_t start_block;
blocknum_t end_block;
u8 spare3[8];
} __attribute__ ((packed, aligned(8)));
struct dasd_diag_rw_io {
u16 dev_nr;
u8 flaga;
u8 spare1[21];
u8 key;
u8 flags;
u8 spare2[2];
u32 block_count;
u32 alet;
u8 spare3[4];
u64 interrupt_params;
u64 bio_list;
u8 spare4[8];
} __attribute__ ((packed, aligned(8)));
#else /* CONFIG_ARCH_S390X */
#define DASD_DIAG_FLAGA_DEFAULT 0x0
typedef u32 blocknum_t;
typedef s32 sblocknum_t;
struct dasd_diag_bio { struct dasd_diag_bio {
u8 type; u8 type;
u8 status; u8 status;
u16 spare1; u16 spare1;
u32 block_number; blocknum_t block_number;
u32 alet; u32 alet;
u32 buffer; u32 buffer;
} __attribute__ ((packed, aligned(8))); } __attribute__ ((packed, aligned(8)));
struct dasd_diag_init_io { struct dasd_diag_init_io {
u16 dev_nr; u16 dev_nr;
u16 spare1[11]; u8 flaga;
u8 spare1[21];
u32 block_size; u32 block_size;
u32 offset; blocknum_t offset;
u32 start_block; sblocknum_t start_block;
u32 end_block; blocknum_t end_block;
u32 spare2[6]; u8 spare2[24];
} __attribute__ ((packed, aligned(8))); } __attribute__ ((packed, aligned(8)));
struct dasd_diag_rw_io { struct dasd_diag_rw_io {
u16 dev_nr; u16 dev_nr;
u16 spare1[11]; u8 flaga;
u8 spare1[21];
u8 key; u8 key;
u8 flags; u8 flags;
u16 spare2; u8 spare2[2];
u32 block_count; u32 block_count;
u32 alet; u32 alet;
u32 bio_list; u32 bio_list;
u32 interrupt_params; u32 interrupt_params;
u32 spare3[5]; u8 spare3[20];
} __attribute__ ((packed, aligned(8))); } __attribute__ ((packed, aligned(8)));
#endif /* CONFIG_ARCH_S390X */