Add SR-IOV guest support to the mlx5en driver.

This patch adds the missing pieces needed for device setup using the
mlx5en driver inside a virtual machine which is providing hardware
access through SR-IOV.

Sponsored by:	Mellanox Technologies
MFC after:	1 week
This commit is contained in:
Hans Petter Selasky 2016-06-07 13:58:52 +00:00
parent d8571d3ea3
commit 76a5241f2c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=301545
5 changed files with 526 additions and 0 deletions

View File

@ -33,6 +33,7 @@
#include <linux/pci.h>
#include <linux/cache.h>
#include <linux/rbtree.h>
#include <linux/if_ether.h>
#include <linux/semaphore.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>

View File

@ -471,6 +471,241 @@ int mlx5_set_nic_vport_promisc(struct mlx5_core_dev *mdev, int vport,
return mlx5_modify_nic_vport_context(mdev, in, sizeof(in));
}
EXPORT_SYMBOL_GPL(mlx5_set_nic_vport_promisc);
int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev,
u32 vport,
enum mlx5_list_type list_type,
u8 addr_list[][ETH_ALEN],
int *list_size)
{
u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)];
void *nic_vport_ctx;
int max_list_size;
int req_list_size;
u8 *mac_addr;
int out_sz;
void *out;
int err;
int i;
req_list_size = *list_size;
max_list_size = (list_type == MLX5_NIC_VPORT_LIST_TYPE_UC) ?
1 << MLX5_CAP_GEN_MAX(dev, log_max_current_uc_list) :
1 << MLX5_CAP_GEN_MAX(dev, log_max_current_mc_list);
if (req_list_size > max_list_size) {
mlx5_core_warn(dev, "Requested list size (%d) > (%d) max_list_size\n",
req_list_size, max_list_size);
req_list_size = max_list_size;
}
out_sz = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in) +
req_list_size * MLX5_ST_SZ_BYTES(mac_address_layout);
memset(in, 0, sizeof(in));
out = kzalloc(out_sz, GFP_KERNEL);
if (!out)
return -ENOMEM;
MLX5_SET(query_nic_vport_context_in, in, opcode,
MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT);
MLX5_SET(query_nic_vport_context_in, in, allowed_list_type, list_type);
MLX5_SET(query_nic_vport_context_in, in, vport_number, vport);
if (vport)
MLX5_SET(query_nic_vport_context_in, in, other_vport, 1);
err = mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, out_sz);
if (err)
goto out;
nic_vport_ctx = MLX5_ADDR_OF(query_nic_vport_context_out, out,
nic_vport_context);
req_list_size = MLX5_GET(nic_vport_context, nic_vport_ctx,
allowed_list_size);
*list_size = req_list_size;
for (i = 0; i < req_list_size; i++) {
mac_addr = MLX5_ADDR_OF(nic_vport_context,
nic_vport_ctx,
current_uc_mac_address[i]) + 2;
ether_addr_copy(addr_list[i], mac_addr);
}
out:
kfree(out);
return err;
}
EXPORT_SYMBOL_GPL(mlx5_query_nic_vport_mac_list);
int mlx5_modify_nic_vport_mac_list(struct mlx5_core_dev *dev,
enum mlx5_list_type list_type,
u8 addr_list[][ETH_ALEN],
int list_size)
{
u32 out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)];
void *nic_vport_ctx;
int max_list_size;
int in_sz;
void *in;
int err;
int i;
max_list_size = list_type == MLX5_NIC_VPORT_LIST_TYPE_UC ?
1 << MLX5_CAP_GEN(dev, log_max_current_uc_list) :
1 << MLX5_CAP_GEN(dev, log_max_current_mc_list);
if (list_size > max_list_size)
return -ENOSPC;
in_sz = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in) +
list_size * MLX5_ST_SZ_BYTES(mac_address_layout);
memset(out, 0, sizeof(out));
in = kzalloc(in_sz, GFP_KERNEL);
if (!in)
return -ENOMEM;
MLX5_SET(modify_nic_vport_context_in, in, opcode,
MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
MLX5_SET(modify_nic_vport_context_in, in,
field_select.addresses_list, 1);
nic_vport_ctx = MLX5_ADDR_OF(modify_nic_vport_context_in, in,
nic_vport_context);
MLX5_SET(nic_vport_context, nic_vport_ctx,
allowed_list_type, list_type);
MLX5_SET(nic_vport_context, nic_vport_ctx,
allowed_list_size, list_size);
for (i = 0; i < list_size; i++) {
u8 *curr_mac = MLX5_ADDR_OF(nic_vport_context,
nic_vport_ctx,
current_uc_mac_address[i]) + 2;
ether_addr_copy(curr_mac, addr_list[i]);
}
err = mlx5_cmd_exec_check_status(dev, in, in_sz, out, sizeof(out));
kfree(in);
return err;
}
EXPORT_SYMBOL_GPL(mlx5_modify_nic_vport_mac_list);
int mlx5_query_nic_vport_vlan_list(struct mlx5_core_dev *dev,
u32 vport,
u16 *vlan_list,
int *list_size)
{
u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)];
void *nic_vport_ctx;
int max_list_size;
int req_list_size;
int out_sz;
void *out;
void *vlan_addr;
int err;
int i;
req_list_size = *list_size;
max_list_size = 1 << MLX5_CAP_GEN_MAX(dev, log_max_vlan_list);
if (req_list_size > max_list_size) {
mlx5_core_warn(dev, "Requested list size (%d) > (%d) max_list_size\n",
req_list_size, max_list_size);
req_list_size = max_list_size;
}
out_sz = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in) +
req_list_size * MLX5_ST_SZ_BYTES(vlan_layout);
memset(in, 0, sizeof(in));
out = kzalloc(out_sz, GFP_KERNEL);
if (!out)
return -ENOMEM;
MLX5_SET(query_nic_vport_context_in, in, opcode,
MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT);
MLX5_SET(query_nic_vport_context_in, in, allowed_list_type,
MLX5_NIC_VPORT_CONTEXT_ALLOWED_LIST_TYPE_VLAN_LIST);
MLX5_SET(query_nic_vport_context_in, in, vport_number, vport);
if (vport)
MLX5_SET(query_nic_vport_context_in, in, other_vport, 1);
err = mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, out_sz);
if (err)
goto out;
nic_vport_ctx = MLX5_ADDR_OF(query_nic_vport_context_out, out,
nic_vport_context);
req_list_size = MLX5_GET(nic_vport_context, nic_vport_ctx,
allowed_list_size);
*list_size = req_list_size;
for (i = 0; i < req_list_size; i++) {
vlan_addr = MLX5_ADDR_OF(nic_vport_context, nic_vport_ctx,
current_uc_mac_address[i]);
vlan_list[i] = MLX5_GET(vlan_layout, vlan_addr, vlan);
}
out:
kfree(out);
return err;
}
EXPORT_SYMBOL_GPL(mlx5_query_nic_vport_vlan_list);
int mlx5_modify_nic_vport_vlans(struct mlx5_core_dev *dev,
u16 vlans[],
int list_size)
{
u32 out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)];
void *nic_vport_ctx;
int max_list_size;
int in_sz;
void *in;
int err;
int i;
max_list_size = 1 << MLX5_CAP_GEN(dev, log_max_vlan_list);
if (list_size > max_list_size)
return -ENOSPC;
in_sz = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in) +
list_size * MLX5_ST_SZ_BYTES(vlan_layout);
memset(out, 0, sizeof(out));
in = kzalloc(in_sz, GFP_KERNEL);
if (!in)
return -ENOMEM;
MLX5_SET(modify_nic_vport_context_in, in, opcode,
MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
MLX5_SET(modify_nic_vport_context_in, in,
field_select.addresses_list, 1);
nic_vport_ctx = MLX5_ADDR_OF(modify_nic_vport_context_in, in,
nic_vport_context);
MLX5_SET(nic_vport_context, nic_vport_ctx,
allowed_list_type, MLX5_NIC_VPORT_LIST_TYPE_VLAN);
MLX5_SET(nic_vport_context, nic_vport_ctx,
allowed_list_size, list_size);
for (i = 0; i < list_size; i++) {
void *vlan_addr = MLX5_ADDR_OF(nic_vport_context,
nic_vport_ctx,
current_uc_mac_address[i]);
MLX5_SET(vlan_layout, vlan_addr, vlan, vlans[i]);
}
err = mlx5_cmd_exec_check_status(dev, in, in_sz, out, sizeof(out));
kfree(in);
return err;
}
EXPORT_SYMBOL_GPL(mlx5_modify_nic_vport_vlans);
int mlx5_set_nic_vport_permanent_mac(struct mlx5_core_dev *mdev, int vport,
u8 *addr)
{
@ -785,6 +1020,129 @@ int mlx5_set_eswitch_cvlan_info(struct mlx5_core_dev *mdev, u8 vport,
}
EXPORT_SYMBOL_GPL(mlx5_set_eswitch_cvlan_info);
int mlx5_arm_vport_context_events(struct mlx5_core_dev *mdev,
u8 vport,
u32 events_mask)
{
u32 *in;
u32 inlen = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in);
void *nic_vport_ctx;
int err;
in = mlx5_vzalloc(inlen);
if (!in)
return -ENOMEM;
MLX5_SET(modify_nic_vport_context_in,
in,
opcode,
MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
MLX5_SET(modify_nic_vport_context_in,
in,
field_select.change_event,
1);
MLX5_SET(modify_nic_vport_context_in, in, vport_number, vport);
if (vport)
MLX5_SET(modify_nic_vport_context_in, in, other_vport, 1);
nic_vport_ctx = MLX5_ADDR_OF(modify_nic_vport_context_in,
in,
nic_vport_context);
MLX5_SET(nic_vport_context, nic_vport_ctx, arm_change_event, 1);
if (events_mask & MLX5_UC_ADDR_CHANGE)
MLX5_SET(nic_vport_context,
nic_vport_ctx,
event_on_uc_address_change,
1);
if (events_mask & MLX5_MC_ADDR_CHANGE)
MLX5_SET(nic_vport_context,
nic_vport_ctx,
event_on_mc_address_change,
1);
if (events_mask & MLX5_VLAN_CHANGE)
MLX5_SET(nic_vport_context,
nic_vport_ctx,
event_on_vlan_change,
1);
if (events_mask & MLX5_PROMISC_CHANGE)
MLX5_SET(nic_vport_context,
nic_vport_ctx,
event_on_promisc_change,
1);
if (events_mask & MLX5_MTU_CHANGE)
MLX5_SET(nic_vport_context,
nic_vport_ctx,
event_on_mtu,
1);
err = mlx5_modify_nic_vport_context(mdev, in, inlen);
kvfree(in);
return err;
}
EXPORT_SYMBOL_GPL(mlx5_arm_vport_context_events);
int mlx5_query_vport_promisc(struct mlx5_core_dev *mdev,
u32 vport,
u8 *promisc_uc,
u8 *promisc_mc,
u8 *promisc_all)
{
u32 *out;
int outlen = MLX5_ST_SZ_BYTES(query_nic_vport_context_out);
int err;
out = kzalloc(outlen, GFP_KERNEL);
if (!out)
return -ENOMEM;
err = mlx5_query_nic_vport_context(mdev, vport, out, outlen);
if (err)
goto out;
*promisc_uc = MLX5_GET(query_nic_vport_context_out, out,
nic_vport_context.promisc_uc);
*promisc_mc = MLX5_GET(query_nic_vport_context_out, out,
nic_vport_context.promisc_mc);
*promisc_all = MLX5_GET(query_nic_vport_context_out, out,
nic_vport_context.promisc_all);
out:
kfree(out);
return err;
}
EXPORT_SYMBOL_GPL(mlx5_query_nic_vport_promisc);
int mlx5_modify_nic_vport_promisc(struct mlx5_core_dev *mdev,
int promisc_uc,
int promisc_mc,
int promisc_all)
{
void *in;
int inlen = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in);
int err;
in = mlx5_vzalloc(inlen);
if (!in) {
mlx5_core_err(mdev, "failed to allocate inbox\n");
return -ENOMEM;
}
MLX5_SET(modify_nic_vport_context_in, in, field_select.promisc, 1);
MLX5_SET(modify_nic_vport_context_in, in,
nic_vport_context.promisc_uc, promisc_uc);
MLX5_SET(modify_nic_vport_context_in, in,
nic_vport_context.promisc_mc, promisc_mc);
MLX5_SET(modify_nic_vport_context_in, in,
nic_vport_context.promisc_all, promisc_all);
err = mlx5_modify_nic_vport_context(mdev, in, inlen);
kvfree(in);
return err;
}
EXPORT_SYMBOL_GPL(mlx5_modify_nic_vport_promisc);
int mlx5_query_vport_counter(struct mlx5_core_dev *dev,
u8 port_num, u16 vport_num,
void *out, int out_size)

View File

@ -390,6 +390,49 @@ mlx5e_add_eth_addr_rule(struct mlx5e_priv *priv,
return (err);
}
static int mlx5e_vport_context_update_vlans(struct mlx5e_priv *priv)
{
struct ifnet *ifp = priv->ifp;
int max_list_size;
int list_size;
u16 *vlans;
int vlan;
int err;
int i;
list_size = 0;
for_each_set_bit(vlan, priv->vlan.active_vlans, VLAN_N_VID)
list_size++;
max_list_size = 1 << MLX5_CAP_GEN(priv->mdev, log_max_vlan_list);
if (list_size > max_list_size) {
if_printf(ifp,
"ifnet vlans list size (%d) > (%d) max vport list size, some vlans will be dropped\n",
list_size, max_list_size);
list_size = max_list_size;
}
vlans = kcalloc(list_size, sizeof(*vlans), GFP_KERNEL);
if (!vlans)
return -ENOMEM;
i = 0;
for_each_set_bit(vlan, priv->vlan.active_vlans, VLAN_N_VID) {
if (i >= list_size)
break;
vlans[i++] = vlan;
}
err = mlx5_modify_nic_vport_vlans(priv->mdev, vlans, list_size);
if (err)
if_printf(ifp, "Failed to modify vport vlans list err(%d)\n",
err);
kfree(vlans);
return err;
}
enum mlx5e_vlan_rule_type {
MLX5E_VLAN_RULE_TYPE_UNTAGGED,
MLX5E_VLAN_RULE_TYPE_ANY_VID,
@ -448,6 +491,7 @@ mlx5e_add_vlan_rule(struct mlx5e_priv *priv,
outer_headers.first_vid);
MLX5_SET(fte_match_param, match_value, outer_headers.first_vid,
vid);
mlx5e_vport_context_update_vlans(priv);
break;
}
@ -478,6 +522,7 @@ mlx5e_del_vlan_rule(struct mlx5e_priv *priv,
case MLX5E_VLAN_RULE_TYPE_MATCH_VID:
mlx5_del_flow_table_entry(priv->ft.vlan,
priv->vlan.active_vlans_ft_ix[vid]);
mlx5e_vport_context_update_vlans(priv);
break;
}
}
@ -628,6 +673,91 @@ mlx5e_sync_ifp_addr(struct mlx5e_priv *priv)
if_maddr_runlock(ifp);
}
static void mlx5e_fill_addr_array(struct mlx5e_priv *priv, int list_type,
u8 addr_array[][ETH_ALEN], int size)
{
bool is_uc = (list_type == MLX5_NIC_VPORT_LIST_TYPE_UC);
struct ifnet *ifp = priv->ifp;
struct mlx5e_eth_addr_hash_node *hn;
struct mlx5e_eth_addr_hash_head *addr_list;
struct mlx5e_eth_addr_hash_node *tmp;
int i = 0;
int hi;
addr_list = is_uc ? priv->eth_addr.if_uc : priv->eth_addr.if_mc;
if (is_uc) /* Make sure our own address is pushed first */
ether_addr_copy(addr_array[i++], IF_LLADDR(ifp));
else if (priv->eth_addr.broadcast_enabled)
ether_addr_copy(addr_array[i++], ifp->if_broadcastaddr);
mlx5e_for_each_hash_node(hn, tmp, addr_list, hi) {
if (ether_addr_equal(IF_LLADDR(ifp), hn->ai.addr))
continue;
if (i >= size)
break;
ether_addr_copy(addr_array[i++], hn->ai.addr);
}
}
static void mlx5e_vport_context_update_addr_list(struct mlx5e_priv *priv,
int list_type)
{
bool is_uc = (list_type == MLX5_NIC_VPORT_LIST_TYPE_UC);
struct mlx5e_eth_addr_hash_node *hn;
u8 (*addr_array)[ETH_ALEN] = NULL;
struct mlx5e_eth_addr_hash_head *addr_list;
struct mlx5e_eth_addr_hash_node *tmp;
int max_size;
int size;
int err;
int hi;
size = is_uc ? 0 : (priv->eth_addr.broadcast_enabled ? 1 : 0);
max_size = is_uc ?
1 << MLX5_CAP_GEN(priv->mdev, log_max_current_uc_list) :
1 << MLX5_CAP_GEN(priv->mdev, log_max_current_mc_list);
addr_list = is_uc ? priv->eth_addr.if_uc : priv->eth_addr.if_mc;
mlx5e_for_each_hash_node(hn, tmp, addr_list, hi)
size++;
if (size > max_size) {
if_printf(priv->ifp,
"ifp %s list size (%d) > (%d) max vport list size, some addresses will be dropped\n",
is_uc ? "UC" : "MC", size, max_size);
size = max_size;
}
if (size) {
addr_array = kcalloc(size, ETH_ALEN, GFP_KERNEL);
if (!addr_array) {
err = -ENOMEM;
goto out;
}
mlx5e_fill_addr_array(priv, list_type, addr_array, size);
}
err = mlx5_modify_nic_vport_mac_list(priv->mdev, list_type, addr_array, size);
out:
if (err)
if_printf(priv->ifp,
"Failed to modify vport %s list err(%d)\n",
is_uc ? "UC" : "MC", err);
kfree(addr_array);
}
static void mlx5e_vport_context_update(struct mlx5e_priv *priv)
{
struct mlx5e_eth_addr_db *ea = &priv->eth_addr;
mlx5e_vport_context_update_addr_list(priv, MLX5_NIC_VPORT_LIST_TYPE_UC);
mlx5e_vport_context_update_addr_list(priv, MLX5_NIC_VPORT_LIST_TYPE_MC);
mlx5_modify_nic_vport_promisc(priv->mdev, 0,
ea->allmulti_enabled,
ea->promisc_enabled);
}
static void
mlx5e_apply_ifp_addr(struct mlx5e_priv *priv)
{
@ -701,6 +831,8 @@ mlx5e_set_rx_mode_core(struct mlx5e_priv *priv)
ea->promisc_enabled = promisc_enabled;
ea->allmulti_enabled = allmulti_enabled;
ea->broadcast_enabled = broadcast_enabled;
mlx5e_vport_context_update(priv);
}
void

View File

@ -3001,6 +3001,13 @@ mlx5e_create_ifp(struct mlx5_core_dev *mdev)
}
mlx5_query_nic_vport_mac_address(priv->mdev, 0, dev_addr);
/* check if we should generate a random MAC address */
if (MLX5_CAP_GEN(priv->mdev, vport_group_manager) == 0 &&
is_zero_ether_addr(dev_addr)) {
random_ether_addr(dev_addr);
if_printf(ifp, "Assigned random MAC address\n");
}
/* set default MTU */
mlx5e_set_dev_port_mtu(ifp, ifp->if_mtu);

View File

@ -38,6 +38,18 @@ int mlx5_vport_query_out_of_rx_buffer(struct mlx5_core_dev *mdev,
u32 *out_of_rx_buffer);
u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod);
int mlx5_arm_vport_context_events(struct mlx5_core_dev *mdev,
u8 vport,
u32 events_mask);
int mlx5_query_vport_promisc(struct mlx5_core_dev *mdev,
u32 vport,
u8 *promisc_uc,
u8 *promisc_mc,
u8 *promisc_all);
int mlx5_modify_nic_vport_promisc(struct mlx5_core_dev *mdev,
int promisc_uc,
int promisc_mc,
int promisc_all);
int mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev,
u32 vport, u8 *addr);
int mlx5_set_nic_vport_current_mac(struct mlx5_core_dev *mdev, int vport,
@ -49,6 +61,22 @@ int mlx5_set_nic_vport_mc_list(struct mlx5_core_dev *mdev, int vport,
int mlx5_set_nic_vport_promisc(struct mlx5_core_dev *mdev, int vport,
bool promisc_mc, bool promisc_uc,
bool promisc_all);
int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev,
u32 vport,
enum mlx5_list_type list_type,
u8 addr_list[][ETH_ALEN],
int *list_size);
int mlx5_modify_nic_vport_mac_list(struct mlx5_core_dev *dev,
enum mlx5_list_type list_type,
u8 addr_list[][ETH_ALEN],
int list_size);
int mlx5_query_nic_vport_vlan_list(struct mlx5_core_dev *dev,
u32 vport,
u16 *vlan_list,
int *list_size);
int mlx5_modify_nic_vport_vlans(struct mlx5_core_dev *dev,
u16 vlans[],
int list_size);
int mlx5_set_nic_vport_permanent_mac(struct mlx5_core_dev *mdev, int vport,
u8 *addr);
int mlx5_nic_vport_enable_roce(struct mlx5_core_dev *mdev);