net/mlx5: implement max_sfs parameter

Implement max_sfs generic parameter to allow users to control the total
light-weight NIC subfunctions that can be created using devlink instead
of external vendor tools. A value of 0 will effectively disable creation
of new subfunction devices. A warning is sent to user-space via extack
(returning extack without error code is interpreted as a warning by
user-space tools). The maximum value is capped at U16_MAX.

Signed-off-by: Nikolay Aleksandrov <nikolay@nvidia.com>
Reviewed-by: David Ahern <dsahern@kernel.org>
Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
Link: https://patch.msgid.link/20260806073037.3001886-3-tariqt@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Nikolay Aleksandrov
2026-08-06 10:30:37 +03:00
committed by Jakub Kicinski
parent 26ba30221c
commit 38c35fdd80
2 changed files with 128 additions and 4 deletions

View File

@@ -45,8 +45,13 @@ Parameters
- The range is between 1 and a device-specific max.
- Applies to each physical function (PF) independently, if the device
supports it. Otherwise, it applies symmetrically to all PFs.
* - ``max_sfs``
- permanent
- The range is between 0 and a device-specific max.
- Applies to each physical function (PF) independently.
Note: permanent parameters such as ``enable_sriov`` and ``total_vfs`` require FW reset to take effect
Note: permanent parameters such as ``enable_sriov``, ``total_vfs`` and ``max_sfs``
require FW reset to take effect
.. code-block:: bash

View File

@@ -68,7 +68,9 @@ struct mlx5_ifc_mnvda_reg_bits {
struct mlx5_ifc_nv_global_pci_conf_bits {
u8 sriov_valid[0x1];
u8 reserved_at_1[0x10];
u8 reserved_at_1[0xa];
u8 per_pf_num_sf[0x1];
u8 reserved_at_c[0x5];
u8 per_pf_total_vf[0x1];
u8 reserved_at_12[0xe];
@@ -93,9 +95,11 @@ struct mlx5_ifc_nv_global_pci_cap_bits {
};
struct mlx5_ifc_nv_pf_pci_conf_bits {
u8 reserved_at_0[0x9];
u8 log_sf_bar_size[0x8];
u8 pf_total_sf_en[0x1];
u8 pf_total_vf_en[0x1];
u8 reserved_at_a[0x16];
u8 reserved_at_a[0x6];
u8 total_sf[0x10];
u8 reserved_at_20[0x20];
@@ -158,6 +162,8 @@ struct mlx5_ifc_nv_sw_accelerate_conf_bits {
#define MLX5_GET_CFG_HDR_LEN(_mnvda_ptr) \
MLX5_GET(mnvda_reg, _mnvda_ptr, configuration_item_header.length)
#define MLX5_DEFAULT_LOG_SF_BAR_SIZE 12
static int mlx5_nv_param_read(struct mlx5_core_dev *dev, void *mnvda,
size_t len)
{
@@ -755,6 +761,115 @@ static int mlx5_devlink_total_vfs_validate(struct devlink *devlink, u32 id,
return 0;
}
static int mlx5_devlink_max_sfs_get(struct devlink *devlink, u32 id,
struct devlink_param_gset_ctx *ctx,
struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
u32 mnvda[MLX5_ST_SZ_DW(mnvda_reg)] = {};
void *data;
int err;
err = mlx5_nv_param_read_global_pci_conf(dev, mnvda, sizeof(mnvda));
if (err) {
NL_SET_ERR_MSG_MOD(extack,
"Failed to read global PCI configuration");
return err;
}
data = MLX5_ADDR_OF(mnvda_reg, mnvda, configuration_item_data);
if (!MLX5_GET(nv_global_pci_conf, data, per_pf_num_sf)) {
ctx->val.vu32 = 0;
return 0;
}
memset(mnvda, 0, sizeof(mnvda));
err = mlx5_nv_param_read_per_host_pf_conf(dev, mnvda, sizeof(mnvda));
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Failed to read PF configuration");
return err;
}
data = MLX5_ADDR_OF(mnvda_reg, mnvda, configuration_item_data);
if (MLX5_GET(nv_pf_pci_conf, data, pf_total_sf_en))
ctx->val.vu32 = MLX5_GET(nv_pf_pci_conf, data, total_sf);
else
ctx->val.vu32 = 0;
return 0;
}
static int mlx5_devlink_max_sfs_validate(struct devlink *devlink, u32 id,
union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
if (val->vu32 > U16_MAX) {
NL_SET_ERR_MSG_FMT_MOD(extack,
"Max SFs allowed value is %u", U16_MAX);
return -EINVAL;
}
return 0;
}
static int mlx5_devlink_max_sfs_set(struct devlink *devlink, u32 id,
struct devlink_param_gset_ctx *ctx,
struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
u32 mnvda[MLX5_ST_SZ_DW(mnvda_reg)] = {};
void *data;
int err;
/* we don't explicitly disable per_pf_num_sf when max_sfs is 0 because
* another PF may be using SFs
*/
if (ctx->val.vu32) {
err = mlx5_nv_param_read_global_pci_conf(dev, mnvda,
sizeof(mnvda));
if (err) {
NL_SET_ERR_MSG_MOD(extack,
"Failed to read global PCI configuration");
return err;
}
data = MLX5_ADDR_OF(mnvda_reg, mnvda, configuration_item_data);
/* always enable per_pf_num_sf because another PF may use SFs */
MLX5_SET(nv_global_pci_conf, data, per_pf_num_sf, 1);
err = mlx5_nv_param_write(dev, mnvda, sizeof(mnvda));
if (err) {
NL_SET_ERR_MSG_MOD(extack,
"Failed to change per_pf_num_sf global PCI configuration");
return err;
}
memset(mnvda, 0, sizeof(mnvda));
}
err = mlx5_nv_param_read_per_host_pf_conf(dev, mnvda, sizeof(mnvda));
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Failed to read PF configuration");
return err;
}
data = MLX5_ADDR_OF(mnvda_reg, mnvda, configuration_item_data);
MLX5_SET(nv_pf_pci_conf, data, log_sf_bar_size,
ctx->val.vu32 ? MLX5_DEFAULT_LOG_SF_BAR_SIZE : 0);
MLX5_SET(nv_pf_pci_conf, data, pf_total_sf_en, !!ctx->val.vu32);
MLX5_SET(nv_pf_pci_conf, data, total_sf, ctx->val.vu32);
err = mlx5_nv_param_write(dev, mnvda, sizeof(mnvda));
if (err) {
NL_SET_ERR_MSG_MOD(extack,
"Failed to change PF PCI configuration");
return err;
}
NL_SET_ERR_MSG_MOD(extack,
"Modifying max_sfs requires a FW reset and PCI bus rescan");
return 0;
}
static const struct devlink_param mlx5_nv_param_devlink_params[] = {
DEVLINK_PARAM_GENERIC(ENABLE_SRIOV, BIT(DEVLINK_PARAM_CMODE_PERMANENT),
mlx5_devlink_enable_sriov_get,
@@ -763,6 +878,10 @@ static const struct devlink_param mlx5_nv_param_devlink_params[] = {
mlx5_devlink_total_vfs_get,
mlx5_devlink_total_vfs_set,
mlx5_devlink_total_vfs_validate),
DEVLINK_PARAM_GENERIC(MAX_SFS, BIT(DEVLINK_PARAM_CMODE_PERMANENT),
mlx5_devlink_max_sfs_get,
mlx5_devlink_max_sfs_set,
mlx5_devlink_max_sfs_validate),
DEVLINK_PARAM_DRIVER(MLX5_DEVLINK_PARAM_ID_CQE_COMPRESSION_TYPE,
"cqe_compress_type", DEVLINK_PARAM_TYPE_STRING,
BIT(DEVLINK_PARAM_CMODE_PERMANENT),