2007-04-27 16:01:28 +02:00
/*
* drivers / s390 / cio / chp . c
*
* Copyright IBM Corp . 1999 , 2007
* Author ( s ) : Cornelia Huck ( cornelia . huck @ de . ibm . com )
* Arnd Bergmann ( arndb @ de . ibm . com )
* Peter Oberparleiter < peter . oberparleiter @ de . ibm . com >
*/
# include <linux/bug.h>
# include <linux/workqueue.h>
# include <linux/spinlock.h>
2007-04-27 16:01:31 +02:00
# include <linux/init.h>
# include <linux/jiffies.h>
# include <linux/wait.h>
# include <linux/mutex.h>
2007-10-12 16:11:19 +02:00
# include <linux/errno.h>
2007-04-27 16:01:31 +02:00
# include <asm/chpid.h>
# include <asm/sclp.h>
2009-03-26 15:24:01 +01:00
# include <asm/crw.h>
2007-04-27 16:01:28 +02:00
# include "cio.h"
# include "css.h"
# include "ioasm.h"
# include "cio_debug.h"
# include "chp.h"
# define to_channelpath(device) container_of(device, struct channel_path, dev)
2007-04-27 16:01:31 +02:00
# define CHP_INFO_UPDATE_INTERVAL 1*HZ
enum cfg_task_t {
cfg_none ,
cfg_configure ,
cfg_deconfigure
} ;
/* Map for pending configure tasks. */
static enum cfg_task_t chp_cfg_task [ __MAX_CSSID + 1 ] [ __MAX_CHPID + 1 ] ;
static DEFINE_MUTEX ( cfg_lock ) ;
static int cfg_busy ;
/* Map for channel-path status. */
static struct sclp_chp_info chp_info ;
static DEFINE_MUTEX ( info_lock ) ;
/* Time after which channel-path status may be outdated. */
static unsigned long chp_info_expires ;
/* Workqueue to perform pending configure tasks. */
static struct workqueue_struct * chp_wq ;
static struct work_struct cfg_work ;
/* Wait queue for configure completion events. */
static wait_queue_head_t cfg_wait_queue ;
2007-04-27 16:01:28 +02:00
/* Return channel_path struct for given chpid. */
static inline struct channel_path * chpid_to_chp ( struct chp_id chpid )
{
2007-10-12 16:11:13 +02:00
return channel_subsystems [ chpid . cssid ] - > chps [ chpid . id ] ;
2007-04-27 16:01:28 +02:00
}
/* Set vary state for given chpid. */
static void set_chp_logically_online ( struct chp_id chpid , int onoff )
{
chpid_to_chp ( chpid ) - > state = onoff ;
}
tree-wide: fix assorted typos all over the place
That is "success", "unknown", "through", "performance", "[re|un]mapping"
, "access", "default", "reasonable", "[con]currently", "temperature"
, "channel", "[un]used", "application", "example","hierarchy", "therefore"
, "[over|under]flow", "contiguous", "threshold", "enough" and others.
Signed-off-by: André Goddard Rosa <andre.goddard@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2009-11-14 13:09:05 -02:00
/* On success return 0 if channel-path is varied offline, 1 if it is varied
2007-04-27 16:01:28 +02:00
* online . Return - ENODEV if channel - path is not registered . */
int chp_get_status ( struct chp_id chpid )
{
return ( chpid_to_chp ( chpid ) ? chpid_to_chp ( chpid ) - > state : - ENODEV ) ;
}
/**
* chp_get_sch_opm - return opm for subchannel
* @ sch : subchannel
*
* Calculate and return the operational path mask ( opm ) based on the chpids
* used by the subchannel and the status of the associated channel - paths .
*/
u8 chp_get_sch_opm ( struct subchannel * sch )
{
struct chp_id chpid ;
int opm ;
int i ;
opm = 0 ;
chp_id_init ( & chpid ) ;
2007-10-12 16:11:19 +02:00
for ( i = 0 ; i < 8 ; i + + ) {
2007-04-27 16:01:28 +02:00
opm < < = 1 ;
chpid . id = sch - > schib . pmcw . chpid [ i ] ;
if ( chp_get_status ( chpid ) ! = 0 )
opm | = 1 ;
}
return opm ;
}
2008-07-14 09:58:47 +02:00
EXPORT_SYMBOL_GPL ( chp_get_sch_opm ) ;
2007-04-27 16:01:28 +02:00
/**
* chp_is_registered - check if a channel - path is registered
* @ chpid : channel - path ID
*
* Return non - zero if a channel - path with the given chpid is registered ,
* zero otherwise .
*/
int chp_is_registered ( struct chp_id chpid )
{
return chpid_to_chp ( chpid ) ! = NULL ;
}
/*
* Function : s390_vary_chpid
* Varies the specified chpid online or offline
*/
static int s390_vary_chpid ( struct chp_id chpid , int on )
{
char dbf_text [ 15 ] ;
int status ;
sprintf ( dbf_text , on ? " varyon%x.%02x " : " varyoff%x.%02x " , chpid . cssid ,
chpid . id ) ;
2007-10-12 16:11:19 +02:00
CIO_TRACE_EVENT ( 2 , dbf_text ) ;
2007-04-27 16:01:28 +02:00
status = chp_get_status ( chpid ) ;
2008-07-14 09:59:22 +02:00
if ( ! on & & ! status )
return 0 ;
2007-04-27 16:01:28 +02:00
set_chp_logically_online ( chpid , on ) ;
chsc_chp_vary ( chpid , on ) ;
return 0 ;
}
/*
* Channel measurement related functions
*/
2007-06-09 13:57:22 +08:00
static ssize_t chp_measurement_chars_read ( struct kobject * kobj ,
struct bin_attribute * bin_attr ,
char * buf , loff_t off , size_t count )
2007-04-27 16:01:28 +02:00
{
struct channel_path * chp ;
2007-10-12 16:11:35 +02:00
struct device * device ;
2007-04-27 16:01:28 +02:00
2007-10-12 16:11:35 +02:00
device = container_of ( kobj , struct device , kobj ) ;
chp = to_channelpath ( device ) ;
2007-04-27 16:01:28 +02:00
if ( ! chp - > cmg_chars )
return 0 ;
2008-07-14 09:59:15 +02:00
return memory_read_from_buffer ( buf , count , & off ,
chp - > cmg_chars , sizeof ( struct cmg_chars ) ) ;
2007-04-27 16:01:28 +02:00
}
static struct bin_attribute chp_measurement_chars_attr = {
. attr = {
. name = " measurement_chars " ,
. mode = S_IRUSR ,
} ,
. size = sizeof ( struct cmg_chars ) ,
. read = chp_measurement_chars_read ,
} ;
static void chp_measurement_copy_block ( struct cmg_entry * buf ,
struct channel_subsystem * css ,
struct chp_id chpid )
{
void * area ;
struct cmg_entry * entry , reference_buf ;
int idx ;
if ( chpid . id < 128 ) {
area = css - > cub_addr1 ;
idx = chpid . id ;
} else {
area = css - > cub_addr2 ;
idx = chpid . id - 128 ;
}
entry = area + ( idx * sizeof ( struct cmg_entry ) ) ;
do {
memcpy ( buf , entry , sizeof ( * entry ) ) ;
memcpy ( & reference_buf , entry , sizeof ( * entry ) ) ;
} while ( reference_buf . values [ 0 ] ! = buf - > values [ 0 ] ) ;
}
2007-06-09 13:57:22 +08:00
static ssize_t chp_measurement_read ( struct kobject * kobj ,
struct bin_attribute * bin_attr ,
char * buf , loff_t off , size_t count )
2007-04-27 16:01:28 +02:00
{
struct channel_path * chp ;
struct channel_subsystem * css ;
2007-10-12 16:11:35 +02:00
struct device * device ;
2007-04-27 16:01:28 +02:00
unsigned int size ;
2007-10-12 16:11:35 +02:00
device = container_of ( kobj , struct device , kobj ) ;
chp = to_channelpath ( device ) ;
2007-04-27 16:01:28 +02:00
css = to_css ( chp - > dev . parent ) ;
size = sizeof ( struct cmg_entry ) ;
/* Only allow single reads. */
if ( off | | count < size )
return 0 ;
chp_measurement_copy_block ( ( struct cmg_entry * ) buf , css , chp - > chpid ) ;
count = size ;
return count ;
}
static struct bin_attribute chp_measurement_attr = {
. attr = {
. name = " measurement " ,
. mode = S_IRUSR ,
} ,
. size = sizeof ( struct cmg_entry ) ,
. read = chp_measurement_read ,
} ;
void chp_remove_cmg_attr ( struct channel_path * chp )
{
device_remove_bin_file ( & chp - > dev , & chp_measurement_chars_attr ) ;
device_remove_bin_file ( & chp - > dev , & chp_measurement_attr ) ;
}
int chp_add_cmg_attr ( struct channel_path * chp )
{
int ret ;
ret = device_create_bin_file ( & chp - > dev , & chp_measurement_chars_attr ) ;
if ( ret )
return ret ;
ret = device_create_bin_file ( & chp - > dev , & chp_measurement_attr ) ;
if ( ret )
device_remove_bin_file ( & chp - > dev , & chp_measurement_chars_attr ) ;
return ret ;
}
/*
* Files for the channel path entries .
*/
static ssize_t chp_status_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2007-10-22 12:52:40 +02:00
struct channel_path * chp = to_channelpath ( dev ) ;
2007-04-27 16:01:28 +02:00
if ( ! chp )
return 0 ;
return ( chp_get_status ( chp - > chpid ) ? sprintf ( buf , " online \n " ) :
sprintf ( buf , " offline \n " ) ) ;
}
static ssize_t chp_status_write ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
2007-10-22 12:52:40 +02:00
struct channel_path * cp = to_channelpath ( dev ) ;
2007-04-27 16:01:28 +02:00
char cmd [ 10 ] ;
int num_args ;
int error ;
num_args = sscanf ( buf , " %5s " , cmd ) ;
if ( ! num_args )
return count ;
if ( ! strnicmp ( cmd , " on " , 2 ) | | ! strcmp ( cmd , " 1 " ) )
error = s390_vary_chpid ( cp - > chpid , 1 ) ;
else if ( ! strnicmp ( cmd , " off " , 3 ) | | ! strcmp ( cmd , " 0 " ) )
error = s390_vary_chpid ( cp - > chpid , 0 ) ;
else
error = - EINVAL ;
return error < 0 ? error : count ;
}
static DEVICE_ATTR ( status , 0644 , chp_status_show , chp_status_write ) ;
2007-04-27 16:01:31 +02:00
static ssize_t chp_configure_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct channel_path * cp ;
int status ;
2007-10-22 12:52:40 +02:00
cp = to_channelpath ( dev ) ;
2007-04-27 16:01:31 +02:00
status = chp_info_get_status ( cp - > chpid ) ;
if ( status < 0 )
return status ;
return snprintf ( buf , PAGE_SIZE , " %d \n " , status ) ;
}
static int cfg_wait_idle ( void ) ;
static ssize_t chp_configure_write ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct channel_path * cp ;
int val ;
char delim ;
if ( sscanf ( buf , " %d %c " , & val , & delim ) ! = 1 )
return - EINVAL ;
if ( val ! = 0 & & val ! = 1 )
return - EINVAL ;
2007-10-22 12:52:40 +02:00
cp = to_channelpath ( dev ) ;
2007-04-27 16:01:31 +02:00
chp_cfg_schedule ( cp - > chpid , val ) ;
cfg_wait_idle ( ) ;
return count ;
}
static DEVICE_ATTR ( configure , 0644 , chp_configure_show , chp_configure_write ) ;
2007-04-27 16:01:28 +02:00
static ssize_t chp_type_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
2007-10-22 12:52:40 +02:00
struct channel_path * chp = to_channelpath ( dev ) ;
2007-04-27 16:01:28 +02:00
if ( ! chp )
return 0 ;
return sprintf ( buf , " %x \n " , chp - > desc . desc ) ;
}
static DEVICE_ATTR ( type , 0444 , chp_type_show , NULL ) ;
static ssize_t chp_cmg_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct channel_path * chp = to_channelpath ( dev ) ;
if ( ! chp )
return 0 ;
if ( chp - > cmg = = - 1 ) /* channel measurements not available */
return sprintf ( buf , " unknown \n " ) ;
return sprintf ( buf , " %x \n " , chp - > cmg ) ;
}
static DEVICE_ATTR ( cmg , 0444 , chp_cmg_show , NULL ) ;
static ssize_t chp_shared_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct channel_path * chp = to_channelpath ( dev ) ;
if ( ! chp )
return 0 ;
if ( chp - > shared = = - 1 ) /* channel measurements not available */
return sprintf ( buf , " unknown \n " ) ;
return sprintf ( buf , " %x \n " , chp - > shared ) ;
}
static DEVICE_ATTR ( shared , 0444 , chp_shared_show , NULL ) ;
2007-10-12 16:11:19 +02:00
static struct attribute * chp_attrs [ ] = {
2007-04-27 16:01:28 +02:00
& dev_attr_status . attr ,
2007-04-27 16:01:31 +02:00
& dev_attr_configure . attr ,
2007-04-27 16:01:28 +02:00
& dev_attr_type . attr ,
& dev_attr_cmg . attr ,
& dev_attr_shared . attr ,
NULL ,
} ;
static struct attribute_group chp_attr_group = {
. attrs = chp_attrs ,
} ;
static void chp_release ( struct device * dev )
{
struct channel_path * cp ;
2007-10-22 12:52:40 +02:00
cp = to_channelpath ( dev ) ;
2007-04-27 16:01:28 +02:00
kfree ( cp ) ;
}
/**
* chp_new - register a new channel - path
* @ chpid - channel - path ID
*
* Create and register data structure representing new channel - path . Return
* zero on success , non - zero otherwise .
*/
int chp_new ( struct chp_id chpid )
{
struct channel_path * chp ;
int ret ;
2007-04-27 16:01:31 +02:00
if ( chp_is_registered ( chpid ) )
return 0 ;
2007-04-27 16:01:28 +02:00
chp = kzalloc ( sizeof ( struct channel_path ) , GFP_KERNEL ) ;
if ( ! chp )
return - ENOMEM ;
/* fill in status, etc. */
chp - > chpid = chpid ;
chp - > state = 1 ;
2007-10-12 16:11:13 +02:00
chp - > dev . parent = & channel_subsystems [ chpid . cssid ] - > device ;
2007-04-27 16:01:28 +02:00
chp - > dev . release = chp_release ;
/* Obtain channel path description and fill it in. */
2008-07-14 09:59:05 +02:00
ret = chsc_determine_base_channel_path_desc ( chpid , & chp - > desc ) ;
2007-04-27 16:01:28 +02:00
if ( ret )
goto out_free ;
2007-04-27 16:01:29 +02:00
if ( ( chp - > desc . flags & 0x80 ) = = 0 ) {
ret = - ENODEV ;
goto out_free ;
}
2007-04-27 16:01:28 +02:00
/* Get channel-measurement characteristics. */
2008-07-14 09:58:57 +02:00
if ( css_chsc_characteristics . scmc & & css_chsc_characteristics . secm ) {
2007-04-27 16:01:28 +02:00
ret = chsc_get_channel_measurement_chars ( chp ) ;
if ( ret )
goto out_free ;
} else {
chp - > cmg = - 1 ;
}
2009-10-06 10:33:59 +02:00
dev_set_name ( & chp - > dev , " chp%x.%02x " , chpid . cssid , chpid . id ) ;
2007-04-27 16:01:28 +02:00
/* make it known to the system */
ret = device_register ( & chp - > dev ) ;
if ( ret ) {
2007-07-27 12:29:19 +02:00
CIO_MSG_EVENT ( 0 , " Could not register chp%x.%02x: %d \n " ,
chpid . cssid , chpid . id , ret ) ;
2009-09-11 10:28:38 +02:00
put_device ( & chp - > dev ) ;
goto out ;
2007-04-27 16:01:28 +02:00
}
ret = sysfs_create_group ( & chp - > dev . kobj , & chp_attr_group ) ;
if ( ret ) {
device_unregister ( & chp - > dev ) ;
2008-09-09 12:38:57 +02:00
goto out ;
2007-04-27 16:01:28 +02:00
}
2007-10-12 16:11:13 +02:00
mutex_lock ( & channel_subsystems [ chpid . cssid ] - > mutex ) ;
if ( channel_subsystems [ chpid . cssid ] - > cm_enabled ) {
2007-04-27 16:01:28 +02:00
ret = chp_add_cmg_attr ( chp ) ;
if ( ret ) {
sysfs_remove_group ( & chp - > dev . kobj , & chp_attr_group ) ;
device_unregister ( & chp - > dev ) ;
2007-10-12 16:11:13 +02:00
mutex_unlock ( & channel_subsystems [ chpid . cssid ] - > mutex ) ;
2008-09-09 12:38:57 +02:00
goto out ;
2007-04-27 16:01:28 +02:00
}
}
2007-10-12 16:11:13 +02:00
channel_subsystems [ chpid . cssid ] - > chps [ chpid . id ] = chp ;
mutex_unlock ( & channel_subsystems [ chpid . cssid ] - > mutex ) ;
2008-09-09 12:38:57 +02:00
goto out ;
2007-04-27 16:01:28 +02:00
out_free :
kfree ( chp ) ;
2008-09-09 12:38:57 +02:00
out :
2007-04-27 16:01:28 +02:00
return ret ;
}
/**
* chp_get_chp_desc - return newly allocated channel - path description
* @ chpid : channel - path ID
*
* On success return a newly allocated copy of the channel - path description
* data associated with the given channel - path ID . Return % NULL on error .
*/
void * chp_get_chp_desc ( struct chp_id chpid )
{
struct channel_path * chp ;
struct channel_path_desc * desc ;
chp = chpid_to_chp ( chpid ) ;
if ( ! chp )
return NULL ;
desc = kmalloc ( sizeof ( struct channel_path_desc ) , GFP_KERNEL ) ;
if ( ! desc )
return NULL ;
memcpy ( desc , & chp - > desc , sizeof ( struct channel_path_desc ) ) ;
return desc ;
}
/**
* chp_process_crw - process channel - path status change
2008-07-14 09:58:46 +02:00
* @ crw0 : channel report - word to handler
* @ crw1 : second channel - report word ( always NULL )
* @ overflow : crw overflow indication
2007-04-27 16:01:28 +02:00
*
* Handle channel - report - words indicating that the status of a channel - path
* has changed .
*/
2008-07-14 09:58:46 +02:00
static void chp_process_crw ( struct crw * crw0 , struct crw * crw1 ,
int overflow )
2007-04-27 16:01:28 +02:00
{
struct chp_id chpid ;
2008-07-14 09:58:46 +02:00
if ( overflow ) {
css_schedule_eval_all ( ) ;
return ;
}
CIO_CRW_EVENT ( 2 , " CRW reports slct=%d, oflw=%d, "
" chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X \n " ,
crw0 - > slct , crw0 - > oflw , crw0 - > chn , crw0 - > rsc , crw0 - > anc ,
crw0 - > erc , crw0 - > rsid ) ;
/*
* Check for solicited machine checks . These are
* created by reset channel path and need not be
* handled here .
*/
if ( crw0 - > slct ) {
CIO_CRW_EVENT ( 2 , " solicited machine check for "
" channel path %02X \n " , crw0 - > rsid ) ;
return ;
}
2007-04-27 16:01:28 +02:00
chp_id_init ( & chpid ) ;
2008-07-14 09:58:46 +02:00
chpid . id = crw0 - > rsid ;
switch ( crw0 - > erc ) {
case CRW_ERC_IPARM : /* Path has come. */
2007-04-27 16:01:28 +02:00
if ( ! chp_is_registered ( chpid ) )
chp_new ( chpid ) ;
2007-04-27 16:01:34 +02:00
chsc_chp_online ( chpid ) ;
2008-07-14 09:58:46 +02:00
break ;
case CRW_ERC_PERRI : /* Path has gone. */
case CRW_ERC_PERRN :
2007-04-27 16:01:28 +02:00
chsc_chp_offline ( chpid ) ;
2008-07-14 09:58:46 +02:00
break ;
default :
CIO_CRW_EVENT ( 2 , " Don't know how to handle erc=%x \n " ,
crw0 - > erc ) ;
}
2007-04-27 16:01:28 +02:00
}
2007-04-27 16:01:31 +02:00
2008-07-14 09:59:02 +02:00
int chp_ssd_get_mask ( struct chsc_ssd_info * ssd , struct chp_link * link )
2008-07-14 09:58:45 +02:00
{
int i ;
int mask ;
for ( i = 0 ; i < 8 ; i + + ) {
mask = 0x80 > > i ;
if ( ! ( ssd - > path_mask & mask ) )
continue ;
2008-07-14 09:59:02 +02:00
if ( ! chp_id_is_equal ( & ssd - > chpid [ i ] , & link - > chpid ) )
2008-07-14 09:58:45 +02:00
continue ;
if ( ( ssd - > fla_valid_mask & mask ) & &
2008-07-14 09:59:02 +02:00
( ( ssd - > fla [ i ] & link - > fla_mask ) ! = link - > fla ) )
2008-07-14 09:58:45 +02:00
continue ;
return mask ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( chp_ssd_get_mask ) ;
2007-04-27 16:01:31 +02:00
static inline int info_bit_num ( struct chp_id id )
{
return id . id + id . cssid * ( __MAX_CHPID + 1 ) ;
}
/* Force chp_info refresh on next call to info_validate(). */
static void info_expire ( void )
{
mutex_lock ( & info_lock ) ;
chp_info_expires = jiffies - 1 ;
mutex_unlock ( & info_lock ) ;
}
/* Ensure that chp_info is up-to-date. */
static int info_update ( void )
{
int rc ;
mutex_lock ( & info_lock ) ;
rc = 0 ;
if ( time_after ( jiffies , chp_info_expires ) ) {
/* Data is too old, update. */
rc = sclp_chp_read_info ( & chp_info ) ;
chp_info_expires = jiffies + CHP_INFO_UPDATE_INTERVAL ;
}
mutex_unlock ( & info_lock ) ;
return rc ;
}
/**
* chp_info_get_status - retrieve configure status of a channel - path
* @ chpid : channel - path ID
*
* On success , return 0 for standby , 1 for configured , 2 for reserved ,
* 3 for not recognized . Return negative error code on error .
*/
int chp_info_get_status ( struct chp_id chpid )
{
int rc ;
int bit ;
rc = info_update ( ) ;
if ( rc )
return rc ;
bit = info_bit_num ( chpid ) ;
mutex_lock ( & info_lock ) ;
if ( ! chp_test_bit ( chp_info . recognized , bit ) )
rc = CHP_STATUS_NOT_RECOGNIZED ;
else if ( chp_test_bit ( chp_info . configured , bit ) )
rc = CHP_STATUS_CONFIGURED ;
else if ( chp_test_bit ( chp_info . standby , bit ) )
rc = CHP_STATUS_STANDBY ;
else
rc = CHP_STATUS_RESERVED ;
mutex_unlock ( & info_lock ) ;
return rc ;
}
/* Return configure task for chpid. */
static enum cfg_task_t cfg_get_task ( struct chp_id chpid )
{
return chp_cfg_task [ chpid . cssid ] [ chpid . id ] ;
}
/* Set configure task for chpid. */
static void cfg_set_task ( struct chp_id chpid , enum cfg_task_t cfg )
{
chp_cfg_task [ chpid . cssid ] [ chpid . id ] = cfg ;
}
/* Perform one configure/deconfigure request. Reschedule work function until
* last request . */
static void cfg_func ( struct work_struct * work )
{
struct chp_id chpid ;
enum cfg_task_t t ;
2008-07-14 09:59:04 +02:00
int rc ;
2007-04-27 16:01:31 +02:00
mutex_lock ( & cfg_lock ) ;
t = cfg_none ;
chp_id_for_each ( & chpid ) {
t = cfg_get_task ( chpid ) ;
if ( t ! = cfg_none ) {
cfg_set_task ( chpid , cfg_none ) ;
break ;
}
}
mutex_unlock ( & cfg_lock ) ;
switch ( t ) {
case cfg_configure :
2008-07-14 09:59:04 +02:00
rc = sclp_chp_configure ( chpid ) ;
if ( rc )
CIO_MSG_EVENT ( 2 , " chp: sclp_chp_configure(%x.%02x)= "
" %d \n " , chpid . cssid , chpid . id , rc ) ;
else {
info_expire ( ) ;
chsc_chp_online ( chpid ) ;
}
2007-04-27 16:01:31 +02:00
break ;
case cfg_deconfigure :
2008-07-14 09:59:04 +02:00
rc = sclp_chp_deconfigure ( chpid ) ;
if ( rc )
CIO_MSG_EVENT ( 2 , " chp: sclp_chp_deconfigure(%x.%02x)= "
" %d \n " , chpid . cssid , chpid . id , rc ) ;
else {
info_expire ( ) ;
chsc_chp_offline ( chpid ) ;
}
2007-04-27 16:01:31 +02:00
break ;
case cfg_none :
/* Get updated information after last change. */
info_update ( ) ;
mutex_lock ( & cfg_lock ) ;
cfg_busy = 0 ;
mutex_unlock ( & cfg_lock ) ;
wake_up_interruptible ( & cfg_wait_queue ) ;
return ;
}
queue_work ( chp_wq , & cfg_work ) ;
}
/**
* chp_cfg_schedule - schedule chpid configuration request
* @ chpid - channel - path ID
* @ configure - Non - zero for configure , zero for deconfigure
*
* Schedule a channel - path configuration / deconfiguration request .
*/
void chp_cfg_schedule ( struct chp_id chpid , int configure )
{
CIO_MSG_EVENT ( 2 , " chp_cfg_sched%x.%02x=%d \n " , chpid . cssid , chpid . id ,
configure ) ;
mutex_lock ( & cfg_lock ) ;
cfg_set_task ( chpid , configure ? cfg_configure : cfg_deconfigure ) ;
cfg_busy = 1 ;
mutex_unlock ( & cfg_lock ) ;
queue_work ( chp_wq , & cfg_work ) ;
}
/**
* chp_cfg_cancel_deconfigure - cancel chpid deconfiguration request
* @ chpid - channel - path ID
*
* Cancel an active channel - path deconfiguration request if it has not yet
* been performed .
*/
void chp_cfg_cancel_deconfigure ( struct chp_id chpid )
{
CIO_MSG_EVENT ( 2 , " chp_cfg_cancel:%x.%02x \n " , chpid . cssid , chpid . id ) ;
mutex_lock ( & cfg_lock ) ;
if ( cfg_get_task ( chpid ) = = cfg_deconfigure )
cfg_set_task ( chpid , cfg_none ) ;
mutex_unlock ( & cfg_lock ) ;
}
static int cfg_wait_idle ( void )
{
if ( wait_event_interruptible ( cfg_wait_queue , ! cfg_busy ) )
return - ERESTARTSYS ;
return 0 ;
}
static int __init chp_init ( void )
{
struct chp_id chpid ;
2008-07-14 09:58:46 +02:00
int ret ;
2007-04-27 16:01:31 +02:00
2009-03-26 15:24:01 +01:00
ret = crw_register_handler ( CRW_RSC_CPATH , chp_process_crw ) ;
2008-07-14 09:58:46 +02:00
if ( ret )
return ret ;
2007-04-27 16:01:31 +02:00
chp_wq = create_singlethread_workqueue ( " cio_chp " ) ;
2008-07-14 09:58:46 +02:00
if ( ! chp_wq ) {
2009-03-26 15:24:01 +01:00
crw_unregister_handler ( CRW_RSC_CPATH ) ;
2007-04-27 16:01:31 +02:00
return - ENOMEM ;
2008-07-14 09:58:46 +02:00
}
2007-04-27 16:01:31 +02:00
INIT_WORK ( & cfg_work , cfg_func ) ;
init_waitqueue_head ( & cfg_wait_queue ) ;
if ( info_update ( ) )
return 0 ;
/* Register available channel-paths. */
chp_id_for_each ( & chpid ) {
if ( chp_info_get_status ( chpid ) ! = CHP_STATUS_NOT_RECOGNIZED )
chp_new ( chpid ) ;
}
return 0 ;
}
subsys_initcall ( chp_init ) ;