2007-04-27 16:01:49 +02:00
/*
* Sclp " store data in absolut storage "
*
* Copyright IBM Corp . 2003 , 2007
* Author ( s ) : Michael Holzheu
*/
2008-12-25 13:39:48 +01:00
# define KMSG_COMPONENT "sclp_sdias"
# define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
2007-04-27 16:01:49 +02:00
# include <linux/sched.h>
# include <asm/sclp.h>
# include <asm/debug.h>
# include <asm/ipl.h>
2008-12-25 13:39:48 +01:00
2007-04-27 16:01:49 +02:00
# include "sclp.h"
# include "sclp_rw.h"
# define TRACE(x...) debug_sprintf_event(sdias_dbf, 1, x)
# define SDIAS_RETRIES 300
# define SDIAS_SLEEP_TICKS 50
# define EQ_STORE_DATA 0x0
# define EQ_SIZE 0x1
# define DI_FCP_DUMP 0x0
# define ASA_SIZE_32 0x0
# define ASA_SIZE_64 0x1
# define EVSTATE_ALL_STORED 0x0
# define EVSTATE_NO_DATA 0x3
# define EVSTATE_PART_STORED 0x10
static struct debug_info * sdias_dbf ;
static struct sclp_register sclp_sdias_register = {
2007-04-27 16:01:53 +02:00
. send_mask = EVTYP_SDIAS_MASK ,
2007-04-27 16:01:49 +02:00
} ;
struct sdias_evbuf {
struct evbuf_header hdr ;
u8 event_qual ;
u8 data_id ;
u64 reserved2 ;
u32 event_id ;
u16 reserved3 ;
u8 asa_size ;
u8 event_status ;
u32 reserved4 ;
u32 blk_cnt ;
u64 asa ;
u32 reserved5 ;
u32 fbn ;
u32 reserved6 ;
u32 lbn ;
u16 reserved7 ;
u16 dbs ;
} __attribute__ ( ( packed ) ) ;
struct sdias_sccb {
struct sccb_header hdr ;
struct sdias_evbuf evbuf ;
} __attribute__ ( ( packed ) ) ;
static struct sdias_sccb sccb __attribute__ ( ( aligned ( 4096 ) ) ) ;
static int sclp_req_done ;
static wait_queue_head_t sdias_wq ;
static DEFINE_MUTEX ( sdias_mutex ) ;
static void sdias_callback ( struct sclp_req * request , void * data )
{
2007-05-10 15:45:46 +02:00
struct sdias_sccb * cbsccb ;
2007-04-27 16:01:49 +02:00
2007-05-10 15:45:46 +02:00
cbsccb = ( struct sdias_sccb * ) request - > sccb ;
2007-04-27 16:01:49 +02:00
sclp_req_done = 1 ;
wake_up ( & sdias_wq ) ; /* Inform caller, that request is complete */
TRACE ( " callback done \n " ) ;
}
static int sdias_sclp_send ( struct sclp_req * req )
{
int retries ;
int rc ;
for ( retries = SDIAS_RETRIES ; retries ; retries - - ) {
sclp_req_done = 0 ;
TRACE ( " add request \n " ) ;
rc = sclp_add_request ( req ) ;
if ( rc ) {
/* not initiated, wait some time and retry */
set_current_state ( TASK_INTERRUPTIBLE ) ;
TRACE ( " add request failed: rc = %i \n " , rc ) ;
schedule_timeout ( SDIAS_SLEEP_TICKS ) ;
continue ;
}
/* initiated, wait for completion of service call */
wait_event ( sdias_wq , ( sclp_req_done = = 1 ) ) ;
if ( req - > status = = SCLP_REQ_FAILED ) {
TRACE ( " sclp request failed \n " ) ;
rc = - EIO ;
continue ;
}
TRACE ( " request done \n " ) ;
break ;
}
return rc ;
}
/*
* Get number of blocks ( 4 K ) available in the HSA
*/
int sclp_sdias_blk_count ( void )
{
struct sclp_req request ;
int rc ;
mutex_lock ( & sdias_mutex ) ;
memset ( & sccb , 0 , sizeof ( sccb ) ) ;
memset ( & request , 0 , sizeof ( request ) ) ;
sccb . hdr . length = sizeof ( sccb ) ;
sccb . evbuf . hdr . length = sizeof ( struct sdias_evbuf ) ;
2007-04-27 16:01:53 +02:00
sccb . evbuf . hdr . type = EVTYP_SDIAS ;
2007-04-27 16:01:49 +02:00
sccb . evbuf . event_qual = EQ_SIZE ;
sccb . evbuf . data_id = DI_FCP_DUMP ;
sccb . evbuf . event_id = 4712 ;
sccb . evbuf . dbs = 1 ;
request . sccb = & sccb ;
request . command = SCLP_CMDW_WRITE_EVENT_DATA ;
request . status = SCLP_REQ_FILLED ;
request . callback = sdias_callback ;
rc = sdias_sclp_send ( & request ) ;
if ( rc ) {
2008-12-25 13:39:48 +01:00
pr_err ( " sclp_send failed for get_nr_blocks \n " ) ;
2007-04-27 16:01:49 +02:00
goto out ;
}
if ( sccb . hdr . response_code ! = 0x0020 ) {
TRACE ( " send failed: %x \n " , sccb . hdr . response_code ) ;
rc = - EIO ;
goto out ;
}
switch ( sccb . evbuf . event_status ) {
case 0 :
rc = sccb . evbuf . blk_cnt ;
break ;
default :
2008-12-25 13:39:48 +01:00
pr_err ( " SCLP error: %x \n " ,
sccb . evbuf . event_status ) ;
2007-04-27 16:01:49 +02:00
rc = - EIO ;
goto out ;
}
TRACE ( " %i blocks \n " , rc ) ;
out :
mutex_unlock ( & sdias_mutex ) ;
return rc ;
}
/*
* Copy from HSA to absolute storage ( not reentrant ) :
*
* @ dest : Address of buffer where data should be copied
* @ start_blk : Start Block ( beginning with 1 )
* @ nr_blks : Number of 4 K blocks to copy
*
* Return Value : 0 : Requested ' number ' of blocks of data copied
* < 0 : ERROR - negative event status
*/
int sclp_sdias_copy ( void * dest , int start_blk , int nr_blks )
{
struct sclp_req request ;
int rc ;
mutex_lock ( & sdias_mutex ) ;
memset ( & sccb , 0 , sizeof ( sccb ) ) ;
memset ( & request , 0 , sizeof ( request ) ) ;
sccb . hdr . length = sizeof ( sccb ) ;
sccb . evbuf . hdr . length = sizeof ( struct sdias_evbuf ) ;
2007-04-27 16:01:53 +02:00
sccb . evbuf . hdr . type = EVTYP_SDIAS ;
2007-04-27 16:01:49 +02:00
sccb . evbuf . hdr . flags = 0 ;
sccb . evbuf . event_qual = EQ_STORE_DATA ;
sccb . evbuf . data_id = DI_FCP_DUMP ;
sccb . evbuf . event_id = 4712 ;
# ifdef __s390x__
sccb . evbuf . asa_size = ASA_SIZE_64 ;
# else
sccb . evbuf . asa_size = ASA_SIZE_32 ;
# endif
sccb . evbuf . event_status = 0 ;
sccb . evbuf . blk_cnt = nr_blks ;
sccb . evbuf . asa = ( unsigned long ) dest ;
sccb . evbuf . fbn = start_blk ;
sccb . evbuf . lbn = 0 ;
sccb . evbuf . dbs = 1 ;
request . sccb = & sccb ;
request . command = SCLP_CMDW_WRITE_EVENT_DATA ;
request . status = SCLP_REQ_FILLED ;
request . callback = sdias_callback ;
rc = sdias_sclp_send ( & request ) ;
if ( rc ) {
2008-12-25 13:39:48 +01:00
pr_err ( " sclp_send failed: %x \n " , rc ) ;
2007-04-27 16:01:49 +02:00
goto out ;
}
if ( sccb . hdr . response_code ! = 0x0020 ) {
TRACE ( " copy failed: %x \n " , sccb . hdr . response_code ) ;
rc = - EIO ;
goto out ;
}
switch ( sccb . evbuf . event_status ) {
case EVSTATE_ALL_STORED :
TRACE ( " all stored \n " ) ;
case EVSTATE_PART_STORED :
TRACE ( " part stored: %i \n " , sccb . evbuf . blk_cnt ) ;
break ;
case EVSTATE_NO_DATA :
TRACE ( " no data \n " ) ;
default :
2008-12-25 13:39:48 +01:00
pr_err ( " Error from SCLP while copying hsa. "
" Event status = %x \n " ,
sccb . evbuf . event_status ) ;
2007-04-27 16:01:49 +02:00
rc = - EIO ;
}
out :
mutex_unlock ( & sdias_mutex ) ;
return rc ;
}
2007-05-10 15:45:46 +02:00
int __init sclp_sdias_init ( void )
2007-04-27 16:01:49 +02:00
{
int rc ;
if ( ipl_info . type ! = IPL_TYPE_FCP_DUMP )
return 0 ;
sdias_dbf = debug_register ( " dump_sdias " , 4 , 1 , 4 * sizeof ( long ) ) ;
debug_register_view ( sdias_dbf , & debug_sprintf_view ) ;
debug_set_level ( sdias_dbf , 6 ) ;
rc = sclp_register ( & sclp_sdias_register ) ;
2008-07-14 09:59:28 +02:00
if ( rc )
2007-04-27 16:01:49 +02:00
return rc ;
init_waitqueue_head ( & sdias_wq ) ;
TRACE ( " init done \n " ) ;
return 0 ;
}
2007-05-10 15:45:46 +02:00
void __exit sclp_sdias_exit ( void )
2007-04-27 16:01:49 +02:00
{
debug_unregister ( sdias_dbf ) ;
sclp_unregister ( & sclp_sdias_register ) ;
}