2006-06-29 16:58:12 +04:00
/*
2005-04-17 02:20:36 +04:00
* File . . . . . . . . . . . : linux / drivers / s390 / block / dasd_eckd . c
* Author ( s ) . . . . . . : Holger Smolinski < Holger . Smolinski @ de . ibm . com >
2006-06-29 16:58:12 +04:00
* Horst Hummel < Horst . Hummel @ de . ibm . com >
2005-04-17 02:20:36 +04:00
* Carsten Otte < Cotte @ de . ibm . com >
* Martin Schwidefsky < schwidefsky @ de . ibm . com >
* Bugreports . to . . : < Linux390 @ de . ibm . com >
2009-06-16 12:30:25 +04:00
* Copyright IBM Corp . 1999 , 2009
2008-10-10 23:33:25 +04:00
* EMC Symmetrix ioctl Copyright EMC Corporation , 2008
* Author . . . . . . . . . : Nigel Hislop < hislop_nigel @ emc . com >
2005-04-17 02:20:36 +04:00
*/
2009-09-11 12:28:30 +04:00
# define KMSG_COMPONENT "dasd-eckd"
2009-03-26 17:23:49 +03:00
2005-04-17 02:20:36 +04:00
# include <linux/stddef.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/hdreg.h> /* HDIO_GETGEO */
# include <linux/bio.h>
# include <linux/module.h>
# include <linux/init.h>
# include <asm/debug.h>
# include <asm/idals.h>
# include <asm/ebcdic.h>
# include <asm/io.h>
# include <asm/todclk.h>
# include <asm/uaccess.h>
2006-06-29 17:08:18 +04:00
# include <asm/cio.h>
2005-04-17 02:20:36 +04:00
# include <asm/ccwdev.h>
2009-03-26 17:23:48 +03:00
# include <asm/itcw.h>
2005-04-17 02:20:36 +04:00
# include "dasd_int.h"
# include "dasd_eckd.h"
2009-03-26 17:23:48 +03:00
# include "../cio/chsc.h"
2005-04-17 02:20:36 +04:00
# ifdef PRINTK_HEADER
# undef PRINTK_HEADER
# endif /* PRINTK_HEADER */
# define PRINTK_HEADER "dasd(eckd):"
# define ECKD_C0(i) (i->home_bytes)
# define ECKD_F(i) (i->formula)
# define ECKD_F1(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f1):\
( i - > factors . f_0x02 . f1 ) )
# define ECKD_F2(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f2):\
( i - > factors . f_0x02 . f2 ) )
# define ECKD_F3(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f3):\
( i - > factors . f_0x02 . f3 ) )
# define ECKD_F4(i) (ECKD_F(i)==0x02?(i->factors.f_0x02.f4):0)
# define ECKD_F5(i) (ECKD_F(i)==0x02?(i->factors.f_0x02.f5):0)
# define ECKD_F6(i) (i->factor6)
# define ECKD_F7(i) (i->factor7)
# define ECKD_F8(i) (i->factor8)
MODULE_LICENSE ( " GPL " ) ;
static struct dasd_discipline dasd_eckd_discipline ;
/* The ccw bus type uses this table to find devices that it sends to
* dasd_eckd_probe */
static struct ccw_device_id dasd_eckd_ids [ ] = {
2006-07-12 18:41:55 +04:00
{ CCW_DEVICE_DEVTYPE ( 0x3990 , 0 , 0x3390 , 0 ) , . driver_info = 0x1 } ,
{ CCW_DEVICE_DEVTYPE ( 0x2105 , 0 , 0x3390 , 0 ) , . driver_info = 0x2 } ,
{ CCW_DEVICE_DEVTYPE ( 0x3880 , 0 , 0x3390 , 0 ) , . driver_info = 0x3 } ,
{ CCW_DEVICE_DEVTYPE ( 0x3990 , 0 , 0x3380 , 0 ) , . driver_info = 0x4 } ,
{ CCW_DEVICE_DEVTYPE ( 0x2105 , 0 , 0x3380 , 0 ) , . driver_info = 0x5 } ,
{ CCW_DEVICE_DEVTYPE ( 0x9343 , 0 , 0x9345 , 0 ) , . driver_info = 0x6 } ,
{ CCW_DEVICE_DEVTYPE ( 0x2107 , 0 , 0x3390 , 0 ) , . driver_info = 0x7 } ,
{ CCW_DEVICE_DEVTYPE ( 0x2107 , 0 , 0x3380 , 0 ) , . driver_info = 0x8 } ,
{ CCW_DEVICE_DEVTYPE ( 0x1750 , 0 , 0x3390 , 0 ) , . driver_info = 0x9 } ,
{ CCW_DEVICE_DEVTYPE ( 0x1750 , 0 , 0x3380 , 0 ) , . driver_info = 0xa } ,
2005-04-17 02:20:36 +04:00
{ /* end of list */ } ,
} ;
MODULE_DEVICE_TABLE ( ccw , dasd_eckd_ids ) ;
static struct ccw_driver dasd_eckd_driver ; /* see below */
/* initial attempt at a probe function. this can be simplified once
* the other detection code is gone */
static int
dasd_eckd_probe ( struct ccw_device * cdev )
{
int ret ;
2006-06-29 17:08:18 +04:00
/* set ECKD specific ccw-device options */
ret = ccw_device_set_options ( cdev , CCWDEV_ALLOW_FORCE ) ;
if ( ret ) {
2009-03-26 17:23:49 +03:00
DBF_EVENT ( DBF_WARNING ,
2006-06-29 17:08:18 +04:00
" dasd_eckd_probe: could not set ccw-device options "
2008-10-10 23:33:09 +04:00
" for %s \n " , dev_name ( & cdev - > dev ) ) ;
2005-04-17 02:20:36 +04:00
return ret ;
2006-06-29 17:08:18 +04:00
}
ret = dasd_generic_probe ( cdev , & dasd_eckd_discipline ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
static int
dasd_eckd_set_online ( struct ccw_device * cdev )
{
2006-06-29 17:08:18 +04:00
return dasd_generic_set_online ( cdev , & dasd_eckd_discipline ) ;
2005-04-17 02:20:36 +04:00
}
static const int sizes_trk0 [ ] = { 28 , 148 , 84 } ;
# define LABEL_SIZE 140
static inline unsigned int
round_up_multiple ( unsigned int no , unsigned int mult )
{
int rem = no % mult ;
return ( rem ? no - rem + mult : no ) ;
}
static inline unsigned int
ceil_quot ( unsigned int d1 , unsigned int d2 )
{
return ( d1 + ( d2 - 1 ) ) / d2 ;
}
2007-02-05 23:18:53 +03:00
static unsigned int
2005-04-17 02:20:36 +04:00
recs_per_track ( struct dasd_eckd_characteristics * rdc ,
unsigned int kl , unsigned int dl )
{
int dn , kn ;
switch ( rdc - > dev_type ) {
case 0x3380 :
if ( kl )
return 1499 / ( 15 + 7 + ceil_quot ( kl + 12 , 32 ) +
ceil_quot ( dl + 12 , 32 ) ) ;
else
return 1499 / ( 15 + ceil_quot ( dl + 12 , 32 ) ) ;
case 0x3390 :
dn = ceil_quot ( dl + 6 , 232 ) + 1 ;
if ( kl ) {
kn = ceil_quot ( kl + 6 , 232 ) + 1 ;
return 1729 / ( 10 + 9 + ceil_quot ( kl + 6 * kn , 34 ) +
9 + ceil_quot ( dl + 6 * dn , 34 ) ) ;
} else
return 1729 / ( 10 + 9 + ceil_quot ( dl + 6 * dn , 34 ) ) ;
case 0x9345 :
dn = ceil_quot ( dl + 6 , 232 ) + 1 ;
if ( kl ) {
kn = ceil_quot ( kl + 6 , 232 ) + 1 ;
return 1420 / ( 18 + 7 + ceil_quot ( kl + 6 * kn , 34 ) +
ceil_quot ( dl + 6 * dn , 34 ) ) ;
} else
return 1420 / ( 18 + 7 + ceil_quot ( dl + 6 * dn , 34 ) ) ;
}
return 0 ;
}
2009-03-26 17:23:47 +03:00
static void set_ch_t ( struct ch_t * geo , __u32 cyl , __u8 head )
{
geo - > cyl = ( __u16 ) cyl ;
geo - > head = cyl > > 16 ;
geo - > head < < = 4 ;
geo - > head | = head ;
}
2007-02-05 23:18:53 +03:00
static int
2005-04-17 02:20:36 +04:00
check_XRC ( struct ccw1 * de_ccw ,
struct DE_eckd_data * data ,
struct dasd_device * device )
{
struct dasd_eckd_private * private ;
2007-02-05 23:18:19 +03:00
int rc ;
2005-04-17 02:20:36 +04:00
private = ( struct dasd_eckd_private * ) device - > private ;
2007-02-05 23:18:19 +03:00
if ( ! private - > rdc_data . facilities . XRC_supported )
return 0 ;
2005-04-17 02:20:36 +04:00
/* switch on System Time Stamp - needed for XRC Support */
2007-02-05 23:18:19 +03:00
data - > ga_extended | = 0x08 ; /* switch on 'Time Stamp Valid' */
data - > ga_extended | = 0x02 ; /* switch on 'Extended Parameter' */
2005-04-17 02:20:36 +04:00
2007-02-05 23:18:19 +03:00
rc = get_sync_clock ( & data - > ep_sys_time ) ;
/* Ignore return code if sync clock is switched off. */
if ( rc = = - ENOSYS | | rc = = - EACCES )
rc = 0 ;
2005-04-17 02:20:36 +04:00
2008-01-26 16:11:23 +03:00
de_ccw - > count = sizeof ( struct DE_eckd_data ) ;
2007-02-05 23:18:19 +03:00
de_ccw - > flags | = CCW_FLAG_SLI ;
return rc ;
}
2005-04-17 02:20:36 +04:00
2007-02-05 23:18:53 +03:00
static int
2009-03-26 17:23:47 +03:00
define_extent ( struct ccw1 * ccw , struct DE_eckd_data * data , unsigned int trk ,
unsigned int totrk , int cmd , struct dasd_device * device )
2005-04-17 02:20:36 +04:00
{
struct dasd_eckd_private * private ;
2009-03-26 17:23:47 +03:00
u32 begcyl , endcyl ;
u16 heads , beghead , endhead ;
2007-02-05 23:18:19 +03:00
int rc = 0 ;
2005-04-17 02:20:36 +04:00
private = ( struct dasd_eckd_private * ) device - > private ;
ccw - > cmd_code = DASD_ECKD_CCW_DEFINE_EXTENT ;
ccw - > flags = 0 ;
ccw - > count = 16 ;
ccw - > cda = ( __u32 ) __pa ( data ) ;
2008-01-26 16:11:23 +03:00
memset ( data , 0 , sizeof ( struct DE_eckd_data ) ) ;
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
case DASD_ECKD_CCW_READ_HOME_ADDRESS :
case DASD_ECKD_CCW_READ_RECORD_ZERO :
case DASD_ECKD_CCW_READ :
case DASD_ECKD_CCW_READ_MT :
case DASD_ECKD_CCW_READ_CKD :
case DASD_ECKD_CCW_READ_CKD_MT :
case DASD_ECKD_CCW_READ_KD :
case DASD_ECKD_CCW_READ_KD_MT :
case DASD_ECKD_CCW_READ_COUNT :
data - > mask . perm = 0x1 ;
data - > attributes . operation = private - > attrib . operation ;
break ;
case DASD_ECKD_CCW_WRITE :
case DASD_ECKD_CCW_WRITE_MT :
case DASD_ECKD_CCW_WRITE_KD :
case DASD_ECKD_CCW_WRITE_KD_MT :
data - > mask . perm = 0x02 ;
data - > attributes . operation = private - > attrib . operation ;
2007-02-05 23:18:19 +03:00
rc = check_XRC ( ccw , data , device ) ;
2005-04-17 02:20:36 +04:00
break ;
case DASD_ECKD_CCW_WRITE_CKD :
case DASD_ECKD_CCW_WRITE_CKD_MT :
data - > attributes . operation = DASD_BYPASS_CACHE ;
2007-02-05 23:18:19 +03:00
rc = check_XRC ( ccw , data , device ) ;
2005-04-17 02:20:36 +04:00
break ;
case DASD_ECKD_CCW_ERASE :
case DASD_ECKD_CCW_WRITE_HOME_ADDRESS :
case DASD_ECKD_CCW_WRITE_RECORD_ZERO :
data - > mask . perm = 0x3 ;
data - > mask . auth = 0x1 ;
data - > attributes . operation = DASD_BYPASS_CACHE ;
2007-02-05 23:18:19 +03:00
rc = check_XRC ( ccw , data , device ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
2009-03-26 17:23:49 +03:00
dev_err ( & device - > cdev - > dev ,
" 0x%x is not a known command \n " , cmd ) ;
2005-04-17 02:20:36 +04:00
break ;
}
data - > attributes . mode = 0x3 ; /* ECKD */
if ( ( private - > rdc_data . cu_type = = 0x2105 | |
private - > rdc_data . cu_type = = 0x2107 | |
private - > rdc_data . cu_type = = 0x1750 )
& & ! ( private - > uses_cdl & & trk < 2 ) )
data - > ga_extended | = 0x40 ; /* Regular Data Format Mode */
2009-03-26 17:23:47 +03:00
heads = private - > rdc_data . trk_per_cyl ;
begcyl = trk / heads ;
beghead = trk % heads ;
endcyl = totrk / heads ;
endhead = totrk % heads ;
2005-04-17 02:20:36 +04:00
/* check for sequential prestage - enhance cylinder range */
if ( data - > attributes . operation = = DASD_SEQ_PRESTAGE | |
data - > attributes . operation = = DASD_SEQ_ACCESS ) {
2006-06-29 16:58:12 +04:00
2009-03-26 17:23:47 +03:00
if ( endcyl + private - > attrib . nr_cyl < private - > real_cyl )
endcyl + = private - > attrib . nr_cyl ;
2005-04-17 02:20:36 +04:00
else
2009-03-26 17:23:47 +03:00
endcyl = ( private - > real_cyl - 1 ) ;
2005-04-17 02:20:36 +04:00
}
2009-03-26 17:23:47 +03:00
set_ch_t ( & data - > beg_ext , begcyl , beghead ) ;
set_ch_t ( & data - > end_ext , endcyl , endhead ) ;
2007-02-05 23:18:19 +03:00
return rc ;
2005-04-17 02:20:36 +04:00
}
2008-01-26 16:11:23 +03:00
static int check_XRC_on_prefix ( struct PFX_eckd_data * pfxdata ,
struct dasd_device * device )
{
struct dasd_eckd_private * private ;
int rc ;
private = ( struct dasd_eckd_private * ) device - > private ;
if ( ! private - > rdc_data . facilities . XRC_supported )
return 0 ;
/* switch on System Time Stamp - needed for XRC Support */
2009-03-26 17:23:48 +03:00
pfxdata - > define_extent . ga_extended | = 0x08 ; /* 'Time Stamp Valid' */
pfxdata - > define_extent . ga_extended | = 0x02 ; /* 'Extended Parameter' */
2008-01-26 16:11:23 +03:00
pfxdata - > validity . time_stamp = 1 ; /* 'Time Stamp Valid' */
2009-03-26 17:23:48 +03:00
rc = get_sync_clock ( & pfxdata - > define_extent . ep_sys_time ) ;
2008-01-26 16:11:23 +03:00
/* Ignore return code if sync clock is switched off. */
if ( rc = = - ENOSYS | | rc = = - EACCES )
rc = 0 ;
return rc ;
}
2009-03-26 17:23:48 +03:00
static void fill_LRE_data ( struct LRE_eckd_data * data , unsigned int trk ,
unsigned int rec_on_trk , int count , int cmd ,
struct dasd_device * device , unsigned int reclen ,
unsigned int tlf )
{
struct dasd_eckd_private * private ;
int sector ;
int dn , d ;
private = ( struct dasd_eckd_private * ) device - > private ;
memset ( data , 0 , sizeof ( * data ) ) ;
sector = 0 ;
if ( rec_on_trk ) {
switch ( private - > rdc_data . dev_type ) {
case 0x3390 :
dn = ceil_quot ( reclen + 6 , 232 ) ;
d = 9 + ceil_quot ( reclen + 6 * ( dn + 1 ) , 34 ) ;
sector = ( 49 + ( rec_on_trk - 1 ) * ( 10 + d ) ) / 8 ;
break ;
case 0x3380 :
d = 7 + ceil_quot ( reclen + 12 , 32 ) ;
sector = ( 39 + ( rec_on_trk - 1 ) * ( 8 + d ) ) / 7 ;
break ;
}
}
data - > sector = sector ;
/* note: meaning of count depends on the operation
* for record based I / O it ' s the number of records , but for
* track based I / O it ' s the number of tracks
*/
data - > count = count ;
switch ( cmd ) {
case DASD_ECKD_CCW_WRITE_HOME_ADDRESS :
data - > operation . orientation = 0x3 ;
data - > operation . operation = 0x03 ;
break ;
case DASD_ECKD_CCW_READ_HOME_ADDRESS :
data - > operation . orientation = 0x3 ;
data - > operation . operation = 0x16 ;
break ;
case DASD_ECKD_CCW_WRITE_RECORD_ZERO :
data - > operation . orientation = 0x1 ;
data - > operation . operation = 0x03 ;
data - > count + + ;
break ;
case DASD_ECKD_CCW_READ_RECORD_ZERO :
data - > operation . orientation = 0x3 ;
data - > operation . operation = 0x16 ;
data - > count + + ;
break ;
case DASD_ECKD_CCW_WRITE :
case DASD_ECKD_CCW_WRITE_MT :
case DASD_ECKD_CCW_WRITE_KD :
case DASD_ECKD_CCW_WRITE_KD_MT :
data - > auxiliary . length_valid = 0x1 ;
data - > length = reclen ;
data - > operation . operation = 0x01 ;
break ;
case DASD_ECKD_CCW_WRITE_CKD :
case DASD_ECKD_CCW_WRITE_CKD_MT :
data - > auxiliary . length_valid = 0x1 ;
data - > length = reclen ;
data - > operation . operation = 0x03 ;
break ;
case DASD_ECKD_CCW_WRITE_TRACK_DATA :
data - > auxiliary . length_valid = 0x1 ;
data - > length = reclen ; /* not tlf, as one might think */
data - > operation . operation = 0x3F ;
data - > extended_operation = 0x23 ;
break ;
case DASD_ECKD_CCW_READ :
case DASD_ECKD_CCW_READ_MT :
case DASD_ECKD_CCW_READ_KD :
case DASD_ECKD_CCW_READ_KD_MT :
data - > auxiliary . length_valid = 0x1 ;
data - > length = reclen ;
data - > operation . operation = 0x06 ;
break ;
case DASD_ECKD_CCW_READ_CKD :
case DASD_ECKD_CCW_READ_CKD_MT :
data - > auxiliary . length_valid = 0x1 ;
data - > length = reclen ;
data - > operation . operation = 0x16 ;
break ;
case DASD_ECKD_CCW_READ_COUNT :
data - > operation . operation = 0x06 ;
break ;
case DASD_ECKD_CCW_READ_TRACK_DATA :
data - > auxiliary . length_valid = 0x1 ;
data - > length = tlf ;
data - > operation . operation = 0x0C ;
break ;
case DASD_ECKD_CCW_ERASE :
data - > length = reclen ;
data - > auxiliary . length_valid = 0x1 ;
data - > operation . operation = 0x0b ;
break ;
default :
DBF_DEV_EVENT ( DBF_ERR , device ,
" fill LRE unknown opcode 0x%x " , cmd ) ;
BUG ( ) ;
}
set_ch_t ( & data - > seek_addr ,
trk / private - > rdc_data . trk_per_cyl ,
trk % private - > rdc_data . trk_per_cyl ) ;
data - > search_arg . cyl = data - > seek_addr . cyl ;
data - > search_arg . head = data - > seek_addr . head ;
data - > search_arg . record = rec_on_trk ;
}
static int prefix_LRE ( struct ccw1 * ccw , struct PFX_eckd_data * pfxdata ,
unsigned int trk , unsigned int totrk , int cmd ,
struct dasd_device * basedev , struct dasd_device * startdev ,
unsigned char format , unsigned int rec_on_trk , int count ,
unsigned int blksize , unsigned int tlf )
2008-01-26 16:11:23 +03:00
{
struct dasd_eckd_private * basepriv , * startpriv ;
2009-03-26 17:23:48 +03:00
struct DE_eckd_data * dedata ;
struct LRE_eckd_data * lredata ;
2009-03-26 17:23:47 +03:00
u32 begcyl , endcyl ;
u16 heads , beghead , endhead ;
2008-01-26 16:11:23 +03:00
int rc = 0 ;
basepriv = ( struct dasd_eckd_private * ) basedev - > private ;
startpriv = ( struct dasd_eckd_private * ) startdev - > private ;
2009-03-26 17:23:48 +03:00
dedata = & pfxdata - > define_extent ;
lredata = & pfxdata - > locate_record ;
2008-01-26 16:11:23 +03:00
ccw - > cmd_code = DASD_ECKD_CCW_PFX ;
ccw - > flags = 0 ;
ccw - > count = sizeof ( * pfxdata ) ;
ccw - > cda = ( __u32 ) __pa ( pfxdata ) ;
memset ( pfxdata , 0 , sizeof ( * pfxdata ) ) ;
/* prefix data */
2009-03-26 17:23:48 +03:00
if ( format > 1 ) {
DBF_DEV_EVENT ( DBF_ERR , basedev ,
" PFX LRE unknown format 0x%x " , format ) ;
BUG ( ) ;
return - EINVAL ;
}
pfxdata - > format = format ;
2008-08-01 18:39:09 +04:00
pfxdata - > base_address = basepriv - > ned - > unit_addr ;
pfxdata - > base_lss = basepriv - > ned - > ID ;
2009-03-26 17:23:48 +03:00
pfxdata - > validity . define_extent = 1 ;
2008-01-26 16:11:23 +03:00
/* private uid is kept up to date, conf_data may be outdated */
if ( startpriv - > uid . type ! = UA_BASE_DEVICE ) {
pfxdata - > validity . verify_base = 1 ;
if ( startpriv - > uid . type = = UA_HYPER_PAV_ALIAS )
pfxdata - > validity . hyper_pav = 1 ;
}
/* define extend data (mostly)*/
switch ( cmd ) {
case DASD_ECKD_CCW_READ_HOME_ADDRESS :
case DASD_ECKD_CCW_READ_RECORD_ZERO :
case DASD_ECKD_CCW_READ :
case DASD_ECKD_CCW_READ_MT :
case DASD_ECKD_CCW_READ_CKD :
case DASD_ECKD_CCW_READ_CKD_MT :
case DASD_ECKD_CCW_READ_KD :
case DASD_ECKD_CCW_READ_KD_MT :
case DASD_ECKD_CCW_READ_COUNT :
2009-03-26 17:23:48 +03:00
dedata - > mask . perm = 0x1 ;
dedata - > attributes . operation = basepriv - > attrib . operation ;
break ;
case DASD_ECKD_CCW_READ_TRACK_DATA :
dedata - > mask . perm = 0x1 ;
dedata - > attributes . operation = basepriv - > attrib . operation ;
dedata - > blk_size = 0 ;
2008-01-26 16:11:23 +03:00
break ;
case DASD_ECKD_CCW_WRITE :
case DASD_ECKD_CCW_WRITE_MT :
case DASD_ECKD_CCW_WRITE_KD :
case DASD_ECKD_CCW_WRITE_KD_MT :
2009-03-26 17:23:48 +03:00
dedata - > mask . perm = 0x02 ;
dedata - > attributes . operation = basepriv - > attrib . operation ;
2008-01-26 16:11:23 +03:00
rc = check_XRC_on_prefix ( pfxdata , basedev ) ;
break ;
case DASD_ECKD_CCW_WRITE_CKD :
case DASD_ECKD_CCW_WRITE_CKD_MT :
2009-03-26 17:23:48 +03:00
dedata - > attributes . operation = DASD_BYPASS_CACHE ;
2008-01-26 16:11:23 +03:00
rc = check_XRC_on_prefix ( pfxdata , basedev ) ;
break ;
case DASD_ECKD_CCW_ERASE :
case DASD_ECKD_CCW_WRITE_HOME_ADDRESS :
case DASD_ECKD_CCW_WRITE_RECORD_ZERO :
2009-03-26 17:23:48 +03:00
dedata - > mask . perm = 0x3 ;
dedata - > mask . auth = 0x1 ;
dedata - > attributes . operation = DASD_BYPASS_CACHE ;
2008-01-26 16:11:23 +03:00
rc = check_XRC_on_prefix ( pfxdata , basedev ) ;
break ;
2009-03-26 17:23:48 +03:00
case DASD_ECKD_CCW_WRITE_TRACK_DATA :
dedata - > mask . perm = 0x02 ;
dedata - > attributes . operation = basepriv - > attrib . operation ;
dedata - > blk_size = blksize ;
rc = check_XRC_on_prefix ( pfxdata , basedev ) ;
2008-01-26 16:11:23 +03:00
break ;
2009-03-26 17:23:48 +03:00
default :
DBF_DEV_EVENT ( DBF_ERR , basedev ,
" PFX LRE unknown opcode 0x%x " , cmd ) ;
BUG ( ) ;
return - EINVAL ;
2008-01-26 16:11:23 +03:00
}
2009-03-26 17:23:48 +03:00
dedata - > attributes . mode = 0x3 ; /* ECKD */
2008-01-26 16:11:23 +03:00
if ( ( basepriv - > rdc_data . cu_type = = 0x2105 | |
basepriv - > rdc_data . cu_type = = 0x2107 | |
basepriv - > rdc_data . cu_type = = 0x1750 )
& & ! ( basepriv - > uses_cdl & & trk < 2 ) )
2009-03-26 17:23:48 +03:00
dedata - > ga_extended | = 0x40 ; /* Regular Data Format Mode */
2008-01-26 16:11:23 +03:00
2009-03-26 17:23:47 +03:00
heads = basepriv - > rdc_data . trk_per_cyl ;
begcyl = trk / heads ;
beghead = trk % heads ;
endcyl = totrk / heads ;
endhead = totrk % heads ;
2008-01-26 16:11:23 +03:00
/* check for sequential prestage - enhance cylinder range */
2009-03-26 17:23:48 +03:00
if ( dedata - > attributes . operation = = DASD_SEQ_PRESTAGE | |
dedata - > attributes . operation = = DASD_SEQ_ACCESS ) {
2008-01-26 16:11:23 +03:00
2009-03-26 17:23:47 +03:00
if ( endcyl + basepriv - > attrib . nr_cyl < basepriv - > real_cyl )
endcyl + = basepriv - > attrib . nr_cyl ;
2008-01-26 16:11:23 +03:00
else
2009-03-26 17:23:47 +03:00
endcyl = ( basepriv - > real_cyl - 1 ) ;
2008-01-26 16:11:23 +03:00
}
2009-03-26 17:23:48 +03:00
set_ch_t ( & dedata - > beg_ext , begcyl , beghead ) ;
set_ch_t ( & dedata - > end_ext , endcyl , endhead ) ;
if ( format = = 1 ) {
fill_LRE_data ( lredata , trk , rec_on_trk , count , cmd ,
basedev , blksize , tlf ) ;
}
2008-01-26 16:11:23 +03:00
return rc ;
}
2009-03-26 17:23:48 +03:00
static int prefix ( struct ccw1 * ccw , struct PFX_eckd_data * pfxdata ,
unsigned int trk , unsigned int totrk , int cmd ,
struct dasd_device * basedev , struct dasd_device * startdev )
{
return prefix_LRE ( ccw , pfxdata , trk , totrk , cmd , basedev , startdev ,
0 , 0 , 0 , 0 , 0 ) ;
}
2007-02-05 23:18:53 +03:00
static void
2009-03-26 17:23:47 +03:00
locate_record ( struct ccw1 * ccw , struct LO_eckd_data * data , unsigned int trk ,
unsigned int rec_on_trk , int no_rec , int cmd ,
2005-04-17 02:20:36 +04:00
struct dasd_device * device , int reclen )
{
struct dasd_eckd_private * private ;
int sector ;
int dn , d ;
2006-06-29 16:58:12 +04:00
2005-04-17 02:20:36 +04:00
private = ( struct dasd_eckd_private * ) device - > private ;
DBF_DEV_EVENT ( DBF_INFO , device ,
" Locate: trk %d, rec %d, no_rec %d, cmd %d, reclen %d " ,
trk , rec_on_trk , no_rec , cmd , reclen ) ;
ccw - > cmd_code = DASD_ECKD_CCW_LOCATE_RECORD ;
ccw - > flags = 0 ;
ccw - > count = 16 ;
ccw - > cda = ( __u32 ) __pa ( data ) ;
2008-01-26 16:11:23 +03:00
memset ( data , 0 , sizeof ( struct LO_eckd_data ) ) ;
2005-04-17 02:20:36 +04:00
sector = 0 ;
if ( rec_on_trk ) {
switch ( private - > rdc_data . dev_type ) {
case 0x3390 :
dn = ceil_quot ( reclen + 6 , 232 ) ;
d = 9 + ceil_quot ( reclen + 6 * ( dn + 1 ) , 34 ) ;
sector = ( 49 + ( rec_on_trk - 1 ) * ( 10 + d ) ) / 8 ;
break ;
case 0x3380 :
d = 7 + ceil_quot ( reclen + 12 , 32 ) ;
sector = ( 39 + ( rec_on_trk - 1 ) * ( 8 + d ) ) / 7 ;
break ;
}
}
data - > sector = sector ;
data - > count = no_rec ;
switch ( cmd ) {
case DASD_ECKD_CCW_WRITE_HOME_ADDRESS :
data - > operation . orientation = 0x3 ;
data - > operation . operation = 0x03 ;
break ;
case DASD_ECKD_CCW_READ_HOME_ADDRESS :
data - > operation . orientation = 0x3 ;
data - > operation . operation = 0x16 ;
break ;
case DASD_ECKD_CCW_WRITE_RECORD_ZERO :
data - > operation . orientation = 0x1 ;
data - > operation . operation = 0x03 ;
data - > count + + ;
break ;
case DASD_ECKD_CCW_READ_RECORD_ZERO :
data - > operation . orientation = 0x3 ;
data - > operation . operation = 0x16 ;
data - > count + + ;
break ;
case DASD_ECKD_CCW_WRITE :
case DASD_ECKD_CCW_WRITE_MT :
case DASD_ECKD_CCW_WRITE_KD :
case DASD_ECKD_CCW_WRITE_KD_MT :
data - > auxiliary . last_bytes_used = 0x1 ;
data - > length = reclen ;
data - > operation . operation = 0x01 ;
break ;
case DASD_ECKD_CCW_WRITE_CKD :
case DASD_ECKD_CCW_WRITE_CKD_MT :
data - > auxiliary . last_bytes_used = 0x1 ;
data - > length = reclen ;
data - > operation . operation = 0x03 ;
break ;
case DASD_ECKD_CCW_READ :
case DASD_ECKD_CCW_READ_MT :
case DASD_ECKD_CCW_READ_KD :
case DASD_ECKD_CCW_READ_KD_MT :
data - > auxiliary . last_bytes_used = 0x1 ;
data - > length = reclen ;
data - > operation . operation = 0x06 ;
break ;
case DASD_ECKD_CCW_READ_CKD :
case DASD_ECKD_CCW_READ_CKD_MT :
data - > auxiliary . last_bytes_used = 0x1 ;
data - > length = reclen ;
data - > operation . operation = 0x16 ;
break ;
case DASD_ECKD_CCW_READ_COUNT :
data - > operation . operation = 0x06 ;
break ;
case DASD_ECKD_CCW_ERASE :
data - > length = reclen ;
data - > auxiliary . last_bytes_used = 0x1 ;
data - > operation . operation = 0x0b ;
break ;
default :
2009-03-26 17:23:49 +03:00
DBF_DEV_EVENT ( DBF_ERR , device , " unknown locate record "
" opcode 0x%x " , cmd ) ;
2005-04-17 02:20:36 +04:00
}
2009-03-26 17:23:47 +03:00
set_ch_t ( & data - > seek_addr ,
trk / private - > rdc_data . trk_per_cyl ,
trk % private - > rdc_data . trk_per_cyl ) ;
data - > search_arg . cyl = data - > seek_addr . cyl ;
data - > search_arg . head = data - > seek_addr . head ;
2005-04-17 02:20:36 +04:00
data - > search_arg . record = rec_on_trk ;
}
/*
* Returns 1 if the block is one of the special blocks that needs
* to get read / written with the KD variant of the command .
* That is DASD_ECKD_READ_KD_MT instead of DASD_ECKD_READ_MT and
* DASD_ECKD_WRITE_KD_MT instead of DASD_ECKD_WRITE_MT .
* Luckily the KD variants differ only by one bit ( 0x08 ) from the
* normal variant . So don ' t wonder about code like :
* if ( dasd_eckd_cdl_special ( blk_per_trk , recid ) )
* ccw - > cmd_code | = 0x8 ;
*/
static inline int
dasd_eckd_cdl_special ( int blk_per_trk , int recid )
{
if ( recid < 3 )
return 1 ;
if ( recid < blk_per_trk )
return 0 ;
if ( recid < 2 * blk_per_trk )
return 1 ;
return 0 ;
}
/*
* Returns the record size for the special blocks of the cdl format .
* Only returns something useful if dasd_eckd_cdl_special is true
* for the recid .
*/
static inline int
dasd_eckd_cdl_reclen ( int recid )
{
if ( recid < 3 )
return sizes_trk0 [ recid ] ;
return LABEL_SIZE ;
}
2006-04-28 05:40:28 +04:00
/*
* Generate device unique id that specifies the physical device .
*/
2008-08-01 18:39:09 +04:00
static int dasd_eckd_generate_uid ( struct dasd_device * device ,
struct dasd_uid * uid )
2006-04-28 05:40:28 +04:00
{
struct dasd_eckd_private * private ;
2008-08-01 18:39:09 +04:00
int count ;
2006-04-28 05:40:28 +04:00
private = ( struct dasd_eckd_private * ) device - > private ;
if ( ! private )
return - ENODEV ;
2008-08-01 18:39:09 +04:00
if ( ! private - > ned | | ! private - > gneq )
2006-04-28 05:40:28 +04:00
return - ENODEV ;
memset ( uid , 0 , sizeof ( struct dasd_uid ) ) ;
2008-08-01 18:39:09 +04:00
memcpy ( uid - > vendor , private - > ned - > HDA_manufacturer ,
2006-08-10 17:45:16 +04:00
sizeof ( uid - > vendor ) - 1 ) ;
2006-04-28 05:40:28 +04:00
EBCASC ( uid - > vendor , sizeof ( uid - > vendor ) - 1 ) ;
2008-08-01 18:39:09 +04:00
memcpy ( uid - > serial , private - > ned - > HDA_location ,
2006-08-10 17:45:16 +04:00
sizeof ( uid - > serial ) - 1 ) ;
2006-04-28 05:40:28 +04:00
EBCASC ( uid - > serial , sizeof ( uid - > serial ) - 1 ) ;
2008-08-01 18:39:09 +04:00
uid - > ssid = private - > gneq - > subsystemID ;
2009-08-18 22:18:35 +04:00
uid - > real_unit_addr = private - > ned - > unit_addr ;
2008-08-01 18:39:09 +04:00
if ( private - > sneq ) {
uid - > type = private - > sneq - > sua_flags ;
2008-01-26 16:11:23 +03:00
if ( uid - > type = = UA_BASE_PAV_ALIAS )
2008-08-01 18:39:09 +04:00
uid - > base_unit_addr = private - > sneq - > base_unit_addr ;
2008-01-26 16:11:23 +03:00
} else {
uid - > type = UA_BASE_DEVICE ;
}
2008-08-01 18:39:09 +04:00
if ( private - > vdsneq ) {
for ( count = 0 ; count < 16 ; count + + ) {
sprintf ( uid - > vduit + 2 * count , " %02x " ,
private - > vdsneq - > uit [ count ] ) ;
}
}
2006-04-28 05:40:28 +04:00
return 0 ;
}
2007-05-10 17:45:46 +04:00
static struct dasd_ccw_req * dasd_eckd_build_rcd_lpm ( struct dasd_device * device ,
void * rcd_buffer ,
struct ciw * ciw , __u8 lpm )
2007-05-04 20:47:51 +04:00
{
struct dasd_ccw_req * cqr ;
struct ccw1 * ccw ;
2009-09-11 12:28:29 +04:00
cqr = dasd_smalloc_request ( DASD_ECKD_MAGIC , 1 /* RCD */ , ciw - > count ,
device ) ;
2007-05-04 20:47:51 +04:00
if ( IS_ERR ( cqr ) ) {
2009-03-26 17:23:49 +03:00
DBF_DEV_EVENT ( DBF_WARNING , device , " %s " ,
" Could not allocate RCD request " ) ;
2007-05-04 20:47:51 +04:00
return cqr ;
}
ccw = cqr - > cpaddr ;
ccw - > cmd_code = ciw - > cmd ;
ccw - > cda = ( __u32 ) ( addr_t ) rcd_buffer ;
ccw - > count = ciw - > count ;
2008-01-26 16:11:23 +03:00
cqr - > startdev = device ;
cqr - > memdev = device ;
cqr - > block = NULL ;
2007-05-04 20:47:51 +04:00
cqr - > expires = 10 * HZ ;
cqr - > lpm = lpm ;
clear_bit ( DASD_CQR_FLAGS_USE_ERP , & cqr - > flags ) ;
cqr - > retries = 2 ;
cqr - > buildclk = get_clock ( ) ;
cqr - > status = DASD_CQR_FILLED ;
return cqr ;
}
static int dasd_eckd_read_conf_lpm ( struct dasd_device * device ,
void * * rcd_buffer ,
int * rcd_buffer_size , __u8 lpm )
{
struct ciw * ciw ;
char * rcd_buf = NULL ;
int ret ;
struct dasd_ccw_req * cqr ;
/*
* scan for RCD command in extended SenseID data
*/
ciw = ccw_device_get_ciw ( device - > cdev , CIW_TYPE_RCD ) ;
if ( ! ciw | | ciw - > cmd = = 0 ) {
ret = - EOPNOTSUPP ;
goto out_error ;
}
rcd_buf = kzalloc ( ciw - > count , GFP_KERNEL | GFP_DMA ) ;
if ( ! rcd_buf ) {
ret = - ENOMEM ;
goto out_error ;
}
2008-08-01 18:39:09 +04:00
/*
* buffer has to start with EBCDIC " V1.0 " to show
* support for virtual device SNEQ
*/
rcd_buf [ 0 ] = 0xE5 ;
rcd_buf [ 1 ] = 0xF1 ;
rcd_buf [ 2 ] = 0x4B ;
rcd_buf [ 3 ] = 0xF0 ;
2007-05-04 20:47:51 +04:00
cqr = dasd_eckd_build_rcd_lpm ( device , rcd_buf , ciw , lpm ) ;
if ( IS_ERR ( cqr ) ) {
ret = PTR_ERR ( cqr ) ;
goto out_error ;
}
ret = dasd_sleep_on ( cqr ) ;
/*
* on success we update the user input parms
*/
2008-01-26 16:11:23 +03:00
dasd_sfree_request ( cqr , cqr - > memdev ) ;
2007-05-04 20:47:51 +04:00
if ( ret )
goto out_error ;
* rcd_buffer_size = ciw - > count ;
* rcd_buffer = rcd_buf ;
return 0 ;
out_error :
kfree ( rcd_buf ) ;
* rcd_buffer = NULL ;
* rcd_buffer_size = 0 ;
return ret ;
}
2008-08-01 18:39:09 +04:00
static int dasd_eckd_identify_conf_parts ( struct dasd_eckd_private * private )
{
struct dasd_sneq * sneq ;
int i , count ;
private - > ned = NULL ;
private - > sneq = NULL ;
private - > vdsneq = NULL ;
private - > gneq = NULL ;
count = private - > conf_len / sizeof ( struct dasd_sneq ) ;
sneq = ( struct dasd_sneq * ) private - > conf_data ;
for ( i = 0 ; i < count ; + + i ) {
if ( sneq - > flags . identifier = = 1 & & sneq - > format = = 1 )
private - > sneq = sneq ;
else if ( sneq - > flags . identifier = = 1 & & sneq - > format = = 4 )
private - > vdsneq = ( struct vd_sneq * ) sneq ;
else if ( sneq - > flags . identifier = = 2 )
private - > gneq = ( struct dasd_gneq * ) sneq ;
else if ( sneq - > flags . identifier = = 3 & & sneq - > res1 = = 1 )
private - > ned = ( struct dasd_ned * ) sneq ;
sneq + + ;
}
if ( ! private - > ned | | ! private - > gneq ) {
private - > ned = NULL ;
private - > sneq = NULL ;
private - > vdsneq = NULL ;
private - > gneq = NULL ;
return - EINVAL ;
}
return 0 ;
} ;
static unsigned char dasd_eckd_path_access ( void * conf_data , int conf_len )
{
struct dasd_gneq * gneq ;
int i , count , found ;
count = conf_len / sizeof ( * gneq ) ;
gneq = ( struct dasd_gneq * ) conf_data ;
found = 0 ;
for ( i = 0 ; i < count ; + + i ) {
if ( gneq - > flags . identifier = = 2 ) {
found = 1 ;
break ;
}
gneq + + ;
}
if ( found )
return ( ( char * ) gneq ) [ 18 ] & 0x07 ;
else
return 0 ;
}
static int dasd_eckd_read_conf ( struct dasd_device * device )
2005-04-17 02:20:36 +04:00
{
void * conf_data ;
int conf_len , conf_data_saved ;
int rc ;
__u8 lpm ;
struct dasd_eckd_private * private ;
struct dasd_eckd_path * path_data ;
private = ( struct dasd_eckd_private * ) device - > private ;
path_data = ( struct dasd_eckd_path * ) & private - > path_data ;
path_data - > opm = ccw_device_get_path_mask ( device - > cdev ) ;
lpm = 0x80 ;
conf_data_saved = 0 ;
/* get configuration data per operational path */
for ( lpm = 0x80 ; lpm ; lpm > > = 1 ) {
if ( lpm & path_data - > opm ) {
2007-05-04 20:47:51 +04:00
rc = dasd_eckd_read_conf_lpm ( device , & conf_data ,
& conf_len , lpm ) ;
2005-04-17 02:20:36 +04:00
if ( rc & & rc ! = - EOPNOTSUPP ) { /* -EOPNOTSUPP is ok */
2009-03-26 17:23:49 +03:00
DBF_EVENT ( DBF_WARNING ,
" Read configuration data returned "
" error %d for device: %s " , rc ,
dev_name ( & device - > cdev - > dev ) ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
if ( conf_data = = NULL ) {
2009-03-26 17:23:49 +03:00
DBF_EVENT ( DBF_WARNING , " No configuration "
" data retrieved for device: %s " ,
dev_name ( & device - > cdev - > dev ) ) ;
2007-07-31 11:39:19 +04:00
continue ; /* no error */
2005-04-17 02:20:36 +04:00
}
/* save first valid configuration data */
2008-08-01 18:39:09 +04:00
if ( ! conf_data_saved ) {
kfree ( private - > conf_data ) ;
private - > conf_data = conf_data ;
private - > conf_len = conf_len ;
if ( dasd_eckd_identify_conf_parts ( private ) ) {
private - > conf_data = NULL ;
private - > conf_len = 0 ;
kfree ( conf_data ) ;
continue ;
}
2005-04-17 02:20:36 +04:00
conf_data_saved + + ;
}
2008-08-01 18:39:09 +04:00
switch ( dasd_eckd_path_access ( conf_data , conf_len ) ) {
2005-04-17 02:20:36 +04:00
case 0x02 :
path_data - > npm | = lpm ;
break ;
case 0x03 :
path_data - > ppm | = lpm ;
break ;
}
2008-08-01 18:39:09 +04:00
if ( conf_data ! = private - > conf_data )
kfree ( conf_data ) ;
2005-04-17 02:20:36 +04:00
}
}
return 0 ;
}
2008-01-26 16:11:23 +03:00
static int dasd_eckd_read_features ( struct dasd_device * device )
{
struct dasd_psf_prssd_data * prssdp ;
struct dasd_rssd_features * features ;
struct dasd_ccw_req * cqr ;
struct ccw1 * ccw ;
int rc ;
struct dasd_eckd_private * private ;
private = ( struct dasd_eckd_private * ) device - > private ;
2009-09-23 00:58:52 +04:00
memset ( & private - > features , 0 , sizeof ( struct dasd_rssd_features ) ) ;
2009-09-11 12:28:29 +04:00
cqr = dasd_smalloc_request ( DASD_ECKD_MAGIC , 1 /* PSF */ + 1 /* RSSD */ ,
2008-01-26 16:11:23 +03:00
( sizeof ( struct dasd_psf_prssd_data ) +
sizeof ( struct dasd_rssd_features ) ) ,
device ) ;
if ( IS_ERR ( cqr ) ) {
2009-03-26 17:23:49 +03:00
DBF_EVENT ( DBF_WARNING , " Could not allocate initialization "
" request for device: %s " ,
dev_name ( & device - > cdev - > dev ) ) ;
2008-01-26 16:11:23 +03:00
return PTR_ERR ( cqr ) ;
}
cqr - > startdev = device ;
cqr - > memdev = device ;
cqr - > block = NULL ;
clear_bit ( DASD_CQR_FLAGS_USE_ERP , & cqr - > flags ) ;
cqr - > retries = 5 ;
cqr - > expires = 10 * HZ ;
/* Prepare for Read Subsystem Data */
prssdp = ( struct dasd_psf_prssd_data * ) cqr - > data ;
memset ( prssdp , 0 , sizeof ( struct dasd_psf_prssd_data ) ) ;
prssdp - > order = PSF_ORDER_PRSSD ;
prssdp - > suborder = 0x41 ; /* Read Feature Codes */
/* all other bytes of prssdp must be zero */
ccw = cqr - > cpaddr ;
ccw - > cmd_code = DASD_ECKD_CCW_PSF ;
ccw - > count = sizeof ( struct dasd_psf_prssd_data ) ;
ccw - > flags | = CCW_FLAG_CC ;
ccw - > cda = ( __u32 ) ( addr_t ) prssdp ;
/* Read Subsystem Data - feature codes */
features = ( struct dasd_rssd_features * ) ( prssdp + 1 ) ;
memset ( features , 0 , sizeof ( struct dasd_rssd_features ) ) ;
ccw + + ;
ccw - > cmd_code = DASD_ECKD_CCW_RSSD ;
ccw - > count = sizeof ( struct dasd_rssd_features ) ;
ccw - > cda = ( __u32 ) ( addr_t ) features ;
cqr - > buildclk = get_clock ( ) ;
cqr - > status = DASD_CQR_FILLED ;
rc = dasd_sleep_on ( cqr ) ;
if ( rc = = 0 ) {
prssdp = ( struct dasd_psf_prssd_data * ) cqr - > data ;
features = ( struct dasd_rssd_features * ) ( prssdp + 1 ) ;
memcpy ( & private - > features , features ,
sizeof ( struct dasd_rssd_features ) ) ;
2009-09-23 00:58:52 +04:00
} else
dev_warn ( & device - > cdev - > dev , " Reading device feature codes "
" failed with rc=%d \n " , rc ) ;
2008-01-26 16:11:23 +03:00
dasd_sfree_request ( cqr , cqr - > memdev ) ;
return rc ;
}
2006-06-29 17:08:18 +04:00
/*
* Build CP for Perform Subsystem Function - SSC .
*/
2009-03-26 17:23:48 +03:00
static struct dasd_ccw_req * dasd_eckd_build_psf_ssc ( struct dasd_device * device ,
int enable_pav )
2006-06-29 17:08:18 +04:00
{
2008-01-26 16:11:23 +03:00
struct dasd_ccw_req * cqr ;
struct dasd_psf_ssc_data * psf_ssc_data ;
struct ccw1 * ccw ;
2006-06-29 17:08:18 +04:00
2009-09-11 12:28:29 +04:00
cqr = dasd_smalloc_request ( DASD_ECKD_MAGIC , 1 /* PSF */ ,
2006-06-29 17:08:18 +04:00
sizeof ( struct dasd_psf_ssc_data ) ,
device ) ;
2008-01-26 16:11:23 +03:00
if ( IS_ERR ( cqr ) ) {
2009-03-26 17:23:49 +03:00
DBF_DEV_EVENT ( DBF_WARNING , device , " %s " ,
2006-06-29 17:08:18 +04:00
" Could not allocate PSF-SSC request " ) ;
2008-01-26 16:11:23 +03:00
return cqr ;
}
psf_ssc_data = ( struct dasd_psf_ssc_data * ) cqr - > data ;
psf_ssc_data - > order = PSF_ORDER_SSC ;
2009-03-26 17:23:48 +03:00
psf_ssc_data - > suborder = 0x40 ;
if ( enable_pav ) {
psf_ssc_data - > suborder | = 0x88 ;
psf_ssc_data - > reserved [ 0 ] = 0x88 ;
}
2008-01-26 16:11:23 +03:00
ccw = cqr - > cpaddr ;
ccw - > cmd_code = DASD_ECKD_CCW_PSF ;
ccw - > cda = ( __u32 ) ( addr_t ) psf_ssc_data ;
ccw - > count = 66 ;
cqr - > startdev = device ;
cqr - > memdev = device ;
cqr - > block = NULL ;
cqr - > expires = 10 * HZ ;
cqr - > buildclk = get_clock ( ) ;
cqr - > status = DASD_CQR_FILLED ;
return cqr ;
2006-06-29 17:08:18 +04:00
}
/*
* Perform Subsystem Function .
* It is necessary to trigger CIO for channel revalidation since this
* call might change behaviour of DASD devices .
*/
static int
2009-03-26 17:23:48 +03:00
dasd_eckd_psf_ssc ( struct dasd_device * device , int enable_pav )
2006-06-29 17:08:18 +04:00
{
2008-01-26 16:11:23 +03:00
struct dasd_ccw_req * cqr ;
int rc ;
2009-03-26 17:23:48 +03:00
cqr = dasd_eckd_build_psf_ssc ( device , enable_pav ) ;
2008-01-26 16:11:23 +03:00
if ( IS_ERR ( cqr ) )
return PTR_ERR ( cqr ) ;
rc = dasd_sleep_on ( cqr ) ;
if ( ! rc )
/* trigger CIO to reprobe devices */
css_schedule_reprobe ( ) ;
dasd_sfree_request ( cqr , cqr - > memdev ) ;
return rc ;
2006-06-29 17:08:18 +04:00
}
/*
* Valide storage server of current device .
*/
2008-01-26 16:11:23 +03:00
static int dasd_eckd_validate_server ( struct dasd_device * device )
2006-06-29 17:08:18 +04:00
{
int rc ;
2008-01-26 16:11:23 +03:00
struct dasd_eckd_private * private ;
2009-03-26 17:23:48 +03:00
int enable_pav ;
2006-06-29 17:08:18 +04:00
if ( dasd_nopav | | MACHINE_IS_VM )
2009-03-26 17:23:48 +03:00
enable_pav = 0 ;
else
enable_pav = 1 ;
rc = dasd_eckd_psf_ssc ( device , enable_pav ) ;
2006-08-24 15:22:36 +04:00
/* may be requested feature is not available on server,
* therefore just report error and go ahead */
2008-01-26 16:11:23 +03:00
private = ( struct dasd_eckd_private * ) device - > private ;
2009-03-26 17:23:49 +03:00
DBF_EVENT ( DBF_WARNING , " PSF-SSC on storage subsystem %s.%s.%04x "
" returned rc=%d for device: %s " ,
private - > uid . vendor , private - > uid . serial ,
private - > uid . ssid , rc , dev_name ( & device - > cdev - > dev ) ) ;
2006-06-29 17:08:18 +04:00
/* RE-Read Configuration Data */
return dasd_eckd_read_conf ( device ) ;
}
2006-04-28 05:40:28 +04:00
/*
* Check device characteristics .
* If the device is accessible using ECKD discipline , the device is enabled .
*/
2005-04-17 02:20:36 +04:00
static int
dasd_eckd_check_characteristics ( struct dasd_device * device )
{
struct dasd_eckd_private * private ;
2008-01-26 16:11:23 +03:00
struct dasd_block * block ;
int is_known , rc ;
2005-04-17 02:20:36 +04:00
private = ( struct dasd_eckd_private * ) device - > private ;
2009-06-12 12:26:37 +04:00
if ( ! private ) {
private = kzalloc ( sizeof ( * private ) , GFP_KERNEL | GFP_DMA ) ;
if ( ! private ) {
2009-03-26 17:23:49 +03:00
dev_warn ( & device - > cdev - > dev ,
" Allocating memory for private DASD data "
" failed \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
device - > private = ( void * ) private ;
2009-06-12 12:26:37 +04:00
} else {
memset ( private , 0 , sizeof ( * private ) ) ;
2005-04-17 02:20:36 +04:00
}
/* Invalidate status of initial analysis. */
private - > init_cqr_status = - 1 ;
/* Set default cache operations. */
private - > attrib . operation = DASD_NORMAL_CACHE ;
private - > attrib . nr_cyl = 0 ;
2006-06-29 17:08:18 +04:00
/* Read Configuration Data */
rc = dasd_eckd_read_conf ( device ) ;
if ( rc )
2008-01-26 16:11:23 +03:00
goto out_err1 ;
2006-06-29 17:08:18 +04:00
/* Generate device unique id and register in devmap */
2008-01-26 16:11:23 +03:00
rc = dasd_eckd_generate_uid ( device , & private - > uid ) ;
2006-06-29 17:08:18 +04:00
if ( rc )
2008-01-26 16:11:23 +03:00
goto out_err1 ;
dasd_set_uid ( device - > cdev , & private - > uid ) ;
if ( private - > uid . type = = UA_BASE_DEVICE ) {
block = dasd_alloc_block ( ) ;
if ( IS_ERR ( block ) ) {
2009-03-26 17:23:49 +03:00
DBF_EVENT ( DBF_WARNING , " could not allocate dasd "
" block structure for device: %s " ,
dev_name ( & device - > cdev - > dev ) ) ;
2008-01-26 16:11:23 +03:00
rc = PTR_ERR ( block ) ;
goto out_err1 ;
}
device - > block = block ;
block - > base = device ;
}
/* register lcu with alias handling, enable PAV if this is a new lcu */
is_known = dasd_alias_make_device_known_to_lcu ( device ) ;
if ( is_known < 0 ) {
rc = is_known ;
goto out_err2 ;
}
if ( ! is_known ) {
/* new lcu found */
rc = dasd_eckd_validate_server ( device ) ; /* will switch pav on */
if ( rc )
goto out_err3 ;
}
/* Read Feature Codes */
2009-09-23 00:58:52 +04:00
dasd_eckd_read_features ( device ) ;
2006-06-29 17:08:18 +04:00
2005-04-17 02:20:36 +04:00
/* Read Device Characteristics */
2009-09-11 12:28:29 +04:00
rc = dasd_generic_read_dev_chars ( device , DASD_ECKD_MAGIC ,
& private - > rdc_data , 64 ) ;
2008-01-26 16:11:23 +03:00
if ( rc ) {
2009-03-26 17:23:49 +03:00
DBF_EVENT ( DBF_WARNING ,
" Read device characteristics failed, rc=%d for "
" device: %s " , rc , dev_name ( & device - > cdev - > dev ) ) ;
2008-01-26 16:11:23 +03:00
goto out_err3 ;
}
2009-03-26 17:23:47 +03:00
/* find the vaild cylinder size */
if ( private - > rdc_data . no_cyl = = LV_COMPAT_CYL & &
private - > rdc_data . long_no_cyl )
private - > real_cyl = private - > rdc_data . long_no_cyl ;
else
private - > real_cyl = private - > rdc_data . no_cyl ;
2009-03-26 17:23:49 +03:00
dev_info ( & device - > cdev - > dev , " New DASD %04X/%02X (CU %04X/%02X) "
" with %d cylinders, %d heads, %d sectors \n " ,
private - > rdc_data . dev_type ,
private - > rdc_data . dev_model ,
private - > rdc_data . cu_type ,
private - > rdc_data . cu_model . model ,
2009-06-12 12:26:37 +04:00
private - > real_cyl ,
2009-03-26 17:23:49 +03:00
private - > rdc_data . trk_per_cyl ,
private - > rdc_data . sec_per_trk ) ;
2008-01-26 16:11:23 +03:00
return 0 ;
out_err3 :
dasd_alias_disconnect_device_from_lcu ( device ) ;
out_err2 :
dasd_free_block ( device - > block ) ;
device - > block = NULL ;
out_err1 :
2008-08-01 18:39:09 +04:00
kfree ( private - > conf_data ) ;
2008-01-26 16:11:23 +03:00
kfree ( device - > private ) ;
device - > private = NULL ;
2006-04-28 05:40:28 +04:00
return rc ;
2005-04-17 02:20:36 +04:00
}
2008-01-26 16:11:23 +03:00
static void dasd_eckd_uncheck_device ( struct dasd_device * device )
{
2008-08-01 18:39:09 +04:00
struct dasd_eckd_private * private ;
private = ( struct dasd_eckd_private * ) device - > private ;
2008-01-26 16:11:23 +03:00
dasd_alias_disconnect_device_from_lcu ( device ) ;
2008-08-01 18:39:09 +04:00
private - > ned = NULL ;
private - > sneq = NULL ;
private - > vdsneq = NULL ;
private - > gneq = NULL ;
private - > conf_len = 0 ;
kfree ( private - > conf_data ) ;
private - > conf_data = NULL ;
2008-01-26 16:11:23 +03:00
}
2005-04-17 02:20:36 +04:00
static struct dasd_ccw_req *
dasd_eckd_analysis_ccw ( struct dasd_device * device )
{
struct dasd_eckd_private * private ;
struct eckd_count * count_data ;
struct LO_eckd_data * LO_data ;
struct dasd_ccw_req * cqr ;
struct ccw1 * ccw ;
int cplength , datasize ;
int i ;
private = ( struct dasd_eckd_private * ) device - > private ;
cplength = 8 ;
datasize = sizeof ( struct DE_eckd_data ) + 2 * sizeof ( struct LO_eckd_data ) ;
2009-09-11 12:28:29 +04:00
cqr = dasd_smalloc_request ( DASD_ECKD_MAGIC , cplength , datasize , device ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( cqr ) )
return cqr ;
ccw = cqr - > cpaddr ;
/* Define extent for the first 3 tracks. */
define_extent ( ccw + + , cqr - > data , 0 , 2 ,
DASD_ECKD_CCW_READ_COUNT , device ) ;
2008-01-26 16:11:23 +03:00
LO_data = cqr - > data + sizeof ( struct DE_eckd_data ) ;
2005-04-17 02:20:36 +04:00
/* Locate record for the first 4 records on track 0. */
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
locate_record ( ccw + + , LO_data + + , 0 , 0 , 4 ,
DASD_ECKD_CCW_READ_COUNT , device , 0 ) ;
count_data = private - > count_area ;
for ( i = 0 ; i < 4 ; i + + ) {
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
ccw - > cmd_code = DASD_ECKD_CCW_READ_COUNT ;
ccw - > flags = 0 ;
ccw - > count = 8 ;
ccw - > cda = ( __u32 ) ( addr_t ) count_data ;
ccw + + ;
count_data + + ;
}
/* Locate record for the first record on track 2. */
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
locate_record ( ccw + + , LO_data + + , 2 , 0 , 1 ,
DASD_ECKD_CCW_READ_COUNT , device , 0 ) ;
/* Read count ccw. */
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
ccw - > cmd_code = DASD_ECKD_CCW_READ_COUNT ;
ccw - > flags = 0 ;
ccw - > count = 8 ;
ccw - > cda = ( __u32 ) ( addr_t ) count_data ;
2008-01-26 16:11:23 +03:00
cqr - > block = NULL ;
cqr - > startdev = device ;
cqr - > memdev = device ;
2005-04-17 02:20:36 +04:00
cqr - > retries = 0 ;
cqr - > buildclk = get_clock ( ) ;
cqr - > status = DASD_CQR_FILLED ;
return cqr ;
}
/*
* This is the callback function for the init_analysis cqr . It saves
* the status of the initial analysis ccw before it frees it and kicks
* the device to continue the startup sequence . This will call
* dasd_eckd_do_analysis again ( if the devices has not been marked
* for deletion in the meantime ) .
*/
static void
dasd_eckd_analysis_callback ( struct dasd_ccw_req * init_cqr , void * data )
{
struct dasd_eckd_private * private ;
struct dasd_device * device ;
2008-01-26 16:11:23 +03:00
device = init_cqr - > startdev ;
2005-04-17 02:20:36 +04:00
private = ( struct dasd_eckd_private * ) device - > private ;
private - > init_cqr_status = init_cqr - > status ;
dasd_sfree_request ( init_cqr , device ) ;
dasd_kick_device ( device ) ;
}
static int
2008-01-26 16:11:23 +03:00
dasd_eckd_start_analysis ( struct dasd_block * block )
2005-04-17 02:20:36 +04:00
{
struct dasd_eckd_private * private ;
struct dasd_ccw_req * init_cqr ;
2008-01-26 16:11:23 +03:00
private = ( struct dasd_eckd_private * ) block - > base - > private ;
init_cqr = dasd_eckd_analysis_ccw ( block - > base ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( init_cqr ) )
return PTR_ERR ( init_cqr ) ;
init_cqr - > callback = dasd_eckd_analysis_callback ;
init_cqr - > callback_data = NULL ;
init_cqr - > expires = 5 * HZ ;
dasd_add_request_head ( init_cqr ) ;
return - EAGAIN ;
}
static int
2008-01-26 16:11:23 +03:00
dasd_eckd_end_analysis ( struct dasd_block * block )
2005-04-17 02:20:36 +04:00
{
2008-01-26 16:11:23 +03:00
struct dasd_device * device ;
2005-04-17 02:20:36 +04:00
struct dasd_eckd_private * private ;
struct eckd_count * count_area ;
unsigned int sb , blk_per_trk ;
int status , i ;
2008-01-26 16:11:23 +03:00
device = block - > base ;
2005-04-17 02:20:36 +04:00
private = ( struct dasd_eckd_private * ) device - > private ;
status = private - > init_cqr_status ;
private - > init_cqr_status = - 1 ;
if ( status ! = DASD_CQR_DONE ) {
2009-03-26 17:23:49 +03:00
dev_warn ( & device - > cdev - > dev ,
" The DASD is not formatted \n " ) ;
2005-04-17 02:20:36 +04:00
return - EMEDIUMTYPE ;
}
private - > uses_cdl = 1 ;
/* Check Track 0 for Compatible Disk Layout */
count_area = NULL ;
for ( i = 0 ; i < 3 ; i + + ) {
if ( private - > count_area [ i ] . kl ! = 4 | |
private - > count_area [ i ] . dl ! = dasd_eckd_cdl_reclen ( i ) - 4 ) {
private - > uses_cdl = 0 ;
break ;
}
}
if ( i = = 3 )
count_area = & private - > count_area [ 4 ] ;
if ( private - > uses_cdl = = 0 ) {
for ( i = 0 ; i < 5 ; i + + ) {
if ( ( private - > count_area [ i ] . kl ! = 0 ) | |
( private - > count_area [ i ] . dl ! =
private - > count_area [ 0 ] . dl ) )
break ;
}
if ( i = = 5 )
count_area = & private - > count_area [ 0 ] ;
} else {
if ( private - > count_area [ 3 ] . record = = 1 )
2009-03-26 17:23:49 +03:00
dev_warn ( & device - > cdev - > dev ,
" Track 0 has no records following the VTOC \n " ) ;
2005-04-17 02:20:36 +04:00
}
if ( count_area ! = NULL & & count_area - > kl = = 0 ) {
/* we found notthing violating our disk layout */
if ( dasd_check_blocksize ( count_area - > dl ) = = 0 )
2008-01-26 16:11:23 +03:00
block - > bp_block = count_area - > dl ;
2005-04-17 02:20:36 +04:00
}
2008-01-26 16:11:23 +03:00
if ( block - > bp_block = = 0 ) {
2009-03-26 17:23:49 +03:00
dev_warn ( & device - > cdev - > dev ,
" The disk layout of the DASD is not supported \n " ) ;
2005-04-17 02:20:36 +04:00
return - EMEDIUMTYPE ;
}
2008-01-26 16:11:23 +03:00
block - > s2b_shift = 0 ; /* bits to shift 512 to get a block */
for ( sb = 512 ; sb < block - > bp_block ; sb = sb < < 1 )
block - > s2b_shift + + ;
2005-04-17 02:20:36 +04:00
2008-01-26 16:11:23 +03:00
blk_per_trk = recs_per_track ( & private - > rdc_data , 0 , block - > bp_block ) ;
2009-03-26 17:23:47 +03:00
block - > blocks = ( private - > real_cyl *
2005-04-17 02:20:36 +04:00
private - > rdc_data . trk_per_cyl *
blk_per_trk ) ;
2009-03-26 17:23:49 +03:00
dev_info ( & device - > cdev - > dev ,
" DASD with %d KB/block, %d KB total size, %d KB/track, "
" %s \n " , ( block - > bp_block > > 10 ) ,
( ( private - > real_cyl *
private - > rdc_data . trk_per_cyl *
blk_per_trk * ( block - > bp_block > > 9 ) ) > > 1 ) ,
( ( blk_per_trk * block - > bp_block ) > > 10 ) ,
private - > uses_cdl ?
" compatible disk layout " : " linux disk layout " ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-01-26 16:11:23 +03:00
static int dasd_eckd_do_analysis ( struct dasd_block * block )
2005-04-17 02:20:36 +04:00
{
struct dasd_eckd_private * private ;
2008-01-26 16:11:23 +03:00
private = ( struct dasd_eckd_private * ) block - > base - > private ;
2005-04-17 02:20:36 +04:00
if ( private - > init_cqr_status < 0 )
2008-01-26 16:11:23 +03:00
return dasd_eckd_start_analysis ( block ) ;
2005-04-17 02:20:36 +04:00
else
2008-01-26 16:11:23 +03:00
return dasd_eckd_end_analysis ( block ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-26 16:11:23 +03:00
static int dasd_eckd_ready_to_online ( struct dasd_device * device )
{
return dasd_alias_add_device ( device ) ;
} ;
static int dasd_eckd_online_to_ready ( struct dasd_device * device )
{
return dasd_alias_remove_device ( device ) ;
} ;
2005-04-17 02:20:36 +04:00
static int
2008-01-26 16:11:23 +03:00
dasd_eckd_fill_geometry ( struct dasd_block * block , struct hd_geometry * geo )
2005-04-17 02:20:36 +04:00
{
struct dasd_eckd_private * private ;
2008-01-26 16:11:23 +03:00
private = ( struct dasd_eckd_private * ) block - > base - > private ;
if ( dasd_check_blocksize ( block - > bp_block ) = = 0 ) {
2005-04-17 02:20:36 +04:00
geo - > sectors = recs_per_track ( & private - > rdc_data ,
2008-01-26 16:11:23 +03:00
0 , block - > bp_block ) ;
2005-04-17 02:20:36 +04:00
}
geo - > cylinders = private - > rdc_data . no_cyl ;
geo - > heads = private - > rdc_data . trk_per_cyl ;
return 0 ;
}
static struct dasd_ccw_req *
dasd_eckd_format_device ( struct dasd_device * device ,
struct format_data_t * fdata )
{
struct dasd_eckd_private * private ;
struct dasd_ccw_req * fcp ;
struct eckd_count * ect ;
struct ccw1 * ccw ;
void * data ;
2009-03-26 17:23:47 +03:00
int rpt ;
struct ch_t address ;
2005-04-17 02:20:36 +04:00
int cplength , datasize ;
int i ;
2009-03-26 17:23:46 +03:00
int intensity = 0 ;
int r0_perm ;
2005-04-17 02:20:36 +04:00
private = ( struct dasd_eckd_private * ) device - > private ;
rpt = recs_per_track ( & private - > rdc_data , 0 , fdata - > blksize ) ;
2009-03-26 17:23:47 +03:00
set_ch_t ( & address ,
fdata - > start_unit / private - > rdc_data . trk_per_cyl ,
fdata - > start_unit % private - > rdc_data . trk_per_cyl ) ;
2005-04-17 02:20:36 +04:00
/* Sanity checks. */
if ( fdata - > start_unit > =
2009-03-26 17:23:47 +03:00
( private - > real_cyl * private - > rdc_data . trk_per_cyl ) ) {
2009-03-26 17:23:49 +03:00
dev_warn ( & device - > cdev - > dev , " Start track number %d used in "
" formatting is too big \n " , fdata - > start_unit ) ;
2005-04-17 02:20:36 +04:00
return ERR_PTR ( - EINVAL ) ;
}
if ( fdata - > start_unit > fdata - > stop_unit ) {
2009-03-26 17:23:49 +03:00
dev_warn ( & device - > cdev - > dev , " Start track %d used in "
" formatting exceeds end track \n " , fdata - > start_unit ) ;
2005-04-17 02:20:36 +04:00
return ERR_PTR ( - EINVAL ) ;
}
if ( dasd_check_blocksize ( fdata - > blksize ) ! = 0 ) {
2009-03-26 17:23:49 +03:00
dev_warn ( & device - > cdev - > dev ,
" The DASD cannot be formatted with block size %d \n " ,
fdata - > blksize ) ;
2005-04-17 02:20:36 +04:00
return ERR_PTR ( - EINVAL ) ;
}
/*
* fdata - > intensity is a bit string that tells us what to do :
* Bit 0 : write record zero
* Bit 1 : write home address , currently not supported
* Bit 2 : invalidate tracks
* Bit 3 : use OS / 390 compatible disk layout ( cdl )
2009-03-26 17:23:46 +03:00
* Bit 4 : do not allow storage subsystem to modify record zero
2005-04-17 02:20:36 +04:00
* Only some bit combinations do make sense .
*/
2009-03-26 17:23:46 +03:00
if ( fdata - > intensity & 0x10 ) {
r0_perm = 0 ;
intensity = fdata - > intensity & ~ 0x10 ;
} else {
r0_perm = 1 ;
intensity = fdata - > intensity ;
}
switch ( intensity ) {
2005-04-17 02:20:36 +04:00
case 0x00 : /* Normal format */
case 0x08 : /* Normal format, use cdl. */
cplength = 2 + rpt ;
datasize = sizeof ( struct DE_eckd_data ) +
sizeof ( struct LO_eckd_data ) +
rpt * sizeof ( struct eckd_count ) ;
break ;
case 0x01 : /* Write record zero and format track. */
case 0x09 : /* Write record zero and format track, use cdl. */
cplength = 3 + rpt ;
datasize = sizeof ( struct DE_eckd_data ) +
sizeof ( struct LO_eckd_data ) +
sizeof ( struct eckd_count ) +
rpt * sizeof ( struct eckd_count ) ;
break ;
case 0x04 : /* Invalidate track. */
case 0x0c : /* Invalidate track, use cdl. */
cplength = 3 ;
datasize = sizeof ( struct DE_eckd_data ) +
sizeof ( struct LO_eckd_data ) +
sizeof ( struct eckd_count ) ;
break ;
default :
2009-03-26 17:23:49 +03:00
dev_warn ( & device - > cdev - > dev , " An I/O control call used "
" incorrect flags 0x%x \n " , fdata - > intensity ) ;
2005-04-17 02:20:36 +04:00
return ERR_PTR ( - EINVAL ) ;
}
/* Allocate the format ccw request. */
2009-09-11 12:28:29 +04:00
fcp = dasd_smalloc_request ( DASD_ECKD_MAGIC , cplength , datasize , device ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( fcp ) )
return fcp ;
data = fcp - > data ;
ccw = fcp - > cpaddr ;
2009-03-26 17:23:46 +03:00
switch ( intensity & ~ 0x08 ) {
2005-04-17 02:20:36 +04:00
case 0x00 : /* Normal format. */
define_extent ( ccw + + , ( struct DE_eckd_data * ) data ,
fdata - > start_unit , fdata - > start_unit ,
DASD_ECKD_CCW_WRITE_CKD , device ) ;
2009-03-26 17:23:46 +03:00
/* grant subsystem permission to format R0 */
if ( r0_perm )
( ( struct DE_eckd_data * ) data ) - > ga_extended | = 0x04 ;
2005-04-17 02:20:36 +04:00
data + = sizeof ( struct DE_eckd_data ) ;
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
locate_record ( ccw + + , ( struct LO_eckd_data * ) data ,
fdata - > start_unit , 0 , rpt ,
DASD_ECKD_CCW_WRITE_CKD , device ,
fdata - > blksize ) ;
data + = sizeof ( struct LO_eckd_data ) ;
break ;
case 0x01 : /* Write record zero + format track. */
define_extent ( ccw + + , ( struct DE_eckd_data * ) data ,
fdata - > start_unit , fdata - > start_unit ,
DASD_ECKD_CCW_WRITE_RECORD_ZERO ,
device ) ;
data + = sizeof ( struct DE_eckd_data ) ;
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
locate_record ( ccw + + , ( struct LO_eckd_data * ) data ,
fdata - > start_unit , 0 , rpt + 1 ,
DASD_ECKD_CCW_WRITE_RECORD_ZERO , device ,
2008-01-26 16:11:23 +03:00
device - > block - > bp_block ) ;
2005-04-17 02:20:36 +04:00
data + = sizeof ( struct LO_eckd_data ) ;
break ;
case 0x04 : /* Invalidate track. */
define_extent ( ccw + + , ( struct DE_eckd_data * ) data ,
fdata - > start_unit , fdata - > start_unit ,
DASD_ECKD_CCW_WRITE_CKD , device ) ;
data + = sizeof ( struct DE_eckd_data ) ;
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
locate_record ( ccw + + , ( struct LO_eckd_data * ) data ,
fdata - > start_unit , 0 , 1 ,
DASD_ECKD_CCW_WRITE_CKD , device , 8 ) ;
data + = sizeof ( struct LO_eckd_data ) ;
break ;
}
2009-03-26 17:23:46 +03:00
if ( intensity & 0x01 ) { /* write record zero */
2005-04-17 02:20:36 +04:00
ect = ( struct eckd_count * ) data ;
data + = sizeof ( struct eckd_count ) ;
2009-03-26 17:23:47 +03:00
ect - > cyl = address . cyl ;
ect - > head = address . head ;
2005-04-17 02:20:36 +04:00
ect - > record = 0 ;
ect - > kl = 0 ;
ect - > dl = 8 ;
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
ccw - > cmd_code = DASD_ECKD_CCW_WRITE_RECORD_ZERO ;
ccw - > flags = CCW_FLAG_SLI ;
ccw - > count = 8 ;
ccw - > cda = ( __u32 ) ( addr_t ) ect ;
ccw + + ;
}
2009-03-26 17:23:46 +03:00
if ( ( intensity & ~ 0x08 ) & 0x04 ) { /* erase track */
2005-04-17 02:20:36 +04:00
ect = ( struct eckd_count * ) data ;
data + = sizeof ( struct eckd_count ) ;
2009-03-26 17:23:47 +03:00
ect - > cyl = address . cyl ;
ect - > head = address . head ;
2005-04-17 02:20:36 +04:00
ect - > record = 1 ;
ect - > kl = 0 ;
ect - > dl = 0 ;
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
ccw - > cmd_code = DASD_ECKD_CCW_WRITE_CKD ;
ccw - > flags = CCW_FLAG_SLI ;
ccw - > count = 8 ;
ccw - > cda = ( __u32 ) ( addr_t ) ect ;
} else { /* write remaining records */
for ( i = 0 ; i < rpt ; i + + ) {
ect = ( struct eckd_count * ) data ;
data + = sizeof ( struct eckd_count ) ;
2009-03-26 17:23:47 +03:00
ect - > cyl = address . cyl ;
ect - > head = address . head ;
2005-04-17 02:20:36 +04:00
ect - > record = i + 1 ;
ect - > kl = 0 ;
ect - > dl = fdata - > blksize ;
/* Check for special tracks 0-1 when formatting CDL */
2009-03-26 17:23:46 +03:00
if ( ( intensity & 0x08 ) & &
2005-04-17 02:20:36 +04:00
fdata - > start_unit = = 0 ) {
if ( i < 3 ) {
ect - > kl = 4 ;
ect - > dl = sizes_trk0 [ i ] - 4 ;
2006-06-29 16:58:12 +04:00
}
2005-04-17 02:20:36 +04:00
}
2009-03-26 17:23:46 +03:00
if ( ( intensity & 0x08 ) & &
2005-04-17 02:20:36 +04:00
fdata - > start_unit = = 1 ) {
ect - > kl = 44 ;
ect - > dl = LABEL_SIZE - 44 ;
}
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
ccw - > cmd_code = DASD_ECKD_CCW_WRITE_CKD ;
ccw - > flags = CCW_FLAG_SLI ;
ccw - > count = 8 ;
ccw - > cda = ( __u32 ) ( addr_t ) ect ;
ccw + + ;
}
}
2008-01-26 16:11:23 +03:00
fcp - > startdev = device ;
fcp - > memdev = device ;
clear_bit ( DASD_CQR_FLAGS_USE_ERP , & fcp - > flags ) ;
fcp - > retries = 5 ; /* set retry counter to enable default ERP */
2005-04-17 02:20:36 +04:00
fcp - > buildclk = get_clock ( ) ;
fcp - > status = DASD_CQR_FILLED ;
return fcp ;
}
2008-01-26 16:11:23 +03:00
static void dasd_eckd_handle_terminated_request ( struct dasd_ccw_req * cqr )
2005-04-17 02:20:36 +04:00
{
2008-01-26 16:11:23 +03:00
cqr - > status = DASD_CQR_FILLED ;
if ( cqr - > block & & ( cqr - > startdev ! = cqr - > block - > base ) ) {
dasd_eckd_reset_ccw_to_base_io ( cqr ) ;
cqr - > startdev = cqr - > block - > base ;
2005-04-17 02:20:36 +04:00
}
2008-01-26 16:11:23 +03:00
} ;
2005-04-17 02:20:36 +04:00
static dasd_erp_fn_t
dasd_eckd_erp_action ( struct dasd_ccw_req * cqr )
{
2008-01-26 16:11:23 +03:00
struct dasd_device * device = ( struct dasd_device * ) cqr - > startdev ;
2005-04-17 02:20:36 +04:00
struct ccw_device * cdev = device - > cdev ;
switch ( cdev - > id . cu_type ) {
case 0x3990 :
case 0x2105 :
case 0x2107 :
case 0x1750 :
return dasd_3990_erp_action ;
case 0x9343 :
case 0x3880 :
default :
return dasd_default_erp_action ;
}
}
static dasd_erp_fn_t
dasd_eckd_erp_postaction ( struct dasd_ccw_req * cqr )
{
return dasd_default_erp_postaction ;
}
2008-01-26 16:11:23 +03:00
static void dasd_eckd_handle_unsolicited_interrupt ( struct dasd_device * device ,
struct irb * irb )
{
char mask ;
2009-03-26 17:23:48 +03:00
char * sense = NULL ;
2008-01-26 16:11:23 +03:00
/* first of all check for state change pending interrupt */
mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP ;
2009-03-26 17:23:48 +03:00
if ( ( scsw_dstat ( & irb - > scsw ) & mask ) = = mask ) {
2008-01-26 16:11:23 +03:00
dasd_generic_handle_state_change ( device ) ;
return ;
}
/* summary unit check */
2009-03-26 17:23:48 +03:00
if ( ( scsw_dstat ( & irb - > scsw ) & DEV_STAT_UNIT_CHECK ) & &
2008-07-14 11:58:50 +04:00
( irb - > ecw [ 7 ] = = 0x0D ) ) {
2008-01-26 16:11:23 +03:00
dasd_alias_handle_summary_unit_check ( device , irb ) ;
return ;
}
2009-03-26 17:23:48 +03:00
sense = dasd_get_sense ( irb ) ;
2008-04-17 09:46:08 +04:00
/* service information message SIM */
2009-03-26 17:23:48 +03:00
if ( sense & & ! ( sense [ 27 ] & DASD_SENSE_BIT_0 ) & &
( ( sense [ 6 ] & DASD_SIM_SENSE ) = = DASD_SIM_SENSE ) ) {
dasd_3990_erp_handle_sim ( device , sense ) ;
2008-07-17 19:16:41 +04:00
dasd_schedule_device_bh ( device ) ;
2008-04-17 09:46:08 +04:00
return ;
}
2009-03-26 17:23:48 +03:00
if ( ( scsw_cc ( & irb - > scsw ) = = 1 ) & &
( scsw_fctl ( & irb - > scsw ) & SCSW_FCTL_START_FUNC ) & &
( scsw_actl ( & irb - > scsw ) & SCSW_ACTL_START_PEND ) & &
( scsw_stctl ( & irb - > scsw ) & SCSW_STCTL_STATUS_PEND ) ) {
2008-10-10 23:33:23 +04:00
/* fake irb do nothing, they are handled elsewhere */
dasd_schedule_device_bh ( device ) ;
return ;
}
2008-01-26 16:11:23 +03:00
2009-03-26 17:23:48 +03:00
if ( ! sense ) {
2008-10-10 23:33:23 +04:00
/* just report other unsolicited interrupts */
2009-03-26 17:23:49 +03:00
DBF_DEV_EVENT ( DBF_ERR , device , " %s " ,
2008-10-10 23:33:23 +04:00
" unsolicited interrupt received " ) ;
} else {
2009-03-26 17:23:49 +03:00
DBF_DEV_EVENT ( DBF_ERR , device , " %s " ,
2008-10-10 23:33:23 +04:00
" unsolicited interrupt received "
" (sense available) " ) ;
2009-07-07 18:37:06 +04:00
device - > discipline - > dump_sense_dbf ( device , irb , " unsolicited " ) ;
2008-10-10 23:33:23 +04:00
}
dasd_schedule_device_bh ( device ) ;
2008-01-26 16:11:23 +03:00
return ;
} ;
2009-03-26 17:23:48 +03:00
static struct dasd_ccw_req * dasd_eckd_build_cp_cmd_single (
struct dasd_device * startdev ,
2008-01-26 16:11:23 +03:00
struct dasd_block * block ,
2009-03-26 17:23:48 +03:00
struct request * req ,
sector_t first_rec ,
sector_t last_rec ,
sector_t first_trk ,
sector_t last_trk ,
unsigned int first_offs ,
unsigned int last_offs ,
unsigned int blk_per_trk ,
unsigned int blksize )
2005-04-17 02:20:36 +04:00
{
struct dasd_eckd_private * private ;
unsigned long * idaws ;
struct LO_eckd_data * LO_data ;
struct dasd_ccw_req * cqr ;
struct ccw1 * ccw ;
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 ;
2009-03-26 17:23:48 +03:00
unsigned int off ;
2005-04-17 02:20:36 +04:00
int count , cidaw , cplength , datasize ;
2009-03-26 17:23:48 +03:00
sector_t recid ;
2005-04-17 02:20:36 +04:00
unsigned char cmd , rcmd ;
2008-01-26 16:11:23 +03:00
int use_prefix ;
struct dasd_device * basedev ;
2005-04-17 02:20:36 +04:00
2008-01-26 16:11:23 +03:00
basedev = block - > base ;
private = ( struct dasd_eckd_private * ) basedev - > private ;
2005-04-17 02:20:36 +04:00
if ( rq_data_dir ( req ) = = READ )
cmd = DASD_ECKD_CCW_READ_MT ;
else if ( rq_data_dir ( req ) = = WRITE )
cmd = DASD_ECKD_CCW_WRITE_MT ;
else
return ERR_PTR ( - EINVAL ) ;
2009-03-26 17:23:48 +03:00
2005-04-17 02:20:36 +04:00
/* Check struct bio and count the number of blocks for the request. */
count = 0 ;
cidaw = 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 ) )
/* Eckd 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 ) ;
2006-01-06 11:19:28 +03:00
# if defined(CONFIG_64BIT)
2007-08-16 15:43:12 +04:00
if ( idal_is_needed ( page_address ( bv - > bv_page ) , bv - > bv_len ) )
2008-01-26 16:11:23 +03:00
cidaw + = bv - > bv_len > > ( block - > s2b_shift + 9 ) ;
2005-04-17 02:20:36 +04:00
# endif
}
/* Paranoia. */
if ( count ! = last_rec - first_rec + 1 )
return ERR_PTR ( - EINVAL ) ;
2008-01-26 16:11:23 +03:00
/* use the prefix command if available */
use_prefix = private - > features . feature [ 8 ] & 0x01 ;
if ( use_prefix ) {
/* 1x prefix + number of blocks */
cplength = 2 + count ;
/* 1x prefix + cidaws*sizeof(long) */
datasize = sizeof ( struct PFX_eckd_data ) +
sizeof ( struct LO_eckd_data ) +
cidaw * sizeof ( unsigned long ) ;
} else {
/* 1x define extent + 1x locate record + number of blocks */
cplength = 2 + count ;
/* 1x define extent + 1x locate record + cidaws*sizeof(long) */
datasize = sizeof ( struct DE_eckd_data ) +
sizeof ( struct LO_eckd_data ) +
cidaw * sizeof ( unsigned long ) ;
}
2005-04-17 02:20:36 +04:00
/* Find out the number of additional locate record ccws for cdl. */
if ( private - > uses_cdl & & first_rec < 2 * blk_per_trk ) {
if ( last_rec > = 2 * blk_per_trk )
count = 2 * blk_per_trk - first_rec ;
cplength + = count ;
datasize + = count * sizeof ( struct LO_eckd_data ) ;
}
/* Allocate the ccw request. */
2009-09-11 12:28:29 +04:00
cqr = dasd_smalloc_request ( DASD_ECKD_MAGIC , cplength , datasize ,
startdev ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( cqr ) )
return cqr ;
ccw = cqr - > cpaddr ;
2008-01-26 16:11:23 +03:00
/* First ccw is define extent or prefix. */
if ( use_prefix ) {
if ( prefix ( ccw + + , cqr - > data , first_trk ,
last_trk , cmd , basedev , startdev ) = = - EAGAIN ) {
/* Clock not in sync and XRC is enabled.
* Try again later .
*/
dasd_sfree_request ( cqr , startdev ) ;
return ERR_PTR ( - EAGAIN ) ;
}
idaws = ( unsigned long * ) ( cqr - > data +
sizeof ( struct PFX_eckd_data ) ) ;
} else {
if ( define_extent ( ccw + + , cqr - > data , first_trk ,
last_trk , cmd , startdev ) = = - EAGAIN ) {
/* Clock not in sync and XRC is enabled.
* Try again later .
*/
dasd_sfree_request ( cqr , startdev ) ;
return ERR_PTR ( - EAGAIN ) ;
}
idaws = ( unsigned long * ) ( cqr - > data +
sizeof ( struct DE_eckd_data ) ) ;
2007-02-05 23:18:19 +03:00
}
2005-04-17 02:20:36 +04:00
/* Build locate_record+read/write/ccws. */
LO_data = ( struct LO_eckd_data * ) ( idaws + cidaw ) ;
recid = first_rec ;
if ( private - > uses_cdl = = 0 | | recid > 2 * blk_per_trk ) {
/* Only standard blocks so there is just one locate record. */
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
locate_record ( ccw + + , LO_data + + , first_trk , first_offs + 1 ,
2008-01-26 16:11:23 +03:00
last_rec - recid + 1 , cmd , basedev , blksize ) ;
2005-04-17 02:20:36 +04:00
}
2007-09-25 14:35:59 +04:00
rq_for_each_segment ( bv , req , iter ) {
2005-04-17 02:20:36 +04:00
dst = page_address ( bv - > bv_page ) + bv - > bv_offset ;
if ( dasd_page_cache ) {
char * copy = kmem_cache_alloc ( dasd_page_cache ,
2006-12-07 07:33:19 +03:00
GFP_DMA | __GFP_NOWARN ) ;
2005-04-17 02:20:36 +04:00
if ( copy & & rq_data_dir ( req ) = = WRITE )
memcpy ( copy + bv - > bv_offset , dst , bv - > bv_len ) ;
if ( copy )
dst = copy + bv - > bv_offset ;
}
for ( off = 0 ; off < bv - > bv_len ; off + = blksize ) {
sector_t trkid = recid ;
unsigned int recoffs = sector_div ( trkid , blk_per_trk ) ;
rcmd = cmd ;
count = blksize ;
/* Locate record for cdl special block ? */
if ( private - > uses_cdl & & recid < 2 * blk_per_trk ) {
if ( dasd_eckd_cdl_special ( blk_per_trk , recid ) ) {
rcmd | = 0x8 ;
count = dasd_eckd_cdl_reclen ( recid ) ;
2005-05-01 19:58:59 +04:00
if ( count < blksize & &
rq_data_dir ( req ) = = READ )
2005-04-17 02:20:36 +04:00
memset ( dst + count , 0xe5 ,
blksize - count ) ;
}
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
locate_record ( ccw + + , LO_data + + ,
trkid , recoffs + 1 ,
2008-01-26 16:11:23 +03:00
1 , rcmd , basedev , count ) ;
2005-04-17 02:20:36 +04:00
}
/* Locate record for standard blocks ? */
if ( private - > uses_cdl & & recid = = 2 * blk_per_trk ) {
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
locate_record ( ccw + + , LO_data + + ,
trkid , recoffs + 1 ,
last_rec - recid + 1 ,
2008-01-26 16:11:23 +03:00
cmd , basedev , count ) ;
2005-04-17 02:20:36 +04:00
}
/* Read/write ccw. */
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
ccw - > cmd_code = rcmd ;
ccw - > count = count ;
if ( idal_is_needed ( dst , blksize ) ) {
ccw - > cda = ( __u32 ) ( addr_t ) idaws ;
ccw - > flags = CCW_FLAG_IDA ;
idaws = idal_create_words ( idaws , dst , blksize ) ;
} else {
ccw - > cda = ( __u32 ) ( addr_t ) dst ;
ccw - > flags = 0 ;
}
ccw + + ;
dst + = blksize ;
recid + + ;
}
}
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 = startdev ;
cqr - > memdev = startdev ;
cqr - > block = block ;
2005-04-17 02:20:36 +04:00
cqr - > expires = 5 * 60 * HZ ; /* 5 minutes */
cqr - > lpm = private - > path_data . ppm ;
cqr - > retries = 256 ;
cqr - > buildclk = get_clock ( ) ;
cqr - > status = DASD_CQR_FILLED ;
return cqr ;
}
2009-03-26 17:23:48 +03:00
static struct dasd_ccw_req * dasd_eckd_build_cp_cmd_track (
struct dasd_device * startdev ,
struct dasd_block * block ,
struct request * req ,
sector_t first_rec ,
sector_t last_rec ,
sector_t first_trk ,
sector_t last_trk ,
unsigned int first_offs ,
unsigned int last_offs ,
unsigned int blk_per_trk ,
unsigned int blksize )
{
struct dasd_eckd_private * private ;
unsigned long * idaws ;
struct dasd_ccw_req * cqr ;
struct ccw1 * ccw ;
struct req_iterator iter ;
struct bio_vec * bv ;
char * dst , * idaw_dst ;
unsigned int cidaw , cplength , datasize ;
unsigned int tlf ;
sector_t recid ;
unsigned char cmd ;
struct dasd_device * basedev ;
unsigned int trkcount , count , count_to_trk_end ;
unsigned int idaw_len , seg_len , part_len , len_to_track_end ;
unsigned char new_track , end_idaw ;
sector_t trkid ;
unsigned int recoffs ;
basedev = block - > base ;
private = ( struct dasd_eckd_private * ) basedev - > private ;
if ( rq_data_dir ( req ) = = READ )
cmd = DASD_ECKD_CCW_READ_TRACK_DATA ;
else if ( rq_data_dir ( req ) = = WRITE )
cmd = DASD_ECKD_CCW_WRITE_TRACK_DATA ;
else
return ERR_PTR ( - EINVAL ) ;
/* Track based I/O needs IDAWs for each page, and not just for
* 64 bit addresses . We need additional idals for pages
* that get filled from two tracks , so we use the number
* of records as upper limit .
*/
cidaw = last_rec - first_rec + 1 ;
trkcount = last_trk - first_trk + 1 ;
/* 1x prefix + one read/write ccw per track */
cplength = 1 + trkcount ;
/* on 31-bit we need space for two 32 bit addresses per page
* on 64 - bit one 64 bit address
*/
datasize = sizeof ( struct PFX_eckd_data ) +
cidaw * sizeof ( unsigned long long ) ;
/* Allocate the ccw request. */
2009-09-11 12:28:29 +04:00
cqr = dasd_smalloc_request ( DASD_ECKD_MAGIC , cplength , datasize ,
startdev ) ;
2009-03-26 17:23:48 +03:00
if ( IS_ERR ( cqr ) )
return cqr ;
ccw = cqr - > cpaddr ;
/* transfer length factor: how many bytes to read from the last track */
if ( first_trk = = last_trk )
tlf = last_offs - first_offs + 1 ;
else
tlf = last_offs + 1 ;
tlf * = blksize ;
if ( prefix_LRE ( ccw + + , cqr - > data , first_trk ,
last_trk , cmd , basedev , startdev ,
1 /* format */ , first_offs + 1 ,
trkcount , blksize ,
tlf ) = = - EAGAIN ) {
/* Clock not in sync and XRC is enabled.
* Try again later .
*/
dasd_sfree_request ( cqr , startdev ) ;
return ERR_PTR ( - EAGAIN ) ;
}
/*
* The translation of request into ccw programs must meet the
* following conditions :
* - all idaws but the first and the last must address full pages
* ( or 2 K blocks on 31 - bit )
* - the scope of a ccw and it ' s idal ends with the track boundaries
*/
idaws = ( unsigned long * ) ( cqr - > data + sizeof ( struct PFX_eckd_data ) ) ;
recid = first_rec ;
new_track = 1 ;
end_idaw = 0 ;
len_to_track_end = 0 ;
idaw_dst = 0 ;
idaw_len = 0 ;
rq_for_each_segment ( bv , req , iter ) {
dst = page_address ( bv - > bv_page ) + bv - > bv_offset ;
seg_len = bv - > bv_len ;
while ( seg_len ) {
if ( new_track ) {
trkid = recid ;
recoffs = sector_div ( trkid , blk_per_trk ) ;
count_to_trk_end = blk_per_trk - recoffs ;
count = min ( ( last_rec - recid + 1 ) ,
( sector_t ) count_to_trk_end ) ;
len_to_track_end = count * blksize ;
ccw [ - 1 ] . flags | = CCW_FLAG_CC ;
ccw - > cmd_code = cmd ;
ccw - > count = len_to_track_end ;
ccw - > cda = ( __u32 ) ( addr_t ) idaws ;
ccw - > flags = CCW_FLAG_IDA ;
ccw + + ;
recid + = count ;
new_track = 0 ;
2009-04-14 17:36:24 +04:00
/* first idaw for a ccw may start anywhere */
if ( ! idaw_dst )
idaw_dst = dst ;
2009-03-26 17:23:48 +03:00
}
2009-04-14 17:36:24 +04:00
/* If we start a new idaw, we must make sure that it
* starts on an IDA_BLOCK_SIZE boundary .
2009-03-26 17:23:48 +03:00
* If we continue an idaw , we must make sure that the
* current segment begins where the so far accumulated
* idaw ends
*/
2009-04-14 17:36:24 +04:00
if ( ! idaw_dst ) {
if ( __pa ( dst ) & ( IDA_BLOCK_SIZE - 1 ) ) {
dasd_sfree_request ( cqr , startdev ) ;
return ERR_PTR ( - ERANGE ) ;
} else
idaw_dst = dst ;
}
2009-03-26 17:23:48 +03:00
if ( ( idaw_dst + idaw_len ) ! = dst ) {
dasd_sfree_request ( cqr , startdev ) ;
return ERR_PTR ( - ERANGE ) ;
}
part_len = min ( seg_len , len_to_track_end ) ;
seg_len - = part_len ;
dst + = part_len ;
idaw_len + = part_len ;
len_to_track_end - = part_len ;
/* collected memory area ends on an IDA_BLOCK border,
* - > create an idaw
* idal_create_words will handle cases where idaw_len
* is larger then IDA_BLOCK_SIZE
*/
if ( ! ( __pa ( idaw_dst + idaw_len ) & ( IDA_BLOCK_SIZE - 1 ) ) )
end_idaw = 1 ;
/* We also need to end the idaw at track end */
if ( ! len_to_track_end ) {
new_track = 1 ;
end_idaw = 1 ;
}
if ( end_idaw ) {
idaws = idal_create_words ( idaws , idaw_dst ,
idaw_len ) ;
idaw_dst = 0 ;
idaw_len = 0 ;
end_idaw = 0 ;
}
}
}
if ( blk_noretry_request ( req ) | |
block - > base - > features & DASD_FEATURE_FAILFAST )
set_bit ( DASD_CQR_FLAGS_FAILFAST , & cqr - > flags ) ;
cqr - > startdev = startdev ;
cqr - > memdev = startdev ;
cqr - > block = block ;
cqr - > expires = 5 * 60 * HZ ; /* 5 minutes */
cqr - > lpm = private - > path_data . ppm ;
cqr - > retries = 256 ;
cqr - > buildclk = get_clock ( ) ;
cqr - > status = DASD_CQR_FILLED ;
return cqr ;
}
static int prepare_itcw ( struct itcw * itcw ,
unsigned int trk , unsigned int totrk , int cmd ,
struct dasd_device * basedev ,
struct dasd_device * startdev ,
unsigned int rec_on_trk , int count ,
unsigned int blksize ,
unsigned int total_data_size ,
unsigned int tlf ,
unsigned int blk_per_trk )
{
struct PFX_eckd_data pfxdata ;
struct dasd_eckd_private * basepriv , * startpriv ;
struct DE_eckd_data * dedata ;
struct LRE_eckd_data * lredata ;
struct dcw * dcw ;
u32 begcyl , endcyl ;
u16 heads , beghead , endhead ;
u8 pfx_cmd ;
int rc = 0 ;
int sector = 0 ;
int dn , d ;
/* setup prefix data */
basepriv = ( struct dasd_eckd_private * ) basedev - > private ;
startpriv = ( struct dasd_eckd_private * ) startdev - > private ;
dedata = & pfxdata . define_extent ;
lredata = & pfxdata . locate_record ;
memset ( & pfxdata , 0 , sizeof ( pfxdata ) ) ;
pfxdata . format = 1 ; /* PFX with LRE */
pfxdata . base_address = basepriv - > ned - > unit_addr ;
pfxdata . base_lss = basepriv - > ned - > ID ;
pfxdata . validity . define_extent = 1 ;
/* private uid is kept up to date, conf_data may be outdated */
if ( startpriv - > uid . type ! = UA_BASE_DEVICE ) {
pfxdata . validity . verify_base = 1 ;
if ( startpriv - > uid . type = = UA_HYPER_PAV_ALIAS )
pfxdata . validity . hyper_pav = 1 ;
}
switch ( cmd ) {
case DASD_ECKD_CCW_READ_TRACK_DATA :
dedata - > mask . perm = 0x1 ;
dedata - > attributes . operation = basepriv - > attrib . operation ;
dedata - > blk_size = blksize ;
dedata - > ga_extended | = 0x42 ;
lredata - > operation . orientation = 0x0 ;
lredata - > operation . operation = 0x0C ;
lredata - > auxiliary . check_bytes = 0x01 ;
pfx_cmd = DASD_ECKD_CCW_PFX_READ ;
break ;
case DASD_ECKD_CCW_WRITE_TRACK_DATA :
dedata - > mask . perm = 0x02 ;
dedata - > attributes . operation = basepriv - > attrib . operation ;
dedata - > blk_size = blksize ;
rc = check_XRC_on_prefix ( & pfxdata , basedev ) ;
dedata - > ga_extended | = 0x42 ;
lredata - > operation . orientation = 0x0 ;
lredata - > operation . operation = 0x3F ;
lredata - > extended_operation = 0x23 ;
lredata - > auxiliary . check_bytes = 0x2 ;
pfx_cmd = DASD_ECKD_CCW_PFX ;
break ;
default :
DBF_DEV_EVENT ( DBF_ERR , basedev ,
" prepare itcw, unknown opcode 0x%x " , cmd ) ;
BUG ( ) ;
break ;
}
if ( rc )
return rc ;
dedata - > attributes . mode = 0x3 ; /* ECKD */
heads = basepriv - > rdc_data . trk_per_cyl ;
begcyl = trk / heads ;
beghead = trk % heads ;
endcyl = totrk / heads ;
endhead = totrk % heads ;
/* check for sequential prestage - enhance cylinder range */
if ( dedata - > attributes . operation = = DASD_SEQ_PRESTAGE | |
dedata - > attributes . operation = = DASD_SEQ_ACCESS ) {
if ( endcyl + basepriv - > attrib . nr_cyl < basepriv - > real_cyl )
endcyl + = basepriv - > attrib . nr_cyl ;
else
endcyl = ( basepriv - > real_cyl - 1 ) ;
}
set_ch_t ( & dedata - > beg_ext , begcyl , beghead ) ;
set_ch_t ( & dedata - > end_ext , endcyl , endhead ) ;
dedata - > ep_format = 0x20 ; /* records per track is valid */
dedata - > ep_rec_per_track = blk_per_trk ;
if ( rec_on_trk ) {
switch ( basepriv - > rdc_data . dev_type ) {
case 0x3390 :
dn = ceil_quot ( blksize + 6 , 232 ) ;
d = 9 + ceil_quot ( blksize + 6 * ( dn + 1 ) , 34 ) ;
sector = ( 49 + ( rec_on_trk - 1 ) * ( 10 + d ) ) / 8 ;
break ;
case 0x3380 :
d = 7 + ceil_quot ( blksize + 12 , 32 ) ;
sector = ( 39 + ( rec_on_trk - 1 ) * ( 8 + d ) ) / 7 ;
break ;
}
}
lredata - > auxiliary . length_valid = 1 ;
lredata - > auxiliary . length_scope = 1 ;
lredata - > auxiliary . imbedded_ccw_valid = 1 ;
lredata - > length = tlf ;
lredata - > imbedded_ccw = cmd ;
lredata - > count = count ;
lredata - > sector = sector ;
set_ch_t ( & lredata - > seek_addr , begcyl , beghead ) ;
lredata - > search_arg . cyl = lredata - > seek_addr . cyl ;
lredata - > search_arg . head = lredata - > seek_addr . head ;
lredata - > search_arg . record = rec_on_trk ;
dcw = itcw_add_dcw ( itcw , pfx_cmd , 0 ,
& pfxdata , sizeof ( pfxdata ) , total_data_size ) ;
return rc ;
}
static struct dasd_ccw_req * dasd_eckd_build_cp_tpm_track (
struct dasd_device * startdev ,
struct dasd_block * block ,
struct request * req ,
sector_t first_rec ,
sector_t last_rec ,
sector_t first_trk ,
sector_t last_trk ,
unsigned int first_offs ,
unsigned int last_offs ,
unsigned int blk_per_trk ,
unsigned int blksize )
{
struct dasd_eckd_private * private ;
struct dasd_ccw_req * cqr ;
struct req_iterator iter ;
struct bio_vec * bv ;
char * dst ;
unsigned int trkcount , ctidaw ;
unsigned char cmd ;
struct dasd_device * basedev ;
unsigned int tlf ;
struct itcw * itcw ;
struct tidaw * last_tidaw = NULL ;
int itcw_op ;
size_t itcw_size ;
basedev = block - > base ;
private = ( struct dasd_eckd_private * ) basedev - > private ;
if ( rq_data_dir ( req ) = = READ ) {
cmd = DASD_ECKD_CCW_READ_TRACK_DATA ;
itcw_op = ITCW_OP_READ ;
} else if ( rq_data_dir ( req ) = = WRITE ) {
cmd = DASD_ECKD_CCW_WRITE_TRACK_DATA ;
itcw_op = ITCW_OP_WRITE ;
} else
return ERR_PTR ( - EINVAL ) ;
/* trackbased I/O needs address all memory via TIDAWs,
* not just for 64 bit addresses . This allows us to map
* each segment directly to one tidaw .
*/
trkcount = last_trk - first_trk + 1 ;
ctidaw = 0 ;
rq_for_each_segment ( bv , req , iter ) {
+ + ctidaw ;
}
/* Allocate the ccw request. */
itcw_size = itcw_calc_size ( 0 , ctidaw , 0 ) ;
2009-09-11 12:28:29 +04:00
cqr = dasd_smalloc_request ( DASD_ECKD_MAGIC , 0 , itcw_size , startdev ) ;
2009-03-26 17:23:48 +03:00
if ( IS_ERR ( cqr ) )
return cqr ;
cqr - > cpmode = 1 ;
cqr - > startdev = startdev ;
cqr - > memdev = startdev ;
cqr - > block = block ;
cqr - > expires = 100 * HZ ;
cqr - > buildclk = get_clock ( ) ;
cqr - > status = DASD_CQR_FILLED ;
cqr - > retries = 10 ;
/* transfer length factor: how many bytes to read from the last track */
if ( first_trk = = last_trk )
tlf = last_offs - first_offs + 1 ;
else
tlf = last_offs + 1 ;
tlf * = blksize ;
itcw = itcw_init ( cqr - > data , itcw_size , itcw_op , 0 , ctidaw , 0 ) ;
cqr - > cpaddr = itcw_get_tcw ( itcw ) ;
if ( prepare_itcw ( itcw , first_trk , last_trk ,
cmd , basedev , startdev ,
first_offs + 1 ,
trkcount , blksize ,
( last_rec - first_rec + 1 ) * blksize ,
tlf , blk_per_trk ) = = - EAGAIN ) {
/* Clock not in sync and XRC is enabled.
* Try again later .
*/
dasd_sfree_request ( cqr , startdev ) ;
return ERR_PTR ( - EAGAIN ) ;
}
/*
* A tidaw can address 4 k of memory , but must not cross page boundaries
* We can let the block layer handle this by setting
* blk_queue_segment_boundary to page boundaries and
* blk_max_segment_size to page size when setting up the request queue .
*/
rq_for_each_segment ( bv , req , iter ) {
dst = page_address ( bv - > bv_page ) + bv - > bv_offset ;
last_tidaw = itcw_add_tidaw ( itcw , 0x00 , dst , bv - > bv_len ) ;
if ( IS_ERR ( last_tidaw ) )
return ( struct dasd_ccw_req * ) last_tidaw ;
}
last_tidaw - > flags | = 0x80 ;
itcw_finalize ( itcw ) ;
if ( blk_noretry_request ( req ) | |
block - > base - > features & DASD_FEATURE_FAILFAST )
set_bit ( DASD_CQR_FLAGS_FAILFAST , & cqr - > flags ) ;
cqr - > startdev = startdev ;
cqr - > memdev = startdev ;
cqr - > block = block ;
cqr - > expires = 5 * 60 * HZ ; /* 5 minutes */
cqr - > lpm = private - > path_data . ppm ;
cqr - > retries = 256 ;
cqr - > buildclk = get_clock ( ) ;
cqr - > status = DASD_CQR_FILLED ;
return cqr ;
}
static struct dasd_ccw_req * dasd_eckd_build_cp ( struct dasd_device * startdev ,
struct dasd_block * block ,
struct request * req )
{
int tpm , cmdrtd , cmdwtd ;
int use_prefix ;
2009-06-12 12:26:36 +04:00
# if defined(CONFIG_64BIT)
2009-03-26 17:23:48 +03:00
int fcx_in_css , fcx_in_gneq , fcx_in_features ;
2009-06-12 12:26:36 +04:00
# endif
struct dasd_eckd_private * private ;
2009-03-26 17:23:48 +03:00
struct dasd_device * basedev ;
sector_t first_rec , last_rec ;
sector_t first_trk , last_trk ;
unsigned int first_offs , last_offs ;
unsigned int blk_per_trk , blksize ;
int cdlspecial ;
struct dasd_ccw_req * cqr ;
basedev = block - > base ;
private = ( struct dasd_eckd_private * ) basedev - > private ;
/* Calculate number of blocks/records per track. */
blksize = block - > bp_block ;
blk_per_trk = recs_per_track ( & private - > rdc_data , 0 , blksize ) ;
2009-10-06 12:34:15 +04:00
if ( blk_per_trk = = 0 )
return ERR_PTR ( - EINVAL ) ;
2009-03-26 17:23:48 +03:00
/* Calculate record id of first and last block. */
2009-05-07 17:24:39 +04:00
first_rec = first_trk = blk_rq_pos ( req ) > > block - > s2b_shift ;
2009-03-26 17:23:48 +03:00
first_offs = sector_div ( first_trk , blk_per_trk ) ;
last_rec = last_trk =
2009-05-07 17:24:39 +04:00
( blk_rq_pos ( req ) + blk_rq_sectors ( req ) - 1 ) > > block - > s2b_shift ;
2009-03-26 17:23:48 +03:00
last_offs = sector_div ( last_trk , blk_per_trk ) ;
cdlspecial = ( private - > uses_cdl & & first_rec < 2 * blk_per_trk ) ;
2009-06-12 12:26:36 +04:00
/* is transport mode supported? */
# if defined(CONFIG_64BIT)
2009-03-26 17:23:48 +03:00
fcx_in_css = css_general_characteristics . fcx ;
fcx_in_gneq = private - > gneq - > reserved2 [ 7 ] & 0x04 ;
fcx_in_features = private - > features . feature [ 40 ] & 0x80 ;
tpm = fcx_in_css & & fcx_in_gneq & & fcx_in_features ;
2009-06-12 12:26:36 +04:00
# else
tpm = 0 ;
# endif
2009-03-26 17:23:48 +03:00
/* is read track data and write track data in command mode supported? */
cmdrtd = private - > features . feature [ 9 ] & 0x20 ;
cmdwtd = private - > features . feature [ 12 ] & 0x40 ;
use_prefix = private - > features . feature [ 8 ] & 0x01 ;
cqr = NULL ;
if ( cdlspecial | | dasd_page_cache ) {
/* do nothing, just fall through to the cmd mode single case */
} else if ( ! dasd_nofcx & & tpm & & ( first_trk = = last_trk ) ) {
cqr = dasd_eckd_build_cp_tpm_track ( startdev , block , req ,
first_rec , last_rec ,
first_trk , last_trk ,
first_offs , last_offs ,
blk_per_trk , blksize ) ;
if ( IS_ERR ( cqr ) & & PTR_ERR ( cqr ) ! = - EAGAIN )
cqr = NULL ;
} else if ( use_prefix & &
( ( ( rq_data_dir ( req ) = = READ ) & & cmdrtd ) | |
( ( rq_data_dir ( req ) = = WRITE ) & & cmdwtd ) ) ) {
cqr = dasd_eckd_build_cp_cmd_track ( startdev , block , req ,
first_rec , last_rec ,
first_trk , last_trk ,
first_offs , last_offs ,
blk_per_trk , blksize ) ;
if ( IS_ERR ( cqr ) & & PTR_ERR ( cqr ) ! = - EAGAIN )
cqr = NULL ;
}
if ( ! cqr )
cqr = dasd_eckd_build_cp_cmd_single ( startdev , block , req ,
first_rec , last_rec ,
first_trk , last_trk ,
first_offs , last_offs ,
blk_per_trk , blksize ) ;
return cqr ;
}
2005-04-17 02:20:36 +04:00
static int
dasd_eckd_free_cp ( struct dasd_ccw_req * cqr , struct request * req )
{
struct dasd_eckd_private * private ;
struct ccw1 * ccw ;
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 , * cda ;
unsigned int blksize , blk_per_trk , off ;
sector_t recid ;
2007-09-25 14:35:59 +04:00
int status ;
2005-04-17 02:20:36 +04:00
if ( ! dasd_page_cache )
goto out ;
2008-01-26 16:11:23 +03:00
private = ( struct dasd_eckd_private * ) cqr - > block - > base - > private ;
blksize = cqr - > block - > bp_block ;
2005-04-17 02:20:36 +04:00
blk_per_trk = recs_per_track ( & private - > rdc_data , 0 , blksize ) ;
2009-05-07 17:24:39 +04:00
recid = blk_rq_pos ( req ) > > cqr - > block - > s2b_shift ;
2005-04-17 02:20:36 +04:00
ccw = cqr - > cpaddr ;
/* Skip over define extent & locate record. */
ccw + + ;
if ( private - > uses_cdl = = 0 | | recid > 2 * blk_per_trk )
ccw + + ;
2007-09-25 14:35:59 +04:00
rq_for_each_segment ( bv , req , iter ) {
2005-04-17 02:20:36 +04:00
dst = page_address ( bv - > bv_page ) + bv - > bv_offset ;
for ( off = 0 ; off < bv - > bv_len ; off + = blksize ) {
/* Skip locate record. */
if ( private - > uses_cdl & & recid < = 2 * blk_per_trk )
ccw + + ;
if ( dst ) {
if ( ccw - > flags & CCW_FLAG_IDA )
cda = * ( ( char * * ) ( ( addr_t ) ccw - > cda ) ) ;
else
cda = ( char * ) ( ( addr_t ) ccw - > cda ) ;
if ( dst ! = cda ) {
if ( rq_data_dir ( req ) = = READ )
memcpy ( dst , cda , bv - > bv_len ) ;
kmem_cache_free ( dasd_page_cache ,
( void * ) ( ( addr_t ) cda & PAGE_MASK ) ) ;
}
dst = NULL ;
}
ccw + + ;
recid + + ;
}
}
out :
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
/*
2009-03-26 17:23:48 +03:00
* Modify ccw / tcw in cqr so it can be started on a base device .
2008-01-26 16:11:23 +03:00
*
* Note that this is not enough to restart the cqr !
* Either reset cqr - > startdev as well ( summary unit check handling )
* or restart via separate cqr ( as in ERP handling ) .
*/
void dasd_eckd_reset_ccw_to_base_io ( struct dasd_ccw_req * cqr )
{
struct ccw1 * ccw ;
struct PFX_eckd_data * pfxdata ;
2009-03-26 17:23:48 +03:00
struct tcw * tcw ;
struct tccb * tccb ;
struct dcw * dcw ;
if ( cqr - > cpmode = = 1 ) {
tcw = cqr - > cpaddr ;
tccb = tcw_get_tccb ( tcw ) ;
dcw = ( struct dcw * ) & tccb - > tca [ 0 ] ;
pfxdata = ( struct PFX_eckd_data * ) & dcw - > cd [ 0 ] ;
2008-01-26 16:11:23 +03:00
pfxdata - > validity . verify_base = 0 ;
pfxdata - > validity . hyper_pav = 0 ;
2009-03-26 17:23:48 +03:00
} else {
ccw = cqr - > cpaddr ;
pfxdata = cqr - > data ;
if ( ccw - > cmd_code = = DASD_ECKD_CCW_PFX ) {
pfxdata - > validity . verify_base = 0 ;
pfxdata - > validity . hyper_pav = 0 ;
}
2008-01-26 16:11:23 +03:00
}
}
# define DASD_ECKD_CHANQ_MAX_SIZE 4
static struct dasd_ccw_req * dasd_eckd_build_alias_cp ( struct dasd_device * base ,
struct dasd_block * block ,
struct request * req )
{
struct dasd_eckd_private * private ;
struct dasd_device * startdev ;
unsigned long flags ;
struct dasd_ccw_req * cqr ;
startdev = dasd_alias_get_start_dev ( base ) ;
if ( ! startdev )
startdev = base ;
private = ( struct dasd_eckd_private * ) startdev - > private ;
if ( private - > count > = DASD_ECKD_CHANQ_MAX_SIZE )
return ERR_PTR ( - EBUSY ) ;
spin_lock_irqsave ( get_ccwdev_lock ( startdev - > cdev ) , flags ) ;
private - > count + + ;
cqr = dasd_eckd_build_cp ( startdev , block , req ) ;
if ( IS_ERR ( cqr ) )
private - > count - - ;
spin_unlock_irqrestore ( get_ccwdev_lock ( startdev - > cdev ) , flags ) ;
return cqr ;
}
static int dasd_eckd_free_alias_cp ( struct dasd_ccw_req * cqr ,
struct request * req )
{
struct dasd_eckd_private * private ;
unsigned long flags ;
spin_lock_irqsave ( get_ccwdev_lock ( cqr - > memdev - > cdev ) , flags ) ;
private = ( struct dasd_eckd_private * ) cqr - > memdev - > private ;
private - > count - - ;
spin_unlock_irqrestore ( get_ccwdev_lock ( cqr - > memdev - > cdev ) , flags ) ;
return dasd_eckd_free_cp ( cqr , req ) ;
}
2005-04-17 02:20:36 +04:00
static int
dasd_eckd_fill_info ( struct dasd_device * device ,
struct dasd_information2_t * info )
{
struct dasd_eckd_private * private ;
private = ( struct dasd_eckd_private * ) device - > private ;
info - > label_block = 2 ;
info - > FBA_layout = private - > uses_cdl ? 0 : 1 ;
info - > format = private - > uses_cdl ? DASD_FORMAT_CDL : DASD_FORMAT_LDL ;
info - > characteristics_size = sizeof ( struct dasd_eckd_characteristics ) ;
memcpy ( info - > characteristics , & private - > rdc_data ,
sizeof ( struct dasd_eckd_characteristics ) ) ;
2008-08-01 18:39:09 +04:00
info - > confdata_size = min ( ( unsigned long ) private - > conf_len ,
sizeof ( info - > configuration_data ) ) ;
memcpy ( info - > configuration_data , private - > conf_data ,
info - > confdata_size ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* SECTION : ioctl functions for eckd devices .
*/
/*
* Release device ioctl .
2006-06-29 16:58:12 +04:00
* Buils a channel programm to releases a prior reserved
2005-04-17 02:20:36 +04:00
* ( see dasd_eckd_reserve ) device .
*/
static int
2006-03-24 14:15:20 +03:00
dasd_eckd_release ( struct dasd_device * device )
2005-04-17 02:20:36 +04:00
{
struct dasd_ccw_req * cqr ;
int rc ;
2009-03-26 17:23:48 +03:00
struct ccw1 * ccw ;
2005-04-17 02:20:36 +04:00
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EACCES ;
2009-09-11 12:28:29 +04:00
cqr = dasd_smalloc_request ( DASD_ECKD_MAGIC , 1 , 32 , device ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( cqr ) ) {
2009-03-26 17:23:49 +03:00
DBF_DEV_EVENT ( DBF_WARNING , device , " %s " ,
2005-04-17 02:20:36 +04:00
" Could not allocate initialization request " ) ;
return PTR_ERR ( cqr ) ;
}
2009-03-26 17:23:48 +03:00
ccw = cqr - > cpaddr ;
ccw - > cmd_code = DASD_ECKD_CCW_RELEASE ;
ccw - > flags | = CCW_FLAG_SLI ;
ccw - > count = 32 ;
ccw - > cda = ( __u32 ) ( addr_t ) cqr - > data ;
2008-01-26 16:11:23 +03:00
cqr - > startdev = device ;
cqr - > memdev = device ;
2005-04-17 02:20:36 +04:00
clear_bit ( DASD_CQR_FLAGS_USE_ERP , & cqr - > flags ) ;
2006-01-06 11:19:15 +03:00
set_bit ( DASD_CQR_FLAGS_FAILFAST , & cqr - > flags ) ;
2007-02-05 23:17:24 +03:00
cqr - > retries = 2 ; /* set retry counter to enable basic ERP */
2005-04-17 02:20:36 +04:00
cqr - > expires = 2 * HZ ;
cqr - > buildclk = get_clock ( ) ;
cqr - > status = DASD_CQR_FILLED ;
rc = dasd_sleep_on_immediatly ( cqr ) ;
2008-01-26 16:11:23 +03:00
dasd_sfree_request ( cqr , cqr - > memdev ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
/*
* Reserve device ioctl .
* Options are set to ' synchronous wait for interrupt ' and
2006-06-29 16:58:12 +04:00
* ' timeout the request ' . This leads to a terminate IO if
* the interrupt is outstanding for a certain time .
2005-04-17 02:20:36 +04:00
*/
static int
2006-03-24 14:15:20 +03:00
dasd_eckd_reserve ( struct dasd_device * device )
2005-04-17 02:20:36 +04:00
{
struct dasd_ccw_req * cqr ;
int rc ;
2009-03-26 17:23:48 +03:00
struct ccw1 * ccw ;
2005-04-17 02:20:36 +04:00
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EACCES ;
2009-09-11 12:28:29 +04:00
cqr = dasd_smalloc_request ( DASD_ECKD_MAGIC , 1 , 32 , device ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( cqr ) ) {
2009-03-26 17:23:49 +03:00
DBF_DEV_EVENT ( DBF_WARNING , device , " %s " ,
2005-04-17 02:20:36 +04:00
" Could not allocate initialization request " ) ;
return PTR_ERR ( cqr ) ;
}
2009-03-26 17:23:48 +03:00
ccw = cqr - > cpaddr ;
ccw - > cmd_code = DASD_ECKD_CCW_RESERVE ;
ccw - > flags | = CCW_FLAG_SLI ;
ccw - > count = 32 ;
ccw - > cda = ( __u32 ) ( addr_t ) cqr - > data ;
2008-01-26 16:11:23 +03:00
cqr - > startdev = device ;
cqr - > memdev = device ;
2005-04-17 02:20:36 +04:00
clear_bit ( DASD_CQR_FLAGS_USE_ERP , & cqr - > flags ) ;
2006-01-06 11:19:15 +03:00
set_bit ( DASD_CQR_FLAGS_FAILFAST , & cqr - > flags ) ;
2007-02-05 23:17:24 +03:00
cqr - > retries = 2 ; /* set retry counter to enable basic ERP */
2005-04-17 02:20:36 +04:00
cqr - > expires = 2 * HZ ;
cqr - > buildclk = get_clock ( ) ;
cqr - > status = DASD_CQR_FILLED ;
rc = dasd_sleep_on_immediatly ( cqr ) ;
2008-01-26 16:11:23 +03:00
dasd_sfree_request ( cqr , cqr - > memdev ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
/*
* Steal lock ioctl - unconditional reserve device .
2006-06-29 16:58:12 +04:00
* Buils a channel programm to break a device ' s reservation .
2005-04-17 02:20:36 +04:00
* ( unconditional reserve )
*/
static int
2006-03-24 14:15:20 +03:00
dasd_eckd_steal_lock ( struct dasd_device * device )
2005-04-17 02:20:36 +04:00
{
struct dasd_ccw_req * cqr ;
int rc ;
2009-03-26 17:23:48 +03:00
struct ccw1 * ccw ;
2005-04-17 02:20:36 +04:00
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EACCES ;
2009-09-11 12:28:29 +04:00
cqr = dasd_smalloc_request ( DASD_ECKD_MAGIC , 1 , 32 , device ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( cqr ) ) {
2009-03-26 17:23:49 +03:00
DBF_DEV_EVENT ( DBF_WARNING , device , " %s " ,
2005-04-17 02:20:36 +04:00
" Could not allocate initialization request " ) ;
return PTR_ERR ( cqr ) ;
}
2009-03-26 17:23:48 +03:00
ccw = cqr - > cpaddr ;
ccw - > cmd_code = DASD_ECKD_CCW_SLCK ;
ccw - > flags | = CCW_FLAG_SLI ;
ccw - > count = 32 ;
ccw - > cda = ( __u32 ) ( addr_t ) cqr - > data ;
2008-01-26 16:11:23 +03:00
cqr - > startdev = device ;
cqr - > memdev = device ;
2005-04-17 02:20:36 +04:00
clear_bit ( DASD_CQR_FLAGS_USE_ERP , & cqr - > flags ) ;
2006-01-06 11:19:15 +03:00
set_bit ( DASD_CQR_FLAGS_FAILFAST , & cqr - > flags ) ;
2007-02-05 23:17:24 +03:00
cqr - > retries = 2 ; /* set retry counter to enable basic ERP */
2005-04-17 02:20:36 +04:00
cqr - > expires = 2 * HZ ;
cqr - > buildclk = get_clock ( ) ;
cqr - > status = DASD_CQR_FILLED ;
rc = dasd_sleep_on_immediatly ( cqr ) ;
2008-01-26 16:11:23 +03:00
dasd_sfree_request ( cqr , cqr - > memdev ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
/*
* Read performance statistics
*/
static int
2006-03-24 14:15:20 +03:00
dasd_eckd_performance ( struct dasd_device * device , void __user * argp )
2005-04-17 02:20:36 +04:00
{
struct dasd_psf_prssd_data * prssdp ;
struct dasd_rssd_perf_stats_t * stats ;
struct dasd_ccw_req * cqr ;
struct ccw1 * ccw ;
int rc ;
2009-09-11 12:28:29 +04:00
cqr = dasd_smalloc_request ( DASD_ECKD_MAGIC , 1 /* PSF */ + 1 /* RSSD */ ,
2008-01-26 16:11:23 +03:00
( sizeof ( struct dasd_psf_prssd_data ) +
sizeof ( struct dasd_rssd_perf_stats_t ) ) ,
2005-04-17 02:20:36 +04:00
device ) ;
if ( IS_ERR ( cqr ) ) {
2009-03-26 17:23:49 +03:00
DBF_DEV_EVENT ( DBF_WARNING , device , " %s " ,
2005-04-17 02:20:36 +04:00
" Could not allocate initialization request " ) ;
return PTR_ERR ( cqr ) ;
}
2008-01-26 16:11:23 +03:00
cqr - > startdev = device ;
cqr - > memdev = device ;
2005-04-17 02:20:36 +04:00
cqr - > retries = 0 ;
cqr - > expires = 10 * HZ ;
/* Prepare for Read Subsystem Data */
prssdp = ( struct dasd_psf_prssd_data * ) cqr - > data ;
2008-01-26 16:11:23 +03:00
memset ( prssdp , 0 , sizeof ( struct dasd_psf_prssd_data ) ) ;
2005-04-17 02:20:36 +04:00
prssdp - > order = PSF_ORDER_PRSSD ;
2008-01-26 16:11:20 +03:00
prssdp - > suborder = 0x01 ; /* Performance Statistics */
2005-04-17 02:20:36 +04:00
prssdp - > varies [ 1 ] = 0x01 ; /* Perf Statistics for the Subsystem */
ccw = cqr - > cpaddr ;
ccw - > cmd_code = DASD_ECKD_CCW_PSF ;
2008-01-26 16:11:23 +03:00
ccw - > count = sizeof ( struct dasd_psf_prssd_data ) ;
2005-04-17 02:20:36 +04:00
ccw - > flags | = CCW_FLAG_CC ;
ccw - > cda = ( __u32 ) ( addr_t ) prssdp ;
/* Read Subsystem Data - Performance Statistics */
stats = ( struct dasd_rssd_perf_stats_t * ) ( prssdp + 1 ) ;
2008-01-26 16:11:23 +03:00
memset ( stats , 0 , sizeof ( struct dasd_rssd_perf_stats_t ) ) ;
2005-04-17 02:20:36 +04:00
ccw + + ;
ccw - > cmd_code = DASD_ECKD_CCW_RSSD ;
2008-01-26 16:11:23 +03:00
ccw - > count = sizeof ( struct dasd_rssd_perf_stats_t ) ;
2005-04-17 02:20:36 +04:00
ccw - > cda = ( __u32 ) ( addr_t ) stats ;
cqr - > buildclk = get_clock ( ) ;
cqr - > status = DASD_CQR_FILLED ;
rc = dasd_sleep_on ( cqr ) ;
if ( rc = = 0 ) {
prssdp = ( struct dasd_psf_prssd_data * ) cqr - > data ;
stats = ( struct dasd_rssd_perf_stats_t * ) ( prssdp + 1 ) ;
2006-03-24 14:15:20 +03:00
if ( copy_to_user ( argp , stats ,
sizeof ( struct dasd_rssd_perf_stats_t ) ) )
rc = - EFAULT ;
2005-04-17 02:20:36 +04:00
}
2008-01-26 16:11:23 +03:00
dasd_sfree_request ( cqr , cqr - > memdev ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
/*
* Get attributes ( cache operations )
* Returnes the cache attributes used in Define Extend ( DE ) .
*/
static int
2006-03-24 14:15:20 +03:00
dasd_eckd_get_attrib ( struct dasd_device * device , void __user * argp )
2005-04-17 02:20:36 +04:00
{
2006-03-24 14:15:20 +03:00
struct dasd_eckd_private * private =
( struct dasd_eckd_private * ) device - > private ;
struct attrib_data_t attrib = private - > attrib ;
2005-04-17 02:20:36 +04:00
int rc ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EACCES ;
2006-03-24 14:15:20 +03:00
if ( ! argp )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2006-03-24 14:15:20 +03:00
rc = 0 ;
if ( copy_to_user ( argp , ( long * ) & attrib ,
2008-01-26 16:11:23 +03:00
sizeof ( struct attrib_data_t ) ) )
2006-03-24 14:15:20 +03:00
rc = - EFAULT ;
2005-04-17 02:20:36 +04:00
return rc ;
}
/*
* Set attributes ( cache operations )
* Stores the attributes for cache operation to be used in Define Extend ( DE ) .
*/
static int
2006-03-24 14:15:20 +03:00
dasd_eckd_set_attrib ( struct dasd_device * device , void __user * argp )
2005-04-17 02:20:36 +04:00
{
2006-03-24 14:15:20 +03:00
struct dasd_eckd_private * private =
( struct dasd_eckd_private * ) device - > private ;
2005-04-17 02:20:36 +04:00
struct attrib_data_t attrib ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EACCES ;
2006-03-24 14:15:20 +03:00
if ( ! argp )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2006-03-24 14:15:20 +03:00
if ( copy_from_user ( & attrib , argp , sizeof ( struct attrib_data_t ) ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
private - > attrib = attrib ;
2009-03-26 17:23:49 +03:00
dev_info ( & device - > cdev - > dev ,
" The DASD cache mode was set to %x (%i cylinder prestage) \n " ,
private - > attrib . operation , private - > attrib . nr_cyl ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-10-10 23:33:25 +04:00
/*
* Issue syscall I / O to EMC Symmetrix array .
* CCWs are PSF and RSSD
*/
static int dasd_symm_io ( struct dasd_device * device , void __user * argp )
{
struct dasd_symmio_parms usrparm ;
char * psf_data , * rssd_result ;
struct dasd_ccw_req * cqr ;
struct ccw1 * ccw ;
int rc ;
/* Copy parms from caller */
rc = - EFAULT ;
if ( copy_from_user ( & usrparm , argp , sizeof ( usrparm ) ) )
goto out ;
# ifndef CONFIG_64BIT
/* Make sure pointers are sane even on 31 bit. */
if ( ( usrparm . psf_data > > 32 ) ! = 0 | | ( usrparm . rssd_result > > 32 ) ! = 0 ) {
rc = - EINVAL ;
goto out ;
}
# endif
/* alloc I/O data area */
psf_data = kzalloc ( usrparm . psf_data_len , GFP_KERNEL | GFP_DMA ) ;
rssd_result = kzalloc ( usrparm . rssd_result_len , GFP_KERNEL | GFP_DMA ) ;
if ( ! psf_data | | ! rssd_result ) {
rc = - ENOMEM ;
goto out_free ;
}
/* get syscall header from user space */
rc = - EFAULT ;
if ( copy_from_user ( psf_data ,
( void __user * ) ( unsigned long ) usrparm . psf_data ,
usrparm . psf_data_len ) )
goto out_free ;
/* sanity check on syscall header */
if ( psf_data [ 0 ] ! = 0x17 & & psf_data [ 1 ] ! = 0xce ) {
rc = - EINVAL ;
goto out_free ;
}
/* setup CCWs for PSF + RSSD */
2009-09-11 12:28:29 +04:00
cqr = dasd_smalloc_request ( DASD_ECKD_MAGIC , 2 , 0 , device ) ;
2008-10-10 23:33:25 +04:00
if ( IS_ERR ( cqr ) ) {
2009-03-26 17:23:49 +03:00
DBF_DEV_EVENT ( DBF_WARNING , device , " %s " ,
2008-10-10 23:33:25 +04:00
" Could not allocate initialization request " ) ;
rc = PTR_ERR ( cqr ) ;
goto out_free ;
}
cqr - > startdev = device ;
cqr - > memdev = device ;
cqr - > retries = 3 ;
cqr - > expires = 10 * HZ ;
cqr - > buildclk = get_clock ( ) ;
cqr - > status = DASD_CQR_FILLED ;
/* Build the ccws */
ccw = cqr - > cpaddr ;
/* PSF ccw */
ccw - > cmd_code = DASD_ECKD_CCW_PSF ;
ccw - > count = usrparm . psf_data_len ;
ccw - > flags | = CCW_FLAG_CC ;
ccw - > cda = ( __u32 ) ( addr_t ) psf_data ;
ccw + + ;
/* RSSD ccw */
ccw - > cmd_code = DASD_ECKD_CCW_RSSD ;
ccw - > count = usrparm . rssd_result_len ;
ccw - > flags = CCW_FLAG_SLI ;
ccw - > cda = ( __u32 ) ( addr_t ) rssd_result ;
rc = dasd_sleep_on ( cqr ) ;
if ( rc )
goto out_sfree ;
rc = - EFAULT ;
if ( copy_to_user ( ( void __user * ) ( unsigned long ) usrparm . rssd_result ,
rssd_result , usrparm . rssd_result_len ) )
goto out_sfree ;
rc = 0 ;
out_sfree :
dasd_sfree_request ( cqr , cqr - > memdev ) ;
out_free :
kfree ( rssd_result ) ;
kfree ( psf_data ) ;
out :
DBF_DEV_EVENT ( DBF_WARNING , device , " Symmetrix ioctl: rc=%d " , rc ) ;
return rc ;
}
2006-03-24 14:15:20 +03:00
static int
2008-01-26 16:11:23 +03:00
dasd_eckd_ioctl ( struct dasd_block * block , unsigned int cmd , void __user * argp )
2006-03-24 14:15:20 +03:00
{
2008-01-26 16:11:23 +03:00
struct dasd_device * device = block - > base ;
2006-03-24 14:15:20 +03:00
switch ( cmd ) {
case BIODASDGATTR :
return dasd_eckd_get_attrib ( device , argp ) ;
case BIODASDSATTR :
return dasd_eckd_set_attrib ( device , argp ) ;
case BIODASDPSRD :
return dasd_eckd_performance ( device , argp ) ;
case BIODASDRLSE :
return dasd_eckd_release ( device ) ;
case BIODASDRSRV :
return dasd_eckd_reserve ( device ) ;
case BIODASDSLCK :
return dasd_eckd_steal_lock ( device ) ;
2008-10-10 23:33:25 +04:00
case BIODASDSYMMIO :
return dasd_symm_io ( device , argp ) ;
2006-03-24 14:15:20 +03:00
default :
return - ENOIOCTLCMD ;
}
}
2006-06-29 16:57:52 +04:00
/*
* Dump the range of CCWs into ' page ' buffer
* and return number of printed chars .
*/
2007-02-05 23:18:53 +03:00
static int
2006-06-29 16:57:52 +04:00
dasd_eckd_dump_ccw_range ( struct ccw1 * from , struct ccw1 * to , char * page )
{
int len , count ;
char * datap ;
len = 0 ;
while ( from < = to ) {
len + = sprintf ( page + len , KERN_ERR PRINTK_HEADER
" CCW %p: %08X %08X DAT: " ,
from , ( ( int * ) from ) [ 0 ] , ( ( int * ) from ) [ 1 ] ) ;
/* get pointer to data (consider IDALs) */
if ( from - > flags & CCW_FLAG_IDA )
datap = ( char * ) * ( ( addr_t * ) ( addr_t ) from - > cda ) ;
else
datap = ( char * ) ( ( addr_t ) from - > cda ) ;
/* dump data (max 32 bytes) */
for ( count = 0 ; count < from - > count & & count < 32 ; count + + ) {
if ( count % 8 = = 0 ) len + = sprintf ( page + len , " " ) ;
if ( count % 4 = = 0 ) len + = sprintf ( page + len , " " ) ;
len + = sprintf ( page + len , " %02x " , datap [ count ] ) ;
}
len + = sprintf ( page + len , " \n " ) ;
from + + ;
}
return len ;
}
2009-03-26 17:23:49 +03:00
static void
2009-07-07 18:37:06 +04:00
dasd_eckd_dump_sense_dbf ( struct dasd_device * device , struct irb * irb ,
char * reason )
2009-03-26 17:23:49 +03:00
{
u64 * sense ;
2009-07-07 18:37:06 +04:00
sense = ( u64 * ) dasd_get_sense ( irb ) ;
2009-03-26 17:23:49 +03:00
if ( sense ) {
2009-07-07 18:37:06 +04:00
DBF_DEV_EVENT ( DBF_EMERG , device ,
" %s: %s %02x%02x%02x %016llx %016llx %016llx "
" %016llx " , reason ,
scsw_is_tm ( & irb - > scsw ) ? " t " : " c " ,
scsw_cc ( & irb - > scsw ) , scsw_cstat ( & irb - > scsw ) ,
scsw_dstat ( & irb - > scsw ) , sense [ 0 ] , sense [ 1 ] ,
sense [ 2 ] , sense [ 3 ] ) ;
2009-03-26 17:23:49 +03:00
} else {
DBF_DEV_EVENT ( DBF_EMERG , device , " %s " ,
" SORRY - NO VALID SENSE AVAILABLE \n " ) ;
}
}
2005-04-17 02:20:36 +04:00
/*
* Print sense data and related channel program .
* Parts are printed because printk buffer is only 1024 bytes .
*/
2009-03-26 17:23:48 +03:00
static void dasd_eckd_dump_sense_ccw ( struct dasd_device * device ,
2008-01-26 16:11:23 +03:00
struct dasd_ccw_req * req , struct irb * irb )
2005-04-17 02:20:36 +04:00
{
char * page ;
2006-06-29 16:57:52 +04:00
struct ccw1 * first , * last , * fail , * from , * to ;
int len , sl , sct ;
2005-04-17 02:20:36 +04:00
page = ( char * ) get_zeroed_page ( GFP_ATOMIC ) ;
if ( page = = NULL ) {
2009-03-26 17:23:49 +03:00
DBF_DEV_EVENT ( DBF_WARNING , device , " %s " ,
" No memory to dump sense data \n " ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2006-06-29 16:57:52 +04:00
/* dump the sense data */
len = sprintf ( page , KERN_ERR PRINTK_HEADER
2005-04-17 02:20:36 +04:00
" I/O status report for device %s: \n " ,
2008-10-10 23:33:09 +04:00
dev_name ( & device - > cdev - > dev ) ) ;
2005-04-17 02:20:36 +04:00
len + = sprintf ( page + len , KERN_ERR PRINTK_HEADER
2009-06-12 12:26:39 +04:00
" in req: %p CS: 0x%02X DS: 0x%02X CC: 0x%02X RC: %d \n " ,
req , scsw_cstat ( & irb - > scsw ) , scsw_dstat ( & irb - > scsw ) ,
scsw_cc ( & irb - > scsw ) , req - > intrc ) ;
2005-04-17 02:20:36 +04:00
len + = sprintf ( page + len , KERN_ERR PRINTK_HEADER
" device %s: Failing CCW: %p \n " ,
2008-10-10 23:33:09 +04:00
dev_name ( & device - > cdev - > dev ) ,
2008-07-14 11:58:50 +04:00
( void * ) ( addr_t ) irb - > scsw . cmd . cpa ) ;
2005-04-17 02:20:36 +04:00
if ( irb - > esw . esw0 . erw . cons ) {
for ( sl = 0 ; sl < 4 ; sl + + ) {
len + = sprintf ( page + len , KERN_ERR PRINTK_HEADER
" Sense(hex) %2d-%2d: " ,
( 8 * sl ) , ( ( 8 * sl ) + 7 ) ) ;
for ( sct = 0 ; sct < 8 ; sct + + ) {
len + = sprintf ( page + len , " %02x " ,
irb - > ecw [ 8 * sl + sct ] ) ;
}
len + = sprintf ( page + len , " \n " ) ;
}
if ( irb - > ecw [ 27 ] & DASD_SENSE_BIT_0 ) {
/* 24 Byte Sense Data */
2006-06-29 16:57:52 +04:00
sprintf ( page + len , KERN_ERR PRINTK_HEADER
" 24 Byte: %x MSG %x, "
" %s MSGb to SYSOP \n " ,
irb - > ecw [ 7 ] > > 4 , irb - > ecw [ 7 ] & 0x0f ,
irb - > ecw [ 1 ] & 0x10 ? " " : " no " ) ;
2005-04-17 02:20:36 +04:00
} else {
/* 32 Byte Sense Data */
2006-06-29 16:57:52 +04:00
sprintf ( page + len , KERN_ERR PRINTK_HEADER
" 32 Byte: Format: %x "
" Exception class %x \n " ,
irb - > ecw [ 6 ] & 0x0f , irb - > ecw [ 22 ] > > 4 ) ;
2005-04-17 02:20:36 +04:00
}
} else {
2006-06-29 16:57:52 +04:00
sprintf ( page + len , KERN_ERR PRINTK_HEADER
" SORRY - NO VALID SENSE AVAILABLE \n " ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-29 16:57:52 +04:00
printk ( " %s " , page ) ;
2008-01-26 16:11:23 +03:00
if ( req ) {
/* req == NULL for unsolicited interrupts */
/* dump the Channel Program (max 140 Bytes per line) */
/* Count CCW and print first CCWs (maximum 1024 % 140 = 7) */
first = req - > cpaddr ;
for ( last = first ; last - > flags & ( CCW_FLAG_CC | CCW_FLAG_DC ) ; last + + ) ;
to = min ( first + 6 , last ) ;
len = sprintf ( page , KERN_ERR PRINTK_HEADER
" Related CP in req: %p \n " , req ) ;
dasd_eckd_dump_ccw_range ( first , to , page + len ) ;
printk ( " %s " , page ) ;
2005-04-17 02:20:36 +04:00
2008-01-26 16:11:23 +03:00
/* print failing CCW area (maximum 4) */
/* scsw->cda is either valid or zero */
len = 0 ;
from = + + to ;
2008-07-14 11:58:50 +04:00
fail = ( struct ccw1 * ) ( addr_t )
irb - > scsw . cmd . cpa ; /* failing CCW */
2008-01-26 16:11:23 +03:00
if ( from < fail - 2 ) {
from = fail - 2 ; /* there is a gap - print header */
len + = sprintf ( page , KERN_ERR PRINTK_HEADER " ...... \n " ) ;
}
to = min ( fail + 1 , last ) ;
len + = dasd_eckd_dump_ccw_range ( from , to , page + len ) ;
/* print last CCWs (maximum 2) */
from = max ( from , + + to ) ;
if ( from < last - 1 ) {
from = last - 1 ; /* there is a gap - print header */
len + = sprintf ( page + len , KERN_ERR PRINTK_HEADER " ...... \n " ) ;
}
len + = dasd_eckd_dump_ccw_range ( from , last , page + len ) ;
if ( len > 0 )
printk ( " %s " , page ) ;
2005-04-17 02:20:36 +04:00
}
free_page ( ( unsigned long ) page ) ;
}
2009-03-26 17:23:48 +03:00
/*
* Print sense data from a tcw .
*/
static void dasd_eckd_dump_sense_tcw ( struct dasd_device * device ,
struct dasd_ccw_req * req , struct irb * irb )
{
char * page ;
int len , sl , sct , residual ;
struct tsb * tsb ;
u8 * sense ;
page = ( char * ) get_zeroed_page ( GFP_ATOMIC ) ;
if ( page = = NULL ) {
2009-03-26 17:23:49 +03:00
DBF_DEV_EVENT ( DBF_WARNING , device , " %s " ,
2009-03-26 17:23:48 +03:00
" No memory to dump sense data " ) ;
return ;
}
/* dump the sense data */
len = sprintf ( page , KERN_ERR PRINTK_HEADER
" I/O status report for device %s: \n " ,
dev_name ( & device - > cdev - > dev ) ) ;
len + = sprintf ( page + len , KERN_ERR PRINTK_HEADER
2009-06-12 12:26:39 +04:00
" in req: %p CS: 0x%02X DS: 0x%02X CC: 0x%02X RC: %d "
2009-03-26 17:23:48 +03:00
" fcxs: 0x%02X schxs: 0x%02X \n " , req ,
scsw_cstat ( & irb - > scsw ) , scsw_dstat ( & irb - > scsw ) ,
2009-06-12 12:26:39 +04:00
scsw_cc ( & irb - > scsw ) , req - > intrc ,
2009-03-26 17:23:48 +03:00
irb - > scsw . tm . fcxs , irb - > scsw . tm . schxs ) ;
len + = sprintf ( page + len , KERN_ERR PRINTK_HEADER
" device %s: Failing TCW: %p \n " ,
dev_name ( & device - > cdev - > dev ) ,
( void * ) ( addr_t ) irb - > scsw . tm . tcw ) ;
tsb = NULL ;
sense = NULL ;
if ( irb - > scsw . tm . tcw )
tsb = tcw_get_tsb (
( struct tcw * ) ( unsigned long ) irb - > scsw . tm . tcw ) ;
if ( tsb & & ( irb - > scsw . tm . fcxs = = 0x01 ) ) {
len + = sprintf ( page + len , KERN_ERR PRINTK_HEADER
" tsb->length %d \n " , tsb - > length ) ;
len + = sprintf ( page + len , KERN_ERR PRINTK_HEADER
" tsb->flags %x \n " , tsb - > flags ) ;
len + = sprintf ( page + len , KERN_ERR PRINTK_HEADER
" tsb->dcw_offset %d \n " , tsb - > dcw_offset ) ;
len + = sprintf ( page + len , KERN_ERR PRINTK_HEADER
" tsb->count %d \n " , tsb - > count ) ;
residual = tsb - > count - 28 ;
len + = sprintf ( page + len , KERN_ERR PRINTK_HEADER
" residual %d \n " , residual ) ;
switch ( tsb - > flags & 0x07 ) {
case 1 : /* tsa_iostat */
len + = sprintf ( page + len , KERN_ERR PRINTK_HEADER
" tsb->tsa.iostat.dev_time %d \n " ,
tsb - > tsa . iostat . dev_time ) ;
len + = sprintf ( page + len , KERN_ERR PRINTK_HEADER
" tsb->tsa.iostat.def_time %d \n " ,
tsb - > tsa . iostat . def_time ) ;
len + = sprintf ( page + len , KERN_ERR PRINTK_HEADER
" tsb->tsa.iostat.queue_time %d \n " ,
tsb - > tsa . iostat . queue_time ) ;
len + = sprintf ( page + len , KERN_ERR PRINTK_HEADER
" tsb->tsa.iostat.dev_busy_time %d \n " ,
tsb - > tsa . iostat . dev_busy_time ) ;
len + = sprintf ( page + len , KERN_ERR PRINTK_HEADER
" tsb->tsa.iostat.dev_act_time %d \n " ,
tsb - > tsa . iostat . dev_act_time ) ;
sense = tsb - > tsa . iostat . sense ;
break ;
case 2 : /* ts_ddpc */
len + = sprintf ( page + len , KERN_ERR PRINTK_HEADER
" tsb->tsa.ddpc.rc %d \n " , tsb - > tsa . ddpc . rc ) ;
len + = sprintf ( page + len , KERN_ERR PRINTK_HEADER
" tsb->tsa.ddpc.rcq: " ) ;
for ( sl = 0 ; sl < 16 ; sl + + ) {
for ( sct = 0 ; sct < 8 ; sct + + ) {
len + = sprintf ( page + len , " %02x " ,
tsb - > tsa . ddpc . rcq [ sl ] ) ;
}
len + = sprintf ( page + len , " \n " ) ;
}
sense = tsb - > tsa . ddpc . sense ;
break ;
case 3 : /* tsa_intrg */
len + = sprintf ( page + len , KERN_ERR PRINTK_HEADER
" tsb->tsa.intrg.: not supportet yet \n " ) ;
break ;
}
if ( sense ) {
for ( sl = 0 ; sl < 4 ; sl + + ) {
len + = sprintf ( page + len ,
KERN_ERR PRINTK_HEADER
" Sense(hex) %2d-%2d: " ,
( 8 * sl ) , ( ( 8 * sl ) + 7 ) ) ;
for ( sct = 0 ; sct < 8 ; sct + + ) {
len + = sprintf ( page + len , " %02x " ,
sense [ 8 * sl + sct ] ) ;
}
len + = sprintf ( page + len , " \n " ) ;
}
if ( sense [ 27 ] & DASD_SENSE_BIT_0 ) {
/* 24 Byte Sense Data */
sprintf ( page + len , KERN_ERR PRINTK_HEADER
" 24 Byte: %x MSG %x, "
" %s MSGb to SYSOP \n " ,
sense [ 7 ] > > 4 , sense [ 7 ] & 0x0f ,
sense [ 1 ] & 0x10 ? " " : " no " ) ;
} else {
/* 32 Byte Sense Data */
sprintf ( page + len , KERN_ERR PRINTK_HEADER
" 32 Byte: Format: %x "
" Exception class %x \n " ,
sense [ 6 ] & 0x0f , sense [ 22 ] > > 4 ) ;
}
} else {
sprintf ( page + len , KERN_ERR PRINTK_HEADER
" SORRY - NO VALID SENSE AVAILABLE \n " ) ;
}
} else {
sprintf ( page + len , KERN_ERR PRINTK_HEADER
" SORRY - NO TSB DATA AVAILABLE \n " ) ;
}
printk ( " %s " , page ) ;
free_page ( ( unsigned long ) page ) ;
}
static void dasd_eckd_dump_sense ( struct dasd_device * device ,
struct dasd_ccw_req * req , struct irb * irb )
{
if ( req & & scsw_is_tm ( & req - > irb . scsw ) )
dasd_eckd_dump_sense_tcw ( device , req , irb ) ;
else
dasd_eckd_dump_sense_ccw ( device , req , irb ) ;
}
2009-06-16 12:30:25 +04:00
int dasd_eckd_pm_freeze ( struct dasd_device * device )
{
/*
* the device should be disconnected from our LCU structure
* on restore we will reconnect it and reread LCU specific
* information like PAV support that might have changed
*/
dasd_alias_remove_device ( device ) ;
dasd_alias_disconnect_device_from_lcu ( device ) ;
return 0 ;
}
int dasd_eckd_restore_device ( struct dasd_device * device )
{
struct dasd_eckd_private * private ;
2009-10-06 12:34:15 +04:00
struct dasd_eckd_characteristics temp_rdc_data ;
2009-06-16 12:30:25 +04:00
int is_known , rc ;
struct dasd_uid temp_uid ;
2009-10-14 14:43:46 +04:00
unsigned long flags ;
2009-06-16 12:30:25 +04:00
private = ( struct dasd_eckd_private * ) device - > private ;
/* Read Configuration Data */
rc = dasd_eckd_read_conf ( device ) ;
if ( rc )
goto out_err ;
/* Generate device unique id and register in devmap */
rc = dasd_eckd_generate_uid ( device , & private - > uid ) ;
dasd_get_uid ( device - > cdev , & temp_uid ) ;
if ( memcmp ( & private - > uid , & temp_uid , sizeof ( struct dasd_uid ) ) ! = 0 )
2009-10-14 14:43:46 +04:00
dev_err ( & device - > cdev - > dev , " The UID of the DASD has "
" changed \n " ) ;
2009-06-16 12:30:25 +04:00
if ( rc )
goto out_err ;
dasd_set_uid ( device - > cdev , & private - > uid ) ;
/* register lcu with alias handling, enable PAV if this is a new lcu */
is_known = dasd_alias_make_device_known_to_lcu ( device ) ;
if ( is_known < 0 )
return is_known ;
if ( ! is_known ) {
/* new lcu found */
rc = dasd_eckd_validate_server ( device ) ; /* will switch pav on */
if ( rc )
goto out_err ;
}
/* Read Feature Codes */
2009-09-23 00:58:52 +04:00
dasd_eckd_read_features ( device ) ;
2009-06-16 12:30:25 +04:00
/* Read Device Characteristics */
2009-09-11 12:28:29 +04:00
rc = dasd_generic_read_dev_chars ( device , DASD_ECKD_MAGIC ,
2009-10-06 12:34:15 +04:00
& temp_rdc_data , 64 ) ;
2009-06-16 12:30:25 +04:00
if ( rc ) {
DBF_EVENT ( DBF_WARNING ,
" Read device characteristics failed, rc=%d for "
" device: %s " , rc , dev_name ( & device - > cdev - > dev ) ) ;
goto out_err ;
}
2009-10-14 14:43:46 +04:00
spin_lock_irqsave ( get_ccwdev_lock ( device - > cdev ) , flags ) ;
2009-10-06 12:34:15 +04:00
memcpy ( & private - > rdc_data , & temp_rdc_data , sizeof ( temp_rdc_data ) ) ;
2009-10-14 14:43:46 +04:00
spin_unlock_irqrestore ( get_ccwdev_lock ( device - > cdev ) , flags ) ;
2009-06-16 12:30:25 +04:00
/* add device to alias management */
dasd_alias_add_device ( device ) ;
return 0 ;
out_err :
2009-06-22 14:08:17 +04:00
return - 1 ;
2009-06-16 12:30:25 +04:00
}
static struct ccw_driver dasd_eckd_driver = {
. name = " dasd-eckd " ,
. owner = THIS_MODULE ,
. ids = dasd_eckd_ids ,
. probe = dasd_eckd_probe ,
. remove = dasd_generic_remove ,
. set_offline = dasd_generic_set_offline ,
. set_online = dasd_eckd_set_online ,
. notify = dasd_generic_notify ,
. freeze = dasd_generic_pm_freeze ,
. thaw = dasd_generic_restore_device ,
. restore = dasd_generic_restore_device ,
} ;
2009-03-26 17:23:48 +03:00
2005-04-17 02:20:36 +04:00
/*
* 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 ) . For 64 bit one dasd_mchunkt_t structure has
* 24 bytes , the struct dasd_ccw_req has 136 bytes and each block can use
* up to 16 bytes ( 8 for the ccw and 8 for the idal pointer ) . In
* addition we have one define extent ccw + 16 bytes of data and one
* locate record ccw + 16 bytes of data . That makes :
* ( 8192 - 24 - 136 - 8 - 16 - 8 - 16 ) / 16 = 499 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 249.5 blocks
* for one request . Give a little safety and the result is 240.
*/
static struct dasd_discipline dasd_eckd_discipline = {
. owner = THIS_MODULE ,
. name = " ECKD " ,
. ebcname = " ECKD " ,
. max_blocks = 240 ,
. check_device = dasd_eckd_check_characteristics ,
2008-01-26 16:11:23 +03:00
. uncheck_device = dasd_eckd_uncheck_device ,
2005-04-17 02:20:36 +04:00
. do_analysis = dasd_eckd_do_analysis ,
2008-01-26 16:11:23 +03:00
. ready_to_online = dasd_eckd_ready_to_online ,
. online_to_ready = dasd_eckd_online_to_ready ,
2005-04-17 02:20:36 +04:00
. fill_geometry = dasd_eckd_fill_geometry ,
. start_IO = dasd_start_IO ,
. term_IO = dasd_term_IO ,
2008-01-26 16:11:23 +03:00
. handle_terminated_request = dasd_eckd_handle_terminated_request ,
2005-04-17 02:20:36 +04:00
. format_device = dasd_eckd_format_device ,
. erp_action = dasd_eckd_erp_action ,
. erp_postaction = dasd_eckd_erp_postaction ,
2008-01-26 16:11:23 +03:00
. handle_unsolicited_interrupt = dasd_eckd_handle_unsolicited_interrupt ,
. build_cp = dasd_eckd_build_alias_cp ,
. free_cp = dasd_eckd_free_alias_cp ,
2005-04-17 02:20:36 +04:00
. dump_sense = dasd_eckd_dump_sense ,
2009-03-26 17:23:49 +03:00
. dump_sense_dbf = dasd_eckd_dump_sense_dbf ,
2005-04-17 02:20:36 +04:00
. fill_info = dasd_eckd_fill_info ,
2006-03-24 14:15:20 +03:00
. ioctl = dasd_eckd_ioctl ,
2009-06-16 12:30:25 +04:00
. freeze = dasd_eckd_pm_freeze ,
. restore = dasd_eckd_restore_device ,
2005-04-17 02:20:36 +04:00
} ;
static int __init
dasd_eckd_init ( void )
{
2009-06-12 12:26:38 +04:00
int ret ;
2005-04-17 02:20:36 +04:00
ASCEBC ( dasd_eckd_discipline . ebcname , 4 ) ;
2009-06-12 12:26:38 +04:00
ret = ccw_driver_register ( & dasd_eckd_driver ) ;
if ( ! ret )
wait_for_device_probe ( ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
static void __exit
dasd_eckd_cleanup ( void )
{
ccw_driver_unregister ( & dasd_eckd_driver ) ;
}
module_init ( dasd_eckd_init ) ;
module_exit ( dasd_eckd_cleanup ) ;