linux-stable/drivers/net/ethernet/sfc/ef100_rep.c
Alejandro Lucero fa78b01718 sfc: add support for devlink port_function_hw_addr_get in ef100
Using the builtin client handle id infrastructure, add support for
obtaining the mac address linked to mports in ef100. This implies
to execute an MCDI command for getting the data from the firmware
for each devlink port.

Signed-off-by: Alejandro Lucero <alejandro.lucero-palau@amd.com>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Acked-by: Martin Habets <habetsm.xilinx@gmail.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2023-02-16 12:03:13 +01:00

499 lines
13 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/****************************************************************************
* Driver for Solarflare network controllers and boards
* Copyright 2019 Solarflare Communications Inc.
* Copyright 2020-2022 Xilinx Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation, incorporated herein by reference.
*/
#include <linux/rhashtable.h>
#include "ef100_rep.h"
#include "ef100_netdev.h"
#include "ef100_nic.h"
#include "mae.h"
#include "rx_common.h"
#include "tc_bindings.h"
#include "efx_devlink.h"
#define EFX_EF100_REP_DRIVER "efx_ef100_rep"
#define EFX_REP_DEFAULT_PSEUDO_RING_SIZE 64
static int efx_ef100_rep_poll(struct napi_struct *napi, int weight);
static int efx_ef100_rep_init_struct(struct efx_nic *efx, struct efx_rep *efv,
unsigned int i)
{
efv->parent = efx;
efv->idx = i;
INIT_LIST_HEAD(&efv->list);
efv->dflt.fw_id = MC_CMD_MAE_ACTION_RULE_INSERT_OUT_ACTION_RULE_ID_NULL;
INIT_LIST_HEAD(&efv->dflt.acts.list);
INIT_LIST_HEAD(&efv->rx_list);
spin_lock_init(&efv->rx_lock);
efv->msg_enable = NETIF_MSG_DRV | NETIF_MSG_PROBE |
NETIF_MSG_LINK | NETIF_MSG_IFDOWN |
NETIF_MSG_IFUP | NETIF_MSG_RX_ERR |
NETIF_MSG_TX_ERR | NETIF_MSG_HW;
return 0;
}
static int efx_ef100_rep_open(struct net_device *net_dev)
{
struct efx_rep *efv = netdev_priv(net_dev);
netif_napi_add(net_dev, &efv->napi, efx_ef100_rep_poll);
napi_enable(&efv->napi);
return 0;
}
static int efx_ef100_rep_close(struct net_device *net_dev)
{
struct efx_rep *efv = netdev_priv(net_dev);
napi_disable(&efv->napi);
netif_napi_del(&efv->napi);
return 0;
}
static netdev_tx_t efx_ef100_rep_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct efx_rep *efv = netdev_priv(dev);
struct efx_nic *efx = efv->parent;
netdev_tx_t rc;
/* __ef100_hard_start_xmit() will always return success even in the
* case of TX drops, where it will increment efx's tx_dropped. The
* efv stats really only count attempted TX, not success/failure.
*/
atomic64_inc(&efv->stats.tx_packets);
atomic64_add(skb->len, &efv->stats.tx_bytes);
netif_tx_lock(efx->net_dev);
rc = __ef100_hard_start_xmit(skb, efx, dev, efv);
netif_tx_unlock(efx->net_dev);
return rc;
}
static int efx_ef100_rep_get_port_parent_id(struct net_device *dev,
struct netdev_phys_item_id *ppid)
{
struct efx_rep *efv = netdev_priv(dev);
struct efx_nic *efx = efv->parent;
struct ef100_nic_data *nic_data;
nic_data = efx->nic_data;
/* nic_data->port_id is a u8[] */
ppid->id_len = sizeof(nic_data->port_id);
memcpy(ppid->id, nic_data->port_id, sizeof(nic_data->port_id));
return 0;
}
static int efx_ef100_rep_get_phys_port_name(struct net_device *dev,
char *buf, size_t len)
{
struct efx_rep *efv = netdev_priv(dev);
struct efx_nic *efx = efv->parent;
struct ef100_nic_data *nic_data;
int ret;
nic_data = efx->nic_data;
ret = snprintf(buf, len, "p%upf%uvf%u", efx->port_num,
nic_data->pf_index, efv->idx);
if (ret >= len)
return -EOPNOTSUPP;
return 0;
}
static int efx_ef100_rep_setup_tc(struct net_device *net_dev,
enum tc_setup_type type, void *type_data)
{
struct efx_rep *efv = netdev_priv(net_dev);
struct efx_nic *efx = efv->parent;
if (type == TC_SETUP_CLSFLOWER)
return efx_tc_flower(efx, net_dev, type_data, efv);
if (type == TC_SETUP_BLOCK)
return efx_tc_setup_block(net_dev, efx, type_data, efv);
return -EOPNOTSUPP;
}
static void efx_ef100_rep_get_stats64(struct net_device *dev,
struct rtnl_link_stats64 *stats)
{
struct efx_rep *efv = netdev_priv(dev);
stats->rx_packets = atomic64_read(&efv->stats.rx_packets);
stats->tx_packets = atomic64_read(&efv->stats.tx_packets);
stats->rx_bytes = atomic64_read(&efv->stats.rx_bytes);
stats->tx_bytes = atomic64_read(&efv->stats.tx_bytes);
stats->rx_dropped = atomic64_read(&efv->stats.rx_dropped);
stats->tx_errors = atomic64_read(&efv->stats.tx_errors);
}
const struct net_device_ops efx_ef100_rep_netdev_ops = {
.ndo_open = efx_ef100_rep_open,
.ndo_stop = efx_ef100_rep_close,
.ndo_start_xmit = efx_ef100_rep_xmit,
.ndo_get_port_parent_id = efx_ef100_rep_get_port_parent_id,
.ndo_get_phys_port_name = efx_ef100_rep_get_phys_port_name,
.ndo_get_stats64 = efx_ef100_rep_get_stats64,
.ndo_setup_tc = efx_ef100_rep_setup_tc,
};
static void efx_ef100_rep_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *drvinfo)
{
strscpy(drvinfo->driver, EFX_EF100_REP_DRIVER, sizeof(drvinfo->driver));
}
static u32 efx_ef100_rep_ethtool_get_msglevel(struct net_device *net_dev)
{
struct efx_rep *efv = netdev_priv(net_dev);
return efv->msg_enable;
}
static void efx_ef100_rep_ethtool_set_msglevel(struct net_device *net_dev,
u32 msg_enable)
{
struct efx_rep *efv = netdev_priv(net_dev);
efv->msg_enable = msg_enable;
}
static void efx_ef100_rep_ethtool_get_ringparam(struct net_device *net_dev,
struct ethtool_ringparam *ring,
struct kernel_ethtool_ringparam *kring,
struct netlink_ext_ack *ext_ack)
{
struct efx_rep *efv = netdev_priv(net_dev);
ring->rx_max_pending = U32_MAX;
ring->rx_pending = efv->rx_pring_size;
}
static int efx_ef100_rep_ethtool_set_ringparam(struct net_device *net_dev,
struct ethtool_ringparam *ring,
struct kernel_ethtool_ringparam *kring,
struct netlink_ext_ack *ext_ack)
{
struct efx_rep *efv = netdev_priv(net_dev);
if (ring->rx_mini_pending || ring->rx_jumbo_pending || ring->tx_pending)
return -EINVAL;
efv->rx_pring_size = ring->rx_pending;
return 0;
}
static const struct ethtool_ops efx_ef100_rep_ethtool_ops = {
.get_drvinfo = efx_ef100_rep_get_drvinfo,
.get_msglevel = efx_ef100_rep_ethtool_get_msglevel,
.set_msglevel = efx_ef100_rep_ethtool_set_msglevel,
.get_ringparam = efx_ef100_rep_ethtool_get_ringparam,
.set_ringparam = efx_ef100_rep_ethtool_set_ringparam,
};
static struct efx_rep *efx_ef100_rep_create_netdev(struct efx_nic *efx,
unsigned int i)
{
struct net_device *net_dev;
struct efx_rep *efv;
int rc;
net_dev = alloc_etherdev_mq(sizeof(*efv), 1);
if (!net_dev)
return ERR_PTR(-ENOMEM);
efv = netdev_priv(net_dev);
rc = efx_ef100_rep_init_struct(efx, efv, i);
if (rc)
goto fail1;
efv->net_dev = net_dev;
rtnl_lock();
spin_lock_bh(&efx->vf_reps_lock);
list_add_tail(&efv->list, &efx->vf_reps);
spin_unlock_bh(&efx->vf_reps_lock);
if (netif_running(efx->net_dev) && efx->state == STATE_NET_UP) {
netif_device_attach(net_dev);
netif_carrier_on(net_dev);
} else {
netif_carrier_off(net_dev);
netif_tx_stop_all_queues(net_dev);
}
rtnl_unlock();
net_dev->netdev_ops = &efx_ef100_rep_netdev_ops;
net_dev->ethtool_ops = &efx_ef100_rep_ethtool_ops;
net_dev->min_mtu = EFX_MIN_MTU;
net_dev->max_mtu = EFX_MAX_MTU;
net_dev->features |= NETIF_F_LLTX;
net_dev->hw_features |= NETIF_F_LLTX;
return efv;
fail1:
free_netdev(net_dev);
return ERR_PTR(rc);
}
static int efx_ef100_configure_rep(struct efx_rep *efv)
{
struct efx_nic *efx = efv->parent;
int rc;
efv->rx_pring_size = EFX_REP_DEFAULT_PSEUDO_RING_SIZE;
/* Look up actual mport ID */
rc = efx_mae_lookup_mport(efx, efv->idx, &efv->mport);
if (rc)
return rc;
pci_dbg(efx->pci_dev, "VF %u has mport ID %#x\n", efv->idx, efv->mport);
/* mport label should fit in 16 bits */
WARN_ON(efv->mport >> 16);
return efx_tc_configure_default_rule_rep(efv);
}
static void efx_ef100_deconfigure_rep(struct efx_rep *efv)
{
struct efx_nic *efx = efv->parent;
efx_tc_deconfigure_default_rule(efx, &efv->dflt);
}
static void efx_ef100_rep_destroy_netdev(struct efx_rep *efv)
{
struct efx_nic *efx = efv->parent;
rtnl_lock();
spin_lock_bh(&efx->vf_reps_lock);
list_del(&efv->list);
spin_unlock_bh(&efx->vf_reps_lock);
rtnl_unlock();
synchronize_rcu();
free_netdev(efv->net_dev);
}
int efx_ef100_vfrep_create(struct efx_nic *efx, unsigned int i)
{
struct efx_rep *efv;
int rc;
efv = efx_ef100_rep_create_netdev(efx, i);
if (IS_ERR(efv)) {
rc = PTR_ERR(efv);
pci_err(efx->pci_dev,
"Failed to create representor for VF %d, rc %d\n", i,
rc);
return rc;
}
rc = efx_ef100_configure_rep(efv);
if (rc) {
pci_err(efx->pci_dev,
"Failed to configure representor for VF %d, rc %d\n",
i, rc);
goto fail1;
}
ef100_rep_set_devlink_port(efv);
rc = register_netdev(efv->net_dev);
if (rc) {
pci_err(efx->pci_dev,
"Failed to register representor for VF %d, rc %d\n",
i, rc);
goto fail2;
}
pci_dbg(efx->pci_dev, "Representor for VF %d is %s\n", i,
efv->net_dev->name);
return 0;
fail2:
ef100_rep_unset_devlink_port(efv);
efx_ef100_deconfigure_rep(efv);
fail1:
efx_ef100_rep_destroy_netdev(efv);
return rc;
}
void efx_ef100_vfrep_destroy(struct efx_nic *efx, struct efx_rep *efv)
{
struct net_device *rep_dev;
rep_dev = efv->net_dev;
if (!rep_dev)
return;
netif_dbg(efx, drv, rep_dev, "Removing VF representor\n");
unregister_netdev(rep_dev);
ef100_rep_unset_devlink_port(efv);
efx_ef100_deconfigure_rep(efv);
efx_ef100_rep_destroy_netdev(efv);
}
void efx_ef100_fini_vfreps(struct efx_nic *efx)
{
struct ef100_nic_data *nic_data = efx->nic_data;
struct efx_rep *efv, *next;
if (!nic_data->grp_mae)
return;
list_for_each_entry_safe(efv, next, &efx->vf_reps, list)
efx_ef100_vfrep_destroy(efx, efv);
}
static bool ef100_mport_is_pcie_vnic(struct mae_mport_desc *mport_desc)
{
return mport_desc->mport_type == MAE_MPORT_DESC_MPORT_TYPE_VNIC &&
mport_desc->vnic_client_type == MAE_MPORT_DESC_VNIC_CLIENT_TYPE_FUNCTION;
}
bool ef100_mport_on_local_intf(struct efx_nic *efx,
struct mae_mport_desc *mport_desc)
{
struct ef100_nic_data *nic_data = efx->nic_data;
bool pcie_func;
pcie_func = ef100_mport_is_pcie_vnic(mport_desc);
return nic_data->have_local_intf && pcie_func &&
mport_desc->interface_idx == nic_data->local_mae_intf;
}
bool ef100_mport_is_vf(struct mae_mport_desc *mport_desc)
{
bool pcie_func;
pcie_func = ef100_mport_is_pcie_vnic(mport_desc);
return pcie_func && (mport_desc->vf_idx != MAE_MPORT_DESC_VF_IDX_NULL);
}
void efx_ef100_init_reps(struct efx_nic *efx)
{
struct ef100_nic_data *nic_data = efx->nic_data;
int rc;
nic_data->have_local_intf = false;
rc = efx_mae_enumerate_mports(efx);
if (rc)
pci_warn(efx->pci_dev,
"Could not enumerate mports (rc=%d), are we admin?",
rc);
}
void efx_ef100_fini_reps(struct efx_nic *efx)
{
struct efx_mae *mae = efx->mae;
rhashtable_free_and_destroy(&mae->mports_ht, efx_mae_remove_mport,
NULL);
}
static int efx_ef100_rep_poll(struct napi_struct *napi, int weight)
{
struct efx_rep *efv = container_of(napi, struct efx_rep, napi);
unsigned int read_index;
struct list_head head;
struct sk_buff *skb;
bool need_resched;
int spent = 0;
INIT_LIST_HEAD(&head);
/* Grab up to 'weight' pending SKBs */
spin_lock_bh(&efv->rx_lock);
read_index = efv->write_index;
while (spent < weight && !list_empty(&efv->rx_list)) {
skb = list_first_entry(&efv->rx_list, struct sk_buff, list);
list_del(&skb->list);
list_add_tail(&skb->list, &head);
spent++;
}
spin_unlock_bh(&efv->rx_lock);
/* Receive them */
netif_receive_skb_list(&head);
if (spent < weight)
if (napi_complete_done(napi, spent)) {
spin_lock_bh(&efv->rx_lock);
efv->read_index = read_index;
/* If write_index advanced while we were doing the
* RX, then storing our read_index won't re-prime the
* fake-interrupt. In that case, we need to schedule
* NAPI again to consume the additional packet(s).
*/
need_resched = efv->write_index != read_index;
spin_unlock_bh(&efv->rx_lock);
if (need_resched)
napi_schedule(&efv->napi);
}
return spent;
}
void efx_ef100_rep_rx_packet(struct efx_rep *efv, struct efx_rx_buffer *rx_buf)
{
u8 *eh = efx_rx_buf_va(rx_buf);
struct sk_buff *skb;
bool primed;
/* Don't allow too many queued SKBs to build up, as they consume
* GFP_ATOMIC memory. If we overrun, just start dropping.
*/
if (efv->write_index - READ_ONCE(efv->read_index) > efv->rx_pring_size) {
atomic64_inc(&efv->stats.rx_dropped);
if (net_ratelimit())
netif_dbg(efv->parent, rx_err, efv->net_dev,
"nodesc-dropped packet of length %u\n",
rx_buf->len);
return;
}
skb = netdev_alloc_skb(efv->net_dev, rx_buf->len);
if (!skb) {
atomic64_inc(&efv->stats.rx_dropped);
if (net_ratelimit())
netif_dbg(efv->parent, rx_err, efv->net_dev,
"noskb-dropped packet of length %u\n",
rx_buf->len);
return;
}
memcpy(skb->data, eh, rx_buf->len);
__skb_put(skb, rx_buf->len);
skb_record_rx_queue(skb, 0); /* rep is single-queue */
/* Move past the ethernet header */
skb->protocol = eth_type_trans(skb, efv->net_dev);
skb_checksum_none_assert(skb);
atomic64_inc(&efv->stats.rx_packets);
atomic64_add(rx_buf->len, &efv->stats.rx_bytes);
/* Add it to the rx list */
spin_lock_bh(&efv->rx_lock);
primed = efv->read_index == efv->write_index;
list_add_tail(&skb->list, &efv->rx_list);
efv->write_index++;
spin_unlock_bh(&efv->rx_lock);
/* Trigger rx work */
if (primed)
napi_schedule(&efv->napi);
}
struct efx_rep *efx_ef100_find_rep_by_mport(struct efx_nic *efx, u16 mport)
{
struct efx_rep *efv, *out = NULL;
/* spinlock guards against list mutation while we're walking it;
* but caller must also hold rcu_read_lock() to ensure the netdev
* isn't freed after we drop the spinlock.
*/
spin_lock_bh(&efx->vf_reps_lock);
list_for_each_entry(efv, &efx->vf_reps, list)
if (efv->mport == mport) {
out = efv;
break;
}
spin_unlock_bh(&efx->vf_reps_lock);
return out;
}