2005-04-16 15:20:36 -07:00
/*
2009-12-07 12:51:27 +01:00
* CCW device PGID and path verification I / O handling .
2005-04-16 15:20:36 -07:00
*
2012-07-20 11:15:04 +02:00
* Copyright IBM Corp . 2002 , 2009
2009-12-07 12:51:27 +01:00
* Author ( s ) : Cornelia Huck < cornelia . huck @ de . ibm . com >
* Martin Schwidefsky < schwidefsky @ de . ibm . com >
* Peter Oberparleiter < peter . oberparleiter @ de . ibm . com >
2005-04-16 15:20:36 -07:00
*/
2009-12-07 12:51:27 +01:00
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/bitops.h>
2005-04-16 15:20:36 -07:00
# include <asm/ccwdev.h>
# include <asm/cio.h>
# include "cio.h"
# include "cio_debug.h"
# include "device.h"
2008-01-26 14:10:43 +01:00
# include "io_sch.h"
2005-04-16 15:20:36 -07:00
2009-12-07 12:51:40 +01:00
# define PGID_RETRIES 256
2009-12-07 12:51:27 +01:00
# define PGID_TIMEOUT (10 * HZ)
2013-01-28 19:32:56 +01:00
static void verify_start ( struct ccw_device * cdev ) ;
2006-08-30 14:33:39 +02:00
/*
2009-12-07 12:51:27 +01:00
* Process path verification data and report result .
2006-08-30 14:33:39 +02:00
*/
2009-12-07 12:51:27 +01:00
static void verify_done ( struct ccw_device * cdev , int rc )
2006-08-30 14:33:39 +02:00
{
2009-12-07 12:51:27 +01:00
struct subchannel * sch = to_subchannel ( cdev - > dev . parent ) ;
struct ccw_dev_id * id = & cdev - > private - > dev_id ;
2009-12-07 12:51:30 +01:00
int mpath = cdev - > private - > flags . mpath ;
int pgroup = cdev - > private - > flags . pgroup ;
2009-12-07 12:51:27 +01:00
if ( rc )
goto out ;
/* Ensure consistent multipathing state at device and channel. */
if ( sch - > config . mp ! = mpath ) {
sch - > config . mp = mpath ;
rc = cio_commit_config ( sch ) ;
}
out :
CIO_MSG_EVENT ( 2 , " vrfy: device 0.%x.%04x: rc=%d pgroup=%d mpath=%d "
" vpm=%02x \n " , id - > ssid , id - > devno , rc , pgroup , mpath ,
sch - > vpm ) ;
ccw_device_verify_done ( cdev , rc ) ;
2006-08-30 14:33:39 +02:00
}
2005-04-16 15:20:36 -07:00
/*
2009-12-07 12:51:27 +01:00
* Create channel program to perform a NOOP .
2005-04-16 15:20:36 -07:00
*/
2009-12-07 12:51:27 +01:00
static void nop_build_cp ( struct ccw_device * cdev )
2005-04-16 15:20:36 -07:00
{
2009-12-07 12:51:27 +01:00
struct ccw_request * req = & cdev - > private - > req ;
struct ccw1 * cp = cdev - > private - > iccws ;
cp - > cmd_code = CCW_CMD_NOOP ;
cp - > cda = 0 ;
cp - > count = 0 ;
cp - > flags = CCW_FLAG_SLI ;
req - > cp = cp ;
2005-04-16 15:20:36 -07:00
}
2009-12-07 12:51:27 +01:00
/*
* Perform NOOP on a single path .
*/
static void nop_do ( struct ccw_device * cdev )
2005-04-16 15:20:36 -07:00
{
2009-12-07 12:51:27 +01:00
struct subchannel * sch = to_subchannel ( cdev - > dev . parent ) ;
struct ccw_request * req = & cdev - > private - > req ;
2013-01-28 19:31:50 +01:00
req - > lpm = lpm_adjust ( req - > lpm , sch - > schib . pmcw . pam & sch - > opm &
~ cdev - > private - > path_noirq_mask ) ;
2009-12-07 12:51:27 +01:00
if ( ! req - > lpm )
goto out_nopath ;
nop_build_cp ( cdev ) ;
ccw_request_start ( cdev ) ;
return ;
out_nopath :
verify_done ( cdev , sch - > vpm ? 0 : - EACCES ) ;
2005-04-16 15:20:36 -07:00
}
/*
2009-12-07 12:51:27 +01:00
* Adjust NOOP I / O status .
2005-04-16 15:20:36 -07:00
*/
2009-12-07 12:51:27 +01:00
static enum io_status nop_filter ( struct ccw_device * cdev , void * data ,
struct irb * irb , enum io_status status )
2005-04-16 15:20:36 -07:00
{
2009-12-07 12:51:27 +01:00
/* Only subchannel status might indicate a path error. */
if ( status = = IO_STATUS_ERROR & & irb - > scsw . cmd . cstat = = 0 )
return IO_DONE ;
return status ;
2005-04-16 15:20:36 -07:00
}
/*
2009-12-07 12:51:27 +01:00
* Process NOOP request result for a single path .
2005-04-16 15:20:36 -07:00
*/
2009-12-07 12:51:27 +01:00
static void nop_callback ( struct ccw_device * cdev , void * data , int rc )
2005-04-16 15:20:36 -07:00
{
2009-12-07 12:51:27 +01:00
struct subchannel * sch = to_subchannel ( cdev - > dev . parent ) ;
struct ccw_request * req = & cdev - > private - > req ;
2013-01-28 19:29:43 +01:00
switch ( rc ) {
case 0 :
2009-12-07 12:51:27 +01:00
sch - > vpm | = req - > lpm ;
2013-01-28 19:29:43 +01:00
break ;
case - ETIME :
cdev - > private - > path_noirq_mask | = req - > lpm ;
break ;
case - EACCES :
cdev - > private - > path_notoper_mask | = req - > lpm ;
break ;
default :
2009-12-07 12:51:27 +01:00
goto err ;
2013-01-28 19:29:43 +01:00
}
/* Continue on the next path. */
2009-12-07 12:51:27 +01:00
req - > lpm > > = 1 ;
nop_do ( cdev ) ;
return ;
err :
verify_done ( cdev , rc ) ;
2005-04-16 15:20:36 -07:00
}
/*
2009-12-07 12:51:27 +01:00
* Create channel program to perform SET PGID on a single path .
2005-04-16 15:20:36 -07:00
*/
2009-12-07 12:51:27 +01:00
static void spid_build_cp ( struct ccw_device * cdev , u8 fn )
2005-04-16 15:20:36 -07:00
{
2009-12-07 12:51:27 +01:00
struct ccw_request * req = & cdev - > private - > req ;
struct ccw1 * cp = cdev - > private - > iccws ;
int i = 8 - ffs ( req - > lpm ) ;
struct pgid * pgid = & cdev - > private - > pgid [ i ] ;
pgid - > inf . fc = fn ;
cp - > cmd_code = CCW_CMD_SET_PGID ;
cp - > cda = ( u32 ) ( addr_t ) pgid ;
cp - > count = sizeof ( * pgid ) ;
cp - > flags = CCW_FLAG_SLI ;
req - > cp = cp ;
2005-04-16 15:20:36 -07:00
}
2013-01-28 19:32:56 +01:00
static void pgid_wipeout_callback ( struct ccw_device * cdev , void * data , int rc )
{
if ( rc ) {
/* We don't know the path groups' state. Abort. */
verify_done ( cdev , rc ) ;
return ;
}
/*
* Path groups have been reset . Restart path verification but
* leave paths in path_noirq_mask out .
*/
cdev - > private - > flags . pgid_unknown = 0 ;
verify_start ( cdev ) ;
}
/*
* Reset pathgroups and restart path verification , leave unusable paths out .
*/
static void pgid_wipeout_start ( struct ccw_device * cdev )
{
struct subchannel * sch = to_subchannel ( cdev - > dev . parent ) ;
struct ccw_dev_id * id = & cdev - > private - > dev_id ;
struct ccw_request * req = & cdev - > private - > req ;
u8 fn ;
CIO_MSG_EVENT ( 2 , " wipe: device 0.%x.%04x: pvm=%02x nim=%02x \n " ,
id - > ssid , id - > devno , cdev - > private - > pgid_valid_mask ,
cdev - > private - > path_noirq_mask ) ;
/* Initialize request data. */
memset ( req , 0 , sizeof ( * req ) ) ;
req - > timeout = PGID_TIMEOUT ;
req - > maxretries = PGID_RETRIES ;
req - > lpm = sch - > schib . pmcw . pam ;
req - > callback = pgid_wipeout_callback ;
fn = SPID_FUNC_DISBAND ;
if ( cdev - > private - > flags . mpath )
fn | = SPID_FUNC_MULTI_PATH ;
spid_build_cp ( cdev , fn ) ;
ccw_request_start ( cdev ) ;
}
2006-07-12 16:40:19 +02:00
/*
2009-12-07 12:51:27 +01:00
* Perform establish / resign SET PGID on a single path .
2006-07-12 16:40:19 +02:00
*/
2009-12-07 12:51:27 +01:00
static void spid_do ( struct ccw_device * cdev )
2006-07-12 16:40:19 +02:00
{
2009-12-07 12:51:27 +01:00
struct subchannel * sch = to_subchannel ( cdev - > dev . parent ) ;
struct ccw_request * req = & cdev - > private - > req ;
u8 fn ;
2009-12-07 12:51:31 +01:00
/* Use next available path that is not already in correct state. */
2009-12-18 17:43:23 +01:00
req - > lpm = lpm_adjust ( req - > lpm , cdev - > private - > pgid_todo_mask ) ;
2009-12-07 12:51:27 +01:00
if ( ! req - > lpm )
goto out_nopath ;
/* Channel program setup. */
if ( req - > lpm & sch - > opm )
fn = SPID_FUNC_ESTABLISH ;
else
fn = SPID_FUNC_RESIGN ;
2009-12-07 12:51:30 +01:00
if ( cdev - > private - > flags . mpath )
2009-12-07 12:51:27 +01:00
fn | = SPID_FUNC_MULTI_PATH ;
spid_build_cp ( cdev , fn ) ;
ccw_request_start ( cdev ) ;
return ;
out_nopath :
2013-01-28 19:32:56 +01:00
if ( cdev - > private - > flags . pgid_unknown ) {
/* At least one SPID could be partially done. */
pgid_wipeout_start ( cdev ) ;
return ;
}
2009-12-07 12:51:27 +01:00
verify_done ( cdev , sch - > vpm ? 0 : - EACCES ) ;
2006-07-12 16:40:19 +02:00
}
2005-04-16 15:20:36 -07:00
/*
2009-12-07 12:51:27 +01:00
* Process SET PGID request result for a single path .
2005-04-16 15:20:36 -07:00
*/
2009-12-07 12:51:27 +01:00
static void spid_callback ( struct ccw_device * cdev , void * data , int rc )
2005-04-16 15:20:36 -07:00
{
2009-12-07 12:51:27 +01:00
struct subchannel * sch = to_subchannel ( cdev - > dev . parent ) ;
struct ccw_request * req = & cdev - > private - > req ;
switch ( rc ) {
case 0 :
sch - > vpm | = req - > lpm & sch - > opm ;
break ;
2013-01-28 19:29:43 +01:00
case - ETIME :
cdev - > private - > flags . pgid_unknown = 1 ;
cdev - > private - > path_noirq_mask | = req - > lpm ;
break ;
2009-12-07 12:51:27 +01:00
case - EACCES :
2013-01-28 19:29:43 +01:00
cdev - > private - > path_notoper_mask | = req - > lpm ;
2009-12-07 12:51:27 +01:00
break ;
case - EOPNOTSUPP :
2009-12-07 12:51:30 +01:00
if ( cdev - > private - > flags . mpath ) {
2009-12-07 12:51:27 +01:00
/* Try without multipathing. */
2009-12-07 12:51:30 +01:00
cdev - > private - > flags . mpath = 0 ;
2009-12-07 12:51:27 +01:00
goto out_restart ;
2006-12-04 15:41:04 +01:00
}
2009-12-07 12:51:27 +01:00
/* Try without pathgrouping. */
2009-12-07 12:51:30 +01:00
cdev - > private - > flags . pgroup = 0 ;
2009-12-07 12:51:27 +01:00
goto out_restart ;
default :
goto err ;
2005-04-16 15:20:36 -07:00
}
2009-12-07 12:51:27 +01:00
req - > lpm > > = 1 ;
spid_do ( cdev ) ;
return ;
out_restart :
verify_start ( cdev ) ;
return ;
err :
verify_done ( cdev , rc ) ;
}
2009-12-07 12:51:31 +01:00
static void spid_start ( struct ccw_device * cdev )
{
struct ccw_request * req = & cdev - > private - > req ;
/* Initialize request data. */
memset ( req , 0 , sizeof ( * req ) ) ;
req - > timeout = PGID_TIMEOUT ;
req - > maxretries = PGID_RETRIES ;
req - > lpm = 0x80 ;
2010-08-09 18:12:53 +02:00
req - > singlepath = 1 ;
2009-12-07 12:51:31 +01:00
req - > callback = spid_callback ;
spid_do ( cdev ) ;
}
2010-10-25 16:10:34 +02:00
static int pgid_is_reset ( struct pgid * p )
{
char * c ;
for ( c = ( char * ) p + 1 ; c < ( char * ) ( p + 1 ) ; c + + ) {
if ( * c ! = 0 )
return 0 ;
}
return 1 ;
}
2009-12-07 12:51:27 +01:00
static int pgid_cmp ( struct pgid * p1 , struct pgid * p2 )
{
return memcmp ( ( char * ) p1 + 1 , ( char * ) p2 + 1 ,
sizeof ( struct pgid ) - 1 ) ;
2005-04-16 15:20:36 -07:00
}
2006-07-12 16:40:19 +02:00
/*
2009-12-07 12:51:27 +01:00
* Determine pathgroup state from PGID data .
2006-07-12 16:40:19 +02:00
*/
2009-12-07 12:51:27 +01:00
static void pgid_analyze ( struct ccw_device * cdev , struct pgid * * p ,
2012-11-30 16:48:59 +01:00
int * mismatch , u8 * reserved , u8 * reset )
2006-07-12 16:40:19 +02:00
{
2009-12-07 12:51:27 +01:00
struct pgid * pgid = & cdev - > private - > pgid [ 0 ] ;
struct pgid * first = NULL ;
int lpm ;
int i ;
* mismatch = 0 ;
* reserved = 0 ;
* reset = 0 ;
for ( i = 0 , lpm = 0x80 ; i < 8 ; i + + , pgid + + , lpm > > = 1 ) {
if ( ( cdev - > private - > pgid_valid_mask & lpm ) = = 0 )
continue ;
if ( pgid - > inf . ps . state2 = = SNID_STATE2_RESVD_ELSE )
2012-11-30 16:48:59 +01:00
* reserved | = lpm ;
2010-10-25 16:10:34 +02:00
if ( pgid_is_reset ( pgid ) ) {
* reset | = lpm ;
2009-12-07 12:51:27 +01:00
continue ;
2006-12-04 15:41:04 +01:00
}
2009-12-07 12:51:27 +01:00
if ( ! first ) {
first = pgid ;
continue ;
}
if ( pgid_cmp ( pgid , first ) ! = 0 )
* mismatch = 1 ;
2006-07-12 16:40:19 +02:00
}
2009-12-07 12:51:27 +01:00
if ( ! first )
first = & channel_subsystems [ 0 ] - > global_pgid ;
* p = first ;
2006-07-12 16:40:19 +02:00
}
2009-12-18 17:43:23 +01:00
static u8 pgid_to_donepm ( struct ccw_device * cdev )
2009-12-07 12:51:31 +01:00
{
struct subchannel * sch = to_subchannel ( cdev - > dev . parent ) ;
struct pgid * pgid ;
int i ;
int lpm ;
2009-12-18 17:43:23 +01:00
u8 donepm = 0 ;
2009-12-07 12:51:31 +01:00
2009-12-18 17:43:23 +01:00
/* Set bits for paths which are already in the target state. */
2009-12-07 12:51:31 +01:00
for ( i = 0 ; i < 8 ; i + + ) {
lpm = 0x80 > > i ;
if ( ( cdev - > private - > pgid_valid_mask & lpm ) = = 0 )
continue ;
pgid = & cdev - > private - > pgid [ i ] ;
if ( sch - > opm & lpm ) {
if ( pgid - > inf . ps . state1 ! = SNID_STATE1_GROUPED )
continue ;
} else {
if ( pgid - > inf . ps . state1 ! = SNID_STATE1_UNGROUPED )
continue ;
}
if ( cdev - > private - > flags . mpath ) {
if ( pgid - > inf . ps . state3 ! = SNID_STATE3_MULTI_PATH )
continue ;
} else {
if ( pgid - > inf . ps . state3 ! = SNID_STATE3_SINGLE_PATH )
continue ;
}
2009-12-18 17:43:23 +01:00
donepm | = lpm ;
2009-12-07 12:51:31 +01:00
}
2009-12-18 17:43:23 +01:00
return donepm ;
2009-12-07 12:51:31 +01:00
}
2009-12-07 12:51:27 +01:00
static void pgid_fill ( struct ccw_device * cdev , struct pgid * pgid )
2005-04-16 15:20:36 -07:00
{
2009-12-07 12:51:27 +01:00
int i ;
for ( i = 0 ; i < 8 ; i + + )
memcpy ( & cdev - > private - > pgid [ i ] , pgid , sizeof ( struct pgid ) ) ;
}
/*
* Process SENSE PGID data and report result .
*/
static void snid_done ( struct ccw_device * cdev , int rc )
{
struct ccw_dev_id * id = & cdev - > private - > dev_id ;
2009-12-07 12:51:31 +01:00
struct subchannel * sch = to_subchannel ( cdev - > dev . parent ) ;
2009-12-07 12:51:27 +01:00
struct pgid * pgid ;
int mismatch = 0 ;
2012-11-30 16:48:59 +01:00
u8 reserved = 0 ;
2010-10-25 16:10:34 +02:00
u8 reset = 0 ;
2009-12-18 17:43:23 +01:00
u8 donepm ;
2009-12-07 12:51:27 +01:00
if ( rc )
goto out ;
pgid_analyze ( cdev , & pgid , & mismatch , & reserved , & reset ) ;
2012-11-30 16:48:59 +01:00
if ( reserved = = cdev - > private - > pgid_valid_mask )
2009-12-07 12:51:27 +01:00
rc = - EUSERS ;
2009-12-07 12:51:31 +01:00
else if ( mismatch )
rc = - EOPNOTSUPP ;
else {
2009-12-18 17:43:23 +01:00
donepm = pgid_to_donepm ( cdev ) ;
sch - > vpm = donepm & sch - > opm ;
2010-10-25 16:10:34 +02:00
cdev - > private - > pgid_reset_mask | = reset ;
2013-01-28 19:31:50 +01:00
cdev - > private - > pgid_todo_mask & =
~ ( donepm | cdev - > private - > path_noirq_mask ) ;
2009-12-07 12:51:31 +01:00
pgid_fill ( cdev , pgid ) ;
}
2009-12-07 12:51:27 +01:00
out :
2009-12-07 12:51:31 +01:00
CIO_MSG_EVENT ( 2 , " snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x "
2012-11-30 16:48:59 +01:00
" todo=%02x mism=%d rsvd=%02x reset=%02x \n " , id - > ssid ,
2009-12-18 17:43:23 +01:00
id - > devno , rc , cdev - > private - > pgid_valid_mask , sch - > vpm ,
cdev - > private - > pgid_todo_mask , mismatch , reserved , reset ) ;
2009-12-07 12:51:31 +01:00
switch ( rc ) {
case 0 :
2013-01-28 19:32:56 +01:00
if ( cdev - > private - > flags . pgid_unknown ) {
pgid_wipeout_start ( cdev ) ;
return ;
}
2009-12-07 12:51:31 +01:00
/* Anything left to do? */
2009-12-18 17:43:23 +01:00
if ( cdev - > private - > pgid_todo_mask = = 0 ) {
2009-12-07 12:51:31 +01:00
verify_done ( cdev , sch - > vpm = = 0 ? - EACCES : 0 ) ;
return ;
}
/* Perform path-grouping. */
spid_start ( cdev ) ;
break ;
case - EOPNOTSUPP :
/* Path-grouping not supported. */
cdev - > private - > flags . pgroup = 0 ;
cdev - > private - > flags . mpath = 0 ;
verify_start ( cdev ) ;
break ;
default :
verify_done ( cdev , rc ) ;
}
2005-04-16 15:20:36 -07:00
}
2009-12-07 12:51:27 +01:00
2005-04-16 15:20:36 -07:00
/*
2009-12-07 12:51:27 +01:00
* Create channel program to perform a SENSE PGID on a single path .
2005-04-16 15:20:36 -07:00
*/
2009-12-07 12:51:27 +01:00
static void snid_build_cp ( struct ccw_device * cdev )
2005-04-16 15:20:36 -07:00
{
2009-12-07 12:51:27 +01:00
struct ccw_request * req = & cdev - > private - > req ;
struct ccw1 * cp = cdev - > private - > iccws ;
int i = 8 - ffs ( req - > lpm ) ;
/* Channel program setup. */
cp - > cmd_code = CCW_CMD_SENSE_PGID ;
cp - > cda = ( u32 ) ( addr_t ) & cdev - > private - > pgid [ i ] ;
cp - > count = sizeof ( struct pgid ) ;
cp - > flags = CCW_FLAG_SLI ;
req - > cp = cp ;
}
2005-04-16 15:20:36 -07:00
2009-12-07 12:51:27 +01:00
/*
* Perform SENSE PGID on a single path .
*/
static void snid_do ( struct ccw_device * cdev )
{
struct subchannel * sch = to_subchannel ( cdev - > dev . parent ) ;
struct ccw_request * req = & cdev - > private - > req ;
2013-01-28 19:32:56 +01:00
int ret ;
2009-12-07 12:51:27 +01:00
2013-01-28 19:31:50 +01:00
req - > lpm = lpm_adjust ( req - > lpm , sch - > schib . pmcw . pam &
~ cdev - > private - > path_noirq_mask ) ;
2009-12-07 12:51:27 +01:00
if ( ! req - > lpm )
goto out_nopath ;
snid_build_cp ( cdev ) ;
ccw_request_start ( cdev ) ;
return ;
out_nopath :
2013-01-28 19:32:56 +01:00
if ( cdev - > private - > pgid_valid_mask )
ret = 0 ;
else if ( cdev - > private - > path_noirq_mask )
ret = - ETIME ;
else
ret = - EACCES ;
snid_done ( cdev , ret ) ;
2009-12-07 12:51:27 +01:00
}
2006-08-30 14:33:39 +02:00
2009-12-07 12:51:27 +01:00
/*
* Process SENSE PGID request result for single path .
*/
static void snid_callback ( struct ccw_device * cdev , void * data , int rc )
{
struct ccw_request * req = & cdev - > private - > req ;
2013-01-28 19:29:43 +01:00
switch ( rc ) {
case 0 :
2009-12-07 12:51:27 +01:00
cdev - > private - > pgid_valid_mask | = req - > lpm ;
2013-01-28 19:29:43 +01:00
break ;
case - ETIME :
cdev - > private - > flags . pgid_unknown = 1 ;
cdev - > private - > path_noirq_mask | = req - > lpm ;
break ;
case - EACCES :
cdev - > private - > path_notoper_mask | = req - > lpm ;
break ;
default :
2009-12-07 12:51:27 +01:00
goto err ;
2013-01-28 19:29:43 +01:00
}
/* Continue on the next path. */
2009-12-07 12:51:27 +01:00
req - > lpm > > = 1 ;
snid_do ( cdev ) ;
return ;
err :
snid_done ( cdev , rc ) ;
}
2006-09-20 15:59:59 +02:00
2009-12-07 12:51:27 +01:00
/*
* Perform path verification .
*/
static void verify_start ( struct ccw_device * cdev )
2005-04-16 15:20:36 -07:00
{
2006-01-06 00:19:13 -08:00
struct subchannel * sch = to_subchannel ( cdev - > dev . parent ) ;
2009-12-07 12:51:27 +01:00
struct ccw_request * req = & cdev - > private - > req ;
2009-12-07 12:51:31 +01:00
struct ccw_dev_id * devid = & cdev - > private - > dev_id ;
2006-01-06 00:19:13 -08:00
2006-09-20 15:59:59 +02:00
sch - > vpm = 0 ;
2009-12-18 17:43:23 +01:00
sch - > lpm = sch - > schib . pmcw . pam ;
2013-01-28 19:29:43 +01:00
/* Initialize PGID data. */
memset ( cdev - > private - > pgid , 0 , sizeof ( cdev - > private - > pgid ) ) ;
cdev - > private - > pgid_valid_mask = 0 ;
cdev - > private - > pgid_todo_mask = sch - > schib . pmcw . pam ;
cdev - > private - > path_notoper_mask = 0 ;
2009-12-07 12:51:27 +01:00
/* Initialize request data. */
memset ( req , 0 , sizeof ( * req ) ) ;
req - > timeout = PGID_TIMEOUT ;
req - > maxretries = PGID_RETRIES ;
req - > lpm = 0x80 ;
2010-08-09 18:12:53 +02:00
req - > singlepath = 1 ;
2009-12-07 12:51:30 +01:00
if ( cdev - > private - > flags . pgroup ) {
2009-12-07 12:51:31 +01:00
CIO_TRACE_EVENT ( 4 , " snid " ) ;
CIO_HEX_EVENT ( 4 , devid , sizeof ( * devid ) ) ;
req - > callback = snid_callback ;
snid_do ( cdev ) ;
2009-12-07 12:51:27 +01:00
} else {
2009-12-07 12:51:31 +01:00
CIO_TRACE_EVENT ( 4 , " nop " ) ;
CIO_HEX_EVENT ( 4 , devid , sizeof ( * devid ) ) ;
2009-12-07 12:51:27 +01:00
req - > filter = nop_filter ;
req - > callback = nop_callback ;
nop_do ( cdev ) ;
2006-01-06 00:19:13 -08:00
}
2005-04-16 15:20:36 -07:00
}
2009-12-07 12:51:27 +01:00
/**
* ccw_device_verify_start - perform path verification
* @ cdev : ccw device
*
* Perform an I / O on each available channel path to @ cdev to determine which
* paths are operational . The resulting path mask is stored in sch - > vpm .
* If device options specify pathgrouping , establish a pathgroup for the
* operational paths . When finished , call ccw_device_verify_done with a
* return code specifying the result .
*/
void ccw_device_verify_start ( struct ccw_device * cdev )
2005-04-16 15:20:36 -07:00
{
2009-12-07 12:51:27 +01:00
CIO_TRACE_EVENT ( 4 , " vrfy " ) ;
CIO_HEX_EVENT ( 4 , & cdev - > private - > dev_id , sizeof ( cdev - > private - > dev_id ) ) ;
2009-12-07 12:51:31 +01:00
/*
* Initialize pathgroup and multipath state with target values .
* They may change in the course of path verification .
*/
cdev - > private - > flags . pgroup = cdev - > private - > options . pgroup ;
cdev - > private - > flags . mpath = cdev - > private - > options . mpath ;
2009-12-07 12:51:27 +01:00
cdev - > private - > flags . doverify = 0 ;
2013-01-28 19:29:43 +01:00
cdev - > private - > path_noirq_mask = 0 ;
2009-12-07 12:51:27 +01:00
verify_start ( cdev ) ;
2005-04-16 15:20:36 -07:00
}
/*
2009-12-07 12:51:27 +01:00
* Process disband SET PGID request result .
2005-04-16 15:20:36 -07:00
*/
2009-12-07 12:51:27 +01:00
static void disband_callback ( struct ccw_device * cdev , void * data , int rc )
2005-04-16 15:20:36 -07:00
{
2009-12-07 12:51:27 +01:00
struct subchannel * sch = to_subchannel ( cdev - > dev . parent ) ;
struct ccw_dev_id * id = & cdev - > private - > dev_id ;
if ( rc )
goto out ;
/* Ensure consistent multipathing state at device and channel. */
2009-12-07 12:51:30 +01:00
cdev - > private - > flags . mpath = 0 ;
2009-12-07 12:51:27 +01:00
if ( sch - > config . mp ) {
sch - > config . mp = 0 ;
rc = cio_commit_config ( sch ) ;
2005-04-16 15:20:36 -07:00
}
2009-12-07 12:51:27 +01:00
out :
CIO_MSG_EVENT ( 0 , " disb: device 0.%x.%04x: rc=%d \n " , id - > ssid , id - > devno ,
rc ) ;
ccw_device_disband_done ( cdev , rc ) ;
2005-04-16 15:20:36 -07:00
}
2009-12-07 12:51:27 +01:00
/**
* ccw_device_disband_start - disband pathgroup
* @ cdev : ccw device
*
* Execute a SET PGID channel program on @ cdev to disband a previously
* established pathgroup . When finished , call ccw_device_disband_done with
* a return code specifying the result .
*/
void ccw_device_disband_start ( struct ccw_device * cdev )
2005-04-16 15:20:36 -07:00
{
2009-12-07 12:51:27 +01:00
struct subchannel * sch = to_subchannel ( cdev - > dev . parent ) ;
struct ccw_request * req = & cdev - > private - > req ;
u8 fn ;
CIO_TRACE_EVENT ( 4 , " disb " ) ;
CIO_HEX_EVENT ( 4 , & cdev - > private - > dev_id , sizeof ( cdev - > private - > dev_id ) ) ;
/* Request setup. */
memset ( req , 0 , sizeof ( * req ) ) ;
req - > timeout = PGID_TIMEOUT ;
req - > maxretries = PGID_RETRIES ;
req - > lpm = sch - > schib . pmcw . pam & sch - > opm ;
2010-08-09 18:12:53 +02:00
req - > singlepath = 1 ;
2009-12-07 12:51:27 +01:00
req - > callback = disband_callback ;
fn = SPID_FUNC_DISBAND ;
2009-12-07 12:51:30 +01:00
if ( cdev - > private - > flags . mpath )
2009-12-07 12:51:27 +01:00
fn | = SPID_FUNC_MULTI_PATH ;
spid_build_cp ( cdev , fn ) ;
ccw_request_start ( cdev ) ;
2005-04-16 15:20:36 -07:00
}
2009-12-07 12:51:32 +01:00
static void stlck_build_cp ( struct ccw_device * cdev , void * buf1 , void * buf2 )
{
struct ccw_request * req = & cdev - > private - > req ;
struct ccw1 * cp = cdev - > private - > iccws ;
cp [ 0 ] . cmd_code = CCW_CMD_STLCK ;
cp [ 0 ] . cda = ( u32 ) ( addr_t ) buf1 ;
cp [ 0 ] . count = 32 ;
cp [ 0 ] . flags = CCW_FLAG_CC ;
cp [ 1 ] . cmd_code = CCW_CMD_RELEASE ;
cp [ 1 ] . cda = ( u32 ) ( addr_t ) buf2 ;
cp [ 1 ] . count = 32 ;
cp [ 1 ] . flags = 0 ;
req - > cp = cp ;
}
static void stlck_callback ( struct ccw_device * cdev , void * data , int rc )
{
ccw_device_stlck_done ( cdev , data , rc ) ;
}
/**
* ccw_device_stlck_start - perform unconditional release
* @ cdev : ccw device
* @ data : data pointer to be passed to ccw_device_stlck_done
* @ buf1 : data pointer used in channel program
* @ buf2 : data pointer used in channel program
*
* Execute a channel program on @ cdev to release an existing PGID reservation .
* When finished , call ccw_device_stlck_done with a return code specifying the
* result .
*/
void ccw_device_stlck_start ( struct ccw_device * cdev , void * data , void * buf1 ,
void * buf2 )
{
struct subchannel * sch = to_subchannel ( cdev - > dev . parent ) ;
struct ccw_request * req = & cdev - > private - > req ;
CIO_TRACE_EVENT ( 4 , " stlck " ) ;
CIO_HEX_EVENT ( 4 , & cdev - > private - > dev_id , sizeof ( cdev - > private - > dev_id ) ) ;
/* Request setup. */
memset ( req , 0 , sizeof ( * req ) ) ;
req - > timeout = PGID_TIMEOUT ;
req - > maxretries = PGID_RETRIES ;
req - > lpm = sch - > schib . pmcw . pam & sch - > opm ;
req - > data = data ;
req - > callback = stlck_callback ;
stlck_build_cp ( cdev , buf1 , buf2 ) ;
ccw_request_start ( cdev ) ;
}