2006-06-29 16:58:12 +04:00
/*
2005-04-17 02:20:36 +04:00
* 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
*
*/
2009-12-18 19:43:16 +03:00
# define KMSG_COMPONENT "dasd"
2009-03-26 17:23:49 +03:00
2011-01-05 14:47:28 +03:00
# include <linux/kernel_stat.h>
2005-04-17 02:20:36 +04:00
# include <linux/stddef.h>
# include <linux/kernel.h>
# include <linux/slab.h>
2005-09-04 02:58:00 +04:00
# include <linux/hdreg.h>
2005-04-17 02:20:36 +04:00
# include <linux/bio.h>
# include <linux/module.h>
# include <linux/init.h>
2005-09-04 02:58:00 +04:00
# include <linux/jiffies.h>
2005-04-17 02:20:36 +04:00
# include <asm/dasd.h>
# include <asm/debug.h>
# include <asm/ebcdic.h>
# include <asm/io.h>
# include <asm/s390_ext.h>
2006-01-06 11:19:09 +03:00
# include <asm/vtoc.h>
2007-08-22 15:51:40 +04:00
# include <asm/diag.h>
2005-04-17 02:20:36 +04:00
# include "dasd_int.h"
# include "dasd_diag.h"
# define PRINTK_HEADER "dasd(diag):"
MODULE_LICENSE ( " GPL " ) ;
2005-09-04 02:58:00 +04:00
/* 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
2010-08-09 20:13:00 +04:00
# define DIAG_TIMEOUT 50
2005-09-04 02:58:00 +04:00
2007-02-05 23:16:47 +03:00
static struct dasd_discipline dasd_diag_discipline ;
2005-04-17 02:20:36 +04:00
struct dasd_diag_private {
struct dasd_diag_characteristics rdc_data ;
struct dasd_diag_rw_io iob ;
struct dasd_diag_init_io iib ;
2005-09-04 02:58:00 +04:00
blocknum_t pt_block ;
2007-05-10 17:45:42 +04:00
struct ccw_dev_id dev_id ;
2005-04-17 02:20:36 +04:00
} ;
struct dasd_diag_req {
2005-09-04 02:58:00 +04:00
unsigned int block_count ;
2005-04-17 02:20:36 +04:00
struct dasd_diag_bio bio [ 0 ] ;
} ;
2005-09-04 02:58:00 +04:00
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 . */
2006-09-28 18:56:43 +04:00
static inline int dia250 ( void * iob , int cmd )
2005-04-17 02:20:36 +04:00
{
2007-03-26 22:42:38 +04:00
register unsigned long reg2 asm ( " 2 " ) = ( unsigned long ) iob ;
2005-11-07 11:59:08 +03:00
typedef union {
struct dasd_diag_init_io init_io ;
struct dasd_diag_rw_io rw_io ;
2005-09-04 02:58:00 +04:00
} addr_type ;
2005-04-17 02:20:36 +04:00
int rc ;
2006-09-28 18:56:43 +04:00
rc = 3 ;
asm volatile (
2007-03-26 22:42:38 +04:00
" diag 2,%2,0x250 \n "
2005-09-04 02:58:00 +04:00
" 0: ipm %0 \n "
" srl %0,28 \n "
2007-03-26 22:42:38 +04:00
" or %0,3 \n "
2005-09-04 02:58:00 +04:00
" 1: \n "
2006-09-28 18:56:43 +04:00
EX_TABLE ( 0 b , 1 b )
: " +d " ( rc ) , " =m " ( * ( addr_type * ) iob )
2007-03-26 22:42:38 +04:00
: " d " ( cmd ) , " d " ( reg2 ) , " m " ( * ( addr_type * ) iob )
: " 3 " , " cc " ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
2005-09-04 02:58:00 +04:00
/* 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 . */
2007-02-05 23:18:53 +03:00
static inline int
2005-09-04 02:58:00 +04:00
mdsk_init_io ( struct dasd_device * device , unsigned int blocksize ,
blocknum_t offset , blocknum_t * end_block )
2005-04-17 02:20:36 +04:00
{
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 ) ) ;
2007-05-10 17:45:42 +04:00
iib - > dev_nr = private - > dev_id . devno ;
2005-04-17 02:20:36 +04:00
iib - > block_size = blocksize ;
iib - > offset = offset ;
2005-09-04 02:58:00 +04:00
iib - > flaga = DASD_DIAG_FLAGA_DEFAULT ;
2005-04-17 02:20:36 +04:00
rc = dia250 ( iib , INIT_BIO ) ;
2005-09-04 02:58:00 +04:00
if ( ( rc & 3 ) = = 0 & & end_block )
* end_block = iib - > end_block ;
return rc ;
2005-04-17 02:20:36 +04:00
}
2005-09-04 02:58:00 +04:00
/* Remove block I/O environment for device. Return zero on success, non-zero
* otherwise . */
2007-02-05 23:18:53 +03:00
static inline int
2005-04-17 02:20:36 +04:00
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 ) ) ;
2007-05-10 17:45:42 +04:00
iib - > dev_nr = private - > dev_id . devno ;
2005-04-17 02:20:36 +04:00
rc = dia250 ( iib , TERM_BIO ) ;
2005-09-04 02:58:00 +04:00
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 ) ;
2008-01-26 16:11:23 +03:00
rc = mdsk_init_io ( device , device - > block - > bp_block , 0 , NULL ) ;
2009-12-07 14:51:48 +03:00
if ( rc = = 4 ) {
2010-03-08 14:26:24 +03:00
if ( ! ( test_and_set_bit ( DASD_FLAG_DEVICE_RO , & device - > flags ) ) )
2009-12-18 19:43:16 +03:00
pr_warning ( " %s: The access mode of a DIAG device "
" changed to read-only \n " ,
dev_name ( & device - > cdev - > dev ) ) ;
2009-12-07 14:51:48 +03:00
rc = 0 ;
}
2005-09-04 02:58:00 +04:00
if ( rc )
2009-12-18 19:43:16 +03:00
pr_warning ( " %s: DIAG ERP failed with "
" rc=%d \n " , dev_name ( & device - > cdev - > dev ) , rc ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-04 02:58:00 +04:00
/* Start a given request at the device. Return zero on success, non-zero
* otherwise . */
2005-04-17 02:20:36 +04:00
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 ;
2008-01-26 16:11:23 +03:00
device = cqr - > startdev ;
2005-09-04 02:58:00 +04:00
if ( cqr - > retries < 0 ) {
2009-03-26 17:23:49 +03:00
DBF_DEV_EVENT ( DBF_ERR , device , " DIAG start_IO: request %p "
2005-09-04 02:58:00 +04:00
" - no retry left) " , cqr ) ;
2008-01-26 16:11:23 +03:00
cqr - > status = DASD_CQR_ERROR ;
2005-09-04 02:58:00 +04:00
return - EIO ;
}
2005-04-17 02:20:36 +04:00
private = ( struct dasd_diag_private * ) device - > private ;
dreq = ( struct dasd_diag_req * ) cqr - > data ;
2007-05-10 17:45:42 +04:00
private - > iob . dev_nr = private - > dev_id . devno ;
2005-04-17 02:20:36 +04:00
private - > iob . key = 0 ;
2005-09-04 02:58:00 +04:00
private - > iob . flags = DASD_DIAG_RWFLAG_ASYNC ;
2005-04-17 02:20:36 +04:00
private - > iob . block_count = dreq - > block_count ;
2005-09-04 02:58:00 +04:00
private - > iob . interrupt_params = ( addr_t ) cqr ;
2005-11-07 11:59:08 +03:00
private - > iob . bio_list = dreq - > bio ;
2005-09-04 02:58:00 +04:00
private - > iob . flaga = DASD_DIAG_FLAGA_DEFAULT ;
2005-04-17 02:20:36 +04:00
cqr - > startclk = get_clock ( ) ;
2005-09-04 02:58:00 +04:00
cqr - > starttime = jiffies ;
cqr - > retries - - ;
2005-04-17 02:20:36 +04:00
rc = dia250 ( & private - > iob , RW_BIO ) ;
2005-09-04 02:58:00 +04:00
switch ( rc ) {
case 0 : /* Synchronous I/O finished successfully */
cqr - > stopclk = get_clock ( ) ;
2008-01-26 16:11:23 +03:00
cqr - > status = DASD_CQR_SUCCESS ;
2005-09-04 02:58:00 +04:00
/* 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 */
2005-04-17 02:20:36 +04:00
cqr - > status = DASD_CQR_IN_IO ;
rc = 0 ;
2005-09-04 02:58:00 +04:00
break ;
default : /* Error condition */
cqr - > status = DASD_CQR_QUEUED ;
2009-03-26 17:23:49 +03:00
DBF_DEV_EVENT ( DBF_WARNING , device , " dia250 returned rc=%d " , rc ) ;
2005-09-04 02:58:00 +04:00
dasd_diag_erp ( device ) ;
rc = - EIO ;
break ;
2005-04-17 02:20:36 +04:00
}
2009-06-12 12:26:39 +04:00
cqr - > intrc = rc ;
2005-04-17 02:20:36 +04:00
return rc ;
}
2005-09-04 02:58:00 +04:00
/* Terminate given request at the device. */
static int
dasd_diag_term_IO ( struct dasd_ccw_req * cqr )
{
struct dasd_device * device ;
2008-01-26 16:11:23 +03:00
device = cqr - > startdev ;
2005-09-04 02:58:00 +04:00
mdsk_term_io ( device ) ;
2008-01-26 16:11:23 +03:00
mdsk_init_io ( device , device - > block - > bp_block , 0 , NULL ) ;
cqr - > status = DASD_CQR_CLEAR_PENDING ;
2005-09-04 02:58:00 +04:00
cqr - > stopclk = get_clock ( ) ;
2008-01-26 16:11:23 +03:00
dasd_schedule_device_bh ( device ) ;
2005-09-04 02:58:00 +04:00
return 0 ;
}
/* Handle external interruption. */
2010-10-25 18:10:38 +04:00
static void dasd_ext_handler ( unsigned int ext_int_code ,
unsigned int param32 , unsigned long param64 )
2005-04-17 02:20:36 +04:00
{
struct dasd_ccw_req * cqr , * next ;
struct dasd_device * device ;
unsigned long long expires ;
unsigned long flags ;
2005-09-04 02:58:00 +04:00
addr_t ip ;
int rc ;
2005-04-17 02:20:36 +04:00
2011-01-05 14:47:28 +03:00
kstat_cpu ( smp_processor_id ( ) ) . irqs [ EXTINT_DSD ] + + ;
2010-10-25 18:10:38 +04:00
switch ( ext_int_code > > 24 ) {
2005-09-04 02:58:00 +04:00
case DASD_DIAG_CODE_31BIT :
2010-10-25 18:10:38 +04:00
ip = ( addr_t ) param32 ;
2005-09-04 02:58:00 +04:00
break ;
case DASD_DIAG_CODE_64BIT :
2010-10-25 18:10:38 +04:00
ip = ( addr_t ) param64 ;
2005-09-04 02:58:00 +04:00
break ;
default :
return ;
}
2005-04-17 02:20:36 +04:00
if ( ! ip ) { /* no intparm: unsolicited interrupt */
2009-03-26 17:23:49 +03:00
DBF_EVENT ( DBF_NOTICE , " %s " , " caught unsolicited "
" interrupt " ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2005-09-04 02:58:00 +04:00
cqr = ( struct dasd_ccw_req * ) ip ;
2008-01-26 16:11:23 +03:00
device = ( struct dasd_device * ) cqr - > startdev ;
2005-04-17 02:20:36 +04:00
if ( strncmp ( device - > discipline - > ebcname , ( char * ) & cqr - > magic , 4 ) ) {
2009-03-26 17:23:49 +03:00
DBF_DEV_EVENT ( DBF_WARNING , device ,
2005-04-17 02:20:36 +04:00
" 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 ) ;
2005-09-04 02:58:00 +04:00
/* Check for a pending clear operation */
2008-01-26 16:11:23 +03:00
if ( cqr - > status = = DASD_CQR_CLEAR_PENDING ) {
cqr - > status = DASD_CQR_CLEARED ;
dasd_device_clear_timer ( device ) ;
dasd_schedule_device_bh ( device ) ;
2005-09-04 02:58:00 +04:00
spin_unlock_irqrestore ( get_ccwdev_lock ( device - > cdev ) , flags ) ;
return ;
}
2005-04-17 02:20:36 +04:00
cqr - > stopclk = get_clock ( ) ;
expires = 0 ;
2010-10-25 18:10:38 +04:00
if ( ( ext_int_code & 0xff0000 ) = = 0 ) {
2008-01-26 16:11:23 +03:00
cqr - > status = DASD_CQR_SUCCESS ;
2005-04-17 02:20:36 +04:00
/* Start first request on queue if possible -> fast_io. */
if ( ! list_empty ( & device - > ccw_queue ) ) {
next = list_entry ( device - > ccw_queue . next ,
2008-01-26 16:11:23 +03:00
struct dasd_ccw_req , devlist ) ;
2005-04-17 02:20:36 +04:00
if ( next - > status = = DASD_CQR_QUEUED ) {
2005-09-04 02:58:00 +04:00
rc = dasd_start_diag ( next ) ;
if ( rc = = 0 )
2005-04-17 02:20:36 +04:00
expires = next - > expires ;
}
}
2005-09-04 02:58:00 +04:00
} else {
cqr - > status = DASD_CQR_QUEUED ;
2009-03-26 17:23:49 +03:00
DBF_DEV_EVENT ( DBF_DEBUG , device , " interrupt status for "
2010-10-25 18:10:38 +04:00
" request %p was %d (%d retries left) " , cqr ,
( ext_int_code > > 16 ) & 0xff , cqr - > retries ) ;
2005-09-04 02:58:00 +04:00
dasd_diag_erp ( device ) ;
}
2005-04-17 02:20:36 +04:00
if ( expires ! = 0 )
2008-01-26 16:11:23 +03:00
dasd_device_set_timer ( device , expires ) ;
2005-04-17 02:20:36 +04:00
else
2008-01-26 16:11:23 +03:00
dasd_device_clear_timer ( device ) ;
dasd_schedule_device_bh ( device ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( get_ccwdev_lock ( device - > cdev ) , flags ) ;
}
2005-09-04 02:58:00 +04:00
/* Check whether device can be controlled by DIAG discipline. Return zero on
* success , non - zero otherwise . */
2005-04-17 02:20:36 +04:00
static int
dasd_diag_check_device ( struct dasd_device * device )
{
2008-01-26 16:11:23 +03:00
struct dasd_block * block ;
2005-04-17 02:20:36 +04:00
struct dasd_diag_private * private ;
struct dasd_diag_characteristics * rdc_data ;
struct dasd_diag_bio bio ;
2006-01-06 11:19:09 +03:00
struct vtoc_cms_label * label ;
2005-09-04 02:58:00 +04:00
blocknum_t end_block ;
unsigned int sb , bsize ;
2005-04-17 02:20:36 +04:00
int rc ;
private = ( struct dasd_diag_private * ) device - > private ;
if ( private = = NULL ) {
2006-06-29 16:58:12 +04:00
private = kzalloc ( sizeof ( struct dasd_diag_private ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( private = = NULL ) {
2009-03-26 17:23:49 +03:00
DBF_DEV_EVENT ( DBF_WARNING , device , " %s " ,
" Allocating memory for private DASD data "
" failed \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
2007-05-10 17:45:42 +04:00
ccw_device_get_id ( device - > cdev , & private - > dev_id ) ;
2005-04-17 02:20:36 +04:00
device - > private = ( void * ) private ;
}
2008-01-26 16:11:23 +03:00
block = dasd_alloc_block ( ) ;
if ( IS_ERR ( block ) ) {
2009-03-26 17:23:49 +03:00
DBF_DEV_EVENT ( DBF_WARNING , device , " %s " ,
2008-01-26 16:11:23 +03:00
" could not allocate dasd block structure " ) ;
2008-07-17 19:16:43 +04:00
device - > private = NULL ;
kfree ( private ) ;
2008-01-26 16:11:23 +03:00
return PTR_ERR ( block ) ;
}
device - > block = block ;
block - > base = device ;
2005-04-17 02:20:36 +04:00
/* Read Device Characteristics */
rdc_data = ( void * ) & ( private - > rdc_data ) ;
2007-05-10 17:45:42 +04:00
rdc_data - > dev_nr = private - > dev_id . devno ;
2005-04-17 02:20:36 +04:00
rdc_data - > rdc_len = sizeof ( struct dasd_diag_characteristics ) ;
rc = diag210 ( ( struct diag210 * ) rdc_data ) ;
2005-09-04 02:58:00 +04:00
if ( rc ) {
2009-03-26 17:23:49 +03:00
DBF_DEV_EVENT ( DBF_WARNING , device , " failed to retrieve device "
2005-09-04 02:58:00 +04:00
" information (rc=%d) " , rc ) ;
2008-07-17 19:16:49 +04:00
rc = - EOPNOTSUPP ;
2008-07-17 19:16:43 +04:00
goto out ;
2005-09-04 02:58:00 +04:00
}
2005-04-17 02:20:36 +04:00
2010-08-09 20:13:00 +04:00
device - > default_expires = DIAG_TIMEOUT ;
2005-04-17 02:20:36 +04:00
/* 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 :
2009-12-18 19:43:16 +03:00
pr_warning ( " %s: Device type %d is not supported "
" in DIAG mode \n " , dev_name ( & device - > cdev - > dev ) ,
private - > rdc_data . vdev_class ) ;
2008-07-17 19:16:49 +04:00
rc = - EOPNOTSUPP ;
2008-07-17 19:16:43 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
}
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 */
2006-01-06 11:19:09 +03:00
label = ( struct vtoc_cms_label * ) get_zeroed_page ( GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( label = = NULL ) {
2009-03-26 17:23:49 +03:00
DBF_DEV_EVENT ( DBF_WARNING , device , " %s " ,
2005-04-17 02:20:36 +04:00
" No memory to allocate initialization request " ) ;
2008-07-17 19:16:43 +04:00
rc = - ENOMEM ;
goto out ;
2005-04-17 02:20:36 +04:00
}
2005-09-04 02:58:00 +04:00
rc = 0 ;
end_block = 0 ;
2005-04-17 02:20:36 +04:00
/* try all sizes - needed for ECKD devices */
for ( bsize = 512 ; bsize < = PAGE_SIZE ; bsize < < = 1 ) {
2005-09-04 02:58:00 +04:00
mdsk_init_io ( device , bsize , 0 , & end_block ) ;
2005-04-17 02:20:36 +04:00
memset ( & bio , 0 , sizeof ( struct dasd_diag_bio ) ) ;
bio . type = MDSK_READ_REQ ;
bio . block_number = private - > pt_block + 1 ;
2005-11-07 11:59:08 +03:00
bio . buffer = label ;
2005-04-17 02:20:36 +04:00
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 ;
2005-11-07 11:59:08 +03:00
private - > iob . bio_list = & bio ;
2005-09-04 02:58:00 +04:00
private - > iob . flaga = DASD_DIAG_FLAGA_DEFAULT ;
rc = dia250 ( & private - > iob , RW_BIO ) ;
2005-11-07 11:59:08 +03:00
if ( rc = = 3 ) {
2009-12-18 19:43:16 +03:00
pr_warning ( " %s: A 64-bit DIAG call failed \n " ,
dev_name ( & device - > cdev - > dev ) ) ;
2005-11-07 11:59:08 +03:00
rc = - EOPNOTSUPP ;
2008-07-17 19:16:43 +04:00
goto out_label ;
2005-11-07 11:59:08 +03:00
}
2005-04-17 02:20:36 +04:00
mdsk_term_io ( device ) ;
2005-11-07 11:59:08 +03:00
if ( rc = = 0 )
break ;
2005-04-17 02:20:36 +04:00
}
2005-11-07 11:59:08 +03:00
if ( bsize > PAGE_SIZE ) {
2009-12-18 19:43:16 +03:00
pr_warning ( " %s: Accessing the DASD failed because of an "
" incorrect format (rc=%d) \n " ,
dev_name ( & device - > cdev - > dev ) , rc ) ;
2005-09-04 02:58:00 +04:00
rc = - EIO ;
2008-07-17 19:16:43 +04:00
goto out_label ;
2005-11-07 11:59:08 +03:00
}
/* 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 ;
2008-01-26 16:11:23 +03:00
block - > blocks = ( unsigned long ) label - > block_count ;
2005-11-07 11:59:08 +03:00
} else
2008-01-26 16:11:23 +03:00
block - > blocks = end_block ;
block - > bp_block = bsize ;
block - > s2b_shift = 0 ; /* bits to shift 512 to get a block */
2005-11-07 11:59:08 +03:00
for ( sb = 512 ; sb < bsize ; sb = sb < < 1 )
2008-01-26 16:11:23 +03:00
block - > s2b_shift + + ;
rc = mdsk_init_io ( device , block - > bp_block , 0 , NULL ) ;
2009-12-07 14:51:48 +03:00
if ( rc & & ( rc ! = 4 ) ) {
2009-12-18 19:43:16 +03:00
pr_warning ( " %s: DIAG initialization failed with rc=%d \n " ,
dev_name ( & device - > cdev - > dev ) , rc ) ;
2005-11-07 11:59:08 +03:00
rc = - EIO ;
2005-09-04 02:58:00 +04:00
} else {
2009-12-07 14:51:48 +03:00
if ( rc = = 4 )
2010-03-08 14:26:24 +03:00
set_bit ( DASD_FLAG_DEVICE_RO , & device - > flags ) ;
2009-12-18 19:43:16 +03:00
pr_info ( " %s: New DASD with %ld byte/block, total size %ld "
" KB%s \n " , dev_name ( & device - > cdev - > dev ) ,
( unsigned long ) block - > bp_block ,
( unsigned long ) ( block - > blocks < <
block - > s2b_shift ) > > 1 ,
( rc = = 4 ) ? " , read-only device " : " " ) ;
2009-12-07 14:51:48 +03:00
rc = 0 ;
2005-04-17 02:20:36 +04:00
}
2008-07-17 19:16:43 +04:00
out_label :
2005-04-17 02:20:36 +04:00
free_page ( ( long ) label ) ;
2008-07-17 19:16:43 +04:00
out :
if ( rc ) {
device - > block = NULL ;
dasd_free_block ( block ) ;
device - > private = NULL ;
kfree ( private ) ;
}
2005-04-17 02:20:36 +04:00
return rc ;
}
2005-09-04 02:58:00 +04:00
/* Fill in virtual disk geometry for device. Return zero on success, non-zero
* otherwise . */
2005-04-17 02:20:36 +04:00
static int
2008-01-26 16:11:23 +03:00
dasd_diag_fill_geometry ( struct dasd_block * block , struct hd_geometry * geo )
2005-04-17 02:20:36 +04:00
{
2008-01-26 16:11:23 +03:00
if ( dasd_check_blocksize ( block - > bp_block ) ! = 0 )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2008-01-26 16:11:23 +03:00
geo - > cylinders = ( block - > blocks < < block - > s2b_shift ) > > 10 ;
2005-04-17 02:20:36 +04:00
geo - > heads = 16 ;
2008-01-26 16:11:23 +03:00
geo - > sectors = 128 > > block - > s2b_shift ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
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 ;
}
2005-09-04 02:58:00 +04:00
/* Create DASD request from block device request. Return pointer to new
* request on success , ERR_PTR otherwise . */
2008-01-26 16:11:23 +03:00
static struct dasd_ccw_req * dasd_diag_build_cp ( struct dasd_device * memdev ,
struct dasd_block * block ,
struct request * req )
2005-04-17 02:20:36 +04:00
{
struct dasd_ccw_req * cqr ;
struct dasd_diag_req * dreq ;
struct dasd_diag_bio * dbio ;
2007-09-25 14:35:59 +04:00
struct req_iterator iter ;
2005-04-17 02:20:36 +04:00
struct bio_vec * bv ;
char * dst ;
2005-09-04 02:58:00 +04:00
unsigned int count , datasize ;
2005-04-17 02:20:36 +04:00
sector_t recid , first_rec , last_rec ;
2005-09-04 02:58:00 +04:00
unsigned int blksize , off ;
2005-04-17 02:20:36 +04:00
unsigned char rw_cmd ;
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 ) ;
2008-01-26 16:11:23 +03:00
blksize = block - > bp_block ;
2005-04-17 02:20:36 +04:00
/* Calculate record id of first and last block. */
2009-05-07 17:24:39 +04:00
first_rec = blk_rq_pos ( req ) > > block - > s2b_shift ;
last_rec =
( blk_rq_pos ( req ) + blk_rq_sectors ( req ) - 1 ) > > block - > s2b_shift ;
2005-04-17 02:20:36 +04:00
/* Check struct bio and count the number of blocks for the request. */
count = 0 ;
2007-09-25 14:35:59 +04:00
rq_for_each_segment ( bv , req , iter ) {
2007-08-16 15:43:12 +04:00
if ( bv - > bv_len & ( blksize - 1 ) )
/* Fba can only do full blocks. */
return ERR_PTR ( - EINVAL ) ;
2008-01-26 16:11:23 +03:00
count + = bv - > bv_len > > ( block - > s2b_shift + 9 ) ;
2005-04-17 02:20:36 +04:00
}
/* 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 ) ;
2009-09-11 12:28:29 +04:00
cqr = dasd_smalloc_request ( DASD_DIAG_MAGIC , 0 , datasize , memdev ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( cqr ) )
return cqr ;
2006-06-29 16:58:12 +04:00
2005-04-17 02:20:36 +04:00
dreq = ( struct dasd_diag_req * ) cqr - > data ;
dreq - > block_count = count ;
dbio = dreq - > bio ;
recid = first_rec ;
2007-09-25 14:35:59 +04:00
rq_for_each_segment ( bv , req , iter ) {
2007-08-16 15:43:12 +04:00
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 ;
dbio - > buffer = dst ;
dbio + + ;
dst + = blksize ;
recid + + ;
}
2005-04-17 02:20:36 +04:00
}
2005-09-04 02:58:00 +04:00
cqr - > retries = DIAG_MAX_RETRIES ;
2005-04-17 02:20:36 +04:00
cqr - > buildclk = get_clock ( ) ;
2009-01-09 14:14:51 +03:00
if ( blk_noretry_request ( req ) | |
block - > base - > features & DASD_FEATURE_FAILFAST )
2006-01-06 11:19:15 +03:00
set_bit ( DASD_CQR_FLAGS_FAILFAST , & cqr - > flags ) ;
2008-01-26 16:11:23 +03:00
cqr - > startdev = memdev ;
cqr - > memdev = memdev ;
cqr - > block = block ;
2010-08-09 20:13:00 +04:00
cqr - > expires = memdev - > default_expires * HZ ;
2005-04-17 02:20:36 +04:00
cqr - > status = DASD_CQR_FILLED ;
return cqr ;
}
2005-09-04 02:58:00 +04:00
/* Release DASD request. Return non-zero if request was successful, zero
* otherwise . */
2005-04-17 02:20:36 +04:00
static int
dasd_diag_free_cp ( struct dasd_ccw_req * cqr , struct request * req )
{
int status ;
status = cqr - > status = = DASD_CQR_DONE ;
2008-01-26 16:11:23 +03:00
dasd_sfree_request ( cqr , cqr - > memdev ) ;
2005-04-17 02:20:36 +04:00
return status ;
}
2008-01-26 16:11:23 +03:00
static void dasd_diag_handle_terminated_request ( struct dasd_ccw_req * cqr )
{
cqr - > status = DASD_CQR_FILLED ;
} ;
2005-09-04 02:58:00 +04:00
/* Fill in IOCTL data for device. */
2005-04-17 02:20:36 +04:00
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 ;
2005-09-04 02:58:00 +04:00
info - > label_block = ( unsigned int ) private - > pt_block ;
2005-04-17 02:20:36 +04:00
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 )
{
2009-03-26 17:23:49 +03:00
DBF_DEV_EVENT ( DBF_WARNING , device , " %s " ,
2005-04-17 02:20:36 +04:00
" dump sense not available for DIAG data " ) ;
}
2007-02-05 23:16:47 +03:00
static struct dasd_discipline dasd_diag_discipline = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. name = " DIAG " ,
. ebcname = " DIAG " ,
2005-09-04 02:58:00 +04:00
. max_blocks = DIAG_MAX_BLOCKS ,
2005-04-17 02:20:36 +04:00
. check_device = dasd_diag_check_device ,
2011-01-05 14:48:03 +03:00
. verify_path = dasd_generic_verify_path ,
2005-04-17 02:20:36 +04:00
. fill_geometry = dasd_diag_fill_geometry ,
. start_IO = dasd_start_diag ,
2005-09-04 02:58:00 +04:00
. term_IO = dasd_diag_term_IO ,
2008-01-26 16:11:23 +03:00
. handle_terminated_request = dasd_diag_handle_terminated_request ,
2005-04-17 02:20:36 +04:00
. 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 ) {
2009-03-26 17:23:49 +03:00
pr_info ( " Discipline %s cannot be used without z/VM \n " ,
dasd_diag_discipline . name ) ;
2005-09-04 02:58:00 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
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 ) ;