2019-04-25 15:59:44 +02:00
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2017 Netronome Systems, Inc.
* Copyright ( C ) 2019 Mellanox Technologies . All rights reserved
*/
# include <linux/device.h>
2019-04-25 15:59:47 +02:00
# include <linux/idr.h>
2019-04-25 15:59:45 +02:00
# include <linux/kernel.h>
2019-04-25 15:59:48 +02:00
# include <linux/list.h>
# include <linux/mutex.h>
2019-04-25 15:59:45 +02:00
# include <linux/rtnetlink.h>
# include <linux/slab.h>
# include <linux/sysfs.h>
2019-04-25 15:59:44 +02:00
# include "netdevsim.h"
2019-04-25 15:59:47 +02:00
static DEFINE_IDA ( nsim_bus_dev_ids ) ;
2019-04-25 15:59:48 +02:00
static LIST_HEAD ( nsim_bus_dev_list ) ;
static DEFINE_MUTEX ( nsim_bus_dev_list_lock ) ;
2019-04-25 15:59:45 +02:00
static struct nsim_bus_dev * to_nsim_bus_dev ( struct device * dev )
{
return container_of ( dev , struct nsim_bus_dev , dev ) ;
}
static int nsim_bus_dev_vfs_enable ( struct nsim_bus_dev * nsim_bus_dev ,
unsigned int num_vfs )
{
nsim_bus_dev - > vfconfigs = kcalloc ( num_vfs ,
sizeof ( struct nsim_vf_config ) ,
GFP_KERNEL ) ;
if ( ! nsim_bus_dev - > vfconfigs )
return - ENOMEM ;
nsim_bus_dev - > num_vfs = num_vfs ;
return 0 ;
}
static void nsim_bus_dev_vfs_disable ( struct nsim_bus_dev * nsim_bus_dev )
{
kfree ( nsim_bus_dev - > vfconfigs ) ;
nsim_bus_dev - > vfconfigs = NULL ;
nsim_bus_dev - > num_vfs = 0 ;
}
static ssize_t
nsim_bus_dev_numvfs_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
struct nsim_bus_dev * nsim_bus_dev = to_nsim_bus_dev ( dev ) ;
unsigned int num_vfs ;
int ret ;
ret = kstrtouint ( buf , 0 , & num_vfs ) ;
if ( ret )
return ret ;
rtnl_lock ( ) ;
if ( nsim_bus_dev - > num_vfs = = num_vfs )
goto exit_good ;
if ( nsim_bus_dev - > num_vfs & & num_vfs ) {
ret = - EBUSY ;
goto exit_unlock ;
}
if ( num_vfs ) {
ret = nsim_bus_dev_vfs_enable ( nsim_bus_dev , num_vfs ) ;
if ( ret )
goto exit_unlock ;
} else {
nsim_bus_dev_vfs_disable ( nsim_bus_dev ) ;
}
exit_good :
ret = count ;
exit_unlock :
rtnl_unlock ( ) ;
return ret ;
}
static ssize_t
nsim_bus_dev_numvfs_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct nsim_bus_dev * nsim_bus_dev = to_nsim_bus_dev ( dev ) ;
return sprintf ( buf , " %u \n " , nsim_bus_dev - > num_vfs ) ;
}
static struct device_attribute nsim_bus_dev_numvfs_attr =
__ATTR ( sriov_numvfs , 0664 , nsim_bus_dev_numvfs_show ,
nsim_bus_dev_numvfs_store ) ;
2019-04-25 15:59:54 +02:00
static ssize_t
new_port_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
struct nsim_bus_dev * nsim_bus_dev = to_nsim_bus_dev ( dev ) ;
unsigned int port_index ;
int ret ;
ret = kstrtouint ( buf , 0 , & port_index ) ;
if ( ret )
return ret ;
ret = nsim_dev_port_add ( nsim_bus_dev , port_index ) ;
return ret ? ret : count ;
}
static struct device_attribute nsim_bus_dev_new_port_attr = __ATTR_WO ( new_port ) ;
static ssize_t
del_port_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
struct nsim_bus_dev * nsim_bus_dev = to_nsim_bus_dev ( dev ) ;
unsigned int port_index ;
int ret ;
ret = kstrtouint ( buf , 0 , & port_index ) ;
if ( ret )
return ret ;
ret = nsim_dev_port_del ( nsim_bus_dev , port_index ) ;
return ret ? ret : count ;
}
static struct device_attribute nsim_bus_dev_del_port_attr = __ATTR_WO ( del_port ) ;
2019-04-25 15:59:45 +02:00
static struct attribute * nsim_bus_dev_attrs [ ] = {
& nsim_bus_dev_numvfs_attr . attr ,
2019-04-25 15:59:54 +02:00
& nsim_bus_dev_new_port_attr . attr ,
& nsim_bus_dev_del_port_attr . attr ,
2019-04-25 15:59:45 +02:00
NULL ,
} ;
static const struct attribute_group nsim_bus_dev_attr_group = {
. attrs = nsim_bus_dev_attrs ,
} ;
static const struct attribute_group * nsim_bus_dev_attr_groups [ ] = {
& nsim_bus_dev_attr_group ,
NULL ,
} ;
static void nsim_bus_dev_release ( struct device * dev )
{
struct nsim_bus_dev * nsim_bus_dev = to_nsim_bus_dev ( dev ) ;
nsim_bus_dev_vfs_disable ( nsim_bus_dev ) ;
}
static struct device_type nsim_bus_dev_type = {
. groups = nsim_bus_dev_attr_groups ,
. release = nsim_bus_dev_release ,
} ;
2019-04-25 15:59:55 +02:00
static struct nsim_bus_dev *
nsim_bus_dev_new ( unsigned int id , unsigned int port_count ) ;
2019-04-25 15:59:48 +02:00
static ssize_t
new_device_store ( struct bus_type * bus , const char * buf , size_t count )
{
struct nsim_bus_dev * nsim_bus_dev ;
unsigned int port_count ;
unsigned int id ;
int err ;
err = sscanf ( buf , " %u %u " , & id , & port_count ) ;
switch ( err ) {
case 1 :
port_count = 1 ;
2019-04-29 12:38:07 -05:00
/* fall through */
2019-04-25 15:59:48 +02:00
case 2 :
if ( id > INT_MAX ) {
pr_err ( " Value of \" id \" is too big. \n " ) ;
return - EINVAL ;
}
break ;
default :
pr_err ( " Format for adding new device is \" id port_count \" (uint uint). \n " ) ;
return - EINVAL ;
}
nsim_bus_dev = nsim_bus_dev_new ( id , port_count ) ;
if ( IS_ERR ( nsim_bus_dev ) )
return PTR_ERR ( nsim_bus_dev ) ;
mutex_lock ( & nsim_bus_dev_list_lock ) ;
list_add_tail ( & nsim_bus_dev - > list , & nsim_bus_dev_list ) ;
mutex_unlock ( & nsim_bus_dev_list_lock ) ;
return count ;
}
static BUS_ATTR_WO ( new_device ) ;
2019-04-25 15:59:55 +02:00
static void nsim_bus_dev_del ( struct nsim_bus_dev * nsim_bus_dev ) ;
2019-04-25 15:59:48 +02:00
static ssize_t
del_device_store ( struct bus_type * bus , const char * buf , size_t count )
{
struct nsim_bus_dev * nsim_bus_dev , * tmp ;
unsigned int id ;
int err ;
err = sscanf ( buf , " %u " , & id ) ;
switch ( err ) {
case 1 :
if ( id > INT_MAX ) {
pr_err ( " Value of \" id \" is too big. \n " ) ;
return - EINVAL ;
}
break ;
default :
pr_err ( " Format for deleting device is \" id \" (uint). \n " ) ;
return - EINVAL ;
}
err = - ENOENT ;
mutex_lock ( & nsim_bus_dev_list_lock ) ;
list_for_each_entry_safe ( nsim_bus_dev , tmp , & nsim_bus_dev_list , list ) {
if ( nsim_bus_dev - > dev . id ! = id )
continue ;
list_del ( & nsim_bus_dev - > list ) ;
nsim_bus_dev_del ( nsim_bus_dev ) ;
err = 0 ;
break ;
}
mutex_unlock ( & nsim_bus_dev_list_lock ) ;
return ! err ? count : err ;
}
static BUS_ATTR_WO ( del_device ) ;
static struct attribute * nsim_bus_attrs [ ] = {
& bus_attr_new_device . attr ,
& bus_attr_del_device . attr ,
NULL
} ;
ATTRIBUTE_GROUPS ( nsim_bus ) ;
2019-04-25 15:59:53 +02:00
static int nsim_bus_probe ( struct device * dev )
{
struct nsim_bus_dev * nsim_bus_dev = to_nsim_bus_dev ( dev ) ;
return nsim_dev_probe ( nsim_bus_dev ) ;
}
static int nsim_bus_remove ( struct device * dev )
{
struct nsim_bus_dev * nsim_bus_dev = to_nsim_bus_dev ( dev ) ;
nsim_dev_remove ( nsim_bus_dev ) ;
return 0 ;
}
2019-05-04 16:12:07 +08:00
static int nsim_num_vf ( struct device * dev )
2019-04-25 15:59:45 +02:00
{
struct nsim_bus_dev * nsim_bus_dev = to_nsim_bus_dev ( dev ) ;
return nsim_bus_dev - > num_vfs ;
}
static struct bus_type nsim_bus = {
2019-04-25 15:59:44 +02:00
. name = DRV_NAME ,
. dev_name = DRV_NAME ,
2019-04-25 15:59:48 +02:00
. bus_groups = nsim_bus_groups ,
2019-04-25 15:59:53 +02:00
. probe = nsim_bus_probe ,
. remove = nsim_bus_remove ,
2019-04-25 15:59:44 +02:00
. num_vf = nsim_num_vf ,
} ;
2019-04-25 15:59:55 +02:00
static struct nsim_bus_dev *
nsim_bus_dev_new ( unsigned int id , unsigned int port_count )
2019-04-25 15:59:45 +02:00
{
struct nsim_bus_dev * nsim_bus_dev ;
int err ;
nsim_bus_dev = kzalloc ( sizeof ( * nsim_bus_dev ) , GFP_KERNEL ) ;
if ( ! nsim_bus_dev )
return ERR_PTR ( - ENOMEM ) ;
2019-04-25 15:59:55 +02:00
err = ida_alloc_range ( & nsim_bus_dev_ids , id , id , GFP_KERNEL ) ;
2019-04-25 15:59:47 +02:00
if ( err < 0 )
goto err_nsim_bus_dev_free ;
nsim_bus_dev - > dev . id = err ;
2019-04-25 15:59:45 +02:00
nsim_bus_dev - > dev . bus = & nsim_bus ;
nsim_bus_dev - > dev . type = & nsim_bus_dev_type ;
2019-04-25 15:59:48 +02:00
nsim_bus_dev - > port_count = port_count ;
2019-10-05 08:10:32 +02:00
nsim_bus_dev - > initial_net = current - > nsproxy - > net_ns ;
2019-04-25 15:59:48 +02:00
2019-04-25 15:59:45 +02:00
err = device_register ( & nsim_bus_dev - > dev ) ;
if ( err )
2019-04-25 15:59:47 +02:00
goto err_nsim_bus_dev_id_free ;
2019-04-25 15:59:45 +02:00
return nsim_bus_dev ;
2019-04-25 15:59:47 +02:00
err_nsim_bus_dev_id_free :
ida_free ( & nsim_bus_dev_ids , nsim_bus_dev - > dev . id ) ;
2019-04-25 15:59:45 +02:00
err_nsim_bus_dev_free :
kfree ( nsim_bus_dev ) ;
return ERR_PTR ( err ) ;
}
2019-04-25 15:59:55 +02:00
static void nsim_bus_dev_del ( struct nsim_bus_dev * nsim_bus_dev )
2019-04-25 15:59:45 +02:00
{
device_unregister ( & nsim_bus_dev - > dev ) ;
2019-04-25 15:59:47 +02:00
ida_free ( & nsim_bus_dev_ids , nsim_bus_dev - > dev . id ) ;
2019-04-25 15:59:45 +02:00
kfree ( nsim_bus_dev ) ;
}
2019-04-25 15:59:46 +02:00
static struct device_driver nsim_driver = {
. name = DRV_NAME ,
. bus = & nsim_bus ,
. owner = THIS_MODULE ,
} ;
2019-04-25 15:59:44 +02:00
int nsim_bus_init ( void )
{
2019-04-25 15:59:46 +02:00
int err ;
err = bus_register ( & nsim_bus ) ;
if ( err )
return err ;
err = driver_register ( & nsim_driver ) ;
if ( err )
goto err_bus_unregister ;
return 0 ;
err_bus_unregister :
bus_unregister ( & nsim_bus ) ;
return err ;
2019-04-25 15:59:44 +02:00
}
void nsim_bus_exit ( void )
{
2019-04-25 15:59:48 +02:00
struct nsim_bus_dev * nsim_bus_dev , * tmp ;
mutex_lock ( & nsim_bus_dev_list_lock ) ;
list_for_each_entry_safe ( nsim_bus_dev , tmp , & nsim_bus_dev_list , list ) {
list_del ( & nsim_bus_dev - > list ) ;
nsim_bus_dev_del ( nsim_bus_dev ) ;
}
mutex_unlock ( & nsim_bus_dev_list_lock ) ;
2019-04-25 15:59:46 +02:00
driver_unregister ( & nsim_driver ) ;
2019-04-25 15:59:44 +02:00
bus_unregister ( & nsim_bus ) ;
}