2023-01-04 20:05:20 -08:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright ( c ) 2016 Mellanox Technologies . All rights reserved .
* Copyright ( c ) 2016 Jiri Pirko < jiri @ mellanox . com >
*/
# include <net/genetlink.h>
2023-01-04 20:05:30 -08:00
# include <net/sock.h>
2023-01-04 20:05:20 -08:00
# include "devl_internal.h"
2023-11-15 13:17:10 +01:00
# define DEVLINK_NL_FLAG_NEED_PORT BIT(0)
# define DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT BIT(1)
2023-11-15 13:17:13 +01:00
# define DEVLINK_NL_FLAG_NEED_DEV_LOCK BIT(2)
2023-11-15 13:17:10 +01:00
2023-01-04 20:05:20 -08:00
static const struct genl_multicast_group devlink_nl_mcgrps [ ] = {
[ DEVLINK_MCGRP_CONFIG ] = { . name = DEVLINK_GENL_MCGRP_CONFIG_NAME } ,
} ;
2023-09-13 09:12:36 +02:00
int devlink_nl_put_nested_handle ( struct sk_buff * msg , struct net * net ,
2023-09-13 09:12:37 +02:00
struct devlink * devlink , int attrtype )
2023-09-13 09:12:36 +02:00
{
struct nlattr * nested_attr ;
2023-10-13 14:10:24 +02:00
struct net * devl_net ;
2023-09-13 09:12:36 +02:00
2023-09-13 09:12:37 +02:00
nested_attr = nla_nest_start ( msg , attrtype ) ;
2023-09-13 09:12:36 +02:00
if ( ! nested_attr )
return - EMSGSIZE ;
if ( devlink_nl_put_handle ( msg , devlink ) )
goto nla_put_failure ;
2023-10-13 14:10:24 +02:00
rcu_read_lock ( ) ;
devl_net = read_pnet_rcu ( & devlink - > _net ) ;
if ( ! net_eq ( net , devl_net ) ) {
int id = peernet2id_alloc ( net , devl_net , GFP_ATOMIC ) ;
rcu_read_unlock ( ) ;
2023-09-13 09:12:36 +02:00
if ( nla_put_s32 ( msg , DEVLINK_ATTR_NETNS_ID , id ) )
return - EMSGSIZE ;
2023-10-13 14:10:24 +02:00
} else {
rcu_read_unlock ( ) ;
2023-09-13 09:12:36 +02:00
}
nla_nest_end ( msg , nested_attr ) ;
return 0 ;
nla_put_failure :
nla_nest_cancel ( msg , nested_attr ) ;
return - EMSGSIZE ;
}
2023-08-28 08:16:46 +02:00
int devlink_nl_msg_reply_and_new ( struct sk_buff * * msg , struct genl_info * info )
{
int err ;
if ( * msg ) {
err = genlmsg_reply ( * msg , info ) ;
if ( err )
return err ;
}
* msg = genlmsg_new ( GENLMSG_DEFAULT_SIZE , GFP_KERNEL ) ;
if ( ! * msg )
return - ENOMEM ;
return 0 ;
}
2023-01-05 22:33:56 -08:00
struct devlink *
2023-11-15 13:17:13 +01:00
devlink_get_from_attrs_lock ( struct net * net , struct nlattr * * attrs ,
bool dev_lock )
2023-01-04 20:05:20 -08:00
{
struct devlink * devlink ;
unsigned long index ;
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 ] ) ;
devlinks_xa_for_each_registered_get ( net , index , devlink ) {
2023-11-15 13:17:13 +01:00
devl_dev_lock ( devlink , dev_lock ) ;
2023-01-05 22:33:57 -08:00
if ( devl_is_registered ( devlink ) & &
strcmp ( devlink - > dev - > bus - > name , busname ) = = 0 & &
2023-01-04 20:05:20 -08:00
strcmp ( dev_name ( devlink - > dev ) , devname ) = = 0 )
return devlink ;
2023-11-15 13:17:13 +01:00
devl_dev_unlock ( devlink , dev_lock ) ;
2023-01-04 20:05:20 -08:00
devlink_put ( devlink ) ;
}
return ERR_PTR ( - ENODEV ) ;
}
2023-08-11 17:57:04 +02:00
static int __devlink_nl_pre_doit ( struct sk_buff * skb , struct genl_info * info ,
u8 flags )
2023-01-04 20:05:20 -08:00
{
2023-11-15 13:17:13 +01:00
bool dev_lock = flags & DEVLINK_NL_FLAG_NEED_DEV_LOCK ;
2023-01-04 20:05:20 -08:00
struct devlink_port * devlink_port ;
struct devlink * devlink ;
int err ;
2023-11-15 13:17:13 +01:00
devlink = devlink_get_from_attrs_lock ( genl_info_net ( info ) , info - > attrs ,
dev_lock ) ;
2023-01-04 20:05:20 -08:00
if ( IS_ERR ( devlink ) )
return PTR_ERR ( devlink ) ;
2023-01-05 22:33:56 -08:00
2023-01-04 20:05:20 -08:00
info - > user_ptr [ 0 ] = devlink ;
2023-08-11 17:57:04 +02:00
if ( flags & DEVLINK_NL_FLAG_NEED_PORT ) {
2023-01-04 20:05:20 -08:00
devlink_port = devlink_port_get_from_info ( devlink , info ) ;
if ( IS_ERR ( devlink_port ) ) {
err = PTR_ERR ( devlink_port ) ;
goto unlock ;
}
info - > user_ptr [ 1 ] = devlink_port ;
2023-08-11 17:57:04 +02:00
} else if ( flags & DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT ) {
2023-01-04 20:05:20 -08:00
devlink_port = devlink_port_get_from_info ( devlink , info ) ;
if ( ! IS_ERR ( devlink_port ) )
info - > user_ptr [ 1 ] = devlink_port ;
}
return 0 ;
unlock :
2023-11-15 13:17:13 +01:00
devl_dev_unlock ( devlink , dev_lock ) ;
2023-01-04 20:05:20 -08:00
devlink_put ( devlink ) ;
return err ;
}
2023-08-11 17:57:04 +02:00
int devlink_nl_pre_doit ( const struct genl_split_ops * ops ,
struct sk_buff * skb , struct genl_info * info )
{
2023-10-21 13:27:11 +02:00
return __devlink_nl_pre_doit ( skb , info , 0 ) ;
2023-08-11 17:57:04 +02:00
}
int devlink_nl_pre_doit_port ( const struct genl_split_ops * ops ,
struct sk_buff * skb , struct genl_info * info )
{
return __devlink_nl_pre_doit ( skb , info , DEVLINK_NL_FLAG_NEED_PORT ) ;
}
2023-11-15 13:17:14 +01:00
int devlink_nl_pre_doit_dev_lock ( const struct genl_split_ops * ops ,
struct sk_buff * skb , struct genl_info * info )
{
return __devlink_nl_pre_doit ( skb , info , DEVLINK_NL_FLAG_NEED_DEV_LOCK ) ;
}
2023-08-11 17:57:04 +02:00
int devlink_nl_pre_doit_port_optional ( const struct genl_split_ops * ops ,
struct sk_buff * skb ,
struct genl_info * info )
{
return __devlink_nl_pre_doit ( skb , info , DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT ) ;
}
2023-11-15 13:17:12 +01:00
static void __devlink_nl_post_doit ( struct sk_buff * skb , struct genl_info * info ,
u8 flags )
2023-01-04 20:05:20 -08:00
{
2023-11-15 13:17:13 +01:00
bool dev_lock = flags & DEVLINK_NL_FLAG_NEED_DEV_LOCK ;
2023-01-04 20:05:20 -08:00
struct devlink * devlink ;
devlink = info - > user_ptr [ 0 ] ;
2023-11-15 13:17:13 +01:00
devl_dev_unlock ( devlink , dev_lock ) ;
2023-01-04 20:05:20 -08:00
devlink_put ( devlink ) ;
}
2023-11-15 13:17:12 +01:00
void devlink_nl_post_doit ( const struct genl_split_ops * ops ,
struct sk_buff * skb , struct genl_info * info )
{
__devlink_nl_post_doit ( skb , info , 0 ) ;
}
2023-11-15 13:17:14 +01:00
void
devlink_nl_post_doit_dev_lock ( const struct genl_split_ops * ops ,
struct sk_buff * skb , struct genl_info * info )
{
__devlink_nl_post_doit ( skb , info , DEVLINK_NL_FLAG_NEED_DEV_LOCK ) ;
}
2023-08-11 17:57:11 +02:00
static int devlink_nl_inst_single_dumpit ( struct sk_buff * msg ,
struct netlink_callback * cb , int flags ,
devlink_nl_dump_one_func_t * dump_one ,
struct nlattr * * attrs )
{
struct devlink * devlink ;
int err ;
2023-11-15 13:17:13 +01:00
devlink = devlink_get_from_attrs_lock ( sock_net ( msg - > sk ) , attrs , false ) ;
2023-08-11 17:57:11 +02:00
if ( IS_ERR ( devlink ) )
return PTR_ERR ( devlink ) ;
err = dump_one ( msg , devlink , cb , flags | NLM_F_DUMP_FILTERED ) ;
devl_unlock ( devlink ) ;
devlink_put ( devlink ) ;
if ( err ! = - EMSGSIZE )
return err ;
return msg - > len ;
}
static int devlink_nl_inst_iter_dumpit ( struct sk_buff * msg ,
struct netlink_callback * cb , int flags ,
devlink_nl_dump_one_func_t * dump_one )
2023-01-04 20:05:30 -08:00
{
struct devlink_nl_dump_state * state = devlink_dump_state ( cb ) ;
struct devlink * devlink ;
int err = 0 ;
2023-01-18 16:21:14 +01:00
while ( ( devlink = devlinks_xa_find_get ( sock_net ( msg - > sk ) ,
& state - > instance ) ) ) {
2023-01-04 20:05:30 -08:00
devl_lock ( devlink ) ;
2023-01-05 22:33:57 -08:00
if ( devl_is_registered ( devlink ) )
2023-08-11 17:57:11 +02:00
err = dump_one ( msg , devlink , cb , flags ) ;
2023-01-05 22:33:57 -08:00
else
err = 0 ;
2023-01-04 20:05:30 -08:00
devl_unlock ( devlink ) ;
devlink_put ( devlink ) ;
if ( err )
break ;
2023-01-18 16:21:14 +01:00
state - > instance + + ;
2023-01-04 20:05:30 -08:00
/* restart sub-object walk for the next instance */
state - > idx = 0 ;
}
if ( err ! = - EMSGSIZE )
return err ;
return msg - > len ;
}
2023-08-11 17:57:11 +02:00
int devlink_nl_dumpit ( struct sk_buff * msg , struct netlink_callback * cb ,
devlink_nl_dump_one_func_t * dump_one )
{
2023-08-14 14:47:18 -07:00
const struct genl_info * info = genl_info_dump ( cb ) ;
2023-08-11 17:57:11 +02:00
struct nlattr * * attrs = info - > attrs ;
int flags = NLM_F_MULTI ;
if ( attrs & &
( attrs [ DEVLINK_ATTR_BUS_NAME ] | | attrs [ DEVLINK_ATTR_DEV_NAME ] ) )
return devlink_nl_inst_single_dumpit ( msg , cb , flags , dump_one ,
attrs ) ;
else
return devlink_nl_inst_iter_dumpit ( msg , cb , flags , dump_one ) ;
}
2023-01-04 20:05:20 -08:00
struct genl_family devlink_nl_family __ro_after_init = {
. name = DEVLINK_GENL_NAME ,
. version = DEVLINK_GENL_VERSION ,
. netnsok = true ,
. parallel_ops = true ,
. module = THIS_MODULE ,
2023-08-03 13:13:40 +02:00
. split_ops = devlink_nl_ops ,
. n_split_ops = ARRAY_SIZE ( devlink_nl_ops ) ,
2023-01-04 20:05:20 -08:00
. resv_start_op = DEVLINK_CMD_SELFTESTS_RUN + 1 ,
. mcgrps = devlink_nl_mcgrps ,
. n_mcgrps = ARRAY_SIZE ( devlink_nl_mcgrps ) ,
} ;