2023-01-05 07:05:20 +03: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-05 07:05:30 +03:00
# include <net/sock.h>
2023-01-05 07:05:20 +03:00
# include "devl_internal.h"
2023-11-15 15:17:10 +03:00
# define DEVLINK_NL_FLAG_NEED_PORT BIT(0)
# define DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT BIT(1)
2023-11-15 15:17:13 +03:00
# define DEVLINK_NL_FLAG_NEED_DEV_LOCK BIT(2)
2023-11-15 15:17:10 +03:00
2023-01-05 07:05:20 +03:00
static const struct genl_multicast_group devlink_nl_mcgrps [ ] = {
[ DEVLINK_MCGRP_CONFIG ] = { . name = DEVLINK_GENL_MCGRP_CONFIG_NAME } ,
} ;
2023-12-16 15:30:00 +03:00
struct devlink_nl_sock_priv {
struct devlink_obj_desc __rcu * flt ;
spinlock_t flt_lock ; /* Protects flt. */
} ;
static void devlink_nl_sock_priv_init ( void * priv )
{
struct devlink_nl_sock_priv * sk_priv = priv ;
spin_lock_init ( & sk_priv - > flt_lock ) ;
}
static void devlink_nl_sock_priv_destroy ( void * priv )
{
struct devlink_nl_sock_priv * sk_priv = priv ;
struct devlink_obj_desc * flt ;
flt = rcu_dereference_protected ( sk_priv - > flt , true ) ;
kfree_rcu ( flt , rcu ) ;
}
int devlink_nl_notify_filter_set_doit ( struct sk_buff * skb ,
struct genl_info * info )
{
struct devlink_nl_sock_priv * sk_priv ;
struct nlattr * * attrs = info - > attrs ;
struct devlink_obj_desc * flt ;
size_t data_offset = 0 ;
size_t data_size = 0 ;
char * pos ;
if ( attrs [ DEVLINK_ATTR_BUS_NAME ] )
data_size = size_add ( data_size ,
nla_len ( attrs [ DEVLINK_ATTR_BUS_NAME ] ) + 1 ) ;
if ( attrs [ DEVLINK_ATTR_DEV_NAME ] )
data_size = size_add ( data_size ,
nla_len ( attrs [ DEVLINK_ATTR_DEV_NAME ] ) + 1 ) ;
flt = kzalloc ( size_add ( sizeof ( * flt ) , data_size ) , GFP_KERNEL ) ;
if ( ! flt )
return - ENOMEM ;
pos = ( char * ) flt - > data ;
if ( attrs [ DEVLINK_ATTR_BUS_NAME ] ) {
data_offset + = nla_strscpy ( pos ,
attrs [ DEVLINK_ATTR_BUS_NAME ] ,
data_size ) + 1 ;
flt - > bus_name = pos ;
pos + = data_offset ;
}
if ( attrs [ DEVLINK_ATTR_DEV_NAME ] ) {
nla_strscpy ( pos , attrs [ DEVLINK_ATTR_DEV_NAME ] ,
data_size - data_offset ) ;
flt - > dev_name = pos ;
}
2023-12-16 15:30:01 +03:00
if ( attrs [ DEVLINK_ATTR_PORT_INDEX ] ) {
flt - > port_index = nla_get_u32 ( attrs [ DEVLINK_ATTR_PORT_INDEX ] ) ;
flt - > port_index_valid = true ;
}
2023-12-16 15:30:00 +03:00
/* Don't attach empty filter. */
2023-12-16 15:30:01 +03:00
if ( ! flt - > bus_name & & ! flt - > dev_name & & ! flt - > port_index_valid ) {
2023-12-16 15:30:00 +03:00
kfree ( flt ) ;
flt = NULL ;
}
sk_priv = genl_sk_priv_get ( & devlink_nl_family , NETLINK_CB ( skb ) . sk ) ;
if ( IS_ERR ( sk_priv ) ) {
kfree ( flt ) ;
return PTR_ERR ( sk_priv ) ;
}
spin_lock ( & sk_priv - > flt_lock ) ;
flt = rcu_replace_pointer ( sk_priv - > flt , flt ,
lockdep_is_held ( & sk_priv - > flt_lock ) ) ;
spin_unlock ( & sk_priv - > flt_lock ) ;
kfree_rcu ( flt , rcu ) ;
return 0 ;
}
static bool devlink_obj_desc_match ( const struct devlink_obj_desc * desc ,
const struct devlink_obj_desc * flt )
{
if ( desc - > bus_name & & flt - > bus_name & &
strcmp ( desc - > bus_name , flt - > bus_name ) )
return false ;
if ( desc - > dev_name & & flt - > dev_name & &
strcmp ( desc - > dev_name , flt - > dev_name ) )
return false ;
2023-12-16 15:30:01 +03:00
if ( desc - > port_index_valid & & flt - > port_index_valid & &
desc - > port_index ! = flt - > port_index )
return false ;
2023-12-16 15:30:00 +03:00
return true ;
}
int devlink_nl_notify_filter ( struct sock * dsk , struct sk_buff * skb , void * data )
{
struct devlink_obj_desc * desc = data ;
struct devlink_nl_sock_priv * sk_priv ;
struct devlink_obj_desc * flt ;
int ret = 0 ;
rcu_read_lock ( ) ;
sk_priv = __genl_sk_priv_get ( & devlink_nl_family , dsk ) ;
if ( ! IS_ERR_OR_NULL ( sk_priv ) ) {
flt = rcu_dereference ( sk_priv - > flt ) ;
if ( flt )
ret = ! devlink_obj_desc_match ( desc , flt ) ;
}
rcu_read_unlock ( ) ;
return ret ;
}
2023-09-13 10:12:36 +03:00
int devlink_nl_put_nested_handle ( struct sk_buff * msg , struct net * net ,
2023-09-13 10:12:37 +03:00
struct devlink * devlink , int attrtype )
2023-09-13 10:12:36 +03:00
{
struct nlattr * nested_attr ;
2023-10-13 15:10:24 +03:00
struct net * devl_net ;
2023-09-13 10:12:36 +03:00
2023-09-13 10:12:37 +03:00
nested_attr = nla_nest_start ( msg , attrtype ) ;
2023-09-13 10:12:36 +03:00
if ( ! nested_attr )
return - EMSGSIZE ;
if ( devlink_nl_put_handle ( msg , devlink ) )
goto nla_put_failure ;
2023-10-13 15:10:24 +03: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 10:12:36 +03:00
if ( nla_put_s32 ( msg , DEVLINK_ATTR_NETNS_ID , id ) )
return - EMSGSIZE ;
2023-10-13 15:10:24 +03:00
} else {
rcu_read_unlock ( ) ;
2023-09-13 10:12:36 +03:00
}
nla_nest_end ( msg , nested_attr ) ;
return 0 ;
nla_put_failure :
nla_nest_cancel ( msg , nested_attr ) ;
return - EMSGSIZE ;
}
2023-08-28 09:16:46 +03: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-06 09:33:56 +03:00
struct devlink *
2023-11-15 15:17:13 +03:00
devlink_get_from_attrs_lock ( struct net * net , struct nlattr * * attrs ,
bool dev_lock )
2023-01-05 07:05:20 +03: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 ) {
2024-03-12 13:52:38 +03:00
if ( strcmp ( devlink - > dev - > bus - > name , busname ) = = 0 & &
strcmp ( dev_name ( devlink - > dev ) , devname ) = = 0 ) {
devl_dev_lock ( devlink , dev_lock ) ;
if ( devl_is_registered ( devlink ) )
return devlink ;
devl_dev_unlock ( devlink , dev_lock ) ;
}
2023-01-05 07:05:20 +03:00
devlink_put ( devlink ) ;
}
return ERR_PTR ( - ENODEV ) ;
}
2023-08-11 18:57:04 +03:00
static int __devlink_nl_pre_doit ( struct sk_buff * skb , struct genl_info * info ,
u8 flags )
2023-01-05 07:05:20 +03:00
{
2023-11-15 15:17:13 +03:00
bool dev_lock = flags & DEVLINK_NL_FLAG_NEED_DEV_LOCK ;
2023-01-05 07:05:20 +03:00
struct devlink_port * devlink_port ;
struct devlink * devlink ;
int err ;
2023-11-15 15:17:13 +03:00
devlink = devlink_get_from_attrs_lock ( genl_info_net ( info ) , info - > attrs ,
dev_lock ) ;
2023-01-05 07:05:20 +03:00
if ( IS_ERR ( devlink ) )
return PTR_ERR ( devlink ) ;
2023-01-06 09:33:56 +03:00
2023-01-05 07:05:20 +03:00
info - > user_ptr [ 0 ] = devlink ;
2023-08-11 18:57:04 +03:00
if ( flags & DEVLINK_NL_FLAG_NEED_PORT ) {
2023-01-05 07:05:20 +03: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 18:57:04 +03:00
} else if ( flags & DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT ) {
2023-01-05 07:05:20 +03: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 15:17:13 +03:00
devl_dev_unlock ( devlink , dev_lock ) ;
2023-01-05 07:05:20 +03:00
devlink_put ( devlink ) ;
return err ;
}
2023-08-11 18:57:04 +03:00
int devlink_nl_pre_doit ( const struct genl_split_ops * ops ,
struct sk_buff * skb , struct genl_info * info )
{
2023-10-21 14:27:11 +03:00
return __devlink_nl_pre_doit ( skb , info , 0 ) ;
2023-08-11 18:57:04 +03: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 15:17:14 +03: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 18:57:04 +03: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 15:17:12 +03:00
static void __devlink_nl_post_doit ( struct sk_buff * skb , struct genl_info * info ,
u8 flags )
2023-01-05 07:05:20 +03:00
{
2023-11-15 15:17:13 +03:00
bool dev_lock = flags & DEVLINK_NL_FLAG_NEED_DEV_LOCK ;
2023-01-05 07:05:20 +03:00
struct devlink * devlink ;
devlink = info - > user_ptr [ 0 ] ;
2023-11-15 15:17:13 +03:00
devl_dev_unlock ( devlink , dev_lock ) ;
2023-01-05 07:05:20 +03:00
devlink_put ( devlink ) ;
}
2023-11-15 15:17:12 +03: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 15:17:14 +03: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 18:57:11 +03: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 15:17:13 +03:00
devlink = devlink_get_from_attrs_lock ( sock_net ( msg - > sk ) , attrs , false ) ;
2023-08-11 18:57:11 +03: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-05 07:05:30 +03:00
{
struct devlink_nl_dump_state * state = devlink_dump_state ( cb ) ;
struct devlink * devlink ;
int err = 0 ;
2023-01-18 18:21:14 +03:00
while ( ( devlink = devlinks_xa_find_get ( sock_net ( msg - > sk ) ,
& state - > instance ) ) ) {
2023-01-05 07:05:30 +03:00
devl_lock ( devlink ) ;
2023-01-06 09:33:57 +03:00
if ( devl_is_registered ( devlink ) )
2023-08-11 18:57:11 +03:00
err = dump_one ( msg , devlink , cb , flags ) ;
2023-01-06 09:33:57 +03:00
else
err = 0 ;
2023-01-05 07:05:30 +03:00
devl_unlock ( devlink ) ;
devlink_put ( devlink ) ;
if ( err )
break ;
2023-01-18 18:21:14 +03:00
state - > instance + + ;
2023-01-05 07:05:30 +03:00
/* restart sub-object walk for the next instance */
state - > idx = 0 ;
}
if ( err ! = - EMSGSIZE )
return err ;
return msg - > len ;
}
2023-08-11 18:57:11 +03:00
int devlink_nl_dumpit ( struct sk_buff * msg , struct netlink_callback * cb ,
devlink_nl_dump_one_func_t * dump_one )
{
2023-08-15 00:47:18 +03:00
const struct genl_info * info = genl_info_dump ( cb ) ;
2023-08-11 18:57:11 +03: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-05 07:05:20 +03: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 14:13:40 +03:00
. split_ops = devlink_nl_ops ,
. n_split_ops = ARRAY_SIZE ( devlink_nl_ops ) ,
2023-01-05 07:05:20 +03:00
. resv_start_op = DEVLINK_CMD_SELFTESTS_RUN + 1 ,
. mcgrps = devlink_nl_mcgrps ,
. n_mcgrps = ARRAY_SIZE ( devlink_nl_mcgrps ) ,
2023-12-16 15:30:00 +03:00
. sock_priv_size = sizeof ( struct devlink_nl_sock_priv ) ,
. sock_priv_init = devlink_nl_sock_priv_init ,
. sock_priv_destroy = devlink_nl_sock_priv_destroy ,
2023-01-05 07:05:20 +03:00
} ;