2017-11-14 18:38:04 +01:00
// SPDX-License-Identifier: GPL-2.0
2008-02-08 00:03:49 +01:00
/*
2009-06-16 10:30:32 +02:00
* Copyright IBM Corp . 2001 , 2009
2008-02-08 00:03:49 +01: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 13:39:54 +01:00
# define KMSG_COMPONENT "ctcm"
# define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
2008-02-08 00:03:49 +01: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 */
2021-09-14 10:33:17 +02:00
/*
2009-11-12 21:46:29 +00:00
* The root device for ctcm group devices
*/
static struct device * ctcm_root_dev ;
2008-02-08 00:03:49 +01:00
/*
* Linked list of all detected channels .
*/
struct channel * channels ;
2021-09-14 10:33:17 +02:00
/*
2008-02-08 00:03:49 +01:00
* 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 17:10:24 +02:00
struct ctcm_priv * priv = dev - > ml_priv ;
2008-02-08 00:03:49 +01: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 15:24:57 +02:00
ch - > logflags | = LOG_FLAG_ILLEGALPKT ;
2008-02-08 00:03:49 +01: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 15:24:57 +02: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 00:03:49 +01:00
}
priv - > stats . rx_dropped + + ;
priv - > stats . rx_frame_errors + + ;
return ;
}
2017-04-07 09:15:37 +02:00
pskb - > protocol = cpu_to_be16 ( header - > type ) ;
2009-03-24 03:27:48 +00:00
if ( ( header - > length < = LL_HEADER_LENGTH ) | |
( len < = LL_HEADER_LENGTH ) ) {
2008-02-08 00:03:49 +01:00
if ( ! ( ch - > logflags & LOG_FLAG_ILLEGALSIZE ) ) {
2008-07-18 15:24:57 +02: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 00:03:49 +01: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 09:15:37 +02:00
( header - > length > len ) ) {
2008-02-08 00:03:49 +01:00
if ( ! ( ch - > logflags & LOG_FLAG_OVERRUN ) ) {
2008-07-18 15:24:57 +02:00
CTCM_DBF_TEXT_ ( ERROR , CTC_DBF_ERROR ,
" %s(%s): Packet size %d (overrun) "
" - dropping " , CTCM_FUNTAIL ,
dev - > name , header - > length ) ;
2008-02-08 00:03:49 +01: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 15:24:57 +02:00
CTCM_DBF_TEXT_ ( ERROR , CTC_DBF_ERROR ,
" %s(%s): MEMORY allocation error " ,
CTCM_FUNTAIL , dev - > name ) ;
2008-02-08 00:03:49 +01: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 ;
2022-03-06 22:57:44 +01:00
netif_rx ( skb ) ;
2008-02-08 00:03:49 +01:00
if ( len > 0 ) {
skb_pull ( pskb , header - > length ) ;
if ( skb_tailroom ( pskb ) < LL_HEADER_LENGTH ) {
2009-03-24 03:27:49 +00:00
CTCM_DBF_DEV_NAME ( TRACE , dev ,
" Overrun in ctcm_unpack_skb " ) ;
ch - > logflags | = LOG_FLAG_OVERRUN ;
2008-02-08 00:03:49 +01:00
return ;
}
skb_put ( pskb , LL_HEADER_LENGTH ) ;
}
}
}
2021-09-14 10:33:17 +02:00
/*
2008-02-08 00:03:49 +01:00
* 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 15:24:57 +02:00
CTCM_DBF_TEXT_ ( SETUP , CTC_DBF_INFO , " %s(%s) " , CTCM_FUNTAIL , ch - > id ) ;
2008-02-08 00:03:49 +01:00
ch - > flags & = ~ CHANNEL_FLAGS_INUSE ;
fsm_newstate ( ch - > fsm , CTC_STATE_IDLE ) ;
}
2021-09-14 10:33:17 +02:00
/*
2008-02-08 00:03:49 +01:00
* 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 ;
2023-10-23 19:35:07 +00:00
char chid [ CTCM_ID_SIZE ] ;
2008-02-08 00:03:49 +01:00
int ok = 0 ;
if ( ch = = NULL )
return ;
else
2023-10-23 19:35:07 +00:00
strscpy ( chid , ch - > id , sizeof ( chid ) ) ;
2008-02-08 00:03:49 +01:00
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 " ) ;
}
2021-09-14 10:33:17 +02:00
/*
2008-02-08 00:03:49 +01:00
* 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-12 21:46:29 +00:00
static struct channel * channel_get ( enum ctcm_channel_types type ,
2008-02-08 00:03:49 +01: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 15:24:57 +02:00
CTCM_DBF_TEXT_ ( ERROR , CTC_DBF_ERROR ,
" %s(%d, %s, %d) not found in channel list \n " ,
2008-02-08 00:03:49 +01: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 01:58:28 +00:00
ch - > flags | = ( direction = = CTCM_WRITE )
2008-02-08 00:03:49 +01: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 15:24:57 +02:00
CTCM_DBF_TEXT_ ( ERROR , CTC_DBF_WARN ,
" irb error %ld on device %s \n " ,
2008-10-10 21:33:09 +02:00
PTR_ERR ( irb ) , dev_name ( & cdev - > dev ) ) ;
2008-02-08 00:03:49 +01:00
switch ( PTR_ERR ( irb ) ) {
case - EIO :
2008-12-25 13:39:54 +01:00
dev_err ( & cdev - > dev ,
" An I/O-error occurred on the CTCM device \n " ) ;
2008-02-08 00:03:49 +01:00
break ;
case - ETIMEDOUT :
2008-12-25 13:39:54 +01:00
dev_err ( & cdev - > dev ,
" An adapter hardware operation timed out \n " ) ;
2008-02-08 00:03:49 +01:00
break ;
default :
2008-12-25 13:39:54 +01:00
dev_err ( & cdev - > dev ,
" An error occurred on the adapter hardware \n " ) ;
2008-02-08 00:03:49 +01:00
}
return PTR_ERR ( irb ) ;
}
2021-09-14 10:33:17 +02:00
/*
2008-02-08 00:03:49 +01:00
* Check sense of a unit check .
*
* ch The channel , the sense code belongs to .
* sense The sense code to inspect .
*/
2017-08-15 17:02:46 +02:00
static void ccw_unit_check ( struct channel * ch , __u8 sense )
2008-02-08 00:03:49 +01:00
{
2008-07-18 15:24:57 +02:00
CTCM_DBF_TEXT_ ( TRACE , CTC_DBF_DEBUG ,
" %s(%s): %02x " ,
CTCM_FUNTAIL , ch - > id , sense ) ;
2008-02-08 00:03:49 +01:00
if ( sense & SNS0_INTERVENTION_REQ ) {
if ( sense & 0x01 ) {
2008-07-18 15:24:57 +02:00
if ( ch - > sense_rc ! = 0x01 ) {
2008-12-25 13:39:54 +01:00
pr_notice (
" %s: The communication peer has "
" disconnected \n " , ch - > id ) ;
2008-07-18 15:24:57 +02:00
ch - > sense_rc = 0x01 ;
}
2008-02-08 00:03:49 +01:00
fsm_event ( ch - > fsm , CTC_EVENT_UC_RCRESET , ch ) ;
} else {
2008-07-18 15:24:57 +02:00
if ( ch - > sense_rc ! = SNS0_INTERVENTION_REQ ) {
2008-12-25 13:39:54 +01:00
pr_notice (
" %s: The remote operating system is "
" not available \n " , ch - > id ) ;
2008-07-18 15:24:57 +02:00
ch - > sense_rc = SNS0_INTERVENTION_REQ ;
}
2008-02-08 00:03:49 +01: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 15:24:57 +02: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 00:03:49 +01:00
fsm_event ( ch - > fsm , CTC_EVENT_UC_HWFAIL , ch ) ;
} else {
2008-07-18 15:24:57 +02: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 00:03:49 +01:00
fsm_event ( ch - > fsm , CTC_EVENT_UC_RXPARITY , ch ) ;
}
} else if ( sense & SNS0_BUS_OUT_CHECK ) {
2008-07-18 15:24:57 +02: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 00:03:49 +01:00
fsm_event ( ch - > fsm , CTC_EVENT_UC_TXTIMEOUT , ch ) ;
2008-07-18 15:24:57 +02:00
else /* Data-transfer parity error */
2008-02-08 00:03:49 +01:00
fsm_event ( ch - > fsm , CTC_EVENT_UC_TXPARITY , ch ) ;
} else if ( sense & SNS0_CMD_REJECT ) {
2008-07-18 15:24:57 +02: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 00:03:49 +01:00
} else if ( sense = = 0 ) {
2008-07-18 15:24:57 +02:00
CTCM_DBF_TEXT_ ( TRACE , CTC_DBF_WARN ,
" %s(%s): Unit check ZERO " ,
CTCM_FUNTAIL , ch - > id ) ;
2008-02-08 00:03:49 +01:00
fsm_event ( ch - > fsm , CTC_EVENT_UC_ZERO , ch ) ;
} else {
2008-07-18 15:24:57 +02:00
CTCM_DBF_TEXT_ ( TRACE , CTC_DBF_WARN ,
" %s(%s): Unit check code %02x unknown " ,
CTCM_FUNTAIL , ch - > id , sense ) ;
2008-02-08 00:03:49 +01: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 15:24:57 +02:00
CTCM_DBF_TEXT_ ( ERROR , CTC_DBF_ERROR ,
" %s(%s): %s trans_skb allocation error " ,
CTCM_FUNTAIL , ch - > id ,
2010-08-12 01:58:28 +00:00
( CHANNEL_DIRECTION ( ch - > flags ) = = CTCM_READ ) ?
" RX " : " TX " ) ;
2008-02-08 00:03:49 +01: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 15:24:57 +02:00
CTCM_DBF_TEXT_ ( ERROR , CTC_DBF_ERROR ,
" %s(%s): %s set norm_cda failed " ,
CTCM_FUNTAIL , ch - > id ,
2010-08-12 01:58:28 +00:00
( CHANNEL_DIRECTION ( ch - > flags ) = = CTCM_READ ) ?
" RX " : " TX " ) ;
2008-02-08 00:03:49 +01: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
*/
2021-09-14 10:33:17 +02:00
/*
2008-02-08 00:03:49 +01:00
* 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 17:10:24 +02:00
struct ctcm_priv * priv = dev - > ml_priv ;
2008-02-08 00:03:49 +01:00
CTCMY_DBF_DEV_NAME ( SETUP , dev , " " ) ;
if ( ! IS_MPC ( priv ) )
fsm_event ( priv - > fsm , DEV_EVENT_START , dev ) ;
return 0 ;
}
2021-09-14 10:33:17 +02:00
/*
2008-02-08 00:03:49 +01:00
* 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 17:10:24 +02:00
struct ctcm_priv * priv = dev - > ml_priv ;
2008-02-08 00:03:49 +01:00
CTCMY_DBF_DEV_NAME ( SETUP , dev , " " ) ;
if ( ! IS_MPC ( priv ) )
fsm_event ( priv - > fsm , DEV_EVENT_STOP , dev ) ;
return 0 ;
}
2021-09-14 10:33:17 +02:00
/*
2008-02-08 00:03:49 +01:00
* 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 00:03:49 +01:00
header . length = l ;
2017-04-07 09:15:37 +02:00
header . type = be16_to_cpu ( skb - > protocol ) ;
2008-02-08 00:03:49 +01: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 ) ;
2023-02-09 12:04:21 +01:00
goto done ;
2008-02-08 00:03:49 +01:00
}
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 00:03:49 +01:00
ch - > prof . txlen + = skb - > len ;
header . length = skb - > len + LL_HEADER_LENGTH ;
2017-04-07 09:15:37 +02:00
header . type = be16_to_cpu ( skb - > protocol ) ;
2008-02-08 00:03:49 +01: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 00:03:49 +01: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 14:29:20 +02: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 00:03:49 +01: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 00:03:49 +01:00
skb_pull ( skb , LL_HEADER_LENGTH + 2 ) ;
ctcm_clear_busy ( ch - > netdev ) ;
2008-07-18 15:24:57 +02:00
return - ENOMEM ;
2008-02-08 00:03:49 +01: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 00:03:49 +01:00
dev_kfree_skb_irq ( skb ) ;
ccw_idx = 0 ;
} else {
skb_queue_tail ( & ch - > io_queue , skb ) ;
ccw_idx = 3 ;
}
2012-03-07 02:06:26 +00:00
if ( do_debug_ccw )
ctcmpc_dumpit ( ( char * ) & ch - > ccw [ ccw_idx ] ,
sizeof ( struct ccw1 ) * 3 ) ;
2008-02-08 00:03:49 +01: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 14:05:45 +01:00
ch - > prof . send_stamp = jiffies ;
2019-08-20 16:46:42 +02:00
rc = ccw_device_start ( ch - > cdev , & ch - > ccw [ ccw_idx ] , 0 , 0xff , 0 ) ;
2008-02-08 00:03:49 +01: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 17:10:24 +02:00
struct ctcm_priv * priv = dev - > ml_priv ;
2008-02-08 00:03:49 +01: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 15:24:57 +02:00
/* int rc = 0; */
2008-02-08 00:03:49 +01:00
2008-08-21 17:10:24 +02:00
priv = dev - > ml_priv ;
2008-02-08 00:03:49 +01:00
grp = priv - > mpcg ;
2010-08-12 01:58:28 +00:00
ch = priv - > channel [ CTCM_WRITE ] ;
2008-02-08 00:03:49 +01: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 01:58:28 +00:00
grp - > sweep_rsp_pend_num = grp - > active_channels [ CTCM_READ ] ;
grp - > sweep_req_pend_num = grp - > active_channels [ CTCM_READ ] ;
2008-02-08 00:03:49 +01:00
}
sweep_skb = __dev_alloc_skb ( MPC_BUFSIZE_DEFAULT , GFP_ATOMIC | GFP_DMA ) ;
if ( sweep_skb = = NULL ) {
2008-07-18 15:24:57 +02:00
/* rc = -ENOMEM; */
goto nomem ;
2008-02-08 00:03:49 +01:00
}
2020-11-30 11:09:45 +01:00
header = skb_put_zero ( sweep_skb , TH_SWEEP_LENGTH ) ;
2008-02-08 00:03:49 +01:00
header - > th . th_ch_flag = TH_SWEEP_REQ ; /* 0x0f */
header - > sw . th_last_seq = ch - > th_seq_num ;
2016-05-03 16:33:13 +02:00
netif_trans_update ( dev ) ;
2008-02-08 00:03:49 +01:00
skb_queue_tail ( & ch - > sweep_queue , sweep_skb ) ;
fsm_addtimer ( & ch - > sweep_timer , 100 , CTC_EVENT_RSWEEP_TIMER , ch ) ;
return ;
2008-07-18 15:24:57 +02:00
nomem :
grp - > in_sweep = 0 ;
ctcm_clear_busy ( dev ) ;
fsm_event ( grp - > fsm , MPCG_EVENT_INOP , dev ) ;
2008-02-08 00:03:49 +01: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 17:10:24 +02:00
struct ctcm_priv * priv = dev - > ml_priv ;
2008-02-08 00:03:49 +01: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 15:24:57 +02: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 00:03:49 +01: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 00:03:49 +01:00
2020-11-30 11:09:47 +01:00
p_header = skb_push ( skb , PDU_HEADER_LENGTH ) ;
p_header - > pdu_offset = skb - > len - PDU_HEADER_LENGTH ;
2008-02-08 00:03:49 +01:00
p_header - > pdu_proto = 0x01 ;
2017-04-07 09:15:37 +02:00
if ( be16_to_cpu ( skb - > protocol ) = = ETH_P_SNAP ) {
2020-11-30 11:09:47 +01:00
p_header - > pdu_flag = PDU_FIRST | PDU_CNTL ;
2008-02-08 00:03:49 +01:00
} else {
2020-11-30 11:09:47 +01:00
p_header - > pdu_flag = PDU_FIRST ;
2008-02-08 00:03:49 +01:00
}
p_header - > pdu_seq = 0 ;
2008-07-18 15:24:57 +02: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 00:03:49 +01:00
skb_queue_tail ( & ch - > collect_queue , skb ) ;
ch - > collect_len + = skb - > len ;
spin_unlock_irqrestore ( & ch - > collect_lock , saveflags ) ;
2023-02-09 12:04:21 +01:00
goto done ;
2008-02-08 00:03:49 +01:00
}
/*
* Protect skb against beeing free ' d by upper
* layers .
*/
2017-06-30 13:07:58 +03:00
refcount_inc ( & skb - > users ) ;
2008-02-08 00:03:49 +01: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 15:24:57 +02:00
goto nomem_exit ;
2008-02-08 00:03:49 +01: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 14:29:20 +02: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 00:03:49 +01:00
dev_kfree_skb_irq ( skb ) ;
skb = nskb ;
}
}
2020-11-30 11:09:47 +01:00
p_header = skb_push ( skb , PDU_HEADER_LENGTH ) ;
p_header - > pdu_offset = skb - > len - PDU_HEADER_LENGTH ;
2008-02-08 00:03:49 +01:00
p_header - > pdu_proto = 0x01 ;
p_header - > pdu_seq = 0 ;
2017-04-07 09:15:37 +02:00
if ( be16_to_cpu ( skb - > protocol ) = = ETH_P_SNAP ) {
2020-11-30 11:09:47 +01:00
p_header - > pdu_flag = PDU_FIRST | PDU_CNTL ;
2008-02-08 00:03:49 +01:00
} else {
2020-11-30 11:09:47 +01:00
p_header - > pdu_flag = PDU_FIRST ;
2008-02-08 00:03:49 +01:00
}
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 ;
2020-11-30 11:09:45 +01:00
/* put the TH on the packet */
header = skb_push ( skb , TH_HEADER_LENGTH ) ;
memset ( header , 0 , TH_HEADER_LENGTH ) ;
2008-02-08 00:03:49 +01:00
header - > th_ch_flag = TH_HAS_PDU ; /* Normal data */
ch - > th_seq_num + + ;
header - > th_seq_num = ch - > th_seq_num ;
2008-07-18 15:24:57 +02:00
CTCM_PR_DBGDATA ( " %s(%s) ToVTAM_th_seq= %08x \n " ,
__func__ , dev - > name , ch - > th_seq_num ) ;
2008-02-08 00:03:49 +01:00
2008-07-18 15:24:57 +02: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 00:03:49 +01:00
ch - > ccw [ 4 ] . count = skb - > len ;
if ( set_normalized_cda ( & ch - > ccw [ 4 ] , skb - > data ) ) {
/*
2008-07-18 15:24:57 +02:00
* idal allocation failed , try via copying to trans_skb .
* trans_skb usually has a pre - allocated idal .
2008-02-08 00:03:49 +01:00
*/
if ( ctcm_checkalloc_buffer ( ch ) ) {
/*
2008-07-18 15:24:57 +02:00
* Remove our header .
* It gets added again on retransmit .
2008-02-08 00:03:49 +01:00
*/
2008-07-18 15:24:57 +02:00
goto nomem_exit ;
2008-02-08 00:03:49 +01: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 14:29:20 +02: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 00:03:49 +01:00
dev_kfree_skb_irq ( skb ) ;
ccw_idx = 0 ;
2008-07-18 15:24:57 +02: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 00:03:49 +01: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 14:05:45 +01:00
ch - > prof . send_stamp = jiffies ;
2019-08-20 16:46:42 +02:00
rc = ccw_device_start ( ch - > cdev , & ch - > ccw [ ccw_idx ] , 0 , 0xff , 0 ) ;
2008-02-08 00:03:49 +01: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 15:24:57 +02:00
if ( ch - > th_seq_num > 0xf0000000 ) /* Chose at random. */
2008-02-08 00:03:49 +01:00
ctcmpc_send_sweep_req ( ch ) ;
2008-07-18 15:24:57 +02: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 15:24:57 +02:00
dev_kfree_skb_any ( skb ) ;
fsm_event ( priv - > mpcg - > fsm , MPCG_EVENT_INOP , dev ) ;
2008-02-08 00:03:49 +01:00
done :
2008-07-18 15:24:57 +02:00
CTCM_PR_DEBUG ( " Exit %s(%s) \n " , __func__ , dev - > name ) ;
return rc ;
2008-02-08 00:03:49 +01:00
}
2021-09-14 10:33:17 +02:00
/*
2008-02-08 00:03:49 +01:00
* Start transmission of a packet .
* Called from generic network device layer .
*/
/* first merge version - leaving both functions separated */
s390/ctcm: Fix return type of ctc{mp,}m_tx()
With clang's kernel control flow integrity (kCFI, CONFIG_CFI_CLANG),
indirect call targets are validated against the expected function
pointer prototype to make sure the call target is valid to help mitigate
ROP attacks. If they are not identical, there is a failure at run time,
which manifests as either a kernel panic or thread getting killed. A
proposed warning in clang aims to catch these at compile time, which
reveals:
drivers/s390/net/ctcm_main.c:1064:21: error: incompatible function pointer types initializing 'netdev_tx_t (*)(struct sk_buff *, struct net_device *)' (aka 'enum netdev_tx (*)(struct sk_buff *, struct net_device *)') with an expression of type 'int (struct sk_buff *, struct net_device *)' [-Werror,-Wincompatible-function-pointer-types-strict]
.ndo_start_xmit = ctcm_tx,
^~~~~~~
drivers/s390/net/ctcm_main.c:1072:21: error: incompatible function pointer types initializing 'netdev_tx_t (*)(struct sk_buff *, struct net_device *)' (aka 'enum netdev_tx (*)(struct sk_buff *, struct net_device *)') with an expression of type 'int (struct sk_buff *, struct net_device *)' [-Werror,-Wincompatible-function-pointer-types-strict]
.ndo_start_xmit = ctcmpc_tx,
^~~~~~~~~
->ndo_start_xmit() in 'struct net_device_ops' expects a return type of
'netdev_tx_t', not 'int'. Adjust the return type of ctc{mp,}m_tx() to
match the prototype's to resolve the warning and potential CFI failure,
should s390 select ARCH_SUPPORTS_CFI_CLANG in the future.
Additionally, while in the area, remove a comment block that is no
longer relevant.
Link: https://github.com/ClangBuiltLinux/linux/issues/1750
Reviewed-by: Alexandra Winter <wintera@linux.ibm.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2022-11-03 10:01:28 -07:00
static netdev_tx_t ctcm_tx ( struct sk_buff * skb , struct net_device * dev )
2008-02-08 00:03:49 +01:00
{
2008-08-21 17:10:24 +02:00
struct ctcm_priv * priv = dev - > ml_priv ;
2008-02-08 00:03:49 +01:00
if ( skb = = NULL ) {
2008-07-18 15:24:57 +02:00
CTCM_DBF_TEXT_ ( ERROR , CTC_DBF_ERROR ,
" %s(%s): NULL sk_buff passed " ,
CTCM_FUNTAIL , dev - > name ) ;
2008-02-08 00:03:49 +01:00
priv - > stats . tx_dropped + + ;
2009-06-23 06:03:08 +00:00
return NETDEV_TX_OK ;
2008-02-08 00:03:49 +01:00
}
if ( skb_headroom ( skb ) < ( LL_HEADER_LENGTH + 2 ) ) {
2008-07-18 15:24:57 +02: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 00:03:49 +01:00
dev_kfree_skb ( skb ) ;
priv - > stats . tx_dropped + + ;
2009-06-23 06:03:08 +00:00
return NETDEV_TX_OK ;
2008-02-08 00:03:49 +01: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 06:03:08 +00:00
return NETDEV_TX_OK ;
2008-02-08 00:03:49 +01:00
}
if ( ctcm_test_and_set_busy ( dev ) )
2009-03-24 03:27:47 +00:00
return NETDEV_TX_BUSY ;
2008-02-08 00:03:49 +01:00
2016-05-03 16:33:13 +02:00
netif_trans_update ( dev ) ;
2010-08-12 01:58:28 +00:00
if ( ctcm_transmit_skb ( priv - > channel [ CTCM_WRITE ] , skb ) ! = 0 )
2009-03-24 03:27:47 +00:00
return NETDEV_TX_BUSY ;
2009-06-23 06:03:08 +00:00
return NETDEV_TX_OK ;
2008-02-08 00:03:49 +01:00
}
/* unmerged MPC variant of ctcm_tx */
s390/ctcm: Fix return type of ctc{mp,}m_tx()
With clang's kernel control flow integrity (kCFI, CONFIG_CFI_CLANG),
indirect call targets are validated against the expected function
pointer prototype to make sure the call target is valid to help mitigate
ROP attacks. If they are not identical, there is a failure at run time,
which manifests as either a kernel panic or thread getting killed. A
proposed warning in clang aims to catch these at compile time, which
reveals:
drivers/s390/net/ctcm_main.c:1064:21: error: incompatible function pointer types initializing 'netdev_tx_t (*)(struct sk_buff *, struct net_device *)' (aka 'enum netdev_tx (*)(struct sk_buff *, struct net_device *)') with an expression of type 'int (struct sk_buff *, struct net_device *)' [-Werror,-Wincompatible-function-pointer-types-strict]
.ndo_start_xmit = ctcm_tx,
^~~~~~~
drivers/s390/net/ctcm_main.c:1072:21: error: incompatible function pointer types initializing 'netdev_tx_t (*)(struct sk_buff *, struct net_device *)' (aka 'enum netdev_tx (*)(struct sk_buff *, struct net_device *)') with an expression of type 'int (struct sk_buff *, struct net_device *)' [-Werror,-Wincompatible-function-pointer-types-strict]
.ndo_start_xmit = ctcmpc_tx,
^~~~~~~~~
->ndo_start_xmit() in 'struct net_device_ops' expects a return type of
'netdev_tx_t', not 'int'. Adjust the return type of ctc{mp,}m_tx() to
match the prototype's to resolve the warning and potential CFI failure,
should s390 select ARCH_SUPPORTS_CFI_CLANG in the future.
Additionally, while in the area, remove a comment block that is no
longer relevant.
Link: https://github.com/ClangBuiltLinux/linux/issues/1750
Reviewed-by: Alexandra Winter <wintera@linux.ibm.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2022-11-03 10:01:28 -07:00
static netdev_tx_t ctcmpc_tx ( struct sk_buff * skb , struct net_device * dev )
2008-02-08 00:03:49 +01:00
{
int len = 0 ;
2008-08-21 17:10:24 +02:00
struct ctcm_priv * priv = dev - > ml_priv ;
2008-07-18 15:24:57 +02:00
struct mpc_group * grp = priv - > mpcg ;
2008-02-08 00:03:49 +01:00
struct sk_buff * newskb = NULL ;
/*
* Some sanity checks . . .
*/
if ( skb = = NULL ) {
2008-07-18 15:24:57 +02:00
CTCM_DBF_TEXT_ ( MPC_ERROR , CTC_DBF_ERROR ,
" %s(%s): NULL sk_buff passed " ,
CTCM_FUNTAIL , dev - > name ) ;
2008-02-08 00:03:49 +01:00
priv - > stats . tx_dropped + + ;
2023-02-09 12:04:21 +01:00
goto done ;
2008-02-08 00:03:49 +01:00
}
if ( skb_headroom ( skb ) < ( TH_HEADER_LENGTH + PDU_HEADER_LENGTH ) ) {
2008-07-18 15:24:57 +02: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 00:03:49 +01:00
2008-07-18 15:24:57 +02:00
CTCM_D3_DUMP ( ( char * ) skb - > data , min_t ( int , 32 , skb - > len ) ) ;
2008-02-08 00:03:49 +01:00
len = skb - > len + TH_HEADER_LENGTH + PDU_HEADER_LENGTH ;
2020-11-30 11:09:50 +01:00
newskb = __dev_alloc_skb ( len , GFP_ATOMIC | GFP_DMA ) ;
2008-02-08 00:03:49 +01:00
if ( ! newskb ) {
2008-07-18 15:24:57 +02:00
CTCM_DBF_TEXT_ ( MPC_TRACE , CTC_DBF_ERROR ,
" %s: %s: __dev_alloc_skb failed " ,
__func__ , dev - > name ) ;
2008-02-08 00:03:49 +01: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 ) ;
2023-02-09 12:04:21 +01:00
goto done ;
2008-02-08 00:03:49 +01:00
}
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 14:29:20 +02:00
skb_put_data ( newskb , skb - > data , skb - > len ) ;
2008-02-08 00:03:49 +01: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 15:24:57 +02:00
CTCM_DBF_TEXT_ ( MPC_ERROR , CTC_DBF_ERROR ,
" %s(%s): inactive MPCGROUP - dropped " ,
CTCM_FUNTAIL , dev - > name ) ;
2008-02-08 00:03:49 +01:00
priv - > stats . tx_dropped + + ;
priv - > stats . tx_errors + + ;
priv - > stats . tx_carrier_errors + + ;
2023-02-09 12:04:21 +01:00
goto done ;
2008-02-08 00:03:49 +01:00
}
if ( ctcm_test_and_set_busy ( dev ) ) {
2008-07-18 15:24:57 +02:00
CTCM_DBF_TEXT_ ( MPC_ERROR , CTC_DBF_ERROR ,
" %s(%s): device busy - dropped " ,
CTCM_FUNTAIL , dev - > name ) ;
2008-02-08 00:03:49 +01: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 ) ;
2023-02-09 12:04:21 +01:00
goto done ;
2008-02-08 00:03:49 +01:00
}
2016-05-03 16:33:13 +02:00
netif_trans_update ( dev ) ;
2010-08-12 01:58:28 +00:00
if ( ctcmpc_transmit_skb ( priv - > channel [ CTCM_WRITE ] , skb ) ! = 0 ) {
2008-07-18 15:24:57 +02:00
CTCM_DBF_TEXT_ ( MPC_ERROR , CTC_DBF_ERROR ,
" %s(%s): device error - dropped " ,
CTCM_FUNTAIL , dev - > name ) ;
2008-02-08 00:03:49 +01: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 ) ;
2023-02-09 12:04:21 +01:00
goto done ;
2008-02-08 00:03:49 +01:00
}
ctcm_clear_busy ( dev ) ;
done :
if ( do_debug )
MPC_DBF_DEV_NAME ( TRACE , dev , " exit " ) ;
2009-06-23 06:03:08 +00:00
return NETDEV_TX_OK ; /* handle freeing of skb here */
2008-02-08 00:03:49 +01:00
}
2021-09-14 10:33:17 +02:00
/*
2008-02-08 00:03:49 +01: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 17:10:24 +02:00
priv = dev - > ml_priv ;
2010-08-12 01:58:28 +00:00
max_bufsize = priv - > channel [ CTCM_READ ] - > max_bufsize ;
2008-02-08 00:03:49 +01: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 ;
}
2021-09-14 10:33:17 +02:00
/*
2008-02-08 00:03:49 +01:00
* 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 17:10:24 +02:00
return & ( ( struct ctcm_priv * ) dev - > ml_priv ) - > stats ;
2008-02-08 00:03:49 +01:00
}
static void ctcm_free_netdevice ( struct net_device * dev )
{
struct ctcm_priv * priv ;
struct mpc_group * grp ;
2008-07-18 15:24:57 +02:00
CTCM_DBF_TEXT_ ( SETUP , CTC_DBF_INFO ,
" %s(%s) " , CTCM_FUNTAIL , dev - > name ) ;
2008-08-21 17:10:24 +02:00
priv = dev - > ml_priv ;
2008-02-08 00:03:49 +01:00
if ( priv ) {
grp = priv - > mpcg ;
if ( grp ) {
if ( grp - > fsm )
kfree_fsm ( grp - > fsm ) ;
2019-09-15 19:21:05 +02:00
dev_kfree_skb ( grp - > xid_skb ) ;
dev_kfree_skb ( grp - > rcvd_xid_skb ) ;
2008-02-08 00:03:49 +01: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 03:43:57 +00: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-12 14:37:34 -07:00
static void ctcm_dev_setup ( struct net_device * dev )
2008-02-08 00:03:49 +01:00
{
dev - > type = ARPHRD_SLIP ;
dev - > tx_queue_len = 100 ;
dev - > flags = IFF_POINTOPOINT | IFF_NOARP ;
2016-10-20 13:55:23 -04:00
dev - > min_mtu = 576 ;
dev - > max_mtu = 65527 ;
2008-02-08 00:03:49 +01: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 16:37:24 +02:00
dev = alloc_netdev ( 0 , MPC_DEVICE_GENE , NET_NAME_UNKNOWN ,
ctcm_dev_setup ) ;
2008-02-08 00:03:49 +01: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 16:37:24 +02:00
dev = alloc_netdev ( 0 , CTC_DEVICE_GENE , NET_NAME_UNKNOWN ,
ctcm_dev_setup ) ;
2008-02-08 00:03:49 +01:00
if ( ! dev ) {
2008-07-18 15:24:57 +02:00
CTCM_DBF_TEXT_ ( ERROR , CTC_DBF_CRIT ,
" %s: MEMORY allocation ERROR " ,
CTCM_FUNTAIL ) ;
2008-02-08 00:03:49 +01:00
return NULL ;
}
2008-08-21 17:10:24 +02:00
dev - > ml_priv = priv ;
2008-02-08 00:03:49 +01: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-26 18:56:06 -07:00
free_netdev ( dev ) ;
2008-02-08 00:03:49 +01: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-26 18:56:06 -07:00
free_netdev ( dev ) ;
2008-02-08 00:03:49 +01: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 03:43:57 +00:00
dev - > netdev_ops = & ctcm_mpc_netdev_ops ;
2008-02-08 00:03:49 +01: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 03:43:57 +00:00
dev - > netdev_ops = & ctcm_netdev_ops ;
2008-02-08 00:03:49 +01:00
dev - > hard_header_len = LL_HEADER_LENGTH + 2 ;
}
CTCMY_DBF_DEV ( SETUP , dev , " finished " ) ;
2008-07-18 15:24:57 +02:00
2008-02-08 00:03:49 +01:00
return dev ;
}
2021-09-14 10:33:17 +02:00
/*
2008-02-08 00:03:49 +01:00
* 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 15:24:57 +02:00
int cstat ;
int dstat ;
CTCM_DBF_TEXT_ ( TRACE , CTC_DBF_DEBUG ,
2008-10-10 21:33:09 +02:00
" Enter %s(%s) " , CTCM_FUNTAIL , dev_name ( & cdev - > dev ) ) ;
2008-02-08 00:03:49 +01:00
if ( ctcm_check_irb_error ( cdev , irb ) )
return ;
cgdev = dev_get_drvdata ( & cdev - > dev ) ;
2008-07-18 15:24:57 +02:00
cstat = irb - > scsw . cmd . cstat ;
dstat = irb - > scsw . cmd . dstat ;
2008-02-08 00:03:49 +01:00
/* Check for unsolicited interrupts. */
if ( cgdev = = NULL ) {
2008-12-25 13:39:54 +01: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 00:03:49 +01:00
return ;
}
priv = dev_get_drvdata ( & cgdev - > dev ) ;
/* Try to extract channel from driver data. */
2010-08-12 01:58:28 +00: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 00:03:49 +01:00
else {
2008-12-25 13:39:54 +01: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 00:03:49 +01:00
return ;
}
2008-07-18 15:24:57 +02:00
dev = ch - > netdev ;
2008-02-08 00:03:49 +01:00
if ( dev = = NULL ) {
2008-12-25 13:39:54 +01: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 00:03:49 +01:00
return ;
}
/* Copy interruption response block. */
memcpy ( ch - > irb , irb , sizeof ( struct irb ) ) ;
2008-12-25 13:39:54 +01:00
/* Issue error message and return on subchannel error code */
2008-07-14 09:58:50 +02:00
if ( irb - > scsw . cmd . cstat ) {
2008-02-08 00:03:49 +01:00
fsm_event ( ch - > fsm , CTC_EVENT_SC_UNKNOWN , ch ) ;
2008-12-25 13:39:54 +01: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 00:03:49 +01:00
return ;
}
/* Check the reason-code of a unit check */
2008-07-14 09:58:50 +02:00
if ( irb - > scsw . cmd . dstat & DEV_STAT_UNIT_CHECK ) {
2008-07-18 15:24:57 +02:00
if ( ( irb - > ecw [ 0 ] & ch - > sense_rc ) = = 0 )
/* print it only once */
2008-12-25 13:39:54 +01:00
CTCM_DBF_TEXT_ ( TRACE , CTC_DBF_WARN ,
2008-07-18 15:24:57 +02:00
" %s(%s): sense=%02x, ds=%02x " ,
CTCM_FUNTAIL , ch - > id , irb - > ecw [ 0 ] , dstat ) ;
2008-02-08 00:03:49 +01:00
ccw_unit_check ( ch , irb - > ecw [ 0 ] ) ;
return ;
}
2008-07-14 09:58:50 +02:00
if ( irb - > scsw . cmd . dstat & DEV_STAT_BUSY ) {
if ( irb - > scsw . cmd . dstat & DEV_STAT_ATTENTION )
2008-02-08 00:03:49 +01:00
fsm_event ( ch - > fsm , CTC_EVENT_ATTNBUSY , ch ) ;
else
fsm_event ( ch - > fsm , CTC_EVENT_BUSY , ch ) ;
return ;
}
2008-07-14 09:58:50 +02:00
if ( irb - > scsw . cmd . dstat & DEV_STAT_ATTENTION ) {
2008-02-08 00:03:49 +01:00
fsm_event ( ch - > fsm , CTC_EVENT_ATTN , ch ) ;
return ;
}
2008-07-14 09:58:50 +02: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 00:03:49 +01: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 17:54:12 +02:00
static const struct device_type ctcm_devtype = {
. name = " ctcm " ,
. groups = ctcm_attr_groups ,
} ;
2021-09-14 10:33:17 +02:00
/*
2008-02-08 00:03:49 +01: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 15:24:57 +02:00
CTCM_DBF_TEXT_ ( SETUP , CTC_DBF_INFO ,
" %s %p " ,
__func__ , cgdev ) ;
2008-02-08 00:03:49 +01:00
if ( ! get_device ( & cgdev - > dev ) )
return - ENODEV ;
priv = kzalloc ( sizeof ( struct ctcm_priv ) , GFP_KERNEL ) ;
if ( ! priv ) {
2008-07-18 15:24:57 +02:00
CTCM_DBF_TEXT_ ( ERROR , CTC_DBF_ERROR ,
" %s: memory allocation failure " ,
CTCM_FUNTAIL ) ;
2008-02-08 00:03:49 +01: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 17:54:12 +02:00
cgdev - > dev . type = & ctcm_devtype ;
2008-02-08 00:03:49 +01:00
return 0 ;
}
2021-09-14 10:33:17 +02:00
/*
2008-02-08 00:03:49 +01:00
* 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-12 21:46:29 +00:00
static int add_channel ( struct ccw_device * cdev , enum ctcm_channel_types type ,
2008-02-08 00:03:49 +01:00
struct ctcm_priv * priv )
{
struct channel * * c = & channels ;
struct channel * ch ;
int ccw_num ;
int rc = 0 ;
2008-07-18 15:24:57 +02:00
CTCM_DBF_TEXT_ ( SETUP , CTC_DBF_INFO ,
" %s(%s), type %d, proto %d " ,
2008-10-10 21:33:09 +02:00
__func__ , dev_name ( & cdev - > dev ) , type , priv - > protocol ) ;
2008-07-18 15:24:57 +02:00
2008-02-08 00:03:49 +01:00
ch = kzalloc ( sizeof ( struct channel ) , GFP_KERNEL ) ;
if ( ch = = NULL )
2008-07-18 15:24:57 +02:00
return - ENOMEM ;
2008-02-08 00:03:49 +01:00
ch - > protocol = priv - > protocol ;
if ( IS_MPC ( priv ) ) {
2020-11-30 11:09:49 +01:00
ch - > discontact_th = kzalloc ( TH_HEADER_LENGTH , GFP_KERNEL ) ;
2008-02-08 00:03:49 +01: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-12 14:03:40 -07:00
ch - > ccw = kcalloc ( ccw_num , sizeof ( struct ccw1 ) , GFP_KERNEL | GFP_DMA ) ;
2008-02-08 00:03:49 +01:00
if ( ch - > ccw = = NULL )
goto nomem_return ;
ch - > cdev = cdev ;
2023-06-21 15:49:21 +02:00
scnprintf ( ch - > id , CTCM_ID_SIZE , " ch-%s " , dev_name ( & cdev - > dev ) ) ;
2008-02-08 00:03:49 +01:00
ch - > type = type ;
2021-09-14 10:33:17 +02:00
/*
2008-02-08 00:03:49 +01:00
* " 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 04:24:27 +00:00
goto nomem_return ;
2008-02-08 00:03:49 +01: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 15:24:57 +02:00
__func__ , ( * c ) - > id ) ;
2008-02-08 00:03:49 +01:00
2023-02-09 12:04:21 +01:00
goto free_return ;
2008-02-08 00:03:49 +01:00
}
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 15:24:57 +02:00
kfree ( ch - > ccw ) ;
2008-02-08 00:03:49 +01:00
kfree ( ch - > discontact_th ) ;
kfree_fsm ( ch - > fsm ) ;
kfree ( ch - > irb ) ;
kfree ( ch ) ;
return rc ;
}
/*
* Return type of a detected device .
*/
2009-11-12 21:46:29 +00:00
static enum ctcm_channel_types get_channel_type ( struct ccw_device_id * id )
2008-02-08 00:03:49 +01:00
{
2009-11-12 21:46:29 +00:00
enum ctcm_channel_types type ;
type = ( enum ctcm_channel_types ) id - > driver_info ;
2008-02-08 00:03:49 +01:00
2009-11-12 21:46:29 +00:00
if ( type = = ctcm_channel_type_ficon )
type = ctcm_channel_type_escon ;
2008-02-08 00:03:49 +01:00
return type ;
}
2021-09-14 10:33:17 +02:00
/*
2008-02-08 00:03:49 +01:00
*
* 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-12 21:46:29 +00:00
enum ctcm_channel_types type ;
2008-02-08 00:03:49 +01:00
struct ctcm_priv * priv ;
struct net_device * dev ;
2008-07-18 15:24:57 +02:00
struct ccw_device * cdev0 ;
struct ccw_device * cdev1 ;
2009-10-14 22:54:59 +00:00
struct channel * readc ;
struct channel * writec ;
2008-02-08 00:03:49 +01:00
int ret ;
2009-10-14 22:54:59 +00:00
int result ;
2008-02-08 00:03:49 +01:00
priv = dev_get_drvdata ( & cgdev - > dev ) ;
2009-10-14 22:54:59 +00:00
if ( ! priv ) {
result = - ENODEV ;
goto out_err_result ;
}
2008-02-08 00:03:49 +01:00
2008-07-18 15:24:57 +02:00
cdev0 = cgdev - > cdev [ 0 ] ;
cdev1 = cgdev - > cdev [ 1 ] ;
type = get_channel_type ( & cdev0 - > id ) ;
2008-02-08 00:03:49 +01:00
2023-06-21 15:49:21 +02:00
scnprintf ( read_id , CTCM_ID_SIZE , " ch-%s " , dev_name ( & cdev0 - > dev ) ) ;
scnprintf ( write_id , CTCM_ID_SIZE , " ch-%s " , dev_name ( & cdev1 - > dev ) ) ;
2008-02-08 00:03:49 +01:00
2008-07-18 15:24:57 +02:00
ret = add_channel ( cdev0 , type , priv ) ;
2009-10-14 22:54:59 +00:00
if ( ret ) {
result = ret ;
goto out_err_result ;
}
2008-07-18 15:24:57 +02:00
ret = add_channel ( cdev1 , type , priv ) ;
2009-10-14 22:54:59 +00:00
if ( ret ) {
result = ret ;
goto out_remove_channel1 ;
}
2008-02-08 00:03:49 +01:00
2008-07-18 15:24:57 +02:00
ret = ccw_device_set_online ( cdev0 ) ;
2008-02-08 00:03:49 +01:00
if ( ret ! = 0 ) {
2008-07-18 15:24:57 +02:00
CTCM_DBF_TEXT_ ( TRACE , CTC_DBF_NOTICE ,
" %s(%s) set_online rc=%d " ,
CTCM_FUNTAIL , read_id , ret ) ;
2009-10-14 22:54:59 +00:00
result = - EIO ;
goto out_remove_channel2 ;
2008-02-08 00:03:49 +01:00
}
2008-07-18 15:24:57 +02:00
ret = ccw_device_set_online ( cdev1 ) ;
2008-02-08 00:03:49 +01:00
if ( ret ! = 0 ) {
2008-07-18 15:24:57 +02:00
CTCM_DBF_TEXT_ ( TRACE , CTC_DBF_NOTICE ,
" %s(%s) set_online rc=%d " ,
CTCM_FUNTAIL , write_id , ret ) ;
2009-10-14 22:54:59 +00:00
result = - EIO ;
goto out_ccw1 ;
2008-02-08 00:03:49 +01:00
}
dev = ctcm_init_netdevice ( priv ) ;
2009-10-14 22:54:59 +00:00
if ( dev = = NULL ) {
result = - ENODEV ;
goto out_ccw2 ;
}
2008-02-08 00:03:49 +01:00
2010-08-12 01:58:28 +00:00
for ( direction = CTCM_READ ; direction < = CTCM_WRITE ; direction + + ) {
2008-02-08 00:03:49 +01:00
priv - > channel [ direction ] =
2010-08-12 01:58:28 +00:00
channel_get ( type , direction = = CTCM_READ ?
read_id : write_id , direction ) ;
2008-02-08 00:03:49 +01:00
if ( priv - > channel [ direction ] = = NULL ) {
2010-08-12 01:58:28 +00:00
if ( direction = = CTCM_WRITE )
channel_free ( priv - > channel [ CTCM_READ ] ) ;
2019-04-17 18:29:13 +02:00
result = - ENODEV ;
2008-07-18 15:24:57 +02:00
goto out_dev ;
2008-02-08 00:03:49 +01: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-14 22:54:59 +00:00
if ( register_netdev ( dev ) ) {
result = - ENODEV ;
goto out_dev ;
}
2008-02-08 00:03:49 +01:00
2022-08-18 22:59:47 +02:00
strscpy ( priv - > fsm - > name , dev - > name , sizeof ( priv - > fsm - > name ) ) ;
2008-02-08 00:03:49 +01:00
2008-12-25 13:39:54 +01:00
dev_info ( & dev - > dev ,
" setup OK : r/w = %s/%s, protocol : %d \n " ,
2010-08-12 01:58:28 +00:00
priv - > channel [ CTCM_READ ] - > id ,
priv - > channel [ CTCM_WRITE ] - > id , priv - > protocol ) ;
2008-12-25 13:39:54 +01:00
2008-02-08 00:03:49 +01:00
CTCM_DBF_TEXT_ ( SETUP , CTC_DBF_INFO ,
2008-07-18 15:24:57 +02:00
" setup(%s) OK : r/w = %s/%s, protocol : %d " , dev - > name ,
2010-08-12 01:58:28 +00:00
priv - > channel [ CTCM_READ ] - > id ,
priv - > channel [ CTCM_WRITE ] - > id , priv - > protocol ) ;
2008-02-08 00:03:49 +01:00
return 0 ;
2008-07-18 15:24:57 +02:00
out_dev :
ctcm_free_netdevice ( dev ) ;
2009-10-14 22:54:59 +00:00
out_ccw2 :
2008-02-08 00:03:49 +01:00
ccw_device_set_offline ( cgdev - > cdev [ 1 ] ) ;
2009-10-14 22:54:59 +00:00
out_ccw1 :
2008-02-08 00:03:49 +01:00
ccw_device_set_offline ( cgdev - > cdev [ 0 ] ) ;
2009-10-14 22:54:59 +00:00
out_remove_channel2 :
2010-08-12 01:58:28 +00:00
readc = channel_get ( type , read_id , CTCM_READ ) ;
2009-10-14 22:54:59 +00:00
channel_remove ( readc ) ;
out_remove_channel1 :
2010-08-12 01:58:28 +00:00
writec = channel_get ( type , write_id , CTCM_WRITE ) ;
2009-10-14 22:54:59 +00:00
channel_remove ( writec ) ;
out_err_result :
return result ;
2008-02-08 00:03:49 +01:00
}
2021-09-14 10:33:17 +02:00
/*
2008-02-08 00:03:49 +01: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 01:58:28 +00:00
if ( priv - > channel [ CTCM_READ ] ) {
dev = priv - > channel [ CTCM_READ ] - > netdev ;
2008-02-08 00:03:49 +01:00
CTCM_DBF_DEV ( SETUP , dev , " " ) ;
/* Close the device */
ctcm_close ( dev ) ;
dev - > flags & = ~ IFF_RUNNING ;
2010-08-12 01:58:28 +00:00
channel_free ( priv - > channel [ CTCM_READ ] ) ;
2008-02-08 00:03:49 +01:00
} else
dev = NULL ;
2010-08-12 01:58:28 +00:00
if ( priv - > channel [ CTCM_WRITE ] )
channel_free ( priv - > channel [ CTCM_WRITE ] ) ;
2008-02-08 00:03:49 +01:00
if ( dev ) {
2008-07-18 15:24:57 +02:00
unregister_netdev ( dev ) ;
2008-02-08 00:03:49 +01: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 12:27:53 +01:00
channel_remove ( priv - > channel [ CTCM_READ ] ) ;
channel_remove ( priv - > channel [ CTCM_WRITE ] ) ;
2010-08-12 01:58:28 +00:00
priv - > channel [ CTCM_READ ] = priv - > channel [ CTCM_WRITE ] = NULL ;
2008-02-08 00:03:49 +01:00
return 0 ;
}
static void ctcm_remove_device ( struct ccwgroup_device * cgdev )
{
2008-07-18 15:24:57 +02:00
struct ctcm_priv * priv = dev_get_drvdata ( & cgdev - > dev ) ;
2008-02-08 00:03:49 +01:00
2008-07-18 15:24:57 +02:00
CTCM_DBF_TEXT_ ( SETUP , CTC_DBF_INFO ,
2009-05-19 21:38:38 +00:00
" removing device %p, proto : %d " ,
cgdev , priv - > protocol ) ;
2008-02-08 00:03:49 +01:00
if ( cgdev - > state = = CCWGROUP_ONLINE )
ctcm_shutdown_device ( cgdev ) ;
dev_set_drvdata ( & cgdev - > dev , NULL ) ;
kfree ( priv ) ;
put_device ( & cgdev - > dev ) ;
}
2009-11-12 21:46:29 +00: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 10:16:02 +01:00
. driver = {
. owner = THIS_MODULE ,
. name = " ctcm " ,
} ,
2009-11-12 21:46:29 +00:00
. ids = ctcm_ids ,
. probe = ccwgroup_probe_ccwdev ,
. remove = ccwgroup_remove_ccwdev ,
2013-01-02 15:18:18 +01:00
. int_class = IRQIO_CTC ,
2009-11-12 21:46:29 +00:00
} ;
2008-02-08 00:03:49 +01:00
static struct ccwgroup_driver ctcm_group_driver = {
2011-03-23 10:16:04 +01:00
. driver = {
. owner = THIS_MODULE ,
. name = CTC_DRIVER_NAME ,
} ,
2017-09-14 09:52:32 +02:00
. ccw_driver = & ctcm_ccw_driver ,
2012-05-15 17:54:12 +02:00
. setup = ctcm_probe_device ,
2008-02-08 00:03:49 +01:00
. remove = ctcm_remove_device ,
. set_online = ctcm_new_device ,
. set_offline = ctcm_shutdown_device ,
} ;
2017-06-09 11:03:13 +02:00
static ssize_t group_store ( struct device_driver * ddrv , const char * buf ,
size_t count )
2009-11-12 21:46:29 +00:00
{
int err ;
2012-05-15 18:03:46 +02:00
err = ccwgroup_create_dev ( ctcm_root_dev , & ctcm_group_driver , 2 , buf ) ;
2009-11-12 21:46:29 +00:00
return err ? err : count ;
}
2017-06-09 11:03:13 +02:00
static DRIVER_ATTR_WO ( group ) ;
2009-11-12 21:46:29 +00:00
2012-05-15 18:05:01 +02:00
static struct attribute * ctcm_drv_attrs [ ] = {
2009-11-12 21:46:29 +00:00
& driver_attr_group . attr ,
NULL ,
} ;
2012-05-15 18:05:01 +02:00
static struct attribute_group ctcm_drv_attr_group = {
. attrs = ctcm_drv_attrs ,
2009-11-12 21:46:29 +00:00
} ;
2012-05-15 18:05:01 +02:00
static const struct attribute_group * ctcm_drv_attr_groups [ ] = {
& ctcm_drv_attr_group ,
2009-11-12 21:46:29 +00:00
NULL ,
} ;
2008-02-08 00:03:49 +01: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-12 21:46:29 +00:00
ccwgroup_driver_unregister ( & ctcm_group_driver ) ;
ccw_driver_unregister ( & ctcm_ccw_driver ) ;
root_device_unregister ( ctcm_root_dev ) ;
2008-02-08 00:03:49 +01:00
ctcm_unregister_dbf_views ( ) ;
2008-12-25 13:39:54 +01:00
pr_info ( " CTCM driver unloaded \n " ) ;
2008-02-08 00:03:49 +01:00
}
/*
* Print Banner .
*/
static void print_banner ( void )
{
2008-12-25 13:39:54 +01:00
pr_info ( " CTCM driver initialized \n " ) ;
2008-02-08 00:03:49 +01:00
}
2021-09-14 10:33:17 +02:00
/*
2008-02-08 00:03:49 +01: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-12 21:46:29 +00:00
if ( ret )
goto out_err ;
ctcm_root_dev = root_device_register ( " ctcm " ) ;
2014-04-28 10:05:11 +02:00
ret = PTR_ERR_OR_ZERO ( ctcm_root_dev ) ;
2009-11-12 21:46:29 +00:00
if ( ret )
goto register_err ;
ret = ccw_driver_register ( & ctcm_ccw_driver ) ;
if ( ret )
goto ccw_err ;
2012-05-15 18:05:01 +02:00
ctcm_group_driver . driver . groups = ctcm_drv_attr_groups ;
2009-11-12 21:46:29 +00:00
ret = ccwgroup_driver_register ( & ctcm_group_driver ) ;
if ( ret )
goto ccwgroup_err ;
2008-02-08 00:03:49 +01:00
print_banner ( ) ;
2009-11-12 21:46:29 +00: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 00:03:49 +01: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 " ) ;