2007-04-27 18:01:49 +04:00
/*
2013-11-13 13:38:27 +04:00
* SCLP " store data in absolute storage "
2007-04-27 18:01:49 +04:00
*
2013-11-13 13:38:27 +04:00
* Copyright IBM Corp . 2003 , 2013
2007-04-27 18:01:49 +04:00
* Author ( s ) : Michael Holzheu
*/
2008-12-25 15:39:48 +03:00
# define KMSG_COMPONENT "sclp_sdias"
# define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
2012-03-11 19:59:29 +04:00
# include <linux/completion.h>
2007-04-27 18:01:49 +04:00
# include <linux/sched.h>
# include <asm/sclp.h>
# include <asm/debug.h>
# include <asm/ipl.h>
2008-12-25 15:39:48 +03:00
2013-11-13 13:38:27 +04:00
# include "sclp_sdias.h"
2007-04-27 18:01:49 +04:00
# include "sclp.h"
# include "sclp_rw.h"
# define TRACE(x...) debug_sprintf_event(sdias_dbf, 1, x)
# define SDIAS_RETRIES 300
static struct debug_info * sdias_dbf ;
static struct sclp_register sclp_sdias_register = {
2007-04-27 18:01:53 +04:00
. send_mask = EVTYP_SDIAS_MASK ,
2007-04-27 18:01:49 +04:00
} ;
static struct sdias_sccb sccb __attribute__ ( ( aligned ( 4096 ) ) ) ;
2012-03-11 19:59:29 +04:00
static struct sdias_evbuf sdias_evbuf ;
2007-04-27 18:01:49 +04:00
2012-03-11 19:59:29 +04:00
static DECLARE_COMPLETION ( evbuf_accepted ) ;
static DECLARE_COMPLETION ( evbuf_done ) ;
2007-04-27 18:01:49 +04:00
static DEFINE_MUTEX ( sdias_mutex ) ;
2012-03-11 19:59:29 +04:00
/*
* Called by SCLP base when read event data has been completed ( async mode only )
*/
static void sclp_sdias_receiver_fn ( struct evbuf_header * evbuf )
{
memcpy ( & sdias_evbuf , evbuf ,
min_t ( unsigned long , sizeof ( sdias_evbuf ) , evbuf - > length ) ) ;
complete ( & evbuf_done ) ;
TRACE ( " sclp_sdias_receiver_fn done \n " ) ;
}
/*
* Called by SCLP base when sdias event has been accepted
*/
2007-04-27 18:01:49 +04:00
static void sdias_callback ( struct sclp_req * request , void * data )
{
2012-03-11 19:59:29 +04:00
complete ( & evbuf_accepted ) ;
2007-04-27 18:01:49 +04:00
TRACE ( " callback done \n " ) ;
}
static int sdias_sclp_send ( struct sclp_req * req )
{
int retries ;
int rc ;
for ( retries = SDIAS_RETRIES ; retries ; retries - - ) {
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 ) ;
2015-05-29 17:50:55 +03:00
schedule_timeout ( msecs_to_jiffies ( 500 ) ) ;
2007-04-27 18:01:49 +04:00
continue ;
}
/* initiated, wait for completion of service call */
2012-03-11 19:59:29 +04:00
wait_for_completion ( & evbuf_accepted ) ;
2007-04-27 18:01:49 +04:00
if ( req - > status = = SCLP_REQ_FAILED ) {
TRACE ( " sclp request failed \n " ) ;
continue ;
}
2012-03-11 19:59:29 +04:00
/* if not accepted, retry */
if ( ! ( sccb . evbuf . hdr . flags & 0x80 ) ) {
TRACE ( " sclp request failed: flags=%x \n " ,
sccb . evbuf . hdr . flags ) ;
continue ;
}
/*
* for the sync interface the response is in the initial sccb
*/
if ( ! sclp_sdias_register . receiver_fn ) {
memcpy ( & sdias_evbuf , & sccb . evbuf , sizeof ( sdias_evbuf ) ) ;
TRACE ( " sync request done \n " ) ;
return 0 ;
}
/* otherwise we wait for completion */
wait_for_completion ( & evbuf_done ) ;
2007-04-27 18:01:49 +04:00
TRACE ( " request done \n " ) ;
2012-03-11 19:59:29 +04:00
return 0 ;
2007-04-27 18:01:49 +04:00
}
2012-03-11 19:59:29 +04:00
return - EIO ;
2007-04-27 18:01:49 +04:00
}
/*
* 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 18:01:53 +04:00
sccb . evbuf . hdr . type = EVTYP_SDIAS ;
2013-11-13 13:38:27 +04:00
sccb . evbuf . event_qual = SDIAS_EQ_SIZE ;
sccb . evbuf . data_id = SDIAS_DI_FCP_DUMP ;
2007-04-27 18:01:49 +04:00
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 15:39:48 +03:00
pr_err ( " sclp_send failed for get_nr_blocks \n " ) ;
2007-04-27 18:01:49 +04:00
goto out ;
}
if ( sccb . hdr . response_code ! = 0x0020 ) {
TRACE ( " send failed: %x \n " , sccb . hdr . response_code ) ;
rc = - EIO ;
goto out ;
}
2012-03-11 19:59:29 +04:00
switch ( sdias_evbuf . event_status ) {
2007-04-27 18:01:49 +04:00
case 0 :
2012-03-11 19:59:29 +04:00
rc = sdias_evbuf . blk_cnt ;
2007-04-27 18:01:49 +04:00
break ;
default :
2012-03-11 19:59:29 +04:00
pr_err ( " SCLP error: %x \n " , sdias_evbuf . event_status ) ;
2007-04-27 18:01:49 +04: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 18:01:53 +04:00
sccb . evbuf . hdr . type = EVTYP_SDIAS ;
2007-04-27 18:01:49 +04:00
sccb . evbuf . hdr . flags = 0 ;
2013-11-13 13:38:27 +04:00
sccb . evbuf . event_qual = SDIAS_EQ_STORE_DATA ;
sccb . evbuf . data_id = SDIAS_DI_FCP_DUMP ;
2007-04-27 18:01:49 +04:00
sccb . evbuf . event_id = 4712 ;
2013-11-13 13:38:27 +04:00
sccb . evbuf . asa_size = SDIAS_ASA_SIZE_64 ;
2007-04-27 18:01:49 +04:00
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 15:39:48 +03:00
pr_err ( " sclp_send failed: %x \n " , rc ) ;
2007-04-27 18:01:49 +04:00
goto out ;
}
if ( sccb . hdr . response_code ! = 0x0020 ) {
TRACE ( " copy failed: %x \n " , sccb . hdr . response_code ) ;
rc = - EIO ;
goto out ;
}
2012-03-11 19:59:29 +04:00
switch ( sdias_evbuf . event_status ) {
2013-11-13 13:38:27 +04:00
case SDIAS_EVSTATE_ALL_STORED :
TRACE ( " all stored \n " ) ;
break ;
case SDIAS_EVSTATE_PART_STORED :
TRACE ( " part stored: %i \n " , sdias_evbuf . blk_cnt ) ;
break ;
case SDIAS_EVSTATE_NO_DATA :
TRACE ( " no data \n " ) ;
/* fall through */
default :
pr_err ( " Error from SCLP while copying hsa. Event status = %x \n " ,
sdias_evbuf . event_status ) ;
rc = - EIO ;
2007-04-27 18:01:49 +04:00
}
out :
mutex_unlock ( & sdias_mutex ) ;
return rc ;
}
2012-03-11 19:59:29 +04:00
static int __init sclp_sdias_register_check ( void )
2007-04-27 18:01:49 +04:00
{
int rc ;
2012-03-11 19:59:29 +04:00
rc = sclp_register ( & sclp_sdias_register ) ;
if ( rc )
return rc ;
if ( sclp_sdias_blk_count ( ) = = 0 ) {
sclp_unregister ( & sclp_sdias_register ) ;
return - ENODEV ;
}
return 0 ;
}
static int __init sclp_sdias_init_sync ( void )
{
TRACE ( " Try synchronous mode \n " ) ;
sclp_sdias_register . receive_mask = 0 ;
sclp_sdias_register . receiver_fn = NULL ;
return sclp_sdias_register_check ( ) ;
}
static int __init sclp_sdias_init_async ( void )
{
TRACE ( " Try asynchronous mode \n " ) ;
sclp_sdias_register . receive_mask = EVTYP_SDIAS_MASK ;
sclp_sdias_register . receiver_fn = sclp_sdias_receiver_fn ;
return sclp_sdias_register_check ( ) ;
}
int __init sclp_sdias_init ( void )
{
2007-04-27 18:01:49 +04:00
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 ) ;
2012-03-11 19:59:29 +04:00
if ( sclp_sdias_init_sync ( ) = = 0 )
goto out ;
if ( sclp_sdias_init_async ( ) = = 0 )
goto out ;
TRACE ( " init failed \n " ) ;
return - ENODEV ;
out :
2007-04-27 18:01:49 +04:00
TRACE ( " init done \n " ) ;
return 0 ;
}
2007-05-10 17:45:46 +04:00
void __exit sclp_sdias_exit ( void )
2007-04-27 18:01:49 +04:00
{
debug_unregister ( sdias_dbf ) ;
sclp_unregister ( & sclp_sdias_register ) ;
}