linux/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c

1115 lines
24 KiB
C
Raw Normal View History

// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/*
* Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
* stmmac TC Handling (HW only)
*/
#include <net/pkt_cls.h>
#include <net/tc_act/tc_gact.h>
#include "common.h"
#include "dwmac4.h"
#include "dwmac5.h"
#include "stmmac.h"
static void tc_fill_all_pass_entry(struct stmmac_tc_entry *entry)
{
memset(entry, 0, sizeof(*entry));
entry->in_use = true;
entry->is_last = true;
entry->is_frag = false;
entry->prio = ~0x0;
entry->handle = 0;
entry->val.match_data = 0x0;
entry->val.match_en = 0x0;
entry->val.af = 1;
entry->val.dma_ch_no = 0x0;
}
static struct stmmac_tc_entry *tc_find_entry(struct stmmac_priv *priv,
struct tc_cls_u32_offload *cls,
bool free)
{
struct stmmac_tc_entry *entry, *first = NULL, *dup = NULL;
u32 loc = cls->knode.handle;
int i;
for (i = 0; i < priv->tc_entries_max; i++) {
entry = &priv->tc_entries[i];
if (!entry->in_use && !first && free)
first = entry;
if ((entry->handle == loc) && !free && !entry->is_frag)
dup = entry;
}
if (dup)
return dup;
if (first) {
first->handle = loc;
first->in_use = true;
/* Reset HW values */
memset(&first->val, 0, sizeof(first->val));
}
return first;
}
static int tc_fill_actions(struct stmmac_tc_entry *entry,
struct stmmac_tc_entry *frag,
struct tc_cls_u32_offload *cls)
{
struct stmmac_tc_entry *action_entry = entry;
const struct tc_action *act;
struct tcf_exts *exts;
int i;
exts = cls->knode.exts;
if (!tcf_exts_has_actions(exts))
return -EINVAL;
if (frag)
action_entry = frag;
tcf_exts_for_each_action(i, act, exts) {
/* Accept */
if (is_tcf_gact_ok(act)) {
action_entry->val.af = 1;
break;
}
/* Drop */
if (is_tcf_gact_shot(act)) {
action_entry->val.rf = 1;
break;
}
/* Unsupported */
return -EINVAL;
}
return 0;
}
static int tc_fill_entry(struct stmmac_priv *priv,
struct tc_cls_u32_offload *cls)
{
struct stmmac_tc_entry *entry, *frag = NULL;
struct tc_u32_sel *sel = cls->knode.sel;
u32 off, data, mask, real_off, rem;
u32 prio = cls->common.prio << 16;
int ret;
/* Only 1 match per entry */
if (sel->nkeys <= 0 || sel->nkeys > 1)
return -EINVAL;
off = sel->keys[0].off << sel->offshift;
data = sel->keys[0].val;
mask = sel->keys[0].mask;
switch (ntohs(cls->common.protocol)) {
case ETH_P_ALL:
break;
case ETH_P_IP:
off += ETH_HLEN;
break;
default:
return -EINVAL;
}
if (off > priv->tc_off_max)
return -EINVAL;
real_off = off / 4;
rem = off % 4;
entry = tc_find_entry(priv, cls, true);
if (!entry)
return -EINVAL;
if (rem) {
frag = tc_find_entry(priv, cls, true);
if (!frag) {
ret = -EINVAL;
goto err_unuse;
}
entry->frag_ptr = frag;
entry->val.match_en = (mask << (rem * 8)) &
GENMASK(31, rem * 8);
entry->val.match_data = (data << (rem * 8)) &
GENMASK(31, rem * 8);
entry->val.frame_offset = real_off;
entry->prio = prio;
frag->val.match_en = (mask >> (rem * 8)) &
GENMASK(rem * 8 - 1, 0);
frag->val.match_data = (data >> (rem * 8)) &
GENMASK(rem * 8 - 1, 0);
frag->val.frame_offset = real_off + 1;
frag->prio = prio;
frag->is_frag = true;
} else {
entry->frag_ptr = NULL;
entry->val.match_en = mask;
entry->val.match_data = data;
entry->val.frame_offset = real_off;
entry->prio = prio;
}
ret = tc_fill_actions(entry, frag, cls);
if (ret)
goto err_unuse;
return 0;
err_unuse:
if (frag)
frag->in_use = false;
entry->in_use = false;
return ret;
}
static void tc_unfill_entry(struct stmmac_priv *priv,
struct tc_cls_u32_offload *cls)
{
struct stmmac_tc_entry *entry;
entry = tc_find_entry(priv, cls, false);
if (!entry)
return;
entry->in_use = false;
if (entry->frag_ptr) {
entry = entry->frag_ptr;
entry->is_frag = false;
entry->in_use = false;
}
}
static int tc_config_knode(struct stmmac_priv *priv,
struct tc_cls_u32_offload *cls)
{
int ret;
ret = tc_fill_entry(priv, cls);
if (ret)
return ret;
ret = stmmac_rxp_config(priv, priv->hw->pcsr, priv->tc_entries,
priv->tc_entries_max);
if (ret)
goto err_unfill;
return 0;
err_unfill:
tc_unfill_entry(priv, cls);
return ret;
}
static int tc_delete_knode(struct stmmac_priv *priv,
struct tc_cls_u32_offload *cls)
{
/* Set entry and fragments as not used */
tc_unfill_entry(priv, cls);
return stmmac_rxp_config(priv, priv->hw->pcsr, priv->tc_entries,
priv->tc_entries_max);
}
static int tc_setup_cls_u32(struct stmmac_priv *priv,
struct tc_cls_u32_offload *cls)
{
switch (cls->command) {
case TC_CLSU32_REPLACE_KNODE:
tc_unfill_entry(priv, cls);
fallthrough;
case TC_CLSU32_NEW_KNODE:
return tc_config_knode(priv, cls);
case TC_CLSU32_DELETE_KNODE:
return tc_delete_knode(priv, cls);
default:
return -EOPNOTSUPP;
}
}
net: stmmac: fix tc flower deletion for VLAN priority Rx steering To replicate the issue:- 1) Add 1 flower filter for VLAN Priority based frame steering:- $ IFDEVNAME=eth0 $ tc qdisc add dev $IFDEVNAME ingress $ tc qdisc add dev $IFDEVNAME root mqprio num_tc 8 \ map 0 1 2 3 4 5 6 7 0 0 0 0 0 0 0 0 \ queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 hw 0 $ tc filter add dev $IFDEVNAME parent ffff: protocol 802.1Q \ flower vlan_prio 0 hw_tc 0 2) Get the 'pref' id $ tc filter show dev $IFDEVNAME ingress 3) Delete a specific tc flower record (say pref 49151) $ tc filter del dev $IFDEVNAME parent ffff: pref 49151 From dmesg, we will observe kernel NULL pointer ooops [ 197.170464] BUG: kernel NULL pointer dereference, address: 0000000000000000 [ 197.171367] #PF: supervisor read access in kernel mode [ 197.171367] #PF: error_code(0x0000) - not-present page [ 197.171367] PGD 0 P4D 0 [ 197.171367] Oops: 0000 [#1] PREEMPT SMP NOPTI <snip> [ 197.171367] RIP: 0010:tc_setup_cls+0x20b/0x4a0 [stmmac] <snip> [ 197.171367] Call Trace: [ 197.171367] <TASK> [ 197.171367] ? __stmmac_disable_all_queues+0xa8/0xe0 [stmmac] [ 197.171367] stmmac_setup_tc_block_cb+0x70/0x110 [stmmac] [ 197.171367] tc_setup_cb_destroy+0xb3/0x180 [ 197.171367] fl_hw_destroy_filter+0x94/0xc0 [cls_flower] The above issue is due to previous incorrect implementation of tc_del_vlan_flow(), shown below, that uses flow_cls_offload_flow_rule() to get struct flow_rule *rule which is no longer valid for tc filter delete operation. struct flow_rule *rule = flow_cls_offload_flow_rule(cls); struct flow_dissector *dissector = rule->match.dissector; So, to ensure tc_del_vlan_flow() deletes the right VLAN cls record for earlier configured RX queue (configured by hw_tc) in tc_add_vlan_flow(), this patch introduces stmmac_rfs_entry as driver-side flow_cls_offload record for 'RX frame steering' tc flower, currently used for VLAN priority. The implementation has taken consideration for future extension to include other type RX frame steering such as EtherType based. v2: - Clean up overly extensive backtrace and rewrite git message to better explain the kernel NULL pointer issue. Fixes: 0e039f5cf86c ("net: stmmac: add RX frame steering based on VLAN priority in tc flower") Tested-by: Kurt Kanzenbach <kurt@linutronix.de> Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2021-12-11 14:51:34 +00:00
static int tc_rfs_init(struct stmmac_priv *priv)
{
int i;
priv->rfs_entries_max[STMMAC_RFS_T_VLAN] = 8;
priv->rfs_entries_max[STMMAC_RFS_T_LLDP] = 1;
priv->rfs_entries_max[STMMAC_RFS_T_1588] = 1;
net: stmmac: fix tc flower deletion for VLAN priority Rx steering To replicate the issue:- 1) Add 1 flower filter for VLAN Priority based frame steering:- $ IFDEVNAME=eth0 $ tc qdisc add dev $IFDEVNAME ingress $ tc qdisc add dev $IFDEVNAME root mqprio num_tc 8 \ map 0 1 2 3 4 5 6 7 0 0 0 0 0 0 0 0 \ queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 hw 0 $ tc filter add dev $IFDEVNAME parent ffff: protocol 802.1Q \ flower vlan_prio 0 hw_tc 0 2) Get the 'pref' id $ tc filter show dev $IFDEVNAME ingress 3) Delete a specific tc flower record (say pref 49151) $ tc filter del dev $IFDEVNAME parent ffff: pref 49151 From dmesg, we will observe kernel NULL pointer ooops [ 197.170464] BUG: kernel NULL pointer dereference, address: 0000000000000000 [ 197.171367] #PF: supervisor read access in kernel mode [ 197.171367] #PF: error_code(0x0000) - not-present page [ 197.171367] PGD 0 P4D 0 [ 197.171367] Oops: 0000 [#1] PREEMPT SMP NOPTI <snip> [ 197.171367] RIP: 0010:tc_setup_cls+0x20b/0x4a0 [stmmac] <snip> [ 197.171367] Call Trace: [ 197.171367] <TASK> [ 197.171367] ? __stmmac_disable_all_queues+0xa8/0xe0 [stmmac] [ 197.171367] stmmac_setup_tc_block_cb+0x70/0x110 [stmmac] [ 197.171367] tc_setup_cb_destroy+0xb3/0x180 [ 197.171367] fl_hw_destroy_filter+0x94/0xc0 [cls_flower] The above issue is due to previous incorrect implementation of tc_del_vlan_flow(), shown below, that uses flow_cls_offload_flow_rule() to get struct flow_rule *rule which is no longer valid for tc filter delete operation. struct flow_rule *rule = flow_cls_offload_flow_rule(cls); struct flow_dissector *dissector = rule->match.dissector; So, to ensure tc_del_vlan_flow() deletes the right VLAN cls record for earlier configured RX queue (configured by hw_tc) in tc_add_vlan_flow(), this patch introduces stmmac_rfs_entry as driver-side flow_cls_offload record for 'RX frame steering' tc flower, currently used for VLAN priority. The implementation has taken consideration for future extension to include other type RX frame steering such as EtherType based. v2: - Clean up overly extensive backtrace and rewrite git message to better explain the kernel NULL pointer issue. Fixes: 0e039f5cf86c ("net: stmmac: add RX frame steering based on VLAN priority in tc flower") Tested-by: Kurt Kanzenbach <kurt@linutronix.de> Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2021-12-11 14:51:34 +00:00
for (i = 0; i < STMMAC_RFS_T_MAX; i++)
priv->rfs_entries_total += priv->rfs_entries_max[i];
priv->rfs_entries = devm_kcalloc(priv->device,
priv->rfs_entries_total,
sizeof(*priv->rfs_entries),
GFP_KERNEL);
if (!priv->rfs_entries)
return -ENOMEM;
dev_info(priv->device, "Enabled RFS Flow TC (entries=%d)\n",
priv->rfs_entries_total);
return 0;
}
static int tc_init(struct stmmac_priv *priv)
{
struct dma_features *dma_cap = &priv->dma_cap;
unsigned int count;
net: stmmac: fix tc flower deletion for VLAN priority Rx steering To replicate the issue:- 1) Add 1 flower filter for VLAN Priority based frame steering:- $ IFDEVNAME=eth0 $ tc qdisc add dev $IFDEVNAME ingress $ tc qdisc add dev $IFDEVNAME root mqprio num_tc 8 \ map 0 1 2 3 4 5 6 7 0 0 0 0 0 0 0 0 \ queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 hw 0 $ tc filter add dev $IFDEVNAME parent ffff: protocol 802.1Q \ flower vlan_prio 0 hw_tc 0 2) Get the 'pref' id $ tc filter show dev $IFDEVNAME ingress 3) Delete a specific tc flower record (say pref 49151) $ tc filter del dev $IFDEVNAME parent ffff: pref 49151 From dmesg, we will observe kernel NULL pointer ooops [ 197.170464] BUG: kernel NULL pointer dereference, address: 0000000000000000 [ 197.171367] #PF: supervisor read access in kernel mode [ 197.171367] #PF: error_code(0x0000) - not-present page [ 197.171367] PGD 0 P4D 0 [ 197.171367] Oops: 0000 [#1] PREEMPT SMP NOPTI <snip> [ 197.171367] RIP: 0010:tc_setup_cls+0x20b/0x4a0 [stmmac] <snip> [ 197.171367] Call Trace: [ 197.171367] <TASK> [ 197.171367] ? __stmmac_disable_all_queues+0xa8/0xe0 [stmmac] [ 197.171367] stmmac_setup_tc_block_cb+0x70/0x110 [stmmac] [ 197.171367] tc_setup_cb_destroy+0xb3/0x180 [ 197.171367] fl_hw_destroy_filter+0x94/0xc0 [cls_flower] The above issue is due to previous incorrect implementation of tc_del_vlan_flow(), shown below, that uses flow_cls_offload_flow_rule() to get struct flow_rule *rule which is no longer valid for tc filter delete operation. struct flow_rule *rule = flow_cls_offload_flow_rule(cls); struct flow_dissector *dissector = rule->match.dissector; So, to ensure tc_del_vlan_flow() deletes the right VLAN cls record for earlier configured RX queue (configured by hw_tc) in tc_add_vlan_flow(), this patch introduces stmmac_rfs_entry as driver-side flow_cls_offload record for 'RX frame steering' tc flower, currently used for VLAN priority. The implementation has taken consideration for future extension to include other type RX frame steering such as EtherType based. v2: - Clean up overly extensive backtrace and rewrite git message to better explain the kernel NULL pointer issue. Fixes: 0e039f5cf86c ("net: stmmac: add RX frame steering based on VLAN priority in tc flower") Tested-by: Kurt Kanzenbach <kurt@linutronix.de> Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2021-12-11 14:51:34 +00:00
int ret, i;
if (dma_cap->l3l4fnum) {
priv->flow_entries_max = dma_cap->l3l4fnum;
priv->flow_entries = devm_kcalloc(priv->device,
dma_cap->l3l4fnum,
sizeof(*priv->flow_entries),
GFP_KERNEL);
if (!priv->flow_entries)
return -ENOMEM;
for (i = 0; i < priv->flow_entries_max; i++)
priv->flow_entries[i].idx = i;
net: stmmac: fix tc flower deletion for VLAN priority Rx steering To replicate the issue:- 1) Add 1 flower filter for VLAN Priority based frame steering:- $ IFDEVNAME=eth0 $ tc qdisc add dev $IFDEVNAME ingress $ tc qdisc add dev $IFDEVNAME root mqprio num_tc 8 \ map 0 1 2 3 4 5 6 7 0 0 0 0 0 0 0 0 \ queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 hw 0 $ tc filter add dev $IFDEVNAME parent ffff: protocol 802.1Q \ flower vlan_prio 0 hw_tc 0 2) Get the 'pref' id $ tc filter show dev $IFDEVNAME ingress 3) Delete a specific tc flower record (say pref 49151) $ tc filter del dev $IFDEVNAME parent ffff: pref 49151 From dmesg, we will observe kernel NULL pointer ooops [ 197.170464] BUG: kernel NULL pointer dereference, address: 0000000000000000 [ 197.171367] #PF: supervisor read access in kernel mode [ 197.171367] #PF: error_code(0x0000) - not-present page [ 197.171367] PGD 0 P4D 0 [ 197.171367] Oops: 0000 [#1] PREEMPT SMP NOPTI <snip> [ 197.171367] RIP: 0010:tc_setup_cls+0x20b/0x4a0 [stmmac] <snip> [ 197.171367] Call Trace: [ 197.171367] <TASK> [ 197.171367] ? __stmmac_disable_all_queues+0xa8/0xe0 [stmmac] [ 197.171367] stmmac_setup_tc_block_cb+0x70/0x110 [stmmac] [ 197.171367] tc_setup_cb_destroy+0xb3/0x180 [ 197.171367] fl_hw_destroy_filter+0x94/0xc0 [cls_flower] The above issue is due to previous incorrect implementation of tc_del_vlan_flow(), shown below, that uses flow_cls_offload_flow_rule() to get struct flow_rule *rule which is no longer valid for tc filter delete operation. struct flow_rule *rule = flow_cls_offload_flow_rule(cls); struct flow_dissector *dissector = rule->match.dissector; So, to ensure tc_del_vlan_flow() deletes the right VLAN cls record for earlier configured RX queue (configured by hw_tc) in tc_add_vlan_flow(), this patch introduces stmmac_rfs_entry as driver-side flow_cls_offload record for 'RX frame steering' tc flower, currently used for VLAN priority. The implementation has taken consideration for future extension to include other type RX frame steering such as EtherType based. v2: - Clean up overly extensive backtrace and rewrite git message to better explain the kernel NULL pointer issue. Fixes: 0e039f5cf86c ("net: stmmac: add RX frame steering based on VLAN priority in tc flower") Tested-by: Kurt Kanzenbach <kurt@linutronix.de> Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2021-12-11 14:51:34 +00:00
dev_info(priv->device, "Enabled L3L4 Flow TC (entries=%d)\n",
priv->flow_entries_max);
}
net: stmmac: fix tc flower deletion for VLAN priority Rx steering To replicate the issue:- 1) Add 1 flower filter for VLAN Priority based frame steering:- $ IFDEVNAME=eth0 $ tc qdisc add dev $IFDEVNAME ingress $ tc qdisc add dev $IFDEVNAME root mqprio num_tc 8 \ map 0 1 2 3 4 5 6 7 0 0 0 0 0 0 0 0 \ queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 hw 0 $ tc filter add dev $IFDEVNAME parent ffff: protocol 802.1Q \ flower vlan_prio 0 hw_tc 0 2) Get the 'pref' id $ tc filter show dev $IFDEVNAME ingress 3) Delete a specific tc flower record (say pref 49151) $ tc filter del dev $IFDEVNAME parent ffff: pref 49151 From dmesg, we will observe kernel NULL pointer ooops [ 197.170464] BUG: kernel NULL pointer dereference, address: 0000000000000000 [ 197.171367] #PF: supervisor read access in kernel mode [ 197.171367] #PF: error_code(0x0000) - not-present page [ 197.171367] PGD 0 P4D 0 [ 197.171367] Oops: 0000 [#1] PREEMPT SMP NOPTI <snip> [ 197.171367] RIP: 0010:tc_setup_cls+0x20b/0x4a0 [stmmac] <snip> [ 197.171367] Call Trace: [ 197.171367] <TASK> [ 197.171367] ? __stmmac_disable_all_queues+0xa8/0xe0 [stmmac] [ 197.171367] stmmac_setup_tc_block_cb+0x70/0x110 [stmmac] [ 197.171367] tc_setup_cb_destroy+0xb3/0x180 [ 197.171367] fl_hw_destroy_filter+0x94/0xc0 [cls_flower] The above issue is due to previous incorrect implementation of tc_del_vlan_flow(), shown below, that uses flow_cls_offload_flow_rule() to get struct flow_rule *rule which is no longer valid for tc filter delete operation. struct flow_rule *rule = flow_cls_offload_flow_rule(cls); struct flow_dissector *dissector = rule->match.dissector; So, to ensure tc_del_vlan_flow() deletes the right VLAN cls record for earlier configured RX queue (configured by hw_tc) in tc_add_vlan_flow(), this patch introduces stmmac_rfs_entry as driver-side flow_cls_offload record for 'RX frame steering' tc flower, currently used for VLAN priority. The implementation has taken consideration for future extension to include other type RX frame steering such as EtherType based. v2: - Clean up overly extensive backtrace and rewrite git message to better explain the kernel NULL pointer issue. Fixes: 0e039f5cf86c ("net: stmmac: add RX frame steering based on VLAN priority in tc flower") Tested-by: Kurt Kanzenbach <kurt@linutronix.de> Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2021-12-11 14:51:34 +00:00
ret = tc_rfs_init(priv);
if (ret)
return -ENOMEM;
net: stmmac: Fix kernel panic due to NULL pointer dereference of fpe_cfg In this patch, "net: stmmac: support FPE link partner hand-shaking procedure", priv->plat->fpe_cfg wouldn`t be "devm_kzalloc"ed if dma_cap->frpsel is 0 (Flexible Rx Parser is not supported in SoC) in tc_init(). So, fpe_cfg will be remain as NULL and accessing it will cause kernel panic. To fix this, move the "devm_kzalloc"ing of priv->plat->fpe_cfg before dma_cap->frpsel checking in tc_init(). Additionally, checking of priv->dma_cap.fpesel is added before calling stmmac_fpe_link_state_handle() as only FPE supported SoC is allowed to call the function. Below is the kernel panic dump reported by Marek Szyprowski <m.szyprowski@samsung.com>: meson8b-dwmac ff3f0000.ethernet eth0: PHY [0.0:00] driver [RTL8211F Gigabit Ethernet] (irq=35) meson8b-dwmac ff3f0000.ethernet eth0: No Safety Features support found meson8b-dwmac ff3f0000.ethernet eth0: PTP not supported by HW meson8b-dwmac ff3f0000.ethernet eth0: configuring for phy/rgmii link mode Unable to handle kernel NULL pointer dereference at virtual address 0000000000000001 Mem abort info: ... user pgtable: 4k pages, 48-bit VAs, pgdp=00000000044eb000 [0000000000000001] pgd=0000000000000000, p4d=0000000000000000 Internal error: Oops: 96000004 [#1] PREEMPT SMP Modules linked in: dw_hdmi_i2s_audio dw_hdmi_cec meson_gxl realtek meson_gxbb_wdt snd_soc_meson_axg_sound_card dwmac_generic axg_audio meson_dw_hdmi crct10dif_ce snd_soc_meson_card_utils snd_soc_meson_axg_tdmout panfrost rc_odroid gpu_sched reset_meson_audio_arb meson_ir snd_soc_meson_g12a_tohdmitx snd_soc_meson_axg_frddr sclk_div clk_phase snd_soc_meson_codec_glue dwmac_meson8b snd_soc_meson_axg_fifo stmmac_platform meson_rng meson_drm stmmac rtc_meson_vrtc rng_core meson_canvas pwm_meson dw_hdmi mdio_mux_meson_g12a pcs_xpcs snd_soc_meson_axg_tdm_interface snd_soc_meson_axg_tdm_formatter nvmem_meson_efuse display_connector CPU: 1 PID: 7 Comm: kworker/u8:0 Not tainted 5.12.0-rc4-next-20210325+ Hardware name: Hardkernel ODROID-C4 (DT) Workqueue: events_power_efficient phylink_resolve pstate: 20400009 (nzCv daif +PAN -UAO -TCO BTYPE=--) pc : stmmac_mac_link_up+0x14c/0x348 [stmmac] lr : stmmac_mac_link_up+0x284/0x348 [stmmac] ... Call trace: stmmac_mac_link_up+0x14c/0x348 [stmmac] phylink_resolve+0x104/0x420 process_one_work+0x2a8/0x718 worker_thread+0x48/0x460 kthread+0x134/0x160 ret_from_fork+0x10/0x18 Code: b971ba60 350007c0 f958c260 f9402000 (39400401) ---[ end trace 0c9deb6c510228aa ]--- Fixes: 5a5586112b92 ("net: stmmac: support FPE link partner hand-shaking procedure") Reported-by: Marek Szyprowski <m.szyprowski@samsung.com> Signed-off-by: Mohammad Athari Bin Ismail <mohammad.athari.ismail@intel.com> Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2021-03-26 09:10:46 +00:00
if (!priv->plat->fpe_cfg) {
priv->plat->fpe_cfg = devm_kzalloc(priv->device,
sizeof(*priv->plat->fpe_cfg),
GFP_KERNEL);
if (!priv->plat->fpe_cfg)
return -ENOMEM;
} else {
memset(priv->plat->fpe_cfg, 0, sizeof(*priv->plat->fpe_cfg));
}
/* Fail silently as we can still use remaining features, e.g. CBS */
if (!dma_cap->frpsel)
return 0;
switch (dma_cap->frpbs) {
case 0x0:
priv->tc_off_max = 64;
break;
case 0x1:
priv->tc_off_max = 128;
break;
case 0x2:
priv->tc_off_max = 256;
break;
default:
return -EINVAL;
}
switch (dma_cap->frpes) {
case 0x0:
count = 64;
break;
case 0x1:
count = 128;
break;
case 0x2:
count = 256;
break;
default:
return -EINVAL;
}
/* Reserve one last filter which lets all pass */
priv->tc_entries_max = count;
treewide: devm_kzalloc() -> devm_kcalloc() The devm_kzalloc() function has a 2-factor argument form, devm_kcalloc(). This patch replaces cases of: devm_kzalloc(handle, a * b, gfp) with: devm_kcalloc(handle, a * b, gfp) as well as handling cases of: devm_kzalloc(handle, a * b * c, gfp) with: devm_kzalloc(handle, array3_size(a, b, c), gfp) as it's slightly less ugly than: devm_kcalloc(handle, array_size(a, b), c, gfp) This does, however, attempt to ignore constant size factors like: devm_kzalloc(handle, 4 * 1024, gfp) though any constants defined via macros get caught up in the conversion. Any factors with a sizeof() of "unsigned char", "char", and "u8" were dropped, since they're redundant. Some manual whitespace fixes were needed in this patch, as Coccinelle really liked to write "=devm_kcalloc..." instead of "= devm_kcalloc...". The Coccinelle script used for this was: // Fix redundant parens around sizeof(). @@ expression HANDLE; type TYPE; expression THING, E; @@ ( devm_kzalloc(HANDLE, - (sizeof(TYPE)) * E + sizeof(TYPE) * E , ...) | devm_kzalloc(HANDLE, - (sizeof(THING)) * E + sizeof(THING) * E , ...) ) // Drop single-byte sizes and redundant parens. @@ expression HANDLE; expression COUNT; typedef u8; typedef __u8; @@ ( devm_kzalloc(HANDLE, - sizeof(u8) * (COUNT) + COUNT , ...) | devm_kzalloc(HANDLE, - sizeof(__u8) * (COUNT) + COUNT , ...) | devm_kzalloc(HANDLE, - sizeof(char) * (COUNT) + COUNT , ...) | devm_kzalloc(HANDLE, - sizeof(unsigned char) * (COUNT) + COUNT , ...) | devm_kzalloc(HANDLE, - sizeof(u8) * COUNT + COUNT , ...) | devm_kzalloc(HANDLE, - sizeof(__u8) * COUNT + COUNT , ...) | devm_kzalloc(HANDLE, - sizeof(char) * COUNT + COUNT , ...) | devm_kzalloc(HANDLE, - sizeof(unsigned char) * COUNT + COUNT , ...) ) // 2-factor product with sizeof(type/expression) and identifier or constant. @@ expression HANDLE; type TYPE; expression THING; identifier COUNT_ID; constant COUNT_CONST; @@ ( - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(TYPE) * (COUNT_ID) + COUNT_ID, sizeof(TYPE) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(TYPE) * COUNT_ID + COUNT_ID, sizeof(TYPE) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(TYPE) * (COUNT_CONST) + COUNT_CONST, sizeof(TYPE) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(TYPE) * COUNT_CONST + COUNT_CONST, sizeof(TYPE) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(THING) * (COUNT_ID) + COUNT_ID, sizeof(THING) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(THING) * COUNT_ID + COUNT_ID, sizeof(THING) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(THING) * (COUNT_CONST) + COUNT_CONST, sizeof(THING) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(THING) * COUNT_CONST + COUNT_CONST, sizeof(THING) , ...) ) // 2-factor product, only identifiers. @@ expression HANDLE; identifier SIZE, COUNT; @@ - devm_kzalloc + devm_kcalloc (HANDLE, - SIZE * COUNT + COUNT, SIZE , ...) // 3-factor product with 1 sizeof(type) or sizeof(expression), with // redundant parens removed. @@ expression HANDLE; expression THING; identifier STRIDE, COUNT; type TYPE; @@ ( devm_kzalloc(HANDLE, - sizeof(TYPE) * (COUNT) * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | devm_kzalloc(HANDLE, - sizeof(TYPE) * (COUNT) * STRIDE + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | devm_kzalloc(HANDLE, - sizeof(TYPE) * COUNT * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | devm_kzalloc(HANDLE, - sizeof(TYPE) * COUNT * STRIDE + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | devm_kzalloc(HANDLE, - sizeof(THING) * (COUNT) * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) | devm_kzalloc(HANDLE, - sizeof(THING) * (COUNT) * STRIDE + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) | devm_kzalloc(HANDLE, - sizeof(THING) * COUNT * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) | devm_kzalloc(HANDLE, - sizeof(THING) * COUNT * STRIDE + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) ) // 3-factor product with 2 sizeof(variable), with redundant parens removed. @@ expression HANDLE; expression THING1, THING2; identifier COUNT; type TYPE1, TYPE2; @@ ( devm_kzalloc(HANDLE, - sizeof(TYPE1) * sizeof(TYPE2) * COUNT + array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2)) , ...) | devm_kzalloc(HANDLE, - sizeof(TYPE1) * sizeof(THING2) * (COUNT) + array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2)) , ...) | devm_kzalloc(HANDLE, - sizeof(THING1) * sizeof(THING2) * COUNT + array3_size(COUNT, sizeof(THING1), sizeof(THING2)) , ...) | devm_kzalloc(HANDLE, - sizeof(THING1) * sizeof(THING2) * (COUNT) + array3_size(COUNT, sizeof(THING1), sizeof(THING2)) , ...) | devm_kzalloc(HANDLE, - sizeof(TYPE1) * sizeof(THING2) * COUNT + array3_size(COUNT, sizeof(TYPE1), sizeof(THING2)) , ...) | devm_kzalloc(HANDLE, - sizeof(TYPE1) * sizeof(THING2) * (COUNT) + array3_size(COUNT, sizeof(TYPE1), sizeof(THING2)) , ...) ) // 3-factor product, only identifiers, with redundant parens removed. @@ expression HANDLE; identifier STRIDE, SIZE, COUNT; @@ ( devm_kzalloc(HANDLE, - (COUNT) * STRIDE * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) | devm_kzalloc(HANDLE, - COUNT * (STRIDE) * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) | devm_kzalloc(HANDLE, - COUNT * STRIDE * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | devm_kzalloc(HANDLE, - (COUNT) * (STRIDE) * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) | devm_kzalloc(HANDLE, - COUNT * (STRIDE) * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | devm_kzalloc(HANDLE, - (COUNT) * STRIDE * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | devm_kzalloc(HANDLE, - (COUNT) * (STRIDE) * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | devm_kzalloc(HANDLE, - COUNT * STRIDE * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) ) // Any remaining multi-factor products, first at least 3-factor products, // when they're not all constants... @@ expression HANDLE; expression E1, E2, E3; constant C1, C2, C3; @@ ( devm_kzalloc(HANDLE, C1 * C2 * C3, ...) | devm_kzalloc(HANDLE, - (E1) * E2 * E3 + array3_size(E1, E2, E3) , ...) | devm_kzalloc(HANDLE, - (E1) * (E2) * E3 + array3_size(E1, E2, E3) , ...) | devm_kzalloc(HANDLE, - (E1) * (E2) * (E3) + array3_size(E1, E2, E3) , ...) | devm_kzalloc(HANDLE, - E1 * E2 * E3 + array3_size(E1, E2, E3) , ...) ) // And then all remaining 2 factors products when they're not all constants, // keeping sizeof() as the second factor argument. @@ expression HANDLE; expression THING, E1, E2; type TYPE; constant C1, C2, C3; @@ ( devm_kzalloc(HANDLE, sizeof(THING) * C2, ...) | devm_kzalloc(HANDLE, sizeof(TYPE) * C2, ...) | devm_kzalloc(HANDLE, C1 * C2 * C3, ...) | devm_kzalloc(HANDLE, C1 * C2, ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(TYPE) * (E2) + E2, sizeof(TYPE) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(TYPE) * E2 + E2, sizeof(TYPE) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(THING) * (E2) + E2, sizeof(THING) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(THING) * E2 + E2, sizeof(THING) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - (E1) * E2 + E1, E2 , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - (E1) * (E2) + E1, E2 , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - E1 * E2 + E1, E2 , ...) ) Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-12 21:07:58 +00:00
priv->tc_entries = devm_kcalloc(priv->device,
count, sizeof(*priv->tc_entries), GFP_KERNEL);
if (!priv->tc_entries)
return -ENOMEM;
tc_fill_all_pass_entry(&priv->tc_entries[count - 1]);
dev_info(priv->device, "Enabling HW TC (entries=%d, max_off=%d)\n",
priv->tc_entries_max, priv->tc_off_max);
net: stmmac: support FPE link partner hand-shaking procedure In order to discover whether remote station supports frame preemption, local station sends verify mPacket and expects response mPacket in return from the remote station. So, we add the functions to send and handle event when verify mPacket and response mPacket are exchanged between the networked stations. The mechanism to handle different FPE states between local and remote station (link partner) is implemented using workqueue which starts a task each time there is some sign of verify & response mPacket exchange as check in FPE IRQ event. The task retries couple of times to try to spot the states that both stations are ready to enter FPE ON. This allows different end points to enable FPE at different time and verify-response mPacket can happen asynchronously. Ultimately, the task will only turn FPE ON when local station have both exchange response in both directions. Thanks to Voon Weifeng for implementing the core functions for detecting FPE events and send mPacket and phylink related change. Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com> Co-developed-by: Voon Weifeng <weifeng.voon@intel.com> Signed-off-by: Voon Weifeng <weifeng.voon@intel.com> Co-developed-by: Tan Tee Min <tee.min.tan@intel.com> Signed-off-by: Tan Tee Min <tee.min.tan@intel.com> Co-developed-by: Mohammad Athari Bin Ismail <mohammad.athari.ismail@intel.com> Signed-off-by: Mohammad Athari Bin Ismail <mohammad.athari.ismail@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2021-03-24 09:07:42 +00:00
return 0;
}
static int tc_setup_cbs(struct stmmac_priv *priv,
struct tc_cbs_qopt_offload *qopt)
{
u32 tx_queues_count = priv->plat->tx_queues_to_use;
u32 queue = qopt->queue;
u32 ptr, speed_div;
u32 mode_to_use;
u64 value;
int ret;
/* Queue 0 is not AVB capable */
if (queue <= 0 || queue >= tx_queues_count)
return -EINVAL;
if (!priv->dma_cap.av)
return -EOPNOTSUPP;
/* Port Transmit Rate and Speed Divider */
switch (priv->speed) {
case SPEED_10000:
ptr = 32;
speed_div = 10000000;
break;
case SPEED_5000:
ptr = 32;
speed_div = 5000000;
break;
case SPEED_2500:
ptr = 8;
speed_div = 2500000;
break;
case SPEED_1000:
ptr = 8;
speed_div = 1000000;
break;
case SPEED_100:
ptr = 4;
speed_div = 100000;
break;
default:
return -EOPNOTSUPP;
}
mode_to_use = priv->plat->tx_queues_cfg[queue].mode_to_use;
if (mode_to_use == MTL_QUEUE_DCB && qopt->enable) {
ret = stmmac_dma_qmode(priv, priv->ioaddr, queue, MTL_QUEUE_AVB);
if (ret)
return ret;
priv->plat->tx_queues_cfg[queue].mode_to_use = MTL_QUEUE_AVB;
} else if (!qopt->enable) {
ret = stmmac_dma_qmode(priv, priv->ioaddr, queue,
MTL_QUEUE_DCB);
if (ret)
return ret;
priv->plat->tx_queues_cfg[queue].mode_to_use = MTL_QUEUE_DCB;
}
/* Final adjustments for HW */
stmmac: fix signed 64-bit division I link error on 32-bit ARM points to yet another arithmetic bug: drivers/net/ethernet/stmicro/stmmac/stmmac_tc.o: In function `tc_setup_cbs': stmmac_tc.c:(.text+0x148): undefined reference to `__aeabi_uldivmod' stmmac_tc.c:(.text+0x1fc): undefined reference to `__aeabi_uldivmod' stmmac_tc.c:(.text+0x308): undefined reference to `__aeabi_uldivmod' stmmac_tc.c:(.text+0x320): undefined reference to `__aeabi_uldivmod' stmmac_tc.c:(.text+0x33c): undefined reference to `__aeabi_uldivmod' drivers/net/ethernet/stmicro/stmmac/stmmac_tc.o:stmmac_tc.c:(.text+0x3a4): more undefined references to `__aeabi_uldivmod' follow I observe that the last change to add the 'ul' prefix was incorrect, as it did not turn the result of the multiplication into a 64-bit expression on 32-bit architectures. Further, it seems that the do_div() macro gets confused by the fact that we pass a signed variable rather than unsigned into it. This changes the code to instead use the div_s64() helper that is meant for signed division, along with changing the constant suffix to 'll' to actually make it a 64-bit argument everywhere, fixing both of the issues I pointed out. I'm not completely convinced that this makes the code correct, but I'm fairly sure that we have two problems less than before. Fixes: 1f705bc61aee ("net: stmmac: Add support for CBS QDISC") Fixes: c18a9c096683 ("net: stmmac_tc: use 64-bit arithmetic instead of 32-bit") Signed-off-by: Arnd Bergmann <arnd@arndb.de> Acked-by: Gustavo A. R. Silva <gustavo@embeddedor.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2018-07-06 13:36:07 +00:00
value = div_s64(qopt->idleslope * 1024ll * ptr, speed_div);
priv->plat->tx_queues_cfg[queue].idle_slope = value & GENMASK(31, 0);
stmmac: fix signed 64-bit division I link error on 32-bit ARM points to yet another arithmetic bug: drivers/net/ethernet/stmicro/stmmac/stmmac_tc.o: In function `tc_setup_cbs': stmmac_tc.c:(.text+0x148): undefined reference to `__aeabi_uldivmod' stmmac_tc.c:(.text+0x1fc): undefined reference to `__aeabi_uldivmod' stmmac_tc.c:(.text+0x308): undefined reference to `__aeabi_uldivmod' stmmac_tc.c:(.text+0x320): undefined reference to `__aeabi_uldivmod' stmmac_tc.c:(.text+0x33c): undefined reference to `__aeabi_uldivmod' drivers/net/ethernet/stmicro/stmmac/stmmac_tc.o:stmmac_tc.c:(.text+0x3a4): more undefined references to `__aeabi_uldivmod' follow I observe that the last change to add the 'ul' prefix was incorrect, as it did not turn the result of the multiplication into a 64-bit expression on 32-bit architectures. Further, it seems that the do_div() macro gets confused by the fact that we pass a signed variable rather than unsigned into it. This changes the code to instead use the div_s64() helper that is meant for signed division, along with changing the constant suffix to 'll' to actually make it a 64-bit argument everywhere, fixing both of the issues I pointed out. I'm not completely convinced that this makes the code correct, but I'm fairly sure that we have two problems less than before. Fixes: 1f705bc61aee ("net: stmmac: Add support for CBS QDISC") Fixes: c18a9c096683 ("net: stmmac_tc: use 64-bit arithmetic instead of 32-bit") Signed-off-by: Arnd Bergmann <arnd@arndb.de> Acked-by: Gustavo A. R. Silva <gustavo@embeddedor.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2018-07-06 13:36:07 +00:00
value = div_s64(-qopt->sendslope * 1024ll * ptr, speed_div);
priv->plat->tx_queues_cfg[queue].send_slope = value & GENMASK(31, 0);
stmmac: fix signed 64-bit division I link error on 32-bit ARM points to yet another arithmetic bug: drivers/net/ethernet/stmicro/stmmac/stmmac_tc.o: In function `tc_setup_cbs': stmmac_tc.c:(.text+0x148): undefined reference to `__aeabi_uldivmod' stmmac_tc.c:(.text+0x1fc): undefined reference to `__aeabi_uldivmod' stmmac_tc.c:(.text+0x308): undefined reference to `__aeabi_uldivmod' stmmac_tc.c:(.text+0x320): undefined reference to `__aeabi_uldivmod' stmmac_tc.c:(.text+0x33c): undefined reference to `__aeabi_uldivmod' drivers/net/ethernet/stmicro/stmmac/stmmac_tc.o:stmmac_tc.c:(.text+0x3a4): more undefined references to `__aeabi_uldivmod' follow I observe that the last change to add the 'ul' prefix was incorrect, as it did not turn the result of the multiplication into a 64-bit expression on 32-bit architectures. Further, it seems that the do_div() macro gets confused by the fact that we pass a signed variable rather than unsigned into it. This changes the code to instead use the div_s64() helper that is meant for signed division, along with changing the constant suffix to 'll' to actually make it a 64-bit argument everywhere, fixing both of the issues I pointed out. I'm not completely convinced that this makes the code correct, but I'm fairly sure that we have two problems less than before. Fixes: 1f705bc61aee ("net: stmmac: Add support for CBS QDISC") Fixes: c18a9c096683 ("net: stmmac_tc: use 64-bit arithmetic instead of 32-bit") Signed-off-by: Arnd Bergmann <arnd@arndb.de> Acked-by: Gustavo A. R. Silva <gustavo@embeddedor.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2018-07-06 13:36:07 +00:00
value = qopt->hicredit * 1024ll * 8;
priv->plat->tx_queues_cfg[queue].high_credit = value & GENMASK(31, 0);
stmmac: fix signed 64-bit division I link error on 32-bit ARM points to yet another arithmetic bug: drivers/net/ethernet/stmicro/stmmac/stmmac_tc.o: In function `tc_setup_cbs': stmmac_tc.c:(.text+0x148): undefined reference to `__aeabi_uldivmod' stmmac_tc.c:(.text+0x1fc): undefined reference to `__aeabi_uldivmod' stmmac_tc.c:(.text+0x308): undefined reference to `__aeabi_uldivmod' stmmac_tc.c:(.text+0x320): undefined reference to `__aeabi_uldivmod' stmmac_tc.c:(.text+0x33c): undefined reference to `__aeabi_uldivmod' drivers/net/ethernet/stmicro/stmmac/stmmac_tc.o:stmmac_tc.c:(.text+0x3a4): more undefined references to `__aeabi_uldivmod' follow I observe that the last change to add the 'ul' prefix was incorrect, as it did not turn the result of the multiplication into a 64-bit expression on 32-bit architectures. Further, it seems that the do_div() macro gets confused by the fact that we pass a signed variable rather than unsigned into it. This changes the code to instead use the div_s64() helper that is meant for signed division, along with changing the constant suffix to 'll' to actually make it a 64-bit argument everywhere, fixing both of the issues I pointed out. I'm not completely convinced that this makes the code correct, but I'm fairly sure that we have two problems less than before. Fixes: 1f705bc61aee ("net: stmmac: Add support for CBS QDISC") Fixes: c18a9c096683 ("net: stmmac_tc: use 64-bit arithmetic instead of 32-bit") Signed-off-by: Arnd Bergmann <arnd@arndb.de> Acked-by: Gustavo A. R. Silva <gustavo@embeddedor.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2018-07-06 13:36:07 +00:00
value = qopt->locredit * 1024ll * 8;
priv->plat->tx_queues_cfg[queue].low_credit = value & GENMASK(31, 0);
ret = stmmac_config_cbs(priv, priv->hw,
priv->plat->tx_queues_cfg[queue].send_slope,
priv->plat->tx_queues_cfg[queue].idle_slope,
priv->plat->tx_queues_cfg[queue].high_credit,
priv->plat->tx_queues_cfg[queue].low_credit,
queue);
if (ret)
return ret;
dev_info(priv->device, "CBS queue %d: send %d, idle %d, hi %d, lo %d\n",
queue, qopt->sendslope, qopt->idleslope,
qopt->hicredit, qopt->locredit);
return 0;
}
static int tc_parse_flow_actions(struct stmmac_priv *priv,
struct flow_action *action,
struct stmmac_flow_entry *entry,
struct netlink_ext_ack *extack)
{
struct flow_action_entry *act;
int i;
if (!flow_action_has_entries(action))
return -EINVAL;
if (!flow_action_basic_hw_stats_check(action, extack))
return -EOPNOTSUPP;
flow_action_for_each(i, act, action) {
switch (act->id) {
case FLOW_ACTION_DROP:
entry->action |= STMMAC_FLOW_ACTION_DROP;
return 0;
default:
break;
}
}
/* Nothing to do, maybe inverse filter ? */
return 0;
}
#define ETHER_TYPE_FULL_MASK cpu_to_be16(~0)
static int tc_add_basic_flow(struct stmmac_priv *priv,
struct flow_cls_offload *cls,
struct stmmac_flow_entry *entry)
{
struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
struct flow_dissector *dissector = rule->match.dissector;
struct flow_match_basic match;
/* Nothing to do here */
if (!dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_BASIC))
return -EINVAL;
flow_rule_match_basic(rule, &match);
entry->ip_proto = match.key->ip_proto;
return 0;
}
static int tc_add_ip4_flow(struct stmmac_priv *priv,
struct flow_cls_offload *cls,
struct stmmac_flow_entry *entry)
{
struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
struct flow_dissector *dissector = rule->match.dissector;
bool inv = entry->action & STMMAC_FLOW_ACTION_DROP;
struct flow_match_ipv4_addrs match;
u32 hw_match;
int ret;
/* Nothing to do here */
if (!dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_IPV4_ADDRS))
return -EINVAL;
flow_rule_match_ipv4_addrs(rule, &match);
hw_match = ntohl(match.key->src) & ntohl(match.mask->src);
if (hw_match) {
ret = stmmac_config_l3_filter(priv, priv->hw, entry->idx, true,
false, true, inv, hw_match);
if (ret)
return ret;
}
hw_match = ntohl(match.key->dst) & ntohl(match.mask->dst);
if (hw_match) {
ret = stmmac_config_l3_filter(priv, priv->hw, entry->idx, true,
false, false, inv, hw_match);
if (ret)
return ret;
}
return 0;
}
static int tc_add_ports_flow(struct stmmac_priv *priv,
struct flow_cls_offload *cls,
struct stmmac_flow_entry *entry)
{
struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
struct flow_dissector *dissector = rule->match.dissector;
bool inv = entry->action & STMMAC_FLOW_ACTION_DROP;
struct flow_match_ports match;
u32 hw_match;
bool is_udp;
int ret;
/* Nothing to do here */
if (!dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_PORTS))
return -EINVAL;
switch (entry->ip_proto) {
case IPPROTO_TCP:
is_udp = false;
break;
case IPPROTO_UDP:
is_udp = true;
break;
default:
return -EINVAL;
}
flow_rule_match_ports(rule, &match);
hw_match = ntohs(match.key->src) & ntohs(match.mask->src);
if (hw_match) {
ret = stmmac_config_l4_filter(priv, priv->hw, entry->idx, true,
is_udp, true, inv, hw_match);
if (ret)
return ret;
}
hw_match = ntohs(match.key->dst) & ntohs(match.mask->dst);
if (hw_match) {
ret = stmmac_config_l4_filter(priv, priv->hw, entry->idx, true,
is_udp, false, inv, hw_match);
if (ret)
return ret;
}
entry->is_l4 = true;
return 0;
}
static struct stmmac_flow_entry *tc_find_flow(struct stmmac_priv *priv,
struct flow_cls_offload *cls,
bool get_free)
{
int i;
for (i = 0; i < priv->flow_entries_max; i++) {
struct stmmac_flow_entry *entry = &priv->flow_entries[i];
if (entry->cookie == cls->cookie)
return entry;
if (get_free && (entry->in_use == false))
return entry;
}
return NULL;
}
static struct {
int (*fn)(struct stmmac_priv *priv, struct flow_cls_offload *cls,
struct stmmac_flow_entry *entry);
} tc_flow_parsers[] = {
{ .fn = tc_add_basic_flow },
{ .fn = tc_add_ip4_flow },
{ .fn = tc_add_ports_flow },
};
static int tc_add_flow(struct stmmac_priv *priv,
struct flow_cls_offload *cls)
{
struct stmmac_flow_entry *entry = tc_find_flow(priv, cls, false);
struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
int i, ret;
if (!entry) {
entry = tc_find_flow(priv, cls, true);
if (!entry)
return -ENOENT;
}
ret = tc_parse_flow_actions(priv, &rule->action, entry,
cls->common.extack);
if (ret)
return ret;
for (i = 0; i < ARRAY_SIZE(tc_flow_parsers); i++) {
ret = tc_flow_parsers[i].fn(priv, cls, entry);
if (!ret)
entry->in_use = true;
}
if (!entry->in_use)
return -EINVAL;
entry->cookie = cls->cookie;
return 0;
}
static int tc_del_flow(struct stmmac_priv *priv,
struct flow_cls_offload *cls)
{
struct stmmac_flow_entry *entry = tc_find_flow(priv, cls, false);
int ret;
if (!entry || !entry->in_use)
return -ENOENT;
if (entry->is_l4) {
ret = stmmac_config_l4_filter(priv, priv->hw, entry->idx, false,
false, false, false, 0);
} else {
ret = stmmac_config_l3_filter(priv, priv->hw, entry->idx, false,
false, false, false, 0);
}
entry->in_use = false;
entry->cookie = 0;
entry->is_l4 = false;
return ret;
}
net: stmmac: fix tc flower deletion for VLAN priority Rx steering To replicate the issue:- 1) Add 1 flower filter for VLAN Priority based frame steering:- $ IFDEVNAME=eth0 $ tc qdisc add dev $IFDEVNAME ingress $ tc qdisc add dev $IFDEVNAME root mqprio num_tc 8 \ map 0 1 2 3 4 5 6 7 0 0 0 0 0 0 0 0 \ queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 hw 0 $ tc filter add dev $IFDEVNAME parent ffff: protocol 802.1Q \ flower vlan_prio 0 hw_tc 0 2) Get the 'pref' id $ tc filter show dev $IFDEVNAME ingress 3) Delete a specific tc flower record (say pref 49151) $ tc filter del dev $IFDEVNAME parent ffff: pref 49151 From dmesg, we will observe kernel NULL pointer ooops [ 197.170464] BUG: kernel NULL pointer dereference, address: 0000000000000000 [ 197.171367] #PF: supervisor read access in kernel mode [ 197.171367] #PF: error_code(0x0000) - not-present page [ 197.171367] PGD 0 P4D 0 [ 197.171367] Oops: 0000 [#1] PREEMPT SMP NOPTI <snip> [ 197.171367] RIP: 0010:tc_setup_cls+0x20b/0x4a0 [stmmac] <snip> [ 197.171367] Call Trace: [ 197.171367] <TASK> [ 197.171367] ? __stmmac_disable_all_queues+0xa8/0xe0 [stmmac] [ 197.171367] stmmac_setup_tc_block_cb+0x70/0x110 [stmmac] [ 197.171367] tc_setup_cb_destroy+0xb3/0x180 [ 197.171367] fl_hw_destroy_filter+0x94/0xc0 [cls_flower] The above issue is due to previous incorrect implementation of tc_del_vlan_flow(), shown below, that uses flow_cls_offload_flow_rule() to get struct flow_rule *rule which is no longer valid for tc filter delete operation. struct flow_rule *rule = flow_cls_offload_flow_rule(cls); struct flow_dissector *dissector = rule->match.dissector; So, to ensure tc_del_vlan_flow() deletes the right VLAN cls record for earlier configured RX queue (configured by hw_tc) in tc_add_vlan_flow(), this patch introduces stmmac_rfs_entry as driver-side flow_cls_offload record for 'RX frame steering' tc flower, currently used for VLAN priority. The implementation has taken consideration for future extension to include other type RX frame steering such as EtherType based. v2: - Clean up overly extensive backtrace and rewrite git message to better explain the kernel NULL pointer issue. Fixes: 0e039f5cf86c ("net: stmmac: add RX frame steering based on VLAN priority in tc flower") Tested-by: Kurt Kanzenbach <kurt@linutronix.de> Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2021-12-11 14:51:34 +00:00
static struct stmmac_rfs_entry *tc_find_rfs(struct stmmac_priv *priv,
struct flow_cls_offload *cls,
bool get_free)
{
int i;
for (i = 0; i < priv->rfs_entries_total; i++) {
struct stmmac_rfs_entry *entry = &priv->rfs_entries[i];
if (entry->cookie == cls->cookie)
return entry;
if (get_free && entry->in_use == false)
return entry;
}
return NULL;
}
#define VLAN_PRIO_FULL_MASK (0x07)
static int tc_add_vlan_flow(struct stmmac_priv *priv,
struct flow_cls_offload *cls)
{
net: stmmac: fix tc flower deletion for VLAN priority Rx steering To replicate the issue:- 1) Add 1 flower filter for VLAN Priority based frame steering:- $ IFDEVNAME=eth0 $ tc qdisc add dev $IFDEVNAME ingress $ tc qdisc add dev $IFDEVNAME root mqprio num_tc 8 \ map 0 1 2 3 4 5 6 7 0 0 0 0 0 0 0 0 \ queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 hw 0 $ tc filter add dev $IFDEVNAME parent ffff: protocol 802.1Q \ flower vlan_prio 0 hw_tc 0 2) Get the 'pref' id $ tc filter show dev $IFDEVNAME ingress 3) Delete a specific tc flower record (say pref 49151) $ tc filter del dev $IFDEVNAME parent ffff: pref 49151 From dmesg, we will observe kernel NULL pointer ooops [ 197.170464] BUG: kernel NULL pointer dereference, address: 0000000000000000 [ 197.171367] #PF: supervisor read access in kernel mode [ 197.171367] #PF: error_code(0x0000) - not-present page [ 197.171367] PGD 0 P4D 0 [ 197.171367] Oops: 0000 [#1] PREEMPT SMP NOPTI <snip> [ 197.171367] RIP: 0010:tc_setup_cls+0x20b/0x4a0 [stmmac] <snip> [ 197.171367] Call Trace: [ 197.171367] <TASK> [ 197.171367] ? __stmmac_disable_all_queues+0xa8/0xe0 [stmmac] [ 197.171367] stmmac_setup_tc_block_cb+0x70/0x110 [stmmac] [ 197.171367] tc_setup_cb_destroy+0xb3/0x180 [ 197.171367] fl_hw_destroy_filter+0x94/0xc0 [cls_flower] The above issue is due to previous incorrect implementation of tc_del_vlan_flow(), shown below, that uses flow_cls_offload_flow_rule() to get struct flow_rule *rule which is no longer valid for tc filter delete operation. struct flow_rule *rule = flow_cls_offload_flow_rule(cls); struct flow_dissector *dissector = rule->match.dissector; So, to ensure tc_del_vlan_flow() deletes the right VLAN cls record for earlier configured RX queue (configured by hw_tc) in tc_add_vlan_flow(), this patch introduces stmmac_rfs_entry as driver-side flow_cls_offload record for 'RX frame steering' tc flower, currently used for VLAN priority. The implementation has taken consideration for future extension to include other type RX frame steering such as EtherType based. v2: - Clean up overly extensive backtrace and rewrite git message to better explain the kernel NULL pointer issue. Fixes: 0e039f5cf86c ("net: stmmac: add RX frame steering based on VLAN priority in tc flower") Tested-by: Kurt Kanzenbach <kurt@linutronix.de> Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2021-12-11 14:51:34 +00:00
struct stmmac_rfs_entry *entry = tc_find_rfs(priv, cls, false);
struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
struct flow_dissector *dissector = rule->match.dissector;
int tc = tc_classid_to_hwtc(priv->dev, cls->classid);
struct flow_match_vlan match;
net: stmmac: fix tc flower deletion for VLAN priority Rx steering To replicate the issue:- 1) Add 1 flower filter for VLAN Priority based frame steering:- $ IFDEVNAME=eth0 $ tc qdisc add dev $IFDEVNAME ingress $ tc qdisc add dev $IFDEVNAME root mqprio num_tc 8 \ map 0 1 2 3 4 5 6 7 0 0 0 0 0 0 0 0 \ queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 hw 0 $ tc filter add dev $IFDEVNAME parent ffff: protocol 802.1Q \ flower vlan_prio 0 hw_tc 0 2) Get the 'pref' id $ tc filter show dev $IFDEVNAME ingress 3) Delete a specific tc flower record (say pref 49151) $ tc filter del dev $IFDEVNAME parent ffff: pref 49151 From dmesg, we will observe kernel NULL pointer ooops [ 197.170464] BUG: kernel NULL pointer dereference, address: 0000000000000000 [ 197.171367] #PF: supervisor read access in kernel mode [ 197.171367] #PF: error_code(0x0000) - not-present page [ 197.171367] PGD 0 P4D 0 [ 197.171367] Oops: 0000 [#1] PREEMPT SMP NOPTI <snip> [ 197.171367] RIP: 0010:tc_setup_cls+0x20b/0x4a0 [stmmac] <snip> [ 197.171367] Call Trace: [ 197.171367] <TASK> [ 197.171367] ? __stmmac_disable_all_queues+0xa8/0xe0 [stmmac] [ 197.171367] stmmac_setup_tc_block_cb+0x70/0x110 [stmmac] [ 197.171367] tc_setup_cb_destroy+0xb3/0x180 [ 197.171367] fl_hw_destroy_filter+0x94/0xc0 [cls_flower] The above issue is due to previous incorrect implementation of tc_del_vlan_flow(), shown below, that uses flow_cls_offload_flow_rule() to get struct flow_rule *rule which is no longer valid for tc filter delete operation. struct flow_rule *rule = flow_cls_offload_flow_rule(cls); struct flow_dissector *dissector = rule->match.dissector; So, to ensure tc_del_vlan_flow() deletes the right VLAN cls record for earlier configured RX queue (configured by hw_tc) in tc_add_vlan_flow(), this patch introduces stmmac_rfs_entry as driver-side flow_cls_offload record for 'RX frame steering' tc flower, currently used for VLAN priority. The implementation has taken consideration for future extension to include other type RX frame steering such as EtherType based. v2: - Clean up overly extensive backtrace and rewrite git message to better explain the kernel NULL pointer issue. Fixes: 0e039f5cf86c ("net: stmmac: add RX frame steering based on VLAN priority in tc flower") Tested-by: Kurt Kanzenbach <kurt@linutronix.de> Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2021-12-11 14:51:34 +00:00
if (!entry) {
entry = tc_find_rfs(priv, cls, true);
if (!entry)
return -ENOENT;
}
if (priv->rfs_entries_cnt[STMMAC_RFS_T_VLAN] >=
priv->rfs_entries_max[STMMAC_RFS_T_VLAN])
return -ENOENT;
/* Nothing to do here */
if (!dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_VLAN))
return -EINVAL;
if (tc < 0) {
netdev_err(priv->dev, "Invalid traffic class\n");
return -EINVAL;
}
flow_rule_match_vlan(rule, &match);
if (match.mask->vlan_priority) {
u32 prio;
if (match.mask->vlan_priority != VLAN_PRIO_FULL_MASK) {
netdev_err(priv->dev, "Only full mask is supported for VLAN priority");
return -EINVAL;
}
prio = BIT(match.key->vlan_priority);
stmmac_rx_queue_prio(priv, priv->hw, prio, tc);
net: stmmac: fix tc flower deletion for VLAN priority Rx steering To replicate the issue:- 1) Add 1 flower filter for VLAN Priority based frame steering:- $ IFDEVNAME=eth0 $ tc qdisc add dev $IFDEVNAME ingress $ tc qdisc add dev $IFDEVNAME root mqprio num_tc 8 \ map 0 1 2 3 4 5 6 7 0 0 0 0 0 0 0 0 \ queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 hw 0 $ tc filter add dev $IFDEVNAME parent ffff: protocol 802.1Q \ flower vlan_prio 0 hw_tc 0 2) Get the 'pref' id $ tc filter show dev $IFDEVNAME ingress 3) Delete a specific tc flower record (say pref 49151) $ tc filter del dev $IFDEVNAME parent ffff: pref 49151 From dmesg, we will observe kernel NULL pointer ooops [ 197.170464] BUG: kernel NULL pointer dereference, address: 0000000000000000 [ 197.171367] #PF: supervisor read access in kernel mode [ 197.171367] #PF: error_code(0x0000) - not-present page [ 197.171367] PGD 0 P4D 0 [ 197.171367] Oops: 0000 [#1] PREEMPT SMP NOPTI <snip> [ 197.171367] RIP: 0010:tc_setup_cls+0x20b/0x4a0 [stmmac] <snip> [ 197.171367] Call Trace: [ 197.171367] <TASK> [ 197.171367] ? __stmmac_disable_all_queues+0xa8/0xe0 [stmmac] [ 197.171367] stmmac_setup_tc_block_cb+0x70/0x110 [stmmac] [ 197.171367] tc_setup_cb_destroy+0xb3/0x180 [ 197.171367] fl_hw_destroy_filter+0x94/0xc0 [cls_flower] The above issue is due to previous incorrect implementation of tc_del_vlan_flow(), shown below, that uses flow_cls_offload_flow_rule() to get struct flow_rule *rule which is no longer valid for tc filter delete operation. struct flow_rule *rule = flow_cls_offload_flow_rule(cls); struct flow_dissector *dissector = rule->match.dissector; So, to ensure tc_del_vlan_flow() deletes the right VLAN cls record for earlier configured RX queue (configured by hw_tc) in tc_add_vlan_flow(), this patch introduces stmmac_rfs_entry as driver-side flow_cls_offload record for 'RX frame steering' tc flower, currently used for VLAN priority. The implementation has taken consideration for future extension to include other type RX frame steering such as EtherType based. v2: - Clean up overly extensive backtrace and rewrite git message to better explain the kernel NULL pointer issue. Fixes: 0e039f5cf86c ("net: stmmac: add RX frame steering based on VLAN priority in tc flower") Tested-by: Kurt Kanzenbach <kurt@linutronix.de> Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2021-12-11 14:51:34 +00:00
entry->in_use = true;
entry->cookie = cls->cookie;
entry->tc = tc;
entry->type = STMMAC_RFS_T_VLAN;
priv->rfs_entries_cnt[STMMAC_RFS_T_VLAN]++;
}
return 0;
}
static int tc_del_vlan_flow(struct stmmac_priv *priv,
struct flow_cls_offload *cls)
{
net: stmmac: fix tc flower deletion for VLAN priority Rx steering To replicate the issue:- 1) Add 1 flower filter for VLAN Priority based frame steering:- $ IFDEVNAME=eth0 $ tc qdisc add dev $IFDEVNAME ingress $ tc qdisc add dev $IFDEVNAME root mqprio num_tc 8 \ map 0 1 2 3 4 5 6 7 0 0 0 0 0 0 0 0 \ queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 hw 0 $ tc filter add dev $IFDEVNAME parent ffff: protocol 802.1Q \ flower vlan_prio 0 hw_tc 0 2) Get the 'pref' id $ tc filter show dev $IFDEVNAME ingress 3) Delete a specific tc flower record (say pref 49151) $ tc filter del dev $IFDEVNAME parent ffff: pref 49151 From dmesg, we will observe kernel NULL pointer ooops [ 197.170464] BUG: kernel NULL pointer dereference, address: 0000000000000000 [ 197.171367] #PF: supervisor read access in kernel mode [ 197.171367] #PF: error_code(0x0000) - not-present page [ 197.171367] PGD 0 P4D 0 [ 197.171367] Oops: 0000 [#1] PREEMPT SMP NOPTI <snip> [ 197.171367] RIP: 0010:tc_setup_cls+0x20b/0x4a0 [stmmac] <snip> [ 197.171367] Call Trace: [ 197.171367] <TASK> [ 197.171367] ? __stmmac_disable_all_queues+0xa8/0xe0 [stmmac] [ 197.171367] stmmac_setup_tc_block_cb+0x70/0x110 [stmmac] [ 197.171367] tc_setup_cb_destroy+0xb3/0x180 [ 197.171367] fl_hw_destroy_filter+0x94/0xc0 [cls_flower] The above issue is due to previous incorrect implementation of tc_del_vlan_flow(), shown below, that uses flow_cls_offload_flow_rule() to get struct flow_rule *rule which is no longer valid for tc filter delete operation. struct flow_rule *rule = flow_cls_offload_flow_rule(cls); struct flow_dissector *dissector = rule->match.dissector; So, to ensure tc_del_vlan_flow() deletes the right VLAN cls record for earlier configured RX queue (configured by hw_tc) in tc_add_vlan_flow(), this patch introduces stmmac_rfs_entry as driver-side flow_cls_offload record for 'RX frame steering' tc flower, currently used for VLAN priority. The implementation has taken consideration for future extension to include other type RX frame steering such as EtherType based. v2: - Clean up overly extensive backtrace and rewrite git message to better explain the kernel NULL pointer issue. Fixes: 0e039f5cf86c ("net: stmmac: add RX frame steering based on VLAN priority in tc flower") Tested-by: Kurt Kanzenbach <kurt@linutronix.de> Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2021-12-11 14:51:34 +00:00
struct stmmac_rfs_entry *entry = tc_find_rfs(priv, cls, false);
net: stmmac: fix tc flower deletion for VLAN priority Rx steering To replicate the issue:- 1) Add 1 flower filter for VLAN Priority based frame steering:- $ IFDEVNAME=eth0 $ tc qdisc add dev $IFDEVNAME ingress $ tc qdisc add dev $IFDEVNAME root mqprio num_tc 8 \ map 0 1 2 3 4 5 6 7 0 0 0 0 0 0 0 0 \ queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 hw 0 $ tc filter add dev $IFDEVNAME parent ffff: protocol 802.1Q \ flower vlan_prio 0 hw_tc 0 2) Get the 'pref' id $ tc filter show dev $IFDEVNAME ingress 3) Delete a specific tc flower record (say pref 49151) $ tc filter del dev $IFDEVNAME parent ffff: pref 49151 From dmesg, we will observe kernel NULL pointer ooops [ 197.170464] BUG: kernel NULL pointer dereference, address: 0000000000000000 [ 197.171367] #PF: supervisor read access in kernel mode [ 197.171367] #PF: error_code(0x0000) - not-present page [ 197.171367] PGD 0 P4D 0 [ 197.171367] Oops: 0000 [#1] PREEMPT SMP NOPTI <snip> [ 197.171367] RIP: 0010:tc_setup_cls+0x20b/0x4a0 [stmmac] <snip> [ 197.171367] Call Trace: [ 197.171367] <TASK> [ 197.171367] ? __stmmac_disable_all_queues+0xa8/0xe0 [stmmac] [ 197.171367] stmmac_setup_tc_block_cb+0x70/0x110 [stmmac] [ 197.171367] tc_setup_cb_destroy+0xb3/0x180 [ 197.171367] fl_hw_destroy_filter+0x94/0xc0 [cls_flower] The above issue is due to previous incorrect implementation of tc_del_vlan_flow(), shown below, that uses flow_cls_offload_flow_rule() to get struct flow_rule *rule which is no longer valid for tc filter delete operation. struct flow_rule *rule = flow_cls_offload_flow_rule(cls); struct flow_dissector *dissector = rule->match.dissector; So, to ensure tc_del_vlan_flow() deletes the right VLAN cls record for earlier configured RX queue (configured by hw_tc) in tc_add_vlan_flow(), this patch introduces stmmac_rfs_entry as driver-side flow_cls_offload record for 'RX frame steering' tc flower, currently used for VLAN priority. The implementation has taken consideration for future extension to include other type RX frame steering such as EtherType based. v2: - Clean up overly extensive backtrace and rewrite git message to better explain the kernel NULL pointer issue. Fixes: 0e039f5cf86c ("net: stmmac: add RX frame steering based on VLAN priority in tc flower") Tested-by: Kurt Kanzenbach <kurt@linutronix.de> Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2021-12-11 14:51:34 +00:00
if (!entry || !entry->in_use || entry->type != STMMAC_RFS_T_VLAN)
return -ENOENT;
net: stmmac: fix tc flower deletion for VLAN priority Rx steering To replicate the issue:- 1) Add 1 flower filter for VLAN Priority based frame steering:- $ IFDEVNAME=eth0 $ tc qdisc add dev $IFDEVNAME ingress $ tc qdisc add dev $IFDEVNAME root mqprio num_tc 8 \ map 0 1 2 3 4 5 6 7 0 0 0 0 0 0 0 0 \ queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 hw 0 $ tc filter add dev $IFDEVNAME parent ffff: protocol 802.1Q \ flower vlan_prio 0 hw_tc 0 2) Get the 'pref' id $ tc filter show dev $IFDEVNAME ingress 3) Delete a specific tc flower record (say pref 49151) $ tc filter del dev $IFDEVNAME parent ffff: pref 49151 From dmesg, we will observe kernel NULL pointer ooops [ 197.170464] BUG: kernel NULL pointer dereference, address: 0000000000000000 [ 197.171367] #PF: supervisor read access in kernel mode [ 197.171367] #PF: error_code(0x0000) - not-present page [ 197.171367] PGD 0 P4D 0 [ 197.171367] Oops: 0000 [#1] PREEMPT SMP NOPTI <snip> [ 197.171367] RIP: 0010:tc_setup_cls+0x20b/0x4a0 [stmmac] <snip> [ 197.171367] Call Trace: [ 197.171367] <TASK> [ 197.171367] ? __stmmac_disable_all_queues+0xa8/0xe0 [stmmac] [ 197.171367] stmmac_setup_tc_block_cb+0x70/0x110 [stmmac] [ 197.171367] tc_setup_cb_destroy+0xb3/0x180 [ 197.171367] fl_hw_destroy_filter+0x94/0xc0 [cls_flower] The above issue is due to previous incorrect implementation of tc_del_vlan_flow(), shown below, that uses flow_cls_offload_flow_rule() to get struct flow_rule *rule which is no longer valid for tc filter delete operation. struct flow_rule *rule = flow_cls_offload_flow_rule(cls); struct flow_dissector *dissector = rule->match.dissector; So, to ensure tc_del_vlan_flow() deletes the right VLAN cls record for earlier configured RX queue (configured by hw_tc) in tc_add_vlan_flow(), this patch introduces stmmac_rfs_entry as driver-side flow_cls_offload record for 'RX frame steering' tc flower, currently used for VLAN priority. The implementation has taken consideration for future extension to include other type RX frame steering such as EtherType based. v2: - Clean up overly extensive backtrace and rewrite git message to better explain the kernel NULL pointer issue. Fixes: 0e039f5cf86c ("net: stmmac: add RX frame steering based on VLAN priority in tc flower") Tested-by: Kurt Kanzenbach <kurt@linutronix.de> Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2021-12-11 14:51:34 +00:00
stmmac_rx_queue_prio(priv, priv->hw, 0, entry->tc);
entry->in_use = false;
entry->cookie = 0;
entry->tc = 0;
entry->type = 0;
net: stmmac: fix tc flower deletion for VLAN priority Rx steering To replicate the issue:- 1) Add 1 flower filter for VLAN Priority based frame steering:- $ IFDEVNAME=eth0 $ tc qdisc add dev $IFDEVNAME ingress $ tc qdisc add dev $IFDEVNAME root mqprio num_tc 8 \ map 0 1 2 3 4 5 6 7 0 0 0 0 0 0 0 0 \ queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 hw 0 $ tc filter add dev $IFDEVNAME parent ffff: protocol 802.1Q \ flower vlan_prio 0 hw_tc 0 2) Get the 'pref' id $ tc filter show dev $IFDEVNAME ingress 3) Delete a specific tc flower record (say pref 49151) $ tc filter del dev $IFDEVNAME parent ffff: pref 49151 From dmesg, we will observe kernel NULL pointer ooops [ 197.170464] BUG: kernel NULL pointer dereference, address: 0000000000000000 [ 197.171367] #PF: supervisor read access in kernel mode [ 197.171367] #PF: error_code(0x0000) - not-present page [ 197.171367] PGD 0 P4D 0 [ 197.171367] Oops: 0000 [#1] PREEMPT SMP NOPTI <snip> [ 197.171367] RIP: 0010:tc_setup_cls+0x20b/0x4a0 [stmmac] <snip> [ 197.171367] Call Trace: [ 197.171367] <TASK> [ 197.171367] ? __stmmac_disable_all_queues+0xa8/0xe0 [stmmac] [ 197.171367] stmmac_setup_tc_block_cb+0x70/0x110 [stmmac] [ 197.171367] tc_setup_cb_destroy+0xb3/0x180 [ 197.171367] fl_hw_destroy_filter+0x94/0xc0 [cls_flower] The above issue is due to previous incorrect implementation of tc_del_vlan_flow(), shown below, that uses flow_cls_offload_flow_rule() to get struct flow_rule *rule which is no longer valid for tc filter delete operation. struct flow_rule *rule = flow_cls_offload_flow_rule(cls); struct flow_dissector *dissector = rule->match.dissector; So, to ensure tc_del_vlan_flow() deletes the right VLAN cls record for earlier configured RX queue (configured by hw_tc) in tc_add_vlan_flow(), this patch introduces stmmac_rfs_entry as driver-side flow_cls_offload record for 'RX frame steering' tc flower, currently used for VLAN priority. The implementation has taken consideration for future extension to include other type RX frame steering such as EtherType based. v2: - Clean up overly extensive backtrace and rewrite git message to better explain the kernel NULL pointer issue. Fixes: 0e039f5cf86c ("net: stmmac: add RX frame steering based on VLAN priority in tc flower") Tested-by: Kurt Kanzenbach <kurt@linutronix.de> Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2021-12-11 14:51:34 +00:00
priv->rfs_entries_cnt[STMMAC_RFS_T_VLAN]--;
return 0;
}
static int tc_add_ethtype_flow(struct stmmac_priv *priv,
struct flow_cls_offload *cls)
{
struct stmmac_rfs_entry *entry = tc_find_rfs(priv, cls, false);
struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
struct flow_dissector *dissector = rule->match.dissector;
int tc = tc_classid_to_hwtc(priv->dev, cls->classid);
struct flow_match_basic match;
if (!entry) {
entry = tc_find_rfs(priv, cls, true);
if (!entry)
return -ENOENT;
}
/* Nothing to do here */
if (!dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_BASIC))
return -EINVAL;
if (tc < 0) {
netdev_err(priv->dev, "Invalid traffic class\n");
return -EINVAL;
}
flow_rule_match_basic(rule, &match);
if (match.mask->n_proto) {
u16 etype = ntohs(match.key->n_proto);
if (match.mask->n_proto != ETHER_TYPE_FULL_MASK) {
netdev_err(priv->dev, "Only full mask is supported for EthType filter");
return -EINVAL;
}
switch (etype) {
case ETH_P_LLDP:
if (priv->rfs_entries_cnt[STMMAC_RFS_T_LLDP] >=
priv->rfs_entries_max[STMMAC_RFS_T_LLDP])
return -ENOENT;
entry->type = STMMAC_RFS_T_LLDP;
priv->rfs_entries_cnt[STMMAC_RFS_T_LLDP]++;
stmmac_rx_queue_routing(priv, priv->hw,
PACKET_DCBCPQ, tc);
break;
case ETH_P_1588:
if (priv->rfs_entries_cnt[STMMAC_RFS_T_1588] >=
priv->rfs_entries_max[STMMAC_RFS_T_1588])
return -ENOENT;
entry->type = STMMAC_RFS_T_1588;
priv->rfs_entries_cnt[STMMAC_RFS_T_1588]++;
stmmac_rx_queue_routing(priv, priv->hw,
PACKET_PTPQ, tc);
break;
default:
netdev_err(priv->dev, "EthType(0x%x) is not supported", etype);
return -EINVAL;
}
entry->in_use = true;
entry->cookie = cls->cookie;
entry->tc = tc;
entry->etype = etype;
return 0;
}
return -EINVAL;
}
static int tc_del_ethtype_flow(struct stmmac_priv *priv,
struct flow_cls_offload *cls)
{
struct stmmac_rfs_entry *entry = tc_find_rfs(priv, cls, false);
if (!entry || !entry->in_use ||
entry->type < STMMAC_RFS_T_LLDP ||
entry->type > STMMAC_RFS_T_1588)
return -ENOENT;
switch (entry->etype) {
case ETH_P_LLDP:
stmmac_rx_queue_routing(priv, priv->hw,
PACKET_DCBCPQ, 0);
priv->rfs_entries_cnt[STMMAC_RFS_T_LLDP]--;
break;
case ETH_P_1588:
stmmac_rx_queue_routing(priv, priv->hw,
PACKET_PTPQ, 0);
priv->rfs_entries_cnt[STMMAC_RFS_T_1588]--;
break;
default:
netdev_err(priv->dev, "EthType(0x%x) is not supported",
entry->etype);
return -EINVAL;
}
entry->in_use = false;
entry->cookie = 0;
entry->tc = 0;
entry->etype = 0;
entry->type = 0;
return 0;
}
static int tc_add_flow_cls(struct stmmac_priv *priv,
struct flow_cls_offload *cls)
{
int ret;
ret = tc_add_flow(priv, cls);
if (!ret)
return ret;
ret = tc_add_ethtype_flow(priv, cls);
if (!ret)
return ret;
return tc_add_vlan_flow(priv, cls);
}
static int tc_del_flow_cls(struct stmmac_priv *priv,
struct flow_cls_offload *cls)
{
int ret;
ret = tc_del_flow(priv, cls);
if (!ret)
return ret;
ret = tc_del_ethtype_flow(priv, cls);
if (!ret)
return ret;
return tc_del_vlan_flow(priv, cls);
}
static int tc_setup_cls(struct stmmac_priv *priv,
struct flow_cls_offload *cls)
{
int ret = 0;
/* When RSS is enabled, the filtering will be bypassed */
if (priv->rss.enable)
return -EBUSY;
switch (cls->command) {
case FLOW_CLS_REPLACE:
ret = tc_add_flow_cls(priv, cls);
break;
case FLOW_CLS_DESTROY:
ret = tc_del_flow_cls(priv, cls);
break;
default:
return -EOPNOTSUPP;
}
return ret;
}
struct timespec64 stmmac_calc_tas_basetime(ktime_t old_base_time,
ktime_t current_time,
u64 cycle_time)
{
struct timespec64 time;
if (ktime_after(old_base_time, current_time)) {
time = ktime_to_timespec64(old_base_time);
} else {
s64 n;
ktime_t base_time;
n = div64_s64(ktime_sub_ns(current_time, old_base_time),
cycle_time);
base_time = ktime_add_ns(old_base_time,
(n + 1) * cycle_time);
time = ktime_to_timespec64(base_time);
}
return time;
}
static int tc_setup_taprio(struct stmmac_priv *priv,
struct tc_taprio_qopt_offload *qopt)
{
u32 size, wid = priv->dma_cap.estwid, dep = priv->dma_cap.estdep;
struct plat_stmmacenet_data *plat = priv->plat;
struct timespec64 time, current_time, qopt_time;
ktime_t current_time_ns;
bool fpe = false;
int i, ret = 0;
u64 ctr;
if (!priv->dma_cap.estsel)
return -EOPNOTSUPP;
switch (wid) {
case 0x1:
wid = 16;
break;
case 0x2:
wid = 20;
break;
case 0x3:
wid = 24;
break;
default:
return -EOPNOTSUPP;
}
switch (dep) {
case 0x1:
dep = 64;
break;
case 0x2:
dep = 128;
break;
case 0x3:
dep = 256;
break;
case 0x4:
dep = 512;
break;
case 0x5:
dep = 1024;
break;
default:
return -EOPNOTSUPP;
}
if (!qopt->enable)
goto disable;
if (qopt->num_entries >= dep)
return -EINVAL;
if (!qopt->cycle_time)
return -ERANGE;
if (!plat->est) {
plat->est = devm_kzalloc(priv->device, sizeof(*plat->est),
GFP_KERNEL);
if (!plat->est)
return -ENOMEM;
mutex_init(&priv->plat->est->lock);
} else {
memset(plat->est, 0, sizeof(*plat->est));
}
size = qopt->num_entries;
mutex_lock(&priv->plat->est->lock);
priv->plat->est->gcl_size = size;
priv->plat->est->enable = qopt->enable;
mutex_unlock(&priv->plat->est->lock);
for (i = 0; i < size; i++) {
s64 delta_ns = qopt->entries[i].interval;
u32 gates = qopt->entries[i].gate_mask;
if (delta_ns > GENMASK(wid, 0))
return -ERANGE;
if (gates > GENMASK(31 - wid, 0))
return -ERANGE;
switch (qopt->entries[i].command) {
case TC_TAPRIO_CMD_SET_GATES:
if (fpe)
return -EINVAL;
break;
case TC_TAPRIO_CMD_SET_AND_HOLD:
gates |= BIT(0);
fpe = true;
break;
case TC_TAPRIO_CMD_SET_AND_RELEASE:
gates &= ~BIT(0);
fpe = true;
break;
default:
return -EOPNOTSUPP;
}
priv->plat->est->gcl[i] = delta_ns | (gates << wid);
}
mutex_lock(&priv->plat->est->lock);
/* Adjust for real system time */
priv->ptp_clock_ops.gettime64(&priv->ptp_clock_ops, &current_time);
current_time_ns = timespec64_to_ktime(current_time);
time = stmmac_calc_tas_basetime(qopt->base_time, current_time_ns,
qopt->cycle_time);
priv->plat->est->btr[0] = (u32)time.tv_nsec;
priv->plat->est->btr[1] = (u32)time.tv_sec;
qopt_time = ktime_to_timespec64(qopt->base_time);
priv->plat->est->btr_reserve[0] = (u32)qopt_time.tv_nsec;
priv->plat->est->btr_reserve[1] = (u32)qopt_time.tv_sec;
ctr = qopt->cycle_time;
priv->plat->est->ctr[0] = do_div(ctr, NSEC_PER_SEC);
priv->plat->est->ctr[1] = (u32)ctr;
if (fpe && !priv->dma_cap.fpesel) {
mutex_unlock(&priv->plat->est->lock);
return -EOPNOTSUPP;
}
net: stmmac: support FPE link partner hand-shaking procedure In order to discover whether remote station supports frame preemption, local station sends verify mPacket and expects response mPacket in return from the remote station. So, we add the functions to send and handle event when verify mPacket and response mPacket are exchanged between the networked stations. The mechanism to handle different FPE states between local and remote station (link partner) is implemented using workqueue which starts a task each time there is some sign of verify & response mPacket exchange as check in FPE IRQ event. The task retries couple of times to try to spot the states that both stations are ready to enter FPE ON. This allows different end points to enable FPE at different time and verify-response mPacket can happen asynchronously. Ultimately, the task will only turn FPE ON when local station have both exchange response in both directions. Thanks to Voon Weifeng for implementing the core functions for detecting FPE events and send mPacket and phylink related change. Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com> Co-developed-by: Voon Weifeng <weifeng.voon@intel.com> Signed-off-by: Voon Weifeng <weifeng.voon@intel.com> Co-developed-by: Tan Tee Min <tee.min.tan@intel.com> Signed-off-by: Tan Tee Min <tee.min.tan@intel.com> Co-developed-by: Mohammad Athari Bin Ismail <mohammad.athari.ismail@intel.com> Signed-off-by: Mohammad Athari Bin Ismail <mohammad.athari.ismail@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2021-03-24 09:07:42 +00:00
/* Actual FPE register configuration will be done after FPE handshake
* is success.
*/
priv->plat->fpe_cfg->enable = fpe;
ret = stmmac_est_configure(priv, priv->ioaddr, priv->plat->est,
priv->plat->clk_ptp_rate);
mutex_unlock(&priv->plat->est->lock);
if (ret) {
netdev_err(priv->dev, "failed to configure EST\n");
goto disable;
}
netdev_info(priv->dev, "configured EST\n");
net: stmmac: support FPE link partner hand-shaking procedure In order to discover whether remote station supports frame preemption, local station sends verify mPacket and expects response mPacket in return from the remote station. So, we add the functions to send and handle event when verify mPacket and response mPacket are exchanged between the networked stations. The mechanism to handle different FPE states between local and remote station (link partner) is implemented using workqueue which starts a task each time there is some sign of verify & response mPacket exchange as check in FPE IRQ event. The task retries couple of times to try to spot the states that both stations are ready to enter FPE ON. This allows different end points to enable FPE at different time and verify-response mPacket can happen asynchronously. Ultimately, the task will only turn FPE ON when local station have both exchange response in both directions. Thanks to Voon Weifeng for implementing the core functions for detecting FPE events and send mPacket and phylink related change. Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com> Co-developed-by: Voon Weifeng <weifeng.voon@intel.com> Signed-off-by: Voon Weifeng <weifeng.voon@intel.com> Co-developed-by: Tan Tee Min <tee.min.tan@intel.com> Signed-off-by: Tan Tee Min <tee.min.tan@intel.com> Co-developed-by: Mohammad Athari Bin Ismail <mohammad.athari.ismail@intel.com> Signed-off-by: Mohammad Athari Bin Ismail <mohammad.athari.ismail@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2021-03-24 09:07:42 +00:00
if (fpe) {
stmmac_fpe_handshake(priv, true);
netdev_info(priv->dev, "start FPE handshake\n");
}
return 0;
disable:
if (priv->plat->est) {
mutex_lock(&priv->plat->est->lock);
priv->plat->est->enable = false;
stmmac_est_configure(priv, priv->ioaddr, priv->plat->est,
priv->plat->clk_ptp_rate);
mutex_unlock(&priv->plat->est->lock);
}
net: stmmac: support FPE link partner hand-shaking procedure In order to discover whether remote station supports frame preemption, local station sends verify mPacket and expects response mPacket in return from the remote station. So, we add the functions to send and handle event when verify mPacket and response mPacket are exchanged between the networked stations. The mechanism to handle different FPE states between local and remote station (link partner) is implemented using workqueue which starts a task each time there is some sign of verify & response mPacket exchange as check in FPE IRQ event. The task retries couple of times to try to spot the states that both stations are ready to enter FPE ON. This allows different end points to enable FPE at different time and verify-response mPacket can happen asynchronously. Ultimately, the task will only turn FPE ON when local station have both exchange response in both directions. Thanks to Voon Weifeng for implementing the core functions for detecting FPE events and send mPacket and phylink related change. Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com> Co-developed-by: Voon Weifeng <weifeng.voon@intel.com> Signed-off-by: Voon Weifeng <weifeng.voon@intel.com> Co-developed-by: Tan Tee Min <tee.min.tan@intel.com> Signed-off-by: Tan Tee Min <tee.min.tan@intel.com> Co-developed-by: Mohammad Athari Bin Ismail <mohammad.athari.ismail@intel.com> Signed-off-by: Mohammad Athari Bin Ismail <mohammad.athari.ismail@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2021-03-24 09:07:42 +00:00
priv->plat->fpe_cfg->enable = false;
stmmac_fpe_configure(priv, priv->ioaddr,
priv->plat->tx_queues_to_use,
priv->plat->rx_queues_to_use,
false);
netdev_info(priv->dev, "disabled FPE\n");
stmmac_fpe_handshake(priv, false);
netdev_info(priv->dev, "stop FPE handshake\n");
return ret;
}
static int tc_setup_etf(struct stmmac_priv *priv,
struct tc_etf_qopt_offload *qopt)
{
if (!priv->dma_cap.tbssel)
return -EOPNOTSUPP;
if (qopt->queue >= priv->plat->tx_queues_to_use)
return -EINVAL;
if (!(priv->tx_queue[qopt->queue].tbs & STMMAC_TBS_AVAIL))
return -EINVAL;
if (qopt->enable)
priv->tx_queue[qopt->queue].tbs |= STMMAC_TBS_EN;
else
priv->tx_queue[qopt->queue].tbs &= ~STMMAC_TBS_EN;
netdev_info(priv->dev, "%s ETF for Queue %d\n",
qopt->enable ? "enabled" : "disabled", qopt->queue);
return 0;
}
const struct stmmac_tc_ops dwmac510_tc_ops = {
.init = tc_init,
.setup_cls_u32 = tc_setup_cls_u32,
.setup_cbs = tc_setup_cbs,
.setup_cls = tc_setup_cls,
.setup_taprio = tc_setup_taprio,
.setup_etf = tc_setup_etf,
};