net: aquantia: add infrastructure for ntuple rules
Add infrastructure to support ntuple filter configuration. Add rule, remove rule, reapply on interface up. Signed-off-by: Dmitry Bogdanov <dmitry.bogdanov@aquantia.com> Signed-off-by: Igor Russkikh <igor.russkikh@aquantia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
23e7a718a4
commit
8d0bcb012f
|
@ -36,6 +36,7 @@ atlantic-objs := aq_main.o \
|
|||
aq_ring.o \
|
||||
aq_hw_utils.o \
|
||||
aq_ethtool.o \
|
||||
aq_filters.o \
|
||||
hw_atl/hw_atl_a0.o \
|
||||
hw_atl/hw_atl_b0.o \
|
||||
hw_atl/hw_atl_utils.o \
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "aq_ethtool.h"
|
||||
#include "aq_nic.h"
|
||||
#include "aq_vec.h"
|
||||
#include "aq_filters.h"
|
||||
|
||||
static void aq_ethtool_get_regs(struct net_device *ndev,
|
||||
struct ethtool_regs *regs, void *p)
|
||||
|
@ -213,7 +214,36 @@ static int aq_ethtool_get_rxnfc(struct net_device *ndev,
|
|||
case ETHTOOL_GRXRINGS:
|
||||
cmd->data = cfg->vecs;
|
||||
break;
|
||||
case ETHTOOL_GRXCLSRLCNT:
|
||||
cmd->rule_cnt = aq_get_rxnfc_count_all_rules(aq_nic);
|
||||
break;
|
||||
case ETHTOOL_GRXCLSRULE:
|
||||
err = aq_get_rxnfc_rule(aq_nic, cmd);
|
||||
break;
|
||||
case ETHTOOL_GRXCLSRLALL:
|
||||
err = aq_get_rxnfc_all_rules(aq_nic, cmd, rule_locs);
|
||||
break;
|
||||
default:
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int aq_ethtool_set_rxnfc(struct net_device *ndev,
|
||||
struct ethtool_rxnfc *cmd)
|
||||
{
|
||||
int err = 0;
|
||||
struct aq_nic_s *aq_nic = netdev_priv(ndev);
|
||||
|
||||
switch (cmd->cmd) {
|
||||
case ETHTOOL_SRXCLSRLINS:
|
||||
err = aq_add_rxnfc_rule(aq_nic, cmd);
|
||||
break;
|
||||
case ETHTOOL_SRXCLSRLDEL:
|
||||
err = aq_del_rxnfc_rule(aq_nic, cmd);
|
||||
break;
|
||||
default:
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
|
@ -520,6 +550,7 @@ const struct ethtool_ops aq_ethtool_ops = {
|
|||
.get_rxfh_key_size = aq_ethtool_get_rss_key_size,
|
||||
.get_rxfh = aq_ethtool_get_rss,
|
||||
.get_rxnfc = aq_ethtool_get_rxnfc,
|
||||
.set_rxnfc = aq_ethtool_set_rxnfc,
|
||||
.get_sset_count = aq_ethtool_get_sset_count,
|
||||
.get_ethtool_stats = aq_ethtool_stats,
|
||||
.get_link_ksettings = aq_ethtool_get_link_ksettings,
|
||||
|
|
|
@ -0,0 +1,413 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/* Copyright (C) 2014-2017 aQuantia Corporation. */
|
||||
|
||||
/* File aq_filters.c: RX filters related functions. */
|
||||
|
||||
#include "aq_filters.h"
|
||||
|
||||
static bool __must_check
|
||||
aq_rule_is_approve(struct ethtool_rx_flow_spec *fsp)
|
||||
{
|
||||
if (fsp->flow_type & FLOW_MAC_EXT)
|
||||
return false;
|
||||
|
||||
switch (fsp->flow_type & ~FLOW_EXT) {
|
||||
case ETHER_FLOW:
|
||||
case TCP_V4_FLOW:
|
||||
case UDP_V4_FLOW:
|
||||
case SCTP_V4_FLOW:
|
||||
case TCP_V6_FLOW:
|
||||
case UDP_V6_FLOW:
|
||||
case SCTP_V6_FLOW:
|
||||
case IPV4_FLOW:
|
||||
case IPV6_FLOW:
|
||||
return true;
|
||||
case IP_USER_FLOW:
|
||||
switch (fsp->h_u.usr_ip4_spec.proto) {
|
||||
case IPPROTO_TCP:
|
||||
case IPPROTO_UDP:
|
||||
case IPPROTO_SCTP:
|
||||
case IPPROTO_IP:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
case IPV6_USER_FLOW:
|
||||
switch (fsp->h_u.usr_ip6_spec.l4_proto) {
|
||||
case IPPROTO_TCP:
|
||||
case IPPROTO_UDP:
|
||||
case IPPROTO_SCTP:
|
||||
case IPPROTO_IP:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool __must_check
|
||||
aq_match_filter(struct ethtool_rx_flow_spec *fsp1,
|
||||
struct ethtool_rx_flow_spec *fsp2)
|
||||
{
|
||||
if (fsp1->flow_type != fsp2->flow_type ||
|
||||
memcmp(&fsp1->h_u, &fsp2->h_u, sizeof(fsp2->h_u)) ||
|
||||
memcmp(&fsp1->h_ext, &fsp2->h_ext, sizeof(fsp2->h_ext)) ||
|
||||
memcmp(&fsp1->m_u, &fsp2->m_u, sizeof(fsp2->m_u)) ||
|
||||
memcmp(&fsp1->m_ext, &fsp2->m_ext, sizeof(fsp2->m_ext)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __must_check
|
||||
aq_rule_already_exists(struct aq_nic_s *aq_nic,
|
||||
struct ethtool_rx_flow_spec *fsp)
|
||||
{
|
||||
struct aq_rx_filter *rule;
|
||||
struct hlist_node *aq_node2;
|
||||
struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
|
||||
|
||||
hlist_for_each_entry_safe(rule, aq_node2,
|
||||
&rx_fltrs->filter_list, aq_node) {
|
||||
if (rule->aq_fsp.location == fsp->location)
|
||||
continue;
|
||||
if (aq_match_filter(&rule->aq_fsp, fsp)) {
|
||||
netdev_err(aq_nic->ndev,
|
||||
"ethtool: This filter is already set\n");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int __must_check
|
||||
aq_check_filter(struct aq_nic_s *aq_nic,
|
||||
struct ethtool_rx_flow_spec *fsp)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (fsp->flow_type & FLOW_EXT) {
|
||||
err = -EOPNOTSUPP;
|
||||
} else {
|
||||
switch (fsp->flow_type & ~FLOW_EXT) {
|
||||
case ETHER_FLOW:
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
case TCP_V4_FLOW:
|
||||
case UDP_V4_FLOW:
|
||||
case SCTP_V4_FLOW:
|
||||
case IPV4_FLOW:
|
||||
case IP_USER_FLOW:
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
case TCP_V6_FLOW:
|
||||
case UDP_V6_FLOW:
|
||||
case SCTP_V6_FLOW:
|
||||
case IPV6_FLOW:
|
||||
case IPV6_USER_FLOW:
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
default:
|
||||
netdev_err(aq_nic->ndev,
|
||||
"ethtool: unknown flow-type specified");
|
||||
err = -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool __must_check
|
||||
aq_rule_is_not_support(struct aq_nic_s *aq_nic,
|
||||
struct ethtool_rx_flow_spec *fsp)
|
||||
{
|
||||
bool rule_is_not_support = false;
|
||||
|
||||
if (!(aq_nic->ndev->features & NETIF_F_NTUPLE)) {
|
||||
netdev_err(aq_nic->ndev,
|
||||
"ethtool: Please, to enable the RX flow control:\n"
|
||||
"ethtool -K %s ntuple on\n", aq_nic->ndev->name);
|
||||
rule_is_not_support = true;
|
||||
} else if (!aq_rule_is_approve(fsp)) {
|
||||
netdev_err(aq_nic->ndev,
|
||||
"ethtool: The specified flow type is not supported\n");
|
||||
rule_is_not_support = true;
|
||||
} else if ((fsp->flow_type & ~FLOW_EXT) != ETHER_FLOW &&
|
||||
(fsp->h_u.tcp_ip4_spec.tos ||
|
||||
fsp->h_u.tcp_ip6_spec.tclass)) {
|
||||
netdev_err(aq_nic->ndev,
|
||||
"ethtool: The specified tos tclass are not supported\n");
|
||||
rule_is_not_support = true;
|
||||
}
|
||||
|
||||
return rule_is_not_support;
|
||||
}
|
||||
|
||||
static bool __must_check
|
||||
aq_rule_is_not_correct(struct aq_nic_s *aq_nic,
|
||||
struct ethtool_rx_flow_spec *fsp)
|
||||
{
|
||||
bool rule_is_not_correct = false;
|
||||
|
||||
if (!aq_nic) {
|
||||
rule_is_not_correct = true;
|
||||
} else if (aq_check_filter(aq_nic, fsp)) {
|
||||
rule_is_not_correct = true;
|
||||
} else if (fsp->ring_cookie != RX_CLS_FLOW_DISC) {
|
||||
if (fsp->ring_cookie >= aq_nic->aq_nic_cfg.num_rss_queues) {
|
||||
netdev_err(aq_nic->ndev,
|
||||
"ethtool: The specified action is invalid.\n"
|
||||
"Maximum allowable value action is %u.\n",
|
||||
aq_nic->aq_nic_cfg.num_rss_queues - 1);
|
||||
rule_is_not_correct = true;
|
||||
}
|
||||
}
|
||||
|
||||
return rule_is_not_correct;
|
||||
}
|
||||
|
||||
static int __must_check
|
||||
aq_check_rule(struct aq_nic_s *aq_nic,
|
||||
struct ethtool_rx_flow_spec *fsp)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (aq_rule_is_not_correct(aq_nic, fsp))
|
||||
err = -EINVAL;
|
||||
else if (aq_rule_is_not_support(aq_nic, fsp))
|
||||
err = -EOPNOTSUPP;
|
||||
else if (aq_rule_already_exists(aq_nic, fsp))
|
||||
err = -EEXIST;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int aq_add_del_rule(struct aq_nic_s *aq_nic,
|
||||
struct aq_rx_filter *aq_rx_fltr, bool add)
|
||||
{
|
||||
int err = -EINVAL;
|
||||
|
||||
if (aq_rx_fltr->aq_fsp.flow_type & FLOW_EXT) {
|
||||
err = -EOPNOTSUPP;
|
||||
} else {
|
||||
switch (aq_rx_fltr->aq_fsp.flow_type & ~FLOW_EXT) {
|
||||
case ETHER_FLOW:
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
case TCP_V4_FLOW:
|
||||
case UDP_V4_FLOW:
|
||||
case SCTP_V4_FLOW:
|
||||
case IP_USER_FLOW:
|
||||
case TCP_V6_FLOW:
|
||||
case UDP_V6_FLOW:
|
||||
case SCTP_V6_FLOW:
|
||||
case IPV6_USER_FLOW:
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int aq_update_table_filters(struct aq_nic_s *aq_nic,
|
||||
struct aq_rx_filter *aq_rx_fltr, u16 index,
|
||||
struct ethtool_rxnfc *cmd)
|
||||
{
|
||||
struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
|
||||
struct aq_rx_filter *rule = NULL, *parent = NULL;
|
||||
struct hlist_node *aq_node2;
|
||||
int err = -EINVAL;
|
||||
|
||||
hlist_for_each_entry_safe(rule, aq_node2,
|
||||
&rx_fltrs->filter_list, aq_node) {
|
||||
if (rule->aq_fsp.location >= index)
|
||||
break;
|
||||
parent = rule;
|
||||
}
|
||||
|
||||
if (rule && rule->aq_fsp.location == index) {
|
||||
err = aq_add_del_rule(aq_nic, rule, false);
|
||||
hlist_del(&rule->aq_node);
|
||||
kfree(rule);
|
||||
--rx_fltrs->active_filters;
|
||||
}
|
||||
|
||||
if (unlikely(!aq_rx_fltr))
|
||||
return err;
|
||||
|
||||
INIT_HLIST_NODE(&aq_rx_fltr->aq_node);
|
||||
|
||||
if (parent)
|
||||
hlist_add_behind(&aq_rx_fltr->aq_node, &parent->aq_node);
|
||||
else
|
||||
hlist_add_head(&aq_rx_fltr->aq_node, &rx_fltrs->filter_list);
|
||||
|
||||
++rx_fltrs->active_filters;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u16 aq_get_rxnfc_count_all_rules(struct aq_nic_s *aq_nic)
|
||||
{
|
||||
struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
|
||||
|
||||
return rx_fltrs->active_filters;
|
||||
}
|
||||
|
||||
struct aq_hw_rx_fltrs_s *aq_get_hw_rx_fltrs(struct aq_nic_s *aq_nic)
|
||||
{
|
||||
return &aq_nic->aq_hw_rx_fltrs;
|
||||
}
|
||||
|
||||
int aq_add_rxnfc_rule(struct aq_nic_s *aq_nic, const struct ethtool_rxnfc *cmd)
|
||||
{
|
||||
struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
|
||||
struct ethtool_rx_flow_spec *fsp =
|
||||
(struct ethtool_rx_flow_spec *)&cmd->fs;
|
||||
struct aq_rx_filter *aq_rx_fltr;
|
||||
int err = 0;
|
||||
|
||||
err = aq_check_rule(aq_nic, fsp);
|
||||
if (err)
|
||||
goto err_exit;
|
||||
|
||||
aq_rx_fltr = kzalloc(sizeof(*aq_rx_fltr), GFP_KERNEL);
|
||||
if (unlikely(!aq_rx_fltr)) {
|
||||
err = -ENOMEM;
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
memcpy(&aq_rx_fltr->aq_fsp, fsp, sizeof(*fsp));
|
||||
|
||||
err = aq_update_table_filters(aq_nic, aq_rx_fltr, fsp->location, NULL);
|
||||
if (unlikely(err))
|
||||
goto err_free;
|
||||
|
||||
err = aq_add_del_rule(aq_nic, aq_rx_fltr, true);
|
||||
if (unlikely(err)) {
|
||||
hlist_del(&aq_rx_fltr->aq_node);
|
||||
--rx_fltrs->active_filters;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
kfree(aq_rx_fltr);
|
||||
err_exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int aq_del_rxnfc_rule(struct aq_nic_s *aq_nic, const struct ethtool_rxnfc *cmd)
|
||||
{
|
||||
struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
|
||||
struct aq_rx_filter *rule = NULL;
|
||||
struct hlist_node *aq_node2;
|
||||
int err = -EINVAL;
|
||||
|
||||
hlist_for_each_entry_safe(rule, aq_node2,
|
||||
&rx_fltrs->filter_list, aq_node) {
|
||||
if (rule->aq_fsp.location == cmd->fs.location)
|
||||
break;
|
||||
}
|
||||
|
||||
if (rule && rule->aq_fsp.location == cmd->fs.location) {
|
||||
err = aq_add_del_rule(aq_nic, rule, false);
|
||||
hlist_del(&rule->aq_node);
|
||||
kfree(rule);
|
||||
--rx_fltrs->active_filters;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int aq_get_rxnfc_rule(struct aq_nic_s *aq_nic, struct ethtool_rxnfc *cmd)
|
||||
{
|
||||
struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
|
||||
struct ethtool_rx_flow_spec *fsp =
|
||||
(struct ethtool_rx_flow_spec *)&cmd->fs;
|
||||
struct aq_rx_filter *rule = NULL;
|
||||
struct hlist_node *aq_node2;
|
||||
|
||||
hlist_for_each_entry_safe(rule, aq_node2,
|
||||
&rx_fltrs->filter_list, aq_node)
|
||||
if (fsp->location <= rule->aq_fsp.location)
|
||||
break;
|
||||
|
||||
if (unlikely(!rule || fsp->location != rule->aq_fsp.location))
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(fsp, &rule->aq_fsp, sizeof(*fsp));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int aq_get_rxnfc_all_rules(struct aq_nic_s *aq_nic, struct ethtool_rxnfc *cmd,
|
||||
u32 *rule_locs)
|
||||
{
|
||||
struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
|
||||
struct hlist_node *aq_node2;
|
||||
struct aq_rx_filter *rule;
|
||||
int count = 0;
|
||||
|
||||
cmd->data = aq_get_rxnfc_count_all_rules(aq_nic);
|
||||
|
||||
hlist_for_each_entry_safe(rule, aq_node2,
|
||||
&rx_fltrs->filter_list, aq_node) {
|
||||
if (unlikely(count == cmd->rule_cnt))
|
||||
return -EMSGSIZE;
|
||||
|
||||
rule_locs[count++] = rule->aq_fsp.location;
|
||||
}
|
||||
|
||||
cmd->rule_cnt = count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int aq_clear_rxnfc_all_rules(struct aq_nic_s *aq_nic)
|
||||
{
|
||||
struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
|
||||
struct hlist_node *aq_node2;
|
||||
struct aq_rx_filter *rule;
|
||||
int err = 0;
|
||||
|
||||
hlist_for_each_entry_safe(rule, aq_node2,
|
||||
&rx_fltrs->filter_list, aq_node) {
|
||||
err = aq_add_del_rule(aq_nic, rule, false);
|
||||
if (err)
|
||||
goto err_exit;
|
||||
hlist_del(&rule->aq_node);
|
||||
kfree(rule);
|
||||
--rx_fltrs->active_filters;
|
||||
}
|
||||
|
||||
err_exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
int aq_reapply_rxnfc_all_rules(struct aq_nic_s *aq_nic)
|
||||
{
|
||||
struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
|
||||
struct hlist_node *aq_node2;
|
||||
struct aq_rx_filter *rule;
|
||||
int err = 0;
|
||||
|
||||
hlist_for_each_entry_safe(rule, aq_node2,
|
||||
&rx_fltrs->filter_list, aq_node) {
|
||||
err = aq_add_del_rule(aq_nic, rule, true);
|
||||
if (err)
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
err_exit:
|
||||
return err;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/* Copyright (C) 2014-2017 aQuantia Corporation. */
|
||||
|
||||
/* File aq_filters.h: RX filters related functions. */
|
||||
|
||||
#ifndef AQ_FILTERS_H
|
||||
#define AQ_FILTERS_H
|
||||
|
||||
#include "aq_nic.h"
|
||||
|
||||
enum aq_rx_filter_type {
|
||||
aq_rx_filter_l3l4
|
||||
};
|
||||
|
||||
struct aq_rx_filter {
|
||||
struct hlist_node aq_node;
|
||||
enum aq_rx_filter_type type;
|
||||
struct ethtool_rx_flow_spec aq_fsp;
|
||||
};
|
||||
|
||||
u16 aq_get_rxnfc_count_all_rules(struct aq_nic_s *aq_nic);
|
||||
struct aq_hw_rx_fltrs_s *aq_get_hw_rx_fltrs(struct aq_nic_s *aq_nic);
|
||||
int aq_add_rxnfc_rule(struct aq_nic_s *aq_nic, const struct ethtool_rxnfc *cmd);
|
||||
int aq_del_rxnfc_rule(struct aq_nic_s *aq_nic, const struct ethtool_rxnfc *cmd);
|
||||
int aq_get_rxnfc_rule(struct aq_nic_s *aq_nic, struct ethtool_rxnfc *cmd);
|
||||
int aq_get_rxnfc_all_rules(struct aq_nic_s *aq_nic, struct ethtool_rxnfc *cmd,
|
||||
u32 *rule_locs);
|
||||
int aq_clear_rxnfc_all_rules(struct aq_nic_s *aq_nic);
|
||||
int aq_reapply_rxnfc_all_rules(struct aq_nic_s *aq_nic);
|
||||
|
||||
#endif /* AQ_FILTERS_H */
|
|
@ -13,6 +13,7 @@
|
|||
#include "aq_nic.h"
|
||||
#include "aq_pci_func.h"
|
||||
#include "aq_ethtool.h"
|
||||
#include "aq_filters.h"
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -49,6 +50,11 @@ static int aq_ndev_open(struct net_device *ndev)
|
|||
err = aq_nic_init(aq_nic);
|
||||
if (err < 0)
|
||||
goto err_exit;
|
||||
|
||||
err = aq_reapply_rxnfc_all_rules(aq_nic);
|
||||
if (err < 0)
|
||||
goto err_exit;
|
||||
|
||||
err = aq_nic_start(aq_nic);
|
||||
if (err < 0)
|
||||
goto err_exit;
|
||||
|
@ -101,6 +107,14 @@ static int aq_ndev_set_features(struct net_device *ndev,
|
|||
bool is_lro = false;
|
||||
int err = 0;
|
||||
|
||||
if (!(features & NETIF_F_NTUPLE)) {
|
||||
if (aq_nic->ndev->features & NETIF_F_NTUPLE) {
|
||||
err = aq_clear_rxnfc_all_rules(aq_nic);
|
||||
if (unlikely(err))
|
||||
goto err_exit;
|
||||
}
|
||||
}
|
||||
|
||||
aq_cfg->features = features;
|
||||
|
||||
if (aq_cfg->aq_hw_caps->hw_features & NETIF_F_LRO) {
|
||||
|
@ -119,6 +133,7 @@ static int aq_ndev_set_features(struct net_device *ndev,
|
|||
err = aq_nic->aq_hw_ops->hw_set_offload(aq_nic->aq_hw,
|
||||
aq_cfg);
|
||||
|
||||
err_exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,11 @@ struct aq_nic_cfg_s {
|
|||
#define AQ_NIC_TCVEC2RING(_NIC_, _TC_, _VEC_) \
|
||||
((_TC_) * AQ_CFG_TCS_MAX + (_VEC_))
|
||||
|
||||
struct aq_hw_rx_fltrs_s {
|
||||
struct hlist_head filter_list;
|
||||
u16 active_filters;
|
||||
};
|
||||
|
||||
struct aq_nic_s {
|
||||
atomic_t flags;
|
||||
struct aq_vec_s *aq_vec[AQ_CFG_VECS_MAX];
|
||||
|
@ -85,6 +90,7 @@ struct aq_nic_s {
|
|||
struct pci_dev *pdev;
|
||||
unsigned int msix_entry_mask;
|
||||
u32 irqvecs;
|
||||
struct aq_hw_rx_fltrs_s aq_hw_rx_fltrs;
|
||||
};
|
||||
|
||||
static inline struct device *aq_nic_get_dev(struct aq_nic_s *self)
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "aq_pci_func.h"
|
||||
#include "hw_atl/hw_atl_a0.h"
|
||||
#include "hw_atl/hw_atl_b0.h"
|
||||
#include "aq_filters.h"
|
||||
|
||||
static const struct pci_device_id aq_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(AQUANTIA, AQ_DEVICE_ID_0001), },
|
||||
|
@ -309,6 +310,7 @@ static void aq_pci_remove(struct pci_dev *pdev)
|
|||
struct aq_nic_s *self = pci_get_drvdata(pdev);
|
||||
|
||||
if (self->ndev) {
|
||||
aq_clear_rxnfc_all_rules(self);
|
||||
if (self->ndev->reg_state == NETREG_REGISTERED)
|
||||
unregister_netdev(self->ndev);
|
||||
aq_nic_free_vectors(self);
|
||||
|
|
|
@ -41,7 +41,8 @@
|
|||
NETIF_F_RXHASH | \
|
||||
NETIF_F_SG | \
|
||||
NETIF_F_TSO | \
|
||||
NETIF_F_LRO, \
|
||||
NETIF_F_LRO | \
|
||||
NETIF_F_NTUPLE, \
|
||||
.hw_priv_flags = IFF_UNICAST_FLT, \
|
||||
.flow_control = true, \
|
||||
.mtu = HW_ATL_B0_MTU_JUMBO, \
|
||||
|
|
Loading…
Reference in New Issue