mirror of
https://github.com/torvalds/linux
synced 2024-10-17 16:58:15 +00:00
Merge branch 'net-hns3-add-aRFS-feature-and-fix-FEC-bugs-for-HNS3-driver'
Huazhong Tan says: ==================== net: hns3: add aRFS feature and fix FEC bugs for HNS3 driver This patchset adds some new features support and fixes some bugs: [Patch 1/4 - 3/4] adds support for aRFS [Patch 4/4] fix FEC configuration issue ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
b25ba113dd
|
@ -343,6 +343,8 @@ struct hnae3_ae_dev {
|
|||
* Enable/disable hardware strip vlan tag of packets received
|
||||
* set_gro_en
|
||||
* Enable/disable HW GRO
|
||||
* add_arfs_entry
|
||||
* Check the 5-tuples of flow, and create flow director rule
|
||||
*/
|
||||
struct hnae3_ae_ops {
|
||||
int (*init_ae_dev)(struct hnae3_ae_dev *ae_dev);
|
||||
|
@ -492,6 +494,8 @@ struct hnae3_ae_ops {
|
|||
struct ethtool_rxnfc *cmd, u32 *rule_locs);
|
||||
int (*restore_fd_rules)(struct hnae3_handle *handle);
|
||||
void (*enable_fd)(struct hnae3_handle *handle, bool enable);
|
||||
int (*add_arfs_entry)(struct hnae3_handle *handle, u16 queue_id,
|
||||
u16 flow_id, struct flow_keys *fkeys);
|
||||
int (*dbg_run_cmd)(struct hnae3_handle *handle, char *cmd_buf);
|
||||
pci_ers_result_t (*handle_hw_ras_error)(struct hnae3_ae_dev *ae_dev);
|
||||
bool (*get_hw_reset_stat)(struct hnae3_handle *handle);
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
#include <linux/dma-mapping.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/interrupt.h>
|
||||
#ifdef CONFIG_RFS_ACCEL
|
||||
#include <linux/cpu_rmap.h>
|
||||
#endif
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
|
@ -79,23 +82,6 @@ static irqreturn_t hns3_irq_handle(int irq, void *vector)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* This callback function is used to set affinity changes to the irq affinity
|
||||
* masks when the irq_set_affinity_notifier function is used.
|
||||
*/
|
||||
static void hns3_nic_irq_affinity_notify(struct irq_affinity_notify *notify,
|
||||
const cpumask_t *mask)
|
||||
{
|
||||
struct hns3_enet_tqp_vector *tqp_vectors =
|
||||
container_of(notify, struct hns3_enet_tqp_vector,
|
||||
affinity_notify);
|
||||
|
||||
tqp_vectors->affinity_mask = *mask;
|
||||
}
|
||||
|
||||
static void hns3_nic_irq_affinity_release(struct kref *ref)
|
||||
{
|
||||
}
|
||||
|
||||
static void hns3_nic_uninit_irq(struct hns3_nic_priv *priv)
|
||||
{
|
||||
struct hns3_enet_tqp_vector *tqp_vectors;
|
||||
|
@ -107,8 +93,7 @@ static void hns3_nic_uninit_irq(struct hns3_nic_priv *priv)
|
|||
if (tqp_vectors->irq_init_flag != HNS3_VECTOR_INITED)
|
||||
continue;
|
||||
|
||||
/* clear the affinity notifier and affinity mask */
|
||||
irq_set_affinity_notifier(tqp_vectors->vector_irq, NULL);
|
||||
/* clear the affinity mask */
|
||||
irq_set_affinity_hint(tqp_vectors->vector_irq, NULL);
|
||||
|
||||
/* release the irq resource */
|
||||
|
@ -161,12 +146,6 @@ static int hns3_nic_init_irq(struct hns3_nic_priv *priv)
|
|||
return ret;
|
||||
}
|
||||
|
||||
tqp_vectors->affinity_notify.notify =
|
||||
hns3_nic_irq_affinity_notify;
|
||||
tqp_vectors->affinity_notify.release =
|
||||
hns3_nic_irq_affinity_release;
|
||||
irq_set_affinity_notifier(tqp_vectors->vector_irq,
|
||||
&tqp_vectors->affinity_notify);
|
||||
irq_set_affinity_hint(tqp_vectors->vector_irq,
|
||||
&tqp_vectors->affinity_mask);
|
||||
|
||||
|
@ -340,6 +319,40 @@ static void hns3_tqp_disable(struct hnae3_queue *tqp)
|
|||
hns3_write_dev(tqp, HNS3_RING_EN_REG, rcb_reg);
|
||||
}
|
||||
|
||||
static void hns3_free_rx_cpu_rmap(struct net_device *netdev)
|
||||
{
|
||||
#ifdef CONFIG_RFS_ACCEL
|
||||
free_irq_cpu_rmap(netdev->rx_cpu_rmap);
|
||||
netdev->rx_cpu_rmap = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int hns3_set_rx_cpu_rmap(struct net_device *netdev)
|
||||
{
|
||||
#ifdef CONFIG_RFS_ACCEL
|
||||
struct hns3_nic_priv *priv = netdev_priv(netdev);
|
||||
struct hns3_enet_tqp_vector *tqp_vector;
|
||||
int i, ret;
|
||||
|
||||
if (!netdev->rx_cpu_rmap) {
|
||||
netdev->rx_cpu_rmap = alloc_irq_cpu_rmap(priv->vector_num);
|
||||
if (!netdev->rx_cpu_rmap)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < priv->vector_num; i++) {
|
||||
tqp_vector = &priv->tqp_vector[i];
|
||||
ret = irq_cpu_rmap_add(netdev->rx_cpu_rmap,
|
||||
tqp_vector->vector_irq);
|
||||
if (ret) {
|
||||
hns3_free_rx_cpu_rmap(netdev);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hns3_nic_net_up(struct net_device *netdev)
|
||||
{
|
||||
struct hns3_nic_priv *priv = netdev_priv(netdev);
|
||||
|
@ -351,11 +364,16 @@ static int hns3_nic_net_up(struct net_device *netdev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* the device can work without cpu rmap, only aRFS needs it */
|
||||
ret = hns3_set_rx_cpu_rmap(netdev);
|
||||
if (ret)
|
||||
netdev_warn(netdev, "set rx cpu rmap fail, ret=%d!\n", ret);
|
||||
|
||||
/* get irq resource for all vectors */
|
||||
ret = hns3_nic_init_irq(priv);
|
||||
if (ret) {
|
||||
netdev_err(netdev, "hns init irq failed! ret=%d\n", ret);
|
||||
return ret;
|
||||
goto free_rmap;
|
||||
}
|
||||
|
||||
clear_bit(HNS3_NIC_STATE_DOWN, &priv->state);
|
||||
|
@ -384,7 +402,8 @@ static int hns3_nic_net_up(struct net_device *netdev)
|
|||
hns3_vector_disable(&priv->tqp_vector[j]);
|
||||
|
||||
hns3_nic_uninit_irq(priv);
|
||||
|
||||
free_rmap:
|
||||
hns3_free_rx_cpu_rmap(netdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -467,6 +486,8 @@ static void hns3_nic_net_down(struct net_device *netdev)
|
|||
if (ops->stop)
|
||||
ops->stop(priv->ae_handle);
|
||||
|
||||
hns3_free_rx_cpu_rmap(netdev);
|
||||
|
||||
/* free irq resources */
|
||||
hns3_nic_uninit_irq(priv);
|
||||
|
||||
|
@ -1722,6 +1743,32 @@ static void hns3_nic_net_timeout(struct net_device *ndev)
|
|||
h->ae_algo->ops->reset_event(h->pdev, h);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_RFS_ACCEL
|
||||
static int hns3_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb,
|
||||
u16 rxq_index, u32 flow_id)
|
||||
{
|
||||
struct hnae3_handle *h = hns3_get_handle(dev);
|
||||
struct flow_keys fkeys;
|
||||
|
||||
if (!h->ae_algo->ops->add_arfs_entry)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (skb->encapsulation)
|
||||
return -EPROTONOSUPPORT;
|
||||
|
||||
if (!skb_flow_dissect_flow_keys(skb, &fkeys, 0))
|
||||
return -EPROTONOSUPPORT;
|
||||
|
||||
if ((fkeys.basic.n_proto != htons(ETH_P_IP) &&
|
||||
fkeys.basic.n_proto != htons(ETH_P_IPV6)) ||
|
||||
(fkeys.basic.ip_proto != IPPROTO_TCP &&
|
||||
fkeys.basic.ip_proto != IPPROTO_UDP))
|
||||
return -EPROTONOSUPPORT;
|
||||
|
||||
return h->ae_algo->ops->add_arfs_entry(h, rxq_index, flow_id, &fkeys);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct net_device_ops hns3_nic_netdev_ops = {
|
||||
.ndo_open = hns3_nic_net_open,
|
||||
.ndo_stop = hns3_nic_net_stop,
|
||||
|
@ -1737,6 +1784,10 @@ static const struct net_device_ops hns3_nic_netdev_ops = {
|
|||
.ndo_vlan_rx_add_vid = hns3_vlan_rx_add_vid,
|
||||
.ndo_vlan_rx_kill_vid = hns3_vlan_rx_kill_vid,
|
||||
.ndo_set_vf_vlan = hns3_ndo_set_vf_vlan,
|
||||
#ifdef CONFIG_RFS_ACCEL
|
||||
.ndo_rx_flow_steer = hns3_rx_flow_steer,
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
bool hns3_is_phys_func(struct pci_dev *pdev)
|
||||
|
@ -2828,6 +2879,7 @@ static int hns3_handle_rx_bd(struct hns3_enet_ring *ring,
|
|||
return ret;
|
||||
}
|
||||
|
||||
skb_record_rx_queue(skb, ring->tqp->tqp_index);
|
||||
*out_skb = skb;
|
||||
|
||||
return 0;
|
||||
|
@ -3331,8 +3383,6 @@ static void hns3_nic_uninit_vector_data(struct hns3_nic_priv *priv)
|
|||
hns3_free_vector_ring_chain(tqp_vector, &vector_ring_chain);
|
||||
|
||||
if (tqp_vector->irq_init_flag == HNS3_VECTOR_INITED) {
|
||||
irq_set_affinity_notifier(tqp_vector->vector_irq,
|
||||
NULL);
|
||||
irq_set_affinity_hint(tqp_vector->vector_irq, NULL);
|
||||
free_irq(tqp_vector->vector_irq, tqp_vector);
|
||||
tqp_vector->irq_init_flag = HNS3_VECTOR_NOT_INITED;
|
||||
|
|
|
@ -35,6 +35,8 @@ static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev);
|
|||
static bool hclge_get_hw_reset_stat(struct hnae3_handle *handle);
|
||||
static int hclge_set_umv_space(struct hclge_dev *hdev, u16 space_size,
|
||||
u16 *allocated_size, bool is_alloc);
|
||||
static void hclge_rfs_filter_expire(struct hclge_dev *hdev);
|
||||
static void hclge_clear_arfs_rules(struct hnae3_handle *handle);
|
||||
|
||||
static struct hnae3_ae_algo ae_algo;
|
||||
|
||||
|
@ -1226,8 +1228,10 @@ static int hclge_configure(struct hclge_dev *hdev)
|
|||
hdev->tm_info.hw_pfc_map = 0;
|
||||
hdev->wanted_umv_size = cfg.umv_space;
|
||||
|
||||
if (hnae3_dev_fd_supported(hdev))
|
||||
if (hnae3_dev_fd_supported(hdev)) {
|
||||
hdev->fd_en = true;
|
||||
hdev->fd_active_type = HCLGE_FD_RULE_NONE;
|
||||
}
|
||||
|
||||
ret = hclge_parse_speed(cfg.default_speed, &hdev->hw.mac.speed);
|
||||
if (ret) {
|
||||
|
@ -2508,6 +2512,9 @@ static void hclge_update_link_status(struct hclge_dev *hdev)
|
|||
|
||||
static void hclge_update_port_capability(struct hclge_mac *mac)
|
||||
{
|
||||
/* update fec ability by speed */
|
||||
hclge_convert_setting_fec(mac);
|
||||
|
||||
/* firmware can not identify back plane type, the media type
|
||||
* read from configuration can help deal it
|
||||
*/
|
||||
|
@ -2580,6 +2587,10 @@ static int hclge_get_sfp_info(struct hclge_dev *hdev, struct hclge_mac *mac)
|
|||
mac->speed_ability = le32_to_cpu(resp->speed_ability);
|
||||
mac->autoneg = resp->autoneg;
|
||||
mac->support_autoneg = resp->autoneg_ability;
|
||||
if (!resp->active_fec)
|
||||
mac->fec_mode = 0;
|
||||
else
|
||||
mac->fec_mode = BIT(resp->active_fec);
|
||||
} else {
|
||||
mac->speed_type = QUERY_SFP_SPEED;
|
||||
}
|
||||
|
@ -2645,6 +2656,7 @@ static void hclge_service_timer(struct timer_list *t)
|
|||
|
||||
mod_timer(&hdev->service_timer, jiffies + HZ);
|
||||
hdev->hw_stats.stats_timer++;
|
||||
hdev->fd_arfs_expire_timer++;
|
||||
hclge_task_schedule(hdev);
|
||||
}
|
||||
|
||||
|
@ -3521,6 +3533,10 @@ static void hclge_service_task(struct work_struct *work)
|
|||
hclge_update_port_info(hdev);
|
||||
hclge_update_link_status(hdev);
|
||||
hclge_update_vport_alive(hdev);
|
||||
if (hdev->fd_arfs_expire_timer >= HCLGE_FD_ARFS_EXPIRE_TIMER_INTERVAL) {
|
||||
hclge_rfs_filter_expire(hdev);
|
||||
hdev->fd_arfs_expire_timer = 0;
|
||||
}
|
||||
hclge_service_complete(hdev);
|
||||
}
|
||||
|
||||
|
@ -4906,14 +4922,18 @@ static bool hclge_fd_rule_exist(struct hclge_dev *hdev, u16 location)
|
|||
struct hclge_fd_rule *rule = NULL;
|
||||
struct hlist_node *node2;
|
||||
|
||||
spin_lock_bh(&hdev->fd_rule_lock);
|
||||
hlist_for_each_entry_safe(rule, node2, &hdev->fd_rule_list, rule_node) {
|
||||
if (rule->location >= location)
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock_bh(&hdev->fd_rule_lock);
|
||||
|
||||
return rule && rule->location == location;
|
||||
}
|
||||
|
||||
/* make sure being called after lock up with fd_rule_lock */
|
||||
static int hclge_fd_update_rule_list(struct hclge_dev *hdev,
|
||||
struct hclge_fd_rule *new_rule,
|
||||
u16 location,
|
||||
|
@ -4937,9 +4957,13 @@ static int hclge_fd_update_rule_list(struct hclge_dev *hdev,
|
|||
kfree(rule);
|
||||
hdev->hclge_fd_rule_num--;
|
||||
|
||||
if (!is_add)
|
||||
return 0;
|
||||
if (!is_add) {
|
||||
if (!hdev->hclge_fd_rule_num)
|
||||
hdev->fd_active_type = HCLGE_FD_RULE_NONE;
|
||||
clear_bit(location, hdev->fd_bmap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} else if (!is_add) {
|
||||
dev_err(&hdev->pdev->dev,
|
||||
"delete fail, rule %d is inexistent\n",
|
||||
|
@ -4954,7 +4978,9 @@ static int hclge_fd_update_rule_list(struct hclge_dev *hdev,
|
|||
else
|
||||
hlist_add_head(&new_rule->rule_node, &hdev->fd_rule_list);
|
||||
|
||||
set_bit(location, hdev->fd_bmap);
|
||||
hdev->hclge_fd_rule_num++;
|
||||
hdev->fd_active_type = new_rule->rule_type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -5112,6 +5138,36 @@ static int hclge_fd_get_tuple(struct hclge_dev *hdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* make sure being called after lock up with fd_rule_lock */
|
||||
static int hclge_fd_config_rule(struct hclge_dev *hdev,
|
||||
struct hclge_fd_rule *rule)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!rule) {
|
||||
dev_err(&hdev->pdev->dev,
|
||||
"The flow director rule is NULL\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* it will never fail here, so needn't to check return value */
|
||||
hclge_fd_update_rule_list(hdev, rule, rule->location, true);
|
||||
|
||||
ret = hclge_config_action(hdev, HCLGE_FD_STAGE_1, rule);
|
||||
if (ret)
|
||||
goto clear_rule;
|
||||
|
||||
ret = hclge_config_key(hdev, HCLGE_FD_STAGE_1, rule);
|
||||
if (ret)
|
||||
goto clear_rule;
|
||||
|
||||
return 0;
|
||||
|
||||
clear_rule:
|
||||
hclge_fd_update_rule_list(hdev, rule, rule->location, false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hclge_add_fd_entry(struct hnae3_handle *handle,
|
||||
struct ethtool_rxnfc *cmd)
|
||||
{
|
||||
|
@ -5174,8 +5230,10 @@ static int hclge_add_fd_entry(struct hnae3_handle *handle,
|
|||
return -ENOMEM;
|
||||
|
||||
ret = hclge_fd_get_tuple(hdev, fs, rule);
|
||||
if (ret)
|
||||
goto free_rule;
|
||||
if (ret) {
|
||||
kfree(rule);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rule->flow_type = fs->flow_type;
|
||||
|
||||
|
@ -5184,24 +5242,19 @@ static int hclge_add_fd_entry(struct hnae3_handle *handle,
|
|||
rule->vf_id = dst_vport_id;
|
||||
rule->queue_id = q_index;
|
||||
rule->action = action;
|
||||
rule->rule_type = HCLGE_FD_EP_ACTIVE;
|
||||
|
||||
ret = hclge_config_action(hdev, HCLGE_FD_STAGE_1, rule);
|
||||
if (ret)
|
||||
goto free_rule;
|
||||
/* to avoid rule conflict, when user configure rule by ethtool,
|
||||
* we need to clear all arfs rules
|
||||
*/
|
||||
hclge_clear_arfs_rules(handle);
|
||||
|
||||
ret = hclge_config_key(hdev, HCLGE_FD_STAGE_1, rule);
|
||||
if (ret)
|
||||
goto free_rule;
|
||||
spin_lock_bh(&hdev->fd_rule_lock);
|
||||
ret = hclge_fd_config_rule(hdev, rule);
|
||||
|
||||
ret = hclge_fd_update_rule_list(hdev, rule, fs->location, true);
|
||||
if (ret)
|
||||
goto free_rule;
|
||||
spin_unlock_bh(&hdev->fd_rule_lock);
|
||||
|
||||
return ret;
|
||||
|
||||
free_rule:
|
||||
kfree(rule);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hclge_del_fd_entry(struct hnae3_handle *handle,
|
||||
|
@ -5232,8 +5285,12 @@ static int hclge_del_fd_entry(struct hnae3_handle *handle,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return hclge_fd_update_rule_list(hdev, NULL, fs->location,
|
||||
false);
|
||||
spin_lock_bh(&hdev->fd_rule_lock);
|
||||
ret = hclge_fd_update_rule_list(hdev, NULL, fs->location, false);
|
||||
|
||||
spin_unlock_bh(&hdev->fd_rule_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hclge_del_all_fd_entries(struct hnae3_handle *handle,
|
||||
|
@ -5243,25 +5300,30 @@ static void hclge_del_all_fd_entries(struct hnae3_handle *handle,
|
|||
struct hclge_dev *hdev = vport->back;
|
||||
struct hclge_fd_rule *rule;
|
||||
struct hlist_node *node;
|
||||
u16 location;
|
||||
|
||||
if (!hnae3_dev_fd_supported(hdev))
|
||||
return;
|
||||
|
||||
spin_lock_bh(&hdev->fd_rule_lock);
|
||||
for_each_set_bit(location, hdev->fd_bmap,
|
||||
hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1])
|
||||
hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, location,
|
||||
NULL, false);
|
||||
|
||||
if (clear_list) {
|
||||
hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list,
|
||||
rule_node) {
|
||||
hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true,
|
||||
rule->location, NULL, false);
|
||||
hlist_del(&rule->rule_node);
|
||||
kfree(rule);
|
||||
hdev->hclge_fd_rule_num--;
|
||||
}
|
||||
} else {
|
||||
hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list,
|
||||
rule_node)
|
||||
hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true,
|
||||
rule->location, NULL, false);
|
||||
hdev->fd_active_type = HCLGE_FD_RULE_NONE;
|
||||
hdev->hclge_fd_rule_num = 0;
|
||||
bitmap_zero(hdev->fd_bmap,
|
||||
hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]);
|
||||
}
|
||||
|
||||
spin_unlock_bh(&hdev->fd_rule_lock);
|
||||
}
|
||||
|
||||
static int hclge_restore_fd_entries(struct hnae3_handle *handle)
|
||||
|
@ -5283,6 +5345,7 @@ static int hclge_restore_fd_entries(struct hnae3_handle *handle)
|
|||
if (!hdev->fd_en)
|
||||
return 0;
|
||||
|
||||
spin_lock_bh(&hdev->fd_rule_lock);
|
||||
hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
|
||||
ret = hclge_config_action(hdev, HCLGE_FD_STAGE_1, rule);
|
||||
if (!ret)
|
||||
|
@ -5292,11 +5355,18 @@ static int hclge_restore_fd_entries(struct hnae3_handle *handle)
|
|||
dev_warn(&hdev->pdev->dev,
|
||||
"Restore rule %d failed, remove it\n",
|
||||
rule->location);
|
||||
clear_bit(rule->location, hdev->fd_bmap);
|
||||
hlist_del(&rule->rule_node);
|
||||
kfree(rule);
|
||||
hdev->hclge_fd_rule_num--;
|
||||
}
|
||||
}
|
||||
|
||||
if (hdev->hclge_fd_rule_num)
|
||||
hdev->fd_active_type = HCLGE_FD_EP_ACTIVE;
|
||||
|
||||
spin_unlock_bh(&hdev->fd_rule_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -5329,13 +5399,18 @@ static int hclge_get_fd_rule_info(struct hnae3_handle *handle,
|
|||
|
||||
fs = (struct ethtool_rx_flow_spec *)&cmd->fs;
|
||||
|
||||
spin_lock_bh(&hdev->fd_rule_lock);
|
||||
|
||||
hlist_for_each_entry_safe(rule, node2, &hdev->fd_rule_list, rule_node) {
|
||||
if (rule->location >= fs->location)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!rule || fs->location != rule->location)
|
||||
if (!rule || fs->location != rule->location) {
|
||||
spin_unlock_bh(&hdev->fd_rule_lock);
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
fs->flow_type = rule->flow_type;
|
||||
switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) {
|
||||
|
@ -5474,6 +5549,7 @@ static int hclge_get_fd_rule_info(struct hnae3_handle *handle,
|
|||
|
||||
break;
|
||||
default:
|
||||
spin_unlock_bh(&hdev->fd_rule_lock);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
|
@ -5505,6 +5581,8 @@ static int hclge_get_fd_rule_info(struct hnae3_handle *handle,
|
|||
fs->ring_cookie |= vf_id;
|
||||
}
|
||||
|
||||
spin_unlock_bh(&hdev->fd_rule_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -5522,20 +5600,210 @@ static int hclge_get_all_rules(struct hnae3_handle *handle,
|
|||
|
||||
cmd->data = hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1];
|
||||
|
||||
spin_lock_bh(&hdev->fd_rule_lock);
|
||||
hlist_for_each_entry_safe(rule, node2,
|
||||
&hdev->fd_rule_list, rule_node) {
|
||||
if (cnt == cmd->rule_cnt)
|
||||
if (cnt == cmd->rule_cnt) {
|
||||
spin_unlock_bh(&hdev->fd_rule_lock);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
rule_locs[cnt] = rule->location;
|
||||
cnt++;
|
||||
}
|
||||
|
||||
spin_unlock_bh(&hdev->fd_rule_lock);
|
||||
|
||||
cmd->rule_cnt = cnt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hclge_fd_get_flow_tuples(const struct flow_keys *fkeys,
|
||||
struct hclge_fd_rule_tuples *tuples)
|
||||
{
|
||||
tuples->ether_proto = be16_to_cpu(fkeys->basic.n_proto);
|
||||
tuples->ip_proto = fkeys->basic.ip_proto;
|
||||
tuples->dst_port = be16_to_cpu(fkeys->ports.dst);
|
||||
|
||||
if (fkeys->basic.n_proto == htons(ETH_P_IP)) {
|
||||
tuples->src_ip[3] = be32_to_cpu(fkeys->addrs.v4addrs.src);
|
||||
tuples->dst_ip[3] = be32_to_cpu(fkeys->addrs.v4addrs.dst);
|
||||
} else {
|
||||
memcpy(tuples->src_ip,
|
||||
fkeys->addrs.v6addrs.src.in6_u.u6_addr32,
|
||||
sizeof(tuples->src_ip));
|
||||
memcpy(tuples->dst_ip,
|
||||
fkeys->addrs.v6addrs.dst.in6_u.u6_addr32,
|
||||
sizeof(tuples->dst_ip));
|
||||
}
|
||||
}
|
||||
|
||||
/* traverse all rules, check whether an existed rule has the same tuples */
|
||||
static struct hclge_fd_rule *
|
||||
hclge_fd_search_flow_keys(struct hclge_dev *hdev,
|
||||
const struct hclge_fd_rule_tuples *tuples)
|
||||
{
|
||||
struct hclge_fd_rule *rule = NULL;
|
||||
struct hlist_node *node;
|
||||
|
||||
hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
|
||||
if (!memcmp(tuples, &rule->tuples, sizeof(*tuples)))
|
||||
return rule;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void hclge_fd_build_arfs_rule(const struct hclge_fd_rule_tuples *tuples,
|
||||
struct hclge_fd_rule *rule)
|
||||
{
|
||||
rule->unused_tuple = BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) |
|
||||
BIT(INNER_VLAN_TAG_FST) | BIT(INNER_IP_TOS) |
|
||||
BIT(INNER_SRC_PORT);
|
||||
rule->action = 0;
|
||||
rule->vf_id = 0;
|
||||
rule->rule_type = HCLGE_FD_ARFS_ACTIVE;
|
||||
if (tuples->ether_proto == ETH_P_IP) {
|
||||
if (tuples->ip_proto == IPPROTO_TCP)
|
||||
rule->flow_type = TCP_V4_FLOW;
|
||||
else
|
||||
rule->flow_type = UDP_V4_FLOW;
|
||||
} else {
|
||||
if (tuples->ip_proto == IPPROTO_TCP)
|
||||
rule->flow_type = TCP_V6_FLOW;
|
||||
else
|
||||
rule->flow_type = UDP_V6_FLOW;
|
||||
}
|
||||
memcpy(&rule->tuples, tuples, sizeof(rule->tuples));
|
||||
memset(&rule->tuples_mask, 0xFF, sizeof(rule->tuples_mask));
|
||||
}
|
||||
|
||||
static int hclge_add_fd_entry_by_arfs(struct hnae3_handle *handle, u16 queue_id,
|
||||
u16 flow_id, struct flow_keys *fkeys)
|
||||
{
|
||||
#ifdef CONFIG_RFS_ACCEL
|
||||
struct hclge_vport *vport = hclge_get_vport(handle);
|
||||
struct hclge_fd_rule_tuples new_tuples;
|
||||
struct hclge_dev *hdev = vport->back;
|
||||
struct hclge_fd_rule *rule;
|
||||
u16 tmp_queue_id;
|
||||
u16 bit_id;
|
||||
int ret;
|
||||
|
||||
if (!hnae3_dev_fd_supported(hdev))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
memset(&new_tuples, 0, sizeof(new_tuples));
|
||||
hclge_fd_get_flow_tuples(fkeys, &new_tuples);
|
||||
|
||||
spin_lock_bh(&hdev->fd_rule_lock);
|
||||
|
||||
/* when there is already fd rule existed add by user,
|
||||
* arfs should not work
|
||||
*/
|
||||
if (hdev->fd_active_type == HCLGE_FD_EP_ACTIVE) {
|
||||
spin_unlock_bh(&hdev->fd_rule_lock);
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* check is there flow director filter existed for this flow,
|
||||
* if not, create a new filter for it;
|
||||
* if filter exist with different queue id, modify the filter;
|
||||
* if filter exist with same queue id, do nothing
|
||||
*/
|
||||
rule = hclge_fd_search_flow_keys(hdev, &new_tuples);
|
||||
if (!rule) {
|
||||
bit_id = find_first_zero_bit(hdev->fd_bmap, MAX_FD_FILTER_NUM);
|
||||
if (bit_id >= hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) {
|
||||
spin_unlock_bh(&hdev->fd_rule_lock);
|
||||
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
rule = kzalloc(sizeof(*rule), GFP_KERNEL);
|
||||
if (!rule) {
|
||||
spin_unlock_bh(&hdev->fd_rule_lock);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
set_bit(bit_id, hdev->fd_bmap);
|
||||
rule->location = bit_id;
|
||||
rule->flow_id = flow_id;
|
||||
rule->queue_id = queue_id;
|
||||
hclge_fd_build_arfs_rule(&new_tuples, rule);
|
||||
ret = hclge_fd_config_rule(hdev, rule);
|
||||
|
||||
spin_unlock_bh(&hdev->fd_rule_lock);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return rule->location;
|
||||
}
|
||||
|
||||
spin_unlock_bh(&hdev->fd_rule_lock);
|
||||
|
||||
if (rule->queue_id == queue_id)
|
||||
return rule->location;
|
||||
|
||||
tmp_queue_id = rule->queue_id;
|
||||
rule->queue_id = queue_id;
|
||||
ret = hclge_config_action(hdev, HCLGE_FD_STAGE_1, rule);
|
||||
if (ret) {
|
||||
rule->queue_id = tmp_queue_id;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return rule->location;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void hclge_rfs_filter_expire(struct hclge_dev *hdev)
|
||||
{
|
||||
#ifdef CONFIG_RFS_ACCEL
|
||||
struct hnae3_handle *handle = &hdev->vport[0].nic;
|
||||
struct hclge_fd_rule *rule;
|
||||
struct hlist_node *node;
|
||||
HLIST_HEAD(del_list);
|
||||
|
||||
spin_lock_bh(&hdev->fd_rule_lock);
|
||||
if (hdev->fd_active_type != HCLGE_FD_ARFS_ACTIVE) {
|
||||
spin_unlock_bh(&hdev->fd_rule_lock);
|
||||
return;
|
||||
}
|
||||
hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
|
||||
if (rps_may_expire_flow(handle->netdev, rule->queue_id,
|
||||
rule->flow_id, rule->location)) {
|
||||
hlist_del_init(&rule->rule_node);
|
||||
hlist_add_head(&rule->rule_node, &del_list);
|
||||
hdev->hclge_fd_rule_num--;
|
||||
clear_bit(rule->location, hdev->fd_bmap);
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&hdev->fd_rule_lock);
|
||||
|
||||
hlist_for_each_entry_safe(rule, node, &del_list, rule_node) {
|
||||
hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true,
|
||||
rule->location, NULL, false);
|
||||
kfree(rule);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void hclge_clear_arfs_rules(struct hnae3_handle *handle)
|
||||
{
|
||||
#ifdef CONFIG_RFS_ACCEL
|
||||
struct hclge_vport *vport = hclge_get_vport(handle);
|
||||
struct hclge_dev *hdev = vport->back;
|
||||
|
||||
if (hdev->fd_active_type == HCLGE_FD_ARFS_ACTIVE)
|
||||
hclge_del_all_fd_entries(handle, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool hclge_get_hw_reset_stat(struct hnae3_handle *handle)
|
||||
{
|
||||
struct hclge_vport *vport = hclge_get_vport(handle);
|
||||
|
@ -5565,10 +5833,12 @@ static void hclge_enable_fd(struct hnae3_handle *handle, bool enable)
|
|||
{
|
||||
struct hclge_vport *vport = hclge_get_vport(handle);
|
||||
struct hclge_dev *hdev = vport->back;
|
||||
bool clear;
|
||||
|
||||
hdev->fd_en = enable;
|
||||
clear = hdev->fd_active_type == HCLGE_FD_ARFS_ACTIVE ? true : false;
|
||||
if (!enable)
|
||||
hclge_del_all_fd_entries(handle, false);
|
||||
hclge_del_all_fd_entries(handle, clear);
|
||||
else
|
||||
hclge_restore_fd_entries(handle);
|
||||
}
|
||||
|
@ -5838,6 +6108,8 @@ static void hclge_ae_stop(struct hnae3_handle *handle)
|
|||
|
||||
set_bit(HCLGE_STATE_DOWN, &hdev->state);
|
||||
|
||||
hclge_clear_arfs_rules(handle);
|
||||
|
||||
/* If it is not PF reset, the firmware will disable the MAC,
|
||||
* so it only need to stop phy here.
|
||||
*/
|
||||
|
@ -8143,6 +8415,7 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
|
|||
|
||||
mutex_init(&hdev->vport_lock);
|
||||
mutex_init(&hdev->vport_cfg_mutex);
|
||||
spin_lock_init(&hdev->fd_rule_lock);
|
||||
|
||||
ret = hclge_pci_init(hdev);
|
||||
if (ret) {
|
||||
|
@ -8908,6 +9181,7 @@ static const struct hnae3_ae_ops hclge_ops = {
|
|||
.get_fd_all_rules = hclge_get_all_rules,
|
||||
.restore_fd_rules = hclge_restore_fd_entries,
|
||||
.enable_fd = hclge_enable_fd,
|
||||
.add_arfs_entry = hclge_add_fd_entry_by_arfs,
|
||||
.dbg_run_cmd = hclge_dbg_run_cmd,
|
||||
.handle_hw_ras_error = hclge_handle_hw_ras_error,
|
||||
.get_hw_reset_stat = hclge_get_hw_reset_stat,
|
||||
|
|
|
@ -578,6 +578,16 @@ static const struct key_info tuple_key_info[] = {
|
|||
#define MAX_KEY_BYTES (MAX_KEY_DWORDS * 4)
|
||||
#define MAX_META_DATA_LENGTH 32
|
||||
|
||||
/* assigned by firmware, the real filter number for each pf may be less */
|
||||
#define MAX_FD_FILTER_NUM 4096
|
||||
#define HCLGE_FD_ARFS_EXPIRE_TIMER_INTERVAL 5
|
||||
|
||||
enum HCLGE_FD_ACTIVE_RULE_TYPE {
|
||||
HCLGE_FD_RULE_NONE,
|
||||
HCLGE_FD_ARFS_ACTIVE,
|
||||
HCLGE_FD_EP_ACTIVE,
|
||||
};
|
||||
|
||||
enum HCLGE_FD_PACKET_TYPE {
|
||||
NIC_PACKET,
|
||||
ROCE_PACKET,
|
||||
|
@ -630,6 +640,8 @@ struct hclge_fd_rule {
|
|||
u16 vf_id;
|
||||
u16 queue_id;
|
||||
u16 location;
|
||||
u16 flow_id; /* only used for arfs */
|
||||
enum HCLGE_FD_ACTIVE_RULE_TYPE rule_type;
|
||||
};
|
||||
|
||||
struct hclge_fd_ad_data {
|
||||
|
@ -809,7 +821,11 @@ struct hclge_dev {
|
|||
|
||||
struct hclge_fd_cfg fd_cfg;
|
||||
struct hlist_head fd_rule_list;
|
||||
spinlock_t fd_rule_lock; /* protect fd_rule_list and fd_bmap */
|
||||
u16 hclge_fd_rule_num;
|
||||
u16 fd_arfs_expire_timer;
|
||||
unsigned long fd_bmap[BITS_TO_LONGS(MAX_FD_FILTER_NUM)];
|
||||
enum HCLGE_FD_ACTIVE_RULE_TYPE fd_active_type;
|
||||
u8 fd_en;
|
||||
|
||||
u16 wanted_umv_size;
|
||||
|
|
Loading…
Reference in a new issue