[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20230427164546.31296-3-shannon.nelson@amd.com>
Date: Thu, 27 Apr 2023 09:45:46 -0700
From: Shannon Nelson <shannon.nelson@....com>
To: <shannon.nelson@....com>, <brett.creeley@....com>,
<netdev@...r.kernel.org>
CC: <drivers@...sando.io>
Subject: [PATCH RFC net-next 2/2] pds_core: tc command handling for vlan push-pop
Set up handling of tc commands for adding VF vlan push-pop HW
offload through the VF's representor. The FW doesn't currently
have a proper set of adminq commands for any tc filtering,
that's a future discussion. For now we have to get by with
using the existing VF_SETATTR command that will add the push
and pop in a single request.
Example commands for use:
# add a qdisc for context
tc qdisc add dev eth0 handle ffff: ingress
# add a rule to wrap outgoing traffic with vlan id 124
tc filter add dev eth0 parent ffff: pref 11 protocol all u32 skip_sw \
match u32 0 0 flowid 1:1 action vlan push id 124
# add a rule to pop the tag from incoming traffic
# this is required normally, but redundant with current FW
tc filter add dev eth0 parent ffff: pref 1 protocol 802.1Q u32 skip_sw \
match u32 0 0 action vlan pop
# remove rules
tc filter del dev eth0 parent ffff:
Signed-off-by: Shannon Nelson <shannon.nelson@....com>
---
drivers/net/ethernet/amd/pds_core/core.h | 2 +
drivers/net/ethernet/amd/pds_core/rep.c | 182 +++++++++++++++++++++++
2 files changed, 184 insertions(+)
diff --git a/drivers/net/ethernet/amd/pds_core/core.h b/drivers/net/ethernet/amd/pds_core/core.h
index 2f38143dd5c2..ebd55bc0a7dc 100644
--- a/drivers/net/ethernet/amd/pds_core/core.h
+++ b/drivers/net/ethernet/amd/pds_core/core.h
@@ -37,6 +37,8 @@ struct pdsc_vf {
struct pdsc *vf;
u16 index;
__le16 vif_types[PDS_DEV_TYPE_MAX];
+
+ u16 vlanid;
};
struct pdsc_devinfo {
diff --git a/drivers/net/ethernet/amd/pds_core/rep.c b/drivers/net/ethernet/amd/pds_core/rep.c
index 297d9e2bac31..265b3be5b9d3 100644
--- a/drivers/net/ethernet/amd/pds_core/rep.c
+++ b/drivers/net/ethernet/amd/pds_core/rep.c
@@ -4,15 +4,194 @@
#include <linux/pci.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
+#include <net/pkt_cls.h>
+#include <net/tc_act/tc_vlan.h>
#include "core.h"
struct pds_rep {
struct pdsc *vf;
struct pdsc *pf;
+ u32 vlan_offload_handle;
+ u16 vlan_id;
};
+static int pdsc_set_vf_vlan(struct net_device *netdev, int vf_id,
+ u16 vid, u8 qos, __be16 proto)
+{
+ struct pds_rep *vf_rep = netdev_priv(netdev);
+ union pds_core_dev_comp comp = { 0 };
+ union pds_core_dev_cmd cmd = {
+ .vf_setattr.opcode = PDS_CORE_CMD_VF_SETATTR,
+ .vf_setattr.attr = PDS_CORE_VF_ATTR_VLAN,
+ .vf_setattr.vf_index = cpu_to_le16(vf_id),
+ .vf_setattr.vlanid = cpu_to_le16(vid),
+ };
+ struct pdsc *pf = vf_rep->pf;
+ int err;
+
+ netdev_info(netdev, "%s: vf %d vlan %d\n", __func__, vf_id, vid);
+
+ err = pdsc_devcmd(pf, &cmd, &comp, pf->devcmd_timeout);
+ if (!err)
+ pf->vfs[vf_id].vlanid = vid;
+
+ return err;
+}
+
+static void print_cls(struct net_device *netdev,
+ struct tc_cls_u32_offload *cls)
+{
+ netdev_info(netdev, "cmd %d chain_i %d proto %#x prio %d\n",
+ cls->command, cls->common.chain_index,
+ cls->common.protocol, cls->common.prio);
+ netdev_info(netdev, " handle %#x val %#x mask %#x link_handle %#x fshift %d\n",
+ cls->knode.handle, cls->knode.val, cls->knode.mask,
+ cls->knode.link_handle, cls->knode.fshift);
+ netdev_info(netdev, " exts %p res %p sel %p\n",
+ cls->knode.exts, cls->knode.res, cls->knode.sel);
+}
+
+static int pdsc_configure_clsu32(struct net_device *netdev,
+ struct tc_cls_u32_offload *cls)
+{
+ struct pds_rep *vf_rep = netdev_priv(netdev);
+ const struct tc_action *a, *act = NULL;
+ int err = 0;
+ u16 vid;
+ int i;
+
+ netdev_info(netdev, "%s: top handle %#x\n",
+ __func__, vf_rep->vlan_offload_handle);
+ print_cls(netdev, cls);
+
+ if (!tcf_exts_has_actions(cls->knode.exts))
+ return -EINVAL;
+
+ /* only one action TCA_ID_VLAN is supported */
+ tcf_exts_for_each_action(i, a, cls->knode.exts) {
+ if (!is_tcf_vlan(a)) {
+ netdev_err(netdev, "%s: unsupported action %d\n",
+ __func__, a->ops->id);
+ return -EOPNOTSUPP;
+ } else if (act) {
+ netdev_err(netdev, "%s: multiple vlan actions?\n",
+ __func__);
+ return -EOPNOTSUPP;
+ }
+
+ act = a;
+ }
+
+ switch (tcf_vlan_action(act)) {
+ case TCA_VLAN_ACT_PUSH:
+ vid = tcf_vlan_push_vid(act);
+ if (!vid)
+ return -EINVAL;
+
+ /* only one allowed at a time */
+ if (vf_rep->vlan_id)
+ return -EBUSY;
+
+ /* with existing FW this will set up both push and pop */
+ err = pdsc_set_vf_vlan(netdev, vf_rep->vf->vf_id, vid, 0,
+ tcf_vlan_push_proto(act));
+ if (!err) {
+ vf_rep->vlan_id = vid;
+ vf_rep->vlan_offload_handle = TC_U32_USERHTID(cls->knode.handle);
+ }
+ break;
+ case TCA_VLAN_ACT_POP:
+ /* with existing FW this is redundant */
+ err = 0;
+ break;
+ default:
+ netdev_err(netdev, "%s: tcf_vlan_action %d unsupported\n",
+ __func__, tcf_vlan_action(act));
+ return -EOPNOTSUPP;
+ }
+
+ return err;
+}
+
+static int pdsc_delete_clsu32(struct net_device *netdev,
+ struct tc_cls_u32_offload *cls)
+{
+ struct pds_rep *vf_rep = netdev_priv(netdev);
+ int err;
+
+ netdev_info(netdev, "%s: top handle %#x\n",
+ __func__, vf_rep->vlan_offload_handle);
+ print_cls(netdev, cls);
+
+ if (vf_rep->vlan_offload_handle != TC_U32_USERHTID(cls->knode.handle))
+ return -EINVAL;
+
+ netdev_info(netdev, "%s: vf_id %d vlan_id %d delete\n",
+ __func__, vf_rep->vf->vf_id, vf_rep->vlan_id);
+
+ /* with existing FW this will remove both push and pop */
+ err = pdsc_set_vf_vlan(netdev, vf_rep->vf->vf_id, 0, 0, 0);
+ vf_rep->vlan_id = 0;
+ vf_rep->vlan_offload_handle = 0;
+
+ return err;
+}
+
+static int pdsc_setup_tc_cls_u32(struct net_device *netdev,
+ struct tc_cls_u32_offload *cls_u32)
+{
+ switch (cls_u32->command) {
+ case TC_CLSU32_NEW_KNODE:
+ case TC_CLSU32_REPLACE_KNODE:
+ return pdsc_configure_clsu32(netdev, cls_u32);
+ case TC_CLSU32_DELETE_KNODE:
+ return pdsc_delete_clsu32(netdev, cls_u32);
+
+ case TC_CLSU32_NEW_HNODE:
+ case TC_CLSU32_REPLACE_HNODE:
+ case TC_CLSU32_DELETE_HNODE:
+ return 0;
+ default:
+ netdev_info(netdev, "%s: unhandled cls_u32->command = %d\n",
+ __func__, cls_u32->command);
+ return -EOPNOTSUPP;
+ }
+}
+
+static int pdsc_setup_tc_block_cb(enum tc_setup_type tc_type, void *type_data,
+ void *cb_priv)
+{
+ struct net_device *netdev = cb_priv;
+
+ if (!tc_cls_can_offload_and_chain0(netdev, type_data))
+ return -EOPNOTSUPP;
+
+ switch (tc_type) {
+ case TC_SETUP_CLSU32:
+ return pdsc_setup_tc_cls_u32(netdev, type_data);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static LIST_HEAD(pdsc_block_cb_list);
+
+static int pdsc_setup_tc(struct net_device *netdev, enum tc_setup_type tc_type, void *type_data)
+{
+ switch (tc_type) {
+ case TC_SETUP_BLOCK:
+ return flow_block_cb_setup_simple(type_data,
+ &pdsc_block_cb_list,
+ pdsc_setup_tc_block_cb,
+ netdev, netdev, true);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
static const struct net_device_ops pdsc_rep_netdev_ops = {
+ .ndo_setup_tc = pdsc_setup_tc,
};
static void pdsc_get_rep_drvinfo(struct net_device *netdev,
@@ -60,6 +239,9 @@ int pdsc_add_rep(struct pdsc *vf, struct pdsc *pf)
vf->netdev->netdev_ops = &pdsc_rep_netdev_ops;
vf->netdev->ethtool_ops = &pdsc_rep_ethtool_ops;
+ vf->netdev->features |= NETIF_F_HW_TC;
+ vf->netdev->hw_features |= NETIF_F_HW_TC;
+
netif_carrier_off(vf->netdev);
SET_NETDEV_DEVLINK_PORT(vf->netdev, &vf->dl_port);
--
2.17.1
Powered by blists - more mailing lists