2017-11-14 20:38:04 +03:00
// SPDX-License-Identifier: GPL-2.0
2008-02-08 02:03:49 +03:00
/*
2009-06-16 12:30:32 +04:00
* Copyright IBM Corp . 2001 , 2009
2008-02-08 02:03:49 +03:00
* Author ( s ) :
* Original CTC driver ( s ) :
* Fritz Elfert ( felfert @ millenux . com )
* Dieter Wellerdiek ( wel @ de . ibm . com )
* Martin Schwidefsky ( schwidefsky @ de . ibm . com )
* Denis Joseph Barrow ( barrow_dj @ yahoo . com )
* Jochen Roehrig ( roehrig @ de . ibm . com )
* Cornelia Huck < cornelia . huck @ de . ibm . com >
* MPC additions :
* Belinda Thompson ( belindat @ us . ibm . com )
* Andy Richter ( richtera @ us . ibm . com )
* Revived by :
* Peter Tiedemann ( ptiedem @ de . ibm . com )
*/
# undef DEBUG
# undef DEBUGDATA
# undef DEBUGCCW
2008-12-25 15:39:54 +03:00
# define KMSG_COMPONENT "ctcm"
# define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
2008-02-08 02:03:49 +03:00
# 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/ip.h>
# include <linux/if_arp.h>
# include <linux/tcp.h>
# include <linux/skbuff.h>
# include <linux/ctype.h>
# include <net/dst.h>
# include <linux/io.h>
# include <asm/ccwdev.h>
# include <asm/ccwgroup.h>
# include <linux/uaccess.h>
# include <asm/idals.h>
# include "ctcm_fsms.h"
# include "ctcm_main.h"
/* Some common global variables */
2009-11-13 00:46:29 +03:00
/**
* The root device for ctcm group devices
*/
static struct device * ctcm_root_dev ;
2008-02-08 02:03:49 +03:00
/*
* Linked list of all detected channels .
*/
struct channel * channels ;
/**
* Unpack a just received skb and hand it over to
* upper layers .
*
* ch The channel where this skb has been received .
* pskb The received skb .
*/
void ctcm_unpack_skb ( struct channel * ch , struct sk_buff * pskb )
{
struct net_device * dev = ch - > netdev ;
2008-08-21 19:10:24 +04:00
struct ctcm_priv * priv = dev - > ml_priv ;
2008-02-08 02:03:49 +03:00
__u16 len = * ( ( __u16 * ) pskb - > data ) ;
skb_put ( pskb , 2 + LL_HEADER_LENGTH ) ;
skb_pull ( pskb , 2 ) ;
pskb - > dev = dev ;
pskb - > ip_summed = CHECKSUM_UNNECESSARY ;
while ( len > 0 ) {
struct sk_buff * skb ;
int skblen ;
struct ll_header * header = ( struct ll_header * ) pskb - > data ;
skb_pull ( pskb , LL_HEADER_LENGTH ) ;
if ( ( ch - > protocol = = CTCM_PROTO_S390 ) & &
( header - > type ! = ETH_P_IP ) ) {
if ( ! ( ch - > logflags & LOG_FLAG_ILLEGALPKT ) ) {
2008-07-18 17:24:57 +04:00
ch - > logflags | = LOG_FLAG_ILLEGALPKT ;
2008-02-08 02:03:49 +03:00
/*
* Check packet type only if we stick strictly
* to S / 390 ' s protocol of OS390 . This only
* supports IP . Otherwise allow any packet
* type .
*/
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( ERROR , CTC_DBF_ERROR ,
" %s(%s): Illegal packet type 0x%04x "
" - dropping " ,
CTCM_FUNTAIL , dev - > name , header - > type ) ;
2008-02-08 02:03:49 +03:00
}
priv - > stats . rx_dropped + + ;
priv - > stats . rx_frame_errors + + ;
return ;
}
2017-04-07 10:15:37 +03:00
pskb - > protocol = cpu_to_be16 ( header - > type ) ;
2009-03-24 06:27:48 +03:00
if ( ( header - > length < = LL_HEADER_LENGTH ) | |
( len < = LL_HEADER_LENGTH ) ) {
2008-02-08 02:03:49 +03:00
if ( ! ( ch - > logflags & LOG_FLAG_ILLEGALSIZE ) ) {
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( ERROR , CTC_DBF_ERROR ,
" %s(%s): Illegal packet size %d(%d,%d) "
" - dropping " ,
CTCM_FUNTAIL , dev - > name ,
header - > length , dev - > mtu , len ) ;
2008-02-08 02:03:49 +03:00
ch - > logflags | = LOG_FLAG_ILLEGALSIZE ;
}
priv - > stats . rx_dropped + + ;
priv - > stats . rx_length_errors + + ;
return ;
}
header - > length - = LL_HEADER_LENGTH ;
len - = LL_HEADER_LENGTH ;
if ( ( header - > length > skb_tailroom ( pskb ) ) | |
2017-04-07 10:15:37 +03:00
( header - > length > len ) ) {
2008-02-08 02:03:49 +03:00
if ( ! ( ch - > logflags & LOG_FLAG_OVERRUN ) ) {
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( ERROR , CTC_DBF_ERROR ,
" %s(%s): Packet size %d (overrun) "
" - dropping " , CTCM_FUNTAIL ,
dev - > name , header - > length ) ;
2008-02-08 02:03:49 +03:00
ch - > logflags | = LOG_FLAG_OVERRUN ;
}
priv - > stats . rx_dropped + + ;
priv - > stats . rx_length_errors + + ;
return ;
}
skb_put ( pskb , header - > length ) ;
skb_reset_mac_header ( pskb ) ;
len - = header - > length ;
skb = dev_alloc_skb ( pskb - > len ) ;
if ( ! skb ) {
if ( ! ( ch - > logflags & LOG_FLAG_NOMEM ) ) {
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( ERROR , CTC_DBF_ERROR ,
" %s(%s): MEMORY allocation error " ,
CTCM_FUNTAIL , dev - > name ) ;
2008-02-08 02:03:49 +03:00
ch - > logflags | = LOG_FLAG_NOMEM ;
}
priv - > stats . rx_dropped + + ;
return ;
}
skb_copy_from_linear_data ( pskb , skb_put ( skb , pskb - > len ) ,
pskb - > len ) ;
skb_reset_mac_header ( skb ) ;
skb - > dev = pskb - > dev ;
skb - > protocol = pskb - > protocol ;
pskb - > ip_summed = CHECKSUM_UNNECESSARY ;
skblen = skb - > len ;
/*
* reset logflags
*/
ch - > logflags = 0 ;
priv - > stats . rx_packets + + ;
priv - > stats . rx_bytes + = skblen ;
netif_rx_ni ( skb ) ;
if ( len > 0 ) {
skb_pull ( pskb , header - > length ) ;
if ( skb_tailroom ( pskb ) < LL_HEADER_LENGTH ) {
2009-03-24 06:27:49 +03:00
CTCM_DBF_DEV_NAME ( TRACE , dev ,
" Overrun in ctcm_unpack_skb " ) ;
ch - > logflags | = LOG_FLAG_OVERRUN ;
2008-02-08 02:03:49 +03:00
return ;
}
skb_put ( pskb , LL_HEADER_LENGTH ) ;
}
}
}
/**
* Release a specific channel in the channel list .
*
* ch Pointer to channel struct to be released .
*/
static void channel_free ( struct channel * ch )
{
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( SETUP , CTC_DBF_INFO , " %s(%s) " , CTCM_FUNTAIL , ch - > id ) ;
2008-02-08 02:03:49 +03:00
ch - > flags & = ~ CHANNEL_FLAGS_INUSE ;
fsm_newstate ( ch - > fsm , CTC_STATE_IDLE ) ;
}
/**
* Remove a specific channel in the channel list .
*
* ch Pointer to channel struct to be released .
*/
static void channel_remove ( struct channel * ch )
{
struct channel * * c = & channels ;
char chid [ CTCM_ID_SIZE + 1 ] ;
int ok = 0 ;
if ( ch = = NULL )
return ;
else
strncpy ( chid , ch - > id , CTCM_ID_SIZE ) ;
channel_free ( ch ) ;
while ( * c ) {
if ( * c = = ch ) {
* c = ch - > next ;
fsm_deltimer ( & ch - > timer ) ;
if ( IS_MPC ( ch ) )
fsm_deltimer ( & ch - > sweep_timer ) ;
kfree_fsm ( ch - > fsm ) ;
clear_normalized_cda ( & ch - > ccw [ 4 ] ) ;
if ( ch - > trans_skb ! = NULL ) {
clear_normalized_cda ( & ch - > ccw [ 1 ] ) ;
dev_kfree_skb_any ( ch - > trans_skb ) ;
}
if ( IS_MPC ( ch ) ) {
tasklet_kill ( & ch - > ch_tasklet ) ;
tasklet_kill ( & ch - > ch_disc_tasklet ) ;
kfree ( ch - > discontact_th ) ;
}
kfree ( ch - > ccw ) ;
kfree ( ch - > irb ) ;
kfree ( ch ) ;
ok = 1 ;
break ;
}
c = & ( ( * c ) - > next ) ;
}
CTCM_DBF_TEXT_ ( SETUP , CTC_DBF_INFO , " %s(%s) %s " , CTCM_FUNTAIL ,
chid , ok ? " OK " : " failed " ) ;
}
/**
* Get a specific channel from the channel list .
*
* type Type of channel we are interested in .
* id Id of channel we are interested in .
* direction Direction we want to use this channel for .
*
* returns Pointer to a channel or NULL if no matching channel available .
*/
2009-11-13 00:46:29 +03:00
static struct channel * channel_get ( enum ctcm_channel_types type ,
2008-02-08 02:03:49 +03:00
char * id , int direction )
{
struct channel * ch = channels ;
while ( ch & & ( strncmp ( ch - > id , id , CTCM_ID_SIZE ) | | ( ch - > type ! = type ) ) )
ch = ch - > next ;
if ( ! ch ) {
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( ERROR , CTC_DBF_ERROR ,
" %s(%d, %s, %d) not found in channel list \n " ,
2008-02-08 02:03:49 +03:00
CTCM_FUNTAIL , type , id , direction ) ;
} else {
if ( ch - > flags & CHANNEL_FLAGS_INUSE )
ch = NULL ;
else {
ch - > flags | = CHANNEL_FLAGS_INUSE ;
ch - > flags & = ~ CHANNEL_FLAGS_RWMASK ;
2010-08-12 05:58:28 +04:00
ch - > flags | = ( direction = = CTCM_WRITE )
2008-02-08 02:03:49 +03:00
? CHANNEL_FLAGS_WRITE : CHANNEL_FLAGS_READ ;
fsm_newstate ( ch - > fsm , CTC_STATE_STOPPED ) ;
}
}
return ch ;
}
static long ctcm_check_irb_error ( struct ccw_device * cdev , struct irb * irb )
{
if ( ! IS_ERR ( irb ) )
return 0 ;
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( ERROR , CTC_DBF_WARN ,
" irb error %ld on device %s \n " ,
2008-10-10 23:33:09 +04:00
PTR_ERR ( irb ) , dev_name ( & cdev - > dev ) ) ;
2008-02-08 02:03:49 +03:00
switch ( PTR_ERR ( irb ) ) {
case - EIO :
2008-12-25 15:39:54 +03:00
dev_err ( & cdev - > dev ,
" An I/O-error occurred on the CTCM device \n " ) ;
2008-02-08 02:03:49 +03:00
break ;
case - ETIMEDOUT :
2008-12-25 15:39:54 +03:00
dev_err ( & cdev - > dev ,
" An adapter hardware operation timed out \n " ) ;
2008-02-08 02:03:49 +03:00
break ;
default :
2008-12-25 15:39:54 +03:00
dev_err ( & cdev - > dev ,
" An error occurred on the adapter hardware \n " ) ;
2008-02-08 02:03:49 +03:00
}
return PTR_ERR ( irb ) ;
}
/**
* Check sense of a unit check .
*
* ch The channel , the sense code belongs to .
* sense The sense code to inspect .
*/
2017-08-15 18:02:46 +03:00
static void ccw_unit_check ( struct channel * ch , __u8 sense )
2008-02-08 02:03:49 +03:00
{
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( TRACE , CTC_DBF_DEBUG ,
" %s(%s): %02x " ,
CTCM_FUNTAIL , ch - > id , sense ) ;
2008-02-08 02:03:49 +03:00
if ( sense & SNS0_INTERVENTION_REQ ) {
if ( sense & 0x01 ) {
2008-07-18 17:24:57 +04:00
if ( ch - > sense_rc ! = 0x01 ) {
2008-12-25 15:39:54 +03:00
pr_notice (
" %s: The communication peer has "
" disconnected \n " , ch - > id ) ;
2008-07-18 17:24:57 +04:00
ch - > sense_rc = 0x01 ;
}
2008-02-08 02:03:49 +03:00
fsm_event ( ch - > fsm , CTC_EVENT_UC_RCRESET , ch ) ;
} else {
2008-07-18 17:24:57 +04:00
if ( ch - > sense_rc ! = SNS0_INTERVENTION_REQ ) {
2008-12-25 15:39:54 +03:00
pr_notice (
" %s: The remote operating system is "
" not available \n " , ch - > id ) ;
2008-07-18 17:24:57 +04:00
ch - > sense_rc = SNS0_INTERVENTION_REQ ;
}
2008-02-08 02:03:49 +03:00
fsm_event ( ch - > fsm , CTC_EVENT_UC_RSRESET , ch ) ;
}
} else if ( sense & SNS0_EQUIPMENT_CHECK ) {
if ( sense & SNS0_BUS_OUT_CHECK ) {
2008-07-18 17:24:57 +04:00
if ( ch - > sense_rc ! = SNS0_BUS_OUT_CHECK ) {
CTCM_DBF_TEXT_ ( TRACE , CTC_DBF_WARN ,
" %s(%s): remote HW error %02x " ,
CTCM_FUNTAIL , ch - > id , sense ) ;
ch - > sense_rc = SNS0_BUS_OUT_CHECK ;
}
2008-02-08 02:03:49 +03:00
fsm_event ( ch - > fsm , CTC_EVENT_UC_HWFAIL , ch ) ;
} else {
2008-07-18 17:24:57 +04:00
if ( ch - > sense_rc ! = SNS0_EQUIPMENT_CHECK ) {
CTCM_DBF_TEXT_ ( TRACE , CTC_DBF_WARN ,
" %s(%s): remote read parity error %02x " ,
CTCM_FUNTAIL , ch - > id , sense ) ;
ch - > sense_rc = SNS0_EQUIPMENT_CHECK ;
}
2008-02-08 02:03:49 +03:00
fsm_event ( ch - > fsm , CTC_EVENT_UC_RXPARITY , ch ) ;
}
} else if ( sense & SNS0_BUS_OUT_CHECK ) {
2008-07-18 17:24:57 +04:00
if ( ch - > sense_rc ! = SNS0_BUS_OUT_CHECK ) {
CTCM_DBF_TEXT_ ( TRACE , CTC_DBF_WARN ,
" %s(%s): BUS OUT error %02x " ,
CTCM_FUNTAIL , ch - > id , sense ) ;
ch - > sense_rc = SNS0_BUS_OUT_CHECK ;
}
if ( sense & 0x04 ) /* data-streaming timeout */
2008-02-08 02:03:49 +03:00
fsm_event ( ch - > fsm , CTC_EVENT_UC_TXTIMEOUT , ch ) ;
2008-07-18 17:24:57 +04:00
else /* Data-transfer parity error */
2008-02-08 02:03:49 +03:00
fsm_event ( ch - > fsm , CTC_EVENT_UC_TXPARITY , ch ) ;
} else if ( sense & SNS0_CMD_REJECT ) {
2008-07-18 17:24:57 +04:00
if ( ch - > sense_rc ! = SNS0_CMD_REJECT ) {
CTCM_DBF_TEXT_ ( TRACE , CTC_DBF_WARN ,
" %s(%s): Command rejected " ,
CTCM_FUNTAIL , ch - > id ) ;
ch - > sense_rc = SNS0_CMD_REJECT ;
}
2008-02-08 02:03:49 +03:00
} else if ( sense = = 0 ) {
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( TRACE , CTC_DBF_WARN ,
" %s(%s): Unit check ZERO " ,
CTCM_FUNTAIL , ch - > id ) ;
2008-02-08 02:03:49 +03:00
fsm_event ( ch - > fsm , CTC_EVENT_UC_ZERO , ch ) ;
} else {
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( TRACE , CTC_DBF_WARN ,
" %s(%s): Unit check code %02x unknown " ,
CTCM_FUNTAIL , ch - > id , sense ) ;
2008-02-08 02:03:49 +03:00
fsm_event ( ch - > fsm , CTC_EVENT_UC_UNKNOWN , ch ) ;
}
}
int ctcm_ch_alloc_buffer ( struct channel * ch )
{
clear_normalized_cda ( & ch - > ccw [ 1 ] ) ;
ch - > trans_skb = __dev_alloc_skb ( ch - > max_bufsize , GFP_ATOMIC | GFP_DMA ) ;
if ( ch - > trans_skb = = NULL ) {
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( ERROR , CTC_DBF_ERROR ,
" %s(%s): %s trans_skb allocation error " ,
CTCM_FUNTAIL , ch - > id ,
2010-08-12 05:58:28 +04:00
( CHANNEL_DIRECTION ( ch - > flags ) = = CTCM_READ ) ?
" RX " : " TX " ) ;
2008-02-08 02:03:49 +03:00
return - ENOMEM ;
}
ch - > ccw [ 1 ] . count = ch - > max_bufsize ;
if ( set_normalized_cda ( & ch - > ccw [ 1 ] , ch - > trans_skb - > data ) ) {
dev_kfree_skb ( ch - > trans_skb ) ;
ch - > trans_skb = NULL ;
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( ERROR , CTC_DBF_ERROR ,
" %s(%s): %s set norm_cda failed " ,
CTCM_FUNTAIL , ch - > id ,
2010-08-12 05:58:28 +04:00
( CHANNEL_DIRECTION ( ch - > flags ) = = CTCM_READ ) ?
" RX " : " TX " ) ;
2008-02-08 02:03:49 +03:00
return - ENOMEM ;
}
ch - > ccw [ 1 ] . count = 0 ;
ch - > trans_skb_data = ch - > trans_skb - > data ;
ch - > flags & = ~ CHANNEL_FLAGS_BUFSIZE_CHANGED ;
return 0 ;
}
/*
* Interface API for upper network layers
*/
/**
* Open an interface .
* Called from generic network layer when ifconfig up is run .
*
* dev Pointer to interface struct .
*
* returns 0 on success , - ERRNO on failure . ( Never fails . )
*/
int ctcm_open ( struct net_device * dev )
{
2008-08-21 19:10:24 +04:00
struct ctcm_priv * priv = dev - > ml_priv ;
2008-02-08 02:03:49 +03:00
CTCMY_DBF_DEV_NAME ( SETUP , dev , " " ) ;
if ( ! IS_MPC ( priv ) )
fsm_event ( priv - > fsm , DEV_EVENT_START , dev ) ;
return 0 ;
}
/**
* Close an interface .
* Called from generic network layer when ifconfig down is run .
*
* dev Pointer to interface struct .
*
* returns 0 on success , - ERRNO on failure . ( Never fails . )
*/
int ctcm_close ( struct net_device * dev )
{
2008-08-21 19:10:24 +04:00
struct ctcm_priv * priv = dev - > ml_priv ;
2008-02-08 02:03:49 +03:00
CTCMY_DBF_DEV_NAME ( SETUP , dev , " " ) ;
if ( ! IS_MPC ( priv ) )
fsm_event ( priv - > fsm , DEV_EVENT_STOP , dev ) ;
return 0 ;
}
/**
* Transmit a packet .
* This is a helper function for ctcm_tx ( ) .
*
* ch Channel to be used for sending .
* skb Pointer to struct sk_buff of packet to send .
* The linklevel header has already been set up
* by ctcm_tx ( ) .
*
* returns 0 on success , - ERRNO on failure . ( Never fails . )
*/
static int ctcm_transmit_skb ( struct channel * ch , struct sk_buff * skb )
{
unsigned long saveflags ;
struct ll_header header ;
int rc = 0 ;
__u16 block_len ;
int ccw_idx ;
struct sk_buff * nskb ;
unsigned long hi ;
/* we need to acquire the lock for testing the state
* otherwise we can have an IRQ changing the state to
* TXIDLE after the test but before acquiring the lock .
*/
spin_lock_irqsave ( & ch - > collect_lock , saveflags ) ;
if ( fsm_getstate ( ch - > fsm ) ! = CTC_STATE_TXIDLE ) {
int l = skb - > len + LL_HEADER_LENGTH ;
if ( ch - > collect_len + l > ch - > max_bufsize - 2 ) {
spin_unlock_irqrestore ( & ch - > collect_lock , saveflags ) ;
return - EBUSY ;
} else {
2017-06-30 13:07:58 +03:00
refcount_inc ( & skb - > users ) ;
2008-02-08 02:03:49 +03:00
header . length = l ;
2017-04-07 10:15:37 +03:00
header . type = be16_to_cpu ( skb - > protocol ) ;
2008-02-08 02:03:49 +03:00
header . unused = 0 ;
memcpy ( skb_push ( skb , LL_HEADER_LENGTH ) , & header ,
LL_HEADER_LENGTH ) ;
skb_queue_tail ( & ch - > collect_queue , skb ) ;
ch - > collect_len + = l ;
}
spin_unlock_irqrestore ( & ch - > collect_lock , saveflags ) ;
goto done ;
}
spin_unlock_irqrestore ( & ch - > collect_lock , saveflags ) ;
/*
* Protect skb against beeing free ' d by upper
* layers .
*/
2017-06-30 13:07:58 +03:00
refcount_inc ( & skb - > users ) ;
2008-02-08 02:03:49 +03:00
ch - > prof . txlen + = skb - > len ;
header . length = skb - > len + LL_HEADER_LENGTH ;
2017-04-07 10:15:37 +03:00
header . type = be16_to_cpu ( skb - > protocol ) ;
2008-02-08 02:03:49 +03:00
header . unused = 0 ;
memcpy ( skb_push ( skb , LL_HEADER_LENGTH ) , & header , LL_HEADER_LENGTH ) ;
block_len = skb - > len + 2 ;
* ( ( __u16 * ) skb_push ( skb , 2 ) ) = block_len ;
/*
* IDAL support in CTCM is broken , so we have to
* care about skb ' s above 2 G ourselves .
*/
hi = ( ( unsigned long ) skb_tail_pointer ( skb ) + LL_HEADER_LENGTH ) > > 31 ;
if ( hi ) {
nskb = alloc_skb ( skb - > len , GFP_ATOMIC | GFP_DMA ) ;
if ( ! nskb ) {
2017-06-30 13:07:58 +03:00
refcount_dec ( & skb - > users ) ;
2008-02-08 02:03:49 +03:00
skb_pull ( skb , LL_HEADER_LENGTH + 2 ) ;
ctcm_clear_busy ( ch - > netdev ) ;
return - ENOMEM ;
} else {
networking: introduce and use skb_put_data()
A common pattern with skb_put() is to just want to memcpy()
some data into the new space, introduce skb_put_data() for
this.
An spatch similar to the one for skb_put_zero() converts many
of the places using it:
@@
identifier p, p2;
expression len, skb, data;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_data(skb, data, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_data(skb, data, len);
)
(
p2 = (t2)p;
-memcpy(p2, data, len);
|
-memcpy(p, data, len);
)
@@
type t, t2;
identifier p, p2;
expression skb, data;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
)
(
p2 = (t2)p;
-memcpy(p2, data, sizeof(*p));
|
-memcpy(p, data, sizeof(*p));
)
@@
expression skb, len, data;
@@
-memcpy(skb_put(skb, len), data, len);
+skb_put_data(skb, data, len);
(again, manually post-processed to retain some comments)
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 15:29:20 +03:00
skb_put_data ( nskb , skb - > data , skb - > len ) ;
2017-06-30 13:07:58 +03:00
refcount_inc ( & nskb - > users ) ;
refcount_dec ( & skb - > users ) ;
2008-02-08 02:03:49 +03:00
dev_kfree_skb_irq ( skb ) ;
skb = nskb ;
}
}
ch - > ccw [ 4 ] . count = block_len ;
if ( set_normalized_cda ( & ch - > ccw [ 4 ] , skb - > data ) ) {
/*
* idal allocation failed , try via copying to
* trans_skb . trans_skb usually has a pre - allocated
* idal .
*/
if ( ctcm_checkalloc_buffer ( ch ) ) {
/*
* Remove our header . It gets added
* again on retransmit .
*/
2017-06-30 13:07:58 +03:00
refcount_dec ( & skb - > users ) ;
2008-02-08 02:03:49 +03:00
skb_pull ( skb , LL_HEADER_LENGTH + 2 ) ;
ctcm_clear_busy ( ch - > netdev ) ;
2008-07-18 17:24:57 +04:00
return - ENOMEM ;
2008-02-08 02:03:49 +03:00
}
skb_reset_tail_pointer ( ch - > trans_skb ) ;
ch - > trans_skb - > len = 0 ;
ch - > ccw [ 1 ] . count = skb - > len ;
skb_copy_from_linear_data ( skb ,
skb_put ( ch - > trans_skb , skb - > len ) , skb - > len ) ;
2017-06-30 13:07:58 +03:00
refcount_dec ( & skb - > users ) ;
2008-02-08 02:03:49 +03:00
dev_kfree_skb_irq ( skb ) ;
ccw_idx = 0 ;
} else {
skb_queue_tail ( & ch - > io_queue , skb ) ;
ccw_idx = 3 ;
}
2012-03-07 06:06:26 +04:00
if ( do_debug_ccw )
ctcmpc_dumpit ( ( char * ) & ch - > ccw [ ccw_idx ] ,
sizeof ( struct ccw1 ) * 3 ) ;
2008-02-08 02:03:49 +03:00
ch - > retry = 0 ;
fsm_newstate ( ch - > fsm , CTC_STATE_TX ) ;
fsm_addtimer ( & ch - > timer , CTCM_TIME_5_SEC , CTC_EVENT_TIMER , ch ) ;
spin_lock_irqsave ( get_ccwdev_lock ( ch - > cdev ) , saveflags ) ;
2015-01-16 16:05:45 +03:00
ch - > prof . send_stamp = jiffies ;
2019-08-20 17:46:42 +03:00
rc = ccw_device_start ( ch - > cdev , & ch - > ccw [ ccw_idx ] , 0 , 0xff , 0 ) ;
2008-02-08 02:03:49 +03:00
spin_unlock_irqrestore ( get_ccwdev_lock ( ch - > cdev ) , saveflags ) ;
if ( ccw_idx = = 3 )
ch - > prof . doios_single + + ;
if ( rc ! = 0 ) {
fsm_deltimer ( & ch - > timer ) ;
ctcm_ccw_check_rc ( ch , rc , " single skb TX " ) ;
if ( ccw_idx = = 3 )
skb_dequeue_tail ( & ch - > io_queue ) ;
/*
* Remove our header . It gets added
* again on retransmit .
*/
skb_pull ( skb , LL_HEADER_LENGTH + 2 ) ;
} else if ( ccw_idx = = 0 ) {
struct net_device * dev = ch - > netdev ;
2008-08-21 19:10:24 +04:00
struct ctcm_priv * priv = dev - > ml_priv ;
2008-02-08 02:03:49 +03:00
priv - > stats . tx_packets + + ;
priv - > stats . tx_bytes + = skb - > len - LL_HEADER_LENGTH ;
}
done :
ctcm_clear_busy ( ch - > netdev ) ;
return rc ;
}
static void ctcmpc_send_sweep_req ( struct channel * rch )
{
struct net_device * dev = rch - > netdev ;
struct ctcm_priv * priv ;
struct mpc_group * grp ;
struct th_sweep * header ;
struct sk_buff * sweep_skb ;
struct channel * ch ;
2008-07-18 17:24:57 +04:00
/* int rc = 0; */
2008-02-08 02:03:49 +03:00
2008-08-21 19:10:24 +04:00
priv = dev - > ml_priv ;
2008-02-08 02:03:49 +03:00
grp = priv - > mpcg ;
2010-08-12 05:58:28 +04:00
ch = priv - > channel [ CTCM_WRITE ] ;
2008-02-08 02:03:49 +03:00
/* sweep processing is not complete until response and request */
/* has completed for all read channels in group */
if ( grp - > in_sweep = = 0 ) {
grp - > in_sweep = 1 ;
2010-08-12 05:58:28 +04:00
grp - > sweep_rsp_pend_num = grp - > active_channels [ CTCM_READ ] ;
grp - > sweep_req_pend_num = grp - > active_channels [ CTCM_READ ] ;
2008-02-08 02:03:49 +03:00
}
sweep_skb = __dev_alloc_skb ( MPC_BUFSIZE_DEFAULT , GFP_ATOMIC | GFP_DMA ) ;
if ( sweep_skb = = NULL ) {
2008-07-18 17:24:57 +04:00
/* rc = -ENOMEM; */
goto nomem ;
2008-02-08 02:03:49 +03:00
}
header = kmalloc ( TH_SWEEP_LENGTH , gfp_type ( ) ) ;
if ( ! header ) {
dev_kfree_skb_any ( sweep_skb ) ;
2008-07-18 17:24:57 +04:00
/* rc = -ENOMEM; */
goto nomem ;
2008-02-08 02:03:49 +03:00
}
header - > th . th_seg = 0x00 ;
header - > th . th_ch_flag = TH_SWEEP_REQ ; /* 0x0f */
header - > th . th_blk_flag = 0x00 ;
header - > th . th_is_xid = 0x00 ;
header - > th . th_seq_num = 0x00 ;
header - > sw . th_last_seq = ch - > th_seq_num ;
networking: introduce and use skb_put_data()
A common pattern with skb_put() is to just want to memcpy()
some data into the new space, introduce skb_put_data() for
this.
An spatch similar to the one for skb_put_zero() converts many
of the places using it:
@@
identifier p, p2;
expression len, skb, data;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_data(skb, data, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_data(skb, data, len);
)
(
p2 = (t2)p;
-memcpy(p2, data, len);
|
-memcpy(p, data, len);
)
@@
type t, t2;
identifier p, p2;
expression skb, data;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
)
(
p2 = (t2)p;
-memcpy(p2, data, sizeof(*p));
|
-memcpy(p, data, sizeof(*p));
)
@@
expression skb, len, data;
@@
-memcpy(skb_put(skb, len), data, len);
+skb_put_data(skb, data, len);
(again, manually post-processed to retain some comments)
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 15:29:20 +03:00
skb_put_data ( sweep_skb , header , TH_SWEEP_LENGTH ) ;
2008-02-08 02:03:49 +03:00
kfree ( header ) ;
2016-05-03 17:33:13 +03:00
netif_trans_update ( dev ) ;
2008-02-08 02:03:49 +03:00
skb_queue_tail ( & ch - > sweep_queue , sweep_skb ) ;
fsm_addtimer ( & ch - > sweep_timer , 100 , CTC_EVENT_RSWEEP_TIMER , ch ) ;
return ;
2008-07-18 17:24:57 +04:00
nomem :
grp - > in_sweep = 0 ;
ctcm_clear_busy ( dev ) ;
fsm_event ( grp - > fsm , MPCG_EVENT_INOP , dev ) ;
2008-02-08 02:03:49 +03:00
return ;
}
/*
* MPC mode version of transmit_skb
*/
static int ctcmpc_transmit_skb ( struct channel * ch , struct sk_buff * skb )
{
struct pdu * p_header ;
struct net_device * dev = ch - > netdev ;
2008-08-21 19:10:24 +04:00
struct ctcm_priv * priv = dev - > ml_priv ;
2008-02-08 02:03:49 +03:00
struct mpc_group * grp = priv - > mpcg ;
struct th_header * header ;
struct sk_buff * nskb ;
int rc = 0 ;
int ccw_idx ;
unsigned long hi ;
unsigned long saveflags = 0 ; /* avoids compiler warning */
2008-07-18 17:24:57 +04:00
CTCM_PR_DEBUG ( " Enter %s: %s, cp=%i ch=0x%p id=%s state=%s \n " ,
__func__ , dev - > name , smp_processor_id ( ) , ch ,
ch - > id , fsm_getstate_str ( ch - > fsm ) ) ;
2008-02-08 02:03:49 +03:00
if ( ( fsm_getstate ( ch - > fsm ) ! = CTC_STATE_TXIDLE ) | | grp - > in_sweep ) {
spin_lock_irqsave ( & ch - > collect_lock , saveflags ) ;
2017-06-30 13:07:58 +03:00
refcount_inc ( & skb - > users ) ;
2008-02-08 02:03:49 +03:00
p_header = kmalloc ( PDU_HEADER_LENGTH , gfp_type ( ) ) ;
if ( ! p_header ) {
spin_unlock_irqrestore ( & ch - > collect_lock , saveflags ) ;
2008-07-18 17:24:57 +04:00
goto nomem_exit ;
2008-02-08 02:03:49 +03:00
}
p_header - > pdu_offset = skb - > len ;
p_header - > pdu_proto = 0x01 ;
p_header - > pdu_flag = 0x00 ;
2017-04-07 10:15:37 +03:00
if ( be16_to_cpu ( skb - > protocol ) = = ETH_P_SNAP ) {
2008-02-08 02:03:49 +03:00
p_header - > pdu_flag | = PDU_FIRST | PDU_CNTL ;
} else {
p_header - > pdu_flag | = PDU_FIRST ;
}
p_header - > pdu_seq = 0 ;
memcpy ( skb_push ( skb , PDU_HEADER_LENGTH ) , p_header ,
PDU_HEADER_LENGTH ) ;
2008-07-18 17:24:57 +04:00
CTCM_PR_DEBUG ( " %s(%s): Put on collect_q - skb len: %04x \n "
" pdu header and data for up to 32 bytes: \n " ,
__func__ , dev - > name , skb - > len ) ;
CTCM_D3_DUMP ( ( char * ) skb - > data , min_t ( int , 32 , skb - > len ) ) ;
2008-02-08 02:03:49 +03:00
skb_queue_tail ( & ch - > collect_queue , skb ) ;
ch - > collect_len + = skb - > len ;
kfree ( p_header ) ;
spin_unlock_irqrestore ( & ch - > collect_lock , saveflags ) ;
goto done ;
}
/*
* Protect skb against beeing free ' d by upper
* layers .
*/
2017-06-30 13:07:58 +03:00
refcount_inc ( & skb - > users ) ;
2008-02-08 02:03:49 +03:00
/*
* IDAL support in CTCM is broken , so we have to
* care about skb ' s above 2 G ourselves .
*/
hi = ( ( unsigned long ) skb - > tail + TH_HEADER_LENGTH ) > > 31 ;
if ( hi ) {
nskb = __dev_alloc_skb ( skb - > len , GFP_ATOMIC | GFP_DMA ) ;
if ( ! nskb ) {
2008-07-18 17:24:57 +04:00
goto nomem_exit ;
2008-02-08 02:03:49 +03:00
} else {
networking: introduce and use skb_put_data()
A common pattern with skb_put() is to just want to memcpy()
some data into the new space, introduce skb_put_data() for
this.
An spatch similar to the one for skb_put_zero() converts many
of the places using it:
@@
identifier p, p2;
expression len, skb, data;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_data(skb, data, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_data(skb, data, len);
)
(
p2 = (t2)p;
-memcpy(p2, data, len);
|
-memcpy(p, data, len);
)
@@
type t, t2;
identifier p, p2;
expression skb, data;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
)
(
p2 = (t2)p;
-memcpy(p2, data, sizeof(*p));
|
-memcpy(p, data, sizeof(*p));
)
@@
expression skb, len, data;
@@
-memcpy(skb_put(skb, len), data, len);
+skb_put_data(skb, data, len);
(again, manually post-processed to retain some comments)
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 15:29:20 +03:00
skb_put_data ( nskb , skb - > data , skb - > len ) ;
2017-06-30 13:07:58 +03:00
refcount_inc ( & nskb - > users ) ;
refcount_dec ( & skb - > users ) ;
2008-02-08 02:03:49 +03:00
dev_kfree_skb_irq ( skb ) ;
skb = nskb ;
}
}
p_header = kmalloc ( PDU_HEADER_LENGTH , gfp_type ( ) ) ;
2008-07-18 17:24:57 +04:00
if ( ! p_header )
goto nomem_exit ;
2008-02-08 02:03:49 +03:00
p_header - > pdu_offset = skb - > len ;
p_header - > pdu_proto = 0x01 ;
p_header - > pdu_flag = 0x00 ;
p_header - > pdu_seq = 0 ;
2017-04-07 10:15:37 +03:00
if ( be16_to_cpu ( skb - > protocol ) = = ETH_P_SNAP ) {
2008-02-08 02:03:49 +03:00
p_header - > pdu_flag | = PDU_FIRST | PDU_CNTL ;
} else {
p_header - > pdu_flag | = PDU_FIRST ;
}
memcpy ( skb_push ( skb , PDU_HEADER_LENGTH ) , p_header , PDU_HEADER_LENGTH ) ;
kfree ( p_header ) ;
if ( ch - > collect_len > 0 ) {
spin_lock_irqsave ( & ch - > collect_lock , saveflags ) ;
skb_queue_tail ( & ch - > collect_queue , skb ) ;
ch - > collect_len + = skb - > len ;
skb = skb_dequeue ( & ch - > collect_queue ) ;
ch - > collect_len - = skb - > len ;
spin_unlock_irqrestore ( & ch - > collect_lock , saveflags ) ;
}
p_header = ( struct pdu * ) skb - > data ;
p_header - > pdu_flag | = PDU_LAST ;
ch - > prof . txlen + = skb - > len - PDU_HEADER_LENGTH ;
header = kmalloc ( TH_HEADER_LENGTH , gfp_type ( ) ) ;
2008-07-18 17:24:57 +04:00
if ( ! header )
goto nomem_exit ;
2008-02-08 02:03:49 +03:00
header - > th_seg = 0x00 ;
header - > th_ch_flag = TH_HAS_PDU ; /* Normal data */
header - > th_blk_flag = 0x00 ;
header - > th_is_xid = 0x00 ; /* Just data here */
ch - > th_seq_num + + ;
header - > th_seq_num = ch - > th_seq_num ;
2008-07-18 17:24:57 +04:00
CTCM_PR_DBGDATA ( " %s(%s) ToVTAM_th_seq= %08x \n " ,
__func__ , dev - > name , ch - > th_seq_num ) ;
2008-02-08 02:03:49 +03:00
/* put the TH on the packet */
memcpy ( skb_push ( skb , TH_HEADER_LENGTH ) , header , TH_HEADER_LENGTH ) ;
kfree ( header ) ;
2008-07-18 17:24:57 +04:00
CTCM_PR_DBGDATA ( " %s(%s): skb len: %04x \n - pdu header and data for "
" up to 32 bytes sent to vtam: \n " ,
__func__ , dev - > name , skb - > len ) ;
CTCM_D3_DUMP ( ( char * ) skb - > data , min_t ( int , 32 , skb - > len ) ) ;
2008-02-08 02:03:49 +03:00
ch - > ccw [ 4 ] . count = skb - > len ;
if ( set_normalized_cda ( & ch - > ccw [ 4 ] , skb - > data ) ) {
/*
2008-07-18 17:24:57 +04:00
* idal allocation failed , try via copying to trans_skb .
* trans_skb usually has a pre - allocated idal .
2008-02-08 02:03:49 +03:00
*/
if ( ctcm_checkalloc_buffer ( ch ) ) {
/*
2008-07-18 17:24:57 +04:00
* Remove our header .
* It gets added again on retransmit .
2008-02-08 02:03:49 +03:00
*/
2008-07-18 17:24:57 +04:00
goto nomem_exit ;
2008-02-08 02:03:49 +03:00
}
skb_reset_tail_pointer ( ch - > trans_skb ) ;
ch - > trans_skb - > len = 0 ;
ch - > ccw [ 1 ] . count = skb - > len ;
networking: introduce and use skb_put_data()
A common pattern with skb_put() is to just want to memcpy()
some data into the new space, introduce skb_put_data() for
this.
An spatch similar to the one for skb_put_zero() converts many
of the places using it:
@@
identifier p, p2;
expression len, skb, data;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_data(skb, data, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_data(skb, data, len);
)
(
p2 = (t2)p;
-memcpy(p2, data, len);
|
-memcpy(p, data, len);
)
@@
type t, t2;
identifier p, p2;
expression skb, data;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
)
(
p2 = (t2)p;
-memcpy(p2, data, sizeof(*p));
|
-memcpy(p, data, sizeof(*p));
)
@@
expression skb, len, data;
@@
-memcpy(skb_put(skb, len), data, len);
+skb_put_data(skb, data, len);
(again, manually post-processed to retain some comments)
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 15:29:20 +03:00
skb_put_data ( ch - > trans_skb , skb - > data , skb - > len ) ;
2017-06-30 13:07:58 +03:00
refcount_dec ( & skb - > users ) ;
2008-02-08 02:03:49 +03:00
dev_kfree_skb_irq ( skb ) ;
ccw_idx = 0 ;
2008-07-18 17:24:57 +04:00
CTCM_PR_DBGDATA ( " %s(%s): trans_skb len: %04x \n "
" up to 32 bytes sent to vtam: \n " ,
__func__ , dev - > name , ch - > trans_skb - > len ) ;
CTCM_D3_DUMP ( ( char * ) ch - > trans_skb - > data ,
min_t ( int , 32 , ch - > trans_skb - > len ) ) ;
2008-02-08 02:03:49 +03:00
} else {
skb_queue_tail ( & ch - > io_queue , skb ) ;
ccw_idx = 3 ;
}
ch - > retry = 0 ;
fsm_newstate ( ch - > fsm , CTC_STATE_TX ) ;
fsm_addtimer ( & ch - > timer , CTCM_TIME_5_SEC , CTC_EVENT_TIMER , ch ) ;
if ( do_debug_ccw )
ctcmpc_dumpit ( ( char * ) & ch - > ccw [ ccw_idx ] ,
sizeof ( struct ccw1 ) * 3 ) ;
spin_lock_irqsave ( get_ccwdev_lock ( ch - > cdev ) , saveflags ) ;
2015-01-16 16:05:45 +03:00
ch - > prof . send_stamp = jiffies ;
2019-08-20 17:46:42 +03:00
rc = ccw_device_start ( ch - > cdev , & ch - > ccw [ ccw_idx ] , 0 , 0xff , 0 ) ;
2008-02-08 02:03:49 +03:00
spin_unlock_irqrestore ( get_ccwdev_lock ( ch - > cdev ) , saveflags ) ;
if ( ccw_idx = = 3 )
ch - > prof . doios_single + + ;
if ( rc ! = 0 ) {
fsm_deltimer ( & ch - > timer ) ;
ctcm_ccw_check_rc ( ch , rc , " single skb TX " ) ;
if ( ccw_idx = = 3 )
skb_dequeue_tail ( & ch - > io_queue ) ;
} else if ( ccw_idx = = 0 ) {
priv - > stats . tx_packets + + ;
priv - > stats . tx_bytes + = skb - > len - TH_HEADER_LENGTH ;
}
2008-07-18 17:24:57 +04:00
if ( ch - > th_seq_num > 0xf0000000 ) /* Chose at random. */
2008-02-08 02:03:49 +03:00
ctcmpc_send_sweep_req ( ch ) ;
2008-07-18 17:24:57 +04:00
goto done ;
nomem_exit :
CTCM_DBF_TEXT_ ( MPC_ERROR , CTC_DBF_CRIT ,
" %s(%s): MEMORY allocation ERROR \n " ,
CTCM_FUNTAIL , ch - > id ) ;
rc = - ENOMEM ;
2017-06-30 13:07:58 +03:00
refcount_dec ( & skb - > users ) ;
2008-07-18 17:24:57 +04:00
dev_kfree_skb_any ( skb ) ;
fsm_event ( priv - > mpcg - > fsm , MPCG_EVENT_INOP , dev ) ;
2008-02-08 02:03:49 +03:00
done :
2008-07-18 17:24:57 +04:00
CTCM_PR_DEBUG ( " Exit %s(%s) \n " , __func__ , dev - > name ) ;
return rc ;
2008-02-08 02:03:49 +03:00
}
/**
* Start transmission of a packet .
* Called from generic network device layer .
*
* skb Pointer to buffer containing the packet .
* dev Pointer to interface struct .
*
* returns 0 if packet consumed , ! 0 if packet rejected .
* Note : If we return ! 0 , then the packet is free ' d by
* the generic network layer .
*/
/* first merge version - leaving both functions separated */
static int ctcm_tx ( struct sk_buff * skb , struct net_device * dev )
{
2008-08-21 19:10:24 +04:00
struct ctcm_priv * priv = dev - > ml_priv ;
2008-02-08 02:03:49 +03:00
if ( skb = = NULL ) {
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( ERROR , CTC_DBF_ERROR ,
" %s(%s): NULL sk_buff passed " ,
CTCM_FUNTAIL , dev - > name ) ;
2008-02-08 02:03:49 +03:00
priv - > stats . tx_dropped + + ;
2009-06-23 10:03:08 +04:00
return NETDEV_TX_OK ;
2008-02-08 02:03:49 +03:00
}
if ( skb_headroom ( skb ) < ( LL_HEADER_LENGTH + 2 ) ) {
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( ERROR , CTC_DBF_ERROR ,
" %s(%s): Got sk_buff with head room < %ld bytes " ,
CTCM_FUNTAIL , dev - > name , LL_HEADER_LENGTH + 2 ) ;
2008-02-08 02:03:49 +03:00
dev_kfree_skb ( skb ) ;
priv - > stats . tx_dropped + + ;
2009-06-23 10:03:08 +04:00
return NETDEV_TX_OK ;
2008-02-08 02:03:49 +03:00
}
/*
* If channels are not running , try to restart them
* and throw away packet .
*/
if ( fsm_getstate ( priv - > fsm ) ! = DEV_STATE_RUNNING ) {
fsm_event ( priv - > fsm , DEV_EVENT_START , dev ) ;
dev_kfree_skb ( skb ) ;
priv - > stats . tx_dropped + + ;
priv - > stats . tx_errors + + ;
priv - > stats . tx_carrier_errors + + ;
2009-06-23 10:03:08 +04:00
return NETDEV_TX_OK ;
2008-02-08 02:03:49 +03:00
}
if ( ctcm_test_and_set_busy ( dev ) )
2009-03-24 06:27:47 +03:00
return NETDEV_TX_BUSY ;
2008-02-08 02:03:49 +03:00
2016-05-03 17:33:13 +03:00
netif_trans_update ( dev ) ;
2010-08-12 05:58:28 +04:00
if ( ctcm_transmit_skb ( priv - > channel [ CTCM_WRITE ] , skb ) ! = 0 )
2009-03-24 06:27:47 +03:00
return NETDEV_TX_BUSY ;
2009-06-23 10:03:08 +04:00
return NETDEV_TX_OK ;
2008-02-08 02:03:49 +03:00
}
/* unmerged MPC variant of ctcm_tx */
static int ctcmpc_tx ( struct sk_buff * skb , struct net_device * dev )
{
int len = 0 ;
2008-08-21 19:10:24 +04:00
struct ctcm_priv * priv = dev - > ml_priv ;
2008-07-18 17:24:57 +04:00
struct mpc_group * grp = priv - > mpcg ;
2008-02-08 02:03:49 +03:00
struct sk_buff * newskb = NULL ;
/*
* Some sanity checks . . .
*/
if ( skb = = NULL ) {
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( MPC_ERROR , CTC_DBF_ERROR ,
" %s(%s): NULL sk_buff passed " ,
CTCM_FUNTAIL , dev - > name ) ;
2008-02-08 02:03:49 +03:00
priv - > stats . tx_dropped + + ;
goto done ;
}
if ( skb_headroom ( skb ) < ( TH_HEADER_LENGTH + PDU_HEADER_LENGTH ) ) {
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( MPC_TRACE , CTC_DBF_ERROR ,
" %s(%s): Got sk_buff with head room < %ld bytes " ,
CTCM_FUNTAIL , dev - > name ,
TH_HEADER_LENGTH + PDU_HEADER_LENGTH ) ;
2008-02-08 02:03:49 +03:00
2008-07-18 17:24:57 +04:00
CTCM_D3_DUMP ( ( char * ) skb - > data , min_t ( int , 32 , skb - > len ) ) ;
2008-02-08 02:03:49 +03:00
len = skb - > len + TH_HEADER_LENGTH + PDU_HEADER_LENGTH ;
newskb = __dev_alloc_skb ( len , gfp_type ( ) | GFP_DMA ) ;
if ( ! newskb ) {
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( MPC_TRACE , CTC_DBF_ERROR ,
" %s: %s: __dev_alloc_skb failed " ,
__func__ , dev - > name ) ;
2008-02-08 02:03:49 +03:00
dev_kfree_skb_any ( skb ) ;
priv - > stats . tx_dropped + + ;
priv - > stats . tx_errors + + ;
priv - > stats . tx_carrier_errors + + ;
fsm_event ( grp - > fsm , MPCG_EVENT_INOP , dev ) ;
goto done ;
}
newskb - > protocol = skb - > protocol ;
skb_reserve ( newskb , TH_HEADER_LENGTH + PDU_HEADER_LENGTH ) ;
networking: introduce and use skb_put_data()
A common pattern with skb_put() is to just want to memcpy()
some data into the new space, introduce skb_put_data() for
this.
An spatch similar to the one for skb_put_zero() converts many
of the places using it:
@@
identifier p, p2;
expression len, skb, data;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_data(skb, data, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_data(skb, data, len);
)
(
p2 = (t2)p;
-memcpy(p2, data, len);
|
-memcpy(p, data, len);
)
@@
type t, t2;
identifier p, p2;
expression skb, data;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
)
(
p2 = (t2)p;
-memcpy(p2, data, sizeof(*p));
|
-memcpy(p, data, sizeof(*p));
)
@@
expression skb, len, data;
@@
-memcpy(skb_put(skb, len), data, len);
+skb_put_data(skb, data, len);
(again, manually post-processed to retain some comments)
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 15:29:20 +03:00
skb_put_data ( newskb , skb - > data , skb - > len ) ;
2008-02-08 02:03:49 +03:00
dev_kfree_skb_any ( skb ) ;
skb = newskb ;
}
/*
* If channels are not running ,
* notify anybody about a link failure and throw
* away packet .
*/
if ( ( fsm_getstate ( priv - > fsm ) ! = DEV_STATE_RUNNING ) | |
( fsm_getstate ( grp - > fsm ) < MPCG_STATE_XID2INITW ) ) {
dev_kfree_skb_any ( skb ) ;
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( MPC_ERROR , CTC_DBF_ERROR ,
" %s(%s): inactive MPCGROUP - dropped " ,
CTCM_FUNTAIL , dev - > name ) ;
2008-02-08 02:03:49 +03:00
priv - > stats . tx_dropped + + ;
priv - > stats . tx_errors + + ;
priv - > stats . tx_carrier_errors + + ;
goto done ;
}
if ( ctcm_test_and_set_busy ( dev ) ) {
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( MPC_ERROR , CTC_DBF_ERROR ,
" %s(%s): device busy - dropped " ,
CTCM_FUNTAIL , dev - > name ) ;
2008-02-08 02:03:49 +03:00
dev_kfree_skb_any ( skb ) ;
priv - > stats . tx_dropped + + ;
priv - > stats . tx_errors + + ;
priv - > stats . tx_carrier_errors + + ;
fsm_event ( grp - > fsm , MPCG_EVENT_INOP , dev ) ;
goto done ;
}
2016-05-03 17:33:13 +03:00
netif_trans_update ( dev ) ;
2010-08-12 05:58:28 +04:00
if ( ctcmpc_transmit_skb ( priv - > channel [ CTCM_WRITE ] , skb ) ! = 0 ) {
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( MPC_ERROR , CTC_DBF_ERROR ,
" %s(%s): device error - dropped " ,
CTCM_FUNTAIL , dev - > name ) ;
2008-02-08 02:03:49 +03:00
dev_kfree_skb_any ( skb ) ;
priv - > stats . tx_dropped + + ;
priv - > stats . tx_errors + + ;
priv - > stats . tx_carrier_errors + + ;
ctcm_clear_busy ( dev ) ;
fsm_event ( grp - > fsm , MPCG_EVENT_INOP , dev ) ;
goto done ;
}
ctcm_clear_busy ( dev ) ;
done :
if ( do_debug )
MPC_DBF_DEV_NAME ( TRACE , dev , " exit " ) ;
2009-06-23 10:03:08 +04:00
return NETDEV_TX_OK ; /* handle freeing of skb here */
2008-02-08 02:03:49 +03:00
}
/**
* Sets MTU of an interface .
*
* dev Pointer to interface struct .
* new_mtu The new MTU to use for this interface .
*
* returns 0 on success , - EINVAL if MTU is out of valid range .
* ( valid range is 576 . . 65527 ) . If VM is on the
* remote side , maximum MTU is 32760 , however this is
* not checked here .
*/
static int ctcm_change_mtu ( struct net_device * dev , int new_mtu )
{
struct ctcm_priv * priv ;
int max_bufsize ;
2008-08-21 19:10:24 +04:00
priv = dev - > ml_priv ;
2010-08-12 05:58:28 +04:00
max_bufsize = priv - > channel [ CTCM_READ ] - > max_bufsize ;
2008-02-08 02:03:49 +03:00
if ( IS_MPC ( priv ) ) {
if ( new_mtu > max_bufsize - TH_HEADER_LENGTH )
return - EINVAL ;
dev - > hard_header_len = TH_HEADER_LENGTH + PDU_HEADER_LENGTH ;
} else {
if ( new_mtu > max_bufsize - LL_HEADER_LENGTH - 2 )
return - EINVAL ;
dev - > hard_header_len = LL_HEADER_LENGTH + 2 ;
}
dev - > mtu = new_mtu ;
return 0 ;
}
/**
* Returns interface statistics of a device .
*
* dev Pointer to interface struct .
*
* returns Pointer to stats struct of this interface .
*/
static struct net_device_stats * ctcm_stats ( struct net_device * dev )
{
2008-08-21 19:10:24 +04:00
return & ( ( struct ctcm_priv * ) dev - > ml_priv ) - > stats ;
2008-02-08 02:03:49 +03:00
}
static void ctcm_free_netdevice ( struct net_device * dev )
{
struct ctcm_priv * priv ;
struct mpc_group * grp ;
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( SETUP , CTC_DBF_INFO ,
" %s(%s) " , CTCM_FUNTAIL , dev - > name ) ;
2008-08-21 19:10:24 +04:00
priv = dev - > ml_priv ;
2008-02-08 02:03:49 +03:00
if ( priv ) {
grp = priv - > mpcg ;
if ( grp ) {
if ( grp - > fsm )
kfree_fsm ( grp - > fsm ) ;
2019-09-15 20:21:05 +03:00
dev_kfree_skb ( grp - > xid_skb ) ;
dev_kfree_skb ( grp - > rcvd_xid_skb ) ;
2008-02-08 02:03:49 +03:00
tasklet_kill ( & grp - > mpc_tasklet2 ) ;
kfree ( grp ) ;
priv - > mpcg = NULL ;
}
if ( priv - > fsm ) {
kfree_fsm ( priv - > fsm ) ;
priv - > fsm = NULL ;
}
kfree ( priv - > xid ) ;
priv - > xid = NULL ;
/*
* Note : kfree ( priv ) ; is done in " opposite " function of
* allocator function probe_device which is remove_device .
*/
}
# ifdef MODULE
free_netdev ( dev ) ;
# endif
}
struct mpc_group * ctcmpc_init_mpc_group ( struct ctcm_priv * priv ) ;
2009-01-09 06:43:57 +03:00
static const struct net_device_ops ctcm_netdev_ops = {
. ndo_open = ctcm_open ,
. ndo_stop = ctcm_close ,
. ndo_get_stats = ctcm_stats ,
. ndo_change_mtu = ctcm_change_mtu ,
. ndo_start_xmit = ctcm_tx ,
} ;
static const struct net_device_ops ctcm_mpc_netdev_ops = {
. ndo_open = ctcm_open ,
. ndo_stop = ctcm_close ,
. ndo_get_stats = ctcm_stats ,
. ndo_change_mtu = ctcm_change_mtu ,
. ndo_start_xmit = ctcmpc_tx ,
} ;
2017-07-13 00:37:34 +03:00
static void ctcm_dev_setup ( struct net_device * dev )
2008-02-08 02:03:49 +03:00
{
dev - > type = ARPHRD_SLIP ;
dev - > tx_queue_len = 100 ;
dev - > flags = IFF_POINTOPOINT | IFF_NOARP ;
2016-10-20 20:55:23 +03:00
dev - > min_mtu = 576 ;
dev - > max_mtu = 65527 ;
2008-02-08 02:03:49 +03:00
}
/*
* Initialize everything of the net device except the name and the
* channel structs .
*/
static struct net_device * ctcm_init_netdevice ( struct ctcm_priv * priv )
{
struct net_device * dev ;
struct mpc_group * grp ;
if ( ! priv )
return NULL ;
if ( IS_MPC ( priv ) )
net: set name_assign_type in alloc_netdev()
Extend alloc_netdev{,_mq{,s}}() to take name_assign_type as argument, and convert
all users to pass NET_NAME_UNKNOWN.
Coccinelle patch:
@@
expression sizeof_priv, name, setup, txqs, rxqs, count;
@@
(
-alloc_netdev_mqs(sizeof_priv, name, setup, txqs, rxqs)
+alloc_netdev_mqs(sizeof_priv, name, NET_NAME_UNKNOWN, setup, txqs, rxqs)
|
-alloc_netdev_mq(sizeof_priv, name, setup, count)
+alloc_netdev_mq(sizeof_priv, name, NET_NAME_UNKNOWN, setup, count)
|
-alloc_netdev(sizeof_priv, name, setup)
+alloc_netdev(sizeof_priv, name, NET_NAME_UNKNOWN, setup)
)
v9: move comments here from the wrong commit
Signed-off-by: Tom Gundersen <teg@jklm.no>
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2014-07-14 18:37:24 +04:00
dev = alloc_netdev ( 0 , MPC_DEVICE_GENE , NET_NAME_UNKNOWN ,
ctcm_dev_setup ) ;
2008-02-08 02:03:49 +03:00
else
net: set name_assign_type in alloc_netdev()
Extend alloc_netdev{,_mq{,s}}() to take name_assign_type as argument, and convert
all users to pass NET_NAME_UNKNOWN.
Coccinelle patch:
@@
expression sizeof_priv, name, setup, txqs, rxqs, count;
@@
(
-alloc_netdev_mqs(sizeof_priv, name, setup, txqs, rxqs)
+alloc_netdev_mqs(sizeof_priv, name, NET_NAME_UNKNOWN, setup, txqs, rxqs)
|
-alloc_netdev_mq(sizeof_priv, name, setup, count)
+alloc_netdev_mq(sizeof_priv, name, NET_NAME_UNKNOWN, setup, count)
|
-alloc_netdev(sizeof_priv, name, setup)
+alloc_netdev(sizeof_priv, name, NET_NAME_UNKNOWN, setup)
)
v9: move comments here from the wrong commit
Signed-off-by: Tom Gundersen <teg@jklm.no>
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2014-07-14 18:37:24 +04:00
dev = alloc_netdev ( 0 , CTC_DEVICE_GENE , NET_NAME_UNKNOWN ,
ctcm_dev_setup ) ;
2008-02-08 02:03:49 +03:00
if ( ! dev ) {
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( ERROR , CTC_DBF_CRIT ,
" %s: MEMORY allocation ERROR " ,
CTCM_FUNTAIL ) ;
2008-02-08 02:03:49 +03:00
return NULL ;
}
2008-08-21 19:10:24 +04:00
dev - > ml_priv = priv ;
2008-02-08 02:03:49 +03:00
priv - > fsm = init_fsm ( " ctcmdev " , dev_state_names , dev_event_names ,
CTCM_NR_DEV_STATES , CTCM_NR_DEV_EVENTS ,
dev_fsm , dev_fsm_len , GFP_KERNEL ) ;
if ( priv - > fsm = = NULL ) {
CTCMY_DBF_DEV ( SETUP , dev , " init_fsm error " ) ;
2010-09-27 05:56:06 +04:00
free_netdev ( dev ) ;
2008-02-08 02:03:49 +03:00
return NULL ;
}
fsm_newstate ( priv - > fsm , DEV_STATE_STOPPED ) ;
fsm_settimer ( priv - > fsm , & priv - > restart_timer ) ;
if ( IS_MPC ( priv ) ) {
/* MPC Group Initializations */
grp = ctcmpc_init_mpc_group ( priv ) ;
if ( grp = = NULL ) {
MPC_DBF_DEV ( SETUP , dev , " init_mpc_group error " ) ;
2010-09-27 05:56:06 +04:00
free_netdev ( dev ) ;
2008-02-08 02:03:49 +03:00
return NULL ;
}
tasklet_init ( & grp - > mpc_tasklet2 ,
mpc_group_ready , ( unsigned long ) dev ) ;
dev - > mtu = MPC_BUFSIZE_DEFAULT -
TH_HEADER_LENGTH - PDU_HEADER_LENGTH ;
2009-01-09 06:43:57 +03:00
dev - > netdev_ops = & ctcm_mpc_netdev_ops ;
2008-02-08 02:03:49 +03:00
dev - > hard_header_len = TH_HEADER_LENGTH + PDU_HEADER_LENGTH ;
priv - > buffer_size = MPC_BUFSIZE_DEFAULT ;
} else {
dev - > mtu = CTCM_BUFSIZE_DEFAULT - LL_HEADER_LENGTH - 2 ;
2009-01-09 06:43:57 +03:00
dev - > netdev_ops = & ctcm_netdev_ops ;
2008-02-08 02:03:49 +03:00
dev - > hard_header_len = LL_HEADER_LENGTH + 2 ;
}
CTCMY_DBF_DEV ( SETUP , dev , " finished " ) ;
2008-07-18 17:24:57 +04:00
2008-02-08 02:03:49 +03:00
return dev ;
}
/**
* Main IRQ handler .
*
* cdev The ccw_device the interrupt is for .
* intparm interruption parameter .
* irb interruption response block .
*/
static void ctcm_irq_handler ( struct ccw_device * cdev ,
unsigned long intparm , struct irb * irb )
{
struct channel * ch ;
struct net_device * dev ;
struct ctcm_priv * priv ;
struct ccwgroup_device * cgdev ;
2008-07-18 17:24:57 +04:00
int cstat ;
int dstat ;
CTCM_DBF_TEXT_ ( TRACE , CTC_DBF_DEBUG ,
2008-10-10 23:33:09 +04:00
" Enter %s(%s) " , CTCM_FUNTAIL , dev_name ( & cdev - > dev ) ) ;
2008-02-08 02:03:49 +03:00
if ( ctcm_check_irb_error ( cdev , irb ) )
return ;
cgdev = dev_get_drvdata ( & cdev - > dev ) ;
2008-07-18 17:24:57 +04:00
cstat = irb - > scsw . cmd . cstat ;
dstat = irb - > scsw . cmd . dstat ;
2008-02-08 02:03:49 +03:00
/* Check for unsolicited interrupts. */
if ( cgdev = = NULL ) {
2008-12-25 15:39:54 +03:00
CTCM_DBF_TEXT_ ( TRACE , CTC_DBF_ERROR ,
" %s(%s) unsolicited irq: c-%02x d-%02x \n " ,
CTCM_FUNTAIL , dev_name ( & cdev - > dev ) , cstat , dstat ) ;
dev_warn ( & cdev - > dev ,
" The adapter received a non-specific IRQ \n " ) ;
2008-02-08 02:03:49 +03:00
return ;
}
priv = dev_get_drvdata ( & cgdev - > dev ) ;
/* Try to extract channel from driver data. */
2010-08-12 05:58:28 +04:00
if ( priv - > channel [ CTCM_READ ] - > cdev = = cdev )
ch = priv - > channel [ CTCM_READ ] ;
else if ( priv - > channel [ CTCM_WRITE ] - > cdev = = cdev )
ch = priv - > channel [ CTCM_WRITE ] ;
2008-02-08 02:03:49 +03:00
else {
2008-12-25 15:39:54 +03:00
dev_err ( & cdev - > dev ,
" %s: Internal error: Can't determine channel for "
" interrupt device %s \n " ,
__func__ , dev_name ( & cdev - > dev ) ) ;
/* Explain: inconsistent internal structures */
2008-02-08 02:03:49 +03:00
return ;
}
2008-07-18 17:24:57 +04:00
dev = ch - > netdev ;
2008-02-08 02:03:49 +03:00
if ( dev = = NULL ) {
2008-12-25 15:39:54 +03:00
dev_err ( & cdev - > dev ,
" %s Internal error: net_device is NULL, ch = 0x%p \n " ,
__func__ , ch ) ;
/* Explain: inconsistent internal structures */
2008-02-08 02:03:49 +03:00
return ;
}
/* Copy interruption response block. */
memcpy ( ch - > irb , irb , sizeof ( struct irb ) ) ;
2008-12-25 15:39:54 +03:00
/* Issue error message and return on subchannel error code */
2008-07-14 11:58:50 +04:00
if ( irb - > scsw . cmd . cstat ) {
2008-02-08 02:03:49 +03:00
fsm_event ( ch - > fsm , CTC_EVENT_SC_UNKNOWN , ch ) ;
2008-12-25 15:39:54 +03:00
CTCM_DBF_TEXT_ ( TRACE , CTC_DBF_WARN ,
" %s(%s): sub-ch check %s: cs=%02x ds=%02x " ,
CTCM_FUNTAIL , dev - > name , ch - > id , cstat , dstat ) ;
dev_warn ( & cdev - > dev ,
" A check occurred on the subchannel \n " ) ;
2008-02-08 02:03:49 +03:00
return ;
}
/* Check the reason-code of a unit check */
2008-07-14 11:58:50 +04:00
if ( irb - > scsw . cmd . dstat & DEV_STAT_UNIT_CHECK ) {
2008-07-18 17:24:57 +04:00
if ( ( irb - > ecw [ 0 ] & ch - > sense_rc ) = = 0 )
/* print it only once */
2008-12-25 15:39:54 +03:00
CTCM_DBF_TEXT_ ( TRACE , CTC_DBF_WARN ,
2008-07-18 17:24:57 +04:00
" %s(%s): sense=%02x, ds=%02x " ,
CTCM_FUNTAIL , ch - > id , irb - > ecw [ 0 ] , dstat ) ;
2008-02-08 02:03:49 +03:00
ccw_unit_check ( ch , irb - > ecw [ 0 ] ) ;
return ;
}
2008-07-14 11:58:50 +04:00
if ( irb - > scsw . cmd . dstat & DEV_STAT_BUSY ) {
if ( irb - > scsw . cmd . dstat & DEV_STAT_ATTENTION )
2008-02-08 02:03:49 +03:00
fsm_event ( ch - > fsm , CTC_EVENT_ATTNBUSY , ch ) ;
else
fsm_event ( ch - > fsm , CTC_EVENT_BUSY , ch ) ;
return ;
}
2008-07-14 11:58:50 +04:00
if ( irb - > scsw . cmd . dstat & DEV_STAT_ATTENTION ) {
2008-02-08 02:03:49 +03:00
fsm_event ( ch - > fsm , CTC_EVENT_ATTN , ch ) ;
return ;
}
2008-07-14 11:58:50 +04:00
if ( ( irb - > scsw . cmd . stctl & SCSW_STCTL_SEC_STATUS ) | |
( irb - > scsw . cmd . stctl = = SCSW_STCTL_STATUS_PEND ) | |
( irb - > scsw . cmd . stctl = =
2008-02-08 02:03:49 +03:00
( SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND ) ) )
fsm_event ( ch - > fsm , CTC_EVENT_FINSTAT , ch ) ;
else
fsm_event ( ch - > fsm , CTC_EVENT_IRQ , ch ) ;
}
2012-05-15 19:54:12 +04:00
static const struct device_type ctcm_devtype = {
. name = " ctcm " ,
. groups = ctcm_attr_groups ,
} ;
2008-02-08 02:03:49 +03:00
/**
* Add ctcm specific attributes .
* Add ctcm private data .
*
* cgdev pointer to ccwgroup_device just added
*
* returns 0 on success , ! 0 on failure .
*/
static int ctcm_probe_device ( struct ccwgroup_device * cgdev )
{
struct ctcm_priv * priv ;
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( SETUP , CTC_DBF_INFO ,
" %s %p " ,
__func__ , cgdev ) ;
2008-02-08 02:03:49 +03:00
if ( ! get_device ( & cgdev - > dev ) )
return - ENODEV ;
priv = kzalloc ( sizeof ( struct ctcm_priv ) , GFP_KERNEL ) ;
if ( ! priv ) {
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( ERROR , CTC_DBF_ERROR ,
" %s: memory allocation failure " ,
CTCM_FUNTAIL ) ;
2008-02-08 02:03:49 +03:00
put_device ( & cgdev - > dev ) ;
return - ENOMEM ;
}
priv - > buffer_size = CTCM_BUFSIZE_DEFAULT ;
cgdev - > cdev [ 0 ] - > handler = ctcm_irq_handler ;
cgdev - > cdev [ 1 ] - > handler = ctcm_irq_handler ;
dev_set_drvdata ( & cgdev - > dev , priv ) ;
2012-05-15 19:54:12 +04:00
cgdev - > dev . type = & ctcm_devtype ;
2008-02-08 02:03:49 +03:00
return 0 ;
}
/**
* Add a new channel to the list of channels .
* Keeps the channel list sorted .
*
* cdev The ccw_device to be added .
* type The type class of the new channel .
* priv Points to the private data of the ccwgroup_device .
*
* returns 0 on success , ! 0 on error .
*/
2009-11-13 00:46:29 +03:00
static int add_channel ( struct ccw_device * cdev , enum ctcm_channel_types type ,
2008-02-08 02:03:49 +03:00
struct ctcm_priv * priv )
{
struct channel * * c = & channels ;
struct channel * ch ;
int ccw_num ;
int rc = 0 ;
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( SETUP , CTC_DBF_INFO ,
" %s(%s), type %d, proto %d " ,
2008-10-10 23:33:09 +04:00
__func__ , dev_name ( & cdev - > dev ) , type , priv - > protocol ) ;
2008-07-18 17:24:57 +04:00
2008-02-08 02:03:49 +03:00
ch = kzalloc ( sizeof ( struct channel ) , GFP_KERNEL ) ;
if ( ch = = NULL )
2008-07-18 17:24:57 +04:00
return - ENOMEM ;
2008-02-08 02:03:49 +03:00
ch - > protocol = priv - > protocol ;
if ( IS_MPC ( priv ) ) {
2010-05-17 01:15:13 +04:00
ch - > discontact_th = kzalloc ( TH_HEADER_LENGTH , gfp_type ( ) ) ;
2008-02-08 02:03:49 +03:00
if ( ch - > discontact_th = = NULL )
goto nomem_return ;
ch - > discontact_th - > th_blk_flag = TH_DISCONTACT ;
tasklet_init ( & ch - > ch_disc_tasklet ,
mpc_action_send_discontact , ( unsigned long ) ch ) ;
tasklet_init ( & ch - > ch_tasklet , ctcmpc_bh , ( unsigned long ) ch ) ;
ch - > max_bufsize = ( MPC_BUFSIZE_DEFAULT - 35 ) ;
ccw_num = 17 ;
} else
ccw_num = 8 ;
treewide: kzalloc() -> kcalloc()
The kzalloc() function has a 2-factor argument form, kcalloc(). This
patch replaces cases of:
kzalloc(a * b, gfp)
with:
kcalloc(a * b, gfp)
as well as handling cases of:
kzalloc(a * b * c, gfp)
with:
kzalloc(array3_size(a, b, c), gfp)
as it's slightly less ugly than:
kzalloc_array(array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
kzalloc(4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
kzalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
kzalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
kzalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
- kzalloc
+ kcalloc
(
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
kzalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
kzalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kzalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
kzalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
kzalloc(C1 * C2 * C3, ...)
|
kzalloc(
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
kzalloc(sizeof(THING) * C2, ...)
|
kzalloc(sizeof(TYPE) * C2, ...)
|
kzalloc(C1 * C2 * C3, ...)
|
kzalloc(C1 * C2, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- (E1) * E2
+ E1, E2
, ...)
|
- kzalloc
+ kcalloc
(
- (E1) * (E2)
+ E1, E2
, ...)
|
- kzalloc
+ kcalloc
(
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 00:03:40 +03:00
ch - > ccw = kcalloc ( ccw_num , sizeof ( struct ccw1 ) , GFP_KERNEL | GFP_DMA ) ;
2008-02-08 02:03:49 +03:00
if ( ch - > ccw = = NULL )
goto nomem_return ;
ch - > cdev = cdev ;
2008-10-10 23:33:09 +04:00
snprintf ( ch - > id , CTCM_ID_SIZE , " ch-%s " , dev_name ( & cdev - > dev ) ) ;
2008-02-08 02:03:49 +03:00
ch - > type = type ;
/**
* " static " ccws are used in the following way :
*
* ccw [ 0. .2 ] ( Channel program for generic I / O ) :
* 0 : prepare
* 1 : read or write ( depending on direction ) with fixed
* buffer ( idal allocated once when buffer is allocated )
* 2 : nop
* ccw [ 3. .5 ] ( Channel program for direct write of packets )
* 3 : prepare
* 4 : write ( idal allocated on every write ) .
* 5 : nop
* ccw [ 6. .7 ] ( Channel program for initial channel setup ) :
* 6 : set extended mode
* 7 : nop
*
* ch - > ccw [ 0. .5 ] are initialized in ch_action_start because
* the channel ' s direction is yet unknown here .
*
* ccws used for xid2 negotiations
* ch - ccw [ 8 - 14 ] need to be used for the XID exchange either
* X side XID2 Processing
* 8 : write control
* 9 : write th
* 10 : write XID
* 11 : read th from secondary
* 12 : read XID from secondary
* 13 : read 4 byte ID
* 14 : nop
* Y side XID Processing
* 8 : sense
* 9 : read th
* 10 : read XID
* 11 : write th
* 12 : write XID
* 13 : write 4 byte ID
* 14 : nop
*
* ccws used for double noop due to VM timing issues
* which result in unrecoverable Busy on channel
* 15 : nop
* 16 : nop
*/
ch - > ccw [ 6 ] . cmd_code = CCW_CMD_SET_EXTENDED ;
ch - > ccw [ 6 ] . flags = CCW_FLAG_SLI ;
ch - > ccw [ 7 ] . cmd_code = CCW_CMD_NOOP ;
ch - > ccw [ 7 ] . flags = CCW_FLAG_SLI ;
if ( IS_MPC ( priv ) ) {
ch - > ccw [ 15 ] . cmd_code = CCW_CMD_WRITE ;
ch - > ccw [ 15 ] . flags = CCW_FLAG_SLI | CCW_FLAG_CC ;
ch - > ccw [ 15 ] . count = TH_HEADER_LENGTH ;
ch - > ccw [ 15 ] . cda = virt_to_phys ( ch - > discontact_th ) ;
ch - > ccw [ 16 ] . cmd_code = CCW_CMD_NOOP ;
ch - > ccw [ 16 ] . flags = CCW_FLAG_SLI ;
ch - > fsm = init_fsm ( ch - > id , ctc_ch_state_names ,
ctc_ch_event_names , CTC_MPC_NR_STATES ,
CTC_MPC_NR_EVENTS , ctcmpc_ch_fsm ,
mpc_ch_fsm_len , GFP_KERNEL ) ;
} else {
ch - > fsm = init_fsm ( ch - > id , ctc_ch_state_names ,
ctc_ch_event_names , CTC_NR_STATES ,
CTC_NR_EVENTS , ch_fsm ,
ch_fsm_len , GFP_KERNEL ) ;
}
if ( ch - > fsm = = NULL )
2012-09-24 08:24:27 +04:00
goto nomem_return ;
2008-02-08 02:03:49 +03:00
fsm_newstate ( ch - > fsm , CTC_STATE_IDLE ) ;
ch - > irb = kzalloc ( sizeof ( struct irb ) , GFP_KERNEL ) ;
if ( ch - > irb = = NULL )
goto nomem_return ;
while ( * c & & ctcm_less_than ( ( * c ) - > id , ch - > id ) )
c = & ( * c ) - > next ;
if ( * c & & ( ! strncmp ( ( * c ) - > id , ch - > id , CTCM_ID_SIZE ) ) ) {
CTCM_DBF_TEXT_ ( SETUP , CTC_DBF_INFO ,
" %s (%s) already in list, using old entry " ,
2008-07-18 17:24:57 +04:00
__func__ , ( * c ) - > id ) ;
2008-02-08 02:03:49 +03:00
goto free_return ;
}
spin_lock_init ( & ch - > collect_lock ) ;
fsm_settimer ( ch - > fsm , & ch - > timer ) ;
skb_queue_head_init ( & ch - > io_queue ) ;
skb_queue_head_init ( & ch - > collect_queue ) ;
if ( IS_MPC ( priv ) ) {
fsm_settimer ( ch - > fsm , & ch - > sweep_timer ) ;
skb_queue_head_init ( & ch - > sweep_queue ) ;
}
ch - > next = * c ;
* c = ch ;
return 0 ;
nomem_return :
rc = - ENOMEM ;
free_return : /* note that all channel pointers are 0 or valid */
2008-07-18 17:24:57 +04:00
kfree ( ch - > ccw ) ;
2008-02-08 02:03:49 +03:00
kfree ( ch - > discontact_th ) ;
kfree_fsm ( ch - > fsm ) ;
kfree ( ch - > irb ) ;
kfree ( ch ) ;
return rc ;
}
/*
* Return type of a detected device .
*/
2009-11-13 00:46:29 +03:00
static enum ctcm_channel_types get_channel_type ( struct ccw_device_id * id )
2008-02-08 02:03:49 +03:00
{
2009-11-13 00:46:29 +03:00
enum ctcm_channel_types type ;
type = ( enum ctcm_channel_types ) id - > driver_info ;
2008-02-08 02:03:49 +03:00
2009-11-13 00:46:29 +03:00
if ( type = = ctcm_channel_type_ficon )
type = ctcm_channel_type_escon ;
2008-02-08 02:03:49 +03:00
return type ;
}
/**
*
* Setup an interface .
*
* cgdev Device to be setup .
*
* returns 0 on success , ! 0 on failure .
*/
static int ctcm_new_device ( struct ccwgroup_device * cgdev )
{
char read_id [ CTCM_ID_SIZE ] ;
char write_id [ CTCM_ID_SIZE ] ;
int direction ;
2009-11-13 00:46:29 +03:00
enum ctcm_channel_types type ;
2008-02-08 02:03:49 +03:00
struct ctcm_priv * priv ;
struct net_device * dev ;
2008-07-18 17:24:57 +04:00
struct ccw_device * cdev0 ;
struct ccw_device * cdev1 ;
2009-10-15 02:54:59 +04:00
struct channel * readc ;
struct channel * writec ;
2008-02-08 02:03:49 +03:00
int ret ;
2009-10-15 02:54:59 +04:00
int result ;
2008-02-08 02:03:49 +03:00
priv = dev_get_drvdata ( & cgdev - > dev ) ;
2009-10-15 02:54:59 +04:00
if ( ! priv ) {
result = - ENODEV ;
goto out_err_result ;
}
2008-02-08 02:03:49 +03:00
2008-07-18 17:24:57 +04:00
cdev0 = cgdev - > cdev [ 0 ] ;
cdev1 = cgdev - > cdev [ 1 ] ;
type = get_channel_type ( & cdev0 - > id ) ;
2008-02-08 02:03:49 +03:00
2008-10-10 23:33:11 +04:00
snprintf ( read_id , CTCM_ID_SIZE , " ch-%s " , dev_name ( & cdev0 - > dev ) ) ;
snprintf ( write_id , CTCM_ID_SIZE , " ch-%s " , dev_name ( & cdev1 - > dev ) ) ;
2008-02-08 02:03:49 +03:00
2008-07-18 17:24:57 +04:00
ret = add_channel ( cdev0 , type , priv ) ;
2009-10-15 02:54:59 +04:00
if ( ret ) {
result = ret ;
goto out_err_result ;
}
2008-07-18 17:24:57 +04:00
ret = add_channel ( cdev1 , type , priv ) ;
2009-10-15 02:54:59 +04:00
if ( ret ) {
result = ret ;
goto out_remove_channel1 ;
}
2008-02-08 02:03:49 +03:00
2008-07-18 17:24:57 +04:00
ret = ccw_device_set_online ( cdev0 ) ;
2008-02-08 02:03:49 +03:00
if ( ret ! = 0 ) {
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( TRACE , CTC_DBF_NOTICE ,
" %s(%s) set_online rc=%d " ,
CTCM_FUNTAIL , read_id , ret ) ;
2009-10-15 02:54:59 +04:00
result = - EIO ;
goto out_remove_channel2 ;
2008-02-08 02:03:49 +03:00
}
2008-07-18 17:24:57 +04:00
ret = ccw_device_set_online ( cdev1 ) ;
2008-02-08 02:03:49 +03:00
if ( ret ! = 0 ) {
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( TRACE , CTC_DBF_NOTICE ,
" %s(%s) set_online rc=%d " ,
CTCM_FUNTAIL , write_id , ret ) ;
2009-10-15 02:54:59 +04:00
result = - EIO ;
goto out_ccw1 ;
2008-02-08 02:03:49 +03:00
}
dev = ctcm_init_netdevice ( priv ) ;
2009-10-15 02:54:59 +04:00
if ( dev = = NULL ) {
result = - ENODEV ;
goto out_ccw2 ;
}
2008-02-08 02:03:49 +03:00
2010-08-12 05:58:28 +04:00
for ( direction = CTCM_READ ; direction < = CTCM_WRITE ; direction + + ) {
2008-02-08 02:03:49 +03:00
priv - > channel [ direction ] =
2010-08-12 05:58:28 +04:00
channel_get ( type , direction = = CTCM_READ ?
read_id : write_id , direction ) ;
2008-02-08 02:03:49 +03:00
if ( priv - > channel [ direction ] = = NULL ) {
2010-08-12 05:58:28 +04:00
if ( direction = = CTCM_WRITE )
channel_free ( priv - > channel [ CTCM_READ ] ) ;
2019-04-17 19:29:13 +03:00
result = - ENODEV ;
2008-07-18 17:24:57 +04:00
goto out_dev ;
2008-02-08 02:03:49 +03:00
}
priv - > channel [ direction ] - > netdev = dev ;
priv - > channel [ direction ] - > protocol = priv - > protocol ;
priv - > channel [ direction ] - > max_bufsize = priv - > buffer_size ;
}
/* sysfs magic */
SET_NETDEV_DEV ( dev , & cgdev - > dev ) ;
2009-10-15 02:54:59 +04:00
if ( register_netdev ( dev ) ) {
result = - ENODEV ;
goto out_dev ;
}
2008-02-08 02:03:49 +03:00
strlcpy ( priv - > fsm - > name , dev - > name , sizeof ( priv - > fsm - > name ) ) ;
2008-12-25 15:39:54 +03:00
dev_info ( & dev - > dev ,
" setup OK : r/w = %s/%s, protocol : %d \n " ,
2010-08-12 05:58:28 +04:00
priv - > channel [ CTCM_READ ] - > id ,
priv - > channel [ CTCM_WRITE ] - > id , priv - > protocol ) ;
2008-12-25 15:39:54 +03:00
2008-02-08 02:03:49 +03:00
CTCM_DBF_TEXT_ ( SETUP , CTC_DBF_INFO ,
2008-07-18 17:24:57 +04:00
" setup(%s) OK : r/w = %s/%s, protocol : %d " , dev - > name ,
2010-08-12 05:58:28 +04:00
priv - > channel [ CTCM_READ ] - > id ,
priv - > channel [ CTCM_WRITE ] - > id , priv - > protocol ) ;
2008-02-08 02:03:49 +03:00
return 0 ;
2008-07-18 17:24:57 +04:00
out_dev :
ctcm_free_netdevice ( dev ) ;
2009-10-15 02:54:59 +04:00
out_ccw2 :
2008-02-08 02:03:49 +03:00
ccw_device_set_offline ( cgdev - > cdev [ 1 ] ) ;
2009-10-15 02:54:59 +04:00
out_ccw1 :
2008-02-08 02:03:49 +03:00
ccw_device_set_offline ( cgdev - > cdev [ 0 ] ) ;
2009-10-15 02:54:59 +04:00
out_remove_channel2 :
2010-08-12 05:58:28 +04:00
readc = channel_get ( type , read_id , CTCM_READ ) ;
2009-10-15 02:54:59 +04:00
channel_remove ( readc ) ;
out_remove_channel1 :
2010-08-12 05:58:28 +04:00
writec = channel_get ( type , write_id , CTCM_WRITE ) ;
2009-10-15 02:54:59 +04:00
channel_remove ( writec ) ;
out_err_result :
return result ;
2008-02-08 02:03:49 +03:00
}
/**
* Shutdown an interface .
*
* cgdev Device to be shut down .
*
* returns 0 on success , ! 0 on failure .
*/
static int ctcm_shutdown_device ( struct ccwgroup_device * cgdev )
{
struct ctcm_priv * priv ;
struct net_device * dev ;
priv = dev_get_drvdata ( & cgdev - > dev ) ;
if ( ! priv )
return - ENODEV ;
2010-08-12 05:58:28 +04:00
if ( priv - > channel [ CTCM_READ ] ) {
dev = priv - > channel [ CTCM_READ ] - > netdev ;
2008-02-08 02:03:49 +03:00
CTCM_DBF_DEV ( SETUP , dev , " " ) ;
/* Close the device */
ctcm_close ( dev ) ;
dev - > flags & = ~ IFF_RUNNING ;
2010-08-12 05:58:28 +04:00
channel_free ( priv - > channel [ CTCM_READ ] ) ;
2008-02-08 02:03:49 +03:00
} else
dev = NULL ;
2010-08-12 05:58:28 +04:00
if ( priv - > channel [ CTCM_WRITE ] )
channel_free ( priv - > channel [ CTCM_WRITE ] ) ;
2008-02-08 02:03:49 +03:00
if ( dev ) {
2008-07-18 17:24:57 +04:00
unregister_netdev ( dev ) ;
2008-02-08 02:03:49 +03:00
ctcm_free_netdevice ( dev ) ;
}
if ( priv - > fsm )
kfree_fsm ( priv - > fsm ) ;
ccw_device_set_offline ( cgdev - > cdev [ 1 ] ) ;
ccw_device_set_offline ( cgdev - > cdev [ 0 ] ) ;
2015-12-11 14:27:53 +03:00
channel_remove ( priv - > channel [ CTCM_READ ] ) ;
channel_remove ( priv - > channel [ CTCM_WRITE ] ) ;
2010-08-12 05:58:28 +04:00
priv - > channel [ CTCM_READ ] = priv - > channel [ CTCM_WRITE ] = NULL ;
2008-02-08 02:03:49 +03:00
return 0 ;
}
static void ctcm_remove_device ( struct ccwgroup_device * cgdev )
{
2008-07-18 17:24:57 +04:00
struct ctcm_priv * priv = dev_get_drvdata ( & cgdev - > dev ) ;
2008-02-08 02:03:49 +03:00
2008-07-18 17:24:57 +04:00
CTCM_DBF_TEXT_ ( SETUP , CTC_DBF_INFO ,
2009-05-20 01:38:38 +04:00
" removing device %p, proto : %d " ,
cgdev , priv - > protocol ) ;
2008-02-08 02:03:49 +03:00
if ( cgdev - > state = = CCWGROUP_ONLINE )
ctcm_shutdown_device ( cgdev ) ;
dev_set_drvdata ( & cgdev - > dev , NULL ) ;
kfree ( priv ) ;
put_device ( & cgdev - > dev ) ;
}
2009-11-13 00:46:29 +03:00
static struct ccw_device_id ctcm_ids [ ] = {
{ CCW_DEVICE ( 0x3088 , 0x08 ) , . driver_info = ctcm_channel_type_parallel } ,
{ CCW_DEVICE ( 0x3088 , 0x1e ) , . driver_info = ctcm_channel_type_ficon } ,
{ CCW_DEVICE ( 0x3088 , 0x1f ) , . driver_info = ctcm_channel_type_escon } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( ccw , ctcm_ids ) ;
static struct ccw_driver ctcm_ccw_driver = {
2011-03-23 12:16:02 +03:00
. driver = {
. owner = THIS_MODULE ,
. name = " ctcm " ,
} ,
2009-11-13 00:46:29 +03:00
. ids = ctcm_ids ,
. probe = ccwgroup_probe_ccwdev ,
. remove = ccwgroup_remove_ccwdev ,
2013-01-02 18:18:18 +04:00
. int_class = IRQIO_CTC ,
2009-11-13 00:46:29 +03:00
} ;
2008-02-08 02:03:49 +03:00
static struct ccwgroup_driver ctcm_group_driver = {
2011-03-23 12:16:04 +03:00
. driver = {
. owner = THIS_MODULE ,
. name = CTC_DRIVER_NAME ,
} ,
2017-09-14 10:52:32 +03:00
. ccw_driver = & ctcm_ccw_driver ,
2012-05-15 19:54:12 +04:00
. setup = ctcm_probe_device ,
2008-02-08 02:03:49 +03:00
. remove = ctcm_remove_device ,
. set_online = ctcm_new_device ,
. set_offline = ctcm_shutdown_device ,
} ;
2017-06-09 12:03:13 +03:00
static ssize_t group_store ( struct device_driver * ddrv , const char * buf ,
size_t count )
2009-11-13 00:46:29 +03:00
{
int err ;
2012-05-15 20:03:46 +04:00
err = ccwgroup_create_dev ( ctcm_root_dev , & ctcm_group_driver , 2 , buf ) ;
2009-11-13 00:46:29 +03:00
return err ? err : count ;
}
2017-06-09 12:03:13 +03:00
static DRIVER_ATTR_WO ( group ) ;
2009-11-13 00:46:29 +03:00
2012-05-15 20:05:01 +04:00
static struct attribute * ctcm_drv_attrs [ ] = {
2009-11-13 00:46:29 +03:00
& driver_attr_group . attr ,
NULL ,
} ;
2012-05-15 20:05:01 +04:00
static struct attribute_group ctcm_drv_attr_group = {
. attrs = ctcm_drv_attrs ,
2009-11-13 00:46:29 +03:00
} ;
2012-05-15 20:05:01 +04:00
static const struct attribute_group * ctcm_drv_attr_groups [ ] = {
& ctcm_drv_attr_group ,
2009-11-13 00:46:29 +03:00
NULL ,
} ;
2008-02-08 02:03:49 +03:00
/*
* Module related routines
*/
/*
* Prepare to be unloaded . Free IRQ ' s and release all resources .
* This is called just before this module is unloaded . It is
* not called , if the usage count is ! 0 , so we don ' t need to check
* for that .
*/
static void __exit ctcm_exit ( void )
{
2009-11-13 00:46:29 +03:00
ccwgroup_driver_unregister ( & ctcm_group_driver ) ;
ccw_driver_unregister ( & ctcm_ccw_driver ) ;
root_device_unregister ( ctcm_root_dev ) ;
2008-02-08 02:03:49 +03:00
ctcm_unregister_dbf_views ( ) ;
2008-12-25 15:39:54 +03:00
pr_info ( " CTCM driver unloaded \n " ) ;
2008-02-08 02:03:49 +03:00
}
/*
* Print Banner .
*/
static void print_banner ( void )
{
2008-12-25 15:39:54 +03:00
pr_info ( " CTCM driver initialized \n " ) ;
2008-02-08 02:03:49 +03:00
}
/**
* Initialize module .
* This is called just after the module is loaded .
*
* returns 0 on success , ! 0 on error .
*/
static int __init ctcm_init ( void )
{
int ret ;
channels = NULL ;
ret = ctcm_register_dbf_views ( ) ;
2009-11-13 00:46:29 +03:00
if ( ret )
goto out_err ;
ctcm_root_dev = root_device_register ( " ctcm " ) ;
2014-04-28 12:05:11 +04:00
ret = PTR_ERR_OR_ZERO ( ctcm_root_dev ) ;
2009-11-13 00:46:29 +03:00
if ( ret )
goto register_err ;
ret = ccw_driver_register ( & ctcm_ccw_driver ) ;
if ( ret )
goto ccw_err ;
2012-05-15 20:05:01 +04:00
ctcm_group_driver . driver . groups = ctcm_drv_attr_groups ;
2009-11-13 00:46:29 +03:00
ret = ccwgroup_driver_register ( & ctcm_group_driver ) ;
if ( ret )
goto ccwgroup_err ;
2008-02-08 02:03:49 +03:00
print_banner ( ) ;
2009-11-13 00:46:29 +03:00
return 0 ;
ccwgroup_err :
ccw_driver_unregister ( & ctcm_ccw_driver ) ;
ccw_err :
root_device_unregister ( ctcm_root_dev ) ;
register_err :
ctcm_unregister_dbf_views ( ) ;
out_err :
pr_err ( " %s / Initializing the ctcm device driver failed, ret = %d \n " ,
__func__ , ret ) ;
2008-02-08 02:03:49 +03:00
return ret ;
}
module_init ( ctcm_init ) ;
module_exit ( ctcm_exit ) ;
MODULE_AUTHOR ( " Peter Tiedemann <ptiedem@de.ibm.com> " ) ;
MODULE_DESCRIPTION ( " Network driver for S/390 CTC + CTCMPC (SNA) " ) ;
MODULE_LICENSE ( " GPL " ) ;