2021-10-27 18:46:41 +05:00
// SPDX-License-Identifier: GPL-2.0-only
/* Huawei HiNIC PCI Express Linux driver
* Copyright ( c ) 2017 Huawei Technologies Co . , Ltd
*/
# include <linux/pci.h>
# include <linux/if_vlan.h>
# include <linux/interrupt.h>
# include <linux/etherdevice.h>
# include <linux/netdevice.h>
2022-04-02 17:19:37 +05:00
# include <linux/module.h>
2021-10-27 18:46:41 +05:00
# include "hinic_hw_dev.h"
# include "hinic_dev.h"
# include "hinic_hw_mbox.h"
# include "hinic_hw_cmdq.h"
# include "hinic_port.h"
# include "hinic_sriov.h"
static unsigned char set_vf_link_state ;
module_param ( set_vf_link_state , byte , 0444 ) ;
MODULE_PARM_DESC ( set_vf_link_state , " Set vf link state, 0 represents link auto, 1 represents link always up, 2 represents link always down. - default is 0. " ) ;
# define HINIC_VLAN_PRIORITY_SHIFT 13
# define HINIC_ADD_VLAN_IN_MAC 0x8000
# define HINIC_TX_RATE_TABLE_FULL 12
static int hinic_set_mac ( struct hinic_hwdev * hwdev , const u8 * mac_addr ,
u16 vlan_id , u16 func_id )
{
struct hinic_port_mac_cmd mac_info = { 0 } ;
u16 out_size = sizeof ( mac_info ) ;
int err ;
mac_info . func_idx = func_id ;
mac_info . vlan_id = vlan_id ;
memcpy ( mac_info . mac , mac_addr , ETH_ALEN ) ;
err = hinic_port_msg_cmd ( hwdev , HINIC_PORT_CMD_SET_MAC , & mac_info ,
sizeof ( mac_info ) , & mac_info , & out_size ) ;
if ( err | | out_size ! = sizeof ( mac_info ) | |
( mac_info . status & & mac_info . status ! = HINIC_MGMT_STATUS_EXIST ) ) {
dev_err ( & hwdev - > func_to_io . hwif - > pdev - > dev , " Failed to set MAC, err: %d, status: 0x%x, out size: 0x%x \n " ,
err , mac_info . status , out_size ) ;
return - EIO ;
}
return 0 ;
}
static void hinic_notify_vf_link_status ( struct hinic_hwdev * hwdev , u16 vf_id ,
u8 link_status )
{
struct vf_data_storage * vf_infos = hwdev - > func_to_io . vf_infos ;
struct hinic_port_link_status link = { 0 } ;
u16 out_size = sizeof ( link ) ;
int err ;
if ( vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . registered ) {
link . link = link_status ;
link . func_id = hinic_glb_pf_vf_offset ( hwdev - > hwif ) + vf_id ;
err = hinic_mbox_to_vf ( hwdev , HINIC_MOD_L2NIC ,
vf_id , HINIC_PORT_CMD_LINK_STATUS_REPORT ,
& link , sizeof ( link ) ,
& link , & out_size , 0 ) ;
if ( err | | ! out_size | | link . status )
dev_err ( & hwdev - > hwif - > pdev - > dev ,
" Send link change event to VF %d failed, err: %d, status: 0x%x, out_size: 0x%x \n " ,
HW_VF_ID_TO_OS ( vf_id ) , err ,
link . status , out_size ) ;
}
}
/* send link change event mbox msg to active vfs under the pf */
void hinic_notify_all_vfs_link_changed ( struct hinic_hwdev * hwdev ,
u8 link_status )
{
struct hinic_func_to_io * nic_io = & hwdev - > func_to_io ;
u16 i ;
nic_io - > link_status = link_status ;
for ( i = 1 ; i < = nic_io - > max_vfs ; i + + ) {
if ( ! nic_io - > vf_infos [ HW_VF_ID_TO_OS ( i ) ] . link_forced )
hinic_notify_vf_link_status ( hwdev , i , link_status ) ;
}
}
static u16 hinic_vf_info_vlanprio ( struct hinic_hwdev * hwdev , int vf_id )
{
struct hinic_func_to_io * nic_io = & hwdev - > func_to_io ;
u16 pf_vlan , vlanprio ;
u8 pf_qos ;
pf_vlan = nic_io - > vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . pf_vlan ;
pf_qos = nic_io - > vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . pf_qos ;
vlanprio = pf_vlan | pf_qos < < HINIC_VLAN_PRIORITY_SHIFT ;
return vlanprio ;
}
static int hinic_set_vf_vlan ( struct hinic_hwdev * hwdev , bool add , u16 vid ,
u8 qos , int vf_id )
{
struct hinic_vf_vlan_config vf_vlan = { 0 } ;
u16 out_size = sizeof ( vf_vlan ) ;
int err ;
u8 cmd ;
/* VLAN 0 is a special case, don't allow it to be removed */
if ( ! vid & & ! add )
return 0 ;
vf_vlan . func_id = hinic_glb_pf_vf_offset ( hwdev - > hwif ) + vf_id ;
vf_vlan . vlan_id = vid ;
vf_vlan . qos = qos ;
if ( add )
cmd = HINIC_PORT_CMD_SET_VF_VLAN ;
else
cmd = HINIC_PORT_CMD_CLR_VF_VLAN ;
err = hinic_port_msg_cmd ( hwdev , cmd , & vf_vlan ,
sizeof ( vf_vlan ) , & vf_vlan , & out_size ) ;
if ( err | | ! out_size | | vf_vlan . status ) {
dev_err ( & hwdev - > hwif - > pdev - > dev , " Failed to set VF %d vlan, err: %d, status: 0x%x, out size: 0x%x \n " ,
HW_VF_ID_TO_OS ( vf_id ) , err , vf_vlan . status , out_size ) ;
return - EFAULT ;
}
return 0 ;
}
static int hinic_set_vf_tx_rate_max_min ( struct hinic_hwdev * hwdev , u16 vf_id ,
u32 max_rate , u32 min_rate )
{
struct hinic_func_to_io * nic_io = & hwdev - > func_to_io ;
struct hinic_tx_rate_cfg_max_min rate_cfg = { 0 } ;
u16 out_size = sizeof ( rate_cfg ) ;
int err ;
rate_cfg . func_id = hinic_glb_pf_vf_offset ( hwdev - > hwif ) + vf_id ;
rate_cfg . max_rate = max_rate ;
rate_cfg . min_rate = min_rate ;
err = hinic_port_msg_cmd ( hwdev , HINIC_PORT_CMD_SET_VF_MAX_MIN_RATE ,
& rate_cfg , sizeof ( rate_cfg ) , & rate_cfg ,
& out_size ) ;
if ( ( rate_cfg . status ! = HINIC_MGMT_CMD_UNSUPPORTED & &
rate_cfg . status ) | | err | | ! out_size ) {
dev_err ( & hwdev - > hwif - > pdev - > dev , " Failed to set VF(%d) max rate(%d), min rate(%d), err: %d, status: 0x%x, out size: 0x%x \n " ,
HW_VF_ID_TO_OS ( vf_id ) , max_rate , min_rate , err ,
rate_cfg . status , out_size ) ;
return - EIO ;
}
if ( ! rate_cfg . status ) {
nic_io - > vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . max_rate = max_rate ;
nic_io - > vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . min_rate = min_rate ;
}
return rate_cfg . status ;
}
static int hinic_set_vf_rate_limit ( struct hinic_hwdev * hwdev , u16 vf_id ,
u32 tx_rate )
{
struct hinic_func_to_io * nic_io = & hwdev - > func_to_io ;
struct hinic_tx_rate_cfg rate_cfg = { 0 } ;
u16 out_size = sizeof ( rate_cfg ) ;
int err ;
rate_cfg . func_id = hinic_glb_pf_vf_offset ( hwdev - > hwif ) + vf_id ;
rate_cfg . tx_rate = tx_rate ;
err = hinic_port_msg_cmd ( hwdev , HINIC_PORT_CMD_SET_VF_RATE ,
& rate_cfg , sizeof ( rate_cfg ) , & rate_cfg ,
& out_size ) ;
if ( err | | ! out_size | | rate_cfg . status ) {
dev_err ( & hwdev - > hwif - > pdev - > dev , " Failed to set VF(%d) rate(%d), err: %d, status: 0x%x, out size: 0x%x \n " ,
HW_VF_ID_TO_OS ( vf_id ) , tx_rate , err , rate_cfg . status ,
out_size ) ;
if ( rate_cfg . status )
return rate_cfg . status ;
return - EIO ;
}
nic_io - > vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . max_rate = tx_rate ;
nic_io - > vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . min_rate = 0 ;
return 0 ;
}
static int hinic_set_vf_tx_rate ( struct hinic_hwdev * hwdev , u16 vf_id ,
u32 max_rate , u32 min_rate )
{
int err ;
err = hinic_set_vf_tx_rate_max_min ( hwdev , vf_id , max_rate , min_rate ) ;
if ( err ! = HINIC_MGMT_CMD_UNSUPPORTED )
return err ;
if ( min_rate ) {
dev_err ( & hwdev - > hwif - > pdev - > dev , " Current firmware doesn't support to set min tx rate \n " ) ;
return - EOPNOTSUPP ;
}
dev_info ( & hwdev - > hwif - > pdev - > dev , " Current firmware doesn't support to set min tx rate, force min_tx_rate = max_tx_rate \n " ) ;
return hinic_set_vf_rate_limit ( hwdev , vf_id , max_rate ) ;
}
static int hinic_init_vf_config ( struct hinic_hwdev * hwdev , u16 vf_id )
{
struct vf_data_storage * vf_info ;
u16 func_id , vlan_id ;
int err = 0 ;
vf_info = hwdev - > func_to_io . vf_infos + HW_VF_ID_TO_OS ( vf_id ) ;
if ( vf_info - > pf_set_mac ) {
func_id = hinic_glb_pf_vf_offset ( hwdev - > hwif ) + vf_id ;
vlan_id = 0 ;
err = hinic_set_mac ( hwdev , vf_info - > vf_mac_addr , vlan_id ,
func_id ) ;
if ( err ) {
dev_err ( & hwdev - > func_to_io . hwif - > pdev - > dev , " Failed to set VF %d MAC \n " ,
HW_VF_ID_TO_OS ( vf_id ) ) ;
return err ;
}
}
if ( hinic_vf_info_vlanprio ( hwdev , vf_id ) ) {
err = hinic_set_vf_vlan ( hwdev , true , vf_info - > pf_vlan ,
vf_info - > pf_qos , vf_id ) ;
if ( err ) {
dev_err ( & hwdev - > hwif - > pdev - > dev , " Failed to add VF %d VLAN_QOS \n " ,
HW_VF_ID_TO_OS ( vf_id ) ) ;
return err ;
}
}
if ( vf_info - > max_rate ) {
err = hinic_set_vf_tx_rate ( hwdev , vf_id , vf_info - > max_rate ,
vf_info - > min_rate ) ;
if ( err ) {
dev_err ( & hwdev - > hwif - > pdev - > dev , " Failed to set VF %d max rate: %d, min rate: %d \n " ,
HW_VF_ID_TO_OS ( vf_id ) , vf_info - > max_rate ,
vf_info - > min_rate ) ;
return err ;
}
}
return 0 ;
}
static int hinic_register_vf_msg_handler ( void * hwdev , u16 vf_id ,
void * buf_in , u16 in_size ,
void * buf_out , u16 * out_size )
{
struct hinic_register_vf * register_info = buf_out ;
struct hinic_hwdev * hw_dev = hwdev ;
struct hinic_func_to_io * nic_io ;
int err ;
nic_io = & hw_dev - > func_to_io ;
if ( vf_id > nic_io - > max_vfs ) {
dev_err ( & hw_dev - > hwif - > pdev - > dev , " Register VF id %d exceed limit[0-%d] \n " ,
HW_VF_ID_TO_OS ( vf_id ) , HW_VF_ID_TO_OS ( nic_io - > max_vfs ) ) ;
register_info - > status = EFAULT ;
return - EFAULT ;
}
* out_size = sizeof ( * register_info ) ;
err = hinic_init_vf_config ( hw_dev , vf_id ) ;
if ( err ) {
register_info - > status = EFAULT ;
return err ;
}
nic_io - > vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . registered = true ;
return 0 ;
}
static int hinic_unregister_vf_msg_handler ( void * hwdev , u16 vf_id ,
void * buf_in , u16 in_size ,
void * buf_out , u16 * out_size )
{
struct hinic_hwdev * hw_dev = hwdev ;
struct hinic_func_to_io * nic_io ;
nic_io = & hw_dev - > func_to_io ;
* out_size = 0 ;
if ( vf_id > nic_io - > max_vfs )
return 0 ;
nic_io - > vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . registered = false ;
return 0 ;
}
static int hinic_change_vf_mtu_msg_handler ( void * hwdev , u16 vf_id ,
void * buf_in , u16 in_size ,
void * buf_out , u16 * out_size )
{
struct hinic_hwdev * hw_dev = hwdev ;
int err ;
err = hinic_port_msg_cmd ( hwdev , HINIC_PORT_CMD_CHANGE_MTU , buf_in ,
in_size , buf_out , out_size ) ;
if ( err ) {
dev_err ( & hw_dev - > hwif - > pdev - > dev , " Failed to set VF %u mtu \n " ,
vf_id ) ;
return err ;
}
return 0 ;
}
static int hinic_get_vf_mac_msg_handler ( void * hwdev , u16 vf_id ,
void * buf_in , u16 in_size ,
void * buf_out , u16 * out_size )
{
struct hinic_port_mac_cmd * mac_info = buf_out ;
struct hinic_hwdev * dev = hwdev ;
struct hinic_func_to_io * nic_io ;
struct vf_data_storage * vf_info ;
nic_io = & dev - > func_to_io ;
vf_info = nic_io - > vf_infos + HW_VF_ID_TO_OS ( vf_id ) ;
memcpy ( mac_info - > mac , vf_info - > vf_mac_addr , ETH_ALEN ) ;
mac_info - > status = 0 ;
* out_size = sizeof ( * mac_info ) ;
return 0 ;
}
static int hinic_set_vf_mac_msg_handler ( void * hwdev , u16 vf_id ,
void * buf_in , u16 in_size ,
void * buf_out , u16 * out_size )
{
struct hinic_port_mac_cmd * mac_out = buf_out ;
struct hinic_port_mac_cmd * mac_in = buf_in ;
struct hinic_hwdev * hw_dev = hwdev ;
struct hinic_func_to_io * nic_io ;
struct vf_data_storage * vf_info ;
int err ;
nic_io = & hw_dev - > func_to_io ;
vf_info = nic_io - > vf_infos + HW_VF_ID_TO_OS ( vf_id ) ;
if ( vf_info - > pf_set_mac & & ! ( vf_info - > trust ) & &
is_valid_ether_addr ( mac_in - > mac ) ) {
dev_warn ( & hw_dev - > hwif - > pdev - > dev , " PF has already set VF %d MAC address \n " ,
HW_VF_ID_TO_OS ( vf_id ) ) ;
mac_out - > status = HINIC_PF_SET_VF_ALREADY ;
* out_size = sizeof ( * mac_out ) ;
return 0 ;
}
err = hinic_port_msg_cmd ( hw_dev , HINIC_PORT_CMD_SET_MAC , buf_in ,
in_size , buf_out , out_size ) ;
if ( ( err & & err ! = HINIC_MBOX_PF_BUSY_ACTIVE_FW ) | | ! ( * out_size ) ) {
dev_err ( & hw_dev - > hwif - > pdev - > dev ,
" Failed to set VF %d MAC address, err: %d, status: 0x%x, out size: 0x%x \n " ,
HW_VF_ID_TO_OS ( vf_id ) , err , mac_out - > status , * out_size ) ;
return - EFAULT ;
}
return err ;
}
static int hinic_del_vf_mac_msg_handler ( void * hwdev , u16 vf_id ,
void * buf_in , u16 in_size ,
void * buf_out , u16 * out_size )
{
struct hinic_port_mac_cmd * mac_out = buf_out ;
struct hinic_port_mac_cmd * mac_in = buf_in ;
struct hinic_hwdev * hw_dev = hwdev ;
struct hinic_func_to_io * nic_io ;
struct vf_data_storage * vf_info ;
int err ;
nic_io = & hw_dev - > func_to_io ;
vf_info = nic_io - > vf_infos + HW_VF_ID_TO_OS ( vf_id ) ;
if ( vf_info - > pf_set_mac & & is_valid_ether_addr ( mac_in - > mac ) & &
! memcmp ( vf_info - > vf_mac_addr , mac_in - > mac , ETH_ALEN ) ) {
dev_warn ( & hw_dev - > hwif - > pdev - > dev , " PF has already set VF mac. \n " ) ;
mac_out - > status = HINIC_PF_SET_VF_ALREADY ;
* out_size = sizeof ( * mac_out ) ;
return 0 ;
}
err = hinic_port_msg_cmd ( hw_dev , HINIC_PORT_CMD_DEL_MAC , buf_in ,
in_size , buf_out , out_size ) ;
if ( ( err & & err ! = HINIC_MBOX_PF_BUSY_ACTIVE_FW ) | | ! ( * out_size ) ) {
dev_err ( & hw_dev - > hwif - > pdev - > dev , " Failed to delete VF %d MAC, err: %d, status: 0x%x, out size: 0x%x \n " ,
HW_VF_ID_TO_OS ( vf_id ) , err , mac_out - > status , * out_size ) ;
return - EFAULT ;
}
return err ;
}
static int hinic_get_vf_link_status_msg_handler ( void * hwdev , u16 vf_id ,
void * buf_in , u16 in_size ,
void * buf_out , u16 * out_size )
{
struct hinic_port_link_cmd * get_link = buf_out ;
struct hinic_hwdev * hw_dev = hwdev ;
struct vf_data_storage * vf_infos ;
struct hinic_func_to_io * nic_io ;
bool link_forced , link_up ;
nic_io = & hw_dev - > func_to_io ;
vf_infos = nic_io - > vf_infos ;
link_forced = vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . link_forced ;
link_up = vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . link_up ;
if ( link_forced )
get_link - > state = link_up ?
HINIC_LINK_STATE_UP : HINIC_LINK_STATE_DOWN ;
else
get_link - > state = nic_io - > link_status ;
get_link - > status = 0 ;
* out_size = sizeof ( * get_link ) ;
return 0 ;
}
static bool check_func_table ( struct hinic_hwdev * hwdev , u16 func_idx ,
void * buf_in , u16 in_size )
{
struct hinic_cmd_fw_ctxt * function_table = buf_in ;
if ( ! hinic_mbox_check_func_id_8B ( hwdev , func_idx , buf_in , in_size ) | |
! function_table - > rx_buf_sz )
return false ;
return true ;
}
static struct vf_cmd_msg_handle nic_vf_cmd_msg_handler [ ] = {
{ HINIC_PORT_CMD_VF_REGISTER , hinic_register_vf_msg_handler } ,
{ HINIC_PORT_CMD_VF_UNREGISTER , hinic_unregister_vf_msg_handler } ,
{ HINIC_PORT_CMD_CHANGE_MTU , hinic_change_vf_mtu_msg_handler } ,
{ HINIC_PORT_CMD_GET_MAC , hinic_get_vf_mac_msg_handler } ,
{ HINIC_PORT_CMD_SET_MAC , hinic_set_vf_mac_msg_handler } ,
{ HINIC_PORT_CMD_DEL_MAC , hinic_del_vf_mac_msg_handler } ,
{ HINIC_PORT_CMD_GET_LINK_STATE , hinic_get_vf_link_status_msg_handler } ,
} ;
static struct vf_cmd_check_handle nic_cmd_support_vf [ ] = {
{ HINIC_PORT_CMD_VF_REGISTER , NULL } ,
{ HINIC_PORT_CMD_VF_UNREGISTER , NULL } ,
{ HINIC_PORT_CMD_CHANGE_MTU , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_ADD_VLAN , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_DEL_VLAN , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_SET_MAC , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_GET_MAC , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_DEL_MAC , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_SET_RX_MODE , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_GET_PAUSE_INFO , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_GET_LINK_STATE , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_SET_LRO , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_SET_RX_CSUM , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_SET_RX_VLAN_OFFLOAD , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_GET_VPORT_STAT , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_CLEAN_VPORT_STAT , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_GET_RSS_TEMPLATE_INDIR_TBL ,
hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_SET_RSS_TEMPLATE_TBL , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_GET_RSS_TEMPLATE_TBL , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_SET_RSS_HASH_ENGINE , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_GET_RSS_HASH_ENGINE , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_GET_RSS_CTX_TBL , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_SET_RSS_CTX_TBL , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_RSS_TEMP_MGR , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_RSS_CFG , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_FWCTXT_INIT , check_func_table } ,
{ HINIC_PORT_CMD_GET_MGMT_VERSION , NULL } ,
{ HINIC_PORT_CMD_SET_FUNC_STATE , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_GET_GLOBAL_QPN , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_SET_TSO , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_SET_RQ_IQ_MAP , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_LINK_STATUS_REPORT , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_UPDATE_MAC , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_GET_CAP , hinic_mbox_check_func_id_8B } ,
{ HINIC_PORT_CMD_GET_LINK_MODE , hinic_mbox_check_func_id_8B } ,
} ;
# define CHECK_IPSU_15BIT 0X8000
static
struct hinic_sriov_info * hinic_get_sriov_info_by_pcidev ( struct pci_dev * pdev )
{
struct net_device * netdev = pci_get_drvdata ( pdev ) ;
struct hinic_dev * nic_dev = netdev_priv ( netdev ) ;
return & nic_dev - > sriov_info ;
}
static int hinic_check_mac_info ( u8 status , u16 vlan_id )
{
if ( ( status & & status ! = HINIC_MGMT_STATUS_EXIST ) | |
( vlan_id & CHECK_IPSU_15BIT & &
status = = HINIC_MGMT_STATUS_EXIST ) )
return - EINVAL ;
return 0 ;
}
# define HINIC_VLAN_ID_MASK 0x7FFF
static int hinic_update_mac ( struct hinic_hwdev * hwdev , u8 * old_mac ,
u8 * new_mac , u16 vlan_id , u16 func_id )
{
struct hinic_port_mac_update mac_info = { 0 } ;
u16 out_size = sizeof ( mac_info ) ;
int err ;
if ( ! hwdev | | ! old_mac | | ! new_mac )
return - EINVAL ;
if ( ( vlan_id & HINIC_VLAN_ID_MASK ) > = VLAN_N_VID ) {
dev_err ( & hwdev - > hwif - > pdev - > dev , " Invalid VLAN number: %d \n " ,
( vlan_id & HINIC_VLAN_ID_MASK ) ) ;
return - EINVAL ;
}
mac_info . func_id = func_id ;
mac_info . vlan_id = vlan_id ;
memcpy ( mac_info . old_mac , old_mac , ETH_ALEN ) ;
memcpy ( mac_info . new_mac , new_mac , ETH_ALEN ) ;
err = hinic_port_msg_cmd ( hwdev , HINIC_PORT_CMD_UPDATE_MAC , & mac_info ,
sizeof ( mac_info ) , & mac_info , & out_size ) ;
if ( err | | ! out_size | |
hinic_check_mac_info ( mac_info . status , mac_info . vlan_id ) ) {
dev_err ( & hwdev - > hwif - > pdev - > dev ,
" Failed to update MAC, err: %d, status: 0x%x, out size: 0x%x \n " ,
err , mac_info . status , out_size ) ;
return - EINVAL ;
}
if ( mac_info . status = = HINIC_MGMT_STATUS_EXIST )
dev_warn ( & hwdev - > hwif - > pdev - > dev , " MAC is repeated. Ignore update operation \n " ) ;
return 0 ;
}
static void hinic_get_vf_config ( struct hinic_hwdev * hwdev , u16 vf_id ,
struct ifla_vf_info * ivi )
{
struct vf_data_storage * vfinfo ;
vfinfo = hwdev - > func_to_io . vf_infos + HW_VF_ID_TO_OS ( vf_id ) ;
ivi - > vf = HW_VF_ID_TO_OS ( vf_id ) ;
memcpy ( ivi - > mac , vfinfo - > vf_mac_addr , ETH_ALEN ) ;
ivi - > vlan = vfinfo - > pf_vlan ;
ivi - > qos = vfinfo - > pf_qos ;
ivi - > spoofchk = vfinfo - > spoofchk ;
ivi - > trusted = vfinfo - > trust ;
ivi - > max_tx_rate = vfinfo - > max_rate ;
ivi - > min_tx_rate = vfinfo - > min_rate ;
if ( ! vfinfo - > link_forced )
ivi - > linkstate = IFLA_VF_LINK_STATE_AUTO ;
else if ( vfinfo - > link_up )
ivi - > linkstate = IFLA_VF_LINK_STATE_ENABLE ;
else
ivi - > linkstate = IFLA_VF_LINK_STATE_DISABLE ;
}
int hinic_ndo_get_vf_config ( struct net_device * netdev ,
int vf , struct ifla_vf_info * ivi )
{
struct hinic_dev * nic_dev = netdev_priv ( netdev ) ;
struct hinic_sriov_info * sriov_info ;
sriov_info = & nic_dev - > sriov_info ;
if ( vf > = sriov_info - > num_vfs )
return - EINVAL ;
hinic_get_vf_config ( sriov_info - > hwdev , OS_VF_ID_TO_HW ( vf ) , ivi ) ;
return 0 ;
}
static int hinic_set_vf_mac ( struct hinic_hwdev * hwdev , int vf ,
unsigned char * mac_addr )
{
struct hinic_func_to_io * nic_io = & hwdev - > func_to_io ;
struct vf_data_storage * vf_info ;
u16 func_id ;
int err ;
vf_info = nic_io - > vf_infos + HW_VF_ID_TO_OS ( vf ) ;
/* duplicate request, so just return success */
if ( vf_info - > pf_set_mac & &
! memcmp ( vf_info - > vf_mac_addr , mac_addr , ETH_ALEN ) )
return 0 ;
vf_info - > pf_set_mac = true ;
func_id = hinic_glb_pf_vf_offset ( hwdev - > hwif ) + vf ;
err = hinic_update_mac ( hwdev , vf_info - > vf_mac_addr ,
mac_addr , 0 , func_id ) ;
if ( err ) {
vf_info - > pf_set_mac = false ;
return err ;
}
memcpy ( vf_info - > vf_mac_addr , mac_addr , ETH_ALEN ) ;
return 0 ;
}
int hinic_ndo_set_vf_mac ( struct net_device * netdev , int vf , u8 * mac )
{
struct hinic_dev * nic_dev = netdev_priv ( netdev ) ;
struct hinic_sriov_info * sriov_info ;
int err ;
sriov_info = & nic_dev - > sriov_info ;
if ( ! is_valid_ether_addr ( mac ) | | vf > = sriov_info - > num_vfs )
return - EINVAL ;
err = hinic_set_vf_mac ( sriov_info - > hwdev , OS_VF_ID_TO_HW ( vf ) , mac ) ;
if ( err )
return err ;
netif_info ( nic_dev , drv , netdev , " Setting MAC %pM on VF %d \n " , mac , vf ) ;
netif_info ( nic_dev , drv , netdev , " Reload the VF driver to make this change effective. " ) ;
return 0 ;
}
static int hinic_add_vf_vlan ( struct hinic_hwdev * hwdev , int vf_id ,
u16 vlan , u8 qos )
{
struct hinic_func_to_io * nic_io = & hwdev - > func_to_io ;
int err ;
err = hinic_set_vf_vlan ( hwdev , true , vlan , qos , vf_id ) ;
if ( err )
return err ;
nic_io - > vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . pf_vlan = vlan ;
nic_io - > vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . pf_qos = qos ;
dev_info ( & hwdev - > hwif - > pdev - > dev , " Setting VLAN %d, QOS 0x%x on VF %d \n " ,
vlan , qos , HW_VF_ID_TO_OS ( vf_id ) ) ;
return 0 ;
}
static int hinic_kill_vf_vlan ( struct hinic_hwdev * hwdev , int vf_id )
{
struct hinic_func_to_io * nic_io = & hwdev - > func_to_io ;
int err ;
err = hinic_set_vf_vlan ( hwdev , false ,
nic_io - > vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . pf_vlan ,
nic_io - > vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . pf_qos ,
vf_id ) ;
if ( err )
return err ;
dev_info ( & hwdev - > hwif - > pdev - > dev , " Remove VLAN %d on VF %d \n " ,
nic_io - > vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . pf_vlan ,
HW_VF_ID_TO_OS ( vf_id ) ) ;
nic_io - > vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . pf_vlan = 0 ;
nic_io - > vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . pf_qos = 0 ;
return 0 ;
}
static int hinic_update_mac_vlan ( struct hinic_dev * nic_dev , u16 old_vlan ,
u16 new_vlan , int vf_id )
{
struct vf_data_storage * vf_info ;
u16 vlan_id ;
int err ;
if ( ! nic_dev | | old_vlan > = VLAN_N_VID | | new_vlan > = VLAN_N_VID )
return - EINVAL ;
vf_info = nic_dev - > hwdev - > func_to_io . vf_infos + HW_VF_ID_TO_OS ( vf_id ) ;
if ( ! vf_info - > pf_set_mac )
return 0 ;
vlan_id = old_vlan ;
if ( vlan_id )
vlan_id | = HINIC_ADD_VLAN_IN_MAC ;
err = hinic_port_del_mac ( nic_dev , vf_info - > vf_mac_addr , vlan_id ) ;
if ( err ) {
dev_err ( & nic_dev - > hwdev - > hwif - > pdev - > dev , " Failed to delete VF %d MAC %pM vlan %d \n " ,
HW_VF_ID_TO_OS ( vf_id ) , vf_info - > vf_mac_addr , old_vlan ) ;
return err ;
}
vlan_id = new_vlan ;
if ( vlan_id )
vlan_id | = HINIC_ADD_VLAN_IN_MAC ;
err = hinic_port_add_mac ( nic_dev , vf_info - > vf_mac_addr , vlan_id ) ;
if ( err ) {
dev_err ( & nic_dev - > hwdev - > hwif - > pdev - > dev , " Failed to add VF %d MAC %pM vlan %d \n " ,
HW_VF_ID_TO_OS ( vf_id ) , vf_info - > vf_mac_addr , new_vlan ) ;
goto out ;
}
return 0 ;
out :
vlan_id = old_vlan ;
if ( vlan_id )
vlan_id | = HINIC_ADD_VLAN_IN_MAC ;
hinic_port_add_mac ( nic_dev , vf_info - > vf_mac_addr , vlan_id ) ;
return err ;
}
static int set_hw_vf_vlan ( struct hinic_dev * nic_dev ,
u16 cur_vlanprio , int vf , u16 vlan , u8 qos )
{
u16 old_vlan = cur_vlanprio & VLAN_VID_MASK ;
int err = 0 ;
if ( vlan | | qos ) {
if ( cur_vlanprio ) {
err = hinic_kill_vf_vlan ( nic_dev - > hwdev ,
OS_VF_ID_TO_HW ( vf ) ) ;
if ( err ) {
dev_err ( & nic_dev - > sriov_info . pdev - > dev , " Failed to delete vf %d old vlan %d \n " ,
vf , old_vlan ) ;
goto out ;
}
}
err = hinic_add_vf_vlan ( nic_dev - > hwdev ,
OS_VF_ID_TO_HW ( vf ) , vlan , qos ) ;
if ( err ) {
dev_err ( & nic_dev - > sriov_info . pdev - > dev , " Failed to add vf %d new vlan %d \n " ,
vf , vlan ) ;
goto out ;
}
} else {
err = hinic_kill_vf_vlan ( nic_dev - > hwdev , OS_VF_ID_TO_HW ( vf ) ) ;
if ( err ) {
dev_err ( & nic_dev - > sriov_info . pdev - > dev , " Failed to delete vf %d vlan %d \n " ,
vf , old_vlan ) ;
goto out ;
}
}
err = hinic_update_mac_vlan ( nic_dev , old_vlan , vlan ,
OS_VF_ID_TO_HW ( vf ) ) ;
out :
return err ;
}
int hinic_ndo_set_vf_vlan ( struct net_device * netdev , int vf , u16 vlan , u8 qos ,
__be16 vlan_proto )
{
struct hinic_dev * nic_dev = netdev_priv ( netdev ) ;
struct hinic_sriov_info * sriov_info ;
u16 vlanprio , cur_vlanprio ;
sriov_info = & nic_dev - > sriov_info ;
if ( vf > = sriov_info - > num_vfs | | vlan > 4095 | | qos > 7 )
return - EINVAL ;
if ( vlan_proto ! = htons ( ETH_P_8021Q ) )
return - EPROTONOSUPPORT ;
vlanprio = vlan | qos < < HINIC_VLAN_PRIORITY_SHIFT ;
cur_vlanprio = hinic_vf_info_vlanprio ( nic_dev - > hwdev ,
OS_VF_ID_TO_HW ( vf ) ) ;
/* duplicate request, so just return success */
if ( vlanprio = = cur_vlanprio )
return 0 ;
return set_hw_vf_vlan ( nic_dev , cur_vlanprio , vf , vlan , qos ) ;
}
static int hinic_set_vf_trust ( struct hinic_hwdev * hwdev , u16 vf_id ,
bool trust )
{
struct vf_data_storage * vf_infos ;
struct hinic_func_to_io * nic_io ;
if ( ! hwdev )
return - EINVAL ;
nic_io = & hwdev - > func_to_io ;
vf_infos = nic_io - > vf_infos ;
vf_infos [ vf_id ] . trust = trust ;
return 0 ;
}
int hinic_ndo_set_vf_trust ( struct net_device * netdev , int vf , bool setting )
{
struct hinic_dev * adapter = netdev_priv ( netdev ) ;
struct hinic_sriov_info * sriov_info ;
struct hinic_func_to_io * nic_io ;
bool cur_trust ;
int err ;
sriov_info = & adapter - > sriov_info ;
nic_io = & adapter - > hwdev - > func_to_io ;
if ( vf > = sriov_info - > num_vfs )
return - EINVAL ;
cur_trust = nic_io - > vf_infos [ vf ] . trust ;
/* same request, so just return success */
if ( ( setting & & cur_trust ) | | ( ! setting & & ! cur_trust ) )
return 0 ;
err = hinic_set_vf_trust ( adapter - > hwdev , vf , setting ) ;
if ( ! err )
dev_info ( & sriov_info - > pdev - > dev , " Set VF %d trusted %s succeed \n " ,
vf , setting ? " on " : " off " ) ;
else
dev_err ( & sriov_info - > pdev - > dev , " Failed set VF %d trusted %s \n " ,
vf , setting ? " on " : " off " ) ;
return err ;
}
int hinic_ndo_set_vf_bw ( struct net_device * netdev ,
int vf , int min_tx_rate , int max_tx_rate )
{
static const u32 speeds [ ] = {
SPEED_10 , SPEED_100 , SPEED_1000 , SPEED_10000 ,
SPEED_25000 , SPEED_40000 , SPEED_100000
} ;
struct hinic_dev * nic_dev = netdev_priv ( netdev ) ;
struct hinic_port_cap port_cap = { 0 } ;
enum hinic_port_link_state link_state ;
int err ;
if ( vf > = nic_dev - > sriov_info . num_vfs ) {
netif_err ( nic_dev , drv , netdev , " VF number must be less than %d \n " ,
nic_dev - > sriov_info . num_vfs ) ;
return - EINVAL ;
}
if ( max_tx_rate < min_tx_rate ) {
netif_err ( nic_dev , drv , netdev , " Max rate %d must be greater than or equal to min rate %d \n " ,
max_tx_rate , min_tx_rate ) ;
return - EINVAL ;
}
err = hinic_port_link_state ( nic_dev , & link_state ) ;
if ( err ) {
netif_err ( nic_dev , drv , netdev ,
" Get link status failed when setting vf tx rate \n " ) ;
return - EIO ;
}
if ( link_state = = HINIC_LINK_STATE_DOWN ) {
netif_err ( nic_dev , drv , netdev ,
" Link status must be up when setting vf tx rate \n " ) ;
return - EPERM ;
}
err = hinic_port_get_cap ( nic_dev , & port_cap ) ;
if ( err | | port_cap . speed > LINK_SPEED_100GB )
return - EIO ;
/* rate limit cannot be less than 0 and greater than link speed */
if ( max_tx_rate < 0 | | max_tx_rate > speeds [ port_cap . speed ] ) {
netif_err ( nic_dev , drv , netdev , " Max tx rate must be in [0 - %d] \n " ,
speeds [ port_cap . speed ] ) ;
return - EINVAL ;
}
err = hinic_set_vf_tx_rate ( nic_dev - > hwdev , OS_VF_ID_TO_HW ( vf ) ,
max_tx_rate , min_tx_rate ) ;
if ( err ) {
netif_err ( nic_dev , drv , netdev ,
" Unable to set VF %d max rate %d min rate %d%s \n " ,
vf , max_tx_rate , min_tx_rate ,
err = = HINIC_TX_RATE_TABLE_FULL ?
" , tx rate profile is full " : " " ) ;
return - EIO ;
}
netif_info ( nic_dev , drv , netdev ,
" Set VF %d max tx rate %d min tx rate %d successfully \n " ,
vf , max_tx_rate , min_tx_rate ) ;
return 0 ;
}
static int hinic_set_vf_spoofchk ( struct hinic_hwdev * hwdev , u16 vf_id ,
bool spoofchk )
{
struct hinic_spoofchk_set spoofchk_cfg = { 0 } ;
struct vf_data_storage * vf_infos = NULL ;
u16 out_size = sizeof ( spoofchk_cfg ) ;
int err ;
if ( ! hwdev )
return - EINVAL ;
vf_infos = hwdev - > func_to_io . vf_infos ;
spoofchk_cfg . func_id = hinic_glb_pf_vf_offset ( hwdev - > hwif ) + vf_id ;
spoofchk_cfg . state = spoofchk ? 1 : 0 ;
err = hinic_port_msg_cmd ( hwdev , HINIC_PORT_CMD_ENABLE_SPOOFCHK ,
& spoofchk_cfg , sizeof ( spoofchk_cfg ) ,
& spoofchk_cfg , & out_size ) ;
if ( spoofchk_cfg . status = = HINIC_MGMT_CMD_UNSUPPORTED ) {
err = HINIC_MGMT_CMD_UNSUPPORTED ;
} else if ( err | | ! out_size | | spoofchk_cfg . status ) {
dev_err ( & hwdev - > hwif - > pdev - > dev , " Failed to set VF(%d) spoofchk, err: %d, status: 0x%x, out size: 0x%x \n " ,
HW_VF_ID_TO_OS ( vf_id ) , err , spoofchk_cfg . status ,
out_size ) ;
err = - EIO ;
}
vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . spoofchk = spoofchk ;
return err ;
}
int hinic_ndo_set_vf_spoofchk ( struct net_device * netdev , int vf , bool setting )
{
struct hinic_dev * nic_dev = netdev_priv ( netdev ) ;
struct hinic_sriov_info * sriov_info ;
bool cur_spoofchk ;
int err ;
sriov_info = & nic_dev - > sriov_info ;
if ( vf > = sriov_info - > num_vfs )
return - EINVAL ;
cur_spoofchk = nic_dev - > hwdev - > func_to_io . vf_infos [ vf ] . spoofchk ;
/* same request, so just return success */
if ( ( setting & & cur_spoofchk ) | | ( ! setting & & ! cur_spoofchk ) )
return 0 ;
err = hinic_set_vf_spoofchk ( sriov_info - > hwdev ,
OS_VF_ID_TO_HW ( vf ) , setting ) ;
if ( ! err ) {
netif_info ( nic_dev , drv , netdev , " Set VF %d spoofchk %s successfully \n " ,
vf , setting ? " on " : " off " ) ;
} else if ( err = = HINIC_MGMT_CMD_UNSUPPORTED ) {
netif_err ( nic_dev , drv , netdev ,
" Current firmware doesn't support to set vf spoofchk, need to upgrade latest firmware version \n " ) ;
err = - EOPNOTSUPP ;
}
return err ;
}
static int hinic_set_vf_link_state ( struct hinic_hwdev * hwdev , u16 vf_id ,
int link )
{
struct hinic_func_to_io * nic_io = & hwdev - > func_to_io ;
struct vf_data_storage * vf_infos = nic_io - > vf_infos ;
u8 link_status = 0 ;
switch ( link ) {
case HINIC_IFLA_VF_LINK_STATE_AUTO :
vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . link_forced = false ;
vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . link_up = nic_io - > link_status ?
true : false ;
link_status = nic_io - > link_status ;
break ;
case HINIC_IFLA_VF_LINK_STATE_ENABLE :
vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . link_forced = true ;
vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . link_up = true ;
link_status = HINIC_LINK_UP ;
break ;
case HINIC_IFLA_VF_LINK_STATE_DISABLE :
vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . link_forced = true ;
vf_infos [ HW_VF_ID_TO_OS ( vf_id ) ] . link_up = false ;
link_status = HINIC_LINK_DOWN ;
break ;
default :
return - EINVAL ;
}
/* Notify the VF of its new link state */
hinic_notify_vf_link_status ( hwdev , vf_id , link_status ) ;
return 0 ;
}
int hinic_ndo_set_vf_link_state ( struct net_device * netdev , int vf_id , int link )
{
struct hinic_dev * nic_dev = netdev_priv ( netdev ) ;
struct hinic_sriov_info * sriov_info ;
sriov_info = & nic_dev - > sriov_info ;
if ( vf_id > = sriov_info - > num_vfs ) {
netif_err ( nic_dev , drv , netdev ,
" Invalid VF Identifier %d \n " , vf_id ) ;
return - EINVAL ;
}
return hinic_set_vf_link_state ( sriov_info - > hwdev ,
OS_VF_ID_TO_HW ( vf_id ) , link ) ;
}
/* pf receive message from vf */
static int nic_pf_mbox_handler ( void * hwdev , u16 vf_id , u8 cmd , void * buf_in ,
u16 in_size , void * buf_out , u16 * out_size )
{
u8 size = ARRAY_SIZE ( nic_cmd_support_vf ) ;
struct vf_cmd_msg_handle * vf_msg_handle ;
struct hinic_hwdev * dev = hwdev ;
struct hinic_func_to_io * nic_io ;
struct hinic_pfhwdev * pfhwdev ;
int err = 0 ;
u32 i ;
if ( ! hwdev )
return - EINVAL ;
if ( ! hinic_mbox_check_cmd_valid ( hwdev , nic_cmd_support_vf , vf_id , cmd ,
buf_in , in_size , size ) ) {
dev_err ( & dev - > hwif - > pdev - > dev ,
" PF Receive VF nic cmd: 0x%x, mbox len: 0x%x is invalid \n " ,
cmd , in_size ) ;
return HINIC_MBOX_VF_CMD_ERROR ;
}
pfhwdev = container_of ( dev , struct hinic_pfhwdev , hwdev ) ;
nic_io = & dev - > func_to_io ;
for ( i = 0 ; i < ARRAY_SIZE ( nic_vf_cmd_msg_handler ) ; i + + ) {
vf_msg_handle = & nic_vf_cmd_msg_handler [ i ] ;
if ( cmd = = vf_msg_handle - > cmd & &
vf_msg_handle - > cmd_msg_handler ) {
err = vf_msg_handle - > cmd_msg_handler ( hwdev , vf_id ,
buf_in , in_size ,
buf_out ,
out_size ) ;
break ;
}
}
if ( i = = ARRAY_SIZE ( nic_vf_cmd_msg_handler ) )
err = hinic_msg_to_mgmt ( & pfhwdev - > pf_to_mgmt , HINIC_MOD_L2NIC ,
cmd , buf_in , in_size , buf_out ,
out_size , HINIC_MGMT_MSG_SYNC ) ;
if ( err & & err ! = HINIC_MBOX_PF_BUSY_ACTIVE_FW )
dev_err ( & nic_io - > hwif - > pdev - > dev , " PF receive VF L2NIC cmd: %d process error, err:%d \n " ,
cmd , err ) ;
return err ;
}
static int cfg_mbx_pf_proc_vf_msg ( void * hwdev , u16 vf_id , u8 cmd , void * buf_in ,
u16 in_size , void * buf_out , u16 * out_size )
{
struct hinic_dev_cap * dev_cap = buf_out ;
struct hinic_hwdev * dev = hwdev ;
struct hinic_cap * cap ;
cap = & dev - > nic_cap ;
memset ( dev_cap , 0 , sizeof ( * dev_cap ) ) ;
dev_cap - > max_vf = cap - > max_vf ;
dev_cap - > max_sqs = cap - > max_vf_qps ;
dev_cap - > max_rqs = cap - > max_vf_qps ;
dev_cap - > port_id = dev - > port_id ;
* out_size = sizeof ( * dev_cap ) ;
return 0 ;
}
static int hinic_init_vf_infos ( struct hinic_func_to_io * nic_io , u16 vf_id )
{
struct vf_data_storage * vf_infos = nic_io - > vf_infos ;
if ( set_vf_link_state > HINIC_IFLA_VF_LINK_STATE_DISABLE ) {
dev_warn ( & nic_io - > hwif - > pdev - > dev , " Module Parameter set_vf_link_state value %d is out of range, resetting to %d \n " ,
set_vf_link_state , HINIC_IFLA_VF_LINK_STATE_AUTO ) ;
set_vf_link_state = HINIC_IFLA_VF_LINK_STATE_AUTO ;
}
switch ( set_vf_link_state ) {
case HINIC_IFLA_VF_LINK_STATE_AUTO :
vf_infos [ vf_id ] . link_forced = false ;
break ;
case HINIC_IFLA_VF_LINK_STATE_ENABLE :
vf_infos [ vf_id ] . link_forced = true ;
vf_infos [ vf_id ] . link_up = true ;
break ;
case HINIC_IFLA_VF_LINK_STATE_DISABLE :
vf_infos [ vf_id ] . link_forced = true ;
vf_infos [ vf_id ] . link_up = false ;
break ;
default :
dev_err ( & nic_io - > hwif - > pdev - > dev , " Invalid input parameter set_vf_link_state: %d \n " ,
set_vf_link_state ) ;
return - EINVAL ;
}
return 0 ;
}
static void hinic_clear_vf_infos ( struct hinic_dev * nic_dev , u16 vf_id )
{
struct vf_data_storage * vf_infos ;
vf_infos = nic_dev - > hwdev - > func_to_io . vf_infos + HW_VF_ID_TO_OS ( vf_id ) ;
if ( vf_infos - > pf_set_mac )
hinic_port_del_mac ( nic_dev , vf_infos - > vf_mac_addr , 0 ) ;
if ( hinic_vf_info_vlanprio ( nic_dev - > hwdev , vf_id ) )
hinic_kill_vf_vlan ( nic_dev - > hwdev , vf_id ) ;
if ( vf_infos - > max_rate )
hinic_set_vf_tx_rate ( nic_dev - > hwdev , vf_id , 0 , 0 ) ;
if ( vf_infos - > spoofchk )
hinic_set_vf_spoofchk ( nic_dev - > hwdev , vf_id , false ) ;
if ( vf_infos - > trust )
hinic_set_vf_trust ( nic_dev - > hwdev , vf_id , false ) ;
memset ( vf_infos , 0 , sizeof ( * vf_infos ) ) ;
/* set vf_infos to default */
hinic_init_vf_infos ( & nic_dev - > hwdev - > func_to_io , HW_VF_ID_TO_OS ( vf_id ) ) ;
}
static int hinic_deinit_vf_hw ( struct hinic_sriov_info * sriov_info ,
u16 start_vf_id , u16 end_vf_id )
{
struct hinic_dev * nic_dev ;
u16 func_idx , idx ;
nic_dev = container_of ( sriov_info , struct hinic_dev , sriov_info ) ;
for ( idx = start_vf_id ; idx < = end_vf_id ; idx + + ) {
func_idx = hinic_glb_pf_vf_offset ( nic_dev - > hwdev - > hwif ) + idx ;
hinic_set_wq_page_size ( nic_dev - > hwdev , func_idx ,
HINIC_HW_WQ_PAGE_SIZE ) ;
hinic_clear_vf_infos ( nic_dev , idx ) ;
}
return 0 ;
}
int hinic_vf_func_init ( struct hinic_hwdev * hwdev )
{
struct hinic_register_vf register_info = { 0 } ;
u16 out_size = sizeof ( register_info ) ;
struct hinic_func_to_io * nic_io ;
int err = 0 ;
u32 size , i ;
err = hinic_vf_mbox_random_id_init ( hwdev ) ;
if ( err ) {
dev_err ( & hwdev - > hwif - > pdev - > dev , " Failed to init vf mbox random id, err: %d \n " ,
err ) ;
return err ;
}
nic_io = & hwdev - > func_to_io ;
if ( HINIC_IS_VF ( hwdev - > hwif ) ) {
err = hinic_mbox_to_pf ( hwdev , HINIC_MOD_L2NIC ,
HINIC_PORT_CMD_VF_REGISTER ,
& register_info , sizeof ( register_info ) ,
& register_info , & out_size , 0 ) ;
if ( err | | register_info . status | | ! out_size ) {
dev_err ( & hwdev - > hwif - > pdev - > dev ,
" Failed to register VF, err: %d, status: 0x%x, out size: 0x%x \n " ,
err , register_info . status , out_size ) ;
hinic_unregister_vf_mbox_cb ( hwdev , HINIC_MOD_L2NIC ) ;
return - EIO ;
}
} else {
err = hinic_register_pf_mbox_cb ( hwdev , HINIC_MOD_CFGM ,
cfg_mbx_pf_proc_vf_msg ) ;
if ( err ) {
dev_err ( & hwdev - > hwif - > pdev - > dev ,
" Register PF mailbox callback failed \n " ) ;
return err ;
}
nic_io - > max_vfs = hwdev - > nic_cap . max_vf ;
size = sizeof ( * nic_io - > vf_infos ) * nic_io - > max_vfs ;
if ( size ! = 0 ) {
nic_io - > vf_infos = kzalloc ( size , GFP_KERNEL ) ;
if ( ! nic_io - > vf_infos ) {
err = - ENOMEM ;
goto out_free_nic_io ;
}
for ( i = 0 ; i < nic_io - > max_vfs ; i + + ) {
err = hinic_init_vf_infos ( nic_io , i ) ;
if ( err )
goto err_init_vf_infos ;
}
err = hinic_register_pf_mbox_cb ( hwdev , HINIC_MOD_L2NIC ,
nic_pf_mbox_handler ) ;
if ( err )
goto err_register_pf_mbox_cb ;
}
}
return 0 ;
err_register_pf_mbox_cb :
err_init_vf_infos :
kfree ( nic_io - > vf_infos ) ;
out_free_nic_io :
return err ;
}
void hinic_vf_func_free ( struct hinic_hwdev * hwdev )
{
struct hinic_register_vf unregister = { 0 } ;
u16 out_size = sizeof ( unregister ) ;
int err ;
if ( HINIC_IS_VF ( hwdev - > hwif ) ) {
err = hinic_mbox_to_pf ( hwdev , HINIC_MOD_L2NIC ,
HINIC_PORT_CMD_VF_UNREGISTER ,
& unregister , sizeof ( unregister ) ,
& unregister , & out_size , 0 ) ;
if ( err | | ! out_size | | unregister . status )
dev_err ( & hwdev - > hwif - > pdev - > dev , " Failed to unregister VF, err: %d, status: 0x%x, out_size: 0x%x \n " ,
err , unregister . status , out_size ) ;
} else {
if ( hwdev - > func_to_io . vf_infos ) {
hinic_unregister_pf_mbox_cb ( hwdev , HINIC_MOD_L2NIC ) ;
kfree ( hwdev - > func_to_io . vf_infos ) ;
}
}
}
static int hinic_init_vf_hw ( struct hinic_hwdev * hwdev , u16 start_vf_id ,
u16 end_vf_id )
{
u16 i , func_idx ;
int err ;
/* vf use 256K as default wq page size, and can't change it */
for ( i = start_vf_id ; i < = end_vf_id ; i + + ) {
func_idx = hinic_glb_pf_vf_offset ( hwdev - > hwif ) + i ;
err = hinic_set_wq_page_size ( hwdev , func_idx ,
HINIC_DEFAULT_WQ_PAGE_SIZE ) ;
if ( err )
return err ;
}
return 0 ;
}
int hinic_pci_sriov_disable ( struct pci_dev * pdev )
{
struct hinic_sriov_info * sriov_info ;
u16 tmp_vfs ;
sriov_info = hinic_get_sriov_info_by_pcidev ( pdev ) ;
/* if SR-IOV is already disabled then nothing will be done */
if ( ! sriov_info - > sriov_enabled )
return 0 ;
set_bit ( HINIC_SRIOV_DISABLE , & sriov_info - > state ) ;
/* If our VFs are assigned we cannot shut down SR-IOV
* without causing issues , so just leave the hardware
* available but disabled
*/
if ( pci_vfs_assigned ( sriov_info - > pdev ) ) {
clear_bit ( HINIC_SRIOV_DISABLE , & sriov_info - > state ) ;
dev_warn ( & pdev - > dev , " Unloading driver while VFs are assigned - VFs will not be deallocated \n " ) ;
return - EPERM ;
}
sriov_info - > sriov_enabled = false ;
/* disable iov and allow time for transactions to clear */
pci_disable_sriov ( sriov_info - > pdev ) ;
tmp_vfs = ( u16 ) sriov_info - > num_vfs ;
sriov_info - > num_vfs = 0 ;
hinic_deinit_vf_hw ( sriov_info , OS_VF_ID_TO_HW ( 0 ) ,
OS_VF_ID_TO_HW ( tmp_vfs - 1 ) ) ;
clear_bit ( HINIC_SRIOV_DISABLE , & sriov_info - > state ) ;
return 0 ;
}
int hinic_pci_sriov_enable ( struct pci_dev * pdev , int num_vfs )
{
struct hinic_sriov_info * sriov_info ;
int err ;
sriov_info = hinic_get_sriov_info_by_pcidev ( pdev ) ;
if ( test_and_set_bit ( HINIC_SRIOV_ENABLE , & sriov_info - > state ) ) {
dev_err ( & pdev - > dev ,
" SR-IOV enable in process, please wait, num_vfs %d \n " ,
num_vfs ) ;
return - EPERM ;
}
err = hinic_init_vf_hw ( sriov_info - > hwdev , OS_VF_ID_TO_HW ( 0 ) ,
OS_VF_ID_TO_HW ( ( u16 ) num_vfs - 1 ) ) ;
if ( err ) {
dev_err ( & sriov_info - > pdev - > dev ,
" Failed to init vf in hardware before enable sriov, error %d \n " ,
err ) ;
clear_bit ( HINIC_SRIOV_ENABLE , & sriov_info - > state ) ;
return err ;
}
err = pci_enable_sriov ( sriov_info - > pdev , num_vfs ) ;
if ( err ) {
dev_err ( & pdev - > dev ,
" Failed to enable SR-IOV, error %d \n " , err ) ;
clear_bit ( HINIC_SRIOV_ENABLE , & sriov_info - > state ) ;
return err ;
}
sriov_info - > sriov_enabled = true ;
sriov_info - > num_vfs = num_vfs ;
clear_bit ( HINIC_SRIOV_ENABLE , & sriov_info - > state ) ;
return num_vfs ;
}
int hinic_pci_sriov_configure ( struct pci_dev * dev , int num_vfs )
{
struct hinic_sriov_info * sriov_info ;
sriov_info = hinic_get_sriov_info_by_pcidev ( dev ) ;
if ( test_bit ( HINIC_FUNC_REMOVE , & sriov_info - > state ) )
return - EBUSY ;
if ( ! num_vfs )
return hinic_pci_sriov_disable ( dev ) ;
else
return hinic_pci_sriov_enable ( dev , num_vfs ) ;
}