2007-02-09 00:37:42 +03:00
/*
* IUCV base infrastructure .
*
2009-06-16 12:30:41 +04:00
* Copyright IBM Corp . 2001 , 2009
*
2007-02-09 00:37:42 +03:00
* Author ( s ) :
* Original source :
* Alan Altmark ( Alan_Altmark @ us . ibm . com ) Sept . 2000
* Xenia Tkatschow ( xenia @ us . ibm . com )
* 2 Gb awareness and general cleanup :
* Fritz Elfert ( elfert @ de . ibm . com , felfert @ millenux . com )
* Rewritten for af_iucv :
* Martin Schwidefsky < schwidefsky @ de . ibm . com >
2009-06-16 12:30:42 +04:00
* PM functions :
* Ursula Braun ( ursula . braun @ de . ibm . com )
2007-02-09 00:37:42 +03:00
*
* Documentation used :
* The original source
* CP Programming Service , IBM document # SC24 - 5760
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 , or ( at your option )
* any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
2008-12-25 15:39:47 +03:00
# define KMSG_COMPONENT "iucv"
# define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
2011-01-05 14:47:28 +03:00
# include <linux/kernel_stat.h>
2007-02-09 00:37:42 +03:00
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/spinlock.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/list.h>
# include <linux/errno.h>
# include <linux/err.h>
# include <linux/device.h>
# include <linux/cpu.h>
2009-06-16 12:30:41 +04:00
# include <linux/reboot.h>
2007-02-09 00:37:42 +03:00
# include <net/iucv/iucv.h>
2011-07-27 03:09:06 +04:00
# include <linux/atomic.h>
2007-02-09 00:37:42 +03:00
# include <asm/ebcdic.h>
# include <asm/io.h>
2011-05-26 11:48:24 +04:00
# include <asm/irq.h>
2007-02-09 00:37:42 +03:00
# include <asm/smp.h>
/*
* FLAGS :
* All flags are defined in the field IPFLAGS1 of each function
* and can be found in CP Programming Services .
* IPSRCCLS - Indicates you have specified a source class .
* IPTRGCLS - Indicates you have specified a target class .
* IPFGPID - Indicates you have specified a pathid .
* IPFGMID - Indicates you have specified a message ID .
* IPNORPY - Indicates a one - way message . No reply expected .
* IPALL - Indicates that all paths are affected .
*/
# define IUCV_IPSRCCLS 0x01
# define IUCV_IPTRGCLS 0x01
# define IUCV_IPFGPID 0x02
# define IUCV_IPFGMID 0x04
# define IUCV_IPNORPY 0x10
# define IUCV_IPALL 0x80
2007-05-04 23:23:27 +04:00
static int iucv_bus_match ( struct device * dev , struct device_driver * drv )
2007-02-09 00:37:42 +03:00
{
return 0 ;
}
2009-09-16 08:37:22 +04:00
enum iucv_pm_states {
IUCV_PM_INITIAL = 0 ,
IUCV_PM_FREEZING = 1 ,
IUCV_PM_THAWING = 2 ,
IUCV_PM_RESTORING = 3 ,
} ;
static enum iucv_pm_states iucv_pm_state ;
2009-06-16 12:30:42 +04:00
static int iucv_pm_prepare ( struct device * ) ;
static void iucv_pm_complete ( struct device * ) ;
static int iucv_pm_freeze ( struct device * ) ;
static int iucv_pm_thaw ( struct device * ) ;
static int iucv_pm_restore ( struct device * ) ;
2009-12-15 05:00:08 +03:00
static const struct dev_pm_ops iucv_pm_ops = {
2009-06-16 12:30:42 +04:00
. prepare = iucv_pm_prepare ,
. complete = iucv_pm_complete ,
. freeze = iucv_pm_freeze ,
. thaw = iucv_pm_thaw ,
. restore = iucv_pm_restore ,
} ;
2007-02-09 00:37:42 +03:00
struct bus_type iucv_bus = {
. name = " iucv " ,
. match = iucv_bus_match ,
2009-06-16 12:30:42 +04:00
. pm = & iucv_pm_ops ,
2007-02-09 00:37:42 +03:00
} ;
2007-05-04 23:23:27 +04:00
EXPORT_SYMBOL ( iucv_bus ) ;
2007-02-09 00:37:42 +03:00
struct device * iucv_root ;
2007-05-04 23:23:27 +04:00
EXPORT_SYMBOL ( iucv_root ) ;
2007-02-09 00:37:42 +03:00
static int iucv_available ;
/* General IUCV interrupt structure */
struct iucv_irq_data {
u16 ippathid ;
u8 ipflags1 ;
u8 iptype ;
u32 res2 [ 8 ] ;
} ;
2007-04-29 10:03:59 +04:00
struct iucv_irq_list {
2007-02-09 00:37:42 +03:00
struct list_head list ;
struct iucv_irq_data data ;
} ;
2007-11-20 13:13:38 +03:00
static struct iucv_irq_data * iucv_irq_data [ NR_CPUS ] ;
2011-05-12 22:45:09 +04:00
static cpumask_t iucv_buffer_cpumask = { CPU_BITS_NONE } ;
static cpumask_t iucv_irq_cpumask = { CPU_BITS_NONE } ;
2007-02-09 00:37:42 +03:00
2007-04-29 10:03:59 +04:00
/*
* Queue of interrupt buffers lock for delivery via the tasklet
* ( fast but can ' t call smp_call_function ) .
*/
static LIST_HEAD ( iucv_task_queue ) ;
/*
* The tasklet for fast delivery of iucv interrupts .
*/
static void iucv_tasklet_fn ( unsigned long ) ;
static DECLARE_TASKLET ( iucv_tasklet , iucv_tasklet_fn , 0 ) ;
/*
* Queue of interrupt buffers for delivery via a work queue
* ( slower but can call smp_call_function ) .
*/
static LIST_HEAD ( iucv_work_queue ) ;
/*
* The work element to deliver path pending interrupts .
*/
static void iucv_work_fn ( struct work_struct * work ) ;
static DECLARE_WORK ( iucv_work , iucv_work_fn ) ;
/*
* Spinlock protecting task and work queue .
*/
static DEFINE_SPINLOCK ( iucv_queue_lock ) ;
2007-02-09 00:37:42 +03:00
enum iucv_command_codes {
IUCV_QUERY = 0 ,
IUCV_RETRIEVE_BUFFER = 2 ,
IUCV_SEND = 4 ,
IUCV_RECEIVE = 5 ,
IUCV_REPLY = 6 ,
IUCV_REJECT = 8 ,
IUCV_PURGE = 9 ,
IUCV_ACCEPT = 10 ,
IUCV_CONNECT = 11 ,
IUCV_DECLARE_BUFFER = 12 ,
IUCV_QUIESCE = 13 ,
IUCV_RESUME = 14 ,
IUCV_SEVER = 15 ,
IUCV_SETMASK = 16 ,
2009-06-16 12:30:42 +04:00
IUCV_SETCONTROLMASK = 17 ,
2007-02-09 00:37:42 +03:00
} ;
/*
* Error messages that are used with the iucv_sever function . They get
* converted to EBCDIC .
*/
static char iucv_error_no_listener [ 16 ] = " NO LISTENER " ;
static char iucv_error_no_memory [ 16 ] = " NO MEMORY " ;
static char iucv_error_pathid [ 16 ] = " INVALID PATHID " ;
/*
* iucv_handler_list : List of registered handlers .
*/
static LIST_HEAD ( iucv_handler_list ) ;
/*
* iucv_path_table : an array of iucv_path structures .
*/
static struct iucv_path * * iucv_path_table ;
static unsigned long iucv_max_pathid ;
/*
* iucv_lock : spinlock protecting iucv_handler_list and iucv_pathid_table
*/
static DEFINE_SPINLOCK ( iucv_table_lock ) ;
/*
2007-04-29 10:03:59 +04:00
* iucv_active_cpu : contains the number of the cpu executing the tasklet
* or the work handler . Needed for iucv_path_sever called from tasklet .
2007-02-09 00:37:42 +03:00
*/
2007-04-29 10:03:59 +04:00
static int iucv_active_cpu = - 1 ;
2007-02-09 00:37:42 +03:00
/*
* Mutex and wait queue for iucv_register / iucv_unregister .
*/
static DEFINE_MUTEX ( iucv_register_mutex ) ;
/*
* Counter for number of non - smp capable handlers .
*/
static int iucv_nonsmp_handler ;
/*
* IUCV control data structure . Used by iucv_path_accept , iucv_path_connect ,
* iucv_path_quiesce and iucv_path_sever .
*/
struct iucv_cmd_control {
u16 ippathid ;
u8 ipflags1 ;
u8 iprcode ;
u16 ipmsglim ;
u16 res1 ;
u8 ipvmid [ 8 ] ;
u8 ipuser [ 16 ] ;
u8 iptarget [ 8 ] ;
} __attribute__ ( ( packed , aligned ( 8 ) ) ) ;
/*
* Data in parameter list iucv structure . Used by iucv_message_send ,
* iucv_message_send2way and iucv_message_reply .
*/
struct iucv_cmd_dpl {
u16 ippathid ;
u8 ipflags1 ;
u8 iprcode ;
u32 ipmsgid ;
u32 iptrgcls ;
u8 iprmmsg [ 8 ] ;
u32 ipsrccls ;
u32 ipmsgtag ;
u32 ipbfadr2 ;
u32 ipbfln2f ;
u32 res ;
} __attribute__ ( ( packed , aligned ( 8 ) ) ) ;
/*
* Data in buffer iucv structure . Used by iucv_message_receive ,
* iucv_message_reject , iucv_message_send , iucv_message_send2way
* and iucv_declare_cpu .
*/
struct iucv_cmd_db {
u16 ippathid ;
u8 ipflags1 ;
u8 iprcode ;
u32 ipmsgid ;
u32 iptrgcls ;
u32 ipbfadr1 ;
u32 ipbfln1f ;
u32 ipsrccls ;
u32 ipmsgtag ;
u32 ipbfadr2 ;
u32 ipbfln2f ;
u32 res ;
} __attribute__ ( ( packed , aligned ( 8 ) ) ) ;
/*
* Purge message iucv structure . Used by iucv_message_purge .
*/
struct iucv_cmd_purge {
u16 ippathid ;
u8 ipflags1 ;
u8 iprcode ;
u32 ipmsgid ;
u8 ipaudit [ 3 ] ;
u8 res1 [ 5 ] ;
u32 res2 ;
u32 ipsrccls ;
u32 ipmsgtag ;
u32 res3 [ 3 ] ;
} __attribute__ ( ( packed , aligned ( 8 ) ) ) ;
/*
* Set mask iucv structure . Used by iucv_enable_cpu .
*/
struct iucv_cmd_set_mask {
u8 ipmask ;
u8 res1 [ 2 ] ;
u8 iprcode ;
u32 res2 [ 9 ] ;
} __attribute__ ( ( packed , aligned ( 8 ) ) ) ;
union iucv_param {
struct iucv_cmd_control ctrl ;
struct iucv_cmd_dpl dpl ;
struct iucv_cmd_db db ;
struct iucv_cmd_purge purge ;
struct iucv_cmd_set_mask set_mask ;
} ;
/*
* Anchor for per - cpu IUCV command parameter block .
*/
2007-11-20 13:13:38 +03:00
static union iucv_param * iucv_param [ NR_CPUS ] ;
2009-04-22 03:26:20 +04:00
static union iucv_param * iucv_param_irq [ NR_CPUS ] ;
2007-02-09 00:37:42 +03:00
/**
* iucv_call_b2f0
* @ code : identifier of IUCV call to CP .
* @ parm : pointer to a struct iucv_parm block
*
* Calls CP to execute IUCV commands .
*
* Returns the result of the CP IUCV call .
*/
static inline int iucv_call_b2f0 ( int command , union iucv_param * parm )
{
register unsigned long reg0 asm ( " 0 " ) ;
register unsigned long reg1 asm ( " 1 " ) ;
int ccode ;
reg0 = command ;
reg1 = virt_to_phys ( parm ) ;
asm volatile (
" .long 0xb2f01000 \n "
" ipm %0 \n "
" srl %0,28 \n "
: " =d " ( ccode ) , " =m " ( * parm ) , " +d " ( reg0 ) , " +a " ( reg1 )
: " m " ( * parm ) : " cc " ) ;
return ( ccode = = 1 ) ? parm - > ctrl . iprcode : ccode ;
}
/**
* iucv_query_maxconn
*
* Determines the maximum number of connections that may be established .
*
* Returns the maximum number of connections or - EPERM is IUCV is not
* available .
*/
static int iucv_query_maxconn ( void )
{
register unsigned long reg0 asm ( " 0 " ) ;
register unsigned long reg1 asm ( " 1 " ) ;
void * param ;
int ccode ;
param = kzalloc ( sizeof ( union iucv_param ) , GFP_KERNEL | GFP_DMA ) ;
if ( ! param )
return - ENOMEM ;
reg0 = IUCV_QUERY ;
reg1 = ( unsigned long ) param ;
asm volatile (
" .long 0xb2f01000 \n "
" ipm %0 \n "
" srl %0,28 \n "
: " =d " ( ccode ) , " +d " ( reg0 ) , " +d " ( reg1 ) : : " cc " ) ;
if ( ccode = = 0 )
iucv: use correct output register in iucv_query_maxconn()
The iucv_query_maxconn() function uses the wrong output register and
stores the size of the interrupt buffer instead of the maximum number
of connections.
According to the QUERY IUCV function, general register 1 contains the
maximum number of connections.
If the maximum number of connections is not set properly, the following
warning is displayed:
Badness at /usr/src/kernel-source/2.6.30-39.x.20090806/net/iucv/iucv.c:1808
Modules linked in: netiucv fsm af_iucv sunrpc qeth_l3 dm_multipath dm_mod vmur qeth ccwgroup
CPU: 0 Tainted: G W 2.6.30 #4
Process seq (pid: 16925, task: 0000000030e24a40, ksp: 000000003033bd98)
Krnl PSW : 0404200180000000 000000000053b270 (iucv_external_interrupt+0x64/0x224)
R:0 T:1 IO:0 EX:0 Key:0 M:1 W:0 P:0 AS:0 CC:2 PM:0 EA:3
Krnl GPRS: 00000000011279c2 00000000014bdb70 0029000000000000 0000000000000029
000000000053b236 000000000001dba4 0000000000000000 0000000000859210
0000000000a67f68 00000000008a6100 000000003f83fb90 0000000000004000
000000003f8c7bc8 00000000005a2250 000000000053b236 000000003fc2fe08
Krnl Code: 000000000053b262: e33010000021 clg %r3,0(%r1)
000000000053b268: a7440010 brc 4,53b288
000000000053b26c: a7f40001 brc 15,53b26e
>000000000053b270: c03000184134 larl %r3,8434d8
000000000053b276: eb220030000c srlg %r2,%r2,48
000000000053b27c: eb6ff0a00004 lmg %r6,%r15,160(%r15)
000000000053b282: c0f4fffff6a7 brcl 15,539fd0
000000000053b288: 4310a003 ic %r1,3(%r10)
Call Trace:
([<000000000053b236>] iucv_external_interrupt+0x2a/0x224)
[<000000000010e09e>] do_extint+0x132/0x190
[<00000000001184b6>] ext_no_vtime+0x1e/0x22
[<0000000000549f7a>] _spin_unlock_irqrestore+0x96/0xa4
([<0000000000549f70>] _spin_unlock_irqrestore+0x8c/0xa4)
[<00000000002101d6>] pipe_write+0x3da/0x5bc
[<0000000000205d14>] do_sync_write+0xe4/0x13c
[<0000000000206a7e>] vfs_write+0xae/0x15c
[<0000000000206c24>] SyS_write+0x54/0xac
[<0000000000117c8e>] sysc_noemu+0x10/0x16
[<00000042ff8defcc>] 0x42ff8defcc
Signed-off-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
Signed-off-by: Ursula Braun <ursula.braun@de.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2009-09-16 08:37:24 +04:00
iucv_max_pathid = reg1 ;
2007-02-09 00:37:42 +03:00
kfree ( param ) ;
return ccode ? - EPERM : 0 ;
}
/**
* iucv_allow_cpu
* @ data : unused
*
* Allow iucv interrupts on this cpu .
*/
static void iucv_allow_cpu ( void * data )
{
int cpu = smp_processor_id ( ) ;
union iucv_param * parm ;
/*
* Enable all iucv interrupts .
* ipmask contains bits for the different interrupts
* 0x80 - Flag to allow nonpriority message pending interrupts
* 0x40 - Flag to allow priority message pending interrupts
* 0x20 - Flag to allow nonpriority message completion interrupts
* 0x10 - Flag to allow priority message completion interrupts
* 0x08 - Flag to allow IUCV control interrupts
*/
2009-04-22 03:26:20 +04:00
parm = iucv_param_irq [ cpu ] ;
2007-02-09 00:37:42 +03:00
memset ( parm , 0 , sizeof ( union iucv_param ) ) ;
parm - > set_mask . ipmask = 0xf8 ;
iucv_call_b2f0 ( IUCV_SETMASK , parm ) ;
2009-06-16 12:30:42 +04:00
/*
* Enable all iucv control interrupts .
* ipmask contains bits for the different interrupts
* 0x80 - Flag to allow pending connections interrupts
* 0x40 - Flag to allow connection complete interrupts
* 0x20 - Flag to allow connection severed interrupts
* 0x10 - Flag to allow connection quiesced interrupts
* 0x08 - Flag to allow connection resumed interrupts
*/
memset ( parm , 0 , sizeof ( union iucv_param ) ) ;
parm - > set_mask . ipmask = 0xf8 ;
iucv_call_b2f0 ( IUCV_SETCONTROLMASK , parm ) ;
2007-02-09 00:37:42 +03:00
/* Set indication that iucv interrupts are allowed for this cpu. */
2011-05-12 22:45:09 +04:00
cpumask_set_cpu ( cpu , & iucv_irq_cpumask ) ;
2007-02-09 00:37:42 +03:00
}
/**
* iucv_block_cpu
* @ data : unused
*
* Block iucv interrupts on this cpu .
*/
static void iucv_block_cpu ( void * data )
{
int cpu = smp_processor_id ( ) ;
union iucv_param * parm ;
/* Disable all iucv interrupts. */
2009-04-22 03:26:20 +04:00
parm = iucv_param_irq [ cpu ] ;
2007-02-09 00:37:42 +03:00
memset ( parm , 0 , sizeof ( union iucv_param ) ) ;
iucv_call_b2f0 ( IUCV_SETMASK , parm ) ;
/* Clear indication that iucv interrupts are allowed for this cpu. */
2011-05-12 22:45:09 +04:00
cpumask_clear_cpu ( cpu , & iucv_irq_cpumask ) ;
2007-02-09 00:37:42 +03:00
}
2009-06-16 12:30:42 +04:00
/**
* iucv_block_cpu_almost
* @ data : unused
*
* Allow connection - severed interrupts only on this cpu .
*/
static void iucv_block_cpu_almost ( void * data )
{
int cpu = smp_processor_id ( ) ;
union iucv_param * parm ;
/* Allow iucv control interrupts only */
parm = iucv_param_irq [ cpu ] ;
memset ( parm , 0 , sizeof ( union iucv_param ) ) ;
parm - > set_mask . ipmask = 0x08 ;
iucv_call_b2f0 ( IUCV_SETMASK , parm ) ;
/* Allow iucv-severed interrupt only */
memset ( parm , 0 , sizeof ( union iucv_param ) ) ;
parm - > set_mask . ipmask = 0x20 ;
iucv_call_b2f0 ( IUCV_SETCONTROLMASK , parm ) ;
/* Clear indication that iucv interrupts are allowed for this cpu. */
2011-05-12 22:45:09 +04:00
cpumask_clear_cpu ( cpu , & iucv_irq_cpumask ) ;
2009-06-16 12:30:42 +04:00
}
2007-02-09 00:37:42 +03:00
/**
* iucv_declare_cpu
* @ data : unused
*
2007-10-20 01:10:43 +04:00
* Declare a interrupt buffer on this cpu .
2007-02-09 00:37:42 +03:00
*/
static void iucv_declare_cpu ( void * data )
{
int cpu = smp_processor_id ( ) ;
union iucv_param * parm ;
int rc ;
2011-05-12 22:45:09 +04:00
if ( cpumask_test_cpu ( cpu , & iucv_buffer_cpumask ) )
2007-02-09 00:37:42 +03:00
return ;
/* Declare interrupt buffer. */
2009-04-22 03:26:20 +04:00
parm = iucv_param_irq [ cpu ] ;
2007-02-09 00:37:42 +03:00
memset ( parm , 0 , sizeof ( union iucv_param ) ) ;
2007-11-20 13:13:38 +03:00
parm - > db . ipbfadr1 = virt_to_phys ( iucv_irq_data [ cpu ] ) ;
2007-02-09 00:37:42 +03:00
rc = iucv_call_b2f0 ( IUCV_DECLARE_BUFFER , parm ) ;
if ( rc ) {
char * err = " Unknown " ;
2007-05-04 23:23:27 +04:00
switch ( rc ) {
2007-02-09 00:37:42 +03:00
case 0x03 :
err = " Directory error " ;
break ;
case 0x0a :
err = " Invalid length " ;
break ;
case 0x13 :
err = " Buffer already exists " ;
break ;
case 0x3e :
err = " Buffer overlap " ;
break ;
case 0x5c :
err = " Paging or storage error " ;
break ;
}
2008-12-25 15:39:47 +03:00
pr_warning ( " Defining an interrupt buffer on CPU %i "
" failed with 0x%02x (%s) \n " , cpu , rc , err ) ;
2007-02-09 00:37:42 +03:00
return ;
}
/* Set indication that an iucv buffer exists for this cpu. */
2011-05-12 22:45:09 +04:00
cpumask_set_cpu ( cpu , & iucv_buffer_cpumask ) ;
2007-02-09 00:37:42 +03:00
2011-05-12 22:45:09 +04:00
if ( iucv_nonsmp_handler = = 0 | | cpumask_empty ( & iucv_irq_cpumask ) )
2007-02-09 00:37:42 +03:00
/* Enable iucv interrupts on this cpu. */
iucv_allow_cpu ( NULL ) ;
else
/* Disable iucv interrupts on this cpu. */
iucv_block_cpu ( NULL ) ;
}
/**
* iucv_retrieve_cpu
* @ data : unused
*
* Retrieve interrupt buffer on this cpu .
*/
static void iucv_retrieve_cpu ( void * data )
{
int cpu = smp_processor_id ( ) ;
union iucv_param * parm ;
2011-05-12 22:45:09 +04:00
if ( ! cpumask_test_cpu ( cpu , & iucv_buffer_cpumask ) )
2007-02-09 00:37:42 +03:00
return ;
/* Block iucv interrupts. */
iucv_block_cpu ( NULL ) ;
/* Retrieve interrupt buffer. */
2009-04-22 03:26:20 +04:00
parm = iucv_param_irq [ cpu ] ;
2007-02-09 00:37:42 +03:00
iucv_call_b2f0 ( IUCV_RETRIEVE_BUFFER , parm ) ;
/* Clear indication that an iucv buffer exists for this cpu. */
2011-05-12 22:45:09 +04:00
cpumask_clear_cpu ( cpu , & iucv_buffer_cpumask ) ;
2007-02-09 00:37:42 +03:00
}
/**
* iucv_setmask_smp
*
* Allow iucv interrupts on all cpus .
*/
static void iucv_setmask_mp ( void )
{
int cpu ;
2008-06-10 02:50:30 +04:00
get_online_cpus ( ) ;
2007-02-09 00:37:42 +03:00
for_each_online_cpu ( cpu )
/* Enable all cpus with a declared buffer. */
2011-05-12 22:45:09 +04:00
if ( cpumask_test_cpu ( cpu , & iucv_buffer_cpumask ) & &
! cpumask_test_cpu ( cpu , & iucv_irq_cpumask ) )
2007-07-27 14:29:08 +04:00
smp_call_function_single ( cpu , iucv_allow_cpu ,
2008-06-06 13:18:06 +04:00
NULL , 1 ) ;
2008-06-10 02:50:30 +04:00
put_online_cpus ( ) ;
2007-02-09 00:37:42 +03:00
}
/**
* iucv_setmask_up
*
2007-04-29 10:03:59 +04:00
* Allow iucv interrupts on a single cpu .
2007-02-09 00:37:42 +03:00
*/
static void iucv_setmask_up ( void )
{
cpumask_t cpumask ;
int cpu ;
/* Disable all cpu but the first in cpu_irq_cpumask. */
2011-05-12 22:45:09 +04:00
cpumask_copy ( & cpumask , & iucv_irq_cpumask ) ;
cpumask_clear_cpu ( cpumask_first ( & iucv_irq_cpumask ) , & cpumask ) ;
for_each_cpu ( cpu , & cpumask )
2008-06-06 13:18:06 +04:00
smp_call_function_single ( cpu , iucv_block_cpu , NULL , 1 ) ;
2007-02-09 00:37:42 +03:00
}
/**
* iucv_enable
*
* This function makes iucv ready for use . It allocates the pathid
* table , declares an iucv interrupt buffer and enables the iucv
* interrupts . Called when the first user has registered an iucv
* handler .
*/
static int iucv_enable ( void )
{
size_t alloc_size ;
int cpu , rc ;
2009-01-06 05:09:02 +03:00
get_online_cpus ( ) ;
2007-02-09 00:37:42 +03:00
rc = - ENOMEM ;
alloc_size = iucv_max_pathid * sizeof ( struct iucv_path ) ;
iucv_path_table = kzalloc ( alloc_size , GFP_KERNEL ) ;
if ( ! iucv_path_table )
goto out ;
/* Declare per cpu buffers. */
rc = - EIO ;
for_each_online_cpu ( cpu )
2008-06-06 13:18:06 +04:00
smp_call_function_single ( cpu , iucv_declare_cpu , NULL , 1 ) ;
2011-05-12 22:45:09 +04:00
if ( cpumask_empty ( & iucv_buffer_cpumask ) )
2007-02-09 00:37:42 +03:00
/* No cpu could declare an iucv buffer. */
2009-01-06 05:09:02 +03:00
goto out ;
2008-06-10 02:50:30 +04:00
put_online_cpus ( ) ;
2007-02-09 00:37:42 +03:00
return 0 ;
out :
2009-01-06 05:09:02 +03:00
kfree ( iucv_path_table ) ;
iucv_path_table = NULL ;
put_online_cpus ( ) ;
2007-02-09 00:37:42 +03:00
return rc ;
}
/**
* iucv_disable
*
* This function shuts down iucv . It disables iucv interrupts , retrieves
* the iucv interrupt buffer and frees the pathid table . Called after the
* last user unregister its iucv handler .
*/
static void iucv_disable ( void )
{
2008-09-30 14:03:35 +04:00
get_online_cpus ( ) ;
2008-05-09 11:39:44 +04:00
on_each_cpu ( iucv_retrieve_cpu , NULL , 1 ) ;
2007-02-09 00:37:42 +03:00
kfree ( iucv_path_table ) ;
2009-01-06 05:09:02 +03:00
iucv_path_table = NULL ;
put_online_cpus ( ) ;
2007-02-09 00:37:42 +03:00
}
static int __cpuinit iucv_cpu_notify ( struct notifier_block * self ,
unsigned long action , void * hcpu )
{
cpumask_t cpumask ;
long cpu = ( long ) hcpu ;
switch ( action ) {
case CPU_UP_PREPARE :
2007-05-09 13:35:10 +04:00
case CPU_UP_PREPARE_FROZEN :
2007-11-20 13:13:38 +03:00
iucv_irq_data [ cpu ] = kmalloc_node ( sizeof ( struct iucv_irq_data ) ,
GFP_KERNEL | GFP_DMA , cpu_to_node ( cpu ) ) ;
if ( ! iucv_irq_data [ cpu ] )
2010-05-27 01:43:33 +04:00
return notifier_from_errno ( - ENOMEM ) ;
2007-11-20 13:13:38 +03:00
iucv_param [ cpu ] = kmalloc_node ( sizeof ( union iucv_param ) ,
GFP_KERNEL | GFP_DMA , cpu_to_node ( cpu ) ) ;
2008-07-15 13:09:53 +04:00
if ( ! iucv_param [ cpu ] ) {
kfree ( iucv_irq_data [ cpu ] ) ;
iucv_irq_data [ cpu ] = NULL ;
2010-05-27 01:43:33 +04:00
return notifier_from_errno ( - ENOMEM ) ;
2008-07-15 13:09:53 +04:00
}
2009-04-22 03:26:20 +04:00
iucv_param_irq [ cpu ] = kmalloc_node ( sizeof ( union iucv_param ) ,
GFP_KERNEL | GFP_DMA , cpu_to_node ( cpu ) ) ;
if ( ! iucv_param_irq [ cpu ] ) {
kfree ( iucv_param [ cpu ] ) ;
iucv_param [ cpu ] = NULL ;
kfree ( iucv_irq_data [ cpu ] ) ;
iucv_irq_data [ cpu ] = NULL ;
2010-05-27 01:43:33 +04:00
return notifier_from_errno ( - ENOMEM ) ;
2009-04-22 03:26:20 +04:00
}
2007-02-09 00:37:42 +03:00
break ;
case CPU_UP_CANCELED :
2007-05-09 13:35:10 +04:00
case CPU_UP_CANCELED_FROZEN :
2007-02-09 00:37:42 +03:00
case CPU_DEAD :
2007-05-09 13:35:10 +04:00
case CPU_DEAD_FROZEN :
2009-04-22 03:26:20 +04:00
kfree ( iucv_param_irq [ cpu ] ) ;
iucv_param_irq [ cpu ] = NULL ;
2007-11-20 13:13:38 +03:00
kfree ( iucv_param [ cpu ] ) ;
iucv_param [ cpu ] = NULL ;
kfree ( iucv_irq_data [ cpu ] ) ;
iucv_irq_data [ cpu ] = NULL ;
2007-02-09 00:37:42 +03:00
break ;
case CPU_ONLINE :
2007-05-09 13:35:10 +04:00
case CPU_ONLINE_FROZEN :
2007-02-09 00:37:42 +03:00
case CPU_DOWN_FAILED :
2007-05-09 13:35:10 +04:00
case CPU_DOWN_FAILED_FROZEN :
2009-01-06 05:09:02 +03:00
if ( ! iucv_path_table )
break ;
2008-06-06 13:18:06 +04:00
smp_call_function_single ( cpu , iucv_declare_cpu , NULL , 1 ) ;
2007-02-09 00:37:42 +03:00
break ;
case CPU_DOWN_PREPARE :
2007-05-09 13:35:10 +04:00
case CPU_DOWN_PREPARE_FROZEN :
2009-01-06 05:09:02 +03:00
if ( ! iucv_path_table )
break ;
2011-05-12 22:45:09 +04:00
cpumask_copy ( & cpumask , & iucv_buffer_cpumask ) ;
cpumask_clear_cpu ( cpu , & cpumask ) ;
if ( cpumask_empty ( & cpumask ) )
2007-02-09 00:37:42 +03:00
/* Can't offline last IUCV enabled cpu. */
2010-05-27 01:43:33 +04:00
return notifier_from_errno ( - EINVAL ) ;
2008-06-06 13:18:06 +04:00
smp_call_function_single ( cpu , iucv_retrieve_cpu , NULL , 1 ) ;
2011-05-12 22:45:09 +04:00
if ( cpumask_empty ( & iucv_irq_cpumask ) )
smp_call_function_single (
cpumask_first ( & iucv_buffer_cpumask ) ,
iucv_allow_cpu , NULL , 1 ) ;
2007-02-09 00:37:42 +03:00
break ;
}
return NOTIFY_OK ;
}
2008-06-10 02:49:57 +04:00
static struct notifier_block __refdata iucv_cpu_notifier = {
2007-02-09 00:37:42 +03:00
. notifier_call = iucv_cpu_notify ,
} ;
/**
* iucv_sever_pathid
* @ pathid : path identification number .
* @ userdata : 16 - bytes of user data .
*
* Sever an iucv path to free up the pathid . Used internally .
*/
static int iucv_sever_pathid ( u16 pathid , u8 userdata [ 16 ] )
{
union iucv_param * parm ;
2009-04-22 03:26:20 +04:00
parm = iucv_param_irq [ smp_processor_id ( ) ] ;
2007-02-09 00:37:42 +03:00
memset ( parm , 0 , sizeof ( union iucv_param ) ) ;
if ( userdata )
memcpy ( parm - > ctrl . ipuser , userdata , sizeof ( parm - > ctrl . ipuser ) ) ;
parm - > ctrl . ippathid = pathid ;
return iucv_call_b2f0 ( IUCV_SEVER , parm ) ;
}
/**
2007-04-29 10:03:59 +04:00
* __iucv_cleanup_queue
2007-02-09 00:37:42 +03:00
* @ dummy : unused dummy argument
*
* Nop function called via smp_call_function to force work items from
* pending external iucv interrupts to the work queue .
*/
2007-04-29 10:03:59 +04:00
static void __iucv_cleanup_queue ( void * dummy )
2007-02-09 00:37:42 +03:00
{
}
/**
2007-04-29 10:03:59 +04:00
* iucv_cleanup_queue
2007-02-09 00:37:42 +03:00
*
* Function called after a path has been severed to find all remaining
* work items for the now stale pathid . The caller needs to hold the
* iucv_table_lock .
*/
2007-04-29 10:03:59 +04:00
static void iucv_cleanup_queue ( void )
2007-02-09 00:37:42 +03:00
{
2007-04-29 10:03:59 +04:00
struct iucv_irq_list * p , * n ;
2007-02-09 00:37:42 +03:00
/*
2011-03-31 05:57:33 +04:00
* When a path is severed , the pathid can be reused immediately
2007-04-29 10:03:59 +04:00
* on a iucv connect or a connection pending interrupt . Remove
* all entries from the task queue that refer to a stale pathid
* ( iucv_path_table [ ix ] = = NULL ) . Only then do the iucv connect
* or deliver the connection pending interrupt . To get all the
* pending interrupts force them to the work queue by calling
* an empty function on all cpus .
2007-02-09 00:37:42 +03:00
*/
2008-06-06 13:18:06 +04:00
smp_call_function ( __iucv_cleanup_queue , NULL , 1 ) ;
2007-04-29 10:03:59 +04:00
spin_lock_irq ( & iucv_queue_lock ) ;
list_for_each_entry_safe ( p , n , & iucv_task_queue , list ) {
/* Remove stale work items from the task queue. */
if ( iucv_path_table [ p - > data . ippathid ] = = NULL ) {
2007-02-09 00:37:42 +03:00
list_del ( & p - > list ) ;
kfree ( p ) ;
}
}
2007-04-29 10:03:59 +04:00
spin_unlock_irq ( & iucv_queue_lock ) ;
2007-02-09 00:37:42 +03:00
}
/**
* iucv_register :
* @ handler : address of iucv handler structure
* @ smp : ! = 0 indicates that the handler can deal with out of order messages
*
* Registers a driver with IUCV .
*
* Returns 0 on success , - ENOMEM if the memory allocation for the pathid
* table failed , or - EIO if IUCV_DECLARE_BUFFER failed on all cpus .
*/
int iucv_register ( struct iucv_handler * handler , int smp )
{
int rc ;
if ( ! iucv_available )
return - ENOSYS ;
mutex_lock ( & iucv_register_mutex ) ;
if ( ! smp )
iucv_nonsmp_handler + + ;
if ( list_empty ( & iucv_handler_list ) ) {
rc = iucv_enable ( ) ;
if ( rc )
goto out_mutex ;
} else if ( ! smp & & iucv_nonsmp_handler = = 1 )
iucv_setmask_up ( ) ;
INIT_LIST_HEAD ( & handler - > paths ) ;
2008-02-08 05:06:52 +03:00
spin_lock_bh ( & iucv_table_lock ) ;
2007-02-09 00:37:42 +03:00
list_add_tail ( & handler - > list , & iucv_handler_list ) ;
2008-02-08 05:06:52 +03:00
spin_unlock_bh ( & iucv_table_lock ) ;
2007-02-09 00:37:42 +03:00
rc = 0 ;
out_mutex :
mutex_unlock ( & iucv_register_mutex ) ;
return rc ;
}
2007-05-04 23:23:27 +04:00
EXPORT_SYMBOL ( iucv_register ) ;
2007-02-09 00:37:42 +03:00
/**
* iucv_unregister
* @ handler : address of iucv handler structure
* @ smp : ! = 0 indicates that the handler can deal with out of order messages
*
* Unregister driver from IUCV .
*/
void iucv_unregister ( struct iucv_handler * handler , int smp )
{
struct iucv_path * p , * n ;
mutex_lock ( & iucv_register_mutex ) ;
spin_lock_bh ( & iucv_table_lock ) ;
/* Remove handler from the iucv_handler_list. */
list_del_init ( & handler - > list ) ;
2011-03-31 05:57:33 +04:00
/* Sever all pathids still referring to the handler. */
2007-02-09 00:37:42 +03:00
list_for_each_entry_safe ( p , n , & handler - > paths , list ) {
iucv_sever_pathid ( p - > pathid , NULL ) ;
iucv_path_table [ p - > pathid ] = NULL ;
list_del ( & p - > list ) ;
iucv_path_free ( p ) ;
}
spin_unlock_bh ( & iucv_table_lock ) ;
if ( ! smp )
iucv_nonsmp_handler - - ;
if ( list_empty ( & iucv_handler_list ) )
iucv_disable ( ) ;
else if ( ! smp & & iucv_nonsmp_handler = = 0 )
iucv_setmask_mp ( ) ;
mutex_unlock ( & iucv_register_mutex ) ;
}
2007-05-04 23:23:27 +04:00
EXPORT_SYMBOL ( iucv_unregister ) ;
2007-02-09 00:37:42 +03:00
2009-06-16 12:30:41 +04:00
static int iucv_reboot_event ( struct notifier_block * this ,
unsigned long event , void * ptr )
{
2011-05-12 22:45:07 +04:00
int i ;
2009-06-16 12:30:41 +04:00
get_online_cpus ( ) ;
on_each_cpu ( iucv_block_cpu , NULL , 1 ) ;
preempt_disable ( ) ;
for ( i = 0 ; i < iucv_max_pathid ; i + + ) {
if ( iucv_path_table [ i ] )
2011-05-12 22:45:07 +04:00
iucv_sever_pathid ( i , NULL ) ;
2009-06-16 12:30:41 +04:00
}
preempt_enable ( ) ;
put_online_cpus ( ) ;
iucv_disable ( ) ;
return NOTIFY_DONE ;
}
static struct notifier_block iucv_reboot_notifier = {
. notifier_call = iucv_reboot_event ,
} ;
2007-02-09 00:37:42 +03:00
/**
* iucv_path_accept
* @ path : address of iucv path structure
* @ handler : address of iucv handler structure
* @ userdata : 16 bytes of data reflected to the communication partner
* @ private : private data passed to interrupt handlers for this path
*
* This function is issued after the user received a connection pending
* external interrupt and now wishes to complete the IUCV communication path .
*
* Returns the result of the CP IUCV call .
*/
int iucv_path_accept ( struct iucv_path * path , struct iucv_handler * handler ,
u8 userdata [ 16 ] , void * private )
{
union iucv_param * parm ;
int rc ;
local_bh_disable ( ) ;
2011-05-12 22:45:09 +04:00
if ( cpumask_empty ( & iucv_buffer_cpumask ) ) {
2009-06-16 12:30:41 +04:00
rc = - EIO ;
goto out ;
}
2007-02-09 00:37:42 +03:00
/* Prepare parameter block. */
2007-11-20 13:13:38 +03:00
parm = iucv_param [ smp_processor_id ( ) ] ;
2007-02-09 00:37:42 +03:00
memset ( parm , 0 , sizeof ( union iucv_param ) ) ;
parm - > ctrl . ippathid = path - > pathid ;
parm - > ctrl . ipmsglim = path - > msglim ;
if ( userdata )
memcpy ( parm - > ctrl . ipuser , userdata , sizeof ( parm - > ctrl . ipuser ) ) ;
parm - > ctrl . ipflags1 = path - > flags ;
rc = iucv_call_b2f0 ( IUCV_ACCEPT , parm ) ;
if ( ! rc ) {
path - > private = private ;
path - > msglim = parm - > ctrl . ipmsglim ;
path - > flags = parm - > ctrl . ipflags1 ;
}
2009-06-16 12:30:41 +04:00
out :
2007-02-09 00:37:42 +03:00
local_bh_enable ( ) ;
return rc ;
}
2007-05-04 23:23:27 +04:00
EXPORT_SYMBOL ( iucv_path_accept ) ;
2007-02-09 00:37:42 +03:00
/**
* iucv_path_connect
* @ path : address of iucv path structure
* @ handler : address of iucv handler structure
* @ userid : 8 - byte user identification
* @ system : 8 - byte target system identification
* @ userdata : 16 bytes of data reflected to the communication partner
* @ private : private data passed to interrupt handlers for this path
*
* This function establishes an IUCV path . Although the connect may complete
* successfully , you are not able to use the path until you receive an IUCV
* Connection Complete external interrupt .
*
* Returns the result of the CP IUCV call .
*/
int iucv_path_connect ( struct iucv_path * path , struct iucv_handler * handler ,
u8 userid [ 8 ] , u8 system [ 8 ] , u8 userdata [ 16 ] ,
void * private )
{
union iucv_param * parm ;
int rc ;
2007-04-29 10:03:59 +04:00
spin_lock_bh ( & iucv_table_lock ) ;
iucv_cleanup_queue ( ) ;
2011-05-12 22:45:09 +04:00
if ( cpumask_empty ( & iucv_buffer_cpumask ) ) {
2009-06-16 12:30:41 +04:00
rc = - EIO ;
goto out ;
}
2007-11-20 13:13:38 +03:00
parm = iucv_param [ smp_processor_id ( ) ] ;
2007-02-09 00:37:42 +03:00
memset ( parm , 0 , sizeof ( union iucv_param ) ) ;
parm - > ctrl . ipmsglim = path - > msglim ;
parm - > ctrl . ipflags1 = path - > flags ;
if ( userid ) {
memcpy ( parm - > ctrl . ipvmid , userid , sizeof ( parm - > ctrl . ipvmid ) ) ;
ASCEBC ( parm - > ctrl . ipvmid , sizeof ( parm - > ctrl . ipvmid ) ) ;
EBC_TOUPPER ( parm - > ctrl . ipvmid , sizeof ( parm - > ctrl . ipvmid ) ) ;
}
if ( system ) {
memcpy ( parm - > ctrl . iptarget , system ,
sizeof ( parm - > ctrl . iptarget ) ) ;
ASCEBC ( parm - > ctrl . iptarget , sizeof ( parm - > ctrl . iptarget ) ) ;
EBC_TOUPPER ( parm - > ctrl . iptarget , sizeof ( parm - > ctrl . iptarget ) ) ;
}
if ( userdata )
memcpy ( parm - > ctrl . ipuser , userdata , sizeof ( parm - > ctrl . ipuser ) ) ;
rc = iucv_call_b2f0 ( IUCV_CONNECT , parm ) ;
if ( ! rc ) {
if ( parm - > ctrl . ippathid < iucv_max_pathid ) {
path - > pathid = parm - > ctrl . ippathid ;
path - > msglim = parm - > ctrl . ipmsglim ;
path - > flags = parm - > ctrl . ipflags1 ;
path - > handler = handler ;
path - > private = private ;
list_add_tail ( & path - > list , & handler - > paths ) ;
iucv_path_table [ path - > pathid ] = path ;
} else {
iucv_sever_pathid ( parm - > ctrl . ippathid ,
iucv_error_pathid ) ;
rc = - EIO ;
}
}
2009-06-16 12:30:41 +04:00
out :
2007-04-29 10:03:59 +04:00
spin_unlock_bh ( & iucv_table_lock ) ;
2007-02-09 00:37:42 +03:00
return rc ;
}
2007-05-04 23:23:27 +04:00
EXPORT_SYMBOL ( iucv_path_connect ) ;
2007-02-09 00:37:42 +03:00
/**
* iucv_path_quiesce :
* @ path : address of iucv path structure
* @ userdata : 16 bytes of data reflected to the communication partner
*
* This function temporarily suspends incoming messages on an IUCV path .
* You can later reactivate the path by invoking the iucv_resume function .
*
* Returns the result from the CP IUCV call .
*/
int iucv_path_quiesce ( struct iucv_path * path , u8 userdata [ 16 ] )
{
union iucv_param * parm ;
int rc ;
local_bh_disable ( ) ;
2011-05-12 22:45:09 +04:00
if ( cpumask_empty ( & iucv_buffer_cpumask ) ) {
2009-06-16 12:30:41 +04:00
rc = - EIO ;
goto out ;
}
2007-11-20 13:13:38 +03:00
parm = iucv_param [ smp_processor_id ( ) ] ;
2007-02-09 00:37:42 +03:00
memset ( parm , 0 , sizeof ( union iucv_param ) ) ;
if ( userdata )
memcpy ( parm - > ctrl . ipuser , userdata , sizeof ( parm - > ctrl . ipuser ) ) ;
parm - > ctrl . ippathid = path - > pathid ;
rc = iucv_call_b2f0 ( IUCV_QUIESCE , parm ) ;
2009-06-16 12:30:41 +04:00
out :
2007-02-09 00:37:42 +03:00
local_bh_enable ( ) ;
return rc ;
}
2007-05-04 23:23:27 +04:00
EXPORT_SYMBOL ( iucv_path_quiesce ) ;
2007-02-09 00:37:42 +03:00
/**
* iucv_path_resume :
* @ path : address of iucv path structure
* @ userdata : 16 bytes of data reflected to the communication partner
*
* This function resumes incoming messages on an IUCV path that has
* been stopped with iucv_path_quiesce .
*
* Returns the result from the CP IUCV call .
*/
int iucv_path_resume ( struct iucv_path * path , u8 userdata [ 16 ] )
{
union iucv_param * parm ;
int rc ;
local_bh_disable ( ) ;
2011-05-12 22:45:09 +04:00
if ( cpumask_empty ( & iucv_buffer_cpumask ) ) {
2009-06-16 12:30:41 +04:00
rc = - EIO ;
goto out ;
}
2007-11-20 13:13:38 +03:00
parm = iucv_param [ smp_processor_id ( ) ] ;
2007-02-09 00:37:42 +03:00
memset ( parm , 0 , sizeof ( union iucv_param ) ) ;
if ( userdata )
memcpy ( parm - > ctrl . ipuser , userdata , sizeof ( parm - > ctrl . ipuser ) ) ;
parm - > ctrl . ippathid = path - > pathid ;
rc = iucv_call_b2f0 ( IUCV_RESUME , parm ) ;
2009-06-16 12:30:41 +04:00
out :
2007-02-09 00:37:42 +03:00
local_bh_enable ( ) ;
return rc ;
}
/**
* iucv_path_sever
* @ path : address of iucv path structure
* @ userdata : 16 bytes of data reflected to the communication partner
*
* This function terminates an IUCV path .
*
* Returns the result from the CP IUCV call .
*/
int iucv_path_sever ( struct iucv_path * path , u8 userdata [ 16 ] )
{
int rc ;
preempt_disable ( ) ;
2011-05-12 22:45:09 +04:00
if ( cpumask_empty ( & iucv_buffer_cpumask ) ) {
2009-06-16 12:30:41 +04:00
rc = - EIO ;
goto out ;
}
2007-04-29 10:03:59 +04:00
if ( iucv_active_cpu ! = smp_processor_id ( ) )
2007-02-09 00:37:42 +03:00
spin_lock_bh ( & iucv_table_lock ) ;
rc = iucv_sever_pathid ( path - > pathid , userdata ) ;
2009-04-22 03:26:20 +04:00
iucv_path_table [ path - > pathid ] = NULL ;
list_del_init ( & path - > list ) ;
2007-04-29 10:03:59 +04:00
if ( iucv_active_cpu ! = smp_processor_id ( ) )
2007-02-09 00:37:42 +03:00
spin_unlock_bh ( & iucv_table_lock ) ;
2009-06-16 12:30:41 +04:00
out :
2007-02-09 00:37:42 +03:00
preempt_enable ( ) ;
return rc ;
}
2007-05-04 23:23:27 +04:00
EXPORT_SYMBOL ( iucv_path_sever ) ;
2007-02-09 00:37:42 +03:00
/**
* iucv_message_purge
* @ path : address of iucv path structure
* @ msg : address of iucv msg structure
* @ srccls : source class of message
*
* Cancels a message you have sent .
*
* Returns the result from the CP IUCV call .
*/
int iucv_message_purge ( struct iucv_path * path , struct iucv_message * msg ,
u32 srccls )
{
union iucv_param * parm ;
int rc ;
local_bh_disable ( ) ;
2011-05-12 22:45:09 +04:00
if ( cpumask_empty ( & iucv_buffer_cpumask ) ) {
2009-06-16 12:30:41 +04:00
rc = - EIO ;
goto out ;
}
2007-11-20 13:13:38 +03:00
parm = iucv_param [ smp_processor_id ( ) ] ;
2007-02-09 00:37:42 +03:00
memset ( parm , 0 , sizeof ( union iucv_param ) ) ;
parm - > purge . ippathid = path - > pathid ;
parm - > purge . ipmsgid = msg - > id ;
parm - > purge . ipsrccls = srccls ;
parm - > purge . ipflags1 = IUCV_IPSRCCLS | IUCV_IPFGMID | IUCV_IPFGPID ;
rc = iucv_call_b2f0 ( IUCV_PURGE , parm ) ;
if ( ! rc ) {
msg - > audit = ( * ( u32 * ) & parm - > purge . ipaudit ) > > 8 ;
msg - > tag = parm - > purge . ipmsgtag ;
}
2009-06-16 12:30:41 +04:00
out :
2007-02-09 00:37:42 +03:00
local_bh_enable ( ) ;
return rc ;
}
2007-05-04 23:23:27 +04:00
EXPORT_SYMBOL ( iucv_message_purge ) ;
2007-02-09 00:37:42 +03:00
/**
2008-12-25 15:38:58 +03:00
* iucv_message_receive_iprmdata
* @ path : address of iucv path structure
* @ msg : address of iucv msg structure
* @ flags : how the message is received ( IUCV_IPBUFLST )
* @ buffer : address of data buffer or address of struct iucv_array
* @ size : length of data buffer
* @ residual :
*
* Internal function used by iucv_message_receive and __iucv_message_receive
* to receive RMDATA data stored in struct iucv_message .
*/
static int iucv_message_receive_iprmdata ( struct iucv_path * path ,
struct iucv_message * msg ,
u8 flags , void * buffer ,
size_t size , size_t * residual )
{
struct iucv_array * array ;
u8 * rmmsg ;
size_t copy ;
/*
* Message is 8 bytes long and has been stored to the
* message descriptor itself .
*/
if ( residual )
* residual = abs ( size - 8 ) ;
rmmsg = msg - > rmmsg ;
if ( flags & IUCV_IPBUFLST ) {
/* Copy to struct iucv_array. */
size = ( size < 8 ) ? size : 8 ;
for ( array = buffer ; size > 0 ; array + + ) {
copy = min_t ( size_t , size , array - > length ) ;
memcpy ( ( u8 * ) ( addr_t ) array - > address ,
rmmsg , copy ) ;
rmmsg + = copy ;
size - = copy ;
}
} else {
/* Copy to direct buffer. */
memcpy ( buffer , rmmsg , min_t ( size_t , size , 8 ) ) ;
}
return 0 ;
}
/**
* __iucv_message_receive
2007-02-09 00:37:42 +03:00
* @ path : address of iucv path structure
* @ msg : address of iucv msg structure
* @ flags : how the message is received ( IUCV_IPBUFLST )
* @ buffer : address of data buffer or address of struct iucv_array
* @ size : length of data buffer
* @ residual :
*
* This function receives messages that are being sent to you over
* established paths . This function will deal with RMDATA messages
* embedded in struct iucv_message as well .
*
2008-12-25 15:38:58 +03:00
* Locking : no locking
*
2007-02-09 00:37:42 +03:00
* Returns the result from the CP IUCV call .
*/
2008-12-25 15:38:58 +03:00
int __iucv_message_receive ( struct iucv_path * path , struct iucv_message * msg ,
u8 flags , void * buffer , size_t size , size_t * residual )
2007-02-09 00:37:42 +03:00
{
union iucv_param * parm ;
int rc ;
2008-12-25 15:38:58 +03:00
if ( msg - > flags & IUCV_IPRMDATA )
return iucv_message_receive_iprmdata ( path , msg , flags ,
buffer , size , residual ) ;
2011-05-12 22:45:09 +04:00
if ( cpumask_empty ( & iucv_buffer_cpumask ) ) {
2009-06-16 12:30:41 +04:00
rc = - EIO ;
goto out ;
}
2007-11-20 13:13:38 +03:00
parm = iucv_param [ smp_processor_id ( ) ] ;
2007-02-09 00:37:42 +03:00
memset ( parm , 0 , sizeof ( union iucv_param ) ) ;
parm - > db . ipbfadr1 = ( u32 ) ( addr_t ) buffer ;
parm - > db . ipbfln1f = ( u32 ) size ;
parm - > db . ipmsgid = msg - > id ;
parm - > db . ippathid = path - > pathid ;
parm - > db . iptrgcls = msg - > class ;
parm - > db . ipflags1 = ( flags | IUCV_IPFGPID |
IUCV_IPFGMID | IUCV_IPTRGCLS ) ;
rc = iucv_call_b2f0 ( IUCV_RECEIVE , parm ) ;
if ( ! rc | | rc = = 5 ) {
msg - > flags = parm - > db . ipflags1 ;
if ( residual )
* residual = parm - > db . ipbfln1f ;
}
2009-06-16 12:30:41 +04:00
out :
2008-12-25 15:38:58 +03:00
return rc ;
}
EXPORT_SYMBOL ( __iucv_message_receive ) ;
/**
* iucv_message_receive
* @ path : address of iucv path structure
* @ msg : address of iucv msg structure
* @ flags : how the message is received ( IUCV_IPBUFLST )
* @ buffer : address of data buffer or address of struct iucv_array
* @ size : length of data buffer
* @ residual :
*
* This function receives messages that are being sent to you over
* established paths . This function will deal with RMDATA messages
* embedded in struct iucv_message as well .
*
* Locking : local_bh_enable / local_bh_disable
*
* Returns the result from the CP IUCV call .
*/
int iucv_message_receive ( struct iucv_path * path , struct iucv_message * msg ,
u8 flags , void * buffer , size_t size , size_t * residual )
{
int rc ;
if ( msg - > flags & IUCV_IPRMDATA )
return iucv_message_receive_iprmdata ( path , msg , flags ,
buffer , size , residual ) ;
local_bh_disable ( ) ;
rc = __iucv_message_receive ( path , msg , flags , buffer , size , residual ) ;
2007-02-09 00:37:42 +03:00
local_bh_enable ( ) ;
return rc ;
}
2007-05-04 23:23:27 +04:00
EXPORT_SYMBOL ( iucv_message_receive ) ;
2007-02-09 00:37:42 +03:00
/**
* iucv_message_reject
* @ path : address of iucv path structure
* @ msg : address of iucv msg structure
*
* The reject function refuses a specified message . Between the time you
* are notified of a message and the time that you complete the message ,
* the message may be rejected .
*
* Returns the result from the CP IUCV call .
*/
int iucv_message_reject ( struct iucv_path * path , struct iucv_message * msg )
{
union iucv_param * parm ;
int rc ;
local_bh_disable ( ) ;
2011-05-12 22:45:09 +04:00
if ( cpumask_empty ( & iucv_buffer_cpumask ) ) {
2009-06-16 12:30:41 +04:00
rc = - EIO ;
goto out ;
}
2007-11-20 13:13:38 +03:00
parm = iucv_param [ smp_processor_id ( ) ] ;
2007-02-09 00:37:42 +03:00
memset ( parm , 0 , sizeof ( union iucv_param ) ) ;
parm - > db . ippathid = path - > pathid ;
parm - > db . ipmsgid = msg - > id ;
parm - > db . iptrgcls = msg - > class ;
parm - > db . ipflags1 = ( IUCV_IPTRGCLS | IUCV_IPFGMID | IUCV_IPFGPID ) ;
rc = iucv_call_b2f0 ( IUCV_REJECT , parm ) ;
2009-06-16 12:30:41 +04:00
out :
2007-02-09 00:37:42 +03:00
local_bh_enable ( ) ;
return rc ;
}
2007-05-04 23:23:27 +04:00
EXPORT_SYMBOL ( iucv_message_reject ) ;
2007-02-09 00:37:42 +03:00
/**
* iucv_message_reply
* @ path : address of iucv path structure
* @ msg : address of iucv msg structure
* @ flags : how the reply is sent ( IUCV_IPRMDATA , IUCV_IPPRTY , IUCV_IPBUFLST )
* @ reply : address of reply data buffer or address of struct iucv_array
* @ size : length of reply data buffer
*
* This function responds to the two - way messages that you receive . You
* must identify completely the message to which you wish to reply . ie ,
* pathid , msgid , and trgcls . Prmmsg signifies the data is moved into
* the parameter list .
*
* Returns the result from the CP IUCV call .
*/
int iucv_message_reply ( struct iucv_path * path , struct iucv_message * msg ,
u8 flags , void * reply , size_t size )
{
union iucv_param * parm ;
int rc ;
local_bh_disable ( ) ;
2011-05-12 22:45:09 +04:00
if ( cpumask_empty ( & iucv_buffer_cpumask ) ) {
2009-06-16 12:30:41 +04:00
rc = - EIO ;
goto out ;
}
2007-11-20 13:13:38 +03:00
parm = iucv_param [ smp_processor_id ( ) ] ;
2007-02-09 00:37:42 +03:00
memset ( parm , 0 , sizeof ( union iucv_param ) ) ;
if ( flags & IUCV_IPRMDATA ) {
parm - > dpl . ippathid = path - > pathid ;
parm - > dpl . ipflags1 = flags ;
parm - > dpl . ipmsgid = msg - > id ;
parm - > dpl . iptrgcls = msg - > class ;
memcpy ( parm - > dpl . iprmmsg , reply , min_t ( size_t , size , 8 ) ) ;
} else {
parm - > db . ipbfadr1 = ( u32 ) ( addr_t ) reply ;
parm - > db . ipbfln1f = ( u32 ) size ;
parm - > db . ippathid = path - > pathid ;
parm - > db . ipflags1 = flags ;
parm - > db . ipmsgid = msg - > id ;
parm - > db . iptrgcls = msg - > class ;
}
rc = iucv_call_b2f0 ( IUCV_REPLY , parm ) ;
2009-06-16 12:30:41 +04:00
out :
2007-02-09 00:37:42 +03:00
local_bh_enable ( ) ;
return rc ;
}
2007-05-04 23:23:27 +04:00
EXPORT_SYMBOL ( iucv_message_reply ) ;
2007-02-09 00:37:42 +03:00
/**
2008-12-25 15:38:58 +03:00
* __iucv_message_send
2007-02-09 00:37:42 +03:00
* @ path : address of iucv path structure
* @ msg : address of iucv msg structure
* @ flags : how the message is sent ( IUCV_IPRMDATA , IUCV_IPPRTY , IUCV_IPBUFLST )
* @ srccls : source class of message
* @ buffer : address of send buffer or address of struct iucv_array
* @ size : length of send buffer
*
* This function transmits data to another application . Data to be
* transmitted is in a buffer and this is a one - way message and the
* receiver will not reply to the message .
*
2008-12-25 15:38:58 +03:00
* Locking : no locking
*
2007-02-09 00:37:42 +03:00
* Returns the result from the CP IUCV call .
*/
2008-12-25 15:38:58 +03:00
int __iucv_message_send ( struct iucv_path * path , struct iucv_message * msg ,
2007-02-09 00:37:42 +03:00
u8 flags , u32 srccls , void * buffer , size_t size )
{
union iucv_param * parm ;
int rc ;
2011-05-12 22:45:09 +04:00
if ( cpumask_empty ( & iucv_buffer_cpumask ) ) {
2009-06-16 12:30:41 +04:00
rc = - EIO ;
goto out ;
}
2007-11-20 13:13:38 +03:00
parm = iucv_param [ smp_processor_id ( ) ] ;
2007-02-09 00:37:42 +03:00
memset ( parm , 0 , sizeof ( union iucv_param ) ) ;
if ( flags & IUCV_IPRMDATA ) {
/* Message of 8 bytes can be placed into the parameter list. */
parm - > dpl . ippathid = path - > pathid ;
parm - > dpl . ipflags1 = flags | IUCV_IPNORPY ;
parm - > dpl . iptrgcls = msg - > class ;
parm - > dpl . ipsrccls = srccls ;
parm - > dpl . ipmsgtag = msg - > tag ;
memcpy ( parm - > dpl . iprmmsg , buffer , 8 ) ;
} else {
parm - > db . ipbfadr1 = ( u32 ) ( addr_t ) buffer ;
parm - > db . ipbfln1f = ( u32 ) size ;
parm - > db . ippathid = path - > pathid ;
parm - > db . ipflags1 = flags | IUCV_IPNORPY ;
parm - > db . iptrgcls = msg - > class ;
parm - > db . ipsrccls = srccls ;
parm - > db . ipmsgtag = msg - > tag ;
}
rc = iucv_call_b2f0 ( IUCV_SEND , parm ) ;
if ( ! rc )
msg - > id = parm - > db . ipmsgid ;
2009-06-16 12:30:41 +04:00
out :
2008-12-25 15:38:58 +03:00
return rc ;
}
EXPORT_SYMBOL ( __iucv_message_send ) ;
/**
* iucv_message_send
* @ path : address of iucv path structure
* @ msg : address of iucv msg structure
* @ flags : how the message is sent ( IUCV_IPRMDATA , IUCV_IPPRTY , IUCV_IPBUFLST )
* @ srccls : source class of message
* @ buffer : address of send buffer or address of struct iucv_array
* @ size : length of send buffer
*
* This function transmits data to another application . Data to be
* transmitted is in a buffer and this is a one - way message and the
* receiver will not reply to the message .
*
* Locking : local_bh_enable / local_bh_disable
*
* Returns the result from the CP IUCV call .
*/
int iucv_message_send ( struct iucv_path * path , struct iucv_message * msg ,
u8 flags , u32 srccls , void * buffer , size_t size )
{
int rc ;
local_bh_disable ( ) ;
rc = __iucv_message_send ( path , msg , flags , srccls , buffer , size ) ;
2007-02-09 00:37:42 +03:00
local_bh_enable ( ) ;
return rc ;
}
2007-05-04 23:23:27 +04:00
EXPORT_SYMBOL ( iucv_message_send ) ;
2007-02-09 00:37:42 +03:00
/**
* iucv_message_send2way
* @ path : address of iucv path structure
* @ msg : address of iucv msg structure
* @ flags : how the message is sent and the reply is received
* ( IUCV_IPRMDATA , IUCV_IPBUFLST , IUCV_IPPRTY , IUCV_ANSLST )
* @ srccls : source class of message
* @ buffer : address of send buffer or address of struct iucv_array
* @ size : length of send buffer
* @ ansbuf : address of answer buffer or address of struct iucv_array
* @ asize : size of reply buffer
*
* This function transmits data to another application . Data to be
* transmitted is in a buffer . The receiver of the send is expected to
* reply to the message and a buffer is provided into which IUCV moves
* the reply to this message .
*
* Returns the result from the CP IUCV call .
*/
int iucv_message_send2way ( struct iucv_path * path , struct iucv_message * msg ,
u8 flags , u32 srccls , void * buffer , size_t size ,
void * answer , size_t asize , size_t * residual )
{
union iucv_param * parm ;
int rc ;
local_bh_disable ( ) ;
2011-05-12 22:45:09 +04:00
if ( cpumask_empty ( & iucv_buffer_cpumask ) ) {
2009-06-16 12:30:41 +04:00
rc = - EIO ;
goto out ;
}
2007-11-20 13:13:38 +03:00
parm = iucv_param [ smp_processor_id ( ) ] ;
2007-02-09 00:37:42 +03:00
memset ( parm , 0 , sizeof ( union iucv_param ) ) ;
if ( flags & IUCV_IPRMDATA ) {
parm - > dpl . ippathid = path - > pathid ;
parm - > dpl . ipflags1 = path - > flags ; /* priority message */
parm - > dpl . iptrgcls = msg - > class ;
parm - > dpl . ipsrccls = srccls ;
parm - > dpl . ipmsgtag = msg - > tag ;
parm - > dpl . ipbfadr2 = ( u32 ) ( addr_t ) answer ;
parm - > dpl . ipbfln2f = ( u32 ) asize ;
memcpy ( parm - > dpl . iprmmsg , buffer , 8 ) ;
} else {
parm - > db . ippathid = path - > pathid ;
parm - > db . ipflags1 = path - > flags ; /* priority message */
parm - > db . iptrgcls = msg - > class ;
parm - > db . ipsrccls = srccls ;
parm - > db . ipmsgtag = msg - > tag ;
parm - > db . ipbfadr1 = ( u32 ) ( addr_t ) buffer ;
parm - > db . ipbfln1f = ( u32 ) size ;
parm - > db . ipbfadr2 = ( u32 ) ( addr_t ) answer ;
parm - > db . ipbfln2f = ( u32 ) asize ;
}
rc = iucv_call_b2f0 ( IUCV_SEND , parm ) ;
if ( ! rc )
msg - > id = parm - > db . ipmsgid ;
2009-06-16 12:30:41 +04:00
out :
2007-02-09 00:37:42 +03:00
local_bh_enable ( ) ;
return rc ;
}
2007-05-04 23:23:27 +04:00
EXPORT_SYMBOL ( iucv_message_send2way ) ;
2007-02-09 00:37:42 +03:00
/**
* iucv_path_pending
* @ data : Pointer to external interrupt buffer
*
* Process connection pending work item . Called from tasklet while holding
* iucv_table_lock .
*/
struct iucv_path_pending {
u16 ippathid ;
u8 ipflags1 ;
u8 iptype ;
u16 ipmsglim ;
u16 res1 ;
u8 ipvmid [ 8 ] ;
u8 ipuser [ 16 ] ;
u32 res3 ;
u8 ippollfg ;
u8 res4 [ 3 ] ;
2010-06-03 14:21:52 +04:00
} __packed ;
2007-02-09 00:37:42 +03:00
static void iucv_path_pending ( struct iucv_irq_data * data )
{
struct iucv_path_pending * ipp = ( void * ) data ;
struct iucv_handler * handler ;
struct iucv_path * path ;
char * error ;
BUG_ON ( iucv_path_table [ ipp - > ippathid ] ) ;
/* New pathid, handler found. Create a new path struct. */
error = iucv_error_no_memory ;
path = iucv_path_alloc ( ipp - > ipmsglim , ipp - > ipflags1 , GFP_ATOMIC ) ;
if ( ! path )
goto out_sever ;
path - > pathid = ipp - > ippathid ;
iucv_path_table [ path - > pathid ] = path ;
EBCASC ( ipp - > ipvmid , 8 ) ;
/* Call registered handler until one is found that wants the path. */
list_for_each_entry ( handler , & iucv_handler_list , list ) {
if ( ! handler - > path_pending )
continue ;
/*
* Add path to handler to allow a call to iucv_path_sever
* inside the path_pending function . If the handler returns
* an error remove the path from the handler again .
*/
list_add ( & path - > list , & handler - > paths ) ;
path - > handler = handler ;
if ( ! handler - > path_pending ( path , ipp - > ipvmid , ipp - > ipuser ) )
return ;
list_del ( & path - > list ) ;
path - > handler = NULL ;
}
/* No handler wanted the path. */
iucv_path_table [ path - > pathid ] = NULL ;
iucv_path_free ( path ) ;
error = iucv_error_no_listener ;
out_sever :
iucv_sever_pathid ( ipp - > ippathid , error ) ;
}
/**
* iucv_path_complete
* @ data : Pointer to external interrupt buffer
*
* Process connection complete work item . Called from tasklet while holding
* iucv_table_lock .
*/
struct iucv_path_complete {
u16 ippathid ;
u8 ipflags1 ;
u8 iptype ;
u16 ipmsglim ;
u16 res1 ;
u8 res2 [ 8 ] ;
u8 ipuser [ 16 ] ;
u32 res3 ;
u8 ippollfg ;
u8 res4 [ 3 ] ;
2010-06-03 14:21:52 +04:00
} __packed ;
2007-02-09 00:37:42 +03:00
static void iucv_path_complete ( struct iucv_irq_data * data )
{
struct iucv_path_complete * ipc = ( void * ) data ;
struct iucv_path * path = iucv_path_table [ ipc - > ippathid ] ;
2009-04-22 03:26:23 +04:00
if ( path )
path - > flags = ipc - > ipflags1 ;
2007-04-29 10:03:59 +04:00
if ( path & & path - > handler & & path - > handler - > path_complete )
2007-02-09 00:37:42 +03:00
path - > handler - > path_complete ( path , ipc - > ipuser ) ;
}
/**
* iucv_path_severed
* @ data : Pointer to external interrupt buffer
*
* Process connection severed work item . Called from tasklet while holding
* iucv_table_lock .
*/
struct iucv_path_severed {
u16 ippathid ;
u8 res1 ;
u8 iptype ;
u32 res2 ;
u8 res3 [ 8 ] ;
u8 ipuser [ 16 ] ;
u32 res4 ;
u8 ippollfg ;
u8 res5 [ 3 ] ;
2010-06-03 14:21:52 +04:00
} __packed ;
2007-02-09 00:37:42 +03:00
static void iucv_path_severed ( struct iucv_irq_data * data )
{
struct iucv_path_severed * ips = ( void * ) data ;
struct iucv_path * path = iucv_path_table [ ips - > ippathid ] ;
2007-04-29 10:03:59 +04:00
if ( ! path | | ! path - > handler ) /* Already severed */
return ;
2007-02-09 00:37:42 +03:00
if ( path - > handler - > path_severed )
path - > handler - > path_severed ( path , ips - > ipuser ) ;
else {
iucv_sever_pathid ( path - > pathid , NULL ) ;
iucv_path_table [ path - > pathid ] = NULL ;
2009-04-22 03:26:20 +04:00
list_del ( & path - > list ) ;
2007-02-09 00:37:42 +03:00
iucv_path_free ( path ) ;
}
}
/**
* iucv_path_quiesced
* @ data : Pointer to external interrupt buffer
*
* Process connection quiesced work item . Called from tasklet while holding
* iucv_table_lock .
*/
struct iucv_path_quiesced {
u16 ippathid ;
u8 res1 ;
u8 iptype ;
u32 res2 ;
u8 res3 [ 8 ] ;
u8 ipuser [ 16 ] ;
u32 res4 ;
u8 ippollfg ;
u8 res5 [ 3 ] ;
2010-06-03 14:21:52 +04:00
} __packed ;
2007-02-09 00:37:42 +03:00
static void iucv_path_quiesced ( struct iucv_irq_data * data )
{
struct iucv_path_quiesced * ipq = ( void * ) data ;
struct iucv_path * path = iucv_path_table [ ipq - > ippathid ] ;
2007-04-29 10:03:59 +04:00
if ( path & & path - > handler & & path - > handler - > path_quiesced )
2007-02-09 00:37:42 +03:00
path - > handler - > path_quiesced ( path , ipq - > ipuser ) ;
}
/**
* iucv_path_resumed
* @ data : Pointer to external interrupt buffer
*
* Process connection resumed work item . Called from tasklet while holding
* iucv_table_lock .
*/
struct iucv_path_resumed {
u16 ippathid ;
u8 res1 ;
u8 iptype ;
u32 res2 ;
u8 res3 [ 8 ] ;
u8 ipuser [ 16 ] ;
u32 res4 ;
u8 ippollfg ;
u8 res5 [ 3 ] ;
2010-06-03 14:21:52 +04:00
} __packed ;
2007-02-09 00:37:42 +03:00
static void iucv_path_resumed ( struct iucv_irq_data * data )
{
struct iucv_path_resumed * ipr = ( void * ) data ;
struct iucv_path * path = iucv_path_table [ ipr - > ippathid ] ;
2007-04-29 10:03:59 +04:00
if ( path & & path - > handler & & path - > handler - > path_resumed )
2007-02-09 00:37:42 +03:00
path - > handler - > path_resumed ( path , ipr - > ipuser ) ;
}
/**
* iucv_message_complete
* @ data : Pointer to external interrupt buffer
*
* Process message complete work item . Called from tasklet while holding
* iucv_table_lock .
*/
struct iucv_message_complete {
u16 ippathid ;
u8 ipflags1 ;
u8 iptype ;
u32 ipmsgid ;
u32 ipaudit ;
u8 iprmmsg [ 8 ] ;
u32 ipsrccls ;
u32 ipmsgtag ;
u32 res ;
u32 ipbfln2f ;
u8 ippollfg ;
u8 res2 [ 3 ] ;
2010-06-03 14:21:52 +04:00
} __packed ;
2007-02-09 00:37:42 +03:00
static void iucv_message_complete ( struct iucv_irq_data * data )
{
struct iucv_message_complete * imc = ( void * ) data ;
struct iucv_path * path = iucv_path_table [ imc - > ippathid ] ;
struct iucv_message msg ;
2007-04-29 10:03:59 +04:00
if ( path & & path - > handler & & path - > handler - > message_complete ) {
2007-02-09 00:37:42 +03:00
msg . flags = imc - > ipflags1 ;
msg . id = imc - > ipmsgid ;
msg . audit = imc - > ipaudit ;
memcpy ( msg . rmmsg , imc - > iprmmsg , 8 ) ;
msg . class = imc - > ipsrccls ;
msg . tag = imc - > ipmsgtag ;
msg . length = imc - > ipbfln2f ;
path - > handler - > message_complete ( path , & msg ) ;
}
}
/**
* iucv_message_pending
* @ data : Pointer to external interrupt buffer
*
* Process message pending work item . Called from tasklet while holding
* iucv_table_lock .
*/
struct iucv_message_pending {
u16 ippathid ;
u8 ipflags1 ;
u8 iptype ;
u32 ipmsgid ;
u32 iptrgcls ;
union {
u32 iprmmsg1_u32 ;
u8 iprmmsg1 [ 4 ] ;
} ln1msg1 ;
union {
u32 ipbfln1f ;
u8 iprmmsg2 [ 4 ] ;
} ln1msg2 ;
u32 res1 [ 3 ] ;
u32 ipbfln2f ;
u8 ippollfg ;
u8 res2 [ 3 ] ;
2010-06-03 14:21:52 +04:00
} __packed ;
2007-02-09 00:37:42 +03:00
static void iucv_message_pending ( struct iucv_irq_data * data )
{
struct iucv_message_pending * imp = ( void * ) data ;
struct iucv_path * path = iucv_path_table [ imp - > ippathid ] ;
struct iucv_message msg ;
2007-04-29 10:03:59 +04:00
if ( path & & path - > handler & & path - > handler - > message_pending ) {
2007-02-09 00:37:42 +03:00
msg . flags = imp - > ipflags1 ;
msg . id = imp - > ipmsgid ;
msg . class = imp - > iptrgcls ;
if ( imp - > ipflags1 & IUCV_IPRMDATA ) {
memcpy ( msg . rmmsg , imp - > ln1msg1 . iprmmsg1 , 8 ) ;
msg . length = 8 ;
} else
msg . length = imp - > ln1msg2 . ipbfln1f ;
msg . reply_size = imp - > ipbfln2f ;
path - > handler - > message_pending ( path , & msg ) ;
}
}
/**
2007-04-29 10:03:59 +04:00
* iucv_tasklet_fn :
2007-02-09 00:37:42 +03:00
*
* This tasklet loops over the queue of irq buffers created by
* iucv_external_interrupt , calls the appropriate action handler
* and then frees the buffer .
*/
2007-04-29 10:03:59 +04:00
static void iucv_tasklet_fn ( unsigned long ignored )
2007-02-09 00:37:42 +03:00
{
typedef void iucv_irq_fn ( struct iucv_irq_data * ) ;
static iucv_irq_fn * irq_fn [ ] = {
[ 0x02 ] = iucv_path_complete ,
[ 0x03 ] = iucv_path_severed ,
[ 0x04 ] = iucv_path_quiesced ,
[ 0x05 ] = iucv_path_resumed ,
[ 0x06 ] = iucv_message_complete ,
[ 0x07 ] = iucv_message_complete ,
[ 0x08 ] = iucv_message_pending ,
[ 0x09 ] = iucv_message_pending ,
} ;
2007-12-07 11:51:45 +03:00
LIST_HEAD ( task_queue ) ;
2007-04-29 10:03:59 +04:00
struct iucv_irq_list * p , * n ;
2007-02-09 00:37:42 +03:00
/* Serialize tasklet, iucv_path_sever and iucv_path_connect. */
2007-07-15 06:03:41 +04:00
if ( ! spin_trylock ( & iucv_table_lock ) ) {
tasklet_schedule ( & iucv_tasklet ) ;
return ;
}
2007-04-29 10:03:59 +04:00
iucv_active_cpu = smp_processor_id ( ) ;
2007-02-09 00:37:42 +03:00
2007-04-29 10:03:59 +04:00
spin_lock_irq ( & iucv_queue_lock ) ;
list_splice_init ( & iucv_task_queue , & task_queue ) ;
spin_unlock_irq ( & iucv_queue_lock ) ;
list_for_each_entry_safe ( p , n , & task_queue , list ) {
2007-02-09 00:37:42 +03:00
list_del_init ( & p - > list ) ;
irq_fn [ p - > data . iptype ] ( & p - > data ) ;
kfree ( p ) ;
}
2007-04-29 10:03:59 +04:00
iucv_active_cpu = - 1 ;
2007-02-09 00:37:42 +03:00
spin_unlock ( & iucv_table_lock ) ;
}
2007-04-29 10:03:59 +04:00
/**
* iucv_work_fn :
*
* This work function loops over the queue of path pending irq blocks
* created by iucv_external_interrupt , calls the appropriate action
* handler and then frees the buffer .
*/
static void iucv_work_fn ( struct work_struct * work )
{
2007-12-07 11:51:45 +03:00
LIST_HEAD ( work_queue ) ;
2007-04-29 10:03:59 +04:00
struct iucv_irq_list * p , * n ;
/* Serialize tasklet, iucv_path_sever and iucv_path_connect. */
spin_lock_bh ( & iucv_table_lock ) ;
iucv_active_cpu = smp_processor_id ( ) ;
spin_lock_irq ( & iucv_queue_lock ) ;
list_splice_init ( & iucv_work_queue , & work_queue ) ;
spin_unlock_irq ( & iucv_queue_lock ) ;
iucv_cleanup_queue ( ) ;
list_for_each_entry_safe ( p , n , & work_queue , list ) {
list_del_init ( & p - > list ) ;
iucv_path_pending ( & p - > data ) ;
kfree ( p ) ;
}
iucv_active_cpu = - 1 ;
spin_unlock_bh ( & iucv_table_lock ) ;
}
2007-02-09 00:37:42 +03:00
/**
* iucv_external_interrupt
* @ code : irq code
*
* Handles external interrupts coming in from CP .
2007-04-29 10:03:59 +04:00
* Places the interrupt buffer on a queue and schedules iucv_tasklet_fn ( ) .
2007-02-09 00:37:42 +03:00
*/
2012-03-11 19:59:31 +04:00
static void iucv_external_interrupt ( struct ext_code ext_code ,
2010-10-25 18:10:38 +04:00
unsigned int param32 , unsigned long param64 )
2007-02-09 00:37:42 +03:00
{
struct iucv_irq_data * p ;
2007-04-29 10:03:59 +04:00
struct iucv_irq_list * work ;
2007-02-09 00:37:42 +03:00
2011-01-05 14:47:28 +03:00
kstat_cpu ( smp_processor_id ( ) ) . irqs [ EXTINT_IUC ] + + ;
2007-11-20 13:13:38 +03:00
p = iucv_irq_data [ smp_processor_id ( ) ] ;
2007-02-09 00:37:42 +03:00
if ( p - > ippathid > = iucv_max_pathid ) {
2008-07-14 11:59:29 +04:00
WARN_ON ( p - > ippathid > = iucv_max_pathid ) ;
2007-02-09 00:37:42 +03:00
iucv_sever_pathid ( p - > ippathid , iucv_error_no_listener ) ;
return ;
}
2008-07-14 11:59:29 +04:00
BUG_ON ( p - > iptype < 0x01 | | p - > iptype > 0x09 ) ;
2007-04-29 10:03:59 +04:00
work = kmalloc ( sizeof ( struct iucv_irq_list ) , GFP_ATOMIC ) ;
2007-02-09 00:37:42 +03:00
if ( ! work ) {
2008-12-25 15:39:47 +03:00
pr_warning ( " iucv_external_interrupt: out of memory \n " ) ;
2007-02-09 00:37:42 +03:00
return ;
}
memcpy ( & work - > data , p , sizeof ( work - > data ) ) ;
2007-04-29 10:03:59 +04:00
spin_lock ( & iucv_queue_lock ) ;
if ( p - > iptype = = 0x01 ) {
/* Path pending interrupt. */
list_add_tail ( & work - > list , & iucv_work_queue ) ;
schedule_work ( & iucv_work ) ;
} else {
/* The other interrupts. */
list_add_tail ( & work - > list , & iucv_task_queue ) ;
tasklet_schedule ( & iucv_tasklet ) ;
}
spin_unlock ( & iucv_queue_lock ) ;
2007-02-09 00:37:42 +03:00
}
2009-06-16 12:30:42 +04:00
static int iucv_pm_prepare ( struct device * dev )
{
int rc = 0 ;
# ifdef CONFIG_PM_DEBUG
printk ( KERN_INFO " iucv_pm_prepare \n " ) ;
# endif
if ( dev - > driver & & dev - > driver - > pm & & dev - > driver - > pm - > prepare )
rc = dev - > driver - > pm - > prepare ( dev ) ;
return rc ;
}
static void iucv_pm_complete ( struct device * dev )
{
# ifdef CONFIG_PM_DEBUG
printk ( KERN_INFO " iucv_pm_complete \n " ) ;
# endif
if ( dev - > driver & & dev - > driver - > pm & & dev - > driver - > pm - > complete )
dev - > driver - > pm - > complete ( dev ) ;
}
/**
* iucv_path_table_empty ( ) - determine if iucv path table is empty
*
* Returns 0 if there are still iucv pathes defined
* 1 if there are no iucv pathes defined
*/
int iucv_path_table_empty ( void )
{
int i ;
for ( i = 0 ; i < iucv_max_pathid ; i + + ) {
if ( iucv_path_table [ i ] )
return 0 ;
}
return 1 ;
}
/**
* iucv_pm_freeze ( ) - Freeze PM callback
* @ dev : iucv - based device
*
* disable iucv interrupts
* invoke callback function of the iucv - based driver
* shut down iucv , if no iucv - pathes are established anymore
*/
static int iucv_pm_freeze ( struct device * dev )
{
int cpu ;
2009-11-13 00:46:27 +03:00
struct iucv_irq_list * p , * n ;
2009-06-16 12:30:42 +04:00
int rc = 0 ;
# ifdef CONFIG_PM_DEBUG
printk ( KERN_WARNING " iucv_pm_freeze \n " ) ;
# endif
2009-11-13 00:46:27 +03:00
if ( iucv_pm_state ! = IUCV_PM_FREEZING ) {
2011-05-12 22:45:09 +04:00
for_each_cpu ( cpu , & iucv_irq_cpumask )
2009-11-13 00:46:27 +03:00
smp_call_function_single ( cpu , iucv_block_cpu_almost ,
NULL , 1 ) ;
cancel_work_sync ( & iucv_work ) ;
list_for_each_entry_safe ( p , n , & iucv_work_queue , list ) {
list_del_init ( & p - > list ) ;
iucv_sever_pathid ( p - > data . ippathid ,
iucv_error_no_listener ) ;
kfree ( p ) ;
}
}
2009-09-16 08:37:22 +04:00
iucv_pm_state = IUCV_PM_FREEZING ;
2009-06-16 12:30:42 +04:00
if ( dev - > driver & & dev - > driver - > pm & & dev - > driver - > pm - > freeze )
rc = dev - > driver - > pm - > freeze ( dev ) ;
if ( iucv_path_table_empty ( ) )
iucv_disable ( ) ;
return rc ;
}
/**
* iucv_pm_thaw ( ) - Thaw PM callback
* @ dev : iucv - based device
*
* make iucv ready for use again : allocate path table , declare interrupt buffers
* and enable iucv interrupts
* invoke callback function of the iucv - based driver
*/
static int iucv_pm_thaw ( struct device * dev )
{
int rc = 0 ;
# ifdef CONFIG_PM_DEBUG
printk ( KERN_WARNING " iucv_pm_thaw \n " ) ;
# endif
2009-09-16 08:37:22 +04:00
iucv_pm_state = IUCV_PM_THAWING ;
2009-06-16 12:30:42 +04:00
if ( ! iucv_path_table ) {
rc = iucv_enable ( ) ;
if ( rc )
goto out ;
}
2011-05-12 22:45:09 +04:00
if ( cpumask_empty ( & iucv_irq_cpumask ) ) {
2009-06-16 12:30:42 +04:00
if ( iucv_nonsmp_handler )
/* enable interrupts on one cpu */
iucv_allow_cpu ( NULL ) ;
else
/* enable interrupts on all cpus */
iucv_setmask_mp ( ) ;
}
if ( dev - > driver & & dev - > driver - > pm & & dev - > driver - > pm - > thaw )
rc = dev - > driver - > pm - > thaw ( dev ) ;
out :
return rc ;
}
/**
* iucv_pm_restore ( ) - Restore PM callback
* @ dev : iucv - based device
*
* make iucv ready for use again : allocate path table , declare interrupt buffers
* and enable iucv interrupts
* invoke callback function of the iucv - based driver
*/
static int iucv_pm_restore ( struct device * dev )
{
int rc = 0 ;
# ifdef CONFIG_PM_DEBUG
printk ( KERN_WARNING " iucv_pm_restore %p \n " , iucv_path_table ) ;
# endif
2009-09-16 08:37:22 +04:00
if ( ( iucv_pm_state ! = IUCV_PM_RESTORING ) & & iucv_path_table )
pr_warning ( " Suspending Linux did not completely close all IUCV "
" connections \n " ) ;
iucv_pm_state = IUCV_PM_RESTORING ;
2011-05-12 22:45:09 +04:00
if ( cpumask_empty ( & iucv_irq_cpumask ) ) {
2009-06-16 12:30:42 +04:00
rc = iucv_query_maxconn ( ) ;
rc = iucv_enable ( ) ;
if ( rc )
goto out ;
}
if ( dev - > driver & & dev - > driver - > pm & & dev - > driver - > pm - > restore )
rc = dev - > driver - > pm - > restore ( dev ) ;
out :
return rc ;
}
2011-08-08 05:33:49 +04:00
struct iucv_interface iucv_if = {
. message_receive = iucv_message_receive ,
. __message_receive = __iucv_message_receive ,
. message_reply = iucv_message_reply ,
. message_reject = iucv_message_reject ,
. message_send = iucv_message_send ,
. __message_send = __iucv_message_send ,
. message_send2way = iucv_message_send2way ,
. message_purge = iucv_message_purge ,
. path_accept = iucv_path_accept ,
. path_connect = iucv_path_connect ,
. path_quiesce = iucv_path_quiesce ,
. path_resume = iucv_path_resume ,
. path_sever = iucv_path_sever ,
. iucv_register = iucv_register ,
. iucv_unregister = iucv_unregister ,
. bus = NULL ,
. root = NULL ,
} ;
EXPORT_SYMBOL ( iucv_if ) ;
2007-02-09 00:37:42 +03:00
/**
* iucv_init
*
* Allocates and initializes various data structures .
*/
2007-05-04 23:23:27 +04:00
static int __init iucv_init ( void )
2007-02-09 00:37:42 +03:00
{
int rc ;
2007-11-20 13:13:38 +03:00
int cpu ;
2007-02-09 00:37:42 +03:00
if ( ! MACHINE_IS_VM ) {
rc = - EPROTONOSUPPORT ;
goto out ;
}
2011-07-24 12:48:28 +04:00
ctl_set_bit ( 0 , 1 ) ;
2007-02-09 00:37:42 +03:00
rc = iucv_query_maxconn ( ) ;
if ( rc )
2011-07-24 12:48:28 +04:00
goto out_ctl ;
2007-05-04 23:23:27 +04:00
rc = register_external_interrupt ( 0x4000 , iucv_external_interrupt ) ;
2007-02-09 00:37:42 +03:00
if ( rc )
2011-07-24 12:48:28 +04:00
goto out_ctl ;
2008-12-15 15:58:29 +03:00
iucv_root = root_device_register ( " iucv " ) ;
2007-02-09 00:37:42 +03:00
if ( IS_ERR ( iucv_root ) ) {
rc = PTR_ERR ( iucv_root ) ;
2008-04-10 13:12:45 +04:00
goto out_int ;
2007-02-09 00:37:42 +03:00
}
2007-11-20 13:13:38 +03:00
for_each_online_cpu ( cpu ) {
/* Note: GFP_DMA used to get memory below 2G */
iucv_irq_data [ cpu ] = kmalloc_node ( sizeof ( struct iucv_irq_data ) ,
GFP_KERNEL | GFP_DMA , cpu_to_node ( cpu ) ) ;
if ( ! iucv_irq_data [ cpu ] ) {
rc = - ENOMEM ;
goto out_free ;
}
/* Allocate parameter blocks. */
iucv_param [ cpu ] = kmalloc_node ( sizeof ( union iucv_param ) ,
GFP_KERNEL | GFP_DMA , cpu_to_node ( cpu ) ) ;
if ( ! iucv_param [ cpu ] ) {
rc = - ENOMEM ;
goto out_free ;
}
2009-04-22 03:26:20 +04:00
iucv_param_irq [ cpu ] = kmalloc_node ( sizeof ( union iucv_param ) ,
GFP_KERNEL | GFP_DMA , cpu_to_node ( cpu ) ) ;
if ( ! iucv_param_irq [ cpu ] ) {
rc = - ENOMEM ;
goto out_free ;
}
2007-02-09 00:37:42 +03:00
}
2008-04-10 13:12:45 +04:00
rc = register_hotcpu_notifier ( & iucv_cpu_notifier ) ;
if ( rc )
goto out_free ;
2009-06-16 12:30:41 +04:00
rc = register_reboot_notifier ( & iucv_reboot_notifier ) ;
if ( rc )
goto out_cpu ;
2007-02-09 00:37:42 +03:00
ASCEBC ( iucv_error_no_listener , 16 ) ;
ASCEBC ( iucv_error_no_memory , 16 ) ;
ASCEBC ( iucv_error_pathid , 16 ) ;
iucv_available = 1 ;
2008-04-10 13:12:45 +04:00
rc = bus_register ( & iucv_bus ) ;
if ( rc )
2009-06-16 12:30:41 +04:00
goto out_reboot ;
2011-08-08 05:33:49 +04:00
iucv_if . root = iucv_root ;
iucv_if . bus = & iucv_bus ;
2007-02-09 00:37:42 +03:00
return 0 ;
2009-06-16 12:30:41 +04:00
out_reboot :
unregister_reboot_notifier ( & iucv_reboot_notifier ) ;
2008-04-10 13:12:45 +04:00
out_cpu :
unregister_hotcpu_notifier ( & iucv_cpu_notifier ) ;
2007-11-20 13:13:38 +03:00
out_free :
for_each_possible_cpu ( cpu ) {
2009-04-22 03:26:20 +04:00
kfree ( iucv_param_irq [ cpu ] ) ;
iucv_param_irq [ cpu ] = NULL ;
2007-11-20 13:13:38 +03:00
kfree ( iucv_param [ cpu ] ) ;
iucv_param [ cpu ] = NULL ;
kfree ( iucv_irq_data [ cpu ] ) ;
iucv_irq_data [ cpu ] = NULL ;
}
2008-12-15 15:58:29 +03:00
root_device_unregister ( iucv_root ) ;
2007-02-09 00:37:42 +03:00
out_int :
unregister_external_interrupt ( 0x4000 , iucv_external_interrupt ) ;
2011-07-24 12:48:28 +04:00
out_ctl :
ctl_clear_bit ( 0 , 1 ) ;
2007-02-09 00:37:42 +03:00
out :
return rc ;
}
/**
* iucv_exit
*
* Frees everything allocated from iucv_init .
*/
2007-05-04 23:23:27 +04:00
static void __exit iucv_exit ( void )
2007-02-09 00:37:42 +03:00
{
2007-04-29 10:03:59 +04:00
struct iucv_irq_list * p , * n ;
2007-11-20 13:13:38 +03:00
int cpu ;
2007-02-09 00:37:42 +03:00
2007-04-29 10:03:59 +04:00
spin_lock_irq ( & iucv_queue_lock ) ;
list_for_each_entry_safe ( p , n , & iucv_task_queue , list )
kfree ( p ) ;
2007-02-09 00:37:42 +03:00
list_for_each_entry_safe ( p , n , & iucv_work_queue , list )
kfree ( p ) ;
2007-04-29 10:03:59 +04:00
spin_unlock_irq ( & iucv_queue_lock ) ;
2009-06-16 12:30:41 +04:00
unregister_reboot_notifier ( & iucv_reboot_notifier ) ;
2007-02-09 00:37:42 +03:00
unregister_hotcpu_notifier ( & iucv_cpu_notifier ) ;
2007-11-20 13:13:38 +03:00
for_each_possible_cpu ( cpu ) {
2009-04-22 03:26:20 +04:00
kfree ( iucv_param_irq [ cpu ] ) ;
iucv_param_irq [ cpu ] = NULL ;
2007-11-20 13:13:38 +03:00
kfree ( iucv_param [ cpu ] ) ;
iucv_param [ cpu ] = NULL ;
kfree ( iucv_irq_data [ cpu ] ) ;
iucv_irq_data [ cpu ] = NULL ;
}
2008-12-15 15:58:29 +03:00
root_device_unregister ( iucv_root ) ;
2007-02-09 00:37:42 +03:00
bus_unregister ( & iucv_bus ) ;
unregister_external_interrupt ( 0x4000 , iucv_external_interrupt ) ;
}
subsys_initcall ( iucv_init ) ;
module_exit ( iucv_exit ) ;
MODULE_AUTHOR ( " (C) 2001 IBM Corp. by Fritz Elfert (felfert@millenux.com) " ) ;
MODULE_DESCRIPTION ( " Linux for S/390 IUCV lowlevel driver " ) ;
MODULE_LICENSE ( " GPL " ) ;