diff options
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/ethernet/sfc/Kconfig | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/Makefile | 3 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/ef100_netdev.c | 30 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/ef100_nic.c | 93 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/ef100_nic.h | 7 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/ef100_rep.c | 57 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/ef100_rep.h | 10 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/efx_devlink.c | 731 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/efx_devlink.h | 47 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/mae.c | 218 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/mae.h | 40 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/mcdi.c | 72 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/mcdi.h | 8 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/net_driver.h | 8 |
14 files changed, 1300 insertions, 25 deletions
diff --git a/drivers/net/ethernet/sfc/Kconfig b/drivers/net/ethernet/sfc/Kconfig index 0950e6b0508f..4af36ba8906b 100644 --- a/drivers/net/ethernet/sfc/Kconfig +++ b/drivers/net/ethernet/sfc/Kconfig @@ -22,6 +22,7 @@ config SFC depends on PTP_1588_CLOCK_OPTIONAL select MDIO select CRC32 + select NET_DEVLINK help This driver supports 10/40-gigabit Ethernet cards based on the Solarflare SFC9100-family controllers. diff --git a/drivers/net/ethernet/sfc/Makefile b/drivers/net/ethernet/sfc/Makefile index 712a48d00069..55b9c73cd8ef 100644 --- a/drivers/net/ethernet/sfc/Makefile +++ b/drivers/net/ethernet/sfc/Makefile @@ -6,7 +6,8 @@ sfc-y += efx.o efx_common.o efx_channels.o nic.o \ mcdi.o mcdi_port.o mcdi_port_common.o \ mcdi_functions.o mcdi_filters.o mcdi_mon.o \ ef100.o ef100_nic.o ef100_netdev.o \ - ef100_ethtool.o ef100_rx.o ef100_tx.o + ef100_ethtool.o ef100_rx.o ef100_tx.o \ + efx_devlink.o sfc-$(CONFIG_SFC_MTD) += mtd.o sfc-$(CONFIG_SFC_SRIOV) += sriov.o ef10_sriov.o ef100_sriov.o ef100_rep.o \ mae.o tc.o tc_bindings.o tc_counters.o diff --git a/drivers/net/ethernet/sfc/ef100_netdev.c b/drivers/net/ethernet/sfc/ef100_netdev.c index ddcc325ed570..d916877b5a9a 100644 --- a/drivers/net/ethernet/sfc/ef100_netdev.c +++ b/drivers/net/ethernet/sfc/ef100_netdev.c @@ -24,6 +24,7 @@ #include "rx_common.h" #include "ef100_sriov.h" #include "tc_bindings.h" +#include "efx_devlink.h" static void ef100_update_name(struct efx_nic *efx) { @@ -332,9 +333,11 @@ void ef100_remove_netdev(struct efx_probe_data *probe_data) efx_ef100_pci_sriov_disable(efx, true); #endif + efx_fini_devlink_lock(efx); ef100_unregister_netdev(efx); #ifdef CONFIG_SFC_SRIOV + ef100_pf_unset_devlink_port(efx); efx_fini_tc(efx); #endif @@ -345,6 +348,8 @@ void ef100_remove_netdev(struct efx_probe_data *probe_data) kfree(efx->phy_data); efx->phy_data = NULL; + efx_fini_devlink_and_unlock(efx); + free_netdev(efx->net_dev); efx->net_dev = NULL; efx->state = STATE_PROBED; @@ -354,6 +359,7 @@ int ef100_probe_netdev(struct efx_probe_data *probe_data) { struct efx_nic *efx = &probe_data->efx; struct efx_probe_data **probe_ptr; + struct ef100_nic_data *nic_data; struct net_device *net_dev; int rc; @@ -405,6 +411,20 @@ int ef100_probe_netdev(struct efx_probe_data *probe_data) /* Don't fail init if RSS setup doesn't work. */ efx_mcdi_push_default_indir_table(efx, efx->n_rx_channels); + nic_data = efx->nic_data; + rc = ef100_get_mac_address(efx, net_dev->perm_addr, CLIENT_HANDLE_SELF, + efx->type->is_vf); + if (rc) + return rc; + /* Assign MAC address */ + eth_hw_addr_set(net_dev, net_dev->perm_addr); + ether_addr_copy(nic_data->port_id, net_dev->perm_addr); + + /* devlink creation, registration and lock */ + rc = efx_probe_devlink_and_lock(efx); + if (rc) + pci_info(efx->pci_dev, "devlink registration failed"); + rc = ef100_register_netdev(efx); if (rc) goto fail; @@ -413,6 +433,9 @@ int ef100_probe_netdev(struct efx_probe_data *probe_data) rc = ef100_probe_netdev_pf(efx); if (rc) goto fail; +#ifdef CONFIG_SFC_SRIOV + ef100_pf_set_devlink_port(efx); +#endif } efx->netdev_notifier.notifier_call = ef100_netdev_event; @@ -423,6 +446,13 @@ int ef100_probe_netdev(struct efx_probe_data *probe_data) goto fail; } + efx_probe_devlink_unlock(efx); + return rc; fail: +#ifdef CONFIG_SFC_SRIOV + /* remove devlink port if does exist */ + ef100_pf_unset_devlink_port(efx); +#endif + efx_probe_devlink_unlock(efx); return rc; } diff --git a/drivers/net/ethernet/sfc/ef100_nic.c b/drivers/net/ethernet/sfc/ef100_nic.c index ad686c671ab8..becd21c2325d 100644 --- a/drivers/net/ethernet/sfc/ef100_nic.c +++ b/drivers/net/ethernet/sfc/ef100_nic.c @@ -130,23 +130,34 @@ static void ef100_mcdi_reboot_detected(struct efx_nic *efx) /* MCDI calls */ -static int ef100_get_mac_address(struct efx_nic *efx, u8 *mac_address) +int ef100_get_mac_address(struct efx_nic *efx, u8 *mac_address, + int client_handle, bool empty_ok) { - MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_MAC_ADDRESSES_OUT_LEN); + MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_CLIENT_MAC_ADDRESSES_OUT_LEN(1)); + MCDI_DECLARE_BUF(inbuf, MC_CMD_GET_CLIENT_MAC_ADDRESSES_IN_LEN); size_t outlen; int rc; BUILD_BUG_ON(MC_CMD_GET_MAC_ADDRESSES_IN_LEN != 0); + MCDI_SET_DWORD(inbuf, GET_CLIENT_MAC_ADDRESSES_IN_CLIENT_HANDLE, + client_handle); - rc = efx_mcdi_rpc(efx, MC_CMD_GET_MAC_ADDRESSES, NULL, 0, - outbuf, sizeof(outbuf), &outlen); + rc = efx_mcdi_rpc(efx, MC_CMD_GET_CLIENT_MAC_ADDRESSES, inbuf, + sizeof(inbuf), outbuf, sizeof(outbuf), &outlen); if (rc) return rc; - if (outlen < MC_CMD_GET_MAC_ADDRESSES_OUT_LEN) - return -EIO; - ether_addr_copy(mac_address, - MCDI_PTR(outbuf, GET_MAC_ADDRESSES_OUT_MAC_ADDR_BASE)); + if (outlen >= MC_CMD_GET_CLIENT_MAC_ADDRESSES_OUT_LEN(1)) { + ether_addr_copy(mac_address, + MCDI_PTR(outbuf, GET_CLIENT_MAC_ADDRESSES_OUT_MAC_ADDRS)); + } else if (empty_ok) { + pci_warn(efx->pci_dev, + "No MAC address provisioned for client ID %#x.\n", + client_handle); + eth_zero_addr(mac_address); + } else { + return -ENOENT; + } return 0; } @@ -736,7 +747,7 @@ static int efx_ef100_get_base_mport(struct efx_nic *efx) /* Construct mport selector for "physical network port" */ efx_mae_mport_wire(efx, &selector); /* Look up actual mport ID */ - rc = efx_mae_lookup_mport(efx, selector, &id); + rc = efx_mae_fw_lookup_mport(efx, selector, &id); if (rc) return rc; /* The ID should always fit in 16 bits, because that's how wide the @@ -747,6 +758,19 @@ static int efx_ef100_get_base_mport(struct efx_nic *efx) id); nic_data->base_mport = id; nic_data->have_mport = true; + + /* Construct mport selector for "calling PF" */ + efx_mae_mport_uplink(efx, &selector); + /* Look up actual mport ID */ + rc = efx_mae_fw_lookup_mport(efx, selector, &id); + if (rc) + return rc; + if (id >> 16) + netif_warn(efx, probe, efx->net_dev, "Bad own m-port id %#x\n", + id); + nic_data->own_mport = id; + nic_data->have_own_mport = true; + return 0; } #endif @@ -1098,19 +1122,39 @@ fail: return rc; } +/* MCDI commands are related to the same device issuing them. This function + * allows to do an MCDI command on behalf of another device, mainly PFs setting + * things for VFs. + */ +int efx_ef100_lookup_client_id(struct efx_nic *efx, efx_qword_t pciefn, u32 *id) +{ + MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_CLIENT_HANDLE_OUT_LEN); + MCDI_DECLARE_BUF(inbuf, MC_CMD_GET_CLIENT_HANDLE_IN_LEN); + u64 pciefn_flat = le64_to_cpu(pciefn.u64[0]); + size_t outlen; + int rc; + + MCDI_SET_DWORD(inbuf, GET_CLIENT_HANDLE_IN_TYPE, + MC_CMD_GET_CLIENT_HANDLE_IN_TYPE_FUNC); + MCDI_SET_QWORD(inbuf, GET_CLIENT_HANDLE_IN_FUNC, + pciefn_flat); + + rc = efx_mcdi_rpc(efx, MC_CMD_GET_CLIENT_HANDLE, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + return rc; + if (outlen < sizeof(outbuf)) + return -EIO; + *id = MCDI_DWORD(outbuf, GET_CLIENT_HANDLE_OUT_HANDLE); + return 0; +} + int ef100_probe_netdev_pf(struct efx_nic *efx) { struct ef100_nic_data *nic_data = efx->nic_data; struct net_device *net_dev = efx->net_dev; int rc; - rc = ef100_get_mac_address(efx, net_dev->perm_addr); - if (rc) - goto fail; - /* Assign MAC address */ - eth_hw_addr_set(net_dev, net_dev->perm_addr); - memcpy(nic_data->port_id, net_dev->perm_addr, ETH_ALEN); - if (!nic_data->grp_mae) return 0; @@ -1126,6 +1170,14 @@ int ef100_probe_netdev_pf(struct efx_nic *efx) rc); } + rc = efx_init_mae(efx); + if (rc) + netif_warn(efx, probe, net_dev, + "Failed to init MAE rc %d; representors will not function\n", + rc); + else + efx_ef100_init_reps(efx); + rc = efx_init_tc(efx); if (rc) { /* Either we don't have an MAE at all (i.e. legacy v-switching), @@ -1142,9 +1194,6 @@ int ef100_probe_netdev_pf(struct efx_nic *efx) efx->fixed_features |= NETIF_F_HW_TC; } #endif - return 0; - -fail: return rc; } @@ -1157,6 +1206,12 @@ void ef100_remove(struct efx_nic *efx) { struct ef100_nic_data *nic_data = efx->nic_data; +#ifdef CONFIG_SFC_SRIOV + if (efx->mae) { + efx_ef100_fini_reps(efx); + efx_fini_mae(efx); + } +#endif efx_mcdi_detach(efx); efx_mcdi_fini(efx); if (nic_data) diff --git a/drivers/net/ethernet/sfc/ef100_nic.h b/drivers/net/ethernet/sfc/ef100_nic.h index 0295933145fa..f1ed481c1260 100644 --- a/drivers/net/ethernet/sfc/ef100_nic.h +++ b/drivers/net/ethernet/sfc/ef100_nic.h @@ -74,6 +74,10 @@ struct ef100_nic_data { u64 stats[EF100_STAT_COUNT]; u32 base_mport; bool have_mport; /* base_mport was populated successfully */ + u32 own_mport; + u32 local_mae_intf; /* interface_idx that corresponds to us, in mport enumerate */ + bool have_own_mport; /* own_mport was populated successfully */ + bool have_local_intf; /* local_mae_intf was populated successfully */ bool grp_mae; /* MAE Privilege */ u16 tso_max_hdr_len; u16 tso_max_payload_num_segs; @@ -88,4 +92,7 @@ int efx_ef100_init_datapath_caps(struct efx_nic *efx); int ef100_phy_probe(struct efx_nic *efx); int ef100_filter_table_probe(struct efx_nic *efx); +int ef100_get_mac_address(struct efx_nic *efx, u8 *mac_address, + int client_handle, bool empty_ok); +int efx_ef100_lookup_client_id(struct efx_nic *efx, efx_qword_t pciefn, u32 *id); #endif /* EFX_EF100_NIC_H */ diff --git a/drivers/net/ethernet/sfc/ef100_rep.c b/drivers/net/ethernet/sfc/ef100_rep.c index 81ab22c74635..0b3083ef0ead 100644 --- a/drivers/net/ethernet/sfc/ef100_rep.c +++ b/drivers/net/ethernet/sfc/ef100_rep.c @@ -9,12 +9,14 @@ * 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" @@ -242,14 +244,11 @@ fail1: static int efx_ef100_configure_rep(struct efx_rep *efv) { struct efx_nic *efx = efv->parent; - u32 selector; int rc; efv->rx_pring_size = EFX_REP_DEFAULT_PSEUDO_RING_SIZE; - /* Construct mport selector for corresponding VF */ - efx_mae_mport_vf(efx, efv->idx, &selector); /* Look up actual mport ID */ - rc = efx_mae_lookup_mport(efx, selector, &efv->mport); + 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); @@ -299,6 +298,7 @@ int efx_ef100_vfrep_create(struct efx_nic *efx, unsigned int i) i, rc); goto fail1; } + ef100_rep_set_devlink_port(efv); rc = register_netdev(efv->net_dev); if (rc) { pci_err(efx->pci_dev, @@ -310,6 +310,7 @@ int efx_ef100_vfrep_create(struct efx_nic *efx, unsigned int 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); @@ -325,6 +326,7 @@ void efx_ef100_vfrep_destroy(struct efx_nic *efx, struct efx_rep *efv) 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); } @@ -341,6 +343,53 @@ void efx_ef100_fini_vfreps(struct efx_nic *efx) 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); diff --git a/drivers/net/ethernet/sfc/ef100_rep.h b/drivers/net/ethernet/sfc/ef100_rep.h index c21bc716f847..a042525a2240 100644 --- a/drivers/net/ethernet/sfc/ef100_rep.h +++ b/drivers/net/ethernet/sfc/ef100_rep.h @@ -22,6 +22,8 @@ struct efx_rep_sw_stats { atomic64_t rx_dropped, tx_errors; }; +struct devlink_port; + /** * struct efx_rep - Private data for an Efx representor * @@ -39,6 +41,7 @@ struct efx_rep_sw_stats { * @rx_lock: protects @rx_list * @napi: NAPI control structure * @stats: software traffic counters for netdev stats + * @dl_port: devlink port associated to this netdev representor */ struct efx_rep { struct efx_nic *parent; @@ -54,6 +57,7 @@ struct efx_rep { spinlock_t rx_lock; struct napi_struct napi; struct efx_rep_sw_stats stats; + struct devlink_port *dl_port; }; int efx_ef100_vfrep_create(struct efx_nic *efx, unsigned int i); @@ -67,4 +71,10 @@ void efx_ef100_rep_rx_packet(struct efx_rep *efv, struct efx_rx_buffer *rx_buf); */ struct efx_rep *efx_ef100_find_rep_by_mport(struct efx_nic *efx, u16 mport); extern const struct net_device_ops efx_ef100_rep_netdev_ops; +void efx_ef100_init_reps(struct efx_nic *efx); +void efx_ef100_fini_reps(struct efx_nic *efx); +struct mae_mport_desc; +bool ef100_mport_on_local_intf(struct efx_nic *efx, + struct mae_mport_desc *mport_desc); +bool ef100_mport_is_vf(struct mae_mport_desc *mport_desc); #endif /* EF100_REP_H */ diff --git a/drivers/net/ethernet/sfc/efx_devlink.c b/drivers/net/ethernet/sfc/efx_devlink.c new file mode 100644 index 000000000000..d2eb6712ba35 --- /dev/null +++ b/drivers/net/ethernet/sfc/efx_devlink.c @@ -0,0 +1,731 @@ +// SPDX-License-Identifier: GPL-2.0-only +/**************************************************************************** + * Driver for AMD network controllers and boards + * Copyright (C) 2023, Advanced Micro Devices, 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 "net_driver.h" +#include "ef100_nic.h" +#include "efx_devlink.h" +#include <linux/rtc.h> +#include "mcdi.h" +#include "mcdi_functions.h" +#include "mcdi_pcol.h" +#ifdef CONFIG_SFC_SRIOV +#include "mae.h" +#include "ef100_rep.h" +#endif + +struct efx_devlink { + struct efx_nic *efx; +}; + +#ifdef CONFIG_SFC_SRIOV +static void efx_devlink_del_port(struct devlink_port *dl_port) +{ + if (!dl_port) + return; + devl_port_unregister(dl_port); +} + +static int efx_devlink_add_port(struct efx_nic *efx, + struct mae_mport_desc *mport) +{ + bool external = false; + + if (!ef100_mport_on_local_intf(efx, mport)) + external = true; + + switch (mport->mport_type) { + case MAE_MPORT_DESC_MPORT_TYPE_VNIC: + if (mport->vf_idx != MAE_MPORT_DESC_VF_IDX_NULL) + devlink_port_attrs_pci_vf_set(&mport->dl_port, 0, mport->pf_idx, + mport->vf_idx, + external); + else + devlink_port_attrs_pci_pf_set(&mport->dl_port, 0, mport->pf_idx, + external); + break; + default: + /* MAE_MPORT_DESC_MPORT_ALIAS and UNDEFINED */ + return 0; + } + + mport->dl_port.index = mport->mport_id; + + return devl_port_register(efx->devlink, &mport->dl_port, mport->mport_id); +} + +static int efx_devlink_port_addr_get(struct devlink_port *port, u8 *hw_addr, + int *hw_addr_len, + struct netlink_ext_ack *extack) +{ + struct efx_devlink *devlink = devlink_priv(port->devlink); + struct mae_mport_desc *mport_desc; + efx_qword_t pciefn; + u32 client_id; + int rc = 0; + + mport_desc = container_of(port, struct mae_mport_desc, dl_port); + + if (!ef100_mport_on_local_intf(devlink->efx, mport_desc)) { + rc = -EINVAL; + NL_SET_ERR_MSG_FMT(extack, + "Port not on local interface (mport: %u)", + mport_desc->mport_id); + goto out; + } + + if (ef100_mport_is_vf(mport_desc)) + EFX_POPULATE_QWORD_3(pciefn, + PCIE_FUNCTION_PF, PCIE_FUNCTION_PF_NULL, + PCIE_FUNCTION_VF, mport_desc->vf_idx, + PCIE_FUNCTION_INTF, PCIE_INTERFACE_CALLER); + else + EFX_POPULATE_QWORD_3(pciefn, + PCIE_FUNCTION_PF, mport_desc->pf_idx, + PCIE_FUNCTION_VF, PCIE_FUNCTION_VF_NULL, + PCIE_FUNCTION_INTF, PCIE_INTERFACE_CALLER); + + rc = efx_ef100_lookup_client_id(devlink->efx, pciefn, &client_id); + if (rc) { + NL_SET_ERR_MSG_FMT(extack, + "No internal client_ID for port (mport: %u)", + mport_desc->mport_id); + goto out; + } + + rc = ef100_get_mac_address(devlink->efx, hw_addr, client_id, true); + if (rc != 0) + NL_SET_ERR_MSG_FMT(extack, + "No available MAC for port (mport: %u)", + mport_desc->mport_id); +out: + *hw_addr_len = ETH_ALEN; + return rc; +} + +static int efx_devlink_port_addr_set(struct devlink_port *port, + const u8 *hw_addr, int hw_addr_len, + struct netlink_ext_ack *extack) +{ + MCDI_DECLARE_BUF(inbuf, MC_CMD_SET_CLIENT_MAC_ADDRESSES_IN_LEN(1)); + struct efx_devlink *devlink = devlink_priv(port->devlink); + struct mae_mport_desc *mport_desc; + efx_qword_t pciefn; + u32 client_id; + int rc; + + mport_desc = container_of(port, struct mae_mport_desc, dl_port); + + if (!ef100_mport_is_vf(mport_desc)) { + NL_SET_ERR_MSG_FMT(extack, + "port mac change not allowed (mport: %u)", + mport_desc->mport_id); + return -EPERM; + } + + EFX_POPULATE_QWORD_3(pciefn, + PCIE_FUNCTION_PF, PCIE_FUNCTION_PF_NULL, + PCIE_FUNCTION_VF, mport_desc->vf_idx, + PCIE_FUNCTION_INTF, PCIE_INTERFACE_CALLER); + + rc = efx_ef100_lookup_client_id(devlink->efx, pciefn, &client_id); + if (rc) { + NL_SET_ERR_MSG_FMT(extack, + "No internal client_ID for port (mport: %u)", + mport_desc->mport_id); + return rc; + } + + MCDI_SET_DWORD(inbuf, SET_CLIENT_MAC_ADDRESSES_IN_CLIENT_HANDLE, + client_id); + + ether_addr_copy(MCDI_PTR(inbuf, SET_CLIENT_MAC_ADDRESSES_IN_MAC_ADDRS), + hw_addr); + + rc = efx_mcdi_rpc(devlink->efx, MC_CMD_SET_CLIENT_MAC_ADDRESSES, inbuf, + sizeof(inbuf), NULL, 0, NULL); + if (rc) + NL_SET_ERR_MSG_FMT(extack, + "sfc MC_CMD_SET_CLIENT_MAC_ADDRESSES mcdi error (mport: %u)", + mport_desc->mport_id); + + return rc; +} + +#endif + +static int efx_devlink_info_nvram_partition(struct efx_nic *efx, + struct devlink_info_req *req, + unsigned int partition_type, + const char *version_name) +{ + char buf[EFX_MAX_VERSION_INFO_LEN]; + u16 version[4]; + int rc; + + rc = efx_mcdi_nvram_metadata(efx, partition_type, NULL, version, NULL, + 0); + if (rc) { + netif_err(efx, drv, efx->net_dev, "mcdi nvram %s: failed\n", + version_name); + return rc; + } + + snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u", version[0], + version[1], version[2], version[3]); + devlink_info_version_stored_put(req, version_name, buf); + + return 0; +} + +static int efx_devlink_info_stored_versions(struct efx_nic *efx, + struct devlink_info_req *req) +{ + int rc; + + rc = efx_devlink_info_nvram_partition(efx, req, + NVRAM_PARTITION_TYPE_BUNDLE, + DEVLINK_INFO_VERSION_GENERIC_FW_BUNDLE_ID); + if (rc) + return rc; + + rc = efx_devlink_info_nvram_partition(efx, req, + NVRAM_PARTITION_TYPE_MC_FIRMWARE, + DEVLINK_INFO_VERSION_GENERIC_FW_MGMT); + if (rc) + return rc; + + rc = efx_devlink_info_nvram_partition(efx, req, + NVRAM_PARTITION_TYPE_SUC_FIRMWARE, + EFX_DEVLINK_INFO_VERSION_FW_MGMT_SUC); + if (rc) + return rc; + + rc = efx_devlink_info_nvram_partition(efx, req, + NVRAM_PARTITION_TYPE_EXPANSION_ROM, + EFX_DEVLINK_INFO_VERSION_FW_EXPROM); + if (rc) + return rc; + + rc = efx_devlink_info_nvram_partition(efx, req, + NVRAM_PARTITION_TYPE_EXPANSION_UEFI, + EFX_DEVLINK_INFO_VERSION_FW_UEFI); + return rc; +} + +#define EFX_VER_FLAG(_f) \ + (MC_CMD_GET_VERSION_V5_OUT_ ## _f ## _PRESENT_LBN) + +static void efx_devlink_info_running_v2(struct efx_nic *efx, + struct devlink_info_req *req, + unsigned int flags, efx_dword_t *outbuf) +{ + char buf[EFX_MAX_VERSION_INFO_LEN]; + union { + const __le32 *dwords; + const __le16 *words; + const char *str; + } ver; + struct rtc_time build_date; + unsigned int build_id; + size_t offset; + __maybe_unused u64 tstamp; + + if (flags & BIT(EFX_VER_FLAG(BOARD_EXT_INFO))) { + snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%s", + MCDI_PTR(outbuf, GET_VERSION_V2_OUT_BOARD_NAME)); + devlink_info_version_fixed_put(req, + DEVLINK_INFO_VERSION_GENERIC_BOARD_ID, + buf); + + /* Favour full board version if present (in V5 or later) */ + if (~flags & BIT(EFX_VER_FLAG(BOARD_VERSION))) { + snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u", + MCDI_DWORD(outbuf, + GET_VERSION_V2_OUT_BOARD_REVISION)); + devlink_info_version_fixed_put(req, + DEVLINK_INFO_VERSION_GENERIC_BOARD_REV, + buf); + } + + ver.str = MCDI_PTR(outbuf, GET_VERSION_V2_OUT_BOARD_SERIAL); + if (ver.str[0]) + devlink_info_board_serial_number_put(req, ver.str); + } + + if (flags & BIT(EFX_VER_FLAG(FPGA_EXT_INFO))) { + ver.dwords = (__le32 *)MCDI_PTR(outbuf, + GET_VERSION_V2_OUT_FPGA_VERSION); + offset = snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u_%c%u", + le32_to_cpu(ver.dwords[0]), + 'A' + le32_to_cpu(ver.dwords[1]), + le32_to_cpu(ver.dwords[2])); + + ver.str = MCDI_PTR(outbuf, GET_VERSION_V2_OUT_FPGA_EXTRA); + if (ver.str[0]) + snprintf(&buf[offset], EFX_MAX_VERSION_INFO_LEN - offset, + " (%s)", ver.str); + + devlink_info_version_running_put(req, + EFX_DEVLINK_INFO_VERSION_FPGA_REV, + buf); + } + + if (flags & BIT(EFX_VER_FLAG(CMC_EXT_INFO))) { + ver.dwords = (__le32 *)MCDI_PTR(outbuf, + GET_VERSION_V2_OUT_CMCFW_VERSION); + offset = snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u", + le32_to_cpu(ver.dwords[0]), + le32_to_cpu(ver.dwords[1]), + le32_to_cpu(ver.dwords[2]), + le32_to_cpu(ver.dwords[3])); + +#ifdef CONFIG_RTC_LIB + tstamp = MCDI_QWORD(outbuf, + GET_VERSION_V2_OUT_CMCFW_BUILD_DATE); + if (tstamp) { + rtc_time64_to_tm(tstamp, &build_date); + snprintf(&buf[offset], EFX_MAX_VERSION_INFO_LEN - offset, + " (%ptRd)", &build_date); + } +#endif + + devlink_info_version_running_put(req, + EFX_DEVLINK_INFO_VERSION_FW_MGMT_CMC, + buf); + } + + ver.words = (__le16 *)MCDI_PTR(outbuf, GET_VERSION_V2_OUT_VERSION); + offset = snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u", + le16_to_cpu(ver.words[0]), le16_to_cpu(ver.words[1]), + le16_to_cpu(ver.words[2]), le16_to_cpu(ver.words[3])); + if (flags & BIT(EFX_VER_FLAG(MCFW_EXT_INFO))) { + build_id = MCDI_DWORD(outbuf, GET_VERSION_V2_OUT_MCFW_BUILD_ID); + snprintf(&buf[offset], EFX_MAX_VERSION_INFO_LEN - offset, + " (%x) %s", build_id, + MCDI_PTR(outbuf, GET_VERSION_V2_OUT_MCFW_BUILD_NAME)); + } + devlink_info_version_running_put(req, + DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, + buf); + + if (flags & BIT(EFX_VER_FLAG(SUCFW_EXT_INFO))) { + ver.dwords = (__le32 *)MCDI_PTR(outbuf, + GET_VERSION_V2_OUT_SUCFW_VERSION); +#ifdef CONFIG_RTC_LIB + tstamp = MCDI_QWORD(outbuf, + GET_VERSION_V2_OUT_SUCFW_BUILD_DATE); + rtc_time64_to_tm(tstamp, &build_date); +#else + memset(&build_date, 0, sizeof(build_date) +#endif + build_id = MCDI_DWORD(outbuf, GET_VERSION_V2_OUT_SUCFW_CHIP_ID); + + snprintf(buf, EFX_MAX_VERSION_INFO_LEN, + "%u.%u.%u.%u type %x (%ptRd)", + le32_to_cpu(ver.dwords[0]), le32_to_cpu(ver.dwords[1]), + le32_to_cpu(ver.dwords[2]), le32_to_cpu(ver.dwords[3]), + build_id, &build_date); + + devlink_info_version_running_put(req, + EFX_DEVLINK_INFO_VERSION_FW_MGMT_SUC, + buf); + } +} + +static void efx_devlink_info_running_v3(struct efx_nic *efx, + struct devlink_info_req *req, + unsigned int flags, efx_dword_t *outbuf) +{ + char buf[EFX_MAX_VERSION_INFO_LEN]; + union { + const __le32 *dwords; + const __le16 *words; + const char *str; + } ver; + + if (flags & BIT(EFX_VER_FLAG(DATAPATH_HW_VERSION))) { + ver.dwords = (__le32 *)MCDI_PTR(outbuf, + GET_VERSION_V3_OUT_DATAPATH_HW_VERSION); + + snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u", + le32_to_cpu(ver.dwords[0]), le32_to_cpu(ver.dwords[1]), + le32_to_cpu(ver.dwords[2])); + + devlink_info_version_running_put(req, + EFX_DEVLINK_INFO_VERSION_DATAPATH_HW, + buf); + } + + if (flags & BIT(EFX_VER_FLAG(DATAPATH_FW_VERSION))) { + ver.dwords = (__le32 *)MCDI_PTR(outbuf, + GET_VERSION_V3_OUT_DATAPATH_FW_VERSION); + + snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u", + le32_to_cpu(ver.dwords[0]), le32_to_cpu(ver.dwords[1]), + le32_to_cpu(ver.dwords[2])); + + devlink_info_version_running_put(req, + EFX_DEVLINK_INFO_VERSION_DATAPATH_FW, + buf); + } +} + +static void efx_devlink_info_running_v4(struct efx_nic *efx, + struct devlink_info_req *req, + unsigned int flags, efx_dword_t *outbuf) +{ + char buf[EFX_MAX_VERSION_INFO_LEN]; + union { + const __le32 *dwords; + const __le16 *words; + const char *str; + } ver; + + if (flags & BIT(EFX_VER_FLAG(SOC_BOOT_VERSION))) { + ver.dwords = (__le32 *)MCDI_PTR(outbuf, + GET_VERSION_V4_OUT_SOC_BOOT_VERSION); + + snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u", + le32_to_cpu(ver.dwords[0]), le32_to_cpu(ver.dwords[1]), + le32_to_cpu(ver.dwords[2]), + le32_to_cpu(ver.dwords[3])); + + devlink_info_version_running_put(req, + EFX_DEVLINK_INFO_VERSION_SOC_BOOT, + buf); + } + + if (flags & BIT(EFX_VER_FLAG(SOC_UBOOT_VERSION))) { + ver.dwords = (__le32 *)MCDI_PTR(outbuf, + GET_VERSION_V4_OUT_SOC_UBOOT_VERSION); + + snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u", + le32_to_cpu(ver.dwords[0]), le32_to_cpu(ver.dwords[1]), + le32_to_cpu(ver.dwords[2]), + le32_to_cpu(ver.dwords[3])); + + devlink_info_version_running_put(req, + EFX_DEVLINK_INFO_VERSION_SOC_UBOOT, + buf); + } + + if (flags & BIT(EFX_VER_FLAG(SOC_MAIN_ROOTFS_VERSION))) { + ver.dwords = (__le32 *)MCDI_PTR(outbuf, + GET_VERSION_V4_OUT_SOC_MAIN_ROOTFS_VERSION); + + snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u", + le32_to_cpu(ver.dwords[0]), le32_to_cpu(ver.dwords[1]), + le32_to_cpu(ver.dwords[2]), + le32_to_cpu(ver.dwords[3])); + + devlink_info_version_running_put(req, + EFX_DEVLINK_INFO_VERSION_SOC_MAIN, + buf); + } + + if (flags & BIT(EFX_VER_FLAG(SOC_RECOVERY_BUILDROOT_VERSION))) { + ver.dwords = (__le32 *)MCDI_PTR(outbuf, + GET_VERSION_V4_OUT_SOC_RECOVERY_BUILDROOT_VERSION); + + snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u", + le32_to_cpu(ver.dwords[0]), le32_to_cpu(ver.dwords[1]), + le32_to_cpu(ver.dwords[2]), + le32_to_cpu(ver.dwords[3])); + + devlink_info_version_running_put(req, + EFX_DEVLINK_INFO_VERSION_SOC_RECOVERY, + buf); + } + + if (flags & BIT(EFX_VER_FLAG(SUCFW_VERSION)) && + ~flags & BIT(EFX_VER_FLAG(SUCFW_EXT_INFO))) { + ver.dwords = (__le32 *)MCDI_PTR(outbuf, + GET_VERSION_V4_OUT_SUCFW_VERSION); + + snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u", + le32_to_cpu(ver.dwords[0]), le32_to_cpu(ver.dwords[1]), + le32_to_cpu(ver.dwords[2]), + le32_to_cpu(ver.dwords[3])); + + devlink_info_version_running_put(req, + EFX_DEVLINK_INFO_VERSION_FW_MGMT_SUC, + buf); + } +} + +static void efx_devlink_info_running_v5(struct efx_nic *efx, + struct devlink_info_req *req, + unsigned int flags, efx_dword_t *outbuf) +{ + char buf[EFX_MAX_VERSION_INFO_LEN]; + union { + const __le32 *dwords; + const __le16 *words; + const char *str; + } ver; + + if (flags & BIT(EFX_VER_FLAG(BOARD_VERSION))) { + ver.dwords = (__le32 *)MCDI_PTR(outbuf, + GET_VERSION_V5_OUT_BOARD_VERSION); + + snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u", + le32_to_cpu(ver.dwords[0]), le32_to_cpu(ver.dwords[1]), + le32_to_cpu(ver.dwords[2]), + le32_to_cpu(ver.dwords[3])); + + devlink_info_version_running_put(req, + DEVLINK_INFO_VERSION_GENERIC_BOARD_REV, + buf); + } + + if (flags & BIT(EFX_VER_FLAG(BUNDLE_VERSION))) { + ver.dwords = (__le32 *)MCDI_PTR(outbuf, + GET_VERSION_V5_OUT_BUNDLE_VERSION); + + snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u", + le32_to_cpu(ver.dwords[0]), le32_to_cpu(ver.dwords[1]), + le32_to_cpu(ver.dwords[2]), + le32_to_cpu(ver.dwords[3])); + + devlink_info_version_running_put(req, + DEVLINK_INFO_VERSION_GENERIC_FW_BUNDLE_ID, + buf); + } +} + +static int efx_devlink_info_running_versions(struct efx_nic *efx, + struct devlink_info_req *req) +{ + MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_VERSION_V5_OUT_LEN); + MCDI_DECLARE_BUF(inbuf, MC_CMD_GET_VERSION_EXT_IN_LEN); + char buf[EFX_MAX_VERSION_INFO_LEN]; + union { + const __le32 *dwords; + const __le16 *words; + const char *str; + } ver; + size_t outlength; + unsigned int flags; + int rc; + + rc = efx_mcdi_rpc(efx, MC_CMD_GET_VERSION, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlength); + if (rc || outlength < MC_CMD_GET_VERSION_OUT_LEN) { + netif_err(efx, drv, efx->net_dev, + "mcdi MC_CMD_GET_VERSION failed\n"); + return rc; + } + + /* Handle previous output */ + if (outlength < MC_CMD_GET_VERSION_V2_OUT_LEN) { + ver.words = (__le16 *)MCDI_PTR(outbuf, + GET_VERSION_EXT_OUT_VERSION); + snprintf(buf, EFX_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u", + le16_to_cpu(ver.words[0]), + le16_to_cpu(ver.words[1]), + le16_to_cpu(ver.words[2]), + le16_to_cpu(ver.words[3])); + + devlink_info_version_running_put(req, + DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, + buf); + return 0; + } + + /* Handle V2 additions */ + flags = MCDI_DWORD(outbuf, GET_VERSION_V2_OUT_FLAGS); + efx_devlink_info_running_v2(efx, req, flags, outbuf); + + if (outlength < MC_CMD_GET_VERSION_V3_OUT_LEN) + return 0; + + /* Handle V3 additions */ + efx_devlink_info_running_v3(efx, req, flags, outbuf); + + if (outlength < MC_CMD_GET_VERSION_V4_OUT_LEN) + return 0; + + /* Handle V4 additions */ + efx_devlink_info_running_v4(efx, req, flags, outbuf); + + if (outlength < MC_CMD_GET_VERSION_V5_OUT_LEN) + return 0; + + /* Handle V5 additions */ + efx_devlink_info_running_v5(efx, req, flags, outbuf); + + return 0; +} + +#define EFX_MAX_SERIALNUM_LEN (ETH_ALEN * 2 + 1) + +static int efx_devlink_info_board_cfg(struct efx_nic *efx, + struct devlink_info_req *req) +{ + char sn[EFX_MAX_SERIALNUM_LEN]; + u8 mac_address[ETH_ALEN]; + int rc; + + rc = efx_mcdi_get_board_cfg(efx, (u8 *)mac_address, NULL, NULL); + if (!rc) { + snprintf(sn, EFX_MAX_SERIALNUM_LEN, "%pm", mac_address); + devlink_info_serial_number_put(req, sn); + } + return rc; +} + +static int efx_devlink_info_get(struct devlink *devlink, + struct devlink_info_req *req, + struct netlink_ext_ack *extack) +{ + struct efx_devlink *devlink_private = devlink_priv(devlink); + struct efx_nic *efx = devlink_private->efx; + int rc; + + /* Several different MCDI commands are used. We report first error + * through extack returning at that point. Specific error + * information via system messages. + */ + rc = efx_devlink_info_board_cfg(efx, req); + if (rc) { + NL_SET_ERR_MSG_MOD(extack, "Getting board info failed"); + return rc; + } + rc = efx_devlink_info_stored_versions(efx, req); + if (rc) { + NL_SET_ERR_MSG_MOD(extack, "Getting stored versions failed"); + return rc; + } + rc = efx_devlink_info_running_versions(efx, req); + if (rc) { + NL_SET_ERR_MSG_MOD(extack, "Getting running versions failed"); + return rc; + } + + return 0; +} + +static const struct devlink_ops sfc_devlink_ops = { + .info_get = efx_devlink_info_get, +#ifdef CONFIG_SFC_SRIOV + .port_function_hw_addr_get = efx_devlink_port_addr_get, + .port_function_hw_addr_set = efx_devlink_port_addr_set, +#endif +}; + +#ifdef CONFIG_SFC_SRIOV +static struct devlink_port *ef100_set_devlink_port(struct efx_nic *efx, u32 idx) +{ + struct mae_mport_desc *mport; + u32 id; + int rc; + + if (efx_mae_lookup_mport(efx, idx, &id)) { + /* This should not happen. */ + if (idx == MAE_MPORT_DESC_VF_IDX_NULL) + pci_warn_once(efx->pci_dev, "No mport ID found for PF.\n"); + else + pci_warn_once(efx->pci_dev, "No mport ID found for VF %u.\n", + idx); + return NULL; + } + + mport = efx_mae_get_mport(efx, id); + if (!mport) { + /* This should not happen. */ + if (idx == MAE_MPORT_DESC_VF_IDX_NULL) + pci_warn_once(efx->pci_dev, "No mport found for PF.\n"); + else + pci_warn_once(efx->pci_dev, "No mport found for VF %u.\n", + idx); + return NULL; + } + + rc = efx_devlink_add_port(efx, mport); + if (rc) { + if (idx == MAE_MPORT_DESC_VF_IDX_NULL) + pci_warn(efx->pci_dev, + "devlink port creation for PF failed.\n"); + else + pci_warn(efx->pci_dev, + "devlink_port creationg for VF %u failed.\n", + idx); + return NULL; + } + + return &mport->dl_port; +} + +void ef100_rep_set_devlink_port(struct efx_rep *efv) +{ + efv->dl_port = ef100_set_devlink_port(efv->parent, efv->idx); +} + +void ef100_pf_set_devlink_port(struct efx_nic *efx) +{ + efx->dl_port = ef100_set_devlink_port(efx, MAE_MPORT_DESC_VF_IDX_NULL); +} + +void ef100_rep_unset_devlink_port(struct efx_rep *efv) +{ + efx_devlink_del_port(efv->dl_port); +} + +void ef100_pf_unset_devlink_port(struct efx_nic *efx) +{ + efx_devlink_del_port(efx->dl_port); +} +#endif + +void efx_fini_devlink_lock(struct efx_nic *efx) +{ + if (efx->devlink) + devl_lock(efx->devlink); +} + +void efx_fini_devlink_and_unlock(struct efx_nic *efx) +{ + if (efx->devlink) { + devl_unregister(efx->devlink); + devl_unlock(efx->devlink); + devlink_free(efx->devlink); + efx->devlink = NULL; + } +} + +int efx_probe_devlink_and_lock(struct efx_nic *efx) +{ + struct efx_devlink *devlink_private; + + if (efx->type->is_vf) + return 0; + + efx->devlink = devlink_alloc(&sfc_devlink_ops, + sizeof(struct efx_devlink), + &efx->pci_dev->dev); + if (!efx->devlink) + return -ENOMEM; + + devl_lock(efx->devlink); + devlink_private = devlink_priv(efx->devlink); + devlink_private->efx = efx; + + devl_register(efx->devlink); + + return 0; +} + +void efx_probe_devlink_unlock(struct efx_nic *efx) +{ + if (!efx->devlink) + return; + + devl_unlock(efx->devlink); +} diff --git a/drivers/net/ethernet/sfc/efx_devlink.h b/drivers/net/ethernet/sfc/efx_devlink.h new file mode 100644 index 000000000000..e5fd5e1dcc27 --- /dev/null +++ b/drivers/net/ethernet/sfc/efx_devlink.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/**************************************************************************** + * Driver for AMD network controllers and boards + * Copyright (C) 2023, Advanced Micro Devices, 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. + */ + +#ifndef _EFX_DEVLINK_H +#define _EFX_DEVLINK_H + +#include "net_driver.h" +#include <net/devlink.h> + +/* Custom devlink-info version object names for details that do not map to the + * generic standardized names. + */ +#define EFX_DEVLINK_INFO_VERSION_FW_MGMT_SUC "fw.mgmt.suc" +#define EFX_DEVLINK_INFO_VERSION_FW_MGMT_CMC "fw.mgmt.cmc" +#define EFX_DEVLINK_INFO_VERSION_FPGA_REV "fpga.rev" +#define EFX_DEVLINK_INFO_VERSION_DATAPATH_HW "fpga.app" +#define EFX_DEVLINK_INFO_VERSION_DATAPATH_FW DEVLINK_INFO_VERSION_GENERIC_FW_APP +#define EFX_DEVLINK_INFO_VERSION_SOC_BOOT "coproc.boot" +#define EFX_DEVLINK_INFO_VERSION_SOC_UBOOT "coproc.uboot" +#define EFX_DEVLINK_INFO_VERSION_SOC_MAIN "coproc.main" +#define EFX_DEVLINK_INFO_VERSION_SOC_RECOVERY "coproc.recovery" +#define EFX_DEVLINK_INFO_VERSION_FW_EXPROM "fw.exprom" +#define EFX_DEVLINK_INFO_VERSION_FW_UEFI "fw.uefi" + +#define EFX_MAX_VERSION_INFO_LEN 64 + +int efx_probe_devlink_and_lock(struct efx_nic *efx); +void efx_probe_devlink_unlock(struct efx_nic *efx); +void efx_fini_devlink_lock(struct efx_nic *efx); +void efx_fini_devlink_and_unlock(struct efx_nic *efx); + +#ifdef CONFIG_SFC_SRIOV +struct efx_rep; + +void ef100_pf_set_devlink_port(struct efx_nic *efx); +void ef100_rep_set_devlink_port(struct efx_rep *efv); +void ef100_pf_unset_devlink_port(struct efx_nic *efx); +void ef100_rep_unset_devlink_port(struct efx_rep *efv); +#endif +#endif /* _EFX_DEVLINK_H */ diff --git a/drivers/net/ethernet/sfc/mae.c b/drivers/net/ethernet/sfc/mae.c index 583baf69981c..6321fd393fc3 100644 --- a/drivers/net/ethernet/sfc/mae.c +++ b/drivers/net/ethernet/sfc/mae.c @@ -9,8 +9,11 @@ * by the Free Software Foundation, incorporated herein by reference. */ +#include <linux/rhashtable.h> +#include "ef100_nic.h" #include "mae.h" #include "mcdi.h" +#include "mcdi_pcol.h" #include "mcdi_pcol_mae.h" int efx_mae_allocate_mport(struct efx_nic *efx, u32 *id, u32 *label) @@ -94,7 +97,7 @@ void efx_mae_mport_mport(struct efx_nic *efx __always_unused, u32 mport_id, u32 } /* id is really only 24 bits wide */ -int efx_mae_lookup_mport(struct efx_nic *efx, u32 selector, u32 *id) +int efx_mae_fw_lookup_mport(struct efx_nic *efx, u32 selector, u32 *id) { MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_MPORT_LOOKUP_OUT_LEN); MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_MPORT_LOOKUP_IN_LEN); @@ -485,11 +488,193 @@ int efx_mae_free_counter(struct efx_nic *efx, struct efx_tc_counter *cnt) return 0; } +int efx_mae_lookup_mport(struct efx_nic *efx, u32 vf_idx, u32 *id) +{ + struct ef100_nic_data *nic_data = efx->nic_data; + struct efx_mae *mae = efx->mae; + struct rhashtable_iter walk; + struct mae_mport_desc *m; + int rc = -ENOENT; + + rhashtable_walk_enter(&mae->mports_ht, &walk); + rhashtable_walk_start(&walk); + while ((m = rhashtable_walk_next(&walk)) != NULL) { + if (m->mport_type == MAE_MPORT_DESC_MPORT_TYPE_VNIC && + m->interface_idx == nic_data->local_mae_intf && + m->pf_idx == 0 && + m->vf_idx == vf_idx) { + *id = m->mport_id; + rc = 0; + break; + } + } + rhashtable_walk_stop(&walk); + rhashtable_walk_exit(&walk); + return rc; +} + static bool efx_mae_asl_id(u32 id) { return !!(id & BIT(31)); } +/* mport handling */ +static const struct rhashtable_params efx_mae_mports_ht_params = { + .key_len = sizeof(u32), + .key_offset = offsetof(struct mae_mport_desc, mport_id), + .head_offset = offsetof(struct mae_mport_desc, linkage), +}; + +struct mae_mport_desc *efx_mae_get_mport(struct efx_nic *efx, u32 mport_id) +{ + return rhashtable_lookup_fast(&efx->mae->mports_ht, &mport_id, + efx_mae_mports_ht_params); +} + +static int efx_mae_add_mport(struct efx_nic *efx, struct mae_mport_desc *desc) +{ + struct efx_mae *mae = efx->mae; + int rc; + + rc = rhashtable_insert_fast(&mae->mports_ht, &desc->linkage, + efx_mae_mports_ht_params); + + if (rc) { + pci_err(efx->pci_dev, "Failed to insert MPORT %08x, rc %d\n", + desc->mport_id, rc); + kfree(desc); + return rc; + } + + return rc; +} + +void efx_mae_remove_mport(void *desc, void *arg) +{ + struct mae_mport_desc *mport = desc; + + synchronize_rcu(); + kfree(mport); +} + +static int efx_mae_process_mport(struct efx_nic *efx, + struct mae_mport_desc *desc) +{ + struct ef100_nic_data *nic_data = efx->nic_data; + struct mae_mport_desc *mport; + + mport = efx_mae_get_mport(efx, desc->mport_id); + if (!IS_ERR_OR_NULL(mport)) { + netif_err(efx, drv, efx->net_dev, + "mport with id %u does exist!!!\n", desc->mport_id); + return -EEXIST; + } + + if (nic_data->have_own_mport && + desc->mport_id == nic_data->own_mport) { + WARN_ON(desc->mport_type != MAE_MPORT_DESC_MPORT_TYPE_VNIC); + WARN_ON(desc->vnic_client_type != + MAE_MPORT_DESC_VNIC_CLIENT_TYPE_FUNCTION); + nic_data->local_mae_intf = desc->interface_idx; + nic_data->have_local_intf = true; + pci_dbg(efx->pci_dev, "MAE interface_idx is %u\n", + nic_data->local_mae_intf); + } + + return efx_mae_add_mport(efx, desc); +} + +#define MCDI_MPORT_JOURNAL_LEN \ + ALIGN(MC_CMD_MAE_MPORT_READ_JOURNAL_OUT_LENMAX_MCDI2, 4) + +int efx_mae_enumerate_mports(struct efx_nic *efx) +{ + efx_dword_t *outbuf = kzalloc(MCDI_MPORT_JOURNAL_LEN, GFP_KERNEL); + MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_MPORT_READ_JOURNAL_IN_LEN); + MCDI_DECLARE_STRUCT_PTR(desc); + size_t outlen, stride, count; + int rc = 0, i; + + if (!outbuf) + return -ENOMEM; + do { + rc = efx_mcdi_rpc(efx, MC_CMD_MAE_MPORT_READ_JOURNAL, inbuf, + sizeof(inbuf), outbuf, + MCDI_MPORT_JOURNAL_LEN, &outlen); + if (rc) + goto fail; + if (outlen < MC_CMD_MAE_MPORT_READ_JOURNAL_OUT_MPORT_DESC_DATA_OFST) { + rc = -EIO; + goto fail; + } + count = MCDI_DWORD(outbuf, MAE_MPORT_READ_JOURNAL_OUT_MPORT_DESC_COUNT); + if (!count) + continue; /* not break; we want to look at MORE flag */ + stride = MCDI_DWORD(outbuf, MAE_MPORT_READ_JOURNAL_OUT_SIZEOF_MPORT_DESC); + if (stride < MAE_MPORT_DESC_LEN) { + rc = -EIO; + goto fail; + } + if (outlen < MC_CMD_MAE_MPORT_READ_JOURNAL_OUT_LEN(count * stride)) { + rc = -EIO; + goto fail; + } + + for (i = 0; i < count; i++) { + struct mae_mport_desc *d; + + d = kzalloc(sizeof(*d), GFP_KERNEL); + if (!d) { + rc = -ENOMEM; + goto fail; + } + + desc = (efx_dword_t *) + _MCDI_PTR(outbuf, MC_CMD_MAE_MPORT_READ_JOURNAL_OUT_MPORT_DESC_DATA_OFST + + i * stride); + d->mport_id = MCDI_STRUCT_DWORD(desc, MAE_MPORT_DESC_MPORT_ID); + d->flags = MCDI_STRUCT_DWORD(desc, MAE_MPORT_DESC_FLAGS); + d->caller_flags = MCDI_STRUCT_DWORD(desc, + MAE_MPORT_DESC_CALLER_FLAGS); + d->mport_type = MCDI_STRUCT_DWORD(desc, + MAE_MPORT_DESC_MPORT_TYPE); + switch (d->mport_type) { + case MAE_MPORT_DESC_MPORT_TYPE_NET_PORT: + d->port_idx = MCDI_STRUCT_DWORD(desc, + MAE_MPORT_DESC_NET_PORT_IDX); + break; + case MAE_MPORT_DESC_MPORT_TYPE_ALIAS: + d->alias_mport_id = MCDI_STRUCT_DWORD(desc, + MAE_MPORT_DESC_ALIAS_DELIVER_MPORT_ID); + break; + case MAE_MPORT_DESC_MPORT_TYPE_VNIC: + d->vnic_client_type = MCDI_STRUCT_DWORD(desc, + MAE_MPORT_DESC_VNIC_CLIENT_TYPE); + d->interface_idx = MCDI_STRUCT_DWORD(desc, + MAE_MPORT_DESC_VNIC_FUNCTION_INTERFACE); + d->pf_idx = MCDI_STRUCT_WORD(desc, + MAE_MPORT_DESC_VNIC_FUNCTION_PF_IDX); + d->vf_idx = MCDI_STRUCT_WORD(desc, + MAE_MPORT_DESC_VNIC_FUNCTION_VF_IDX); + break; + default: + /* Unknown mport_type, just accept it */ + break; + } + rc = efx_mae_process_mport(efx, d); + /* Any failure will be due to memory allocation faiure, + * so there is no point to try subsequent entries. + */ + if (rc) + goto fail; + } + } while (MCDI_FIELD(outbuf, MAE_MPORT_READ_JOURNAL_OUT, MORE) && + !WARN_ON(!count)); +fail: + kfree(outbuf); + return rc; +} + int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act) { MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_ALLOC_OUT_LEN); @@ -805,3 +990,34 @@ int efx_mae_delete_rule(struct efx_nic *efx, u32 id) return -EIO; return 0; } + +int efx_init_mae(struct efx_nic *efx) +{ + struct ef100_nic_data *nic_data = efx->nic_data; + struct efx_mae *mae; + int rc; + + if (!nic_data->have_mport) + return -EINVAL; + + mae = kmalloc(sizeof(*mae), GFP_KERNEL); + if (!mae) + return -ENOMEM; + + rc = rhashtable_init(&mae->mports_ht, &efx_mae_mports_ht_params); + if (rc < 0) { + kfree(mae); + return rc; + } + efx->mae = mae; + mae->efx = efx; + return 0; +} + +void efx_fini_mae(struct efx_nic *efx) +{ + struct efx_mae *mae = efx->mae; + + kfree(mae); + efx->mae = NULL; +} diff --git a/drivers/net/ethernet/sfc/mae.h b/drivers/net/ethernet/sfc/mae.h index 72343e90e222..bec293a06733 100644 --- a/drivers/net/ethernet/sfc/mae.h +++ b/drivers/net/ethernet/sfc/mae.h @@ -13,6 +13,7 @@ #define EF100_MAE_H /* MCDI interface for the ef100 Match-Action Engine */ +#include <net/devlink.h> #include "net_driver.h" #include "tc.h" #include "mcdi_pcol.h" /* needed for various MC_CMD_MAE_*_NULL defines */ @@ -27,6 +28,40 @@ void efx_mae_mport_mport(struct efx_nic *efx, u32 mport_id, u32 *out); int efx_mae_lookup_mport(struct efx_nic *efx, u32 selector, u32 *id); +struct mae_mport_desc { + u32 mport_id; + u32 flags; + u32 caller_flags; /* enum mae_mport_desc_caller_flags */ + u32 mport_type; /* MAE_MPORT_DESC_MPORT_TYPE_* */ + union { + u32 port_idx; /* for mport_type == NET_PORT */ + u32 alias_mport_id; /* for mport_type == ALIAS */ + struct { /* for mport_type == VNIC */ + u32 vnic_client_type; /* MAE_MPORT_DESC_VNIC_CLIENT_TYPE_* */ + u32 interface_idx; + u16 pf_idx; + u16 vf_idx; + }; + }; + struct rhash_head linkage; + struct devlink_port dl_port; +}; + +int efx_mae_enumerate_mports(struct efx_nic *efx); +struct mae_mport_desc *efx_mae_get_mport(struct efx_nic *efx, u32 mport_id); +void efx_mae_put_mport(struct efx_nic *efx, struct mae_mport_desc *desc); + +/** + * struct efx_mae - MAE information + * + * @efx: The associated NIC + * @mports_ht: m-port descriptions from MC_CMD_MAE_MPORT_READ_JOURNAL + */ +struct efx_mae { + struct efx_nic *efx; + struct rhashtable mports_ht; +}; + int efx_mae_start_counters(struct efx_nic *efx, struct efx_rx_queue *rx_queue); int efx_mae_stop_counters(struct efx_nic *efx, struct efx_rx_queue *rx_queue); void efx_mae_counters_grant_credits(struct work_struct *work); @@ -60,4 +95,9 @@ int efx_mae_insert_rule(struct efx_nic *efx, const struct efx_tc_match *match, u32 prio, u32 acts_id, u32 *id); int efx_mae_delete_rule(struct efx_nic *efx, u32 id); +int efx_init_mae(struct efx_nic *efx); +void efx_fini_mae(struct efx_nic *efx); +void efx_mae_remove_mport(void *desc, void *arg); +int efx_mae_fw_lookup_mport(struct efx_nic *efx, u32 selector, u32 *id); +int efx_mae_lookup_mport(struct efx_nic *efx, u32 vf, u32 *id); #endif /* EF100_MAE_H */ diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c index af338208eae9..a7f2c31071e8 100644 --- a/drivers/net/ethernet/sfc/mcdi.c +++ b/drivers/net/ethernet/sfc/mcdi.c @@ -2175,6 +2175,78 @@ int efx_mcdi_get_privilege_mask(struct efx_nic *efx, u32 *mask) return 0; } +int efx_mcdi_nvram_metadata(struct efx_nic *efx, unsigned int type, + u32 *subtype, u16 version[4], char *desc, + size_t descsize) +{ + MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_METADATA_IN_LEN); + efx_dword_t *outbuf; + size_t outlen; + u32 flags; + int rc; + + outbuf = kzalloc(MC_CMD_NVRAM_METADATA_OUT_LENMAX_MCDI2, GFP_KERNEL); + if (!outbuf) + return -ENOMEM; + + MCDI_SET_DWORD(inbuf, NVRAM_METADATA_IN_TYPE, type); + + rc = efx_mcdi_rpc_quiet(efx, MC_CMD_NVRAM_METADATA, inbuf, + sizeof(inbuf), outbuf, + MC_CMD_NVRAM_METADATA_OUT_LENMAX_MCDI2, + &outlen); + if (rc) + goto out_free; + if (outlen < MC_CMD_NVRAM_METADATA_OUT_LENMIN) { + rc = -EIO; + goto out_free; + } + + flags = MCDI_DWORD(outbuf, NVRAM_METADATA_OUT_FLAGS); + + if (desc && descsize > 0) { + if (flags & BIT(MC_CMD_NVRAM_METADATA_OUT_DESCRIPTION_VALID_LBN)) { + if (descsize <= + MC_CMD_NVRAM_METADATA_OUT_DESCRIPTION_NUM(outlen)) { + rc = -E2BIG; + goto out_free; + } + + strncpy(desc, + MCDI_PTR(outbuf, NVRAM_METADATA_OUT_DESCRIPTION), + MC_CMD_NVRAM_METADATA_OUT_DESCRIPTION_NUM(outlen)); + desc[MC_CMD_NVRAM_METADATA_OUT_DESCRIPTION_NUM(outlen)] = '\0'; + } else { + desc[0] = '\0'; + } + } + + if (subtype) { + if (flags & BIT(MC_CMD_NVRAM_METADATA_OUT_SUBTYPE_VALID_LBN)) + *subtype = MCDI_DWORD(outbuf, NVRAM_METADATA_OUT_SUBTYPE); + else + *subtype = 0; + } + + if (version) { + if (flags & BIT(MC_CMD_NVRAM_METADATA_OUT_VERSION_VALID_LBN)) { + version[0] = MCDI_WORD(outbuf, NVRAM_METADATA_OUT_VERSION_W); + version[1] = MCDI_WORD(outbuf, NVRAM_METADATA_OUT_VERSION_X); + version[2] = MCDI_WORD(outbuf, NVRAM_METADATA_OUT_VERSION_Y); + version[3] = MCDI_WORD(outbuf, NVRAM_METADATA_OUT_VERSION_Z); + } else { + version[0] = 0; + version[1] = 0; + version[2] = 0; + version[3] = 0; + } + } + +out_free: + kfree(outbuf); + return rc; +} + #ifdef CONFIG_SFC_MTD #define EFX_MCDI_NVRAM_LEN_MAX 128 diff --git a/drivers/net/ethernet/sfc/mcdi.h b/drivers/net/ethernet/sfc/mcdi.h index 7e35fec9da35..b139b76febff 100644 --- a/drivers/net/ethernet/sfc/mcdi.h +++ b/drivers/net/ethernet/sfc/mcdi.h @@ -229,6 +229,9 @@ void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev); #define MCDI_WORD(_buf, _field) \ ((u16)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 2) + \ le16_to_cpu(*(__force const __le16 *)MCDI_PTR(_buf, _field))) +#define MCDI_STRUCT_WORD(_buf, _field) \ + ((void)BUILD_BUG_ON_ZERO(_field ## _LEN != 2), \ + le16_to_cpu(*(__force const __le16 *)MCDI_STRUCT_PTR(_buf, _field))) /* Write a 16-bit field defined in the protocol as being big-endian. */ #define MCDI_STRUCT_SET_WORD_BE(_buf, _field, _value) do { \ BUILD_BUG_ON(_field ## _LEN != 2); \ @@ -241,6 +244,8 @@ void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev); EFX_POPULATE_DWORD_1(*_MCDI_STRUCT_DWORD(_buf, _field), EFX_DWORD_0, _value) #define MCDI_DWORD(_buf, _field) \ EFX_DWORD_FIELD(*_MCDI_DWORD(_buf, _field), EFX_DWORD_0) +#define MCDI_STRUCT_DWORD(_buf, _field) \ + EFX_DWORD_FIELD(*_MCDI_STRUCT_DWORD(_buf, _field), EFX_DWORD_0) /* Write a 32-bit field defined in the protocol as being big-endian. */ #define MCDI_STRUCT_SET_DWORD_BE(_buf, _field, _value) do { \ BUILD_BUG_ON(_field ## _LEN != 4); \ @@ -378,6 +383,9 @@ int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type, size_t *size_out, size_t *erase_size_out, bool *protected_out); int efx_new_mcdi_nvram_test_all(struct efx_nic *efx); +int efx_mcdi_nvram_metadata(struct efx_nic *efx, unsigned int type, + u32 *subtype, u16 version[4], char *desc, + size_t descsize); int efx_mcdi_nvram_test_all(struct efx_nic *efx); int efx_mcdi_handle_assertion(struct efx_nic *efx); int efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode); diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 3b49e216768b..fcd51d3992fa 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -845,6 +845,8 @@ enum efx_xdp_tx_queues_mode { EFX_XDP_TX_QUEUES_BORROWED /* queues borrowed from net stack */ }; +struct efx_mae; + /** * struct efx_nic - an Efx NIC * @name: Device name (net device name or bus id before net device registered) @@ -881,6 +883,7 @@ enum efx_xdp_tx_queues_mode { * @msi_context: Context for each MSI * @extra_channel_types: Types of extra (non-traffic) channels that * should be allocated for this NIC + * @mae: Details of the Match Action Engine * @xdp_tx_queue_count: Number of entries in %xdp_tx_queues. * @xdp_tx_queues: Array of pointers to tx queues used for XDP transmit. * @xdp_txq_queues_mode: XDP TX queues sharing strategy. @@ -994,6 +997,8 @@ enum efx_xdp_tx_queues_mode { * xdp_rxq_info structures? * @netdev_notifier: Netdevice notifier. * @tc: state for TC offload (EF100). + * @devlink: reference to devlink structure owned by this device + * @dl_port: devlink port associated with the PF * @mem_bar: The BAR that is mapped into membase. * @reg_base: Offset from the start of the bar to the function control window. * @monitor_work: Hardware monitor workitem @@ -1043,6 +1048,7 @@ struct efx_nic { struct efx_msi_context msi_context[EFX_MAX_CHANNELS]; const struct efx_channel_type * extra_channel_type[EFX_MAX_EXTRA_CHANNELS]; + struct efx_mae *mae; unsigned int xdp_tx_queue_count; struct efx_tx_queue **xdp_tx_queues; @@ -1179,6 +1185,8 @@ struct efx_nic { struct notifier_block netdev_notifier; struct efx_tc_state *tc; + struct devlink *devlink; + struct devlink_port *dl_port; unsigned int mem_bar; u32 reg_base; |