2005-04-17 02:20:36 +04:00
/*
* IUCV network driver
*
2009-06-16 12:30:43 +04:00
* Copyright IBM Corp . 2001 , 2009
2005-04-17 02:20:36 +04:00
*
2009-06-16 12:30:43 +04:00
* Author ( s ) :
* Original netiucv driver :
* Fritz Elfert ( elfert @ de . ibm . com , felfert @ millenux . com )
* Sysfs integration and all bugs therein :
* Cornelia Huck ( cornelia . huck @ de . ibm . com )
* PM functions :
* Ursula Braun ( ursula . braun @ de . ibm . com )
2005-04-17 02:20:36 +04:00
*
* Documentation used :
* the source of the original IUCV driver by :
* Stefan Hegewald < hegewald @ de . ibm . com >
* Hartmut Penner < hpenner @ de . ibm . com >
* Denis Joseph Barrow ( djbarrow @ de . ibm . com , barrow_dj @ yahoo . com )
* Martin Schwidefsky ( schwidefsky @ de . ibm . com )
* Alan Altmark ( Alan_Altmark @ us . ibm . com ) Sept . 2000
*
* 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 .
*
*/
2006-05-27 05:58:38 +04:00
2008-12-25 15:39:47 +03:00
# define KMSG_COMPONENT "netiucv"
# define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
2005-04-17 02:20:36 +04:00
# undef DEBUG
# include <linux/module.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/errno.h>
# include <linux/types.h>
# include <linux/interrupt.h>
# include <linux/timer.h>
# include <linux/bitops.h>
# include <linux/signal.h>
# include <linux/string.h>
# include <linux/device.h>
# include <linux/ip.h>
# include <linux/if_arp.h>
# include <linux/tcp.h>
# include <linux/skbuff.h>
# include <linux/ctype.h>
# include <net/dst.h>
# include <asm/io.h>
# include <asm/uaccess.h>
2011-12-20 02:56:34 +04:00
# include <asm/ebcdic.h>
2005-04-17 02:20:36 +04:00
2007-02-09 00:50:33 +03:00
# include <net/iucv/iucv.h>
2005-04-17 02:20:36 +04:00
# include "fsm.h"
MODULE_AUTHOR
( " (C) 2001 IBM Corporation by Fritz Elfert (felfert@millenux.com) " ) ;
MODULE_DESCRIPTION ( " Linux for S/390 IUCV network driver " ) ;
2007-02-09 00:50:33 +03:00
/**
* Debug Facility stuff
*/
# define IUCV_DBF_SETUP_NAME "iucv_setup"
2011-12-20 02:56:34 +04:00
# define IUCV_DBF_SETUP_LEN 64
2007-02-09 00:50:33 +03:00
# define IUCV_DBF_SETUP_PAGES 2
# define IUCV_DBF_SETUP_NR_AREAS 1
# define IUCV_DBF_SETUP_LEVEL 3
# define IUCV_DBF_DATA_NAME "iucv_data"
# define IUCV_DBF_DATA_LEN 128
# define IUCV_DBF_DATA_PAGES 2
# define IUCV_DBF_DATA_NR_AREAS 1
# define IUCV_DBF_DATA_LEVEL 2
# define IUCV_DBF_TRACE_NAME "iucv_trace"
# define IUCV_DBF_TRACE_LEN 16
# define IUCV_DBF_TRACE_PAGES 4
# define IUCV_DBF_TRACE_NR_AREAS 1
# define IUCV_DBF_TRACE_LEVEL 3
# define IUCV_DBF_TEXT(name,level,text) \
do { \
debug_text_event ( iucv_dbf_ # # name , level , text ) ; \
} while ( 0 )
# define IUCV_DBF_HEX(name,level,addr,len) \
do { \
debug_event ( iucv_dbf_ # # name , level , ( void * ) ( addr ) , len ) ; \
} while ( 0 )
DECLARE_PER_CPU ( char [ 256 ] , iucv_dbf_txt_buf ) ;
2008-02-08 15:09:05 +03:00
/* Allow to sort out low debug levels early to avoid wasted sprints */
static inline int iucv_dbf_passes ( debug_info_t * dbf_grp , int level )
{
return ( level < = dbf_grp - > level ) ;
}
# define IUCV_DBF_TEXT_(name, level, text...) \
do { \
if ( iucv_dbf_passes ( iucv_dbf_ # # name , level ) ) { \
2009-10-29 16:34:14 +03:00
char * __buf = get_cpu_var ( iucv_dbf_txt_buf ) ; \
sprintf ( __buf , text ) ; \
debug_text_event ( iucv_dbf_ # # name , level , __buf ) ; \
2008-02-08 15:09:05 +03:00
put_cpu_var ( iucv_dbf_txt_buf ) ; \
} \
2007-02-09 00:50:33 +03:00
} while ( 0 )
# define IUCV_DBF_SPRINTF(name,level,text...) \
do { \
debug_sprintf_event ( iucv_dbf_trace , level , # # text ) ; \
debug_sprintf_event ( iucv_dbf_trace , level , text ) ; \
} while ( 0 )
/**
* some more debug stuff
*/
# define IUCV_HEXDUMP16(importance,header,ptr) \
PRINT_ # # importance ( header " %02x %02x %02x %02x %02x %02x %02x %02x " \
" %02x %02x %02x %02x %02x %02x %02x %02x \n " , \
* ( ( ( char * ) ptr ) ) , * ( ( ( char * ) ptr ) + 1 ) , * ( ( ( char * ) ptr ) + 2 ) , \
* ( ( ( char * ) ptr ) + 3 ) , * ( ( ( char * ) ptr ) + 4 ) , * ( ( ( char * ) ptr ) + 5 ) , \
* ( ( ( char * ) ptr ) + 6 ) , * ( ( ( char * ) ptr ) + 7 ) , * ( ( ( char * ) ptr ) + 8 ) , \
* ( ( ( char * ) ptr ) + 9 ) , * ( ( ( char * ) ptr ) + 10 ) , * ( ( ( char * ) ptr ) + 11 ) , \
* ( ( ( char * ) ptr ) + 12 ) , * ( ( ( char * ) ptr ) + 13 ) , \
* ( ( ( char * ) ptr ) + 14 ) , * ( ( ( char * ) ptr ) + 15 ) ) ; \
PRINT_ # # importance ( header " %02x %02x %02x %02x %02x %02x %02x %02x " \
" %02x %02x %02x %02x %02x %02x %02x %02x \n " , \
* ( ( ( char * ) ptr ) + 16 ) , * ( ( ( char * ) ptr ) + 17 ) , \
* ( ( ( char * ) ptr ) + 18 ) , * ( ( ( char * ) ptr ) + 19 ) , \
* ( ( ( char * ) ptr ) + 20 ) , * ( ( ( char * ) ptr ) + 21 ) , \
* ( ( ( char * ) ptr ) + 22 ) , * ( ( ( char * ) ptr ) + 23 ) , \
* ( ( ( char * ) ptr ) + 24 ) , * ( ( ( char * ) ptr ) + 25 ) , \
* ( ( ( char * ) ptr ) + 26 ) , * ( ( ( char * ) ptr ) + 27 ) , \
* ( ( ( char * ) ptr ) + 28 ) , * ( ( ( char * ) ptr ) + 29 ) , \
* ( ( ( char * ) ptr ) + 30 ) , * ( ( ( char * ) ptr ) + 31 ) ) ;
2005-04-17 02:20:36 +04:00
# define PRINTK_HEADER " iucv: " /* for debugging */
2009-06-16 12:30:43 +04:00
/* dummy device to make sure netiucv_pm functions are called */
static struct device * netiucv_dev ;
static int netiucv_pm_prepare ( struct device * ) ;
static void netiucv_pm_complete ( struct device * ) ;
static int netiucv_pm_freeze ( struct device * ) ;
static int netiucv_pm_restore_thaw ( struct device * ) ;
2009-12-15 05:00:08 +03:00
static const struct dev_pm_ops netiucv_pm_ops = {
2009-06-16 12:30:43 +04:00
. prepare = netiucv_pm_prepare ,
. complete = netiucv_pm_complete ,
. freeze = netiucv_pm_freeze ,
. thaw = netiucv_pm_restore_thaw ,
. restore = netiucv_pm_restore_thaw ,
} ;
2005-04-17 02:20:36 +04:00
static struct device_driver netiucv_driver = {
2008-02-08 15:09:02 +03:00
. owner = THIS_MODULE ,
2005-04-17 02:20:36 +04:00
. name = " netiucv " ,
. bus = & iucv_bus ,
2009-06-16 12:30:43 +04:00
. pm = & netiucv_pm_ops ,
2005-04-17 02:20:36 +04:00
} ;
2007-02-09 00:50:33 +03:00
static int netiucv_callback_connreq ( struct iucv_path * ,
u8 ipvmid [ 8 ] , u8 ipuser [ 16 ] ) ;
static void netiucv_callback_connack ( struct iucv_path * , u8 ipuser [ 16 ] ) ;
static void netiucv_callback_connrej ( struct iucv_path * , u8 ipuser [ 16 ] ) ;
static void netiucv_callback_connsusp ( struct iucv_path * , u8 ipuser [ 16 ] ) ;
static void netiucv_callback_connres ( struct iucv_path * , u8 ipuser [ 16 ] ) ;
static void netiucv_callback_rx ( struct iucv_path * , struct iucv_message * ) ;
static void netiucv_callback_txdone ( struct iucv_path * , struct iucv_message * ) ;
static struct iucv_handler netiucv_handler = {
. path_pending = netiucv_callback_connreq ,
. path_complete = netiucv_callback_connack ,
. path_severed = netiucv_callback_connrej ,
. path_quiesced = netiucv_callback_connsusp ,
. path_resumed = netiucv_callback_connres ,
. message_pending = netiucv_callback_rx ,
. message_complete = netiucv_callback_txdone
} ;
2005-04-17 02:20:36 +04:00
/**
* Per connection profiling data
*/
struct connection_profile {
unsigned long maxmulti ;
unsigned long maxcqueue ;
unsigned long doios_single ;
unsigned long doios_multi ;
unsigned long txlen ;
unsigned long tx_time ;
struct timespec send_stamp ;
unsigned long tx_pending ;
unsigned long tx_max_pending ;
} ;
/**
* Representation of one iucv connection
*/
struct iucv_connection {
2007-02-09 00:50:33 +03:00
struct list_head list ;
struct iucv_path * path ;
2005-04-17 02:20:36 +04:00
struct sk_buff * rx_buff ;
struct sk_buff * tx_buff ;
struct sk_buff_head collect_queue ;
struct sk_buff_head commit_queue ;
spinlock_t collect_lock ;
int collect_len ;
int max_buffsize ;
fsm_timer timer ;
fsm_instance * fsm ;
struct net_device * netdev ;
struct connection_profile prof ;
char userid [ 9 ] ;
2011-12-20 02:56:34 +04:00
char userdata [ 17 ] ;
2005-04-17 02:20:36 +04:00
} ;
/**
* Linked list of all connection structs .
*/
2008-01-26 16:11:13 +03:00
static LIST_HEAD ( iucv_connection_list ) ;
2007-06-20 15:02:55 +04:00
static DEFINE_RWLOCK ( iucv_connection_rwlock ) ;
2005-04-17 02:20:36 +04:00
/**
* Representation of event - data for the
* connection state machine .
*/
struct iucv_event {
struct iucv_connection * conn ;
void * data ;
} ;
/**
* Private part of the network device structure
*/
struct netiucv_priv {
struct net_device_stats stats ;
unsigned long tbusy ;
fsm_instance * fsm ;
struct iucv_connection * conn ;
struct device * dev ;
2009-06-16 12:30:43 +04:00
int pm_state ;
2005-04-17 02:20:36 +04:00
} ;
/**
* Link level header for a packet .
*/
2007-02-09 00:50:33 +03:00
struct ll_header {
u16 next ;
} ;
2005-04-17 02:20:36 +04:00
2007-02-09 00:50:33 +03:00
# define NETIUCV_HDRLEN (sizeof(struct ll_header))
2011-12-20 02:56:34 +04:00
# define NETIUCV_BUFSIZE_MAX 65537
2005-04-17 02:20:36 +04:00
# define NETIUCV_BUFSIZE_DEFAULT NETIUCV_BUFSIZE_MAX
# define NETIUCV_MTU_MAX (NETIUCV_BUFSIZE_MAX - NETIUCV_HDRLEN)
# define NETIUCV_MTU_DEFAULT 9216
# define NETIUCV_QUEUELEN_DEFAULT 50
# define NETIUCV_TIMEOUT_5SEC 5000
/**
* Compatibility macros for busy handling
* of network devices .
*/
2007-02-09 00:50:33 +03:00
static inline void netiucv_clear_busy ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct netiucv_priv * priv = netdev_priv ( dev ) ;
clear_bit ( 0 , & priv - > tbusy ) ;
2005-04-17 02:20:36 +04:00
netif_wake_queue ( dev ) ;
}
2007-02-09 00:50:33 +03:00
static inline int netiucv_test_and_set_busy ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct netiucv_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
netif_stop_queue ( dev ) ;
2007-02-09 00:50:33 +03:00
return test_and_set_bit ( 0 , & priv - > tbusy ) ;
2005-04-17 02:20:36 +04:00
}
2011-12-20 02:56:34 +04:00
static u8 iucvMagic_ascii [ 16 ] = {
0x30 , 0x20 , 0x20 , 0x20 , 0x20 , 0x20 , 0x20 , 0x20 ,
0x30 , 0x20 , 0x20 , 0x20 , 0x20 , 0x20 , 0x20 , 0x20
} ;
static u8 iucvMagic_ebcdic [ 16 ] = {
2005-04-17 02:20:36 +04:00
0xF0 , 0x40 , 0x40 , 0x40 , 0x40 , 0x40 , 0x40 , 0x40 ,
0xF0 , 0x40 , 0x40 , 0x40 , 0x40 , 0x40 , 0x40 , 0x40
} ;
/**
* Convert an iucv userId to its printable
* form ( strip whitespace at end ) .
*
* @ param An iucv userId
*
* @ returns The printable string ( static data ! ! )
*/
2011-12-20 02:56:34 +04:00
static char * netiucv_printname ( char * name , int len )
2005-04-17 02:20:36 +04:00
{
2011-12-20 02:56:34 +04:00
static char tmp [ 17 ] ;
2005-04-17 02:20:36 +04:00
char * p = tmp ;
2011-12-20 02:56:34 +04:00
memcpy ( tmp , name , len ) ;
tmp [ len ] = ' \0 ' ;
while ( * p & & ( ( p - tmp ) < len ) & & ( ! isspace ( * p ) ) )
2005-04-17 02:20:36 +04:00
p + + ;
* p = ' \0 ' ;
return tmp ;
}
2006-05-27 05:58:38 +04:00
2011-12-20 02:56:34 +04:00
static char * netiucv_printuser ( struct iucv_connection * conn )
{
static char tmp_uid [ 9 ] ;
static char tmp_udat [ 17 ] ;
static char buf [ 100 ] ;
if ( memcmp ( conn - > userdata , iucvMagic_ebcdic , 16 ) ) {
tmp_uid [ 8 ] = ' \0 ' ;
tmp_udat [ 16 ] = ' \0 ' ;
memcpy ( tmp_uid , conn - > userid , 8 ) ;
memcpy ( tmp_uid , netiucv_printname ( tmp_uid , 8 ) , 8 ) ;
memcpy ( tmp_udat , conn - > userdata , 16 ) ;
EBCASC ( tmp_udat , 16 ) ;
memcpy ( tmp_udat , netiucv_printname ( tmp_udat , 16 ) , 16 ) ;
sprintf ( buf , " %s.%s " , tmp_uid , tmp_udat ) ;
return buf ;
} else
return netiucv_printname ( conn - > userid , 8 ) ;
}
2005-04-17 02:20:36 +04:00
/**
* States of the interface statemachine .
*/
enum dev_states {
DEV_STATE_STOPPED ,
DEV_STATE_STARTWAIT ,
DEV_STATE_STOPWAIT ,
DEV_STATE_RUNNING ,
/**
* MUST be always the last element ! !
*/
NR_DEV_STATES
} ;
static const char * dev_state_names [ ] = {
" Stopped " ,
" StartWait " ,
" StopWait " ,
" Running " ,
} ;
/**
* Events of the interface statemachine .
*/
enum dev_events {
DEV_EVENT_START ,
DEV_EVENT_STOP ,
DEV_EVENT_CONUP ,
DEV_EVENT_CONDOWN ,
/**
* MUST be always the last element ! !
*/
NR_DEV_EVENTS
} ;
static const char * dev_event_names [ ] = {
" Start " ,
" Stop " ,
" Connection up " ,
" Connection down " ,
} ;
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
/**
* Events of the connection statemachine
*/
enum conn_events {
/**
* Events , representing callbacks from
* lowlevel iucv layer )
*/
CONN_EVENT_CONN_REQ ,
CONN_EVENT_CONN_ACK ,
CONN_EVENT_CONN_REJ ,
CONN_EVENT_CONN_SUS ,
CONN_EVENT_CONN_RES ,
CONN_EVENT_RX ,
CONN_EVENT_TXDONE ,
/**
* Events , representing errors return codes from
* calls to lowlevel iucv layer
*/
/**
* Event , representing timer expiry .
*/
CONN_EVENT_TIMER ,
/**
* Events , representing commands from upper levels .
*/
CONN_EVENT_START ,
CONN_EVENT_STOP ,
/**
* MUST be always the last element ! !
*/
NR_CONN_EVENTS ,
} ;
static const char * conn_event_names [ ] = {
" Remote connection request " ,
" Remote connection acknowledge " ,
" Remote connection reject " ,
" Connection suspended " ,
" Connection resumed " ,
" Data received " ,
" Data sent " ,
" Timer " ,
" Start " ,
" Stop " ,
} ;
/**
* States of the connection statemachine .
*/
enum conn_states {
/**
* Connection not assigned to any device ,
* initial state , invalid
*/
CONN_STATE_INVALID ,
/**
* Userid assigned but not operating
*/
CONN_STATE_STOPPED ,
/**
* Connection registered ,
* no connection request sent yet ,
* no connection request received
*/
CONN_STATE_STARTWAIT ,
/**
* Connection registered and connection request sent ,
* no acknowledge and no connection request received yet .
*/
CONN_STATE_SETUPWAIT ,
/**
* Connection up and running idle
*/
CONN_STATE_IDLE ,
/**
* Data sent , awaiting CONN_EVENT_TXDONE
*/
CONN_STATE_TX ,
/**
* Error during registration .
*/
CONN_STATE_REGERR ,
/**
* Error during registration .
*/
CONN_STATE_CONNERR ,
/**
* MUST be always the last element ! !
*/
NR_CONN_STATES ,
} ;
static const char * conn_state_names [ ] = {
" Invalid " ,
" Stopped " ,
" StartWait " ,
" SetupWait " ,
" Idle " ,
" TX " ,
" Terminating " ,
" Registration error " ,
" Connect error " ,
} ;
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
/**
* Debug Facility Stuff
*/
static debug_info_t * iucv_dbf_setup = NULL ;
static debug_info_t * iucv_dbf_data = NULL ;
static debug_info_t * iucv_dbf_trace = NULL ;
DEFINE_PER_CPU ( char [ 256 ] , iucv_dbf_txt_buf ) ;
2007-02-09 00:50:33 +03:00
static void iucv_unregister_dbf_views ( void )
2005-04-17 02:20:36 +04:00
{
if ( iucv_dbf_setup )
debug_unregister ( iucv_dbf_setup ) ;
if ( iucv_dbf_data )
debug_unregister ( iucv_dbf_data ) ;
if ( iucv_dbf_trace )
debug_unregister ( iucv_dbf_trace ) ;
}
2007-02-09 00:50:33 +03:00
static int iucv_register_dbf_views ( void )
2005-04-17 02:20:36 +04:00
{
iucv_dbf_setup = debug_register ( IUCV_DBF_SETUP_NAME ,
2005-06-26 01:55:33 +04:00
IUCV_DBF_SETUP_PAGES ,
2005-04-17 02:20:36 +04:00
IUCV_DBF_SETUP_NR_AREAS ,
IUCV_DBF_SETUP_LEN ) ;
iucv_dbf_data = debug_register ( IUCV_DBF_DATA_NAME ,
2005-06-26 01:55:33 +04:00
IUCV_DBF_DATA_PAGES ,
2005-04-17 02:20:36 +04:00
IUCV_DBF_DATA_NR_AREAS ,
IUCV_DBF_DATA_LEN ) ;
iucv_dbf_trace = debug_register ( IUCV_DBF_TRACE_NAME ,
2005-06-26 01:55:33 +04:00
IUCV_DBF_TRACE_PAGES ,
2005-04-17 02:20:36 +04:00
IUCV_DBF_TRACE_NR_AREAS ,
IUCV_DBF_TRACE_LEN ) ;
if ( ( iucv_dbf_setup = = NULL ) | | ( iucv_dbf_data = = NULL ) | |
( iucv_dbf_trace = = NULL ) ) {
iucv_unregister_dbf_views ( ) ;
return - ENOMEM ;
}
debug_register_view ( iucv_dbf_setup , & debug_hex_ascii_view ) ;
debug_set_level ( iucv_dbf_setup , IUCV_DBF_SETUP_LEVEL ) ;
debug_register_view ( iucv_dbf_data , & debug_hex_ascii_view ) ;
debug_set_level ( iucv_dbf_data , IUCV_DBF_DATA_LEVEL ) ;
debug_register_view ( iucv_dbf_trace , & debug_hex_ascii_view ) ;
debug_set_level ( iucv_dbf_trace , IUCV_DBF_TRACE_LEVEL ) ;
return 0 ;
}
2007-02-09 00:50:33 +03:00
/*
2005-04-17 02:20:36 +04:00
* Callback - wrappers , called from lowlevel iucv layer .
2007-02-09 00:50:33 +03:00
*/
2005-04-17 02:20:36 +04:00
2007-02-09 00:50:33 +03:00
static void netiucv_callback_rx ( struct iucv_path * path ,
struct iucv_message * msg )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct iucv_connection * conn = path - > private ;
2005-04-17 02:20:36 +04:00
struct iucv_event ev ;
ev . conn = conn ;
2007-02-09 00:50:33 +03:00
ev . data = msg ;
2005-04-17 02:20:36 +04:00
fsm_event ( conn - > fsm , CONN_EVENT_RX , & ev ) ;
}
2007-02-09 00:50:33 +03:00
static void netiucv_callback_txdone ( struct iucv_path * path ,
struct iucv_message * msg )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct iucv_connection * conn = path - > private ;
2005-04-17 02:20:36 +04:00
struct iucv_event ev ;
ev . conn = conn ;
2007-02-09 00:50:33 +03:00
ev . data = msg ;
2005-04-17 02:20:36 +04:00
fsm_event ( conn - > fsm , CONN_EVENT_TXDONE , & ev ) ;
}
2007-02-09 00:50:33 +03:00
static void netiucv_callback_connack ( struct iucv_path * path , u8 ipuser [ 16 ] )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct iucv_connection * conn = path - > private ;
2005-04-17 02:20:36 +04:00
2007-02-09 00:50:33 +03:00
fsm_event ( conn - > fsm , CONN_EVENT_CONN_ACK , conn ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-09 00:50:33 +03:00
static int netiucv_callback_connreq ( struct iucv_path * path ,
u8 ipvmid [ 8 ] , u8 ipuser [ 16 ] )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct iucv_connection * conn = path - > private ;
2005-04-17 02:20:36 +04:00
struct iucv_event ev ;
2011-12-20 02:56:34 +04:00
static char tmp_user [ 9 ] ;
static char tmp_udat [ 17 ] ;
2007-02-09 00:50:33 +03:00
int rc ;
2005-04-17 02:20:36 +04:00
2007-02-09 00:50:33 +03:00
rc = - EINVAL ;
2011-12-20 02:56:34 +04:00
memcpy ( tmp_user , netiucv_printname ( ipvmid , 8 ) , 8 ) ;
memcpy ( tmp_udat , ipuser , 16 ) ;
EBCASC ( tmp_udat , 16 ) ;
2007-02-09 00:50:33 +03:00
read_lock_bh ( & iucv_connection_rwlock ) ;
list_for_each_entry ( conn , & iucv_connection_list , list ) {
2011-12-20 02:56:34 +04:00
if ( strncmp ( ipvmid , conn - > userid , 8 ) | |
strncmp ( ipuser , conn - > userdata , 16 ) )
2007-02-09 00:50:33 +03:00
continue ;
/* Found a matching connection for this path. */
conn - > path = path ;
ev . conn = conn ;
ev . data = path ;
fsm_event ( conn - > fsm , CONN_EVENT_CONN_REQ , & ev ) ;
rc = 0 ;
}
2011-12-20 02:56:34 +04:00
IUCV_DBF_TEXT_ ( setup , 2 , " Connection requested for %s.%s \n " ,
tmp_user , netiucv_printname ( tmp_udat , 16 ) ) ;
2007-02-09 00:50:33 +03:00
read_unlock_bh ( & iucv_connection_rwlock ) ;
return rc ;
2005-04-17 02:20:36 +04:00
}
2007-02-09 00:50:33 +03:00
static void netiucv_callback_connrej ( struct iucv_path * path , u8 ipuser [ 16 ] )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct iucv_connection * conn = path - > private ;
2005-04-17 02:20:36 +04:00
2007-02-09 00:50:33 +03:00
fsm_event ( conn - > fsm , CONN_EVENT_CONN_REJ , conn ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-09 00:50:33 +03:00
static void netiucv_callback_connsusp ( struct iucv_path * path , u8 ipuser [ 16 ] )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct iucv_connection * conn = path - > private ;
2005-04-17 02:20:36 +04:00
2007-02-09 00:50:33 +03:00
fsm_event ( conn - > fsm , CONN_EVENT_CONN_SUS , conn ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-09 00:50:33 +03:00
static void netiucv_callback_connres ( struct iucv_path * path , u8 ipuser [ 16 ] )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct iucv_connection * conn = path - > private ;
2005-04-17 02:20:36 +04:00
2007-02-09 00:50:33 +03:00
fsm_event ( conn - > fsm , CONN_EVENT_CONN_RES , conn ) ;
}
2005-04-17 02:20:36 +04:00
/**
2008-02-08 15:09:03 +03:00
* NOP action for statemachines
2005-04-17 02:20:36 +04:00
*/
2008-02-08 15:09:03 +03:00
static void netiucv_action_nop ( fsm_instance * fi , int event , void * arg )
2005-04-17 02:20:36 +04:00
{
}
2006-05-27 05:58:38 +04:00
2007-02-09 00:50:33 +03:00
/*
2005-04-17 02:20:36 +04:00
* Actions of the connection statemachine
2007-02-09 00:50:33 +03:00
*/
2005-04-17 02:20:36 +04:00
/**
2007-02-09 00:50:33 +03:00
* netiucv_unpack_skb
* @ conn : The connection where this skb has been received .
* @ pskb : The received skb .
2005-04-17 02:20:36 +04:00
*
2007-02-09 00:50:33 +03:00
* Unpack a just received skb and hand it over to upper layers .
* Helper function for conn_action_rx .
2005-04-17 02:20:36 +04:00
*/
2007-02-09 00:50:33 +03:00
static void netiucv_unpack_skb ( struct iucv_connection * conn ,
struct sk_buff * pskb )
2005-04-17 02:20:36 +04:00
{
struct net_device * dev = conn - > netdev ;
2007-02-09 00:50:33 +03:00
struct netiucv_priv * privptr = netdev_priv ( dev ) ;
u16 offset = 0 ;
2005-04-17 02:20:36 +04:00
skb_put ( pskb , NETIUCV_HDRLEN ) ;
pskb - > dev = dev ;
pskb - > ip_summed = CHECKSUM_NONE ;
pskb - > protocol = ntohs ( ETH_P_IP ) ;
while ( 1 ) {
struct sk_buff * skb ;
2007-02-09 00:50:33 +03:00
struct ll_header * header = ( struct ll_header * ) pskb - > data ;
2005-04-17 02:20:36 +04:00
if ( ! header - > next )
break ;
skb_pull ( pskb , NETIUCV_HDRLEN ) ;
header - > next - = offset ;
offset + = header - > next ;
header - > next - = NETIUCV_HDRLEN ;
if ( skb_tailroom ( pskb ) < header - > next ) {
IUCV_DBF_TEXT_ ( data , 2 , " Illegal next field: %d > %d \n " ,
header - > next , skb_tailroom ( pskb ) ) ;
return ;
}
skb_put ( pskb , header - > next ) ;
2007-03-20 01:30:44 +03:00
skb_reset_mac_header ( pskb ) ;
2005-04-17 02:20:36 +04:00
skb = dev_alloc_skb ( pskb - > len ) ;
if ( ! skb ) {
IUCV_DBF_TEXT ( data , 2 ,
" Out of memory in netiucv_unpack_skb \n " ) ;
privptr - > stats . rx_dropped + + ;
return ;
}
2007-03-28 01:55:52 +04:00
skb_copy_from_linear_data ( pskb , skb_put ( skb , pskb - > len ) ,
pskb - > len ) ;
2007-03-20 01:30:44 +03:00
skb_reset_mac_header ( skb ) ;
2005-04-17 02:20:36 +04:00
skb - > dev = pskb - > dev ;
skb - > protocol = pskb - > protocol ;
pskb - > ip_summed = CHECKSUM_UNNECESSARY ;
2007-12-11 04:17:37 +03:00
privptr - > stats . rx_packets + + ;
privptr - > stats . rx_bytes + = skb - > len ;
2005-04-17 02:20:36 +04:00
/*
* Since receiving is always initiated from a tasklet ( in iucv . c ) ,
* we must use netif_rx_ni ( ) instead of netif_rx ( )
*/
netif_rx_ni ( skb ) ;
skb_pull ( pskb , header - > next ) ;
skb_put ( pskb , NETIUCV_HDRLEN ) ;
}
}
2007-02-09 00:50:33 +03:00
static void conn_action_rx ( fsm_instance * fi , int event , void * arg )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct iucv_event * ev = arg ;
2005-04-17 02:20:36 +04:00
struct iucv_connection * conn = ev - > conn ;
2007-02-09 00:50:33 +03:00
struct iucv_message * msg = ev - > data ;
struct netiucv_priv * privptr = netdev_priv ( conn - > netdev ) ;
2005-04-17 02:20:36 +04:00
int rc ;
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 4 , __func__ ) ;
2005-04-17 02:20:36 +04:00
if ( ! conn - > netdev ) {
2007-02-09 00:50:33 +03:00
iucv_message_reject ( conn - > path , msg ) ;
2005-04-17 02:20:36 +04:00
IUCV_DBF_TEXT ( data , 2 ,
2007-02-09 00:50:33 +03:00
" Received data for unlinked connection \n " ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2007-02-09 00:50:33 +03:00
if ( msg - > length > conn - > max_buffsize ) {
iucv_message_reject ( conn - > path , msg ) ;
2005-04-17 02:20:36 +04:00
privptr - > stats . rx_dropped + + ;
IUCV_DBF_TEXT_ ( data , 2 , " msglen %d > max_buffsize %d \n " ,
2007-02-09 00:50:33 +03:00
msg - > length , conn - > max_buffsize ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2007-04-20 07:29:13 +04:00
conn - > rx_buff - > data = conn - > rx_buff - > head ;
skb_reset_tail_pointer ( conn - > rx_buff ) ;
2005-04-17 02:20:36 +04:00
conn - > rx_buff - > len = 0 ;
2007-02-09 00:50:33 +03:00
rc = iucv_message_receive ( conn - > path , msg , 0 , conn - > rx_buff - > data ,
msg - > length , NULL ) ;
if ( rc | | msg - > length < 5 ) {
2005-04-17 02:20:36 +04:00
privptr - > stats . rx_errors + + ;
IUCV_DBF_TEXT_ ( data , 2 , " rc %d from iucv_receive \n " , rc ) ;
return ;
}
netiucv_unpack_skb ( conn , conn - > rx_buff ) ;
}
2007-02-09 00:50:33 +03:00
static void conn_action_txdone ( fsm_instance * fi , int event , void * arg )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct iucv_event * ev = arg ;
2005-04-17 02:20:36 +04:00
struct iucv_connection * conn = ev - > conn ;
2007-02-09 00:50:33 +03:00
struct iucv_message * msg = ev - > data ;
struct iucv_message txmsg ;
2005-04-17 02:20:36 +04:00
struct netiucv_priv * privptr = NULL ;
2007-02-09 00:50:33 +03:00
u32 single_flag = msg - > tag ;
u32 txbytes = 0 ;
u32 txpackets = 0 ;
u32 stat_maxcq = 0 ;
2005-04-17 02:20:36 +04:00
struct sk_buff * skb ;
unsigned long saveflags ;
2007-02-09 00:50:33 +03:00
struct ll_header header ;
int rc ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 4 , __func__ ) ;
2005-04-17 02:20:36 +04:00
2007-02-09 00:50:33 +03:00
if ( conn & & conn - > netdev )
privptr = netdev_priv ( conn - > netdev ) ;
2005-04-17 02:20:36 +04:00
conn - > prof . tx_pending - - ;
if ( single_flag ) {
if ( ( skb = skb_dequeue ( & conn - > commit_queue ) ) ) {
atomic_dec ( & skb - > users ) ;
if ( privptr ) {
privptr - > stats . tx_packets + + ;
privptr - > stats . tx_bytes + =
( skb - > len - NETIUCV_HDRLEN
2009-11-13 00:46:30 +03:00
- NETIUCV_HDRLEN ) ;
2005-04-17 02:20:36 +04:00
}
2009-11-13 00:46:30 +03:00
dev_kfree_skb_any ( skb ) ;
2005-04-17 02:20:36 +04:00
}
}
2007-04-20 07:29:13 +04:00
conn - > tx_buff - > data = conn - > tx_buff - > head ;
skb_reset_tail_pointer ( conn - > tx_buff ) ;
2005-04-17 02:20:36 +04:00
conn - > tx_buff - > len = 0 ;
spin_lock_irqsave ( & conn - > collect_lock , saveflags ) ;
while ( ( skb = skb_dequeue ( & conn - > collect_queue ) ) ) {
header . next = conn - > tx_buff - > len + skb - > len + NETIUCV_HDRLEN ;
memcpy ( skb_put ( conn - > tx_buff , NETIUCV_HDRLEN ) , & header ,
NETIUCV_HDRLEN ) ;
2007-03-28 01:55:52 +04:00
skb_copy_from_linear_data ( skb ,
skb_put ( conn - > tx_buff , skb - > len ) ,
skb - > len ) ;
2005-04-17 02:20:36 +04:00
txbytes + = skb - > len ;
txpackets + + ;
stat_maxcq + + ;
atomic_dec ( & skb - > users ) ;
dev_kfree_skb_any ( skb ) ;
}
if ( conn - > collect_len > conn - > prof . maxmulti )
conn - > prof . maxmulti = conn - > collect_len ;
conn - > collect_len = 0 ;
spin_unlock_irqrestore ( & conn - > collect_lock , saveflags ) ;
2007-02-09 00:50:33 +03:00
if ( conn - > tx_buff - > len = = 0 ) {
fsm_newstate ( fi , CONN_STATE_IDLE ) ;
return ;
}
2005-04-17 02:20:36 +04:00
2007-02-09 00:50:33 +03:00
header . next = 0 ;
memcpy ( skb_put ( conn - > tx_buff , NETIUCV_HDRLEN ) , & header , NETIUCV_HDRLEN ) ;
2007-07-25 04:47:43 +04:00
conn - > prof . send_stamp = current_kernel_time ( ) ;
2007-02-09 00:50:33 +03:00
txmsg . class = 0 ;
txmsg . tag = 0 ;
rc = iucv_message_send ( conn - > path , & txmsg , 0 , 0 ,
2005-04-17 02:20:36 +04:00
conn - > tx_buff - > data , conn - > tx_buff - > len ) ;
2007-02-09 00:50:33 +03:00
conn - > prof . doios_multi + + ;
conn - > prof . txlen + = conn - > tx_buff - > len ;
conn - > prof . tx_pending + + ;
if ( conn - > prof . tx_pending > conn - > prof . tx_max_pending )
conn - > prof . tx_max_pending = conn - > prof . tx_pending ;
if ( rc ) {
conn - > prof . tx_pending - - ;
2005-04-17 02:20:36 +04:00
fsm_newstate ( fi , CONN_STATE_IDLE ) ;
2007-02-09 00:50:33 +03:00
if ( privptr )
privptr - > stats . tx_errors + = txpackets ;
IUCV_DBF_TEXT_ ( data , 2 , " rc %d from iucv_send \n " , rc ) ;
} else {
if ( privptr ) {
privptr - > stats . tx_packets + = txpackets ;
privptr - > stats . tx_bytes + = txbytes ;
}
if ( stat_maxcq > conn - > prof . maxcqueue )
conn - > prof . maxcqueue = stat_maxcq ;
}
2005-04-17 02:20:36 +04:00
}
2007-02-09 00:50:33 +03:00
static void conn_action_connaccept ( fsm_instance * fi , int event , void * arg )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct iucv_event * ev = arg ;
2005-04-17 02:20:36 +04:00
struct iucv_connection * conn = ev - > conn ;
2007-02-09 00:50:33 +03:00
struct iucv_path * path = ev - > data ;
2005-04-17 02:20:36 +04:00
struct net_device * netdev = conn - > netdev ;
2007-02-09 00:50:33 +03:00
struct netiucv_priv * privptr = netdev_priv ( netdev ) ;
2005-04-17 02:20:36 +04:00
int rc ;
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2005-04-17 02:20:36 +04:00
2007-02-09 00:50:33 +03:00
conn - > path = path ;
path - > msglim = NETIUCV_QUEUELEN_DEFAULT ;
path - > flags = 0 ;
2011-12-20 02:56:34 +04:00
rc = iucv_path_accept ( path , & netiucv_handler , conn - > userdata , conn ) ;
2005-04-17 02:20:36 +04:00
if ( rc ) {
IUCV_DBF_TEXT_ ( setup , 2 , " rc %d from iucv_accept " , rc ) ;
return ;
}
fsm_newstate ( fi , CONN_STATE_IDLE ) ;
2007-02-09 00:50:33 +03:00
netdev - > tx_queue_len = conn - > path - > msglim ;
2005-04-17 02:20:36 +04:00
fsm_event ( privptr - > fsm , DEV_EVENT_CONUP , netdev ) ;
}
2007-02-09 00:50:33 +03:00
static void conn_action_connreject ( fsm_instance * fi , int event , void * arg )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct iucv_event * ev = arg ;
struct iucv_path * path = ev - > data ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2007-02-09 00:50:33 +03:00
iucv_path_sever ( path , NULL ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-09 00:50:33 +03:00
static void conn_action_connack ( fsm_instance * fi , int event , void * arg )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct iucv_connection * conn = arg ;
2005-04-17 02:20:36 +04:00
struct net_device * netdev = conn - > netdev ;
2007-02-09 00:50:33 +03:00
struct netiucv_priv * privptr = netdev_priv ( netdev ) ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2005-04-17 02:20:36 +04:00
fsm_deltimer ( & conn - > timer ) ;
fsm_newstate ( fi , CONN_STATE_IDLE ) ;
2007-02-09 00:50:33 +03:00
netdev - > tx_queue_len = conn - > path - > msglim ;
2005-04-17 02:20:36 +04:00
fsm_event ( privptr - > fsm , DEV_EVENT_CONUP , netdev ) ;
}
2007-02-09 00:50:33 +03:00
static void conn_action_conntimsev ( fsm_instance * fi , int event , void * arg )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct iucv_connection * conn = arg ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2005-04-17 02:20:36 +04:00
fsm_deltimer ( & conn - > timer ) ;
2011-12-20 02:56:34 +04:00
iucv_path_sever ( conn - > path , conn - > userdata ) ;
2005-04-17 02:20:36 +04:00
fsm_newstate ( fi , CONN_STATE_STARTWAIT ) ;
}
2007-02-09 00:50:33 +03:00
static void conn_action_connsever ( fsm_instance * fi , int event , void * arg )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct iucv_connection * conn = arg ;
2005-04-17 02:20:36 +04:00
struct net_device * netdev = conn - > netdev ;
2007-02-09 00:50:33 +03:00
struct netiucv_priv * privptr = netdev_priv ( netdev ) ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2005-04-17 02:20:36 +04:00
fsm_deltimer ( & conn - > timer ) ;
2011-12-20 02:56:34 +04:00
iucv_path_sever ( conn - > path , conn - > userdata ) ;
dev_info ( privptr - > dev , " The peer z/VM guest %s has closed the "
" connection \n " , netiucv_printuser ( conn ) ) ;
2005-04-17 02:20:36 +04:00
IUCV_DBF_TEXT ( data , 2 ,
2007-02-09 00:50:33 +03:00
" conn_action_connsever: Remote dropped connection \n " ) ;
2005-04-17 02:20:36 +04:00
fsm_newstate ( fi , CONN_STATE_STARTWAIT ) ;
fsm_event ( privptr - > fsm , DEV_EVENT_CONDOWN , netdev ) ;
}
2007-02-09 00:50:33 +03:00
static void conn_action_start ( fsm_instance * fi , int event , void * arg )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct iucv_connection * conn = arg ;
2008-12-25 15:39:47 +03:00
struct net_device * netdev = conn - > netdev ;
struct netiucv_priv * privptr = netdev_priv ( netdev ) ;
2005-04-17 02:20:36 +04:00
int rc ;
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2005-04-17 02:20:36 +04:00
2007-02-09 00:50:33 +03:00
fsm_newstate ( fi , CONN_STATE_STARTWAIT ) ;
2005-04-17 02:20:36 +04:00
2007-02-09 00:50:33 +03:00
/*
* We must set the state before calling iucv_connect because the
* callback handler could be called at any point after the connection
* request is sent
*/
2005-04-17 02:20:36 +04:00
fsm_newstate ( fi , CONN_STATE_SETUPWAIT ) ;
2007-02-09 00:50:33 +03:00
conn - > path = iucv_path_alloc ( NETIUCV_QUEUELEN_DEFAULT , 0 , GFP_KERNEL ) ;
2011-12-20 02:56:34 +04:00
IUCV_DBF_TEXT_ ( setup , 2 , " %s: connecting to %s ... \n " ,
netdev - > name , netiucv_printuser ( conn ) ) ;
2007-02-09 00:50:33 +03:00
rc = iucv_path_connect ( conn - > path , & netiucv_handler , conn - > userid ,
2011-12-20 02:56:34 +04:00
NULL , conn - > userdata , conn ) ;
2005-04-17 02:20:36 +04:00
switch ( rc ) {
2007-02-09 00:50:33 +03:00
case 0 :
2008-12-25 15:39:47 +03:00
netdev - > tx_queue_len = conn - > path - > msglim ;
2007-02-09 00:50:33 +03:00
fsm_addtimer ( & conn - > timer , NETIUCV_TIMEOUT_5SEC ,
CONN_EVENT_TIMER , conn ) ;
return ;
case 11 :
2008-12-25 15:39:47 +03:00
dev_warn ( privptr - > dev ,
" The IUCV device failed to connect to z/VM guest %s \n " ,
2011-12-20 02:56:34 +04:00
netiucv_printname ( conn - > userid , 8 ) ) ;
2007-02-09 00:50:33 +03:00
fsm_newstate ( fi , CONN_STATE_STARTWAIT ) ;
break ;
case 12 :
2008-12-25 15:39:47 +03:00
dev_warn ( privptr - > dev ,
" The IUCV device failed to connect to the peer on z/VM "
2011-12-20 02:56:34 +04:00
" guest %s \n " , netiucv_printname ( conn - > userid , 8 ) ) ;
2007-02-09 00:50:33 +03:00
fsm_newstate ( fi , CONN_STATE_STARTWAIT ) ;
break ;
case 13 :
2008-12-25 15:39:47 +03:00
dev_err ( privptr - > dev ,
" Connecting the IUCV device would exceed the maximum "
" number of IUCV connections \n " ) ;
2007-02-09 00:50:33 +03:00
fsm_newstate ( fi , CONN_STATE_CONNERR ) ;
break ;
case 14 :
2008-12-25 15:39:47 +03:00
dev_err ( privptr - > dev ,
" z/VM guest %s has too many IUCV connections "
" to connect with the IUCV device \n " ,
2011-12-20 02:56:34 +04:00
netiucv_printname ( conn - > userid , 8 ) ) ;
2007-02-09 00:50:33 +03:00
fsm_newstate ( fi , CONN_STATE_CONNERR ) ;
break ;
case 15 :
2008-12-25 15:39:47 +03:00
dev_err ( privptr - > dev ,
" The IUCV device cannot connect to a z/VM guest with no "
" IUCV authorization \n " ) ;
2007-02-09 00:50:33 +03:00
fsm_newstate ( fi , CONN_STATE_CONNERR ) ;
break ;
default :
2008-12-25 15:39:47 +03:00
dev_err ( privptr - > dev ,
" Connecting the IUCV device failed with error %d \n " ,
rc ) ;
2007-02-09 00:50:33 +03:00
fsm_newstate ( fi , CONN_STATE_CONNERR ) ;
break ;
2005-04-17 02:20:36 +04:00
}
IUCV_DBF_TEXT_ ( setup , 5 , " iucv_connect rc is %d \n " , rc ) ;
2007-02-09 00:50:33 +03:00
kfree ( conn - > path ) ;
conn - > path = NULL ;
2005-04-17 02:20:36 +04:00
}
2007-02-09 00:50:33 +03:00
static void netiucv_purge_skb_queue ( struct sk_buff_head * q )
2005-04-17 02:20:36 +04:00
{
struct sk_buff * skb ;
while ( ( skb = skb_dequeue ( q ) ) ) {
atomic_dec ( & skb - > users ) ;
dev_kfree_skb_any ( skb ) ;
}
}
2007-02-09 00:50:33 +03:00
static void conn_action_stop ( fsm_instance * fi , int event , void * arg )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct iucv_event * ev = arg ;
2005-04-17 02:20:36 +04:00
struct iucv_connection * conn = ev - > conn ;
struct net_device * netdev = conn - > netdev ;
2007-02-09 00:50:33 +03:00
struct netiucv_priv * privptr = netdev_priv ( netdev ) ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2005-04-17 02:20:36 +04:00
fsm_deltimer ( & conn - > timer ) ;
fsm_newstate ( fi , CONN_STATE_STOPPED ) ;
netiucv_purge_skb_queue ( & conn - > collect_queue ) ;
2007-02-09 00:50:33 +03:00
if ( conn - > path ) {
IUCV_DBF_TEXT ( trace , 5 , " calling iucv_path_sever \n " ) ;
2011-12-20 02:56:34 +04:00
iucv_path_sever ( conn - > path , conn - > userdata ) ;
2007-02-09 00:50:33 +03:00
kfree ( conn - > path ) ;
conn - > path = NULL ;
}
2005-04-17 02:20:36 +04:00
netiucv_purge_skb_queue ( & conn - > commit_queue ) ;
fsm_event ( privptr - > fsm , DEV_EVENT_CONDOWN , netdev ) ;
}
2007-02-09 00:50:33 +03:00
static void conn_action_inval ( fsm_instance * fi , int event , void * arg )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct iucv_connection * conn = arg ;
2005-04-17 02:20:36 +04:00
struct net_device * netdev = conn - > netdev ;
2008-07-14 11:59:30 +04:00
IUCV_DBF_TEXT_ ( data , 2 , " %s('%s'): conn_action_inval called \n " ,
netdev - > name , conn - > userid ) ;
2005-04-17 02:20:36 +04:00
}
static const fsm_node conn_fsm [ ] = {
{ CONN_STATE_INVALID , CONN_EVENT_START , conn_action_inval } ,
{ CONN_STATE_STOPPED , CONN_EVENT_START , conn_action_start } ,
{ CONN_STATE_STOPPED , CONN_EVENT_STOP , conn_action_stop } ,
{ CONN_STATE_STARTWAIT , CONN_EVENT_STOP , conn_action_stop } ,
{ CONN_STATE_SETUPWAIT , CONN_EVENT_STOP , conn_action_stop } ,
{ CONN_STATE_IDLE , CONN_EVENT_STOP , conn_action_stop } ,
{ CONN_STATE_TX , CONN_EVENT_STOP , conn_action_stop } ,
{ CONN_STATE_REGERR , CONN_EVENT_STOP , conn_action_stop } ,
{ CONN_STATE_CONNERR , CONN_EVENT_STOP , conn_action_stop } ,
{ CONN_STATE_STOPPED , CONN_EVENT_CONN_REQ , conn_action_connreject } ,
{ CONN_STATE_STARTWAIT , CONN_EVENT_CONN_REQ , conn_action_connaccept } ,
{ CONN_STATE_SETUPWAIT , CONN_EVENT_CONN_REQ , conn_action_connaccept } ,
{ CONN_STATE_IDLE , CONN_EVENT_CONN_REQ , conn_action_connreject } ,
{ CONN_STATE_TX , CONN_EVENT_CONN_REQ , conn_action_connreject } ,
{ CONN_STATE_SETUPWAIT , CONN_EVENT_CONN_ACK , conn_action_connack } ,
{ CONN_STATE_SETUPWAIT , CONN_EVENT_TIMER , conn_action_conntimsev } ,
{ CONN_STATE_SETUPWAIT , CONN_EVENT_CONN_REJ , conn_action_connsever } ,
{ CONN_STATE_IDLE , CONN_EVENT_CONN_REJ , conn_action_connsever } ,
{ CONN_STATE_TX , CONN_EVENT_CONN_REJ , conn_action_connsever } ,
{ CONN_STATE_IDLE , CONN_EVENT_RX , conn_action_rx } ,
{ CONN_STATE_TX , CONN_EVENT_RX , conn_action_rx } ,
{ CONN_STATE_TX , CONN_EVENT_TXDONE , conn_action_txdone } ,
{ CONN_STATE_IDLE , CONN_EVENT_TXDONE , conn_action_txdone } ,
} ;
static const int CONN_FSM_LEN = sizeof ( conn_fsm ) / sizeof ( fsm_node ) ;
2006-05-27 05:58:38 +04:00
2007-02-09 00:50:33 +03:00
/*
2005-04-17 02:20:36 +04:00
* Actions for interface - statemachine .
2007-02-09 00:50:33 +03:00
*/
2005-04-17 02:20:36 +04:00
/**
2007-02-09 00:50:33 +03:00
* dev_action_start
* @ fi : An instance of an interface statemachine .
* @ event : The event , just happened .
* @ arg : Generic pointer , casted from struct net_device * upon call .
2005-04-17 02:20:36 +04:00
*
2007-02-09 00:50:33 +03:00
* Startup connection by sending CONN_EVENT_START to it .
2005-04-17 02:20:36 +04:00
*/
2007-02-09 00:50:33 +03:00
static void dev_action_start ( fsm_instance * fi , int event , void * arg )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct net_device * dev = arg ;
struct netiucv_priv * privptr = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2005-04-17 02:20:36 +04:00
fsm_newstate ( fi , DEV_STATE_STARTWAIT ) ;
2007-02-09 00:50:33 +03:00
fsm_event ( privptr - > conn - > fsm , CONN_EVENT_START , privptr - > conn ) ;
2005-04-17 02:20:36 +04:00
}
/**
* Shutdown connection by sending CONN_EVENT_STOP to it .
*
* @ param fi An instance of an interface statemachine .
* @ param event The event , just happened .
* @ param arg Generic pointer , casted from struct net_device * upon call .
*/
static void
dev_action_stop ( fsm_instance * fi , int event , void * arg )
{
2007-02-09 00:50:33 +03:00
struct net_device * dev = arg ;
struct netiucv_priv * privptr = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
struct iucv_event ev ;
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2005-04-17 02:20:36 +04:00
ev . conn = privptr - > conn ;
fsm_newstate ( fi , DEV_STATE_STOPWAIT ) ;
fsm_event ( privptr - > conn - > fsm , CONN_EVENT_STOP , & ev ) ;
}
/**
* Called from connection statemachine
* when a connection is up and running .
*
* @ param fi An instance of an interface statemachine .
* @ param event The event , just happened .
* @ param arg Generic pointer , casted from struct net_device * upon call .
*/
static void
dev_action_connup ( fsm_instance * fi , int event , void * arg )
{
2007-02-09 00:50:33 +03:00
struct net_device * dev = arg ;
struct netiucv_priv * privptr = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2005-04-17 02:20:36 +04:00
switch ( fsm_getstate ( fi ) ) {
case DEV_STATE_STARTWAIT :
fsm_newstate ( fi , DEV_STATE_RUNNING ) ;
2008-12-25 15:39:47 +03:00
dev_info ( privptr - > dev ,
" The IUCV device has been connected "
2011-12-20 02:56:34 +04:00
" successfully to %s \n " ,
netiucv_printuser ( privptr - > conn ) ) ;
2005-04-17 02:20:36 +04:00
IUCV_DBF_TEXT ( setup , 3 ,
" connection is up and running \n " ) ;
break ;
case DEV_STATE_STOPWAIT :
IUCV_DBF_TEXT ( data , 2 ,
" dev_action_connup: in DEV_STATE_STOPWAIT \n " ) ;
break ;
}
}
/**
* Called from connection statemachine
* when a connection has been shutdown .
*
* @ param fi An instance of an interface statemachine .
* @ param event The event , just happened .
* @ param arg Generic pointer , casted from struct net_device * upon call .
*/
static void
dev_action_conndown ( fsm_instance * fi , int event , void * arg )
{
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2005-04-17 02:20:36 +04:00
switch ( fsm_getstate ( fi ) ) {
case DEV_STATE_RUNNING :
fsm_newstate ( fi , DEV_STATE_STARTWAIT ) ;
break ;
case DEV_STATE_STOPWAIT :
fsm_newstate ( fi , DEV_STATE_STOPPED ) ;
IUCV_DBF_TEXT ( setup , 3 , " connection is down \n " ) ;
break ;
}
}
static const fsm_node dev_fsm [ ] = {
{ DEV_STATE_STOPPED , DEV_EVENT_START , dev_action_start } ,
{ DEV_STATE_STOPWAIT , DEV_EVENT_START , dev_action_start } ,
{ DEV_STATE_STOPWAIT , DEV_EVENT_CONDOWN , dev_action_conndown } ,
{ DEV_STATE_STARTWAIT , DEV_EVENT_STOP , dev_action_stop } ,
{ DEV_STATE_STARTWAIT , DEV_EVENT_CONUP , dev_action_connup } ,
{ DEV_STATE_RUNNING , DEV_EVENT_STOP , dev_action_stop } ,
{ DEV_STATE_RUNNING , DEV_EVENT_CONDOWN , dev_action_conndown } ,
2008-02-08 15:09:03 +03:00
{ DEV_STATE_RUNNING , DEV_EVENT_CONUP , netiucv_action_nop } ,
2005-04-17 02:20:36 +04:00
} ;
static const int DEV_FSM_LEN = sizeof ( dev_fsm ) / sizeof ( fsm_node ) ;
/**
* Transmit a packet .
* This is a helper function for netiucv_tx ( ) .
*
* @ param conn Connection to be used for sending .
* @ param skb Pointer to struct sk_buff of packet to send .
* The linklevel header has already been set up
* by netiucv_tx ( ) .
*
* @ return 0 on success , - ERRNO on failure . ( Never fails . )
*/
2007-02-09 00:50:33 +03:00
static int netiucv_transmit_skb ( struct iucv_connection * conn ,
struct sk_buff * skb )
{
struct iucv_message msg ;
2005-04-17 02:20:36 +04:00
unsigned long saveflags ;
2007-02-09 00:50:33 +03:00
struct ll_header header ;
int rc ;
2005-04-17 02:20:36 +04:00
if ( fsm_getstate ( conn - > fsm ) ! = CONN_STATE_IDLE ) {
int l = skb - > len + NETIUCV_HDRLEN ;
spin_lock_irqsave ( & conn - > collect_lock , saveflags ) ;
if ( conn - > collect_len + l >
( conn - > max_buffsize - NETIUCV_HDRLEN ) ) {
rc = - EBUSY ;
IUCV_DBF_TEXT ( data , 2 ,
2007-02-09 00:50:33 +03:00
" EBUSY from netiucv_transmit_skb \n " ) ;
2005-04-17 02:20:36 +04:00
} else {
atomic_inc ( & skb - > users ) ;
skb_queue_tail ( & conn - > collect_queue , skb ) ;
conn - > collect_len + = l ;
2007-02-09 00:50:33 +03:00
rc = 0 ;
2005-04-17 02:20:36 +04:00
}
spin_unlock_irqrestore ( & conn - > collect_lock , saveflags ) ;
} else {
struct sk_buff * nskb = skb ;
/**
* Copy the skb to a new allocated skb in lowmem only if the
* data is located above 2 G in memory or tailroom is < 2.
*/
2007-04-20 07:29:13 +04:00
unsigned long hi = ( ( unsigned long ) ( skb_tail_pointer ( skb ) +
NETIUCV_HDRLEN ) ) > > 31 ;
2005-04-17 02:20:36 +04:00
int copied = 0 ;
if ( hi | | ( skb_tailroom ( skb ) < 2 ) ) {
nskb = alloc_skb ( skb - > len + NETIUCV_HDRLEN +
NETIUCV_HDRLEN , GFP_ATOMIC | GFP_DMA ) ;
if ( ! nskb ) {
IUCV_DBF_TEXT ( data , 2 , " alloc_skb failed \n " ) ;
rc = - ENOMEM ;
return rc ;
} else {
skb_reserve ( nskb , NETIUCV_HDRLEN ) ;
memcpy ( skb_put ( nskb , skb - > len ) ,
skb - > data , skb - > len ) ;
}
copied = 1 ;
}
/**
* skb now is below 2 G and has enough room . Add headers .
*/
header . next = nskb - > len + NETIUCV_HDRLEN ;
memcpy ( skb_push ( nskb , NETIUCV_HDRLEN ) , & header , NETIUCV_HDRLEN ) ;
header . next = 0 ;
memcpy ( skb_put ( nskb , NETIUCV_HDRLEN ) , & header , NETIUCV_HDRLEN ) ;
fsm_newstate ( conn - > fsm , CONN_STATE_TX ) ;
2007-07-25 04:47:43 +04:00
conn - > prof . send_stamp = current_kernel_time ( ) ;
2006-05-27 05:58:38 +04:00
2007-02-09 00:50:33 +03:00
msg . tag = 1 ;
msg . class = 0 ;
rc = iucv_message_send ( conn - > path , & msg , 0 , 0 ,
nskb - > data , nskb - > len ) ;
2005-04-17 02:20:36 +04:00
conn - > prof . doios_single + + ;
conn - > prof . txlen + = skb - > len ;
conn - > prof . tx_pending + + ;
if ( conn - > prof . tx_pending > conn - > prof . tx_max_pending )
conn - > prof . tx_max_pending = conn - > prof . tx_pending ;
if ( rc ) {
struct netiucv_priv * privptr ;
fsm_newstate ( conn - > fsm , CONN_STATE_IDLE ) ;
conn - > prof . tx_pending - - ;
2007-02-09 00:50:33 +03:00
privptr = netdev_priv ( conn - > netdev ) ;
2005-04-17 02:20:36 +04:00
if ( privptr )
privptr - > stats . tx_errors + + ;
if ( copied )
dev_kfree_skb ( nskb ) ;
else {
/**
* Remove our headers . They get added
* again on retransmit .
*/
skb_pull ( skb , NETIUCV_HDRLEN ) ;
skb_trim ( skb , skb - > len - NETIUCV_HDRLEN ) ;
}
IUCV_DBF_TEXT_ ( data , 2 , " rc %d from iucv_send \n " , rc ) ;
} else {
if ( copied )
dev_kfree_skb ( skb ) ;
atomic_inc ( & nskb - > users ) ;
skb_queue_tail ( & conn - > commit_queue , nskb ) ;
}
}
return rc ;
}
2006-05-27 05:58:38 +04:00
2007-02-09 00:50:33 +03:00
/*
2005-04-17 02:20:36 +04:00
* Interface API for upper network layers
2007-02-09 00:50:33 +03:00
*/
2005-04-17 02:20:36 +04:00
/**
* Open an interface .
* Called from generic network layer when ifconfig up is run .
*
* @ param dev Pointer to interface struct .
*
* @ return 0 on success , - ERRNO on failure . ( Never fails . )
*/
2007-02-09 00:50:33 +03:00
static int netiucv_open ( struct net_device * dev )
{
struct netiucv_priv * priv = netdev_priv ( dev ) ;
fsm_event ( priv - > fsm , DEV_EVENT_START , dev ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/**
* Close an interface .
* Called from generic network layer when ifconfig down is run .
*
* @ param dev Pointer to interface struct .
*
* @ return 0 on success , - ERRNO on failure . ( Never fails . )
*/
2007-02-09 00:50:33 +03:00
static int netiucv_close ( struct net_device * dev )
{
struct netiucv_priv * priv = netdev_priv ( dev ) ;
fsm_event ( priv - > fsm , DEV_EVENT_STOP , dev ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2009-06-16 12:30:43 +04:00
static int netiucv_pm_prepare ( struct device * dev )
{
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
return 0 ;
}
static void netiucv_pm_complete ( struct device * dev )
{
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
return ;
}
/**
* netiucv_pm_freeze ( ) - Freeze PM callback
* @ dev : netiucv device
*
* close open netiucv interfaces
*/
static int netiucv_pm_freeze ( struct device * dev )
{
2009-06-22 14:08:19 +04:00
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2009-06-16 12:30:43 +04:00
struct net_device * ndev = NULL ;
int rc = 0 ;
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
if ( priv & & priv - > conn )
ndev = priv - > conn - > netdev ;
if ( ! ndev )
goto out ;
netif_device_detach ( ndev ) ;
priv - > pm_state = fsm_getstate ( priv - > fsm ) ;
rc = netiucv_close ( ndev ) ;
out :
return rc ;
}
/**
* netiucv_pm_restore_thaw ( ) - Thaw and restore PM callback
* @ dev : netiucv device
*
* re - open netiucv interfaces closed during freeze
*/
static int netiucv_pm_restore_thaw ( struct device * dev )
{
2009-06-22 14:08:19 +04:00
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2009-06-16 12:30:43 +04:00
struct net_device * ndev = NULL ;
int rc = 0 ;
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
if ( priv & & priv - > conn )
ndev = priv - > conn - > netdev ;
if ( ! ndev )
goto out ;
switch ( priv - > pm_state ) {
case DEV_STATE_RUNNING :
case DEV_STATE_STARTWAIT :
rc = netiucv_open ( ndev ) ;
break ;
default :
break ;
}
netif_device_attach ( ndev ) ;
out :
return rc ;
}
2005-04-17 02:20:36 +04:00
/**
* Start transmission of a packet .
* Called from generic network device layer .
*
* @ param skb Pointer to buffer containing the packet .
* @ param dev Pointer to interface struct .
*
* @ return 0 if packet consumed , ! 0 if packet rejected .
* Note : If we return ! 0 , then the packet is free ' d by
* the generic network layer .
*/
static int netiucv_tx ( struct sk_buff * skb , struct net_device * dev )
{
2007-02-09 00:50:33 +03:00
struct netiucv_priv * privptr = netdev_priv ( dev ) ;
int rc ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 4 , __func__ ) ;
2005-04-17 02:20:36 +04:00
/**
* Some sanity checks . . .
*/
if ( skb = = NULL ) {
IUCV_DBF_TEXT ( data , 2 , " netiucv_tx: skb is NULL \n " ) ;
privptr - > stats . tx_dropped + + ;
2009-07-06 06:23:38 +04:00
return NETDEV_TX_OK ;
2005-04-17 02:20:36 +04:00
}
if ( skb_headroom ( skb ) < NETIUCV_HDRLEN ) {
IUCV_DBF_TEXT ( data , 2 ,
" netiucv_tx: skb_headroom < NETIUCV_HDRLEN \n " ) ;
dev_kfree_skb ( skb ) ;
privptr - > stats . tx_dropped + + ;
2009-07-06 06:23:38 +04:00
return NETDEV_TX_OK ;
2005-04-17 02:20:36 +04:00
}
/**
* If connection is not running , try to restart it
2006-05-27 05:58:38 +04:00
* and throw away packet .
2005-04-17 02:20:36 +04:00
*/
if ( fsm_getstate ( privptr - > fsm ) ! = DEV_STATE_RUNNING ) {
dev_kfree_skb ( skb ) ;
privptr - > stats . tx_dropped + + ;
privptr - > stats . tx_errors + + ;
privptr - > stats . tx_carrier_errors + + ;
2009-07-06 06:23:38 +04:00
return NETDEV_TX_OK ;
2005-04-17 02:20:36 +04:00
}
if ( netiucv_test_and_set_busy ( dev ) ) {
IUCV_DBF_TEXT ( data , 2 , " EBUSY from netiucv_tx \n " ) ;
2009-03-24 06:27:45 +03:00
return NETDEV_TX_BUSY ;
2005-04-17 02:20:36 +04:00
}
dev - > trans_start = jiffies ;
2009-06-12 10:22:29 +04:00
rc = netiucv_transmit_skb ( privptr - > conn , skb ) ;
2005-04-17 02:20:36 +04:00
netiucv_clear_busy ( dev ) ;
2009-06-12 10:22:29 +04:00
return rc ? NETDEV_TX_BUSY : NETDEV_TX_OK ;
2005-04-17 02:20:36 +04:00
}
/**
2007-02-09 00:50:33 +03:00
* netiucv_stats
* @ dev : Pointer to interface struct .
2005-04-17 02:20:36 +04:00
*
2007-02-09 00:50:33 +03:00
* Returns interface statistics of a device .
2005-04-17 02:20:36 +04:00
*
2007-02-09 00:50:33 +03:00
* Returns pointer to stats struct of this interface .
2005-04-17 02:20:36 +04:00
*/
2007-02-09 00:50:33 +03:00
static struct net_device_stats * netiucv_stats ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct netiucv_priv * priv = netdev_priv ( dev ) ;
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 5 , __func__ ) ;
2007-02-09 00:50:33 +03:00
return & priv - > stats ;
2005-04-17 02:20:36 +04:00
}
/**
2007-02-09 00:50:33 +03:00
* netiucv_change_mtu
* @ dev : Pointer to interface struct .
* @ new_mtu : The new MTU to use for this interface .
2005-04-17 02:20:36 +04:00
*
2007-02-09 00:50:33 +03:00
* Sets MTU of an interface .
2005-04-17 02:20:36 +04:00
*
2007-02-09 00:50:33 +03:00
* Returns 0 on success , - EINVAL if MTU is out of valid range .
2005-04-17 02:20:36 +04:00
* ( valid range is 576 . . NETIUCV_MTU_MAX ) .
*/
2007-02-09 00:50:33 +03:00
static int netiucv_change_mtu ( struct net_device * dev , int new_mtu )
2005-04-17 02:20:36 +04:00
{
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2007-02-09 00:50:33 +03:00
if ( new_mtu < 576 | | new_mtu > NETIUCV_MTU_MAX ) {
2005-04-17 02:20:36 +04:00
IUCV_DBF_TEXT ( setup , 2 , " given MTU out of valid range \n " ) ;
return - EINVAL ;
}
dev - > mtu = new_mtu ;
return 0 ;
}
2007-02-09 00:50:33 +03:00
/*
2005-04-17 02:20:36 +04:00
* attributes in sysfs
2007-02-09 00:50:33 +03:00
*/
2005-04-17 02:20:36 +04:00
2007-02-09 00:50:33 +03:00
static ssize_t user_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2009-05-04 23:40:54 +04:00
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 5 , __func__ ) ;
2011-12-20 02:56:34 +04:00
return sprintf ( buf , " %s \n " , netiucv_printuser ( priv - > conn ) ) ;
2005-04-17 02:20:36 +04:00
}
2011-12-20 02:56:34 +04:00
static int netiucv_check_user ( const char * buf , size_t count , char * username ,
char * userdata )
2005-04-17 02:20:36 +04:00
{
2011-12-20 02:56:34 +04:00
const char * p ;
int i ;
2005-04-17 02:20:36 +04:00
2011-12-20 02:56:34 +04:00
p = strchr ( buf , ' . ' ) ;
if ( ( p & & ( ( count > 26 ) | |
( ( p - buf ) > 8 ) | |
( buf + count - p > 18 ) ) ) | |
( ! p & & ( count > 9 ) ) ) {
IUCV_DBF_TEXT ( setup , 2 , " conn_write: too long \n " ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2011-12-20 02:56:34 +04:00
for ( i = 0 , p = buf ; i < 8 & & * p & & * p ! = ' . ' ; i + + , p + + ) {
if ( isalnum ( * p ) | | * p = = ' $ ' ) {
username [ i ] = toupper ( * p ) ;
2007-02-09 00:50:33 +03:00
continue ;
}
2011-12-20 02:56:34 +04:00
if ( * p = = ' \n ' )
2005-04-17 02:20:36 +04:00
/* trailing lf, grr */
break ;
2007-02-09 00:50:33 +03:00
IUCV_DBF_TEXT_ ( setup , 2 ,
2011-12-20 02:56:34 +04:00
" conn_write: invalid character %02x \n " , * p ) ;
2007-02-09 00:50:33 +03:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2007-02-09 00:50:33 +03:00
while ( i < 8 )
2005-04-17 02:20:36 +04:00
username [ i + + ] = ' ' ;
2006-09-15 18:25:19 +04:00
username [ 8 ] = ' \0 ' ;
2005-04-17 02:20:36 +04:00
2011-12-20 02:56:34 +04:00
if ( * p = = ' . ' ) {
p + + ;
for ( i = 0 ; i < 16 & & * p ; i + + , p + + ) {
if ( * p = = ' \n ' )
break ;
userdata [ i ] = toupper ( * p ) ;
}
while ( i > 0 & & i < 16 )
userdata [ i + + ] = ' ' ;
} else
memcpy ( userdata , iucvMagic_ascii , 16 ) ;
userdata [ 16 ] = ' \0 ' ;
ASCEBC ( userdata , 16 ) ;
return 0 ;
}
static ssize_t user_write ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
struct net_device * ndev = priv - > conn - > netdev ;
char username [ 9 ] ;
char userdata [ 17 ] ;
int rc ;
struct iucv_connection * cp ;
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
rc = netiucv_check_user ( buf , count , username , userdata ) ;
if ( rc )
return rc ;
2007-02-09 00:50:33 +03:00
if ( memcmp ( username , priv - > conn - > userid , 9 ) & &
( ndev - > flags & ( IFF_UP | IFF_RUNNING ) ) ) {
/* username changed while the interface is active. */
IUCV_DBF_TEXT ( setup , 2 , " user_write: device active \n " ) ;
2008-07-14 11:59:30 +04:00
return - EPERM ;
2007-02-09 00:50:33 +03:00
}
read_lock_bh ( & iucv_connection_rwlock ) ;
list_for_each_entry ( cp , & iucv_connection_list , list ) {
2011-12-20 02:56:34 +04:00
if ( ! strncmp ( username , cp - > userid , 9 ) & &
! strncmp ( userdata , cp - > userdata , 17 ) & & cp - > netdev ! = ndev ) {
2007-02-09 00:50:33 +03:00
read_unlock_bh ( & iucv_connection_rwlock ) ;
2011-12-20 02:56:34 +04:00
IUCV_DBF_TEXT_ ( setup , 2 , " user_write: Connection to %s "
" already exists \n " , netiucv_printuser ( cp ) ) ;
2007-02-09 00:50:33 +03:00
return - EEXIST ;
2005-04-17 02:20:36 +04:00
}
}
2007-02-09 00:50:33 +03:00
read_unlock_bh ( & iucv_connection_rwlock ) ;
2005-04-17 02:20:36 +04:00
memcpy ( priv - > conn - > userid , username , 9 ) ;
2011-12-20 02:56:34 +04:00
memcpy ( priv - > conn - > userdata , userdata , 17 ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
static DEVICE_ATTR ( user , 0644 , user_show , user_write ) ;
2007-02-09 00:50:33 +03:00
static ssize_t buffer_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
2009-05-04 23:40:54 +04:00
{
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 5 , __func__ ) ;
2005-04-17 02:20:36 +04:00
return sprintf ( buf , " %d \n " , priv - > conn - > max_buffsize ) ;
}
2007-02-09 00:50:33 +03:00
static ssize_t buffer_write ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2009-05-04 23:40:54 +04:00
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
struct net_device * ndev = priv - > conn - > netdev ;
char * e ;
int bs1 ;
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2005-04-17 02:20:36 +04:00
if ( count > = 39 )
return - EINVAL ;
bs1 = simple_strtoul ( buf , & e , 0 ) ;
if ( e & & ( ! isspace ( * e ) ) ) {
2011-12-20 02:56:34 +04:00
IUCV_DBF_TEXT_ ( setup , 2 , " buffer_write: invalid char %02x \n " ,
* e ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
if ( bs1 > NETIUCV_BUFSIZE_MAX ) {
IUCV_DBF_TEXT_ ( setup , 2 ,
" buffer_write: buffer size %d too large \n " ,
bs1 ) ;
return - EINVAL ;
}
if ( ( ndev - > flags & IFF_RUNNING ) & &
( bs1 < ( ndev - > mtu + NETIUCV_HDRLEN + 2 ) ) ) {
IUCV_DBF_TEXT_ ( setup , 2 ,
" buffer_write: buffer size %d too small \n " ,
bs1 ) ;
return - EINVAL ;
}
if ( bs1 < ( 576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN ) ) {
IUCV_DBF_TEXT_ ( setup , 2 ,
" buffer_write: buffer size %d too small \n " ,
bs1 ) ;
return - EINVAL ;
}
priv - > conn - > max_buffsize = bs1 ;
if ( ! ( ndev - > flags & IFF_RUNNING ) )
ndev - > mtu = bs1 - NETIUCV_HDRLEN - NETIUCV_HDRLEN ;
return count ;
}
static DEVICE_ATTR ( buffer , 0644 , buffer_show , buffer_write ) ;
2007-02-09 00:50:33 +03:00
static ssize_t dev_fsm_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2009-05-04 23:40:54 +04:00
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 5 , __func__ ) ;
2005-04-17 02:20:36 +04:00
return sprintf ( buf , " %s \n " , fsm_getstate_str ( priv - > fsm ) ) ;
}
static DEVICE_ATTR ( device_fsm_state , 0444 , dev_fsm_show , NULL ) ;
2007-02-09 00:50:33 +03:00
static ssize_t conn_fsm_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
2009-05-04 23:40:54 +04:00
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 5 , __func__ ) ;
2005-04-17 02:20:36 +04:00
return sprintf ( buf , " %s \n " , fsm_getstate_str ( priv - > conn - > fsm ) ) ;
}
static DEVICE_ATTR ( connection_fsm_state , 0444 , conn_fsm_show , NULL ) ;
2007-02-09 00:50:33 +03:00
static ssize_t maxmulti_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
2009-05-04 23:40:54 +04:00
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 5 , __func__ ) ;
2005-04-17 02:20:36 +04:00
return sprintf ( buf , " %ld \n " , priv - > conn - > prof . maxmulti ) ;
}
2007-02-09 00:50:33 +03:00
static ssize_t maxmulti_write ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2009-05-04 23:40:54 +04:00
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 4 , __func__ ) ;
2005-04-17 02:20:36 +04:00
priv - > conn - > prof . maxmulti = 0 ;
return count ;
}
static DEVICE_ATTR ( max_tx_buffer_used , 0644 , maxmulti_show , maxmulti_write ) ;
2007-02-09 00:50:33 +03:00
static ssize_t maxcq_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2009-05-04 23:40:54 +04:00
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 5 , __func__ ) ;
2005-04-17 02:20:36 +04:00
return sprintf ( buf , " %ld \n " , priv - > conn - > prof . maxcqueue ) ;
}
2007-02-09 00:50:33 +03:00
static ssize_t maxcq_write ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2009-05-04 23:40:54 +04:00
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2006-05-27 05:58:38 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 4 , __func__ ) ;
2005-04-17 02:20:36 +04:00
priv - > conn - > prof . maxcqueue = 0 ;
return count ;
}
static DEVICE_ATTR ( max_chained_skbs , 0644 , maxcq_show , maxcq_write ) ;
2007-02-09 00:50:33 +03:00
static ssize_t sdoio_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2009-05-04 23:40:54 +04:00
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 5 , __func__ ) ;
2005-04-17 02:20:36 +04:00
return sprintf ( buf , " %ld \n " , priv - > conn - > prof . doios_single ) ;
}
2007-02-09 00:50:33 +03:00
static ssize_t sdoio_write ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2009-05-04 23:40:54 +04:00
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2006-05-27 05:58:38 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 4 , __func__ ) ;
2005-04-17 02:20:36 +04:00
priv - > conn - > prof . doios_single = 0 ;
return count ;
}
static DEVICE_ATTR ( tx_single_write_ops , 0644 , sdoio_show , sdoio_write ) ;
2007-02-09 00:50:33 +03:00
static ssize_t mdoio_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2009-05-04 23:40:54 +04:00
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 5 , __func__ ) ;
2005-04-17 02:20:36 +04:00
return sprintf ( buf , " %ld \n " , priv - > conn - > prof . doios_multi ) ;
}
2007-02-09 00:50:33 +03:00
static ssize_t mdoio_write ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2009-05-04 23:40:54 +04:00
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2006-05-27 05:58:38 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 5 , __func__ ) ;
2005-04-17 02:20:36 +04:00
priv - > conn - > prof . doios_multi = 0 ;
return count ;
}
static DEVICE_ATTR ( tx_multi_write_ops , 0644 , mdoio_show , mdoio_write ) ;
2007-02-09 00:50:33 +03:00
static ssize_t txlen_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2009-05-04 23:40:54 +04:00
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 5 , __func__ ) ;
2005-04-17 02:20:36 +04:00
return sprintf ( buf , " %ld \n " , priv - > conn - > prof . txlen ) ;
}
2007-02-09 00:50:33 +03:00
static ssize_t txlen_write ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2009-05-04 23:40:54 +04:00
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2006-05-27 05:58:38 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 4 , __func__ ) ;
2005-04-17 02:20:36 +04:00
priv - > conn - > prof . txlen = 0 ;
return count ;
}
static DEVICE_ATTR ( netto_bytes , 0644 , txlen_show , txlen_write ) ;
2007-02-09 00:50:33 +03:00
static ssize_t txtime_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2009-05-04 23:40:54 +04:00
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 5 , __func__ ) ;
2005-04-17 02:20:36 +04:00
return sprintf ( buf , " %ld \n " , priv - > conn - > prof . tx_time ) ;
}
2007-02-09 00:50:33 +03:00
static ssize_t txtime_write ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2009-05-04 23:40:54 +04:00
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2006-05-27 05:58:38 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 4 , __func__ ) ;
2005-04-17 02:20:36 +04:00
priv - > conn - > prof . tx_time = 0 ;
return count ;
}
static DEVICE_ATTR ( max_tx_io_time , 0644 , txtime_show , txtime_write ) ;
2007-02-09 00:50:33 +03:00
static ssize_t txpend_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2009-05-04 23:40:54 +04:00
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 5 , __func__ ) ;
2005-04-17 02:20:36 +04:00
return sprintf ( buf , " %ld \n " , priv - > conn - > prof . tx_pending ) ;
}
2007-02-09 00:50:33 +03:00
static ssize_t txpend_write ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2009-05-04 23:40:54 +04:00
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 4 , __func__ ) ;
2005-04-17 02:20:36 +04:00
priv - > conn - > prof . tx_pending = 0 ;
return count ;
}
static DEVICE_ATTR ( tx_pending , 0644 , txpend_show , txpend_write ) ;
2007-02-09 00:50:33 +03:00
static ssize_t txmpnd_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2009-05-04 23:40:54 +04:00
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 5 , __func__ ) ;
2005-04-17 02:20:36 +04:00
return sprintf ( buf , " %ld \n " , priv - > conn - > prof . tx_max_pending ) ;
}
2007-02-09 00:50:33 +03:00
static ssize_t txmpnd_write ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2009-05-04 23:40:54 +04:00
struct netiucv_priv * priv = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 4 , __func__ ) ;
2005-04-17 02:20:36 +04:00
priv - > conn - > prof . tx_max_pending = 0 ;
return count ;
}
static DEVICE_ATTR ( tx_max_pending , 0644 , txmpnd_show , txmpnd_write ) ;
static struct attribute * netiucv_attrs [ ] = {
& dev_attr_buffer . attr ,
& dev_attr_user . attr ,
NULL ,
} ;
static struct attribute_group netiucv_attr_group = {
. attrs = netiucv_attrs ,
} ;
static struct attribute * netiucv_stat_attrs [ ] = {
& dev_attr_device_fsm_state . attr ,
& dev_attr_connection_fsm_state . attr ,
& dev_attr_max_tx_buffer_used . attr ,
& dev_attr_max_chained_skbs . attr ,
& dev_attr_tx_single_write_ops . attr ,
& dev_attr_tx_multi_write_ops . attr ,
& dev_attr_netto_bytes . attr ,
& dev_attr_max_tx_io_time . attr ,
& dev_attr_tx_pending . attr ,
& dev_attr_tx_max_pending . attr ,
NULL ,
} ;
static struct attribute_group netiucv_stat_attr_group = {
. name = " stats " ,
. attrs = netiucv_stat_attrs ,
} ;
2007-06-20 15:03:57 +04:00
static int netiucv_add_files ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
int ret ;
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2005-04-17 02:20:36 +04:00
ret = sysfs_create_group ( & dev - > kobj , & netiucv_attr_group ) ;
if ( ret )
return ret ;
ret = sysfs_create_group ( & dev - > kobj , & netiucv_stat_attr_group ) ;
if ( ret )
sysfs_remove_group ( & dev - > kobj , & netiucv_attr_group ) ;
return ret ;
}
2007-06-20 15:03:57 +04:00
static void netiucv_remove_files ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2005-04-17 02:20:36 +04:00
sysfs_remove_group ( & dev - > kobj , & netiucv_stat_attr_group ) ;
sysfs_remove_group ( & dev - > kobj , & netiucv_attr_group ) ;
}
2007-02-09 00:50:33 +03:00
static int netiucv_register_device ( struct net_device * ndev )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct netiucv_priv * priv = netdev_priv ( ndev ) ;
2006-03-24 14:15:31 +03:00
struct device * dev = kzalloc ( sizeof ( struct device ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
int ret ;
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2005-04-17 02:20:36 +04:00
if ( dev ) {
2008-10-10 23:33:10 +04:00
dev_set_name ( dev , " net%s " , ndev - > name ) ;
2005-04-17 02:20:36 +04:00
dev - > bus = & iucv_bus ;
dev - > parent = iucv_root ;
/*
* The release function could be called after the
* module has been unloaded . It ' s _only_ task is to
* free the struct . Therefore , we specify kfree ( )
* directly here . ( Probably a little bit obfuscating
* but legitime . . . ) .
*/
dev - > release = ( void ( * ) ( struct device * ) ) kfree ;
dev - > driver = & netiucv_driver ;
} else
return - ENOMEM ;
ret = device_register ( dev ) ;
2009-09-11 12:28:38 +04:00
if ( ret ) {
put_device ( dev ) ;
2005-04-17 02:20:36 +04:00
return ret ;
2009-09-11 12:28:38 +04:00
}
2005-04-17 02:20:36 +04:00
ret = netiucv_add_files ( dev ) ;
if ( ret )
goto out_unreg ;
priv - > dev = dev ;
2009-05-04 23:40:54 +04:00
dev_set_drvdata ( dev , priv ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
out_unreg :
device_unregister ( dev ) ;
return ret ;
}
2007-02-09 00:50:33 +03:00
static void netiucv_unregister_device ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2005-04-17 02:20:36 +04:00
netiucv_remove_files ( dev ) ;
device_unregister ( dev ) ;
}
/**
* Allocate and initialize a new connection structure .
* Add it to the list of netiucv connections ;
*/
2007-02-09 00:50:33 +03:00
static struct iucv_connection * netiucv_new_connection ( struct net_device * dev ,
2011-12-20 02:56:34 +04:00
char * username ,
char * userdata )
2007-02-09 00:50:33 +03:00
{
struct iucv_connection * conn ;
2005-04-17 02:20:36 +04:00
2007-02-09 00:50:33 +03:00
conn = kzalloc ( sizeof ( * conn ) , GFP_KERNEL ) ;
if ( ! conn )
goto out ;
skb_queue_head_init ( & conn - > collect_queue ) ;
skb_queue_head_init ( & conn - > commit_queue ) ;
spin_lock_init ( & conn - > collect_lock ) ;
conn - > max_buffsize = NETIUCV_BUFSIZE_DEFAULT ;
conn - > netdev = dev ;
conn - > rx_buff = alloc_skb ( conn - > max_buffsize , GFP_KERNEL | GFP_DMA ) ;
if ( ! conn - > rx_buff )
goto out_conn ;
conn - > tx_buff = alloc_skb ( conn - > max_buffsize , GFP_KERNEL | GFP_DMA ) ;
if ( ! conn - > tx_buff )
goto out_rx ;
conn - > fsm = init_fsm ( " netiucvconn " , conn_state_names ,
conn_event_names , NR_CONN_STATES ,
NR_CONN_EVENTS , conn_fsm , CONN_FSM_LEN ,
GFP_KERNEL ) ;
if ( ! conn - > fsm )
goto out_tx ;
fsm_settimer ( conn - > fsm , & conn - > timer ) ;
fsm_newstate ( conn - > fsm , CONN_STATE_INVALID ) ;
2011-12-20 02:56:34 +04:00
if ( userdata )
memcpy ( conn - > userdata , userdata , 17 ) ;
2007-02-09 00:50:33 +03:00
if ( username ) {
memcpy ( conn - > userid , username , 9 ) ;
fsm_newstate ( conn - > fsm , CONN_STATE_STOPPED ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-09 00:50:33 +03:00
write_lock_bh ( & iucv_connection_rwlock ) ;
list_add_tail ( & conn - > list , & iucv_connection_list ) ;
write_unlock_bh ( & iucv_connection_rwlock ) ;
2005-04-17 02:20:36 +04:00
return conn ;
2007-02-09 00:50:33 +03:00
out_tx :
kfree_skb ( conn - > tx_buff ) ;
out_rx :
kfree_skb ( conn - > rx_buff ) ;
out_conn :
kfree ( conn ) ;
out :
return NULL ;
2005-04-17 02:20:36 +04:00
}
/**
* Release a connection structure and remove it from the
* list of netiucv connections .
*/
2007-02-09 00:50:33 +03:00
static void netiucv_remove_connection ( struct iucv_connection * conn )
2005-04-17 02:20:36 +04:00
{
2011-12-20 02:56:34 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2007-02-09 00:50:33 +03:00
write_lock_bh ( & iucv_connection_rwlock ) ;
list_del_init ( & conn - > list ) ;
write_unlock_bh ( & iucv_connection_rwlock ) ;
2007-05-02 17:18:44 +04:00
fsm_deltimer ( & conn - > timer ) ;
netiucv_purge_skb_queue ( & conn - > collect_queue ) ;
2007-02-09 00:50:33 +03:00
if ( conn - > path ) {
2011-12-20 02:56:34 +04:00
iucv_path_sever ( conn - > path , conn - > userdata ) ;
2007-02-09 00:50:33 +03:00
kfree ( conn - > path ) ;
conn - > path = NULL ;
2005-04-17 02:20:36 +04:00
}
2007-05-02 17:18:44 +04:00
netiucv_purge_skb_queue ( & conn - > commit_queue ) ;
2007-02-09 00:50:33 +03:00
kfree_fsm ( conn - > fsm ) ;
kfree_skb ( conn - > rx_buff ) ;
kfree_skb ( conn - > tx_buff ) ;
2005-04-17 02:20:36 +04:00
}
/**
* Release everything of a net device .
*/
2007-02-09 00:50:33 +03:00
static void netiucv_free_netdevice ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct netiucv_priv * privptr = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2005-04-17 02:20:36 +04:00
if ( ! dev )
return ;
if ( privptr ) {
if ( privptr - > conn )
netiucv_remove_connection ( privptr - > conn ) ;
if ( privptr - > fsm )
kfree_fsm ( privptr - > fsm ) ;
privptr - > conn = NULL ; privptr - > fsm = NULL ;
/* privptr gets freed by free_netdev() */
}
free_netdev ( dev ) ;
}
/**
* Initialize a net device . ( Called from kernel in alloc_netdev ( ) )
*/
2009-01-09 06:43:58 +03:00
static const struct net_device_ops netiucv_netdev_ops = {
. ndo_open = netiucv_open ,
. ndo_stop = netiucv_close ,
. ndo_get_stats = netiucv_stats ,
. ndo_start_xmit = netiucv_tx ,
. ndo_change_mtu = netiucv_change_mtu ,
} ;
2007-02-09 00:50:33 +03:00
static void netiucv_setup_netdevice ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
dev - > mtu = NETIUCV_MTU_DEFAULT ;
dev - > destructor = netiucv_free_netdevice ;
dev - > hard_header_len = NETIUCV_HDRLEN ;
dev - > addr_len = 0 ;
dev - > type = ARPHRD_SLIP ;
dev - > tx_queue_len = NETIUCV_QUEUELEN_DEFAULT ;
dev - > flags = IFF_POINTOPOINT | IFF_NOARP ;
2009-01-09 06:43:58 +03:00
dev - > netdev_ops = & netiucv_netdev_ops ;
2005-04-17 02:20:36 +04:00
}
/**
* Allocate and initialize everything of a net device .
*/
2011-12-20 02:56:34 +04:00
static struct net_device * netiucv_init_netdevice ( char * username , char * userdata )
2005-04-17 02:20:36 +04:00
{
struct netiucv_priv * privptr ;
struct net_device * dev ;
dev = alloc_netdev ( sizeof ( struct netiucv_priv ) , " iucv%d " ,
netiucv_setup_netdevice ) ;
if ( ! dev )
return NULL ;
2011-11-15 06:31:14 +04:00
if ( dev_alloc_name ( dev , dev - > name ) < 0 )
goto out_netdev ;
2005-04-17 02:20:36 +04:00
2007-02-09 00:50:33 +03:00
privptr = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
privptr - > fsm = init_fsm ( " netiucvdev " , dev_state_names ,
dev_event_names , NR_DEV_STATES , NR_DEV_EVENTS ,
dev_fsm , DEV_FSM_LEN , GFP_KERNEL ) ;
2007-02-09 00:50:33 +03:00
if ( ! privptr - > fsm )
goto out_netdev ;
2011-12-20 02:56:34 +04:00
privptr - > conn = netiucv_new_connection ( dev , username , userdata ) ;
2005-04-17 02:20:36 +04:00
if ( ! privptr - > conn ) {
IUCV_DBF_TEXT ( setup , 2 , " NULL from netiucv_new_connection \n " ) ;
2007-02-09 00:50:33 +03:00
goto out_fsm ;
2005-04-17 02:20:36 +04:00
}
fsm_newstate ( privptr - > fsm , DEV_STATE_STOPPED ) ;
return dev ;
2007-02-09 00:50:33 +03:00
out_fsm :
kfree_fsm ( privptr - > fsm ) ;
out_netdev :
free_netdev ( dev ) ;
return NULL ;
2005-04-17 02:20:36 +04:00
}
2007-02-09 00:50:33 +03:00
static ssize_t conn_write ( struct device_driver * drv ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2006-09-15 18:25:19 +04:00
char username [ 9 ] ;
2011-12-20 02:56:34 +04:00
char userdata [ 17 ] ;
int rc ;
2005-04-17 02:20:36 +04:00
struct net_device * dev ;
2007-02-09 00:50:33 +03:00
struct netiucv_priv * priv ;
struct iucv_connection * cp ;
2005-04-17 02:20:36 +04:00
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2011-12-20 02:56:34 +04:00
rc = netiucv_check_user ( buf , count , username , userdata ) ;
if ( rc )
return rc ;
2006-09-15 18:25:19 +04:00
2007-02-09 00:50:33 +03:00
read_lock_bh ( & iucv_connection_rwlock ) ;
list_for_each_entry ( cp , & iucv_connection_list , list ) {
2011-12-20 02:56:34 +04:00
if ( ! strncmp ( username , cp - > userid , 9 ) & &
! strncmp ( userdata , cp - > userdata , 17 ) ) {
2007-02-09 00:50:33 +03:00
read_unlock_bh ( & iucv_connection_rwlock ) ;
2011-12-20 02:56:34 +04:00
IUCV_DBF_TEXT_ ( setup , 2 , " conn_write: Connection to %s "
" already exists \n " , netiucv_printuser ( cp ) ) ;
2007-02-09 00:50:33 +03:00
return - EEXIST ;
}
2006-09-15 18:25:19 +04:00
}
2007-02-09 00:50:33 +03:00
read_unlock_bh ( & iucv_connection_rwlock ) ;
2011-12-20 02:56:34 +04:00
dev = netiucv_init_netdevice ( username , userdata ) ;
2005-04-17 02:20:36 +04:00
if ( ! dev ) {
IUCV_DBF_TEXT ( setup , 2 , " NULL from netiucv_init_netdevice \n " ) ;
return - ENODEV ;
}
2007-02-09 00:50:33 +03:00
rc = netiucv_register_device ( dev ) ;
if ( rc ) {
2005-04-17 02:20:36 +04:00
IUCV_DBF_TEXT_ ( setup , 2 ,
2007-02-09 00:50:33 +03:00
" ret %d from netiucv_register_device \n " , rc ) ;
2005-04-17 02:20:36 +04:00
goto out_free_ndev ;
}
/* sysfs magic */
2007-02-09 00:50:33 +03:00
priv = netdev_priv ( dev ) ;
SET_NETDEV_DEV ( dev , priv - > dev ) ;
2005-04-17 02:20:36 +04:00
2007-02-09 00:50:33 +03:00
rc = register_netdev ( dev ) ;
if ( rc )
goto out_unreg ;
2005-04-17 02:20:36 +04:00
2011-12-20 02:56:34 +04:00
dev_info ( priv - > dev , " The IUCV interface to %s has been established "
" successfully \n " ,
netiucv_printuser ( priv - > conn ) ) ;
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
return count ;
2007-02-09 00:50:33 +03:00
out_unreg :
netiucv_unregister_device ( priv - > dev ) ;
2005-04-17 02:20:36 +04:00
out_free_ndev :
netiucv_free_netdevice ( dev ) ;
2007-02-09 00:50:33 +03:00
return rc ;
2005-04-17 02:20:36 +04:00
}
2007-02-05 23:16:47 +03:00
static DRIVER_ATTR ( connection , 0200 , NULL , conn_write ) ;
2005-04-17 02:20:36 +04:00
2007-02-09 00:50:33 +03:00
static ssize_t remove_write ( struct device_driver * drv ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct iucv_connection * cp ;
2005-04-17 02:20:36 +04:00
struct net_device * ndev ;
struct netiucv_priv * priv ;
struct device * dev ;
char name [ IFNAMSIZ ] ;
2007-02-09 00:50:33 +03:00
const char * p ;
2005-04-17 02:20:36 +04:00
int i ;
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2005-04-17 02:20:36 +04:00
if ( count > = IFNAMSIZ )
2009-08-18 22:18:35 +04:00
count = IFNAMSIZ - 1 ;
2005-04-17 02:20:36 +04:00
2007-02-09 00:50:33 +03:00
for ( i = 0 , p = buf ; i < count & & * p ; i + + , p + + ) {
if ( * p = = ' \n ' | | * p = = ' ' )
2005-04-17 02:20:36 +04:00
/* trailing lf, grr */
break ;
2007-02-09 00:50:33 +03:00
name [ i ] = * p ;
2005-04-17 02:20:36 +04:00
}
name [ i ] = ' \0 ' ;
2007-02-09 00:50:33 +03:00
read_lock_bh ( & iucv_connection_rwlock ) ;
list_for_each_entry ( cp , & iucv_connection_list , list ) {
ndev = cp - > netdev ;
priv = netdev_priv ( ndev ) ;
2005-04-17 02:20:36 +04:00
dev = priv - > dev ;
2007-02-09 00:50:33 +03:00
if ( strncmp ( name , ndev - > name , count ) )
continue ;
read_unlock_bh ( & iucv_connection_rwlock ) ;
2005-04-17 02:20:36 +04:00
if ( ndev - > flags & ( IFF_UP | IFF_RUNNING ) ) {
2008-12-25 15:39:47 +03:00
dev_warn ( dev , " The IUCV device is connected "
" to %s and cannot be removed \n " ,
priv - > conn - > userid ) ;
2005-04-17 02:20:36 +04:00
IUCV_DBF_TEXT ( data , 2 , " remove_write: still active \n " ) ;
2008-07-14 11:59:30 +04:00
return - EPERM ;
2005-04-17 02:20:36 +04:00
}
unregister_netdev ( ndev ) ;
netiucv_unregister_device ( dev ) ;
return count ;
}
2007-02-09 00:50:33 +03:00
read_unlock_bh ( & iucv_connection_rwlock ) ;
2005-04-17 02:20:36 +04:00
IUCV_DBF_TEXT ( data , 2 , " remove_write: unknown device \n " ) ;
return - EINVAL ;
}
2007-02-05 23:16:47 +03:00
static DRIVER_ATTR ( remove , 0200 , NULL , remove_write ) ;
2005-04-17 02:20:36 +04:00
2007-02-09 00:50:33 +03:00
static struct attribute * netiucv_drv_attrs [ ] = {
& driver_attr_connection . attr ,
& driver_attr_remove . attr ,
NULL ,
} ;
static struct attribute_group netiucv_drv_attr_group = {
. attrs = netiucv_drv_attrs ,
} ;
2009-06-24 21:06:31 +04:00
static const struct attribute_group * netiucv_drv_attr_groups [ ] = {
2007-12-05 14:50:28 +03:00
& netiucv_drv_attr_group ,
NULL ,
} ;
2007-02-09 00:50:33 +03:00
static void netiucv_banner ( void )
2005-04-17 02:20:36 +04:00
{
2008-12-25 15:39:47 +03:00
pr_info ( " driver initialized \n " ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-09 00:50:33 +03:00
static void __exit netiucv_exit ( void )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
struct iucv_connection * cp ;
struct net_device * ndev ;
struct netiucv_priv * priv ;
struct device * dev ;
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2007-02-09 00:50:33 +03:00
while ( ! list_empty ( & iucv_connection_list ) ) {
cp = list_entry ( iucv_connection_list . next ,
struct iucv_connection , list ) ;
ndev = cp - > netdev ;
priv = netdev_priv ( ndev ) ;
dev = priv - > dev ;
2005-04-17 02:20:36 +04:00
unregister_netdev ( ndev ) ;
netiucv_unregister_device ( dev ) ;
}
2009-06-16 12:30:43 +04:00
device_unregister ( netiucv_dev ) ;
2005-04-17 02:20:36 +04:00
driver_unregister ( & netiucv_driver ) ;
2007-02-09 00:50:33 +03:00
iucv_unregister ( & netiucv_handler , 1 ) ;
2005-04-17 02:20:36 +04:00
iucv_unregister_dbf_views ( ) ;
2008-12-25 15:39:47 +03:00
pr_info ( " driver unloaded \n " ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2007-02-09 00:50:33 +03:00
static int __init netiucv_init ( void )
2005-04-17 02:20:36 +04:00
{
2007-02-09 00:50:33 +03:00
int rc ;
2006-05-27 05:58:38 +04:00
2007-02-09 00:50:33 +03:00
rc = iucv_register_dbf_views ( ) ;
if ( rc )
goto out ;
rc = iucv_register ( & netiucv_handler , 1 ) ;
if ( rc )
goto out_dbf ;
2008-04-17 09:46:21 +04:00
IUCV_DBF_TEXT ( trace , 3 , __func__ ) ;
2008-04-24 12:15:28 +04:00
netiucv_driver . groups = netiucv_drv_attr_groups ;
2007-02-09 00:50:33 +03:00
rc = driver_register ( & netiucv_driver ) ;
if ( rc ) {
IUCV_DBF_TEXT_ ( setup , 2 , " ret %d from driver_register \n " , rc ) ;
goto out_iucv ;
2005-04-17 02:20:36 +04:00
}
2009-06-16 12:30:43 +04:00
/* establish dummy device */
netiucv_dev = kzalloc ( sizeof ( struct device ) , GFP_KERNEL ) ;
if ( ! netiucv_dev ) {
rc = - ENOMEM ;
goto out_driver ;
}
dev_set_name ( netiucv_dev , " netiucv " ) ;
netiucv_dev - > bus = & iucv_bus ;
netiucv_dev - > parent = iucv_root ;
netiucv_dev - > release = ( void ( * ) ( struct device * ) ) kfree ;
netiucv_dev - > driver = & netiucv_driver ;
rc = device_register ( netiucv_dev ) ;
2009-09-11 12:28:38 +04:00
if ( rc ) {
put_device ( netiucv_dev ) ;
2009-06-16 12:30:43 +04:00
goto out_driver ;
2009-09-11 12:28:38 +04:00
}
2007-02-09 00:50:33 +03:00
netiucv_banner ( ) ;
return rc ;
2009-06-16 12:30:43 +04:00
out_driver :
driver_unregister ( & netiucv_driver ) ;
2007-02-09 00:50:33 +03:00
out_iucv :
iucv_unregister ( & netiucv_handler , 1 ) ;
out_dbf :
iucv_unregister_dbf_views ( ) ;
out :
return rc ;
2005-04-17 02:20:36 +04:00
}
2006-05-27 05:58:38 +04:00
2005-04-17 02:20:36 +04:00
module_init ( netiucv_init ) ;
module_exit ( netiucv_exit ) ;
MODULE_LICENSE ( " GPL " ) ;