mlxsw: Move Tx header handling to PCI driver

Tx header should be added to all packets transmitted from the CPU to
Spectrum ASICs. Historically, handling this header was added as a driver
function, as Tx header is different between Spectrum and Switch-X. See
SwitchX implementation in commit 31557f0f97 ("mlxsw: Introduce
Mellanox SwitchX-2 ASIC support"). From May 2021, there is no support
for SwitchX-2 ASIC, and all the relevant code was removed.

For now, there is no justification to handle Tx header as part of
spectrum.c, we can handle this as part of PCI, in skb_transmit().

A future patch set will add support for XDP in mlxsw driver, to support
XDP_TX and XDP_REDIRECT actions, Tx header should be added before
transmitting the packet. As preparation for this, move Tx header handling
to PCI driver, so then XDP code will not have to call API from spectrum.c.
This also improves the code as now Tx header is pushed just before
transmitting, so it is not done from many flows which might miss something.

Note that for PTP, we should configure Tx header differently, use the
fields from mlxsw_txhdr_info to configure the packets correctly in PCI
driver. Handle VLAN tagging in switch driver, verify that packet which
should be transmitted as data is tagged, otherwise, tag it.

Remove the calls for thxdr_construct() functions, as now this is done as
part of skb_transmit().

Signed-off-by: Amit Cohen <amcohen@nvidia.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: Petr Machata <petrm@nvidia.com>
Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
Link: https://patch.msgid.link/293a81e6f7d59a8ec9f9592edb7745536649ff11.1737044384.git.petrm@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Amit Cohen
2025-01-16 17:38:17 +01:00
committed by Jakub Kicinski
parent c89d9c3d0a
commit 6ce1aac748
7 changed files with 65 additions and 180 deletions

View File

@@ -737,9 +737,8 @@ static int mlxsw_emad_transmit(struct mlxsw_core *mlxsw_core,
if (!skb)
return -ENOMEM;
trace_devlink_hwmsg(priv_to_devlink(mlxsw_core), false, 0,
skb->data + mlxsw_core->driver->txhdr_len,
skb->len - mlxsw_core->driver->txhdr_len);
trace_devlink_hwmsg(priv_to_devlink(mlxsw_core), false, 0, skb->data,
skb->len);
atomic_set(&trans->active, 1);
err = mlxsw_core_skb_transmit(mlxsw_core, skb, &trans->txhdr_info);
@@ -995,7 +994,6 @@ static int mlxsw_emad_reg_access(struct mlxsw_core *mlxsw_core,
trans->type = type;
mlxsw_emad_construct(mlxsw_core, skb, reg, payload, type, trans->tid);
mlxsw_core->driver->txhdr_construct(skb, &trans->txhdr_info.tx_info);
spin_lock_bh(&mlxsw_core->emad.trans_list_lock);
list_add_tail_rcu(&trans->list, &mlxsw_core->emad.trans_list);

View File

@@ -432,8 +432,6 @@ struct mlxsw_driver {
int (*trap_policer_counter_get)(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_policer *policer,
u64 *p_drops);
void (*txhdr_construct)(struct sk_buff *skb,
const struct mlxsw_tx_info *tx_info);
int (*resources_register)(struct mlxsw_core *mlxsw_core);
int (*kvd_sizes_get)(struct mlxsw_core *mlxsw_core,
const struct mlxsw_config_profile *profile,

View File

@@ -21,6 +21,7 @@
#include "cmd.h"
#include "port.h"
#include "resources.h"
#include "txheader.h"
#define mlxsw_pci_write32(mlxsw_pci, reg, val) \
iowrite32be(val, (mlxsw_pci)->hw_addr + (MLXSW_PCI_ ## reg))
@@ -2095,6 +2096,39 @@ static void mlxsw_pci_fini(void *bus_priv)
mlxsw_pci_free_irq_vectors(mlxsw_pci);
}
static int mlxsw_pci_txhdr_construct(struct sk_buff *skb,
const struct mlxsw_txhdr_info *txhdr_info)
{
const struct mlxsw_tx_info tx_info = txhdr_info->tx_info;
char *txhdr;
if (skb_cow_head(skb, MLXSW_TXHDR_LEN))
return -ENOMEM;
txhdr = skb_push(skb, MLXSW_TXHDR_LEN);
memset(txhdr, 0, MLXSW_TXHDR_LEN);
mlxsw_tx_hdr_version_set(txhdr, MLXSW_TXHDR_VERSION_1);
mlxsw_tx_hdr_proto_set(txhdr, MLXSW_TXHDR_PROTO_ETH);
mlxsw_tx_hdr_swid_set(txhdr, 0);
if (unlikely(txhdr_info->data)) {
u16 fid = txhdr_info->max_fid + tx_info.local_port - 1;
mlxsw_tx_hdr_rx_is_router_set(txhdr, true);
mlxsw_tx_hdr_fid_valid_set(txhdr, true);
mlxsw_tx_hdr_fid_set(txhdr, fid);
mlxsw_tx_hdr_type_set(txhdr, MLXSW_TXHDR_TYPE_DATA);
} else {
mlxsw_tx_hdr_ctl_set(txhdr, MLXSW_TXHDR_ETH_CTL);
mlxsw_tx_hdr_control_tclass_set(txhdr, 1);
mlxsw_tx_hdr_port_mid_set(txhdr, tx_info.local_port);
mlxsw_tx_hdr_type_set(txhdr, MLXSW_TXHDR_TYPE_CONTROL);
}
return 0;
}
static struct mlxsw_pci_queue *
mlxsw_pci_sdq_pick(struct mlxsw_pci *mlxsw_pci,
const struct mlxsw_tx_info *tx_info)
@@ -2131,6 +2165,10 @@ static int mlxsw_pci_skb_transmit(void *bus_priv, struct sk_buff *skb,
int i;
int err;
err = mlxsw_pci_txhdr_construct(skb, txhdr_info);
if (err)
return err;
if (skb_shinfo(skb)->nr_frags > MLXSW_PCI_WQE_SG_ENTRIES - 1) {
err = skb_linearize(skb);
if (err)

View File

@@ -165,61 +165,6 @@ void mlxsw_sp_flow_counter_free(struct mlxsw_sp *mlxsw_sp,
counter_index);
}
void mlxsw_sp_txhdr_construct(struct sk_buff *skb,
const struct mlxsw_tx_info *tx_info)
{
char *txhdr = skb_push(skb, MLXSW_TXHDR_LEN);
memset(txhdr, 0, MLXSW_TXHDR_LEN);
mlxsw_tx_hdr_version_set(txhdr, MLXSW_TXHDR_VERSION_1);
mlxsw_tx_hdr_ctl_set(txhdr, MLXSW_TXHDR_ETH_CTL);
mlxsw_tx_hdr_proto_set(txhdr, MLXSW_TXHDR_PROTO_ETH);
mlxsw_tx_hdr_swid_set(txhdr, 0);
mlxsw_tx_hdr_control_tclass_set(txhdr, 1);
mlxsw_tx_hdr_port_mid_set(txhdr, tx_info->local_port);
mlxsw_tx_hdr_type_set(txhdr, MLXSW_TXHDR_TYPE_CONTROL);
}
int
mlxsw_sp_txhdr_ptp_data_construct(struct mlxsw_core *mlxsw_core,
struct mlxsw_sp_port *mlxsw_sp_port,
struct sk_buff *skb,
const struct mlxsw_tx_info *tx_info)
{
char *txhdr;
u16 max_fid;
int err;
if (skb_cow_head(skb, MLXSW_TXHDR_LEN)) {
err = -ENOMEM;
goto err_skb_cow_head;
}
if (!MLXSW_CORE_RES_VALID(mlxsw_core, FID)) {
err = -EIO;
goto err_res_valid;
}
max_fid = MLXSW_CORE_RES_GET(mlxsw_core, FID);
txhdr = skb_push(skb, MLXSW_TXHDR_LEN);
memset(txhdr, 0, MLXSW_TXHDR_LEN);
mlxsw_tx_hdr_version_set(txhdr, MLXSW_TXHDR_VERSION_1);
mlxsw_tx_hdr_proto_set(txhdr, MLXSW_TXHDR_PROTO_ETH);
mlxsw_tx_hdr_rx_is_router_set(txhdr, true);
mlxsw_tx_hdr_fid_valid_set(txhdr, true);
mlxsw_tx_hdr_fid_set(txhdr, max_fid + tx_info->local_port - 1);
mlxsw_tx_hdr_type_set(txhdr, MLXSW_TXHDR_TYPE_DATA);
return 0;
err_res_valid:
err_skb_cow_head:
this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped);
dev_kfree_skb_any(skb);
return err;
}
static bool mlxsw_sp_skb_requires_ts(struct sk_buff *skb)
{
unsigned int type;
@@ -242,46 +187,38 @@ static void mlxsw_sp_txhdr_info_data_init(struct mlxsw_core *mlxsw_core,
txhdr_info->max_fid = max_fid;
}
static void
static struct sk_buff *
mlxsw_sp_vlan_tag_push(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb)
{
/* In some Spectrum ASICs, in order for PTP event packets to have their
* correction field correctly set on the egress port they must be
* transmitted as data packets. Such packets ingress the ASIC via the
* CPU port and must have a VLAN tag, as the CPU port is not configured
* with a PVID. Push the default VLAN (4095), which is configured as
* egress untagged on all the ports.
*/
if (skb_vlan_tagged(skb))
return skb;
return vlan_insert_tag_set_proto(skb, htons(ETH_P_8021Q),
MLXSW_SP_DEFAULT_VID);
}
static struct sk_buff *
mlxsw_sp_txhdr_preparations(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
struct mlxsw_txhdr_info *txhdr_info)
{
if (likely(!mlxsw_sp_skb_requires_ts(skb)))
return;
return skb;
if (!mlxsw_sp->ptp_ops->tx_as_data)
return;
return skb;
/* Special handling for PTP events that require a time stamp and cannot
* be transmitted as regular control packets.
*/
mlxsw_sp_txhdr_info_data_init(mlxsw_sp->core, skb, txhdr_info);
}
static int mlxsw_sp_txhdr_handle(struct mlxsw_core *mlxsw_core,
struct mlxsw_sp_port *mlxsw_sp_port,
struct sk_buff *skb,
const struct mlxsw_tx_info *tx_info)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
/* In Spectrum-2 and Spectrum-3, PTP events that require a time stamp
* need special handling and cannot be transmitted as regular control
* packets.
*/
if (unlikely(mlxsw_sp_skb_requires_ts(skb)))
return mlxsw_sp->ptp_ops->txhdr_construct(mlxsw_core,
mlxsw_sp_port, skb,
tx_info);
if (skb_cow_head(skb, MLXSW_TXHDR_LEN)) {
this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped);
dev_kfree_skb_any(skb);
return -ENOMEM;
}
mlxsw_sp_txhdr_construct(skb, tx_info);
return 0;
return mlxsw_sp_vlan_tag_push(mlxsw_sp, skb);
}
enum mlxsw_reg_spms_state mlxsw_sp_stp_spms_state(u8 state)
@@ -697,12 +634,11 @@ static netdev_tx_t mlxsw_sp_port_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
}
mlxsw_sp_txhdr_preparations(mlxsw_sp, skb, &txhdr_info);
err = mlxsw_sp_txhdr_handle(mlxsw_sp->core, mlxsw_sp_port, skb,
&txhdr_info.tx_info);
if (err)
skb = mlxsw_sp_txhdr_preparations(mlxsw_sp, skb, &txhdr_info);
if (!skb) {
this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped);
return NETDEV_TX_OK;
}
/* TX header is consumed by HW on the way so we shouldn't count its
* bytes as being sent.
@@ -2753,7 +2689,6 @@ static const struct mlxsw_sp_ptp_ops mlxsw_sp1_ptp_ops = {
.get_stats_count = mlxsw_sp1_get_stats_count,
.get_stats_strings = mlxsw_sp1_get_stats_strings,
.get_stats = mlxsw_sp1_get_stats,
.txhdr_construct = mlxsw_sp_ptp_txhdr_construct,
};
static const struct mlxsw_sp_ptp_ops mlxsw_sp2_ptp_ops = {
@@ -2772,7 +2707,6 @@ static const struct mlxsw_sp_ptp_ops mlxsw_sp2_ptp_ops = {
.get_stats_count = mlxsw_sp2_get_stats_count,
.get_stats_strings = mlxsw_sp2_get_stats_strings,
.get_stats = mlxsw_sp2_get_stats,
.txhdr_construct = mlxsw_sp2_ptp_txhdr_construct,
.tx_as_data = true,
};
@@ -2792,7 +2726,6 @@ static const struct mlxsw_sp_ptp_ops mlxsw_sp4_ptp_ops = {
.get_stats_count = mlxsw_sp2_get_stats_count,
.get_stats_strings = mlxsw_sp2_get_stats_strings,
.get_stats = mlxsw_sp2_get_stats,
.txhdr_construct = mlxsw_sp_ptp_txhdr_construct,
};
struct mlxsw_sp_sample_trigger_node {
@@ -3954,7 +3887,6 @@ static struct mlxsw_driver mlxsw_sp1_driver = {
.trap_policer_fini = mlxsw_sp_trap_policer_fini,
.trap_policer_set = mlxsw_sp_trap_policer_set,
.trap_policer_counter_get = mlxsw_sp_trap_policer_counter_get,
.txhdr_construct = mlxsw_sp_txhdr_construct,
.resources_register = mlxsw_sp1_resources_register,
.kvd_sizes_get = mlxsw_sp_kvd_sizes_get,
.ptp_transmitted = mlxsw_sp_ptp_transmitted,
@@ -3992,7 +3924,6 @@ static struct mlxsw_driver mlxsw_sp2_driver = {
.trap_policer_fini = mlxsw_sp_trap_policer_fini,
.trap_policer_set = mlxsw_sp_trap_policer_set,
.trap_policer_counter_get = mlxsw_sp_trap_policer_counter_get,
.txhdr_construct = mlxsw_sp_txhdr_construct,
.resources_register = mlxsw_sp2_resources_register,
.ptp_transmitted = mlxsw_sp_ptp_transmitted,
.txhdr_len = MLXSW_TXHDR_LEN,
@@ -4029,7 +3960,6 @@ static struct mlxsw_driver mlxsw_sp3_driver = {
.trap_policer_fini = mlxsw_sp_trap_policer_fini,
.trap_policer_set = mlxsw_sp_trap_policer_set,
.trap_policer_counter_get = mlxsw_sp_trap_policer_counter_get,
.txhdr_construct = mlxsw_sp_txhdr_construct,
.resources_register = mlxsw_sp2_resources_register,
.ptp_transmitted = mlxsw_sp_ptp_transmitted,
.txhdr_len = MLXSW_TXHDR_LEN,
@@ -4064,7 +3994,6 @@ static struct mlxsw_driver mlxsw_sp4_driver = {
.trap_policer_fini = mlxsw_sp_trap_policer_fini,
.trap_policer_set = mlxsw_sp_trap_policer_set,
.trap_policer_counter_get = mlxsw_sp_trap_policer_counter_get,
.txhdr_construct = mlxsw_sp_txhdr_construct,
.resources_register = mlxsw_sp2_resources_register,
.ptp_transmitted = mlxsw_sp_ptp_transmitted,
.txhdr_len = MLXSW_TXHDR_LEN,

View File

@@ -243,10 +243,6 @@ struct mlxsw_sp_ptp_ops {
void (*get_stats_strings)(u8 **p);
void (*get_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
u64 *data, int data_index);
int (*txhdr_construct)(struct mlxsw_core *mlxsw_core,
struct mlxsw_sp_port *mlxsw_sp_port,
struct sk_buff *skb,
const struct mlxsw_tx_info *tx_info);
bool tx_as_data;
};
@@ -712,12 +708,6 @@ int mlxsw_sp_flow_counter_alloc(struct mlxsw_sp *mlxsw_sp,
unsigned int *p_counter_index);
void mlxsw_sp_flow_counter_free(struct mlxsw_sp *mlxsw_sp,
unsigned int counter_index);
void mlxsw_sp_txhdr_construct(struct sk_buff *skb,
const struct mlxsw_tx_info *tx_info);
int mlxsw_sp_txhdr_ptp_data_construct(struct mlxsw_core *mlxsw_core,
struct mlxsw_sp_port *mlxsw_sp_port,
struct sk_buff *skb,
const struct mlxsw_tx_info *tx_info);
bool mlxsw_sp_port_dev_check(const struct net_device *dev);
struct mlxsw_sp *mlxsw_sp_lower_get(struct net_device *dev);
struct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find(struct net_device *dev);

View File

@@ -1683,43 +1683,3 @@ int mlxsw_sp2_ptp_get_ts_info(struct mlxsw_sp *mlxsw_sp,
return 0;
}
int mlxsw_sp_ptp_txhdr_construct(struct mlxsw_core *mlxsw_core,
struct mlxsw_sp_port *mlxsw_sp_port,
struct sk_buff *skb,
const struct mlxsw_tx_info *tx_info)
{
if (skb_cow_head(skb, MLXSW_TXHDR_LEN)) {
this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped);
dev_kfree_skb_any(skb);
return -ENOMEM;
}
mlxsw_sp_txhdr_construct(skb, tx_info);
return 0;
}
int mlxsw_sp2_ptp_txhdr_construct(struct mlxsw_core *mlxsw_core,
struct mlxsw_sp_port *mlxsw_sp_port,
struct sk_buff *skb,
const struct mlxsw_tx_info *tx_info)
{
/* In Spectrum-2 and Spectrum-3, in order for PTP event packets to have
* their correction field correctly set on the egress port they must be
* transmitted as data packets. Such packets ingress the ASIC via the
* CPU port and must have a VLAN tag, as the CPU port is not configured
* with a PVID. Push the default VLAN (4095), which is configured as
* egress untagged on all the ports.
*/
if (!skb_vlan_tagged(skb)) {
skb = vlan_insert_tag_set_proto(skb, htons(ETH_P_8021Q),
MLXSW_SP_DEFAULT_VID);
if (!skb) {
this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped);
return -ENOMEM;
}
}
return mlxsw_sp_txhdr_ptp_data_construct(mlxsw_core, mlxsw_sp_port, skb,
tx_info);
}

View File

@@ -49,11 +49,6 @@ void mlxsw_sp1_get_stats_strings(u8 **p);
void mlxsw_sp1_get_stats(struct mlxsw_sp_port *mlxsw_sp_port,
u64 *data, int data_index);
int mlxsw_sp_ptp_txhdr_construct(struct mlxsw_core *mlxsw_core,
struct mlxsw_sp_port *mlxsw_sp_port,
struct sk_buff *skb,
const struct mlxsw_tx_info *tx_info);
struct mlxsw_sp_ptp_clock *
mlxsw_sp2_ptp_clock_init(struct mlxsw_sp *mlxsw_sp, struct device *dev);
@@ -78,11 +73,6 @@ int mlxsw_sp2_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port,
int mlxsw_sp2_ptp_get_ts_info(struct mlxsw_sp *mlxsw_sp,
struct kernel_ethtool_ts_info *info);
int mlxsw_sp2_ptp_txhdr_construct(struct mlxsw_core *mlxsw_core,
struct mlxsw_sp_port *mlxsw_sp_port,
struct sk_buff *skb,
const struct mlxsw_tx_info *tx_info);
#else
static inline struct mlxsw_sp_ptp_clock *
@@ -157,15 +147,6 @@ static inline void mlxsw_sp1_get_stats(struct mlxsw_sp_port *mlxsw_sp_port,
{
}
static inline int
mlxsw_sp_ptp_txhdr_construct(struct mlxsw_core *mlxsw_core,
struct mlxsw_sp_port *mlxsw_sp_port,
struct sk_buff *skb,
const struct mlxsw_tx_info *tx_info)
{
return -EOPNOTSUPP;
}
static inline struct mlxsw_sp_ptp_clock *
mlxsw_sp2_ptp_clock_init(struct mlxsw_sp *mlxsw_sp, struct device *dev)
{
@@ -211,15 +192,6 @@ mlxsw_sp2_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port,
{
return -EOPNOTSUPP;
}
static inline int
mlxsw_sp2_ptp_txhdr_construct(struct mlxsw_core *mlxsw_core,
struct mlxsw_sp_port *mlxsw_sp_port,
struct sk_buff *skb,
const struct mlxsw_tx_info *tx_info)
{
return -EOPNOTSUPP;
}
#endif
static inline void mlxsw_sp2_ptp_shaper_work(struct work_struct *work)