2005-04-17 02:20:36 +04:00
/*
* drivers / s390 / cio / device_pgid . c
*
* Copyright ( C ) 2002 IBM Deutschland Entwicklung GmbH ,
* IBM Corporation
2006-01-15 00:21:04 +03:00
* Author ( s ) : Cornelia Huck ( cornelia . huck @ de . ibm . com )
2005-04-17 02:20:36 +04:00
* Martin Schwidefsky ( schwidefsky @ de . ibm . com )
*
* Path Group ID functions .
*/
# include <linux/module.h>
# include <linux/init.h>
# include <asm/ccwdev.h>
# include <asm/cio.h>
# include <asm/delay.h>
# include <asm/lowcore.h>
# include "cio.h"
# include "cio_debug.h"
# include "css.h"
# include "device.h"
2006-01-06 11:19:13 +03:00
# include "ioasm.h"
2005-04-17 02:20:36 +04:00
2006-08-30 16:33:39 +04:00
/*
* Helper function called from interrupt context to decide whether an
* operation should be tried again .
*/
static int __ccw_device_should_retry ( struct scsw * scsw )
{
/* CC is only valid if start function bit is set. */
if ( ( scsw - > fctl & SCSW_FCTL_START_FUNC ) & & scsw - > cc = = 1 )
return 1 ;
/* No more activity. For sense and set PGID we stubbornly try again. */
if ( ! scsw - > actl )
return 1 ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
/*
* Start Sense Path Group ID helper function . Used in ccw_device_recog
* and ccw_device_sense_pgid .
*/
static int
__ccw_device_sense_pgid_start ( struct ccw_device * cdev )
{
struct subchannel * sch ;
struct ccw1 * ccw ;
int ret ;
2006-07-12 18:40:19 +04:00
int i ;
2005-04-17 02:20:36 +04:00
sch = to_subchannel ( cdev - > dev . parent ) ;
2006-07-12 18:40:19 +04:00
/* Return if we already checked on all paths. */
if ( cdev - > private - > imask = = 0 )
return ( sch - > lpm = = 0 ) ? - ENODEV : - EACCES ;
i = 8 - ffs ( cdev - > private - > imask ) ;
2005-04-17 02:20:36 +04:00
/* Setup sense path group id channel program. */
ccw = cdev - > private - > iccws ;
ccw - > cmd_code = CCW_CMD_SENSE_PGID ;
ccw - > count = sizeof ( struct pgid ) ;
ccw - > flags = CCW_FLAG_SLI ;
/* Reset device status. */
memset ( & cdev - > private - > irb , 0 , sizeof ( struct irb ) ) ;
/* Try on every path. */
ret = - ENODEV ;
while ( cdev - > private - > imask ! = 0 ) {
/* Try every path multiple times. */
2006-07-12 18:40:19 +04:00
ccw - > cda = ( __u32 ) __pa ( & cdev - > private - > pgid [ i ] ) ;
2005-04-17 02:20:36 +04:00
if ( cdev - > private - > iretry > 0 ) {
cdev - > private - > iretry - - ;
ret = cio_start ( sch , cdev - > private - > iccws ,
cdev - > private - > imask ) ;
/* ret is 0, -EBUSY, -EACCES or -ENODEV */
if ( ret ! = - EACCES )
return ret ;
CIO_MSG_EVENT ( 2 , " SNID - Device %04x on Subchannel "
2006-01-06 11:19:25 +03:00
" 0.%x.%04x, lpm %02X, became 'not "
2005-04-17 02:20:36 +04:00
" operational' \n " ,
2006-01-06 11:19:25 +03:00
cdev - > private - > devno , sch - > schid . ssid ,
sch - > schid . sch_no , cdev - > private - > imask ) ;
2005-04-17 02:20:36 +04:00
}
cdev - > private - > imask > > = 1 ;
cdev - > private - > iretry = 5 ;
2006-07-12 18:40:19 +04:00
i + + ;
2005-04-17 02:20:36 +04:00
}
2006-07-12 18:40:19 +04:00
2005-04-17 02:20:36 +04:00
return ret ;
}
void
ccw_device_sense_pgid_start ( struct ccw_device * cdev )
{
int ret ;
cdev - > private - > state = DEV_STATE_SENSE_PGID ;
cdev - > private - > imask = 0x80 ;
cdev - > private - > iretry = 5 ;
2006-07-12 18:40:19 +04:00
memset ( & cdev - > private - > pgid , 0 , sizeof ( cdev - > private - > pgid ) ) ;
2005-04-17 02:20:36 +04:00
ret = __ccw_device_sense_pgid_start ( cdev ) ;
if ( ret & & ret ! = - EBUSY )
ccw_device_sense_pgid_done ( cdev , ret ) ;
}
/*
* Called from interrupt context to check if a valid answer
* to Sense Path Group ID was received .
*/
static int
__ccw_device_check_sense_pgid ( struct ccw_device * cdev )
{
struct subchannel * sch ;
struct irb * irb ;
2006-07-12 18:40:19 +04:00
int i ;
2005-04-17 02:20:36 +04:00
sch = to_subchannel ( cdev - > dev . parent ) ;
irb = & cdev - > private - > irb ;
if ( irb - > scsw . fctl & ( SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC ) )
return - ETIME ;
if ( irb - > esw . esw0 . erw . cons & &
( irb - > ecw [ 0 ] & ( SNS0_CMD_REJECT | SNS0_INTERVENTION_REQ ) ) ) {
/*
* If the device doesn ' t support the Sense Path Group ID
* command further retries wouldn ' t help . . .
*/
return - EOPNOTSUPP ;
}
if ( irb - > esw . esw0 . erw . cons ) {
2006-01-06 11:19:25 +03:00
CIO_MSG_EVENT ( 2 , " SNID - device 0.%x.%04x, unit check, "
2005-04-17 02:20:36 +04:00
" lpum %02X, cnt %02d, sns : "
" %02X%02X%02X%02X %02X%02X%02X%02X ... \n " ,
2006-01-06 11:19:25 +03:00
cdev - > private - > ssid , cdev - > private - > devno ,
2005-04-17 02:20:36 +04:00
irb - > esw . esw0 . sublog . lpum ,
irb - > esw . esw0 . erw . scnt ,
irb - > ecw [ 0 ] , irb - > ecw [ 1 ] ,
irb - > ecw [ 2 ] , irb - > ecw [ 3 ] ,
irb - > ecw [ 4 ] , irb - > ecw [ 5 ] ,
irb - > ecw [ 6 ] , irb - > ecw [ 7 ] ) ;
return - EAGAIN ;
}
if ( irb - > scsw . cc = = 3 ) {
2006-01-06 11:19:25 +03:00
CIO_MSG_EVENT ( 2 , " SNID - Device %04x on Subchannel 0.%x.%04x, "
" lpm %02X, became 'not operational' \n " ,
cdev - > private - > devno , sch - > schid . ssid ,
sch - > schid . sch_no , sch - > orb . lpm ) ;
2005-04-17 02:20:36 +04:00
return - EACCES ;
}
2006-07-12 18:40:19 +04:00
i = 8 - ffs ( cdev - > private - > imask ) ;
if ( cdev - > private - > pgid [ i ] . inf . ps . state2 = = SNID_STATE2_RESVD_ELSE ) {
2006-01-06 11:19:25 +03:00
CIO_MSG_EVENT ( 2 , " SNID - Device %04x on Subchannel 0.%x.%04x "
2005-04-17 02:20:36 +04:00
" is reserved by someone else \n " ,
2006-01-06 11:19:25 +03:00
cdev - > private - > devno , sch - > schid . ssid ,
sch - > schid . sch_no ) ;
2005-04-17 02:20:36 +04:00
return - EUSERS ;
}
return 0 ;
}
/*
* Got interrupt for Sense Path Group ID .
*/
void
ccw_device_sense_pgid_irq ( struct ccw_device * cdev , enum dev_event dev_event )
{
struct subchannel * sch ;
struct irb * irb ;
int ret ;
irb = ( struct irb * ) __LC_IRB ;
2006-08-30 16:33:39 +04:00
2005-04-17 02:20:36 +04:00
if ( irb - > scsw . stctl = =
( SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS ) ) {
2006-08-30 16:33:39 +04:00
if ( __ccw_device_should_retry ( & irb - > scsw ) ) {
2005-04-17 02:20:36 +04:00
ret = __ccw_device_sense_pgid_start ( cdev ) ;
if ( ret & & ret ! = - EBUSY )
ccw_device_sense_pgid_done ( cdev , ret ) ;
}
return ;
}
if ( ccw_device_accumulate_and_sense ( cdev , irb ) ! = 0 )
return ;
sch = to_subchannel ( cdev - > dev . parent ) ;
ret = __ccw_device_check_sense_pgid ( cdev ) ;
memset ( & cdev - > private - > irb , 0 , sizeof ( struct irb ) ) ;
switch ( ret ) {
/* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */
case - EOPNOTSUPP : /* Sense Path Group ID not supported */
ccw_device_sense_pgid_done ( cdev , - EOPNOTSUPP ) ;
break ;
case - ETIME : /* Sense path group id stopped by timeout. */
ccw_device_sense_pgid_done ( cdev , - ETIME ) ;
break ;
case - EACCES : /* channel is not operational. */
sch - > lpm & = ~ cdev - > private - > imask ;
2006-07-12 18:40:19 +04:00
/* Fall through. */
case 0 : /* Sense Path Group ID successful. */
2005-04-17 02:20:36 +04:00
cdev - > private - > imask > > = 1 ;
cdev - > private - > iretry = 5 ;
/* Fall through. */
case - EAGAIN : /* Try again. */
ret = __ccw_device_sense_pgid_start ( cdev ) ;
if ( ret ! = 0 & & ret ! = - EBUSY )
2006-07-12 18:40:19 +04:00
ccw_device_sense_pgid_done ( cdev , ret ) ;
2005-04-17 02:20:36 +04:00
break ;
case - EUSERS : /* device is reserved for someone else. */
ccw_device_sense_pgid_done ( cdev , - EUSERS ) ;
break ;
}
}
/*
* Path Group ID helper function .
*/
static int
__ccw_device_do_pgid ( struct ccw_device * cdev , __u8 func )
{
struct subchannel * sch ;
struct ccw1 * ccw ;
int ret ;
sch = to_subchannel ( cdev - > dev . parent ) ;
/* Setup sense path group id channel program. */
2006-07-12 18:40:19 +04:00
cdev - > private - > pgid [ 0 ] . inf . fc = func ;
2005-04-17 02:20:36 +04:00
ccw = cdev - > private - > iccws ;
if ( ! cdev - > private - > flags . pgid_single ) {
2006-07-12 18:40:19 +04:00
cdev - > private - > pgid [ 0 ] . inf . fc | = SPID_FUNC_MULTI_PATH ;
2005-04-17 02:20:36 +04:00
ccw - > cmd_code = CCW_CMD_SUSPEND_RECONN ;
ccw - > cda = 0 ;
ccw - > count = 0 ;
ccw - > flags = CCW_FLAG_SLI | CCW_FLAG_CC ;
ccw + + ;
} else
2006-07-12 18:40:19 +04:00
cdev - > private - > pgid [ 0 ] . inf . fc | = SPID_FUNC_SINGLE_PATH ;
2005-04-17 02:20:36 +04:00
ccw - > cmd_code = CCW_CMD_SET_PGID ;
2006-07-12 18:40:19 +04:00
ccw - > cda = ( __u32 ) __pa ( & cdev - > private - > pgid [ 0 ] ) ;
2005-04-17 02:20:36 +04:00
ccw - > count = sizeof ( struct pgid ) ;
ccw - > flags = CCW_FLAG_SLI ;
/* Reset device status. */
memset ( & cdev - > private - > irb , 0 , sizeof ( struct irb ) ) ;
/* Try multiple times. */
2006-09-20 17:59:59 +04:00
ret = - EACCES ;
2005-04-17 02:20:36 +04:00
if ( cdev - > private - > iretry > 0 ) {
cdev - > private - > iretry - - ;
ret = cio_start ( sch , cdev - > private - > iccws ,
cdev - > private - > imask ) ;
2006-09-20 17:59:59 +04:00
/* We expect an interrupt in case of success or busy
* indication . */
if ( ( ret = = 0 ) | | ( ret = = - EBUSY ) )
2005-04-17 02:20:36 +04:00
return ret ;
}
2006-09-20 17:59:59 +04:00
/* PGID command failed on this path. */
2005-04-17 02:20:36 +04:00
CIO_MSG_EVENT ( 2 , " SPID - Device %04x on Subchannel "
2006-01-06 11:19:25 +03:00
" 0.%x.%04x, lpm %02X, became 'not operational' \n " ,
cdev - > private - > devno , sch - > schid . ssid ,
sch - > schid . sch_no , cdev - > private - > imask ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2006-07-12 18:40:19 +04:00
/*
* Helper function to send a nop ccw down a path .
*/
static int __ccw_device_do_nop ( struct ccw_device * cdev )
{
struct subchannel * sch ;
struct ccw1 * ccw ;
int ret ;
sch = to_subchannel ( cdev - > dev . parent ) ;
/* Setup nop channel program. */
ccw = cdev - > private - > iccws ;
ccw - > cmd_code = CCW_CMD_NOOP ;
ccw - > cda = 0 ;
ccw - > count = 0 ;
ccw - > flags = CCW_FLAG_SLI ;
/* Reset device status. */
memset ( & cdev - > private - > irb , 0 , sizeof ( struct irb ) ) ;
/* Try multiple times. */
2006-09-20 17:59:59 +04:00
ret = - EACCES ;
2006-07-12 18:40:19 +04:00
if ( cdev - > private - > iretry > 0 ) {
cdev - > private - > iretry - - ;
ret = cio_start ( sch , cdev - > private - > iccws ,
cdev - > private - > imask ) ;
2006-09-20 17:59:59 +04:00
/* We expect an interrupt in case of success or busy
* indication . */
if ( ( ret = = 0 ) | | ( ret = = - EBUSY ) )
2006-07-12 18:40:19 +04:00
return ret ;
}
2006-09-20 17:59:59 +04:00
/* nop command failed on this path. */
2006-07-12 18:40:19 +04:00
CIO_MSG_EVENT ( 2 , " NOP - Device %04x on Subchannel "
" 0.%x.%04x, lpm %02X, became 'not operational' \n " ,
cdev - > private - > devno , sch - > schid . ssid ,
sch - > schid . sch_no , cdev - > private - > imask ) ;
return ret ;
}
2005-04-17 02:20:36 +04:00
/*
* Called from interrupt context to check if a valid answer
* to Set Path Group ID was received .
*/
static int
__ccw_device_check_pgid ( struct ccw_device * cdev )
{
struct subchannel * sch ;
struct irb * irb ;
sch = to_subchannel ( cdev - > dev . parent ) ;
irb = & cdev - > private - > irb ;
if ( irb - > scsw . fctl & ( SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC ) )
return - ETIME ;
if ( irb - > esw . esw0 . erw . cons ) {
if ( irb - > ecw [ 0 ] & SNS0_CMD_REJECT )
return - EOPNOTSUPP ;
/* Hmm, whatever happened, try again. */
2006-01-06 11:19:25 +03:00
CIO_MSG_EVENT ( 2 , " SPID - device 0.%x.%04x, unit check, "
" cnt %02d, "
2005-04-17 02:20:36 +04:00
" sns : %02X%02X%02X%02X %02X%02X%02X%02X ... \n " ,
2006-01-06 11:19:25 +03:00
cdev - > private - > ssid ,
2005-04-17 02:20:36 +04:00
cdev - > private - > devno , irb - > esw . esw0 . erw . scnt ,
irb - > ecw [ 0 ] , irb - > ecw [ 1 ] ,
irb - > ecw [ 2 ] , irb - > ecw [ 3 ] ,
irb - > ecw [ 4 ] , irb - > ecw [ 5 ] ,
irb - > ecw [ 6 ] , irb - > ecw [ 7 ] ) ;
return - EAGAIN ;
}
if ( irb - > scsw . cc = = 3 ) {
2006-01-06 11:19:25 +03:00
CIO_MSG_EVENT ( 2 , " SPID - Device %04x on Subchannel 0.%x.%04x, "
" lpm %02X, became 'not operational' \n " ,
cdev - > private - > devno , sch - > schid . ssid ,
sch - > schid . sch_no , cdev - > private - > imask ) ;
2005-04-17 02:20:36 +04:00
return - EACCES ;
}
return 0 ;
}
2006-07-12 18:40:19 +04:00
/*
* Called from interrupt context to check the path status after a nop has
* been send .
*/
static int __ccw_device_check_nop ( struct ccw_device * cdev )
{
struct subchannel * sch ;
struct irb * irb ;
sch = to_subchannel ( cdev - > dev . parent ) ;
irb = & cdev - > private - > irb ;
if ( irb - > scsw . fctl & ( SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC ) )
return - ETIME ;
if ( irb - > scsw . cc = = 3 ) {
CIO_MSG_EVENT ( 2 , " NOP - Device %04x on Subchannel 0.%x.%04x, "
" lpm %02X, became 'not operational' \n " ,
cdev - > private - > devno , sch - > schid . ssid ,
sch - > schid . sch_no , cdev - > private - > imask ) ;
return - EACCES ;
}
return 0 ;
}
2005-04-17 02:20:36 +04:00
static void
__ccw_device_verify_start ( struct ccw_device * cdev )
{
struct subchannel * sch ;
2006-09-20 17:59:59 +04:00
__u8 func ;
2005-04-17 02:20:36 +04:00
int ret ;
sch = to_subchannel ( cdev - > dev . parent ) ;
2006-09-20 17:59:59 +04:00
/* Repeat for all paths. */
for ( ; cdev - > private - > imask ; cdev - > private - > imask > > = 1 ,
cdev - > private - > iretry = 5 ) {
if ( ( cdev - > private - > imask & sch - > schib . pmcw . pam ) = = 0 )
/* Path not available, try next. */
continue ;
2006-07-12 18:40:19 +04:00
if ( cdev - > private - > options . pgroup ) {
2006-09-20 17:59:59 +04:00
if ( sch - > opm & cdev - > private - > imask )
func = SPID_FUNC_ESTABLISH ;
else
func = SPID_FUNC_RESIGN ;
2006-07-12 18:40:19 +04:00
ret = __ccw_device_do_pgid ( cdev , func ) ;
} else
ret = __ccw_device_do_nop ( cdev ) ;
2006-09-20 17:59:59 +04:00
/* We expect an interrupt in case of success or busy
* indication . */
2005-04-17 02:20:36 +04:00
if ( ret = = 0 | | ret = = - EBUSY )
return ;
2006-09-20 17:59:59 +04:00
/* Permanent path failure, try next. */
2005-04-17 02:20:36 +04:00
}
2006-09-20 17:59:59 +04:00
/* Done with all paths. */
ccw_device_verify_done ( cdev , ( sch - > vpm ! = 0 ) ? 0 : - ENODEV ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Got interrupt for Set Path Group ID .
*/
void
ccw_device_verify_irq ( struct ccw_device * cdev , enum dev_event dev_event )
{
struct subchannel * sch ;
struct irb * irb ;
int ret ;
irb = ( struct irb * ) __LC_IRB ;
2006-08-30 16:33:39 +04:00
2005-04-17 02:20:36 +04:00
if ( irb - > scsw . stctl = =
( SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS ) ) {
2006-08-30 16:33:39 +04:00
if ( __ccw_device_should_retry ( & irb - > scsw ) )
2005-04-17 02:20:36 +04:00
__ccw_device_verify_start ( cdev ) ;
return ;
}
if ( ccw_device_accumulate_and_sense ( cdev , irb ) ! = 0 )
return ;
sch = to_subchannel ( cdev - > dev . parent ) ;
2006-07-12 18:40:19 +04:00
if ( cdev - > private - > options . pgroup )
ret = __ccw_device_check_pgid ( cdev ) ;
else
ret = __ccw_device_check_nop ( cdev ) ;
2005-04-17 02:20:36 +04:00
memset ( & cdev - > private - > irb , 0 , sizeof ( struct irb ) ) ;
2006-09-20 17:59:59 +04:00
2005-04-17 02:20:36 +04:00
switch ( ret ) {
/* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
case 0 :
2006-09-20 17:59:59 +04:00
/* Path verification ccw finished successfully, update lpm. */
sch - > vpm | = sch - > opm & cdev - > private - > imask ;
/* Go on with next path. */
cdev - > private - > imask > > = 1 ;
2005-04-17 02:20:36 +04:00
cdev - > private - > iretry = 5 ;
__ccw_device_verify_start ( cdev ) ;
break ;
case - EOPNOTSUPP :
/*
* One of those strange devices which claim to be able
* to do multipathing but not for Set Path Group ID .
*/
2006-07-12 18:40:19 +04:00
if ( cdev - > private - > flags . pgid_single )
cdev - > private - > options . pgroup = 0 ;
else
cdev - > private - > flags . pgid_single = 1 ;
2006-09-20 17:59:59 +04:00
/* Retry */
sch - > vpm = 0 ;
cdev - > private - > imask = 0x80 ;
cdev - > private - > iretry = 5 ;
2005-04-17 02:20:36 +04:00
/* fall through. */
case - EAGAIN : /* Try again. */
__ccw_device_verify_start ( cdev ) ;
break ;
case - ETIME : /* Set path group id stopped by timeout. */
ccw_device_verify_done ( cdev , - ETIME ) ;
break ;
case - EACCES : /* channel is not operational. */
2006-09-20 17:59:59 +04:00
cdev - > private - > imask > > = 1 ;
2005-04-17 02:20:36 +04:00
cdev - > private - > iretry = 5 ;
__ccw_device_verify_start ( cdev ) ;
break ;
}
}
void
ccw_device_verify_start ( struct ccw_device * cdev )
{
2006-01-06 11:19:13 +03:00
struct subchannel * sch = to_subchannel ( cdev - > dev . parent ) ;
2005-04-17 02:20:36 +04:00
cdev - > private - > flags . pgid_single = 0 ;
2006-09-20 17:59:59 +04:00
cdev - > private - > imask = 0x80 ;
2005-04-17 02:20:36 +04:00
cdev - > private - > iretry = 5 ;
2006-09-20 17:59:59 +04:00
/* Start with empty vpm. */
sch - > vpm = 0 ;
/* Get current pam. */
2006-01-06 11:19:21 +03:00
if ( stsch ( sch - > schid , & sch - > schib ) ) {
2006-01-06 11:19:13 +03:00
ccw_device_verify_done ( cdev , - ENODEV ) ;
return ;
}
2005-04-17 02:20:36 +04:00
__ccw_device_verify_start ( cdev ) ;
}
static void
__ccw_device_disband_start ( struct ccw_device * cdev )
{
struct subchannel * sch ;
int ret ;
sch = to_subchannel ( cdev - > dev . parent ) ;
while ( cdev - > private - > imask ! = 0 ) {
if ( sch - > lpm & cdev - > private - > imask ) {
ret = __ccw_device_do_pgid ( cdev , SPID_FUNC_DISBAND ) ;
if ( ret = = 0 )
return ;
}
cdev - > private - > iretry = 5 ;
cdev - > private - > imask > > = 1 ;
}
2006-02-18 00:52:45 +03:00
ccw_device_disband_done ( cdev , ( sch - > lpm ! = 0 ) ? 0 : - ENODEV ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Got interrupt for Unset Path Group ID .
*/
void
ccw_device_disband_irq ( struct ccw_device * cdev , enum dev_event dev_event )
{
struct subchannel * sch ;
struct irb * irb ;
int ret ;
irb = ( struct irb * ) __LC_IRB ;
2006-08-30 16:33:39 +04:00
2005-04-17 02:20:36 +04:00
if ( irb - > scsw . stctl = =
( SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS ) ) {
2006-08-30 16:33:39 +04:00
if ( __ccw_device_should_retry ( & irb - > scsw ) )
2005-04-17 02:20:36 +04:00
__ccw_device_disband_start ( cdev ) ;
return ;
}
if ( ccw_device_accumulate_and_sense ( cdev , irb ) ! = 0 )
return ;
sch = to_subchannel ( cdev - > dev . parent ) ;
ret = __ccw_device_check_pgid ( cdev ) ;
memset ( & cdev - > private - > irb , 0 , sizeof ( struct irb ) ) ;
switch ( ret ) {
/* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
case 0 : /* disband successful. */
ccw_device_disband_done ( cdev , ret ) ;
break ;
case - EOPNOTSUPP :
/*
* One of those strange devices which claim to be able
* to do multipathing but not for Unset Path Group ID .
*/
cdev - > private - > flags . pgid_single = 1 ;
/* fall through. */
case - EAGAIN : /* Try again. */
__ccw_device_disband_start ( cdev ) ;
break ;
case - ETIME : /* Set path group id stopped by timeout. */
ccw_device_disband_done ( cdev , - ETIME ) ;
break ;
case - EACCES : /* channel is not operational. */
cdev - > private - > imask > > = 1 ;
cdev - > private - > iretry = 5 ;
__ccw_device_disband_start ( cdev ) ;
break ;
}
}
void
ccw_device_disband_start ( struct ccw_device * cdev )
{
cdev - > private - > flags . pgid_single = 0 ;
cdev - > private - > iretry = 5 ;
cdev - > private - > imask = 0x80 ;
__ccw_device_disband_start ( cdev ) ;
}