Merge branch 'hinic-add-some-ethtool-ops-support'
Luo bin says: ==================== hinic: add some ethtool ops support patch #1: support to set and get pause params with "ethtool -A/a" cmd patch #2: support to set and get irq coalesce params with "ethtool -C/c" cmd patch #3: support to do self test with "ethtool -t" cmd patch #4: support to identify physical device with "ethtool -p" cmd patch #5: support to get eeprom information with "ethtool -m" cmd ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
8930449628
|
@ -20,11 +20,14 @@
|
|||
|
||||
#define HINIC_DRV_NAME "hinic"
|
||||
|
||||
#define LP_PKT_CNT 64
|
||||
|
||||
enum hinic_flags {
|
||||
HINIC_LINK_UP = BIT(0),
|
||||
HINIC_INTF_UP = BIT(1),
|
||||
HINIC_RSS_ENABLE = BIT(2),
|
||||
HINIC_LINK_DOWN = BIT(3),
|
||||
HINIC_LP_TEST = BIT(4),
|
||||
};
|
||||
|
||||
struct hinic_rx_mode_work {
|
||||
|
@ -49,6 +52,12 @@ enum hinic_rss_hash_type {
|
|||
HINIC_RSS_HASH_ENGINE_TYPE_MAX,
|
||||
};
|
||||
|
||||
struct hinic_intr_coal_info {
|
||||
u8 pending_limt;
|
||||
u8 coalesce_timer_cfg;
|
||||
u8 resend_timer_cfg;
|
||||
};
|
||||
|
||||
struct hinic_dev {
|
||||
struct net_device *netdev;
|
||||
struct hinic_hwdev *hwdev;
|
||||
|
@ -82,7 +91,12 @@ struct hinic_dev {
|
|||
struct hinic_rss_type rss_type;
|
||||
u8 *rss_hkey_user;
|
||||
s32 *rss_indir_user;
|
||||
struct hinic_intr_coal_info *rx_intr_coalesce;
|
||||
struct hinic_intr_coal_info *tx_intr_coalesce;
|
||||
struct hinic_sriov_info sriov_info;
|
||||
int lb_test_rx_idx;
|
||||
int lb_pkt_len;
|
||||
u8 *lb_test_rx_buf;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <linux/if_vlan.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/sfp.h>
|
||||
|
||||
#include "hinic_hw_qp.h"
|
||||
#include "hinic_hw_dev.h"
|
||||
|
@ -49,6 +50,13 @@
|
|||
#define ETHTOOL_ADD_ADVERTISED_LINK_MODE(ecmd, mode) \
|
||||
((ecmd)->advertising |= ADVERTISED_##mode)
|
||||
|
||||
#define COALESCE_PENDING_LIMIT_UNIT 8
|
||||
#define COALESCE_TIMER_CFG_UNIT 9
|
||||
#define COALESCE_ALL_QUEUE 0xFFFF
|
||||
#define COALESCE_MAX_PENDING_LIMIT (255 * COALESCE_PENDING_LIMIT_UNIT)
|
||||
#define COALESCE_MAX_TIMER_CFG (255 * COALESCE_TIMER_CFG_UNIT)
|
||||
#define OBJ_STR_MAX_LEN 32
|
||||
|
||||
struct hw2ethtool_link_mode {
|
||||
enum ethtool_link_mode_bit_indices link_mode_bit;
|
||||
u32 speed;
|
||||
|
@ -126,6 +134,16 @@ static struct hw2ethtool_link_mode
|
|||
},
|
||||
};
|
||||
|
||||
#define LP_DEFAULT_TIME 5 /* seconds */
|
||||
#define LP_PKT_LEN 1514
|
||||
|
||||
#define PORT_DOWN_ERR_IDX 0
|
||||
enum diag_test_index {
|
||||
INTERNAL_LP_TEST = 0,
|
||||
EXTERNAL_LP_TEST = 1,
|
||||
DIAG_TEST_MAX = 2,
|
||||
};
|
||||
|
||||
static void set_link_speed(struct ethtool_link_ksettings *link_ksettings,
|
||||
enum hinic_speed speed)
|
||||
{
|
||||
|
@ -613,6 +631,273 @@ static int hinic_set_ringparam(struct net_device *netdev,
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __hinic_get_coalesce(struct net_device *netdev,
|
||||
struct ethtool_coalesce *coal, u16 queue)
|
||||
{
|
||||
struct hinic_dev *nic_dev = netdev_priv(netdev);
|
||||
struct hinic_intr_coal_info *rx_intr_coal_info;
|
||||
struct hinic_intr_coal_info *tx_intr_coal_info;
|
||||
|
||||
if (queue == COALESCE_ALL_QUEUE) {
|
||||
/* get tx/rx irq0 as default parameters */
|
||||
rx_intr_coal_info = &nic_dev->rx_intr_coalesce[0];
|
||||
tx_intr_coal_info = &nic_dev->tx_intr_coalesce[0];
|
||||
} else {
|
||||
if (queue >= nic_dev->num_qps) {
|
||||
netif_err(nic_dev, drv, netdev,
|
||||
"Invalid queue_id: %d\n", queue);
|
||||
return -EINVAL;
|
||||
}
|
||||
rx_intr_coal_info = &nic_dev->rx_intr_coalesce[queue];
|
||||
tx_intr_coal_info = &nic_dev->tx_intr_coalesce[queue];
|
||||
}
|
||||
|
||||
/* coalesce_timer is in unit of 9us */
|
||||
coal->rx_coalesce_usecs = rx_intr_coal_info->coalesce_timer_cfg *
|
||||
COALESCE_TIMER_CFG_UNIT;
|
||||
/* coalesced_frames is in unit of 8 */
|
||||
coal->rx_max_coalesced_frames = rx_intr_coal_info->pending_limt *
|
||||
COALESCE_PENDING_LIMIT_UNIT;
|
||||
coal->tx_coalesce_usecs = tx_intr_coal_info->coalesce_timer_cfg *
|
||||
COALESCE_TIMER_CFG_UNIT;
|
||||
coal->tx_max_coalesced_frames = tx_intr_coal_info->pending_limt *
|
||||
COALESCE_PENDING_LIMIT_UNIT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_coalesce_exceed_limit(const struct ethtool_coalesce *coal)
|
||||
{
|
||||
if (coal->rx_coalesce_usecs > COALESCE_MAX_TIMER_CFG ||
|
||||
coal->rx_max_coalesced_frames > COALESCE_MAX_PENDING_LIMIT ||
|
||||
coal->tx_coalesce_usecs > COALESCE_MAX_TIMER_CFG ||
|
||||
coal->tx_max_coalesced_frames > COALESCE_MAX_PENDING_LIMIT)
|
||||
return -ERANGE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_queue_coalesce(struct hinic_dev *nic_dev, u16 q_id,
|
||||
struct hinic_intr_coal_info *coal,
|
||||
bool set_rx_coal)
|
||||
{
|
||||
struct hinic_intr_coal_info *intr_coal = NULL;
|
||||
struct hinic_msix_config interrupt_info = {0};
|
||||
struct net_device *netdev = nic_dev->netdev;
|
||||
u16 msix_idx;
|
||||
int err;
|
||||
|
||||
intr_coal = set_rx_coal ? &nic_dev->rx_intr_coalesce[q_id] :
|
||||
&nic_dev->tx_intr_coalesce[q_id];
|
||||
|
||||
intr_coal->coalesce_timer_cfg = coal->coalesce_timer_cfg;
|
||||
intr_coal->pending_limt = coal->pending_limt;
|
||||
|
||||
/* netdev not running or qp not in using,
|
||||
* don't need to set coalesce to hw
|
||||
*/
|
||||
if (!(nic_dev->flags & HINIC_INTF_UP) ||
|
||||
q_id >= nic_dev->num_qps)
|
||||
return 0;
|
||||
|
||||
msix_idx = set_rx_coal ? nic_dev->rxqs[q_id].rq->msix_entry :
|
||||
nic_dev->txqs[q_id].sq->msix_entry;
|
||||
interrupt_info.msix_index = msix_idx;
|
||||
interrupt_info.coalesce_timer_cnt = intr_coal->coalesce_timer_cfg;
|
||||
interrupt_info.pending_cnt = intr_coal->pending_limt;
|
||||
interrupt_info.resend_timer_cnt = intr_coal->resend_timer_cfg;
|
||||
|
||||
err = hinic_set_interrupt_cfg(nic_dev->hwdev, &interrupt_info);
|
||||
if (err)
|
||||
netif_warn(nic_dev, drv, netdev,
|
||||
"Failed to set %s queue%d coalesce",
|
||||
set_rx_coal ? "rx" : "tx", q_id);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __set_hw_coal_param(struct hinic_dev *nic_dev,
|
||||
struct hinic_intr_coal_info *intr_coal,
|
||||
u16 queue, bool set_rx_coal)
|
||||
{
|
||||
int err;
|
||||
u16 i;
|
||||
|
||||
if (queue == COALESCE_ALL_QUEUE) {
|
||||
for (i = 0; i < nic_dev->max_qps; i++) {
|
||||
err = set_queue_coalesce(nic_dev, i, intr_coal,
|
||||
set_rx_coal);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
if (queue >= nic_dev->num_qps) {
|
||||
netif_err(nic_dev, drv, nic_dev->netdev,
|
||||
"Invalid queue_id: %d\n", queue);
|
||||
return -EINVAL;
|
||||
}
|
||||
err = set_queue_coalesce(nic_dev, queue, intr_coal,
|
||||
set_rx_coal);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __hinic_set_coalesce(struct net_device *netdev,
|
||||
struct ethtool_coalesce *coal, u16 queue)
|
||||
{
|
||||
struct hinic_intr_coal_info *ori_rx_intr_coal = NULL;
|
||||
struct hinic_intr_coal_info *ori_tx_intr_coal = NULL;
|
||||
struct hinic_dev *nic_dev = netdev_priv(netdev);
|
||||
struct hinic_intr_coal_info rx_intr_coal = {0};
|
||||
struct hinic_intr_coal_info tx_intr_coal = {0};
|
||||
char obj_str[OBJ_STR_MAX_LEN] = {0};
|
||||
bool set_rx_coal = false;
|
||||
bool set_tx_coal = false;
|
||||
int err;
|
||||
|
||||
err = is_coalesce_exceed_limit(coal);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (coal->rx_coalesce_usecs || coal->rx_max_coalesced_frames) {
|
||||
rx_intr_coal.coalesce_timer_cfg =
|
||||
(u8)(coal->rx_coalesce_usecs / COALESCE_TIMER_CFG_UNIT);
|
||||
rx_intr_coal.pending_limt = (u8)(coal->rx_max_coalesced_frames /
|
||||
COALESCE_PENDING_LIMIT_UNIT);
|
||||
set_rx_coal = true;
|
||||
}
|
||||
|
||||
if (coal->tx_coalesce_usecs || coal->tx_max_coalesced_frames) {
|
||||
tx_intr_coal.coalesce_timer_cfg =
|
||||
(u8)(coal->tx_coalesce_usecs / COALESCE_TIMER_CFG_UNIT);
|
||||
tx_intr_coal.pending_limt = (u8)(coal->tx_max_coalesced_frames /
|
||||
COALESCE_PENDING_LIMIT_UNIT);
|
||||
set_tx_coal = true;
|
||||
}
|
||||
|
||||
if (queue == COALESCE_ALL_QUEUE) {
|
||||
ori_rx_intr_coal = &nic_dev->rx_intr_coalesce[0];
|
||||
ori_tx_intr_coal = &nic_dev->tx_intr_coalesce[0];
|
||||
err = snprintf(obj_str, OBJ_STR_MAX_LEN, "for netdev");
|
||||
} else {
|
||||
ori_rx_intr_coal = &nic_dev->rx_intr_coalesce[queue];
|
||||
ori_tx_intr_coal = &nic_dev->tx_intr_coalesce[queue];
|
||||
err = snprintf(obj_str, OBJ_STR_MAX_LEN, "for queue %d", queue);
|
||||
}
|
||||
if (err <= 0 || err >= OBJ_STR_MAX_LEN) {
|
||||
netif_err(nic_dev, drv, netdev, "Failed to snprintf string, function return(%d) and dest_len(%d)\n",
|
||||
err, OBJ_STR_MAX_LEN);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* setting coalesce timer or pending limit to zero will disable
|
||||
* coalesce
|
||||
*/
|
||||
if (set_rx_coal && (!rx_intr_coal.coalesce_timer_cfg ||
|
||||
!rx_intr_coal.pending_limt))
|
||||
netif_warn(nic_dev, drv, netdev, "RX coalesce will be disabled\n");
|
||||
if (set_tx_coal && (!tx_intr_coal.coalesce_timer_cfg ||
|
||||
!tx_intr_coal.pending_limt))
|
||||
netif_warn(nic_dev, drv, netdev, "TX coalesce will be disabled\n");
|
||||
|
||||
if (set_rx_coal) {
|
||||
err = __set_hw_coal_param(nic_dev, &rx_intr_coal, queue, true);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
if (set_tx_coal) {
|
||||
err = __set_hw_coal_param(nic_dev, &tx_intr_coal, queue, false);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hinic_get_coalesce(struct net_device *netdev,
|
||||
struct ethtool_coalesce *coal)
|
||||
{
|
||||
return __hinic_get_coalesce(netdev, coal, COALESCE_ALL_QUEUE);
|
||||
}
|
||||
|
||||
static int hinic_set_coalesce(struct net_device *netdev,
|
||||
struct ethtool_coalesce *coal)
|
||||
{
|
||||
return __hinic_set_coalesce(netdev, coal, COALESCE_ALL_QUEUE);
|
||||
}
|
||||
|
||||
static int hinic_get_per_queue_coalesce(struct net_device *netdev, u32 queue,
|
||||
struct ethtool_coalesce *coal)
|
||||
{
|
||||
return __hinic_get_coalesce(netdev, coal, queue);
|
||||
}
|
||||
|
||||
static int hinic_set_per_queue_coalesce(struct net_device *netdev, u32 queue,
|
||||
struct ethtool_coalesce *coal)
|
||||
{
|
||||
return __hinic_set_coalesce(netdev, coal, queue);
|
||||
}
|
||||
|
||||
static void hinic_get_pauseparam(struct net_device *netdev,
|
||||
struct ethtool_pauseparam *pause)
|
||||
{
|
||||
struct hinic_dev *nic_dev = netdev_priv(netdev);
|
||||
struct hinic_pause_config pause_info = {0};
|
||||
struct hinic_nic_cfg *nic_cfg;
|
||||
int err;
|
||||
|
||||
nic_cfg = &nic_dev->hwdev->func_to_io.nic_cfg;
|
||||
|
||||
err = hinic_get_hw_pause_info(nic_dev->hwdev, &pause_info);
|
||||
if (!err) {
|
||||
pause->autoneg = pause_info.auto_neg;
|
||||
if (nic_cfg->pause_set || !pause_info.auto_neg) {
|
||||
pause->rx_pause = nic_cfg->rx_pause;
|
||||
pause->tx_pause = nic_cfg->tx_pause;
|
||||
} else {
|
||||
pause->rx_pause = pause_info.rx_pause;
|
||||
pause->tx_pause = pause_info.tx_pause;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int hinic_set_pauseparam(struct net_device *netdev,
|
||||
struct ethtool_pauseparam *pause)
|
||||
{
|
||||
struct hinic_dev *nic_dev = netdev_priv(netdev);
|
||||
struct hinic_pause_config pause_info = {0};
|
||||
struct hinic_port_cap port_cap = {0};
|
||||
int err;
|
||||
|
||||
err = hinic_port_get_cap(nic_dev, &port_cap);
|
||||
if (err)
|
||||
return -EIO;
|
||||
|
||||
if (pause->autoneg != port_cap.autoneg_state)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
pause_info.auto_neg = pause->autoneg;
|
||||
pause_info.rx_pause = pause->rx_pause;
|
||||
pause_info.tx_pause = pause->tx_pause;
|
||||
|
||||
mutex_lock(&nic_dev->hwdev->func_to_io.nic_cfg.cfg_mutex);
|
||||
err = hinic_set_hw_pause_info(nic_dev->hwdev, &pause_info);
|
||||
if (err) {
|
||||
mutex_unlock(&nic_dev->hwdev->func_to_io.nic_cfg.cfg_mutex);
|
||||
return err;
|
||||
}
|
||||
nic_dev->hwdev->func_to_io.nic_cfg.pause_set = true;
|
||||
nic_dev->hwdev->func_to_io.nic_cfg.auto_neg = pause->autoneg;
|
||||
nic_dev->hwdev->func_to_io.nic_cfg.rx_pause = pause->rx_pause;
|
||||
nic_dev->hwdev->func_to_io.nic_cfg.tx_pause = pause->tx_pause;
|
||||
mutex_unlock(&nic_dev->hwdev->func_to_io.nic_cfg.cfg_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hinic_get_channels(struct net_device *netdev,
|
||||
struct ethtool_channels *channels)
|
||||
{
|
||||
|
@ -970,6 +1255,11 @@ static struct hinic_stats hinic_function_stats[] = {
|
|||
HINIC_FUNC_STAT(rx_err_vport),
|
||||
};
|
||||
|
||||
static char hinic_test_strings[][ETH_GSTRING_LEN] = {
|
||||
"Internal lb test (on/offline)",
|
||||
"External lb test (external_lb)",
|
||||
};
|
||||
|
||||
#define HINIC_PORT_STAT(_stat_item) { \
|
||||
.name = #_stat_item, \
|
||||
.size = sizeof_field(struct hinic_phy_port_stats, _stat_item), \
|
||||
|
@ -1179,6 +1469,8 @@ static int hinic_get_sset_count(struct net_device *netdev, int sset)
|
|||
int count, q_num;
|
||||
|
||||
switch (sset) {
|
||||
case ETH_SS_TEST:
|
||||
return ARRAY_LEN(hinic_test_strings);
|
||||
case ETH_SS_STATS:
|
||||
q_num = nic_dev->num_qps;
|
||||
count = ARRAY_LEN(hinic_function_stats) +
|
||||
|
@ -1201,6 +1493,9 @@ static void hinic_get_strings(struct net_device *netdev,
|
|||
u16 i, j;
|
||||
|
||||
switch (stringset) {
|
||||
case ETH_SS_TEST:
|
||||
memcpy(data, *hinic_test_strings, sizeof(hinic_test_strings));
|
||||
return;
|
||||
case ETH_SS_STATS:
|
||||
for (i = 0; i < ARRAY_LEN(hinic_function_stats); i++) {
|
||||
memcpy(p, hinic_function_stats[i].name,
|
||||
|
@ -1234,13 +1529,311 @@ static void hinic_get_strings(struct net_device *netdev,
|
|||
}
|
||||
}
|
||||
|
||||
static int hinic_run_lp_test(struct hinic_dev *nic_dev, u32 test_time)
|
||||
{
|
||||
u8 *lb_test_rx_buf = nic_dev->lb_test_rx_buf;
|
||||
struct net_device *netdev = nic_dev->netdev;
|
||||
struct sk_buff *skb_tmp = NULL;
|
||||
struct sk_buff *skb = NULL;
|
||||
u32 cnt = test_time * 5;
|
||||
u8 *test_data = NULL;
|
||||
u32 i;
|
||||
u8 j;
|
||||
|
||||
skb_tmp = alloc_skb(LP_PKT_LEN, GFP_ATOMIC);
|
||||
if (!skb_tmp)
|
||||
return -ENOMEM;
|
||||
|
||||
test_data = __skb_put(skb_tmp, LP_PKT_LEN);
|
||||
|
||||
memset(test_data, 0xFF, 2 * ETH_ALEN);
|
||||
test_data[ETH_ALEN] = 0xFE;
|
||||
test_data[2 * ETH_ALEN] = 0x08;
|
||||
test_data[2 * ETH_ALEN + 1] = 0x0;
|
||||
|
||||
for (i = ETH_HLEN; i < LP_PKT_LEN; i++)
|
||||
test_data[i] = i & 0xFF;
|
||||
|
||||
skb_tmp->queue_mapping = 0;
|
||||
skb_tmp->ip_summed = CHECKSUM_COMPLETE;
|
||||
skb_tmp->dev = netdev;
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
nic_dev->lb_test_rx_idx = 0;
|
||||
memset(lb_test_rx_buf, 0, LP_PKT_CNT * LP_PKT_LEN);
|
||||
|
||||
for (j = 0; j < LP_PKT_CNT; j++) {
|
||||
skb = pskb_copy(skb_tmp, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
dev_kfree_skb_any(skb_tmp);
|
||||
netif_err(nic_dev, drv, netdev,
|
||||
"Copy skb failed for loopback test\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* mark index for every pkt */
|
||||
skb->data[LP_PKT_LEN - 1] = j;
|
||||
|
||||
if (hinic_lb_xmit_frame(skb, netdev)) {
|
||||
dev_kfree_skb_any(skb);
|
||||
dev_kfree_skb_any(skb_tmp);
|
||||
netif_err(nic_dev, drv, netdev,
|
||||
"Xmit pkt failed for loopback test\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
/* wait till all pkts received to RX buffer */
|
||||
msleep(200);
|
||||
|
||||
for (j = 0; j < LP_PKT_CNT; j++) {
|
||||
if (memcmp(lb_test_rx_buf + j * LP_PKT_LEN,
|
||||
skb_tmp->data, LP_PKT_LEN - 1) ||
|
||||
(*(lb_test_rx_buf + j * LP_PKT_LEN +
|
||||
LP_PKT_LEN - 1) != j)) {
|
||||
dev_kfree_skb_any(skb_tmp);
|
||||
netif_err(nic_dev, drv, netdev,
|
||||
"Compare pkt failed in loopback test(index=0x%02x, data[%d]=0x%02x)\n",
|
||||
j + i * LP_PKT_CNT,
|
||||
LP_PKT_LEN - 1,
|
||||
*(lb_test_rx_buf + j * LP_PKT_LEN +
|
||||
LP_PKT_LEN - 1));
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dev_kfree_skb_any(skb_tmp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_lp_test(struct hinic_dev *nic_dev, u32 flags, u32 test_time,
|
||||
enum diag_test_index *test_index)
|
||||
{
|
||||
struct net_device *netdev = nic_dev->netdev;
|
||||
u8 *lb_test_rx_buf = NULL;
|
||||
int err = 0;
|
||||
|
||||
if (!(flags & ETH_TEST_FL_EXTERNAL_LB)) {
|
||||
*test_index = INTERNAL_LP_TEST;
|
||||
if (hinic_set_loopback_mode(nic_dev->hwdev,
|
||||
HINIC_INTERNAL_LP_MODE, true)) {
|
||||
netif_err(nic_dev, drv, netdev,
|
||||
"Failed to set port loopback mode before loopback test\n");
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
*test_index = EXTERNAL_LP_TEST;
|
||||
}
|
||||
|
||||
lb_test_rx_buf = vmalloc(LP_PKT_CNT * LP_PKT_LEN);
|
||||
if (!lb_test_rx_buf) {
|
||||
err = -ENOMEM;
|
||||
} else {
|
||||
nic_dev->lb_test_rx_buf = lb_test_rx_buf;
|
||||
nic_dev->lb_pkt_len = LP_PKT_LEN;
|
||||
nic_dev->flags |= HINIC_LP_TEST;
|
||||
err = hinic_run_lp_test(nic_dev, test_time);
|
||||
nic_dev->flags &= ~HINIC_LP_TEST;
|
||||
msleep(100);
|
||||
vfree(lb_test_rx_buf);
|
||||
nic_dev->lb_test_rx_buf = NULL;
|
||||
}
|
||||
|
||||
if (!(flags & ETH_TEST_FL_EXTERNAL_LB)) {
|
||||
if (hinic_set_loopback_mode(nic_dev->hwdev,
|
||||
HINIC_INTERNAL_LP_MODE, false)) {
|
||||
netif_err(nic_dev, drv, netdev,
|
||||
"Failed to cancel port loopback mode after loopback test\n");
|
||||
err = -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void hinic_diag_test(struct net_device *netdev,
|
||||
struct ethtool_test *eth_test, u64 *data)
|
||||
{
|
||||
struct hinic_dev *nic_dev = netdev_priv(netdev);
|
||||
enum hinic_port_link_state link_state;
|
||||
enum diag_test_index test_index = 0;
|
||||
int err = 0;
|
||||
|
||||
memset(data, 0, DIAG_TEST_MAX * sizeof(u64));
|
||||
|
||||
/* don't support loopback test when netdev is closed. */
|
||||
if (!(nic_dev->flags & HINIC_INTF_UP)) {
|
||||
netif_err(nic_dev, drv, netdev,
|
||||
"Do not support loopback test when netdev is closed\n");
|
||||
eth_test->flags |= ETH_TEST_FL_FAILED;
|
||||
data[PORT_DOWN_ERR_IDX] = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
netif_carrier_off(netdev);
|
||||
|
||||
err = do_lp_test(nic_dev, eth_test->flags, LP_DEFAULT_TIME,
|
||||
&test_index);
|
||||
if (err) {
|
||||
eth_test->flags |= ETH_TEST_FL_FAILED;
|
||||
data[test_index] = 1;
|
||||
}
|
||||
|
||||
err = hinic_port_link_state(nic_dev, &link_state);
|
||||
if (!err && link_state == HINIC_LINK_STATE_UP)
|
||||
netif_carrier_on(netdev);
|
||||
}
|
||||
|
||||
static int hinic_set_phys_id(struct net_device *netdev,
|
||||
enum ethtool_phys_id_state state)
|
||||
{
|
||||
struct hinic_dev *nic_dev = netdev_priv(netdev);
|
||||
int err = 0;
|
||||
u8 port;
|
||||
|
||||
port = nic_dev->hwdev->port_id;
|
||||
|
||||
switch (state) {
|
||||
case ETHTOOL_ID_ACTIVE:
|
||||
err = hinic_set_led_status(nic_dev->hwdev, port,
|
||||
HINIC_LED_TYPE_LINK,
|
||||
HINIC_LED_MODE_FORCE_2HZ);
|
||||
if (err)
|
||||
netif_err(nic_dev, drv, netdev,
|
||||
"Set LED blinking in 2HZ failed\n");
|
||||
break;
|
||||
|
||||
case ETHTOOL_ID_INACTIVE:
|
||||
err = hinic_reset_led_status(nic_dev->hwdev, port);
|
||||
if (err)
|
||||
netif_err(nic_dev, drv, netdev,
|
||||
"Reset LED to original status failed\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hinic_get_module_info(struct net_device *netdev,
|
||||
struct ethtool_modinfo *modinfo)
|
||||
{
|
||||
struct hinic_dev *nic_dev = netdev_priv(netdev);
|
||||
u8 sfp_type_ext;
|
||||
u8 sfp_type;
|
||||
int err;
|
||||
|
||||
err = hinic_get_sfp_type(nic_dev->hwdev, &sfp_type, &sfp_type_ext);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (sfp_type) {
|
||||
case SFF8024_ID_SFP:
|
||||
modinfo->type = ETH_MODULE_SFF_8472;
|
||||
modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
|
||||
break;
|
||||
case SFF8024_ID_QSFP_8438:
|
||||
modinfo->type = ETH_MODULE_SFF_8436;
|
||||
modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;
|
||||
break;
|
||||
case SFF8024_ID_QSFP_8436_8636:
|
||||
if (sfp_type_ext >= 0x3) {
|
||||
modinfo->type = ETH_MODULE_SFF_8636;
|
||||
modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN;
|
||||
|
||||
} else {
|
||||
modinfo->type = ETH_MODULE_SFF_8436;
|
||||
modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;
|
||||
}
|
||||
break;
|
||||
case SFF8024_ID_QSFP28_8636:
|
||||
modinfo->type = ETH_MODULE_SFF_8636;
|
||||
modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN;
|
||||
break;
|
||||
default:
|
||||
netif_warn(nic_dev, drv, netdev,
|
||||
"Optical module unknown: 0x%x\n", sfp_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hinic_get_module_eeprom(struct net_device *netdev,
|
||||
struct ethtool_eeprom *ee, u8 *data)
|
||||
{
|
||||
struct hinic_dev *nic_dev = netdev_priv(netdev);
|
||||
u8 sfp_data[STD_SFP_INFO_MAX_SIZE];
|
||||
u16 len;
|
||||
int err;
|
||||
|
||||
if (!ee->len || ((ee->len + ee->offset) > STD_SFP_INFO_MAX_SIZE))
|
||||
return -EINVAL;
|
||||
|
||||
memset(data, 0, ee->len);
|
||||
|
||||
err = hinic_get_sfp_eeprom(nic_dev->hwdev, sfp_data, &len);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memcpy(data, sfp_data + ee->offset, ee->len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ethtool_ops hinic_ethtool_ops = {
|
||||
.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS |
|
||||
ETHTOOL_COALESCE_RX_MAX_FRAMES |
|
||||
ETHTOOL_COALESCE_TX_USECS |
|
||||
ETHTOOL_COALESCE_TX_MAX_FRAMES,
|
||||
|
||||
.get_link_ksettings = hinic_get_link_ksettings,
|
||||
.set_link_ksettings = hinic_set_link_ksettings,
|
||||
.get_drvinfo = hinic_get_drvinfo,
|
||||
.get_link = ethtool_op_get_link,
|
||||
.get_ringparam = hinic_get_ringparam,
|
||||
.set_ringparam = hinic_set_ringparam,
|
||||
.get_coalesce = hinic_get_coalesce,
|
||||
.set_coalesce = hinic_set_coalesce,
|
||||
.get_per_queue_coalesce = hinic_get_per_queue_coalesce,
|
||||
.set_per_queue_coalesce = hinic_set_per_queue_coalesce,
|
||||
.get_pauseparam = hinic_get_pauseparam,
|
||||
.set_pauseparam = hinic_set_pauseparam,
|
||||
.get_channels = hinic_get_channels,
|
||||
.set_channels = hinic_set_channels,
|
||||
.get_rxnfc = hinic_get_rxnfc,
|
||||
.set_rxnfc = hinic_set_rxnfc,
|
||||
.get_rxfh_key_size = hinic_get_rxfh_key_size,
|
||||
.get_rxfh_indir_size = hinic_get_rxfh_indir_size,
|
||||
.get_rxfh = hinic_get_rxfh,
|
||||
.set_rxfh = hinic_set_rxfh,
|
||||
.get_sset_count = hinic_get_sset_count,
|
||||
.get_ethtool_stats = hinic_get_ethtool_stats,
|
||||
.get_strings = hinic_get_strings,
|
||||
.self_test = hinic_diag_test,
|
||||
.set_phys_id = hinic_set_phys_id,
|
||||
.get_module_info = hinic_get_module_info,
|
||||
.get_module_eeprom = hinic_get_module_eeprom,
|
||||
};
|
||||
|
||||
static const struct ethtool_ops hinicvf_ethtool_ops = {
|
||||
.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS |
|
||||
ETHTOOL_COALESCE_RX_MAX_FRAMES |
|
||||
ETHTOOL_COALESCE_TX_USECS |
|
||||
ETHTOOL_COALESCE_TX_MAX_FRAMES,
|
||||
|
||||
.get_link_ksettings = hinic_get_link_ksettings,
|
||||
.get_drvinfo = hinic_get_drvinfo,
|
||||
.get_link = ethtool_op_get_link,
|
||||
.get_ringparam = hinic_get_ringparam,
|
||||
.set_ringparam = hinic_set_ringparam,
|
||||
.get_coalesce = hinic_get_coalesce,
|
||||
.set_coalesce = hinic_set_coalesce,
|
||||
.get_per_queue_coalesce = hinic_get_per_queue_coalesce,
|
||||
.set_per_queue_coalesce = hinic_set_per_queue_coalesce,
|
||||
.get_channels = hinic_get_channels,
|
||||
.set_channels = hinic_set_channels,
|
||||
.get_rxnfc = hinic_get_rxnfc,
|
||||
|
@ -1256,5 +1849,10 @@ static const struct ethtool_ops hinic_ethtool_ops = {
|
|||
|
||||
void hinic_set_ethtool_ops(struct net_device *netdev)
|
||||
{
|
||||
netdev->ethtool_ops = &hinic_ethtool_ops;
|
||||
struct hinic_dev *nic_dev = netdev_priv(netdev);
|
||||
|
||||
if (!HINIC_IS_VF(nic_dev->hwdev->hwif))
|
||||
netdev->ethtool_ops = &hinic_ethtool_ops;
|
||||
else
|
||||
netdev->ethtool_ops = &hinicvf_ethtool_ops;
|
||||
}
|
||||
|
|
|
@ -83,6 +83,8 @@ static int parse_capability(struct hinic_hwdev *hwdev,
|
|||
nic_cap->max_vf_qps = dev_cap->max_vf_sqs + 1;
|
||||
}
|
||||
|
||||
hwdev->port_id = dev_cap->port_id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -705,6 +707,68 @@ static int hinic_l2nic_reset(struct hinic_hwdev *hwdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int hinic_get_interrupt_cfg(struct hinic_hwdev *hwdev,
|
||||
struct hinic_msix_config *interrupt_info)
|
||||
{
|
||||
u16 out_size = sizeof(*interrupt_info);
|
||||
struct hinic_pfhwdev *pfhwdev;
|
||||
int err;
|
||||
|
||||
if (!hwdev || !interrupt_info)
|
||||
return -EINVAL;
|
||||
|
||||
pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);
|
||||
|
||||
interrupt_info->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
|
||||
|
||||
err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM,
|
||||
HINIC_COMM_CMD_MSI_CTRL_REG_RD_BY_UP,
|
||||
interrupt_info, sizeof(*interrupt_info),
|
||||
interrupt_info, &out_size, HINIC_MGMT_MSG_SYNC);
|
||||
if (err || !out_size || interrupt_info->status) {
|
||||
dev_err(&hwdev->hwif->pdev->dev, "Failed to get interrupt config, err: %d, status: 0x%x, out size: 0x%x\n",
|
||||
err, interrupt_info->status, out_size);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hinic_set_interrupt_cfg(struct hinic_hwdev *hwdev,
|
||||
struct hinic_msix_config *interrupt_info)
|
||||
{
|
||||
u16 out_size = sizeof(*interrupt_info);
|
||||
struct hinic_msix_config temp_info;
|
||||
struct hinic_pfhwdev *pfhwdev;
|
||||
int err;
|
||||
|
||||
if (!hwdev)
|
||||
return -EINVAL;
|
||||
|
||||
pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);
|
||||
|
||||
interrupt_info->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
|
||||
|
||||
err = hinic_get_interrupt_cfg(hwdev, &temp_info);
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
|
||||
interrupt_info->lli_credit_cnt = temp_info.lli_timer_cnt;
|
||||
interrupt_info->lli_timer_cnt = temp_info.lli_timer_cnt;
|
||||
|
||||
err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM,
|
||||
HINIC_COMM_CMD_MSI_CTRL_REG_WR_BY_UP,
|
||||
interrupt_info, sizeof(*interrupt_info),
|
||||
interrupt_info, &out_size, HINIC_MGMT_MSG_SYNC);
|
||||
if (err || !out_size || interrupt_info->status) {
|
||||
dev_err(&hwdev->hwif->pdev->dev, "Failed to get interrupt config, err: %d, status: 0x%x, out size: 0x%x\n",
|
||||
err, interrupt_info->status, out_size);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hinic_init_hwdev - Initialize the NIC HW
|
||||
* @pdev: the NIC pci device
|
||||
|
@ -777,6 +841,8 @@ struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev)
|
|||
goto err_dev_cap;
|
||||
}
|
||||
|
||||
mutex_init(&hwdev->func_to_io.nic_cfg.cfg_mutex);
|
||||
|
||||
err = hinic_vf_func_init(hwdev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to init nic mbox\n");
|
||||
|
|
|
@ -48,6 +48,8 @@ enum hinic_port_cmd {
|
|||
HINIC_PORT_CMD_ADD_VLAN = 3,
|
||||
HINIC_PORT_CMD_DEL_VLAN = 4,
|
||||
|
||||
HINIC_PORT_CMD_SET_PFC = 5,
|
||||
|
||||
HINIC_PORT_CMD_SET_MAC = 9,
|
||||
HINIC_PORT_CMD_GET_MAC = 10,
|
||||
HINIC_PORT_CMD_DEL_MAC = 11,
|
||||
|
@ -95,6 +97,9 @@ enum hinic_port_cmd {
|
|||
|
||||
HINIC_PORT_CMD_FWCTXT_INIT = 69,
|
||||
|
||||
HINIC_PORT_CMD_GET_LOOPBACK_MODE = 72,
|
||||
HINIC_PORT_CMD_SET_LOOPBACK_MODE,
|
||||
|
||||
HINIC_PORT_CMD_ENABLE_SPOOFCHK = 78,
|
||||
|
||||
HINIC_PORT_CMD_GET_MGMT_VERSION = 88,
|
||||
|
@ -125,9 +130,13 @@ enum hinic_port_cmd {
|
|||
|
||||
HINIC_PORT_CMD_SET_AUTONEG = 219,
|
||||
|
||||
HINIC_PORT_CMD_GET_STD_SFP_INFO = 240,
|
||||
|
||||
HINIC_PORT_CMD_SET_LRO_TIMER = 244,
|
||||
|
||||
HINIC_PORT_CMD_SET_VF_MAX_MIN_RATE = 249,
|
||||
|
||||
HINIC_PORT_CMD_GET_SFP_ABS = 251,
|
||||
};
|
||||
|
||||
/* cmd of mgmt CPU message for HILINK module */
|
||||
|
@ -283,6 +292,21 @@ struct hinic_cmd_l2nic_reset {
|
|||
u16 reset_flag;
|
||||
};
|
||||
|
||||
struct hinic_msix_config {
|
||||
u8 status;
|
||||
u8 version;
|
||||
u8 rsvd0[6];
|
||||
|
||||
u16 func_id;
|
||||
u16 msix_index;
|
||||
u8 pending_cnt;
|
||||
u8 coalesce_timer_cnt;
|
||||
u8 lli_timer_cnt;
|
||||
u8 lli_credit_cnt;
|
||||
u8 resend_timer_cnt;
|
||||
u8 rsvd1[3];
|
||||
};
|
||||
|
||||
struct hinic_hwdev {
|
||||
struct hinic_hwif *hwif;
|
||||
struct msix_entry *msix_entries;
|
||||
|
@ -292,6 +316,7 @@ struct hinic_hwdev {
|
|||
struct hinic_mbox_func_to_func *func_to_func;
|
||||
|
||||
struct hinic_cap nic_cap;
|
||||
u8 port_id;
|
||||
};
|
||||
|
||||
struct hinic_nic_cb {
|
||||
|
@ -376,4 +401,10 @@ int hinic_hwdev_hw_ci_addr_set(struct hinic_hwdev *hwdev, struct hinic_sq *sq,
|
|||
void hinic_hwdev_set_msix_state(struct hinic_hwdev *hwdev, u16 msix_index,
|
||||
enum hinic_msix_state flag);
|
||||
|
||||
int hinic_get_interrupt_cfg(struct hinic_hwdev *hwdev,
|
||||
struct hinic_msix_config *interrupt_info);
|
||||
|
||||
int hinic_set_interrupt_cfg(struct hinic_hwdev *hwdev,
|
||||
struct hinic_msix_config *interrupt_info);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -47,6 +47,15 @@ struct hinic_free_db_area {
|
|||
struct semaphore idx_lock;
|
||||
};
|
||||
|
||||
struct hinic_nic_cfg {
|
||||
/* lock for getting nic cfg */
|
||||
struct mutex cfg_mutex;
|
||||
bool pause_set;
|
||||
u32 auto_neg;
|
||||
u32 rx_pause;
|
||||
u32 tx_pause;
|
||||
};
|
||||
|
||||
struct hinic_func_to_io {
|
||||
struct hinic_hwif *hwif;
|
||||
struct hinic_hwdev *hwdev;
|
||||
|
@ -78,6 +87,7 @@ struct hinic_func_to_io {
|
|||
u16 max_vfs;
|
||||
struct vf_data_storage *vf_infos;
|
||||
u8 link_status;
|
||||
struct hinic_nic_cfg nic_cfg;
|
||||
};
|
||||
|
||||
struct hinic_wq_page_size {
|
||||
|
|
|
@ -78,7 +78,12 @@ enum hinic_comm_cmd {
|
|||
|
||||
HINIC_COMM_CMD_CEQ_CTRL_REG_WR_BY_UP = 0x33,
|
||||
|
||||
HINIC_COMM_CMD_L2NIC_RESET = 0x4b,
|
||||
HINIC_COMM_CMD_MSI_CTRL_REG_WR_BY_UP,
|
||||
HINIC_COMM_CMD_MSI_CTRL_REG_RD_BY_UP,
|
||||
|
||||
HINIC_COMM_CMD_SET_LED_STATUS = 0x4a,
|
||||
|
||||
HINIC_COMM_CMD_L2NIC_RESET = 0x4b,
|
||||
|
||||
HINIC_COMM_CMD_PAGESIZE_SET = 0x50,
|
||||
|
||||
|
|
|
@ -69,6 +69,10 @@ MODULE_PARM_DESC(rx_weight, "Number Rx packets for NAPI budget (default=64)");
|
|||
|
||||
#define HINIC_WAIT_SRIOV_CFG_TIMEOUT 15000
|
||||
|
||||
#define HINIC_DEAULT_TXRX_MSIX_PENDING_LIMIT 2
|
||||
#define HINIC_DEAULT_TXRX_MSIX_COALESC_TIMER_CFG 32
|
||||
#define HINIC_DEAULT_TXRX_MSIX_RESEND_TIMER_CFG 7
|
||||
|
||||
static int change_mac_addr(struct net_device *netdev, const u8 *addr);
|
||||
|
||||
static int set_features(struct hinic_dev *nic_dev,
|
||||
|
@ -887,6 +891,26 @@ static void netdev_features_init(struct net_device *netdev)
|
|||
netdev->features = netdev->hw_features | NETIF_F_HW_VLAN_CTAG_FILTER;
|
||||
}
|
||||
|
||||
static void hinic_refresh_nic_cfg(struct hinic_dev *nic_dev)
|
||||
{
|
||||
struct hinic_nic_cfg *nic_cfg = &nic_dev->hwdev->func_to_io.nic_cfg;
|
||||
struct hinic_pause_config pause_info = {0};
|
||||
struct hinic_port_cap port_cap = {0};
|
||||
|
||||
if (hinic_port_get_cap(nic_dev, &port_cap))
|
||||
return;
|
||||
|
||||
mutex_lock(&nic_cfg->cfg_mutex);
|
||||
if (nic_cfg->pause_set || !port_cap.autoneg_state) {
|
||||
nic_cfg->auto_neg = port_cap.autoneg_state;
|
||||
pause_info.auto_neg = nic_cfg->auto_neg;
|
||||
pause_info.rx_pause = nic_cfg->rx_pause;
|
||||
pause_info.tx_pause = nic_cfg->tx_pause;
|
||||
hinic_set_hw_pause_info(nic_dev->hwdev, &pause_info);
|
||||
}
|
||||
mutex_unlock(&nic_cfg->cfg_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* link_status_event_handler - link event handler
|
||||
* @handle: nic device for the handler
|
||||
|
@ -918,6 +942,9 @@ static void link_status_event_handler(void *handle, void *buf_in, u16 in_size,
|
|||
|
||||
up(&nic_dev->mgmt_lock);
|
||||
|
||||
if (!HINIC_IS_VF(nic_dev->hwdev->hwif))
|
||||
hinic_refresh_nic_cfg(nic_dev);
|
||||
|
||||
netif_info(nic_dev, drv, nic_dev->netdev, "HINIC_Link is UP\n");
|
||||
} else {
|
||||
down(&nic_dev->mgmt_lock);
|
||||
|
@ -948,28 +975,93 @@ static int set_features(struct hinic_dev *nic_dev,
|
|||
{
|
||||
netdev_features_t changed = force_change ? ~0 : pre_features ^ features;
|
||||
u32 csum_en = HINIC_RX_CSUM_OFFLOAD_EN;
|
||||
netdev_features_t failed_features = 0;
|
||||
int ret = 0;
|
||||
int err = 0;
|
||||
|
||||
if (changed & NETIF_F_TSO)
|
||||
err = hinic_port_set_tso(nic_dev, (features & NETIF_F_TSO) ?
|
||||
if (changed & NETIF_F_TSO) {
|
||||
ret = hinic_port_set_tso(nic_dev, (features & NETIF_F_TSO) ?
|
||||
HINIC_TSO_ENABLE : HINIC_TSO_DISABLE);
|
||||
if (ret) {
|
||||
err = ret;
|
||||
failed_features |= NETIF_F_TSO;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed & NETIF_F_RXCSUM)
|
||||
err = hinic_set_rx_csum_offload(nic_dev, csum_en);
|
||||
if (changed & NETIF_F_RXCSUM) {
|
||||
ret = hinic_set_rx_csum_offload(nic_dev, csum_en);
|
||||
if (ret) {
|
||||
err = ret;
|
||||
failed_features |= NETIF_F_RXCSUM;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed & NETIF_F_LRO) {
|
||||
err = hinic_set_rx_lro_state(nic_dev,
|
||||
ret = hinic_set_rx_lro_state(nic_dev,
|
||||
!!(features & NETIF_F_LRO),
|
||||
HINIC_LRO_RX_TIMER_DEFAULT,
|
||||
HINIC_LRO_MAX_WQE_NUM_DEFAULT);
|
||||
if (ret) {
|
||||
err = ret;
|
||||
failed_features |= NETIF_F_LRO;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed & NETIF_F_HW_VLAN_CTAG_RX)
|
||||
err = hinic_set_rx_vlan_offload(nic_dev,
|
||||
if (changed & NETIF_F_HW_VLAN_CTAG_RX) {
|
||||
ret = hinic_set_rx_vlan_offload(nic_dev,
|
||||
!!(features &
|
||||
NETIF_F_HW_VLAN_CTAG_RX));
|
||||
if (ret) {
|
||||
err = ret;
|
||||
failed_features |= NETIF_F_HW_VLAN_CTAG_RX;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
if (err) {
|
||||
nic_dev->netdev->features = features ^ failed_features;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hinic_init_intr_coalesce(struct hinic_dev *nic_dev)
|
||||
{
|
||||
u64 size;
|
||||
u16 i;
|
||||
|
||||
size = sizeof(struct hinic_intr_coal_info) * nic_dev->max_qps;
|
||||
nic_dev->rx_intr_coalesce = kzalloc(size, GFP_KERNEL);
|
||||
if (!nic_dev->rx_intr_coalesce)
|
||||
return -ENOMEM;
|
||||
nic_dev->tx_intr_coalesce = kzalloc(size, GFP_KERNEL);
|
||||
if (!nic_dev->tx_intr_coalesce) {
|
||||
kfree(nic_dev->rx_intr_coalesce);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < nic_dev->max_qps; i++) {
|
||||
nic_dev->rx_intr_coalesce[i].pending_limt =
|
||||
HINIC_DEAULT_TXRX_MSIX_PENDING_LIMIT;
|
||||
nic_dev->rx_intr_coalesce[i].coalesce_timer_cfg =
|
||||
HINIC_DEAULT_TXRX_MSIX_COALESC_TIMER_CFG;
|
||||
nic_dev->rx_intr_coalesce[i].resend_timer_cfg =
|
||||
HINIC_DEAULT_TXRX_MSIX_RESEND_TIMER_CFG;
|
||||
nic_dev->tx_intr_coalesce[i].pending_limt =
|
||||
HINIC_DEAULT_TXRX_MSIX_PENDING_LIMIT;
|
||||
nic_dev->tx_intr_coalesce[i].coalesce_timer_cfg =
|
||||
HINIC_DEAULT_TXRX_MSIX_COALESC_TIMER_CFG;
|
||||
nic_dev->tx_intr_coalesce[i].resend_timer_cfg =
|
||||
HINIC_DEAULT_TXRX_MSIX_RESEND_TIMER_CFG;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hinic_free_intr_coalesce(struct hinic_dev *nic_dev)
|
||||
{
|
||||
kfree(nic_dev->tx_intr_coalesce);
|
||||
kfree(nic_dev->rx_intr_coalesce);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1008,8 +1100,6 @@ static int nic_dev_init(struct pci_dev *pdev)
|
|||
goto err_alloc_etherdev;
|
||||
}
|
||||
|
||||
hinic_set_ethtool_ops(netdev);
|
||||
|
||||
if (!HINIC_IS_VF(hwdev->hwif))
|
||||
netdev->netdev_ops = &hinic_netdev_ops;
|
||||
else
|
||||
|
@ -1032,6 +1122,8 @@ static int nic_dev_init(struct pci_dev *pdev)
|
|||
nic_dev->sriov_info.pdev = pdev;
|
||||
nic_dev->max_qps = num_qps;
|
||||
|
||||
hinic_set_ethtool_ops(netdev);
|
||||
|
||||
sema_init(&nic_dev->mgmt_lock, 1);
|
||||
|
||||
tx_stats = &nic_dev->tx_stats;
|
||||
|
@ -1100,8 +1192,19 @@ static int nic_dev_init(struct pci_dev *pdev)
|
|||
if (err)
|
||||
goto err_set_features;
|
||||
|
||||
/* enable pause and disable pfc by default */
|
||||
err = hinic_dcb_set_pfc(nic_dev->hwdev, 0, 0);
|
||||
if (err)
|
||||
goto err_set_pfc;
|
||||
|
||||
SET_NETDEV_DEV(netdev, &pdev->dev);
|
||||
|
||||
err = hinic_init_intr_coalesce(nic_dev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to init_intr_coalesce\n");
|
||||
goto err_init_intr;
|
||||
}
|
||||
|
||||
err = register_netdev(netdev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to register netdev\n");
|
||||
|
@ -1111,6 +1214,9 @@ static int nic_dev_init(struct pci_dev *pdev)
|
|||
return 0;
|
||||
|
||||
err_reg_netdev:
|
||||
hinic_free_intr_coalesce(nic_dev);
|
||||
err_init_intr:
|
||||
err_set_pfc:
|
||||
err_set_features:
|
||||
hinic_hwdev_cb_unregister(nic_dev->hwdev,
|
||||
HINIC_MGMT_MSG_CMD_LINK_STATUS);
|
||||
|
@ -1224,6 +1330,8 @@ static void hinic_remove(struct pci_dev *pdev)
|
|||
|
||||
unregister_netdev(netdev);
|
||||
|
||||
hinic_free_intr_coalesce(nic_dev);
|
||||
|
||||
hinic_port_del_mac(nic_dev, netdev->dev_addr, 0);
|
||||
|
||||
hinic_hwdev_cb_unregister(nic_dev->hwdev,
|
||||
|
|
|
@ -1082,6 +1082,7 @@ int hinic_get_link_mode(struct hinic_hwdev *hwdev,
|
|||
if (!hwdev || !link_mode)
|
||||
return -EINVAL;
|
||||
|
||||
link_mode->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
|
||||
out_size = sizeof(*link_mode);
|
||||
|
||||
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_LINK_MODE,
|
||||
|
@ -1172,6 +1173,8 @@ int hinic_get_hw_pause_info(struct hinic_hwdev *hwdev,
|
|||
u16 out_size = sizeof(*pause_info);
|
||||
int err;
|
||||
|
||||
pause_info->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
|
||||
|
||||
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_PAUSE_INFO,
|
||||
pause_info, sizeof(*pause_info),
|
||||
pause_info, &out_size);
|
||||
|
@ -1190,6 +1193,8 @@ int hinic_set_hw_pause_info(struct hinic_hwdev *hwdev,
|
|||
u16 out_size = sizeof(*pause_info);
|
||||
int err;
|
||||
|
||||
pause_info->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
|
||||
|
||||
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_PAUSE_INFO,
|
||||
pause_info, sizeof(*pause_info),
|
||||
pause_info, &out_size);
|
||||
|
@ -1201,3 +1206,192 @@ int hinic_set_hw_pause_info(struct hinic_hwdev *hwdev,
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hinic_dcb_set_pfc(struct hinic_hwdev *hwdev, u8 pfc_en, u8 pfc_bitmap)
|
||||
{
|
||||
struct hinic_nic_cfg *nic_cfg = &hwdev->func_to_io.nic_cfg;
|
||||
struct hinic_set_pfc pfc = {0};
|
||||
u16 out_size = sizeof(pfc);
|
||||
int err;
|
||||
|
||||
if (HINIC_IS_VF(hwdev->hwif))
|
||||
return 0;
|
||||
|
||||
mutex_lock(&nic_cfg->cfg_mutex);
|
||||
|
||||
pfc.func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
|
||||
pfc.pfc_bitmap = pfc_bitmap;
|
||||
pfc.pfc_en = pfc_en;
|
||||
|
||||
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_PFC,
|
||||
&pfc, sizeof(pfc), &pfc, &out_size);
|
||||
if (err || pfc.status || !out_size) {
|
||||
dev_err(&hwdev->hwif->pdev->dev, "Failed to %s pfc, err: %d, status: 0x%x, out size: 0x%x\n",
|
||||
pfc_en ? "enable" : "disable", err, pfc.status,
|
||||
out_size);
|
||||
mutex_unlock(&nic_cfg->cfg_mutex);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* pause settings is opposite from pfc */
|
||||
nic_cfg->rx_pause = pfc_en ? 0 : 1;
|
||||
nic_cfg->tx_pause = pfc_en ? 0 : 1;
|
||||
|
||||
mutex_unlock(&nic_cfg->cfg_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hinic_set_loopback_mode(struct hinic_hwdev *hwdev, u32 mode, u32 enable)
|
||||
{
|
||||
struct hinic_port_loopback lb = {0};
|
||||
u16 out_size = sizeof(lb);
|
||||
int err;
|
||||
|
||||
lb.mode = mode;
|
||||
lb.en = enable;
|
||||
|
||||
if (mode < LOOP_MODE_MIN || mode > LOOP_MODE_MAX) {
|
||||
dev_err(&hwdev->hwif->pdev->dev,
|
||||
"Invalid loopback mode %d to set\n", mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_LOOPBACK_MODE,
|
||||
&lb, sizeof(lb), &lb, &out_size);
|
||||
if (err || !out_size || lb.status) {
|
||||
dev_err(&hwdev->hwif->pdev->dev,
|
||||
"Failed to set loopback mode %d en %d, err: %d, status: 0x%x, out size: 0x%x\n",
|
||||
mode, enable, err, lb.status, out_size);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _set_led_status(struct hinic_hwdev *hwdev, u8 port,
|
||||
enum hinic_led_type type,
|
||||
enum hinic_led_mode mode, u8 reset)
|
||||
{
|
||||
struct hinic_led_info led_info = {0};
|
||||
u16 out_size = sizeof(led_info);
|
||||
struct hinic_pfhwdev *pfhwdev;
|
||||
int err;
|
||||
|
||||
pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);
|
||||
|
||||
led_info.port = port;
|
||||
led_info.reset = reset;
|
||||
|
||||
led_info.type = type;
|
||||
led_info.mode = mode;
|
||||
|
||||
err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM,
|
||||
HINIC_COMM_CMD_SET_LED_STATUS,
|
||||
&led_info, sizeof(led_info),
|
||||
&led_info, &out_size, HINIC_MGMT_MSG_SYNC);
|
||||
if (err || led_info.status || !out_size) {
|
||||
dev_err(&hwdev->hwif->pdev->dev, "Failed to set led status, err: %d, status: 0x%x, out size: 0x%x\n",
|
||||
err, led_info.status, out_size);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hinic_set_led_status(struct hinic_hwdev *hwdev, u8 port,
|
||||
enum hinic_led_type type, enum hinic_led_mode mode)
|
||||
{
|
||||
if (!hwdev)
|
||||
return -EINVAL;
|
||||
|
||||
return _set_led_status(hwdev, port, type, mode, 0);
|
||||
}
|
||||
|
||||
int hinic_reset_led_status(struct hinic_hwdev *hwdev, u8 port)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!hwdev)
|
||||
return -EINVAL;
|
||||
|
||||
err = _set_led_status(hwdev, port, HINIC_LED_TYPE_INVALID,
|
||||
HINIC_LED_MODE_INVALID, 1);
|
||||
if (err)
|
||||
dev_err(&hwdev->hwif->pdev->dev,
|
||||
"Failed to reset led status\n");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool hinic_if_sfp_absent(struct hinic_hwdev *hwdev)
|
||||
{
|
||||
struct hinic_cmd_get_light_module_abs sfp_abs = {0};
|
||||
u16 out_size = sizeof(sfp_abs);
|
||||
u8 port_id = hwdev->port_id;
|
||||
int err;
|
||||
|
||||
sfp_abs.port_id = port_id;
|
||||
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_SFP_ABS,
|
||||
&sfp_abs, sizeof(sfp_abs), &sfp_abs,
|
||||
&out_size);
|
||||
if (sfp_abs.status || err || !out_size) {
|
||||
dev_err(&hwdev->hwif->pdev->dev,
|
||||
"Failed to get port%d sfp absent status, err: %d, status: 0x%x, out size: 0x%x\n",
|
||||
port_id, err, sfp_abs.status, out_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
return ((sfp_abs.abs_status == 0) ? false : true);
|
||||
}
|
||||
|
||||
int hinic_get_sfp_eeprom(struct hinic_hwdev *hwdev, u8 *data, u16 *len)
|
||||
{
|
||||
struct hinic_cmd_get_std_sfp_info sfp_info = {0};
|
||||
u16 out_size = sizeof(sfp_info);
|
||||
u8 port_id;
|
||||
int err;
|
||||
|
||||
if (!hwdev || !data || !len)
|
||||
return -EINVAL;
|
||||
|
||||
port_id = hwdev->port_id;
|
||||
|
||||
if (hinic_if_sfp_absent(hwdev))
|
||||
return -ENXIO;
|
||||
|
||||
sfp_info.port_id = port_id;
|
||||
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_STD_SFP_INFO,
|
||||
&sfp_info, sizeof(sfp_info), &sfp_info,
|
||||
&out_size);
|
||||
if (sfp_info.status || err || !out_size) {
|
||||
dev_err(&hwdev->hwif->pdev->dev,
|
||||
"Failed to get port%d sfp eeprom information, err: %d, status: 0x%x, out size: 0x%x\n",
|
||||
port_id, err, sfp_info.status, out_size);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*len = min_t(u16, sfp_info.eeprom_len, STD_SFP_INFO_MAX_SIZE);
|
||||
memcpy(data, sfp_info.sfp_info, STD_SFP_INFO_MAX_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hinic_get_sfp_type(struct hinic_hwdev *hwdev, u8 *data0, u8 *data1)
|
||||
{
|
||||
u8 sfp_data[STD_SFP_INFO_MAX_SIZE];
|
||||
u16 len;
|
||||
int err;
|
||||
|
||||
if (hinic_if_sfp_absent(hwdev))
|
||||
return -ENXIO;
|
||||
|
||||
err = hinic_get_sfp_eeprom(hwdev, sfp_data, &len);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*data0 = sfp_data[0];
|
||||
*data1 = sfp_data[1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -641,6 +641,68 @@ struct hinic_pause_config {
|
|||
u32 tx_pause;
|
||||
};
|
||||
|
||||
struct hinic_set_pfc {
|
||||
u8 status;
|
||||
u8 version;
|
||||
u8 rsvd0[6];
|
||||
|
||||
u16 func_id;
|
||||
u8 pfc_en;
|
||||
u8 pfc_bitmap;
|
||||
u8 rsvd1[4];
|
||||
};
|
||||
|
||||
/* get or set loopback mode, need to modify by base API */
|
||||
#define HINIC_INTERNAL_LP_MODE 5
|
||||
#define LOOP_MODE_MIN 1
|
||||
#define LOOP_MODE_MAX 6
|
||||
|
||||
struct hinic_port_loopback {
|
||||
u8 status;
|
||||
u8 version;
|
||||
u8 rsvd[6];
|
||||
|
||||
u32 mode;
|
||||
u32 en;
|
||||
};
|
||||
|
||||
struct hinic_led_info {
|
||||
u8 status;
|
||||
u8 version;
|
||||
u8 rsvd0[6];
|
||||
|
||||
u8 port;
|
||||
u8 type;
|
||||
u8 mode;
|
||||
u8 reset;
|
||||
};
|
||||
|
||||
#define STD_SFP_INFO_MAX_SIZE 640
|
||||
|
||||
struct hinic_cmd_get_light_module_abs {
|
||||
u8 status;
|
||||
u8 version;
|
||||
u8 rsvd0[6];
|
||||
|
||||
u8 port_id;
|
||||
u8 abs_status; /* 0:present, 1:absent */
|
||||
u8 rsv[2];
|
||||
};
|
||||
|
||||
#define STD_SFP_INFO_MAX_SIZE 640
|
||||
|
||||
struct hinic_cmd_get_std_sfp_info {
|
||||
u8 status;
|
||||
u8 version;
|
||||
u8 rsvd0[6];
|
||||
|
||||
u8 port_id;
|
||||
u8 wire_type;
|
||||
u16 eeprom_len;
|
||||
u32 rsvd;
|
||||
u8 sfp_info[STD_SFP_INFO_MAX_SIZE];
|
||||
};
|
||||
|
||||
int hinic_port_add_mac(struct hinic_dev *nic_dev, const u8 *addr,
|
||||
u16 vlan_id);
|
||||
|
||||
|
@ -736,6 +798,38 @@ int hinic_get_hw_pause_info(struct hinic_hwdev *hwdev,
|
|||
int hinic_set_hw_pause_info(struct hinic_hwdev *hwdev,
|
||||
struct hinic_pause_config *pause_info);
|
||||
|
||||
int hinic_dcb_set_pfc(struct hinic_hwdev *hwdev, u8 pfc_en, u8 pfc_bitmap);
|
||||
|
||||
int hinic_set_loopback_mode(struct hinic_hwdev *hwdev, u32 mode, u32 enable);
|
||||
|
||||
enum hinic_led_mode {
|
||||
HINIC_LED_MODE_ON,
|
||||
HINIC_LED_MODE_OFF,
|
||||
HINIC_LED_MODE_FORCE_1HZ,
|
||||
HINIC_LED_MODE_FORCE_2HZ,
|
||||
HINIC_LED_MODE_FORCE_4HZ,
|
||||
HINIC_LED_MODE_1HZ,
|
||||
HINIC_LED_MODE_2HZ,
|
||||
HINIC_LED_MODE_4HZ,
|
||||
HINIC_LED_MODE_INVALID,
|
||||
};
|
||||
|
||||
enum hinic_led_type {
|
||||
HINIC_LED_TYPE_LINK,
|
||||
HINIC_LED_TYPE_LOW_SPEED,
|
||||
HINIC_LED_TYPE_HIGH_SPEED,
|
||||
HINIC_LED_TYPE_INVALID,
|
||||
};
|
||||
|
||||
int hinic_reset_led_status(struct hinic_hwdev *hwdev, u8 port);
|
||||
|
||||
int hinic_set_led_status(struct hinic_hwdev *hwdev, u8 port,
|
||||
enum hinic_led_type type, enum hinic_led_mode mode);
|
||||
|
||||
int hinic_get_sfp_type(struct hinic_hwdev *hwdev, u8 *data0, u8 *data1);
|
||||
|
||||
int hinic_get_sfp_eeprom(struct hinic_hwdev *hwdev, u8 *data, u16 *len);
|
||||
|
||||
int hinic_open(struct net_device *netdev);
|
||||
|
||||
int hinic_close(struct net_device *netdev);
|
||||
|
|
|
@ -316,6 +316,39 @@ static int rx_recv_jumbo_pkt(struct hinic_rxq *rxq, struct sk_buff *head_skb,
|
|||
return num_wqes;
|
||||
}
|
||||
|
||||
static void hinic_copy_lp_data(struct hinic_dev *nic_dev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct net_device *netdev = nic_dev->netdev;
|
||||
u8 *lb_buf = nic_dev->lb_test_rx_buf;
|
||||
int lb_len = nic_dev->lb_pkt_len;
|
||||
int pkt_offset, frag_len, i;
|
||||
void *frag_data = NULL;
|
||||
|
||||
if (nic_dev->lb_test_rx_idx == LP_PKT_CNT) {
|
||||
nic_dev->lb_test_rx_idx = 0;
|
||||
netif_warn(nic_dev, drv, netdev, "Loopback test warning, receive too more test pkts\n");
|
||||
}
|
||||
|
||||
if (skb->len != nic_dev->lb_pkt_len) {
|
||||
netif_warn(nic_dev, drv, netdev, "Wrong packet length\n");
|
||||
nic_dev->lb_test_rx_idx++;
|
||||
return;
|
||||
}
|
||||
|
||||
pkt_offset = nic_dev->lb_test_rx_idx * lb_len;
|
||||
frag_len = (int)skb_headlen(skb);
|
||||
memcpy(lb_buf + pkt_offset, skb->data, frag_len);
|
||||
pkt_offset += frag_len;
|
||||
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
|
||||
frag_data = skb_frag_address(&skb_shinfo(skb)->frags[i]);
|
||||
frag_len = (int)skb_frag_size(&skb_shinfo(skb)->frags[i]);
|
||||
memcpy((lb_buf + pkt_offset), frag_data, frag_len);
|
||||
pkt_offset += frag_len;
|
||||
}
|
||||
nic_dev->lb_test_rx_idx++;
|
||||
}
|
||||
|
||||
/**
|
||||
* rxq_recv - Rx handler
|
||||
* @rxq: rx queue
|
||||
|
@ -330,6 +363,7 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
|
|||
u64 pkt_len = 0, rx_bytes = 0;
|
||||
struct hinic_rq *rq = rxq->rq;
|
||||
struct hinic_rq_wqe *rq_wqe;
|
||||
struct hinic_dev *nic_dev;
|
||||
unsigned int free_wqebbs;
|
||||
struct hinic_rq_cqe *cqe;
|
||||
int num_wqes, pkts = 0;
|
||||
|
@ -342,6 +376,8 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
|
|||
u32 vlan_len;
|
||||
u16 vid;
|
||||
|
||||
nic_dev = netdev_priv(netdev);
|
||||
|
||||
while (pkts < budget) {
|
||||
num_wqes = 0;
|
||||
|
||||
|
@ -384,6 +420,9 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
|
|||
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
|
||||
}
|
||||
|
||||
if (unlikely(nic_dev->flags & HINIC_LP_TEST))
|
||||
hinic_copy_lp_data(nic_dev, skb);
|
||||
|
||||
skb_record_rx_queue(skb, qp->q_id);
|
||||
skb->protocol = eth_type_trans(skb, rxq->netdev);
|
||||
|
||||
|
@ -478,11 +517,15 @@ static irqreturn_t rx_irq(int irq, void *data)
|
|||
static int rx_request_irq(struct hinic_rxq *rxq)
|
||||
{
|
||||
struct hinic_dev *nic_dev = netdev_priv(rxq->netdev);
|
||||
struct hinic_msix_config interrupt_info = {0};
|
||||
struct hinic_intr_coal_info *intr_coal = NULL;
|
||||
struct hinic_hwdev *hwdev = nic_dev->hwdev;
|
||||
struct hinic_rq *rq = rxq->rq;
|
||||
struct hinic_qp *qp;
|
||||
int err;
|
||||
|
||||
qp = container_of(rq, struct hinic_qp, rq);
|
||||
|
||||
rx_add_napi(rxq);
|
||||
|
||||
hinic_hwdev_msix_set(hwdev, rq->msix_entry,
|
||||
|
@ -490,13 +533,26 @@ static int rx_request_irq(struct hinic_rxq *rxq)
|
|||
RX_IRQ_NO_LLI_TIMER, RX_IRQ_NO_CREDIT,
|
||||
RX_IRQ_NO_RESEND_TIMER);
|
||||
|
||||
intr_coal = &nic_dev->rx_intr_coalesce[qp->q_id];
|
||||
interrupt_info.msix_index = rq->msix_entry;
|
||||
interrupt_info.coalesce_timer_cnt = intr_coal->coalesce_timer_cfg;
|
||||
interrupt_info.pending_cnt = intr_coal->pending_limt;
|
||||
interrupt_info.resend_timer_cnt = intr_coal->resend_timer_cfg;
|
||||
|
||||
err = hinic_set_interrupt_cfg(hwdev, &interrupt_info);
|
||||
if (err) {
|
||||
netif_err(nic_dev, drv, rxq->netdev,
|
||||
"Failed to set RX interrupt coalescing attribute\n");
|
||||
rx_del_napi(rxq);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = request_irq(rq->irq, rx_irq, 0, rxq->irq_name, rxq);
|
||||
if (err) {
|
||||
rx_del_napi(rxq);
|
||||
return err;
|
||||
}
|
||||
|
||||
qp = container_of(rq, struct hinic_qp, rq);
|
||||
cpumask_set_cpu(qp->q_id % num_online_cpus(), &rq->affinity_mask);
|
||||
return irq_set_affinity_hint(rq->irq, &rq->affinity_mask);
|
||||
}
|
||||
|
|
|
@ -383,7 +383,7 @@ static int hinic_del_vf_mac_msg_handler(void *hwdev, u16 vf_id,
|
|||
|
||||
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) &&
|
||||
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;
|
||||
|
@ -905,7 +905,6 @@ int hinic_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting)
|
|||
|
||||
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");
|
||||
|
@ -1020,6 +1019,7 @@ static int cfg_mbx_pf_proc_vf_msg(void *hwdev, u16 vf_id, u8 cmd, void *buf_in,
|
|||
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);
|
||||
|
||||
|
|
|
@ -459,6 +459,67 @@ static int hinic_tx_offload(struct sk_buff *skb, struct hinic_sq_task *task,
|
|||
return 0;
|
||||
}
|
||||
|
||||
netdev_tx_t hinic_lb_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
|
||||
{
|
||||
struct hinic_dev *nic_dev = netdev_priv(netdev);
|
||||
u16 prod_idx, q_id = skb->queue_mapping;
|
||||
struct netdev_queue *netdev_txq;
|
||||
int nr_sges, err = NETDEV_TX_OK;
|
||||
struct hinic_sq_wqe *sq_wqe;
|
||||
unsigned int wqe_size;
|
||||
struct hinic_txq *txq;
|
||||
struct hinic_qp *qp;
|
||||
|
||||
txq = &nic_dev->txqs[q_id];
|
||||
qp = container_of(txq->sq, struct hinic_qp, sq);
|
||||
nr_sges = skb_shinfo(skb)->nr_frags + 1;
|
||||
|
||||
err = tx_map_skb(nic_dev, skb, txq->sges);
|
||||
if (err)
|
||||
goto skb_error;
|
||||
|
||||
wqe_size = HINIC_SQ_WQE_SIZE(nr_sges);
|
||||
|
||||
sq_wqe = hinic_sq_get_wqe(txq->sq, wqe_size, &prod_idx);
|
||||
if (!sq_wqe) {
|
||||
netif_stop_subqueue(netdev, qp->q_id);
|
||||
|
||||
sq_wqe = hinic_sq_get_wqe(txq->sq, wqe_size, &prod_idx);
|
||||
if (sq_wqe) {
|
||||
netif_wake_subqueue(nic_dev->netdev, qp->q_id);
|
||||
goto process_sq_wqe;
|
||||
}
|
||||
|
||||
tx_unmap_skb(nic_dev, skb, txq->sges);
|
||||
|
||||
u64_stats_update_begin(&txq->txq_stats.syncp);
|
||||
txq->txq_stats.tx_busy++;
|
||||
u64_stats_update_end(&txq->txq_stats.syncp);
|
||||
err = NETDEV_TX_BUSY;
|
||||
wqe_size = 0;
|
||||
goto flush_skbs;
|
||||
}
|
||||
|
||||
process_sq_wqe:
|
||||
hinic_sq_prepare_wqe(txq->sq, prod_idx, sq_wqe, txq->sges, nr_sges);
|
||||
hinic_sq_write_wqe(txq->sq, prod_idx, sq_wqe, skb, wqe_size);
|
||||
|
||||
flush_skbs:
|
||||
netdev_txq = netdev_get_tx_queue(netdev, q_id);
|
||||
if ((!netdev_xmit_more()) || (netif_xmit_stopped(netdev_txq)))
|
||||
hinic_sq_write_db(txq->sq, prod_idx, wqe_size, 0);
|
||||
|
||||
return err;
|
||||
|
||||
skb_error:
|
||||
dev_kfree_skb_any(skb);
|
||||
u64_stats_update_begin(&txq->txq_stats.syncp);
|
||||
txq->txq_stats.tx_dropped++;
|
||||
u64_stats_update_end(&txq->txq_stats.syncp);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
|
||||
{
|
||||
struct hinic_dev *nic_dev = netdev_priv(netdev);
|
||||
|
@ -718,12 +779,17 @@ static irqreturn_t tx_irq(int irq, void *data)
|
|||
static int tx_request_irq(struct hinic_txq *txq)
|
||||
{
|
||||
struct hinic_dev *nic_dev = netdev_priv(txq->netdev);
|
||||
struct hinic_msix_config interrupt_info = {0};
|
||||
struct hinic_intr_coal_info *intr_coal = NULL;
|
||||
struct hinic_hwdev *hwdev = nic_dev->hwdev;
|
||||
struct hinic_hwif *hwif = hwdev->hwif;
|
||||
struct pci_dev *pdev = hwif->pdev;
|
||||
struct hinic_sq *sq = txq->sq;
|
||||
struct hinic_qp *qp;
|
||||
int err;
|
||||
|
||||
qp = container_of(sq, struct hinic_qp, sq);
|
||||
|
||||
tx_napi_add(txq, nic_dev->tx_weight);
|
||||
|
||||
hinic_hwdev_msix_set(nic_dev->hwdev, sq->msix_entry,
|
||||
|
@ -731,6 +797,20 @@ static int tx_request_irq(struct hinic_txq *txq)
|
|||
TX_IRQ_NO_LLI_TIMER, TX_IRQ_NO_CREDIT,
|
||||
TX_IRQ_NO_RESEND_TIMER);
|
||||
|
||||
intr_coal = &nic_dev->tx_intr_coalesce[qp->q_id];
|
||||
interrupt_info.msix_index = sq->msix_entry;
|
||||
interrupt_info.coalesce_timer_cnt = intr_coal->coalesce_timer_cfg;
|
||||
interrupt_info.pending_cnt = intr_coal->pending_limt;
|
||||
interrupt_info.resend_timer_cnt = intr_coal->resend_timer_cfg;
|
||||
|
||||
err = hinic_set_interrupt_cfg(hwdev, &interrupt_info);
|
||||
if (err) {
|
||||
netif_err(nic_dev, drv, txq->netdev,
|
||||
"Failed to set TX interrupt coalescing attribute\n");
|
||||
tx_napi_del(txq);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = request_irq(sq->irq, tx_irq, 0, txq->irq_name, txq);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to request Tx irq\n");
|
||||
|
|
|
@ -44,6 +44,8 @@ void hinic_txq_clean_stats(struct hinic_txq *txq);
|
|||
|
||||
void hinic_txq_get_stats(struct hinic_txq *txq, struct hinic_txq_stats *stats);
|
||||
|
||||
netdev_tx_t hinic_lb_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
|
||||
|
||||
netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
|
||||
|
||||
int hinic_init_txq(struct hinic_txq *txq, struct hinic_sq *sq,
|
||||
|
|
Loading…
Reference in a new issue