2020-03-28 02:01:23 +03:00
// SPDX-License-Identifier: GPL-2.0-only
# include "netlink.h"
# include "common.h"
struct pause_req_info {
struct ethnl_req_info base ;
2023-01-19 15:26:56 +03:00
enum ethtool_mac_stats_src src ;
2020-03-28 02:01:23 +03:00
} ;
2023-01-19 15:26:56 +03:00
# define PAUSE_REQINFO(__req_base) \
container_of ( __req_base , struct pause_req_info , base )
2020-03-28 02:01:23 +03:00
struct pause_reply_data {
struct ethnl_reply_data base ;
struct ethtool_pauseparam pauseparam ;
2020-09-15 03:11:52 +03:00
struct ethtool_pause_stats pausestat ;
2020-03-28 02:01:23 +03:00
} ;
# define PAUSE_REPDATA(__reply_base) \
container_of ( __reply_base , struct pause_reply_data , base )
2020-10-06 01:07:35 +03:00
const struct nla_policy ethnl_pause_get_policy [ ] = {
2020-10-06 01:07:36 +03:00
[ ETHTOOL_A_PAUSE_HEADER ] =
2020-10-06 01:07:39 +03:00
NLA_POLICY_NESTED ( ethnl_header_policy_stats ) ,
2023-01-19 15:26:56 +03:00
[ ETHTOOL_A_PAUSE_STATS_SRC ] =
NLA_POLICY_MAX ( NLA_U32 , ETHTOOL_MAC_STATS_SRC_PMAC ) ,
2020-03-28 02:01:23 +03:00
} ;
2023-01-19 15:26:56 +03:00
static int pause_parse_request ( struct ethnl_req_info * req_base ,
struct nlattr * * tb ,
struct netlink_ext_ack * extack )
{
enum ethtool_mac_stats_src src = ETHTOOL_MAC_STATS_SRC_AGGREGATE ;
struct pause_req_info * req_info = PAUSE_REQINFO ( req_base ) ;
if ( tb [ ETHTOOL_A_PAUSE_STATS_SRC ] ) {
if ( ! ( req_base - > flags & ETHTOOL_FLAG_STATS ) ) {
NL_SET_ERR_MSG_MOD ( extack ,
" ETHTOOL_FLAG_STATS must be set when requesting a source of stats " ) ;
return - EINVAL ;
}
src = nla_get_u32 ( tb [ ETHTOOL_A_PAUSE_STATS_SRC ] ) ;
}
req_info - > src = src ;
return 0 ;
}
2020-03-28 02:01:23 +03:00
static int pause_prepare_data ( const struct ethnl_req_info * req_base ,
struct ethnl_reply_data * reply_base ,
2023-08-15 00:47:23 +03:00
const struct genl_info * info )
2020-03-28 02:01:23 +03:00
{
2023-01-19 15:26:56 +03:00
const struct pause_req_info * req_info = PAUSE_REQINFO ( req_base ) ;
2020-03-28 02:01:23 +03:00
struct pause_reply_data * data = PAUSE_REPDATA ( reply_base ) ;
2023-01-19 15:26:56 +03:00
enum ethtool_mac_stats_src src = req_info - > src ;
2020-03-28 02:01:23 +03:00
struct net_device * dev = reply_base - > dev ;
int ret ;
if ( ! dev - > ethtool_ops - > get_pauseparam )
return - EOPNOTSUPP ;
2020-09-15 03:11:52 +03:00
2021-04-14 06:46:14 +03:00
ethtool_stats_init ( ( u64 * ) & data - > pausestat ,
sizeof ( data - > pausestat ) / 8 ) ;
2023-01-19 15:26:56 +03:00
data - > pausestat . src = src ;
2021-04-14 06:46:14 +03:00
2020-03-28 02:01:23 +03:00
ret = ethnl_ops_begin ( dev ) ;
if ( ret < 0 )
return ret ;
2023-01-19 15:26:56 +03:00
if ( ( src = = ETHTOOL_MAC_STATS_SRC_EMAC | |
src = = ETHTOOL_MAC_STATS_SRC_PMAC ) & &
! __ethtool_dev_mm_supported ( dev ) ) {
2023-08-15 00:47:23 +03:00
NL_SET_ERR_MSG_MOD ( info - > extack ,
2023-01-19 15:26:56 +03:00
" Device does not support MAC merge layer " ) ;
ethnl_ops_complete ( dev ) ;
return - EOPNOTSUPP ;
}
2020-03-28 02:01:23 +03:00
dev - > ethtool_ops - > get_pauseparam ( dev , & data - > pauseparam ) ;
2020-09-15 03:11:52 +03:00
if ( req_base - > flags & ETHTOOL_FLAG_STATS & &
2021-04-14 06:46:14 +03:00
dev - > ethtool_ops - > get_pause_stats )
2020-09-15 03:11:52 +03:00
dev - > ethtool_ops - > get_pause_stats ( dev , & data - > pausestat ) ;
2023-01-19 15:26:56 +03:00
2020-03-28 02:01:23 +03:00
ethnl_ops_complete ( dev ) ;
return 0 ;
}
static int pause_reply_size ( const struct ethnl_req_info * req_base ,
const struct ethnl_reply_data * reply_base )
{
2020-09-15 03:11:52 +03:00
int n = nla_total_size ( sizeof ( u8 ) ) + /* _PAUSE_AUTONEG */
2020-03-28 02:01:23 +03:00
nla_total_size ( sizeof ( u8 ) ) + /* _PAUSE_RX */
nla_total_size ( sizeof ( u8 ) ) ; /* _PAUSE_TX */
2020-09-15 03:11:52 +03:00
if ( req_base - > flags & ETHTOOL_FLAG_STATS )
n + = nla_total_size ( 0 ) + /* _PAUSE_STATS */
2023-01-19 15:26:56 +03:00
nla_total_size ( sizeof ( u32 ) ) + /* _PAUSE_STATS_SRC */
2021-11-03 01:02:36 +03:00
nla_total_size_64bit ( sizeof ( u64 ) ) * ETHTOOL_PAUSE_STAT_CNT ;
2020-09-15 03:11:52 +03:00
return n ;
}
static int ethtool_put_stat ( struct sk_buff * skb , u64 val , u16 attrtype ,
u16 padtype )
{
if ( val = = ETHTOOL_STAT_NOT_SET )
return 0 ;
if ( nla_put_u64_64bit ( skb , attrtype , val , padtype ) )
return - EMSGSIZE ;
return 0 ;
}
static int pause_put_stats ( struct sk_buff * skb ,
const struct ethtool_pause_stats * pause_stats )
{
const u16 pad = ETHTOOL_A_PAUSE_STAT_PAD ;
struct nlattr * nest ;
2023-01-19 15:26:56 +03:00
if ( nla_put_u32 ( skb , ETHTOOL_A_PAUSE_STATS_SRC , pause_stats - > src ) )
return - EMSGSIZE ;
2020-09-15 03:11:52 +03:00
nest = nla_nest_start ( skb , ETHTOOL_A_PAUSE_STATS ) ;
if ( ! nest )
return - EMSGSIZE ;
if ( ethtool_put_stat ( skb , pause_stats - > tx_pause_frames ,
ETHTOOL_A_PAUSE_STAT_TX_FRAMES , pad ) | |
ethtool_put_stat ( skb , pause_stats - > rx_pause_frames ,
ETHTOOL_A_PAUSE_STAT_RX_FRAMES , pad ) )
goto err_cancel ;
nla_nest_end ( skb , nest ) ;
return 0 ;
err_cancel :
nla_nest_cancel ( skb , nest ) ;
return - EMSGSIZE ;
2020-03-28 02:01:23 +03:00
}
static int pause_fill_reply ( struct sk_buff * skb ,
const struct ethnl_req_info * req_base ,
const struct ethnl_reply_data * reply_base )
{
const struct pause_reply_data * data = PAUSE_REPDATA ( reply_base ) ;
const struct ethtool_pauseparam * pauseparam = & data - > pauseparam ;
if ( nla_put_u8 ( skb , ETHTOOL_A_PAUSE_AUTONEG , ! ! pauseparam - > autoneg ) | |
nla_put_u8 ( skb , ETHTOOL_A_PAUSE_RX , ! ! pauseparam - > rx_pause ) | |
nla_put_u8 ( skb , ETHTOOL_A_PAUSE_TX , ! ! pauseparam - > tx_pause ) )
return - EMSGSIZE ;
2020-09-15 03:11:52 +03:00
if ( req_base - > flags & ETHTOOL_FLAG_STATS & &
pause_put_stats ( skb , & data - > pausestat ) )
return - EMSGSIZE ;
2020-03-28 02:01:23 +03:00
return 0 ;
}
2020-03-28 02:01:28 +03:00
/* PAUSE_SET */
2020-10-06 01:07:35 +03:00
const struct nla_policy ethnl_pause_set_policy [ ] = {
2020-10-06 01:07:36 +03:00
[ ETHTOOL_A_PAUSE_HEADER ] =
NLA_POLICY_NESTED ( ethnl_header_policy ) ,
2020-03-28 02:01:28 +03:00
[ ETHTOOL_A_PAUSE_AUTONEG ] = { . type = NLA_U8 } ,
[ ETHTOOL_A_PAUSE_RX ] = { . type = NLA_U8 } ,
[ ETHTOOL_A_PAUSE_TX ] = { . type = NLA_U8 } ,
} ;
2023-01-26 02:05:18 +03:00
static int
ethnl_set_pause_validate ( struct ethnl_req_info * req_info ,
struct genl_info * info )
{
const struct ethtool_ops * ops = req_info - > dev - > ethtool_ops ;
return ops - > get_pauseparam & & ops - > set_pauseparam ? 1 : - EOPNOTSUPP ;
}
static int
ethnl_set_pause ( struct ethnl_req_info * req_info , struct genl_info * info )
2020-03-28 02:01:28 +03:00
{
2023-01-26 02:05:18 +03:00
struct net_device * dev = req_info - > dev ;
2020-03-28 02:01:28 +03:00
struct ethtool_pauseparam params = { } ;
2020-10-06 01:07:34 +03:00
struct nlattr * * tb = info - > attrs ;
2020-03-28 02:01:28 +03:00
bool mod = false ;
int ret ;
2023-01-26 02:05:18 +03:00
dev - > ethtool_ops - > get_pauseparam ( dev , & params ) ;
2020-03-28 02:01:28 +03:00
ethnl_update_bool32 ( & params . autoneg , tb [ ETHTOOL_A_PAUSE_AUTONEG ] , & mod ) ;
ethnl_update_bool32 ( & params . rx_pause , tb [ ETHTOOL_A_PAUSE_RX ] , & mod ) ;
ethnl_update_bool32 ( & params . tx_pause , tb [ ETHTOOL_A_PAUSE_TX ] , & mod ) ;
if ( ! mod )
2023-01-26 02:05:18 +03:00
return 0 ;
2020-03-28 02:01:28 +03:00
ret = dev - > ethtool_ops - > set_pauseparam ( dev , & params ) ;
2023-01-26 02:05:18 +03:00
return ret < 0 ? ret : 1 ;
2020-03-28 02:01:28 +03:00
}
2023-01-26 02:05:18 +03:00
const struct ethnl_request_ops ethnl_pause_request_ops = {
. request_cmd = ETHTOOL_MSG_PAUSE_GET ,
. reply_cmd = ETHTOOL_MSG_PAUSE_GET_REPLY ,
. hdr_attr = ETHTOOL_A_PAUSE_HEADER ,
. req_info_size = sizeof ( struct pause_req_info ) ,
. reply_data_size = sizeof ( struct pause_reply_data ) ,
. parse_request = pause_parse_request ,
. prepare_data = pause_prepare_data ,
. reply_size = pause_reply_size ,
. fill_reply = pause_fill_reply ,
. set_validate = ethnl_set_pause_validate ,
. set = ethnl_set_pause ,
. set_ntf_cmd = ETHTOOL_MSG_PAUSE_NTF ,
} ;