2016-02-26 17:32:23 +01:00
/*
* net / core / devlink . c - Network physical / parent device Netlink interface
*
* Heavily inspired by net / wireless /
* Copyright ( c ) 2016 Mellanox Technologies . All rights reserved .
* Copyright ( c ) 2016 Jiri Pirko < jiri @ mellanox . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/types.h>
# include <linux/slab.h>
# include <linux/gfp.h>
# include <linux/device.h>
# include <linux/list.h>
# include <linux/netdevice.h>
# include <rdma/ib_verbs.h>
# include <net/netlink.h>
# include <net/genetlink.h>
# include <net/rtnetlink.h>
# include <net/net_namespace.h>
# include <net/sock.h>
# include <net/devlink.h>
2016-07-12 18:05:03 +02:00
# define CREATE_TRACE_POINTS
# include <trace/events/devlink.h>
EXPORT_TRACEPOINT_SYMBOL_GPL ( devlink_hwmsg ) ;
2016-02-26 17:32:23 +01:00
static LIST_HEAD ( devlink_list ) ;
/* devlink_mutex
*
* An overall lock guarding every operation coming from userspace .
* It also guards devlink devices list and it is taken when
* driver registers / unregisters it .
*/
static DEFINE_MUTEX ( devlink_mutex ) ;
/* devlink_port_mutex
*
* Shared lock to guard lists of ports in all devlink devices .
*/
static DEFINE_MUTEX ( devlink_port_mutex ) ;
static struct net * devlink_net ( const struct devlink * devlink )
{
return read_pnet ( & devlink - > _net ) ;
}
static void devlink_net_set ( struct devlink * devlink , struct net * net )
{
write_pnet ( & devlink - > _net , net ) ;
}
static struct devlink * devlink_get_from_attrs ( struct net * net ,
struct nlattr * * attrs )
{
struct devlink * devlink ;
char * busname ;
char * devname ;
if ( ! attrs [ DEVLINK_ATTR_BUS_NAME ] | | ! attrs [ DEVLINK_ATTR_DEV_NAME ] )
return ERR_PTR ( - EINVAL ) ;
busname = nla_data ( attrs [ DEVLINK_ATTR_BUS_NAME ] ) ;
devname = nla_data ( attrs [ DEVLINK_ATTR_DEV_NAME ] ) ;
list_for_each_entry ( devlink , & devlink_list , list ) {
if ( strcmp ( devlink - > dev - > bus - > name , busname ) = = 0 & &
strcmp ( dev_name ( devlink - > dev ) , devname ) = = 0 & &
net_eq ( devlink_net ( devlink ) , net ) )
return devlink ;
}
return ERR_PTR ( - ENODEV ) ;
}
static struct devlink * devlink_get_from_info ( struct genl_info * info )
{
return devlink_get_from_attrs ( genl_info_net ( info ) , info - > attrs ) ;
}
static struct devlink_port * devlink_port_get_by_index ( struct devlink * devlink ,
int port_index )
{
struct devlink_port * devlink_port ;
list_for_each_entry ( devlink_port , & devlink - > port_list , list ) {
if ( devlink_port - > index = = port_index )
return devlink_port ;
}
return NULL ;
}
static bool devlink_port_index_exists ( struct devlink * devlink , int port_index )
{
return devlink_port_get_by_index ( devlink , port_index ) ;
}
static struct devlink_port * devlink_port_get_from_attrs ( struct devlink * devlink ,
struct nlattr * * attrs )
{
if ( attrs [ DEVLINK_ATTR_PORT_INDEX ] ) {
u32 port_index = nla_get_u32 ( attrs [ DEVLINK_ATTR_PORT_INDEX ] ) ;
struct devlink_port * devlink_port ;
devlink_port = devlink_port_get_by_index ( devlink , port_index ) ;
if ( ! devlink_port )
return ERR_PTR ( - ENODEV ) ;
return devlink_port ;
}
return ERR_PTR ( - EINVAL ) ;
}
static struct devlink_port * devlink_port_get_from_info ( struct devlink * devlink ,
struct genl_info * info )
{
return devlink_port_get_from_attrs ( devlink , info - > attrs ) ;
}
2016-04-14 18:19:13 +02:00
struct devlink_sb {
struct list_head list ;
unsigned int index ;
u32 size ;
u16 ingress_pools_count ;
u16 egress_pools_count ;
u16 ingress_tc_count ;
u16 egress_tc_count ;
} ;
static u16 devlink_sb_pool_count ( struct devlink_sb * devlink_sb )
{
return devlink_sb - > ingress_pools_count + devlink_sb - > egress_pools_count ;
}
static struct devlink_sb * devlink_sb_get_by_index ( struct devlink * devlink ,
unsigned int sb_index )
{
struct devlink_sb * devlink_sb ;
list_for_each_entry ( devlink_sb , & devlink - > sb_list , list ) {
if ( devlink_sb - > index = = sb_index )
return devlink_sb ;
}
return NULL ;
}
static bool devlink_sb_index_exists ( struct devlink * devlink ,
unsigned int sb_index )
{
return devlink_sb_get_by_index ( devlink , sb_index ) ;
}
static struct devlink_sb * devlink_sb_get_from_attrs ( struct devlink * devlink ,
struct nlattr * * attrs )
{
if ( attrs [ DEVLINK_ATTR_SB_INDEX ] ) {
u32 sb_index = nla_get_u32 ( attrs [ DEVLINK_ATTR_SB_INDEX ] ) ;
struct devlink_sb * devlink_sb ;
devlink_sb = devlink_sb_get_by_index ( devlink , sb_index ) ;
if ( ! devlink_sb )
return ERR_PTR ( - ENODEV ) ;
return devlink_sb ;
}
return ERR_PTR ( - EINVAL ) ;
}
static struct devlink_sb * devlink_sb_get_from_info ( struct devlink * devlink ,
struct genl_info * info )
{
return devlink_sb_get_from_attrs ( devlink , info - > attrs ) ;
}
static int devlink_sb_pool_index_get_from_attrs ( struct devlink_sb * devlink_sb ,
struct nlattr * * attrs ,
u16 * p_pool_index )
{
u16 val ;
if ( ! attrs [ DEVLINK_ATTR_SB_POOL_INDEX ] )
return - EINVAL ;
val = nla_get_u16 ( attrs [ DEVLINK_ATTR_SB_POOL_INDEX ] ) ;
if ( val > = devlink_sb_pool_count ( devlink_sb ) )
return - EINVAL ;
* p_pool_index = val ;
return 0 ;
}
static int devlink_sb_pool_index_get_from_info ( struct devlink_sb * devlink_sb ,
struct genl_info * info ,
u16 * p_pool_index )
{
return devlink_sb_pool_index_get_from_attrs ( devlink_sb , info - > attrs ,
p_pool_index ) ;
}
static int
devlink_sb_pool_type_get_from_attrs ( struct nlattr * * attrs ,
enum devlink_sb_pool_type * p_pool_type )
{
u8 val ;
if ( ! attrs [ DEVLINK_ATTR_SB_POOL_TYPE ] )
return - EINVAL ;
val = nla_get_u8 ( attrs [ DEVLINK_ATTR_SB_POOL_TYPE ] ) ;
if ( val ! = DEVLINK_SB_POOL_TYPE_INGRESS & &
val ! = DEVLINK_SB_POOL_TYPE_EGRESS )
return - EINVAL ;
* p_pool_type = val ;
return 0 ;
}
static int
devlink_sb_pool_type_get_from_info ( struct genl_info * info ,
enum devlink_sb_pool_type * p_pool_type )
{
return devlink_sb_pool_type_get_from_attrs ( info - > attrs , p_pool_type ) ;
}
static int
devlink_sb_th_type_get_from_attrs ( struct nlattr * * attrs ,
enum devlink_sb_threshold_type * p_th_type )
{
u8 val ;
if ( ! attrs [ DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE ] )
return - EINVAL ;
val = nla_get_u8 ( attrs [ DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE ] ) ;
if ( val ! = DEVLINK_SB_THRESHOLD_TYPE_STATIC & &
val ! = DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC )
return - EINVAL ;
* p_th_type = val ;
return 0 ;
}
static int
devlink_sb_th_type_get_from_info ( struct genl_info * info ,
enum devlink_sb_threshold_type * p_th_type )
{
return devlink_sb_th_type_get_from_attrs ( info - > attrs , p_th_type ) ;
}
static int
devlink_sb_tc_index_get_from_attrs ( struct devlink_sb * devlink_sb ,
struct nlattr * * attrs ,
enum devlink_sb_pool_type pool_type ,
u16 * p_tc_index )
{
u16 val ;
if ( ! attrs [ DEVLINK_ATTR_SB_TC_INDEX ] )
return - EINVAL ;
val = nla_get_u16 ( attrs [ DEVLINK_ATTR_SB_TC_INDEX ] ) ;
if ( pool_type = = DEVLINK_SB_POOL_TYPE_INGRESS & &
val > = devlink_sb - > ingress_tc_count )
return - EINVAL ;
if ( pool_type = = DEVLINK_SB_POOL_TYPE_EGRESS & &
val > = devlink_sb - > egress_tc_count )
return - EINVAL ;
* p_tc_index = val ;
return 0 ;
}
static int
devlink_sb_tc_index_get_from_info ( struct devlink_sb * devlink_sb ,
struct genl_info * info ,
enum devlink_sb_pool_type pool_type ,
u16 * p_tc_index )
{
return devlink_sb_tc_index_get_from_attrs ( devlink_sb , info - > attrs ,
pool_type , p_tc_index ) ;
}
2016-04-08 19:12:48 +02:00
# define DEVLINK_NL_FLAG_NEED_DEVLINK BIT(0)
# define DEVLINK_NL_FLAG_NEED_PORT BIT(1)
2016-04-14 18:19:13 +02:00
# define DEVLINK_NL_FLAG_NEED_SB BIT(2)
2016-04-14 18:19:14 +02:00
# define DEVLINK_NL_FLAG_LOCK_PORTS BIT(3)
/* port is not needed but we need to ensure they don't
* change in the middle of command
*/
2016-02-26 17:32:23 +01:00
static int devlink_nl_pre_doit ( const struct genl_ops * ops ,
struct sk_buff * skb , struct genl_info * info )
{
struct devlink * devlink ;
mutex_lock ( & devlink_mutex ) ;
devlink = devlink_get_from_info ( info ) ;
if ( IS_ERR ( devlink ) ) {
mutex_unlock ( & devlink_mutex ) ;
return PTR_ERR ( devlink ) ;
}
2016-04-08 19:12:48 +02:00
if ( ops - > internal_flags & DEVLINK_NL_FLAG_NEED_DEVLINK ) {
info - > user_ptr [ 0 ] = devlink ;
} else if ( ops - > internal_flags & DEVLINK_NL_FLAG_NEED_PORT ) {
2016-02-26 17:32:23 +01:00
struct devlink_port * devlink_port ;
mutex_lock ( & devlink_port_mutex ) ;
devlink_port = devlink_port_get_from_info ( devlink , info ) ;
if ( IS_ERR ( devlink_port ) ) {
mutex_unlock ( & devlink_port_mutex ) ;
mutex_unlock ( & devlink_mutex ) ;
return PTR_ERR ( devlink_port ) ;
}
2016-04-08 19:12:48 +02:00
info - > user_ptr [ 0 ] = devlink_port ;
2016-02-26 17:32:23 +01:00
}
2016-04-14 18:19:14 +02:00
if ( ops - > internal_flags & DEVLINK_NL_FLAG_LOCK_PORTS ) {
mutex_lock ( & devlink_port_mutex ) ;
}
2016-04-14 18:19:13 +02:00
if ( ops - > internal_flags & DEVLINK_NL_FLAG_NEED_SB ) {
struct devlink_sb * devlink_sb ;
devlink_sb = devlink_sb_get_from_info ( devlink , info ) ;
if ( IS_ERR ( devlink_sb ) ) {
if ( ops - > internal_flags & DEVLINK_NL_FLAG_NEED_PORT )
mutex_unlock ( & devlink_port_mutex ) ;
mutex_unlock ( & devlink_mutex ) ;
return PTR_ERR ( devlink_sb ) ;
}
info - > user_ptr [ 1 ] = devlink_sb ;
}
2016-02-26 17:32:23 +01:00
return 0 ;
}
static void devlink_nl_post_doit ( const struct genl_ops * ops ,
struct sk_buff * skb , struct genl_info * info )
{
2016-04-14 18:19:14 +02:00
if ( ops - > internal_flags & DEVLINK_NL_FLAG_NEED_PORT | |
ops - > internal_flags & DEVLINK_NL_FLAG_LOCK_PORTS )
2016-02-26 17:32:23 +01:00
mutex_unlock ( & devlink_port_mutex ) ;
mutex_unlock ( & devlink_mutex ) ;
}
2016-10-24 14:40:03 +02:00
static struct genl_family devlink_nl_family ;
2016-02-26 17:32:23 +01:00
enum devlink_multicast_groups {
DEVLINK_MCGRP_CONFIG ,
} ;
static const struct genl_multicast_group devlink_nl_mcgrps [ ] = {
[ DEVLINK_MCGRP_CONFIG ] = { . name = DEVLINK_GENL_MCGRP_CONFIG_NAME } ,
} ;
static int devlink_nl_put_handle ( struct sk_buff * msg , struct devlink * devlink )
{
if ( nla_put_string ( msg , DEVLINK_ATTR_BUS_NAME , devlink - > dev - > bus - > name ) )
return - EMSGSIZE ;
if ( nla_put_string ( msg , DEVLINK_ATTR_DEV_NAME , dev_name ( devlink - > dev ) ) )
return - EMSGSIZE ;
return 0 ;
}
static int devlink_nl_fill ( struct sk_buff * msg , struct devlink * devlink ,
enum devlink_command cmd , u32 portid ,
u32 seq , int flags )
{
void * hdr ;
hdr = genlmsg_put ( msg , portid , seq , & devlink_nl_family , flags , cmd ) ;
if ( ! hdr )
return - EMSGSIZE ;
if ( devlink_nl_put_handle ( msg , devlink ) )
goto nla_put_failure ;
genlmsg_end ( msg , hdr ) ;
return 0 ;
nla_put_failure :
genlmsg_cancel ( msg , hdr ) ;
return - EMSGSIZE ;
}
static void devlink_notify ( struct devlink * devlink , enum devlink_command cmd )
{
struct sk_buff * msg ;
int err ;
WARN_ON ( cmd ! = DEVLINK_CMD_NEW & & cmd ! = DEVLINK_CMD_DEL ) ;
msg = nlmsg_new ( NLMSG_DEFAULT_SIZE , GFP_KERNEL ) ;
if ( ! msg )
return ;
err = devlink_nl_fill ( msg , devlink , cmd , 0 , 0 , 0 ) ;
if ( err ) {
nlmsg_free ( msg ) ;
return ;
}
genlmsg_multicast_netns ( & devlink_nl_family , devlink_net ( devlink ) ,
msg , 0 , DEVLINK_MCGRP_CONFIG , GFP_KERNEL ) ;
}
static int devlink_nl_port_fill ( struct sk_buff * msg , struct devlink * devlink ,
struct devlink_port * devlink_port ,
enum devlink_command cmd , u32 portid ,
u32 seq , int flags )
{
void * hdr ;
hdr = genlmsg_put ( msg , portid , seq , & devlink_nl_family , flags , cmd ) ;
if ( ! hdr )
return - EMSGSIZE ;
if ( devlink_nl_put_handle ( msg , devlink ) )
goto nla_put_failure ;
if ( nla_put_u32 ( msg , DEVLINK_ATTR_PORT_INDEX , devlink_port - > index ) )
goto nla_put_failure ;
if ( nla_put_u16 ( msg , DEVLINK_ATTR_PORT_TYPE , devlink_port - > type ) )
goto nla_put_failure ;
if ( devlink_port - > desired_type ! = DEVLINK_PORT_TYPE_NOTSET & &
nla_put_u16 ( msg , DEVLINK_ATTR_PORT_DESIRED_TYPE ,
devlink_port - > desired_type ) )
goto nla_put_failure ;
if ( devlink_port - > type = = DEVLINK_PORT_TYPE_ETH ) {
struct net_device * netdev = devlink_port - > type_dev ;
if ( netdev & &
( nla_put_u32 ( msg , DEVLINK_ATTR_PORT_NETDEV_IFINDEX ,
netdev - > ifindex ) | |
nla_put_string ( msg , DEVLINK_ATTR_PORT_NETDEV_NAME ,
netdev - > name ) ) )
goto nla_put_failure ;
}
if ( devlink_port - > type = = DEVLINK_PORT_TYPE_IB ) {
struct ib_device * ibdev = devlink_port - > type_dev ;
if ( ibdev & &
nla_put_string ( msg , DEVLINK_ATTR_PORT_IBDEV_NAME ,
ibdev - > name ) )
goto nla_put_failure ;
}
if ( devlink_port - > split & &
nla_put_u32 ( msg , DEVLINK_ATTR_PORT_SPLIT_GROUP ,
devlink_port - > split_group ) )
goto nla_put_failure ;
genlmsg_end ( msg , hdr ) ;
return 0 ;
nla_put_failure :
genlmsg_cancel ( msg , hdr ) ;
return - EMSGSIZE ;
}
static void devlink_port_notify ( struct devlink_port * devlink_port ,
enum devlink_command cmd )
{
struct devlink * devlink = devlink_port - > devlink ;
struct sk_buff * msg ;
int err ;
if ( ! devlink_port - > registered )
return ;
WARN_ON ( cmd ! = DEVLINK_CMD_PORT_NEW & & cmd ! = DEVLINK_CMD_PORT_DEL ) ;
msg = nlmsg_new ( NLMSG_DEFAULT_SIZE , GFP_KERNEL ) ;
if ( ! msg )
return ;
err = devlink_nl_port_fill ( msg , devlink , devlink_port , cmd , 0 , 0 , 0 ) ;
if ( err ) {
nlmsg_free ( msg ) ;
return ;
}
genlmsg_multicast_netns ( & devlink_nl_family , devlink_net ( devlink ) ,
msg , 0 , DEVLINK_MCGRP_CONFIG , GFP_KERNEL ) ;
}
static int devlink_nl_cmd_get_doit ( struct sk_buff * skb , struct genl_info * info )
{
struct devlink * devlink = info - > user_ptr [ 0 ] ;
struct sk_buff * msg ;
int err ;
msg = nlmsg_new ( NLMSG_DEFAULT_SIZE , GFP_KERNEL ) ;
if ( ! msg )
return - ENOMEM ;
err = devlink_nl_fill ( msg , devlink , DEVLINK_CMD_NEW ,
info - > snd_portid , info - > snd_seq , 0 ) ;
if ( err ) {
nlmsg_free ( msg ) ;
return err ;
}
return genlmsg_reply ( msg , info ) ;
}
static int devlink_nl_cmd_get_dumpit ( struct sk_buff * msg ,
struct netlink_callback * cb )
{
struct devlink * devlink ;
int start = cb - > args [ 0 ] ;
int idx = 0 ;
int err ;
mutex_lock ( & devlink_mutex ) ;
list_for_each_entry ( devlink , & devlink_list , list ) {
if ( ! net_eq ( devlink_net ( devlink ) , sock_net ( msg - > sk ) ) )
continue ;
if ( idx < start ) {
idx + + ;
continue ;
}
err = devlink_nl_fill ( msg , devlink , DEVLINK_CMD_NEW ,
NETLINK_CB ( cb - > skb ) . portid ,
cb - > nlh - > nlmsg_seq , NLM_F_MULTI ) ;
if ( err )
goto out ;
idx + + ;
}
out :
mutex_unlock ( & devlink_mutex ) ;
cb - > args [ 0 ] = idx ;
return msg - > len ;
}
static int devlink_nl_cmd_port_get_doit ( struct sk_buff * skb ,
struct genl_info * info )
{
2016-04-08 19:12:48 +02:00
struct devlink_port * devlink_port = info - > user_ptr [ 0 ] ;
struct devlink * devlink = devlink_port - > devlink ;
2016-02-26 17:32:23 +01:00
struct sk_buff * msg ;
int err ;
msg = nlmsg_new ( NLMSG_DEFAULT_SIZE , GFP_KERNEL ) ;
if ( ! msg )
return - ENOMEM ;
err = devlink_nl_port_fill ( msg , devlink , devlink_port ,
DEVLINK_CMD_PORT_NEW ,
info - > snd_portid , info - > snd_seq , 0 ) ;
if ( err ) {
nlmsg_free ( msg ) ;
return err ;
}
return genlmsg_reply ( msg , info ) ;
}
static int devlink_nl_cmd_port_get_dumpit ( struct sk_buff * msg ,
struct netlink_callback * cb )
{
struct devlink * devlink ;
struct devlink_port * devlink_port ;
int start = cb - > args [ 0 ] ;
int idx = 0 ;
int err ;
mutex_lock ( & devlink_mutex ) ;
mutex_lock ( & devlink_port_mutex ) ;
list_for_each_entry ( devlink , & devlink_list , list ) {
if ( ! net_eq ( devlink_net ( devlink ) , sock_net ( msg - > sk ) ) )
continue ;
list_for_each_entry ( devlink_port , & devlink - > port_list , list ) {
if ( idx < start ) {
idx + + ;
continue ;
}
err = devlink_nl_port_fill ( msg , devlink , devlink_port ,
DEVLINK_CMD_NEW ,
NETLINK_CB ( cb - > skb ) . portid ,
cb - > nlh - > nlmsg_seq ,
NLM_F_MULTI ) ;
if ( err )
goto out ;
idx + + ;
}
}
out :
mutex_unlock ( & devlink_port_mutex ) ;
mutex_unlock ( & devlink_mutex ) ;
cb - > args [ 0 ] = idx ;
return msg - > len ;
}
static int devlink_port_type_set ( struct devlink * devlink ,
struct devlink_port * devlink_port ,
enum devlink_port_type port_type )
{
int err ;
if ( devlink - > ops & & devlink - > ops - > port_type_set ) {
if ( port_type = = DEVLINK_PORT_TYPE_NOTSET )
return - EINVAL ;
2016-10-23 17:43:05 +02:00
if ( port_type = = devlink_port - > type )
return 0 ;
2016-02-26 17:32:23 +01:00
err = devlink - > ops - > port_type_set ( devlink_port , port_type ) ;
if ( err )
return err ;
devlink_port - > desired_type = port_type ;
devlink_port_notify ( devlink_port , DEVLINK_CMD_PORT_NEW ) ;
return 0 ;
}
return - EOPNOTSUPP ;
}
static int devlink_nl_cmd_port_set_doit ( struct sk_buff * skb ,
struct genl_info * info )
{
2016-04-08 19:12:48 +02:00
struct devlink_port * devlink_port = info - > user_ptr [ 0 ] ;
struct devlink * devlink = devlink_port - > devlink ;
2016-02-26 17:32:23 +01:00
int err ;
if ( info - > attrs [ DEVLINK_ATTR_PORT_TYPE ] ) {
enum devlink_port_type port_type ;
port_type = nla_get_u16 ( info - > attrs [ DEVLINK_ATTR_PORT_TYPE ] ) ;
err = devlink_port_type_set ( devlink , devlink_port , port_type ) ;
if ( err )
return err ;
}
return 0 ;
}
static int devlink_port_split ( struct devlink * devlink ,
u32 port_index , u32 count )
{
if ( devlink - > ops & & devlink - > ops - > port_split )
return devlink - > ops - > port_split ( devlink , port_index , count ) ;
return - EOPNOTSUPP ;
}
static int devlink_nl_cmd_port_split_doit ( struct sk_buff * skb ,
struct genl_info * info )
{
struct devlink * devlink = info - > user_ptr [ 0 ] ;
u32 port_index ;
u32 count ;
if ( ! info - > attrs [ DEVLINK_ATTR_PORT_INDEX ] | |
! info - > attrs [ DEVLINK_ATTR_PORT_SPLIT_COUNT ] )
return - EINVAL ;
port_index = nla_get_u32 ( info - > attrs [ DEVLINK_ATTR_PORT_INDEX ] ) ;
count = nla_get_u32 ( info - > attrs [ DEVLINK_ATTR_PORT_SPLIT_COUNT ] ) ;
return devlink_port_split ( devlink , port_index , count ) ;
}
static int devlink_port_unsplit ( struct devlink * devlink , u32 port_index )
{
if ( devlink - > ops & & devlink - > ops - > port_unsplit )
return devlink - > ops - > port_unsplit ( devlink , port_index ) ;
return - EOPNOTSUPP ;
}
static int devlink_nl_cmd_port_unsplit_doit ( struct sk_buff * skb ,
struct genl_info * info )
{
struct devlink * devlink = info - > user_ptr [ 0 ] ;
u32 port_index ;
if ( ! info - > attrs [ DEVLINK_ATTR_PORT_INDEX ] )
return - EINVAL ;
port_index = nla_get_u32 ( info - > attrs [ DEVLINK_ATTR_PORT_INDEX ] ) ;
return devlink_port_unsplit ( devlink , port_index ) ;
}
2016-04-14 18:19:13 +02:00
static int devlink_nl_sb_fill ( struct sk_buff * msg , struct devlink * devlink ,
struct devlink_sb * devlink_sb ,
enum devlink_command cmd , u32 portid ,
u32 seq , int flags )
{
void * hdr ;
hdr = genlmsg_put ( msg , portid , seq , & devlink_nl_family , flags , cmd ) ;
if ( ! hdr )
return - EMSGSIZE ;
if ( devlink_nl_put_handle ( msg , devlink ) )
goto nla_put_failure ;
if ( nla_put_u32 ( msg , DEVLINK_ATTR_SB_INDEX , devlink_sb - > index ) )
goto nla_put_failure ;
if ( nla_put_u32 ( msg , DEVLINK_ATTR_SB_SIZE , devlink_sb - > size ) )
goto nla_put_failure ;
if ( nla_put_u16 ( msg , DEVLINK_ATTR_SB_INGRESS_POOL_COUNT ,
devlink_sb - > ingress_pools_count ) )
goto nla_put_failure ;
if ( nla_put_u16 ( msg , DEVLINK_ATTR_SB_EGRESS_POOL_COUNT ,
devlink_sb - > egress_pools_count ) )
goto nla_put_failure ;
if ( nla_put_u16 ( msg , DEVLINK_ATTR_SB_INGRESS_TC_COUNT ,
devlink_sb - > ingress_tc_count ) )
goto nla_put_failure ;
if ( nla_put_u16 ( msg , DEVLINK_ATTR_SB_EGRESS_TC_COUNT ,
devlink_sb - > egress_tc_count ) )
goto nla_put_failure ;
genlmsg_end ( msg , hdr ) ;
return 0 ;
nla_put_failure :
genlmsg_cancel ( msg , hdr ) ;
return - EMSGSIZE ;
}
static int devlink_nl_cmd_sb_get_doit ( struct sk_buff * skb ,
struct genl_info * info )
{
struct devlink * devlink = info - > user_ptr [ 0 ] ;
struct devlink_sb * devlink_sb = info - > user_ptr [ 1 ] ;
struct sk_buff * msg ;
int err ;
msg = nlmsg_new ( NLMSG_DEFAULT_SIZE , GFP_KERNEL ) ;
if ( ! msg )
return - ENOMEM ;
err = devlink_nl_sb_fill ( msg , devlink , devlink_sb ,
DEVLINK_CMD_SB_NEW ,
info - > snd_portid , info - > snd_seq , 0 ) ;
if ( err ) {
nlmsg_free ( msg ) ;
return err ;
}
return genlmsg_reply ( msg , info ) ;
}
static int devlink_nl_cmd_sb_get_dumpit ( struct sk_buff * msg ,
struct netlink_callback * cb )
{
struct devlink * devlink ;
struct devlink_sb * devlink_sb ;
int start = cb - > args [ 0 ] ;
int idx = 0 ;
int err ;
mutex_lock ( & devlink_mutex ) ;
list_for_each_entry ( devlink , & devlink_list , list ) {
if ( ! net_eq ( devlink_net ( devlink ) , sock_net ( msg - > sk ) ) )
continue ;
list_for_each_entry ( devlink_sb , & devlink - > sb_list , list ) {
if ( idx < start ) {
idx + + ;
continue ;
}
err = devlink_nl_sb_fill ( msg , devlink , devlink_sb ,
DEVLINK_CMD_SB_NEW ,
NETLINK_CB ( cb - > skb ) . portid ,
cb - > nlh - > nlmsg_seq ,
NLM_F_MULTI ) ;
if ( err )
goto out ;
idx + + ;
}
}
out :
mutex_unlock ( & devlink_mutex ) ;
cb - > args [ 0 ] = idx ;
return msg - > len ;
}
static int devlink_nl_sb_pool_fill ( struct sk_buff * msg , struct devlink * devlink ,
struct devlink_sb * devlink_sb ,
u16 pool_index , enum devlink_command cmd ,
u32 portid , u32 seq , int flags )
{
struct devlink_sb_pool_info pool_info ;
void * hdr ;
int err ;
err = devlink - > ops - > sb_pool_get ( devlink , devlink_sb - > index ,
pool_index , & pool_info ) ;
if ( err )
return err ;
hdr = genlmsg_put ( msg , portid , seq , & devlink_nl_family , flags , cmd ) ;
if ( ! hdr )
return - EMSGSIZE ;
if ( devlink_nl_put_handle ( msg , devlink ) )
goto nla_put_failure ;
if ( nla_put_u32 ( msg , DEVLINK_ATTR_SB_INDEX , devlink_sb - > index ) )
goto nla_put_failure ;
if ( nla_put_u16 ( msg , DEVLINK_ATTR_SB_POOL_INDEX , pool_index ) )
goto nla_put_failure ;
if ( nla_put_u8 ( msg , DEVLINK_ATTR_SB_POOL_TYPE , pool_info . pool_type ) )
goto nla_put_failure ;
if ( nla_put_u32 ( msg , DEVLINK_ATTR_SB_POOL_SIZE , pool_info . size ) )
goto nla_put_failure ;
if ( nla_put_u8 ( msg , DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE ,
pool_info . threshold_type ) )
goto nla_put_failure ;
genlmsg_end ( msg , hdr ) ;
return 0 ;
nla_put_failure :
genlmsg_cancel ( msg , hdr ) ;
return - EMSGSIZE ;
}
static int devlink_nl_cmd_sb_pool_get_doit ( struct sk_buff * skb ,
struct genl_info * info )
{
struct devlink * devlink = info - > user_ptr [ 0 ] ;
struct devlink_sb * devlink_sb = info - > user_ptr [ 1 ] ;
struct sk_buff * msg ;
u16 pool_index ;
int err ;
err = devlink_sb_pool_index_get_from_info ( devlink_sb , info ,
& pool_index ) ;
if ( err )
return err ;
if ( ! devlink - > ops | | ! devlink - > ops - > sb_pool_get )
return - EOPNOTSUPP ;
msg = nlmsg_new ( NLMSG_DEFAULT_SIZE , GFP_KERNEL ) ;
if ( ! msg )
return - ENOMEM ;
err = devlink_nl_sb_pool_fill ( msg , devlink , devlink_sb , pool_index ,
DEVLINK_CMD_SB_POOL_NEW ,
info - > snd_portid , info - > snd_seq , 0 ) ;
if ( err ) {
nlmsg_free ( msg ) ;
return err ;
}
return genlmsg_reply ( msg , info ) ;
}
static int __sb_pool_get_dumpit ( struct sk_buff * msg , int start , int * p_idx ,
struct devlink * devlink ,
struct devlink_sb * devlink_sb ,
u32 portid , u32 seq )
{
u16 pool_count = devlink_sb_pool_count ( devlink_sb ) ;
u16 pool_index ;
int err ;
for ( pool_index = 0 ; pool_index < pool_count ; pool_index + + ) {
if ( * p_idx < start ) {
( * p_idx ) + + ;
continue ;
}
err = devlink_nl_sb_pool_fill ( msg , devlink ,
devlink_sb ,
pool_index ,
DEVLINK_CMD_SB_POOL_NEW ,
portid , seq , NLM_F_MULTI ) ;
if ( err )
return err ;
( * p_idx ) + + ;
}
return 0 ;
}
static int devlink_nl_cmd_sb_pool_get_dumpit ( struct sk_buff * msg ,
struct netlink_callback * cb )
{
struct devlink * devlink ;
struct devlink_sb * devlink_sb ;
int start = cb - > args [ 0 ] ;
int idx = 0 ;
int err ;
mutex_lock ( & devlink_mutex ) ;
list_for_each_entry ( devlink , & devlink_list , list ) {
if ( ! net_eq ( devlink_net ( devlink ) , sock_net ( msg - > sk ) ) | |
! devlink - > ops | | ! devlink - > ops - > sb_pool_get )
continue ;
list_for_each_entry ( devlink_sb , & devlink - > sb_list , list ) {
err = __sb_pool_get_dumpit ( msg , start , & idx , devlink ,
devlink_sb ,
NETLINK_CB ( cb - > skb ) . portid ,
cb - > nlh - > nlmsg_seq ) ;
if ( err & & err ! = - EOPNOTSUPP )
goto out ;
}
}
out :
mutex_unlock ( & devlink_mutex ) ;
cb - > args [ 0 ] = idx ;
return msg - > len ;
}
static int devlink_sb_pool_set ( struct devlink * devlink , unsigned int sb_index ,
u16 pool_index , u32 size ,
enum devlink_sb_threshold_type threshold_type )
{
const struct devlink_ops * ops = devlink - > ops ;
if ( ops & & ops - > sb_pool_set )
return ops - > sb_pool_set ( devlink , sb_index , pool_index ,
size , threshold_type ) ;
return - EOPNOTSUPP ;
}
static int devlink_nl_cmd_sb_pool_set_doit ( struct sk_buff * skb ,
struct genl_info * info )
{
struct devlink * devlink = info - > user_ptr [ 0 ] ;
struct devlink_sb * devlink_sb = info - > user_ptr [ 1 ] ;
enum devlink_sb_threshold_type threshold_type ;
u16 pool_index ;
u32 size ;
int err ;
err = devlink_sb_pool_index_get_from_info ( devlink_sb , info ,
& pool_index ) ;
if ( err )
return err ;
err = devlink_sb_th_type_get_from_info ( info , & threshold_type ) ;
if ( err )
return err ;
if ( ! info - > attrs [ DEVLINK_ATTR_SB_POOL_SIZE ] )
return - EINVAL ;
size = nla_get_u32 ( info - > attrs [ DEVLINK_ATTR_SB_POOL_SIZE ] ) ;
return devlink_sb_pool_set ( devlink , devlink_sb - > index ,
pool_index , size , threshold_type ) ;
}
static int devlink_nl_sb_port_pool_fill ( struct sk_buff * msg ,
struct devlink * devlink ,
struct devlink_port * devlink_port ,
struct devlink_sb * devlink_sb ,
u16 pool_index ,
enum devlink_command cmd ,
u32 portid , u32 seq , int flags )
{
2016-04-14 18:19:14 +02:00
const struct devlink_ops * ops = devlink - > ops ;
2016-04-14 18:19:13 +02:00
u32 threshold ;
void * hdr ;
int err ;
2016-04-14 18:19:14 +02:00
err = ops - > sb_port_pool_get ( devlink_port , devlink_sb - > index ,
pool_index , & threshold ) ;
2016-04-14 18:19:13 +02:00
if ( err )
return err ;
hdr = genlmsg_put ( msg , portid , seq , & devlink_nl_family , flags , cmd ) ;
if ( ! hdr )
return - EMSGSIZE ;
if ( devlink_nl_put_handle ( msg , devlink ) )
goto nla_put_failure ;
if ( nla_put_u32 ( msg , DEVLINK_ATTR_PORT_INDEX , devlink_port - > index ) )
goto nla_put_failure ;
if ( nla_put_u32 ( msg , DEVLINK_ATTR_SB_INDEX , devlink_sb - > index ) )
goto nla_put_failure ;
if ( nla_put_u16 ( msg , DEVLINK_ATTR_SB_POOL_INDEX , pool_index ) )
goto nla_put_failure ;
if ( nla_put_u32 ( msg , DEVLINK_ATTR_SB_THRESHOLD , threshold ) )
goto nla_put_failure ;
2016-04-14 18:19:14 +02:00
if ( ops - > sb_occ_port_pool_get ) {
u32 cur ;
u32 max ;
err = ops - > sb_occ_port_pool_get ( devlink_port , devlink_sb - > index ,
pool_index , & cur , & max ) ;
if ( err & & err ! = - EOPNOTSUPP )
return err ;
if ( ! err ) {
if ( nla_put_u32 ( msg , DEVLINK_ATTR_SB_OCC_CUR , cur ) )
goto nla_put_failure ;
if ( nla_put_u32 ( msg , DEVLINK_ATTR_SB_OCC_MAX , max ) )
goto nla_put_failure ;
}
}
2016-04-14 18:19:13 +02:00
genlmsg_end ( msg , hdr ) ;
return 0 ;
nla_put_failure :
genlmsg_cancel ( msg , hdr ) ;
return - EMSGSIZE ;
}
static int devlink_nl_cmd_sb_port_pool_get_doit ( struct sk_buff * skb ,
struct genl_info * info )
{
struct devlink_port * devlink_port = info - > user_ptr [ 0 ] ;
struct devlink * devlink = devlink_port - > devlink ;
struct devlink_sb * devlink_sb = info - > user_ptr [ 1 ] ;
struct sk_buff * msg ;
u16 pool_index ;
int err ;
err = devlink_sb_pool_index_get_from_info ( devlink_sb , info ,
& pool_index ) ;
if ( err )
return err ;
if ( ! devlink - > ops | | ! devlink - > ops - > sb_port_pool_get )
return - EOPNOTSUPP ;
msg = nlmsg_new ( NLMSG_DEFAULT_SIZE , GFP_KERNEL ) ;
if ( ! msg )
return - ENOMEM ;
err = devlink_nl_sb_port_pool_fill ( msg , devlink , devlink_port ,
devlink_sb , pool_index ,
DEVLINK_CMD_SB_PORT_POOL_NEW ,
info - > snd_portid , info - > snd_seq , 0 ) ;
if ( err ) {
nlmsg_free ( msg ) ;
return err ;
}
return genlmsg_reply ( msg , info ) ;
}
static int __sb_port_pool_get_dumpit ( struct sk_buff * msg , int start , int * p_idx ,
struct devlink * devlink ,
struct devlink_sb * devlink_sb ,
u32 portid , u32 seq )
{
struct devlink_port * devlink_port ;
u16 pool_count = devlink_sb_pool_count ( devlink_sb ) ;
u16 pool_index ;
int err ;
list_for_each_entry ( devlink_port , & devlink - > port_list , list ) {
for ( pool_index = 0 ; pool_index < pool_count ; pool_index + + ) {
if ( * p_idx < start ) {
( * p_idx ) + + ;
continue ;
}
err = devlink_nl_sb_port_pool_fill ( msg , devlink ,
devlink_port ,
devlink_sb ,
pool_index ,
DEVLINK_CMD_SB_PORT_POOL_NEW ,
portid , seq ,
NLM_F_MULTI ) ;
if ( err )
return err ;
( * p_idx ) + + ;
}
}
return 0 ;
}
static int devlink_nl_cmd_sb_port_pool_get_dumpit ( struct sk_buff * msg ,
struct netlink_callback * cb )
{
struct devlink * devlink ;
struct devlink_sb * devlink_sb ;
int start = cb - > args [ 0 ] ;
int idx = 0 ;
int err ;
mutex_lock ( & devlink_mutex ) ;
mutex_lock ( & devlink_port_mutex ) ;
list_for_each_entry ( devlink , & devlink_list , list ) {
if ( ! net_eq ( devlink_net ( devlink ) , sock_net ( msg - > sk ) ) | |
! devlink - > ops | | ! devlink - > ops - > sb_port_pool_get )
continue ;
list_for_each_entry ( devlink_sb , & devlink - > sb_list , list ) {
err = __sb_port_pool_get_dumpit ( msg , start , & idx ,
devlink , devlink_sb ,
NETLINK_CB ( cb - > skb ) . portid ,
cb - > nlh - > nlmsg_seq ) ;
if ( err & & err ! = - EOPNOTSUPP )
goto out ;
}
}
out :
mutex_unlock ( & devlink_port_mutex ) ;
mutex_unlock ( & devlink_mutex ) ;
cb - > args [ 0 ] = idx ;
return msg - > len ;
}
static int devlink_sb_port_pool_set ( struct devlink_port * devlink_port ,
unsigned int sb_index , u16 pool_index ,
u32 threshold )
{
const struct devlink_ops * ops = devlink_port - > devlink - > ops ;
if ( ops & & ops - > sb_port_pool_set )
return ops - > sb_port_pool_set ( devlink_port , sb_index ,
pool_index , threshold ) ;
return - EOPNOTSUPP ;
}
static int devlink_nl_cmd_sb_port_pool_set_doit ( struct sk_buff * skb ,
struct genl_info * info )
{
struct devlink_port * devlink_port = info - > user_ptr [ 0 ] ;
struct devlink_sb * devlink_sb = info - > user_ptr [ 1 ] ;
u16 pool_index ;
u32 threshold ;
int err ;
err = devlink_sb_pool_index_get_from_info ( devlink_sb , info ,
& pool_index ) ;
if ( err )
return err ;
if ( ! info - > attrs [ DEVLINK_ATTR_SB_THRESHOLD ] )
return - EINVAL ;
threshold = nla_get_u32 ( info - > attrs [ DEVLINK_ATTR_SB_THRESHOLD ] ) ;
return devlink_sb_port_pool_set ( devlink_port , devlink_sb - > index ,
pool_index , threshold ) ;
}
static int
devlink_nl_sb_tc_pool_bind_fill ( struct sk_buff * msg , struct devlink * devlink ,
struct devlink_port * devlink_port ,
struct devlink_sb * devlink_sb , u16 tc_index ,
enum devlink_sb_pool_type pool_type ,
enum devlink_command cmd ,
u32 portid , u32 seq , int flags )
{
2016-04-14 18:19:14 +02:00
const struct devlink_ops * ops = devlink - > ops ;
2016-04-14 18:19:13 +02:00
u16 pool_index ;
u32 threshold ;
void * hdr ;
int err ;
2016-04-14 18:19:14 +02:00
err = ops - > sb_tc_pool_bind_get ( devlink_port , devlink_sb - > index ,
tc_index , pool_type ,
& pool_index , & threshold ) ;
2016-04-14 18:19:13 +02:00
if ( err )
return err ;
hdr = genlmsg_put ( msg , portid , seq , & devlink_nl_family , flags , cmd ) ;
if ( ! hdr )
return - EMSGSIZE ;
if ( devlink_nl_put_handle ( msg , devlink ) )
goto nla_put_failure ;
if ( nla_put_u32 ( msg , DEVLINK_ATTR_PORT_INDEX , devlink_port - > index ) )
goto nla_put_failure ;
if ( nla_put_u32 ( msg , DEVLINK_ATTR_SB_INDEX , devlink_sb - > index ) )
goto nla_put_failure ;
if ( nla_put_u16 ( msg , DEVLINK_ATTR_SB_TC_INDEX , tc_index ) )
goto nla_put_failure ;
if ( nla_put_u8 ( msg , DEVLINK_ATTR_SB_POOL_TYPE , pool_type ) )
goto nla_put_failure ;
if ( nla_put_u16 ( msg , DEVLINK_ATTR_SB_POOL_INDEX , pool_index ) )
goto nla_put_failure ;
if ( nla_put_u32 ( msg , DEVLINK_ATTR_SB_THRESHOLD , threshold ) )
goto nla_put_failure ;
2016-04-14 18:19:14 +02:00
if ( ops - > sb_occ_tc_port_bind_get ) {
u32 cur ;
u32 max ;
err = ops - > sb_occ_tc_port_bind_get ( devlink_port ,
devlink_sb - > index ,
tc_index , pool_type ,
& cur , & max ) ;
if ( err & & err ! = - EOPNOTSUPP )
return err ;
if ( ! err ) {
if ( nla_put_u32 ( msg , DEVLINK_ATTR_SB_OCC_CUR , cur ) )
goto nla_put_failure ;
if ( nla_put_u32 ( msg , DEVLINK_ATTR_SB_OCC_MAX , max ) )
goto nla_put_failure ;
}
}
2016-04-14 18:19:13 +02:00
genlmsg_end ( msg , hdr ) ;
return 0 ;
nla_put_failure :
genlmsg_cancel ( msg , hdr ) ;
return - EMSGSIZE ;
}
static int devlink_nl_cmd_sb_tc_pool_bind_get_doit ( struct sk_buff * skb ,
struct genl_info * info )
{
struct devlink_port * devlink_port = info - > user_ptr [ 0 ] ;
struct devlink * devlink = devlink_port - > devlink ;
struct devlink_sb * devlink_sb = info - > user_ptr [ 1 ] ;
struct sk_buff * msg ;
enum devlink_sb_pool_type pool_type ;
u16 tc_index ;
int err ;
err = devlink_sb_pool_type_get_from_info ( info , & pool_type ) ;
if ( err )
return err ;
err = devlink_sb_tc_index_get_from_info ( devlink_sb , info ,
pool_type , & tc_index ) ;
if ( err )
return err ;
if ( ! devlink - > ops | | ! devlink - > ops - > sb_tc_pool_bind_get )
return - EOPNOTSUPP ;
msg = nlmsg_new ( NLMSG_DEFAULT_SIZE , GFP_KERNEL ) ;
if ( ! msg )
return - ENOMEM ;
err = devlink_nl_sb_tc_pool_bind_fill ( msg , devlink , devlink_port ,
devlink_sb , tc_index , pool_type ,
DEVLINK_CMD_SB_TC_POOL_BIND_NEW ,
info - > snd_portid ,
info - > snd_seq , 0 ) ;
if ( err ) {
nlmsg_free ( msg ) ;
return err ;
}
return genlmsg_reply ( msg , info ) ;
}
static int __sb_tc_pool_bind_get_dumpit ( struct sk_buff * msg ,
int start , int * p_idx ,
struct devlink * devlink ,
struct devlink_sb * devlink_sb ,
u32 portid , u32 seq )
{
struct devlink_port * devlink_port ;
u16 tc_index ;
int err ;
list_for_each_entry ( devlink_port , & devlink - > port_list , list ) {
for ( tc_index = 0 ;
tc_index < devlink_sb - > ingress_tc_count ; tc_index + + ) {
if ( * p_idx < start ) {
( * p_idx ) + + ;
continue ;
}
err = devlink_nl_sb_tc_pool_bind_fill ( msg , devlink ,
devlink_port ,
devlink_sb ,
tc_index ,
DEVLINK_SB_POOL_TYPE_INGRESS ,
DEVLINK_CMD_SB_TC_POOL_BIND_NEW ,
portid , seq ,
NLM_F_MULTI ) ;
if ( err )
return err ;
( * p_idx ) + + ;
}
for ( tc_index = 0 ;
tc_index < devlink_sb - > egress_tc_count ; tc_index + + ) {
if ( * p_idx < start ) {
( * p_idx ) + + ;
continue ;
}
err = devlink_nl_sb_tc_pool_bind_fill ( msg , devlink ,
devlink_port ,
devlink_sb ,
tc_index ,
DEVLINK_SB_POOL_TYPE_EGRESS ,
DEVLINK_CMD_SB_TC_POOL_BIND_NEW ,
portid , seq ,
NLM_F_MULTI ) ;
if ( err )
return err ;
( * p_idx ) + + ;
}
}
return 0 ;
}
static int
devlink_nl_cmd_sb_tc_pool_bind_get_dumpit ( struct sk_buff * msg ,
struct netlink_callback * cb )
{
struct devlink * devlink ;
struct devlink_sb * devlink_sb ;
int start = cb - > args [ 0 ] ;
int idx = 0 ;
int err ;
mutex_lock ( & devlink_mutex ) ;
mutex_lock ( & devlink_port_mutex ) ;
list_for_each_entry ( devlink , & devlink_list , list ) {
if ( ! net_eq ( devlink_net ( devlink ) , sock_net ( msg - > sk ) ) | |
! devlink - > ops | | ! devlink - > ops - > sb_tc_pool_bind_get )
continue ;
list_for_each_entry ( devlink_sb , & devlink - > sb_list , list ) {
err = __sb_tc_pool_bind_get_dumpit ( msg , start , & idx ,
devlink ,
devlink_sb ,
NETLINK_CB ( cb - > skb ) . portid ,
cb - > nlh - > nlmsg_seq ) ;
if ( err & & err ! = - EOPNOTSUPP )
goto out ;
}
}
out :
mutex_unlock ( & devlink_port_mutex ) ;
mutex_unlock ( & devlink_mutex ) ;
cb - > args [ 0 ] = idx ;
return msg - > len ;
}
static int devlink_sb_tc_pool_bind_set ( struct devlink_port * devlink_port ,
unsigned int sb_index , u16 tc_index ,
enum devlink_sb_pool_type pool_type ,
u16 pool_index , u32 threshold )
{
const struct devlink_ops * ops = devlink_port - > devlink - > ops ;
if ( ops & & ops - > sb_tc_pool_bind_set )
return ops - > sb_tc_pool_bind_set ( devlink_port , sb_index ,
tc_index , pool_type ,
pool_index , threshold ) ;
return - EOPNOTSUPP ;
}
static int devlink_nl_cmd_sb_tc_pool_bind_set_doit ( struct sk_buff * skb ,
struct genl_info * info )
{
struct devlink_port * devlink_port = info - > user_ptr [ 0 ] ;
struct devlink_sb * devlink_sb = info - > user_ptr [ 1 ] ;
enum devlink_sb_pool_type pool_type ;
u16 tc_index ;
u16 pool_index ;
u32 threshold ;
int err ;
err = devlink_sb_pool_type_get_from_info ( info , & pool_type ) ;
if ( err )
return err ;
err = devlink_sb_tc_index_get_from_info ( devlink_sb , info ,
pool_type , & tc_index ) ;
if ( err )
return err ;
err = devlink_sb_pool_index_get_from_info ( devlink_sb , info ,
& pool_index ) ;
if ( err )
return err ;
if ( ! info - > attrs [ DEVLINK_ATTR_SB_THRESHOLD ] )
return - EINVAL ;
threshold = nla_get_u32 ( info - > attrs [ DEVLINK_ATTR_SB_THRESHOLD ] ) ;
return devlink_sb_tc_pool_bind_set ( devlink_port , devlink_sb - > index ,
tc_index , pool_type ,
pool_index , threshold ) ;
}
2016-04-14 18:19:14 +02:00
static int devlink_nl_cmd_sb_occ_snapshot_doit ( struct sk_buff * skb ,
struct genl_info * info )
{
struct devlink * devlink = info - > user_ptr [ 0 ] ;
struct devlink_sb * devlink_sb = info - > user_ptr [ 1 ] ;
const struct devlink_ops * ops = devlink - > ops ;
if ( ops & & ops - > sb_occ_snapshot )
return ops - > sb_occ_snapshot ( devlink , devlink_sb - > index ) ;
return - EOPNOTSUPP ;
}
static int devlink_nl_cmd_sb_occ_max_clear_doit ( struct sk_buff * skb ,
struct genl_info * info )
{
struct devlink * devlink = info - > user_ptr [ 0 ] ;
struct devlink_sb * devlink_sb = info - > user_ptr [ 1 ] ;
const struct devlink_ops * ops = devlink - > ops ;
if ( ops & & ops - > sb_occ_max_clear )
return ops - > sb_occ_max_clear ( devlink , devlink_sb - > index ) ;
return - EOPNOTSUPP ;
}
2016-07-01 14:51:01 +03:00
static int devlink_eswitch_fill ( struct sk_buff * msg , struct devlink * devlink ,
enum devlink_command cmd , u32 portid ,
u32 seq , int flags , u16 mode )
{
void * hdr ;
hdr = genlmsg_put ( msg , portid , seq , & devlink_nl_family , flags , cmd ) ;
if ( ! hdr )
return - EMSGSIZE ;
if ( devlink_nl_put_handle ( msg , devlink ) )
goto nla_put_failure ;
if ( nla_put_u16 ( msg , DEVLINK_ATTR_ESWITCH_MODE , mode ) )
goto nla_put_failure ;
genlmsg_end ( msg , hdr ) ;
return 0 ;
nla_put_failure :
genlmsg_cancel ( msg , hdr ) ;
return - EMSGSIZE ;
}
static int devlink_nl_cmd_eswitch_mode_get_doit ( struct sk_buff * skb ,
struct genl_info * info )
{
struct devlink * devlink = info - > user_ptr [ 0 ] ;
const struct devlink_ops * ops = devlink - > ops ;
struct sk_buff * msg ;
u16 mode ;
int err ;
if ( ! ops | | ! ops - > eswitch_mode_get )
return - EOPNOTSUPP ;
err = ops - > eswitch_mode_get ( devlink , & mode ) ;
if ( err )
return err ;
msg = nlmsg_new ( NLMSG_DEFAULT_SIZE , GFP_KERNEL ) ;
if ( ! msg )
return - ENOMEM ;
err = devlink_eswitch_fill ( msg , devlink , DEVLINK_CMD_ESWITCH_MODE_GET ,
info - > snd_portid , info - > snd_seq , 0 , mode ) ;
if ( err ) {
nlmsg_free ( msg ) ;
return err ;
}
return genlmsg_reply ( msg , info ) ;
}
static int devlink_nl_cmd_eswitch_mode_set_doit ( struct sk_buff * skb ,
struct genl_info * info )
{
struct devlink * devlink = info - > user_ptr [ 0 ] ;
const struct devlink_ops * ops = devlink - > ops ;
u16 mode ;
if ( ! info - > attrs [ DEVLINK_ATTR_ESWITCH_MODE ] )
return - EINVAL ;
mode = nla_get_u16 ( info - > attrs [ DEVLINK_ATTR_ESWITCH_MODE ] ) ;
if ( ops & & ops - > eswitch_mode_set )
return ops - > eswitch_mode_set ( devlink , mode ) ;
return - EOPNOTSUPP ;
}
2016-02-26 17:32:23 +01:00
static const struct nla_policy devlink_nl_policy [ DEVLINK_ATTR_MAX + 1 ] = {
[ DEVLINK_ATTR_BUS_NAME ] = { . type = NLA_NUL_STRING } ,
[ DEVLINK_ATTR_DEV_NAME ] = { . type = NLA_NUL_STRING } ,
[ DEVLINK_ATTR_PORT_INDEX ] = { . type = NLA_U32 } ,
[ DEVLINK_ATTR_PORT_TYPE ] = { . type = NLA_U16 } ,
[ DEVLINK_ATTR_PORT_SPLIT_COUNT ] = { . type = NLA_U32 } ,
2016-04-14 18:19:13 +02:00
[ DEVLINK_ATTR_SB_INDEX ] = { . type = NLA_U32 } ,
[ DEVLINK_ATTR_SB_POOL_INDEX ] = { . type = NLA_U16 } ,
[ DEVLINK_ATTR_SB_POOL_TYPE ] = { . type = NLA_U8 } ,
[ DEVLINK_ATTR_SB_POOL_SIZE ] = { . type = NLA_U32 } ,
[ DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE ] = { . type = NLA_U8 } ,
[ DEVLINK_ATTR_SB_THRESHOLD ] = { . type = NLA_U32 } ,
[ DEVLINK_ATTR_SB_TC_INDEX ] = { . type = NLA_U16 } ,
2016-07-01 14:51:01 +03:00
[ DEVLINK_ATTR_ESWITCH_MODE ] = { . type = NLA_U16 } ,
2016-02-26 17:32:23 +01:00
} ;
static const struct genl_ops devlink_nl_ops [ ] = {
{
. cmd = DEVLINK_CMD_GET ,
. doit = devlink_nl_cmd_get_doit ,
. dumpit = devlink_nl_cmd_get_dumpit ,
. policy = devlink_nl_policy ,
2016-04-08 19:12:48 +02:00
. internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK ,
2016-02-26 17:32:23 +01:00
/* can be retrieved by unprivileged users */
} ,
{
. cmd = DEVLINK_CMD_PORT_GET ,
. doit = devlink_nl_cmd_port_get_doit ,
. dumpit = devlink_nl_cmd_port_get_dumpit ,
. policy = devlink_nl_policy ,
. internal_flags = DEVLINK_NL_FLAG_NEED_PORT ,
/* can be retrieved by unprivileged users */
} ,
{
. cmd = DEVLINK_CMD_PORT_SET ,
. doit = devlink_nl_cmd_port_set_doit ,
. policy = devlink_nl_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = DEVLINK_NL_FLAG_NEED_PORT ,
} ,
{
. cmd = DEVLINK_CMD_PORT_SPLIT ,
. doit = devlink_nl_cmd_port_split_doit ,
. policy = devlink_nl_policy ,
. flags = GENL_ADMIN_PERM ,
2016-04-08 19:12:48 +02:00
. internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK ,
2016-02-26 17:32:23 +01:00
} ,
{
. cmd = DEVLINK_CMD_PORT_UNSPLIT ,
. doit = devlink_nl_cmd_port_unsplit_doit ,
. policy = devlink_nl_policy ,
. flags = GENL_ADMIN_PERM ,
2016-04-08 19:12:48 +02:00
. internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK ,
2016-02-26 17:32:23 +01:00
} ,
2016-04-14 18:19:13 +02:00
{
. cmd = DEVLINK_CMD_SB_GET ,
. doit = devlink_nl_cmd_sb_get_doit ,
. dumpit = devlink_nl_cmd_sb_get_dumpit ,
. policy = devlink_nl_policy ,
. internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
DEVLINK_NL_FLAG_NEED_SB ,
/* can be retrieved by unprivileged users */
} ,
{
. cmd = DEVLINK_CMD_SB_POOL_GET ,
. doit = devlink_nl_cmd_sb_pool_get_doit ,
. dumpit = devlink_nl_cmd_sb_pool_get_dumpit ,
. policy = devlink_nl_policy ,
. internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
DEVLINK_NL_FLAG_NEED_SB ,
/* can be retrieved by unprivileged users */
} ,
{
. cmd = DEVLINK_CMD_SB_POOL_SET ,
. doit = devlink_nl_cmd_sb_pool_set_doit ,
. policy = devlink_nl_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
DEVLINK_NL_FLAG_NEED_SB ,
} ,
{
. cmd = DEVLINK_CMD_SB_PORT_POOL_GET ,
. doit = devlink_nl_cmd_sb_port_pool_get_doit ,
. dumpit = devlink_nl_cmd_sb_port_pool_get_dumpit ,
. policy = devlink_nl_policy ,
. internal_flags = DEVLINK_NL_FLAG_NEED_PORT |
DEVLINK_NL_FLAG_NEED_SB ,
/* can be retrieved by unprivileged users */
} ,
{
. cmd = DEVLINK_CMD_SB_PORT_POOL_SET ,
. doit = devlink_nl_cmd_sb_port_pool_set_doit ,
. policy = devlink_nl_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = DEVLINK_NL_FLAG_NEED_PORT |
DEVLINK_NL_FLAG_NEED_SB ,
} ,
{
. cmd = DEVLINK_CMD_SB_TC_POOL_BIND_GET ,
. doit = devlink_nl_cmd_sb_tc_pool_bind_get_doit ,
. dumpit = devlink_nl_cmd_sb_tc_pool_bind_get_dumpit ,
. policy = devlink_nl_policy ,
. internal_flags = DEVLINK_NL_FLAG_NEED_PORT |
DEVLINK_NL_FLAG_NEED_SB ,
/* can be retrieved by unprivileged users */
} ,
{
. cmd = DEVLINK_CMD_SB_TC_POOL_BIND_SET ,
. doit = devlink_nl_cmd_sb_tc_pool_bind_set_doit ,
. policy = devlink_nl_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = DEVLINK_NL_FLAG_NEED_PORT |
DEVLINK_NL_FLAG_NEED_SB ,
} ,
2016-04-14 18:19:14 +02:00
{
. cmd = DEVLINK_CMD_SB_OCC_SNAPSHOT ,
. doit = devlink_nl_cmd_sb_occ_snapshot_doit ,
. policy = devlink_nl_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
DEVLINK_NL_FLAG_NEED_SB |
DEVLINK_NL_FLAG_LOCK_PORTS ,
} ,
{
. cmd = DEVLINK_CMD_SB_OCC_MAX_CLEAR ,
. doit = devlink_nl_cmd_sb_occ_max_clear_doit ,
. policy = devlink_nl_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
DEVLINK_NL_FLAG_NEED_SB |
DEVLINK_NL_FLAG_LOCK_PORTS ,
} ,
2016-07-01 14:51:01 +03:00
{
. cmd = DEVLINK_CMD_ESWITCH_MODE_GET ,
. doit = devlink_nl_cmd_eswitch_mode_get_doit ,
. policy = devlink_nl_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK ,
} ,
{
. cmd = DEVLINK_CMD_ESWITCH_MODE_SET ,
. doit = devlink_nl_cmd_eswitch_mode_set_doit ,
. policy = devlink_nl_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK ,
} ,
2016-02-26 17:32:23 +01:00
} ;
2016-10-24 14:40:03 +02:00
static struct genl_family devlink_nl_family = {
. name = DEVLINK_GENL_NAME ,
. version = DEVLINK_GENL_VERSION ,
. maxattr = DEVLINK_ATTR_MAX ,
. netnsok = true ,
. pre_doit = devlink_nl_pre_doit ,
. post_doit = devlink_nl_post_doit ,
. module = THIS_MODULE ,
. ops = devlink_nl_ops ,
. n_ops = ARRAY_SIZE ( devlink_nl_ops ) ,
. mcgrps = devlink_nl_mcgrps ,
. n_mcgrps = ARRAY_SIZE ( devlink_nl_mcgrps ) ,
} ;
2016-02-26 17:32:23 +01:00
/**
* devlink_alloc - Allocate new devlink instance resources
*
* @ ops : ops
* @ priv_size : size of user private data
*
* Allocate new devlink instance resources , including devlink index
* and name .
*/
struct devlink * devlink_alloc ( const struct devlink_ops * ops , size_t priv_size )
{
struct devlink * devlink ;
devlink = kzalloc ( sizeof ( * devlink ) + priv_size , GFP_KERNEL ) ;
if ( ! devlink )
return NULL ;
devlink - > ops = ops ;
devlink_net_set ( devlink , & init_net ) ;
INIT_LIST_HEAD ( & devlink - > port_list ) ;
2016-04-14 18:19:13 +02:00
INIT_LIST_HEAD ( & devlink - > sb_list ) ;
2016-02-26 17:32:23 +01:00
return devlink ;
}
EXPORT_SYMBOL_GPL ( devlink_alloc ) ;
/**
* devlink_register - Register devlink instance
*
* @ devlink : devlink
*/
int devlink_register ( struct devlink * devlink , struct device * dev )
{
mutex_lock ( & devlink_mutex ) ;
devlink - > dev = dev ;
list_add_tail ( & devlink - > list , & devlink_list ) ;
devlink_notify ( devlink , DEVLINK_CMD_NEW ) ;
mutex_unlock ( & devlink_mutex ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( devlink_register ) ;
/**
* devlink_unregister - Unregister devlink instance
*
* @ devlink : devlink
*/
void devlink_unregister ( struct devlink * devlink )
{
mutex_lock ( & devlink_mutex ) ;
devlink_notify ( devlink , DEVLINK_CMD_DEL ) ;
list_del ( & devlink - > list ) ;
mutex_unlock ( & devlink_mutex ) ;
}
EXPORT_SYMBOL_GPL ( devlink_unregister ) ;
/**
* devlink_free - Free devlink instance resources
*
* @ devlink : devlink
*/
void devlink_free ( struct devlink * devlink )
{
kfree ( devlink ) ;
}
EXPORT_SYMBOL_GPL ( devlink_free ) ;
/**
* devlink_port_register - Register devlink port
*
* @ devlink : devlink
* @ devlink_port : devlink port
* @ port_index
*
* Register devlink port with provided port index . User can use
* any indexing , even hw - related one . devlink_port structure
* is convenient to be embedded inside user driver private structure .
* Note that the caller should take care of zeroing the devlink_port
* structure .
*/
int devlink_port_register ( struct devlink * devlink ,
struct devlink_port * devlink_port ,
unsigned int port_index )
{
mutex_lock ( & devlink_port_mutex ) ;
if ( devlink_port_index_exists ( devlink , port_index ) ) {
mutex_unlock ( & devlink_port_mutex ) ;
return - EEXIST ;
}
devlink_port - > devlink = devlink ;
devlink_port - > index = port_index ;
devlink_port - > registered = true ;
list_add_tail ( & devlink_port - > list , & devlink - > port_list ) ;
mutex_unlock ( & devlink_port_mutex ) ;
devlink_port_notify ( devlink_port , DEVLINK_CMD_PORT_NEW ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( devlink_port_register ) ;
/**
* devlink_port_unregister - Unregister devlink port
*
* @ devlink_port : devlink port
*/
void devlink_port_unregister ( struct devlink_port * devlink_port )
{
devlink_port_notify ( devlink_port , DEVLINK_CMD_PORT_DEL ) ;
mutex_lock ( & devlink_port_mutex ) ;
list_del ( & devlink_port - > list ) ;
mutex_unlock ( & devlink_port_mutex ) ;
}
EXPORT_SYMBOL_GPL ( devlink_port_unregister ) ;
static void __devlink_port_type_set ( struct devlink_port * devlink_port ,
enum devlink_port_type type ,
void * type_dev )
{
devlink_port - > type = type ;
devlink_port - > type_dev = type_dev ;
devlink_port_notify ( devlink_port , DEVLINK_CMD_PORT_NEW ) ;
}
/**
* devlink_port_type_eth_set - Set port type to Ethernet
*
* @ devlink_port : devlink port
* @ netdev : related netdevice
*/
void devlink_port_type_eth_set ( struct devlink_port * devlink_port ,
struct net_device * netdev )
{
return __devlink_port_type_set ( devlink_port ,
DEVLINK_PORT_TYPE_ETH , netdev ) ;
}
EXPORT_SYMBOL_GPL ( devlink_port_type_eth_set ) ;
/**
* devlink_port_type_ib_set - Set port type to InfiniBand
*
* @ devlink_port : devlink port
* @ ibdev : related IB device
*/
void devlink_port_type_ib_set ( struct devlink_port * devlink_port ,
struct ib_device * ibdev )
{
return __devlink_port_type_set ( devlink_port ,
DEVLINK_PORT_TYPE_IB , ibdev ) ;
}
EXPORT_SYMBOL_GPL ( devlink_port_type_ib_set ) ;
/**
* devlink_port_type_clear - Clear port type
*
* @ devlink_port : devlink port
*/
void devlink_port_type_clear ( struct devlink_port * devlink_port )
{
return __devlink_port_type_set ( devlink_port ,
DEVLINK_PORT_TYPE_NOTSET , NULL ) ;
}
EXPORT_SYMBOL_GPL ( devlink_port_type_clear ) ;
/**
* devlink_port_split_set - Set port is split
*
* @ devlink_port : devlink port
* @ split_group : split group - identifies group split port is part of
*/
void devlink_port_split_set ( struct devlink_port * devlink_port ,
u32 split_group )
{
devlink_port - > split = true ;
devlink_port - > split_group = split_group ;
devlink_port_notify ( devlink_port , DEVLINK_CMD_PORT_NEW ) ;
}
EXPORT_SYMBOL_GPL ( devlink_port_split_set ) ;
2016-04-14 18:19:13 +02:00
int devlink_sb_register ( struct devlink * devlink , unsigned int sb_index ,
u32 size , u16 ingress_pools_count ,
u16 egress_pools_count , u16 ingress_tc_count ,
u16 egress_tc_count )
{
struct devlink_sb * devlink_sb ;
int err = 0 ;
mutex_lock ( & devlink_mutex ) ;
if ( devlink_sb_index_exists ( devlink , sb_index ) ) {
err = - EEXIST ;
goto unlock ;
}
devlink_sb = kzalloc ( sizeof ( * devlink_sb ) , GFP_KERNEL ) ;
if ( ! devlink_sb ) {
err = - ENOMEM ;
goto unlock ;
}
devlink_sb - > index = sb_index ;
devlink_sb - > size = size ;
devlink_sb - > ingress_pools_count = ingress_pools_count ;
devlink_sb - > egress_pools_count = egress_pools_count ;
devlink_sb - > ingress_tc_count = ingress_tc_count ;
devlink_sb - > egress_tc_count = egress_tc_count ;
list_add_tail ( & devlink_sb - > list , & devlink - > sb_list ) ;
unlock :
mutex_unlock ( & devlink_mutex ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( devlink_sb_register ) ;
void devlink_sb_unregister ( struct devlink * devlink , unsigned int sb_index )
{
struct devlink_sb * devlink_sb ;
mutex_lock ( & devlink_mutex ) ;
devlink_sb = devlink_sb_get_by_index ( devlink , sb_index ) ;
WARN_ON ( ! devlink_sb ) ;
list_del ( & devlink_sb - > list ) ;
mutex_unlock ( & devlink_mutex ) ;
kfree ( devlink_sb ) ;
}
EXPORT_SYMBOL_GPL ( devlink_sb_unregister ) ;
2016-02-26 17:32:23 +01:00
static int __init devlink_module_init ( void )
{
2016-10-24 14:40:03 +02:00
return genl_register_family ( & devlink_nl_family ) ;
2016-02-26 17:32:23 +01:00
}
static void __exit devlink_module_exit ( void )
{
genl_unregister_family ( & devlink_nl_family ) ;
}
module_init ( devlink_module_init ) ;
module_exit ( devlink_module_exit ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_AUTHOR ( " Jiri Pirko <jiri@mellanox.com> " ) ;
MODULE_DESCRIPTION ( " Network physical device Netlink interface " ) ;
MODULE_ALIAS_GENL_FAMILY ( DEVLINK_GENL_NAME ) ;