Merge branch 'nfp-pci-core-hwmon-live-mac-addr-change'

Jakub Kicinski says:

====================
nfp: pci core, hwmon, live mac addr change

This series brings updates to core PCI code, SR-IOV, exposes
firmware's capability to change MAC address at runtime and HWMON
interfaces.

The PCI code updates include resiliency improvement in conditions
which are quite unusual, but still shouldn't make the driver oops.
We also handle very large device memory operation more gracefully.
A timeout is added to acquiring mutexes in device memory.

Pablo provides a patch to expose to the stack the ability to change
MAC addresses under traffic while David adds HWMON interface for
reading device temperature and power consumption.

Last three patches are minor improvements to the netdev code.

v2:
 - add patch 1 - fix for devlink build;
 - fix build issue with the hwmon patch.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2017-05-30 11:27:10 -04:00
commit f1bd4daead
18 changed files with 476 additions and 78 deletions

View file

@ -19,6 +19,7 @@ config NFP
tristate "Netronome(R) NFP4000/NFP6000 NIC driver"
depends on PCI && PCI_MSI
depends on VXLAN || VXLAN=n
depends on MAY_USE_DEVLINK
---help---
This driver supports the Netronome(R) NFP4000/NFP6000 based
cards working as a advanced Ethernet NIC. It works with both

View file

@ -16,6 +16,7 @@ nfp-objs := \
nfpcore/nfp_target.o \
nfp_app.o \
nfp_devlink.o \
nfp_hwmon.o \
nfp_main.o \
nfp_net_common.o \
nfp_net_ethtool.o \

View file

@ -0,0 +1,192 @@
/*
* Copyright (C) 2017 Netronome Systems, Inc.
*
* This software is dual licensed under the GNU General License Version 2,
* June 1991 as shown in the file COPYING in the top-level directory of this
* source tree or the BSD 2-Clause License provided below. You have the
* option to license this software under the complete terms of either license.
*
* The BSD 2-Clause License:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/hwmon.h>
#include "nfpcore/nfp_cpp.h"
#include "nfpcore/nfp_nsp.h"
#include "nfp_main.h"
#define NFP_TEMP_MAX (95 * 1000)
#define NFP_TEMP_CRIT (105 * 1000)
#define NFP_POWER_MAX (25 * 1000 * 1000)
static int nfp_hwmon_sensor_id(enum hwmon_sensor_types type, int channel)
{
if (type == hwmon_temp)
return NFP_SENSOR_CHIP_TEMPERATURE;
if (type == hwmon_power)
return NFP_SENSOR_ASSEMBLY_POWER + channel;
return -EINVAL;
}
static int
nfp_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
int channel, long *val)
{
static const struct {
enum hwmon_sensor_types type;
u32 attr;
long val;
} const_vals[] = {
{ hwmon_temp, hwmon_temp_max, NFP_TEMP_MAX },
{ hwmon_temp, hwmon_temp_crit, NFP_TEMP_CRIT },
{ hwmon_power, hwmon_power_max, NFP_POWER_MAX },
};
struct nfp_pf *pf = dev_get_drvdata(dev);
enum nfp_nsp_sensor_id id;
int err, i;
for (i = 0; i < ARRAY_SIZE(const_vals); i++)
if (const_vals[i].type == type && const_vals[i].attr == attr) {
*val = const_vals[i].val;
return 0;
}
err = nfp_hwmon_sensor_id(type, channel);
if (err < 0)
return err;
id = err;
if (!(pf->nspi->sensor_mask & BIT(id)))
return -EOPNOTSUPP;
if (type == hwmon_temp && attr == hwmon_temp_input)
return nfp_hwmon_read_sensor(pf->cpp, id, val);
if (type == hwmon_power && attr == hwmon_power_input)
return nfp_hwmon_read_sensor(pf->cpp, id, val);
return -EINVAL;
}
static umode_t
nfp_hwmon_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr,
int channel)
{
if (type == hwmon_temp) {
switch (attr) {
case hwmon_temp_input:
case hwmon_temp_crit:
case hwmon_temp_max:
return 0444;
}
} else if (type == hwmon_power) {
switch (attr) {
case hwmon_power_input:
case hwmon_power_max:
return 0444;
}
}
return 0;
}
static u32 nfp_chip_config[] = {
HWMON_C_REGISTER_TZ,
0
};
static const struct hwmon_channel_info nfp_chip = {
.type = hwmon_chip,
.config = nfp_chip_config,
};
static u32 nfp_temp_config[] = {
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT,
0
};
static const struct hwmon_channel_info nfp_temp = {
.type = hwmon_temp,
.config = nfp_temp_config,
};
static u32 nfp_power_config[] = {
HWMON_P_INPUT | HWMON_P_MAX,
HWMON_P_INPUT,
HWMON_P_INPUT,
0
};
static const struct hwmon_channel_info nfp_power = {
.type = hwmon_power,
.config = nfp_power_config,
};
static const struct hwmon_channel_info *nfp_hwmon_info[] = {
&nfp_chip,
&nfp_temp,
&nfp_power,
NULL
};
static const struct hwmon_ops nfp_hwmon_ops = {
.is_visible = nfp_hwmon_is_visible,
.read = nfp_hwmon_read,
};
static const struct hwmon_chip_info nfp_chip_info = {
.ops = &nfp_hwmon_ops,
.info = nfp_hwmon_info,
};
int nfp_hwmon_register(struct nfp_pf *pf)
{
if (!IS_REACHABLE(CONFIG_HWMON))
return 0;
if (!pf->nspi) {
nfp_warn(pf->cpp, "not registering HWMON (no NSP info)\n");
return 0;
}
if (!pf->nspi->sensor_mask) {
nfp_info(pf->cpp,
"not registering HWMON (NSP doesn't report sensors)\n");
return 0;
}
pf->hwmon_dev = hwmon_device_register_with_info(&pf->pdev->dev, "nfp",
pf, &nfp_chip_info,
NULL);
return PTR_ERR_OR_ZERO(pf->hwmon_dev);
}
void nfp_hwmon_unregister(struct nfp_pf *pf)
{
if (!IS_REACHABLE(CONFIG_HWMON) || !pf->hwmon_dev)
return;
hwmon_device_unregister(pf->hwmon_dev);
}

View file

@ -73,20 +73,22 @@ static const struct pci_device_id nfp_pci_device_ids[] = {
};
MODULE_DEVICE_TABLE(pci, nfp_pci_device_ids);
static void nfp_pcie_sriov_read_nfd_limit(struct nfp_pf *pf)
static int nfp_pcie_sriov_read_nfd_limit(struct nfp_pf *pf)
{
#ifdef CONFIG_PCI_IOV
int err;
pf->limit_vfs = nfp_rtsym_read_le(pf->cpp, "nfd_vf_cfg_max_vfs", &err);
if (!err)
return;
return pci_sriov_set_totalvfs(pf->pdev, pf->limit_vfs);
pf->limit_vfs = ~0;
pci_sriov_set_totalvfs(pf->pdev, 0); /* 0 is unset */
/* Allow any setting for backwards compatibility if symbol not found */
if (err != -ENOENT)
nfp_warn(pf->cpp, "Warning: VF limit read failed: %d\n", err);
#endif
if (err == -ENOENT)
return 0;
nfp_warn(pf->cpp, "Warning: VF limit read failed: %d\n", err);
return err;
}
static int nfp_pcie_sriov_enable(struct pci_dev *pdev, int num_vfs)
@ -255,7 +257,6 @@ exit_release_fw:
static int nfp_nsp_init(struct pci_dev *pdev, struct nfp_pf *pf)
{
struct nfp_nsp_identify *nspi;
struct nfp_nsp *nsp;
int err;
@ -272,11 +273,9 @@ static int nfp_nsp_init(struct pci_dev *pdev, struct nfp_pf *pf)
pf->eth_tbl = __nfp_eth_read_ports(pf->cpp, nsp);
nspi = __nfp_nsp_identify(nsp);
if (nspi) {
dev_info(&pdev->dev, "BSP: %s\n", nspi->version);
kfree(nspi);
}
pf->nspi = __nfp_nsp_identify(nsp);
if (pf->nspi)
dev_info(&pdev->dev, "BSP: %s\n", pf->nspi->version);
err = nfp_fw_load(pdev, pf, nsp);
if (err < 0) {
@ -373,18 +372,31 @@ static int nfp_pci_probe(struct pci_dev *pdev,
if (err)
goto err_devlink_unreg;
nfp_pcie_sriov_read_nfd_limit(pf);
err = nfp_net_pci_probe(pf);
err = nfp_pcie_sriov_read_nfd_limit(pf);
if (err)
goto err_fw_unload;
err = nfp_net_pci_probe(pf);
if (err)
goto err_sriov_unlimit;
err = nfp_hwmon_register(pf);
if (err) {
dev_err(&pdev->dev, "Failed to register hwmon info\n");
goto err_net_remove;
}
return 0;
err_net_remove:
nfp_net_pci_remove(pf);
err_sriov_unlimit:
pci_sriov_set_totalvfs(pf->pdev, 0);
err_fw_unload:
if (pf->fw_loaded)
nfp_fw_unload(pf);
kfree(pf->eth_tbl);
kfree(pf->nspi);
err_devlink_unreg:
devlink_unregister(devlink);
err_cpp_free:
@ -406,11 +418,14 @@ static void nfp_pci_remove(struct pci_dev *pdev)
struct nfp_pf *pf = pci_get_drvdata(pdev);
struct devlink *devlink;
nfp_hwmon_unregister(pf);
devlink = priv_to_devlink(pf);
nfp_net_pci_remove(pf);
nfp_pcie_sriov_disable(pdev);
pci_sriov_set_totalvfs(pf->pdev, 0);
devlink_unregister(devlink);
@ -421,6 +436,7 @@ static void nfp_pci_remove(struct pci_dev *pdev)
nfp_cpp_free(pf->cpp);
kfree(pf->eth_tbl);
kfree(pf->nspi);
mutex_destroy(&pf->lock);
devlink_free(devlink);
pci_release_regions(pdev);

View file

@ -47,12 +47,14 @@
#include <linux/workqueue.h>
struct dentry;
struct device;
struct devlink_ops;
struct pci_dev;
struct nfp_cpp;
struct nfp_cpp_area;
struct nfp_eth_table;
struct nfp_nsp_identify;
/**
* struct nfp_pf - NFP PF-specific device structure
@ -67,6 +69,8 @@ struct nfp_eth_table;
* @num_vfs: Number of SR-IOV VFs enabled
* @fw_loaded: Is the firmware loaded?
* @eth_tbl: NSP ETH table
* @nspi: NSP identification info
* @hwmon_dev: pointer to hwmon device
* @ddir: Per-device debugfs directory
* @max_data_vnics: Number of data vNICs app firmware supports
* @num_vnics: Number of vNICs spawned
@ -94,6 +98,9 @@ struct nfp_pf {
bool fw_loaded;
struct nfp_eth_table *eth_tbl;
struct nfp_nsp_identify *nspi;
struct device *hwmon_dev;
struct dentry *ddir;
@ -113,4 +120,7 @@ extern const struct devlink_ops nfp_devlink_ops;
int nfp_net_pci_probe(struct nfp_pf *pf);
void nfp_net_pci_remove(struct nfp_pf *pf);
int nfp_hwmon_register(struct nfp_pf *pf);
void nfp_hwmon_unregister(struct nfp_pf *pf);
#endif /* NFP_MAIN_H */

View file

@ -328,8 +328,6 @@ struct nfp_net_rx_buf {
* @idx: Ring index from Linux's perspective
* @fl_qcidx: Queue Controller Peripheral (QCP) queue index for the freelist
* @qcp_fl: Pointer to base of the QCP freelist queue
* @wr_ptr_add: Accumulated number of buffers to add to QCP write pointer
* (used for free list batching)
* @rxbufs: Array of transmitted FL/RX buffers
* @rxds: Virtual address of FL/RX ring in host memory
* @dma: DMA address of the FL/RX ring
@ -343,7 +341,6 @@ struct nfp_net_rx_ring {
u32 rd_p;
u32 idx;
u32 wr_ptr_add;
int fl_qcidx;
u8 __iomem *qcp_fl;

View file

@ -928,7 +928,7 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring)
if (qcp_rd_p == tx_ring->qcp_rd_p)
return;
todo = D_IDX(tx_ring, qcp_rd_p + tx_ring->cnt - tx_ring->qcp_rd_p);
todo = D_IDX(tx_ring, qcp_rd_p - tx_ring->qcp_rd_p);
while (todo--) {
idx = D_IDX(tx_ring, tx_ring->rd_p++);
@ -999,7 +999,7 @@ static bool nfp_net_xdp_complete(struct nfp_net_tx_ring *tx_ring)
if (qcp_rd_p == tx_ring->qcp_rd_p)
return true;
todo = D_IDX(tx_ring, qcp_rd_p + tx_ring->cnt - tx_ring->qcp_rd_p);
todo = D_IDX(tx_ring, qcp_rd_p - tx_ring->qcp_rd_p);
done_all = todo <= NFP_NET_XDP_MAX_COMPLETE;
todo = min(todo, NFP_NET_XDP_MAX_COMPLETE);
@ -1212,14 +1212,12 @@ static void nfp_net_rx_give_one(const struct nfp_net_dp *dp,
dma_addr + dp->rx_dma_off);
rx_ring->wr_p++;
rx_ring->wr_ptr_add++;
if (rx_ring->wr_ptr_add >= NFP_NET_FL_BATCH) {
if (!(rx_ring->wr_p % NFP_NET_FL_BATCH)) {
/* Update write pointer of the freelist queue. Make
* sure all writes are flushed before telling the hardware.
*/
wmb();
nfp_qcp_wr_ptr_add(rx_ring->qcp_fl, rx_ring->wr_ptr_add);
rx_ring->wr_ptr_add = 0;
nfp_qcp_wr_ptr_add(rx_ring->qcp_fl, NFP_NET_FL_BATCH);
}
}
@ -1245,7 +1243,6 @@ static void nfp_net_rx_ring_reset(struct nfp_net_rx_ring *rx_ring)
memset(rx_ring->rxds, 0, sizeof(*rx_ring->rxds) * rx_ring->cnt);
rx_ring->wr_p = 0;
rx_ring->rd_p = 0;
rx_ring->wr_ptr_add = 0;
}
/**
@ -2123,17 +2120,16 @@ void nfp_net_coalesce_write_cfg(struct nfp_net *nn)
/**
* nfp_net_write_mac_addr() - Write mac address to the device control BAR
* @nn: NFP Net device to reconfigure
* @addr: MAC address to write
*
* Writes the MAC address from the netdev to the device control BAR. Does not
* perform the required reconfig. We do a bit of byte swapping dance because
* firmware is LE.
*/
static void nfp_net_write_mac_addr(struct nfp_net *nn)
static void nfp_net_write_mac_addr(struct nfp_net *nn, const u8 *addr)
{
nn_writel(nn, NFP_NET_CFG_MACADDR + 0,
get_unaligned_be32(nn->dp.netdev->dev_addr));
nn_writew(nn, NFP_NET_CFG_MACADDR + 6,
get_unaligned_be16(nn->dp.netdev->dev_addr + 4));
nn_writel(nn, NFP_NET_CFG_MACADDR + 0, get_unaligned_be32(addr));
nn_writew(nn, NFP_NET_CFG_MACADDR + 6, get_unaligned_be16(addr + 4));
}
static void nfp_net_vec_clear_ring_data(struct nfp_net *nn, unsigned int idx)
@ -2238,7 +2234,7 @@ static int nfp_net_set_config_and_enable(struct nfp_net *nn)
nn_writeq(nn, NFP_NET_CFG_RXRS_ENABLE, nn->dp.num_rx_rings == 64 ?
0xffffffffffffffffULL : ((u64)1 << nn->dp.num_rx_rings) - 1);
nfp_net_write_mac_addr(nn);
nfp_net_write_mac_addr(nn, nn->dp.netdev->dev_addr);
nn_writel(nn, NFP_NET_CFG_MTU, nn->dp.netdev->mtu);
@ -2997,6 +2993,27 @@ static int nfp_net_xdp(struct net_device *netdev, struct netdev_xdp *xdp)
}
}
static int nfp_net_set_mac_address(struct net_device *netdev, void *addr)
{
struct nfp_net *nn = netdev_priv(netdev);
struct sockaddr *saddr = addr;
int err;
err = eth_prepare_mac_addr_change(netdev, addr);
if (err)
return err;
nfp_net_write_mac_addr(nn, saddr->sa_data);
err = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_MACADDR);
if (err)
return err;
eth_commit_mac_addr_change(netdev, addr);
return 0;
}
const struct net_device_ops nfp_net_netdev_ops = {
.ndo_open = nfp_net_netdev_open,
.ndo_stop = nfp_net_netdev_close,
@ -3006,7 +3023,7 @@ const struct net_device_ops nfp_net_netdev_ops = {
.ndo_tx_timeout = nfp_net_tx_timeout,
.ndo_set_rx_mode = nfp_net_set_rx_mode,
.ndo_change_mtu = nfp_net_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_set_mac_address = nfp_net_set_mac_address,
.ndo_set_features = nfp_net_set_features,
.ndo_features_check = nfp_net_features_check,
.ndo_get_phys_port_name = nfp_port_get_phys_port_name,
@ -3029,7 +3046,7 @@ void nfp_net_info(struct nfp_net *nn)
nn->fw_ver.resv, nn->fw_ver.class,
nn->fw_ver.major, nn->fw_ver.minor,
nn->max_mtu);
nn_info(nn, "CAP: %#x %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
nn_info(nn, "CAP: %#x %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
nn->cap,
nn->cap & NFP_NET_CFG_CTRL_PROMISC ? "PROMISC " : "",
nn->cap & NFP_NET_CFG_CTRL_L2BC ? "L2BCFILT " : "",
@ -3051,7 +3068,8 @@ void nfp_net_info(struct nfp_net *nn)
nn->cap & NFP_NET_CFG_CTRL_NVGRE ? "NVGRE " : "",
nfp_net_ebpf_capable(nn) ? "BPF " : "",
nn->cap & NFP_NET_CFG_CTRL_CSUM_COMPLETE ?
"RXCSUM_COMPLETE " : "");
"RXCSUM_COMPLETE " : "",
nn->cap & NFP_NET_CFG_CTRL_LIVE_ADDR ? "LIVE_ADDR " : "");
}
/**
@ -3211,7 +3229,7 @@ int nfp_net_init(struct nfp_net *nn)
if (nn->dp.chained_metadata_format && nn->fw_ver.major != 4)
nn->cap &= ~NFP_NET_CFG_CTRL_RSS;
nfp_net_write_mac_addr(nn);
nfp_net_write_mac_addr(nn, nn->dp.netdev->dev_addr);
/* Determine RX packet/metadata boundary offset */
if (nn->fw_ver.major >= 2) {
@ -3241,6 +3259,9 @@ int nfp_net_init(struct nfp_net *nn)
* and netdev->hw_features advertises which features are
* supported. By default we enable most features.
*/
if (nn->cap & NFP_NET_CFG_CTRL_LIVE_ADDR)
netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
netdev->hw_features = NETIF_F_HIGHDMA;
if (nn->cap & NFP_NET_CFG_CTRL_RXCSUM_ANY) {
netdev->hw_features |= NETIF_F_RXCSUM;

View file

@ -135,6 +135,7 @@
#define NFP_NET_CFG_CTRL_LSO2 (0x1 << 28) /* LSO/TSO (version 2) */
#define NFP_NET_CFG_CTRL_RSS2 (0x1 << 29) /* RSS (version 2) */
#define NFP_NET_CFG_CTRL_CSUM_COMPLETE (0x1 << 30) /* Checksum complete */
#define NFP_NET_CFG_CTRL_LIVE_ADDR (0x1 << 31) /* live MAC addr change */
#define NFP_NET_CFG_CTRL_LSO_ANY (NFP_NET_CFG_CTRL_LSO | \
NFP_NET_CFG_CTRL_LSO2)
@ -157,6 +158,7 @@
#define NFP_NET_CFG_UPDATE_IRQMOD (0x1 << 8) /* IRQ mod change */
#define NFP_NET_CFG_UPDATE_VXLAN (0x1 << 9) /* VXLAN port change */
#define NFP_NET_CFG_UPDATE_BPF (0x1 << 10) /* BPF program load */
#define NFP_NET_CFG_UPDATE_MACADDR (0x1 << 11) /* MAC address change */
#define NFP_NET_CFG_UPDATE_ERR (0x1 << 31) /* A error occurred */
#define NFP_NET_CFG_TXRS_ENABLE 0x0008
#define NFP_NET_CFG_RXRS_ENABLE 0x0010

View file

@ -62,7 +62,7 @@ static int nfp_net_debugfs_rx_q_read(struct seq_file *file, void *data)
fl_rd_p = nfp_qcp_rd_ptr_read(rx_ring->qcp_fl);
fl_wr_p = nfp_qcp_wr_ptr_read(rx_ring->qcp_fl);
seq_printf(file, "RX[%02d,%02d]: cnt=%d dma=%pad host=%p H_RD=%d H_WR=%d FL_RD=%d FL_WR=%d\n",
seq_printf(file, "RX[%02d,%02d]: cnt=%u dma=%pad host=%p H_RD=%u H_WR=%u FL_RD=%u FL_WR=%u\n",
rx_ring->idx, rx_ring->fl_qcidx,
rx_ring->cnt, &rx_ring->dma, rx_ring->rxds,
rx_ring->rd_p, rx_ring->wr_p, fl_rd_p, fl_wr_p);
@ -146,7 +146,7 @@ static int nfp_net_debugfs_tx_q_read(struct seq_file *file, void *data)
d_rd_p = nfp_qcp_rd_ptr_read(tx_ring->qcp_q);
d_wr_p = nfp_qcp_wr_ptr_read(tx_ring->qcp_q);
seq_printf(file, "TX[%02d,%02d%s]: cnt=%d dma=%pad host=%p H_RD=%d H_WR=%d D_RD=%d D_WR=%d\n",
seq_printf(file, "TX[%02d,%02d%s]: cnt=%u dma=%pad host=%p H_RD=%u H_WR=%u D_RD=%u D_WR=%u\n",
tx_ring->idx, tx_ring->qcidx,
tx_ring == r_vec->tx_ring ? "" : "xdp",
tx_ring->cnt, &tx_ring->dma, tx_ring->txds,

View file

@ -64,6 +64,8 @@ int nfp_nsp_read_eth_table(struct nfp_nsp *state, void *buf, unsigned int size);
int nfp_nsp_write_eth_table(struct nfp_nsp *state,
const void *buf, unsigned int size);
int nfp_nsp_read_identify(struct nfp_nsp *state, void *buf, unsigned int size);
int nfp_nsp_read_sensors(struct nfp_nsp *state, unsigned int sensor_mask,
void *buf, unsigned int size);
/* Implemented in nfp_resource.c */

View file

@ -119,6 +119,11 @@
#define NFP_PCIE_EM 0x020000
#define NFP_PCIE_SRAM 0x000000
/* Minimal size of the PCIe cfg memory we depend on being mapped,
* queue controller and DMA controller don't have to be covered.
*/
#define NFP_PCI_MIN_MAP_SIZE 0x080000
#define NFP_PCIE_P2C_FIXED_SIZE(bar) (1 << (bar)->bitsize)
#define NFP_PCIE_P2C_BULK_SIZE(bar) (1 << (bar)->bitsize)
#define NFP_PCIE_P2C_GENERAL_TARGET_OFFSET(bar, x) ((x) << ((bar)->bitsize - 2))
@ -583,9 +588,15 @@ static int enable_bars(struct nfp6000_pcie *nfp, u16 interface)
NFP_PCIE_BAR_PCIE2CPP_MapType(
NFP_PCIE_BAR_PCIE2CPP_MapType_EXPLICIT3),
};
char status_msg[196] = {};
struct nfp_bar *bar;
int i, bars_free;
int expl_groups;
char *msg, *end;
msg = status_msg +
snprintf(status_msg, sizeof(status_msg) - 1, "RESERVED BARs: ");
end = status_msg + sizeof(status_msg) - 1;
bar = &nfp->bar[0];
for (i = 0; i < ARRAY_SIZE(nfp->bar); i++, bar++) {
@ -628,34 +639,38 @@ static int enable_bars(struct nfp6000_pcie *nfp, u16 interface)
/* Configure, and lock, BAR0.0 for General Target use (MSI-X SRAM) */
bar = &nfp->bar[0];
bar->iomem = ioremap_nocache(nfp_bar_resource_start(bar),
nfp_bar_resource_len(bar));
if (nfp_bar_resource_len(bar) >= NFP_PCI_MIN_MAP_SIZE)
bar->iomem = ioremap_nocache(nfp_bar_resource_start(bar),
nfp_bar_resource_len(bar));
if (bar->iomem) {
dev_info(nfp->dev,
"BAR0.0 RESERVED: General Mapping/MSI-X SRAM\n");
msg += snprintf(msg, end - msg, "0.0: General/MSI-X SRAM, ");
atomic_inc(&bar->refcnt);
bars_free--;
nfp6000_bar_write(nfp, bar, barcfg_msix_general);
nfp->expl.data = bar->iomem + NFP_PCIE_SRAM + 0x1000;
if (nfp->pdev->device == PCI_DEVICE_ID_NETRONOME_NFP4000 ||
nfp->pdev->device == PCI_DEVICE_ID_NETRONOME_NFP6000) {
nfp->iomem.csr = bar->iomem + NFP_PCIE_BAR(0);
} else {
int pf = nfp->pdev->devfn & 7;
nfp->iomem.csr = bar->iomem + NFP_PCIE_BAR(pf);
}
nfp->iomem.em = bar->iomem + NFP_PCIE_EM;
}
if (nfp->pdev->device == PCI_DEVICE_ID_NETRONOME_NFP4000 ||
nfp->pdev->device == PCI_DEVICE_ID_NETRONOME_NFP6000) {
nfp->iomem.csr = bar->iomem + NFP_PCIE_BAR(0);
nfp->pdev->device == PCI_DEVICE_ID_NETRONOME_NFP6000)
expl_groups = 4;
} else {
int pf = nfp->pdev->devfn & 7;
nfp->iomem.csr = bar->iomem + NFP_PCIE_BAR(pf);
else
expl_groups = 1;
}
nfp->iomem.em = bar->iomem + NFP_PCIE_EM;
/* Configure, and lock, BAR0.1 for PCIe XPB (MSI-X PBA) */
bar = &nfp->bar[1];
dev_info(nfp->dev, "BAR0.1 RESERVED: PCIe XPB/MSI-X PBA\n");
msg += snprintf(msg, end - msg, "0.1: PCIe XPB/MSI-X PBA, ");
atomic_inc(&bar->refcnt);
bars_free--;
@ -674,9 +689,8 @@ static int enable_bars(struct nfp6000_pcie *nfp, u16 interface)
bar->iomem = ioremap_nocache(nfp_bar_resource_start(bar),
nfp_bar_resource_len(bar));
if (bar->iomem) {
dev_info(nfp->dev,
"BAR0.%d RESERVED: Explicit%d Mapping\n",
4 + i, i);
msg += snprintf(msg, end - msg,
"0.%d: Explicit%d, ", 4 + i, i);
atomic_inc(&bar->refcnt);
bars_free--;
@ -694,8 +708,7 @@ static int enable_bars(struct nfp6000_pcie *nfp, u16 interface)
sort(&nfp->bar[0], nfp->bars, sizeof(nfp->bar[0]),
bar_cmp, NULL);
dev_info(nfp->dev, "%d NFP PCI2CPP BARs, %d free\n",
nfp->bars, bars_free);
dev_info(nfp->dev, "%sfree: %d/%d\n", status_msg, bars_free, nfp->bars);
return 0;
}

View file

@ -42,6 +42,7 @@
#include <linux/ctype.h>
#include <linux/types.h>
#include <linux/sizes.h>
#ifndef NFP_SUBSYS
#define NFP_SUBSYS "nfp"
@ -59,6 +60,13 @@
#define PCI_64BIT_BAR_COUNT 3
#define NFP_CPP_NUM_TARGETS 16
/* Max size of area it should be safe to request */
#define NFP_CPP_SAFE_AREA_SIZE SZ_2M
/* NFP_MUTEX_WAIT_* are timeouts in seconds when waiting for a mutex */
#define NFP_MUTEX_WAIT_FIRST_WARN 15
#define NFP_MUTEX_WAIT_NEXT_WARN 5
#define NFP_MUTEX_WAIT_ERROR 60
struct device;

View file

@ -924,18 +924,9 @@ area_cache_put(struct nfp_cpp *cpp, struct nfp_cpp_area_cache *cache)
mutex_unlock(&cpp->area_cache_mutex);
}
/**
* nfp_cpp_read() - read from CPP target
* @cpp: CPP handle
* @destination: CPP id
* @address: offset into CPP target
* @kernel_vaddr: kernel buffer for result
* @length: number of bytes to read
*
* Return: length of io, or -ERRNO
*/
int nfp_cpp_read(struct nfp_cpp *cpp, u32 destination,
unsigned long long address, void *kernel_vaddr, size_t length)
static int __nfp_cpp_read(struct nfp_cpp *cpp, u32 destination,
unsigned long long address, void *kernel_vaddr,
size_t length)
{
struct nfp_cpp_area_cache *cache;
struct nfp_cpp_area *area;
@ -968,18 +959,43 @@ int nfp_cpp_read(struct nfp_cpp *cpp, u32 destination,
}
/**
* nfp_cpp_write() - write to CPP target
* nfp_cpp_read() - read from CPP target
* @cpp: CPP handle
* @destination: CPP id
* @address: offset into CPP target
* @kernel_vaddr: kernel buffer to read from
* @length: number of bytes to write
* @kernel_vaddr: kernel buffer for result
* @length: number of bytes to read
*
* Return: length of io, or -ERRNO
*/
int nfp_cpp_write(struct nfp_cpp *cpp, u32 destination,
unsigned long long address,
const void *kernel_vaddr, size_t length)
int nfp_cpp_read(struct nfp_cpp *cpp, u32 destination,
unsigned long long address, void *kernel_vaddr,
size_t length)
{
size_t n, offset;
int ret;
for (offset = 0; offset < length; offset += n) {
unsigned long long r_addr = address + offset;
/* make first read smaller to align to safe window */
n = min_t(size_t, length - offset,
ALIGN(r_addr + 1, NFP_CPP_SAFE_AREA_SIZE) - r_addr);
ret = __nfp_cpp_read(cpp, destination, address + offset,
kernel_vaddr + offset, n);
if (ret < 0)
return ret;
if (ret != n)
return offset + n;
}
return length;
}
static int __nfp_cpp_write(struct nfp_cpp *cpp, u32 destination,
unsigned long long address,
const void *kernel_vaddr, size_t length)
{
struct nfp_cpp_area_cache *cache;
struct nfp_cpp_area *area;
@ -1011,6 +1027,41 @@ int nfp_cpp_write(struct nfp_cpp *cpp, u32 destination,
return err;
}
/**
* nfp_cpp_write() - write to CPP target
* @cpp: CPP handle
* @destination: CPP id
* @address: offset into CPP target
* @kernel_vaddr: kernel buffer to read from
* @length: number of bytes to write
*
* Return: length of io, or -ERRNO
*/
int nfp_cpp_write(struct nfp_cpp *cpp, u32 destination,
unsigned long long address,
const void *kernel_vaddr, size_t length)
{
size_t n, offset;
int ret;
for (offset = 0; offset < length; offset += n) {
unsigned long long w_addr = address + offset;
/* make first write smaller to align to safe window */
n = min_t(size_t, length - offset,
ALIGN(w_addr + 1, NFP_CPP_SAFE_AREA_SIZE) - w_addr);
ret = __nfp_cpp_write(cpp, destination, address + offset,
kernel_vaddr + offset, n);
if (ret < 0)
return ret;
if (ret != n)
return offset + n;
}
return length;
}
/* Return the correct CPP address, and fixup xpb_addr as needed. */
static u32 nfp_xpb_to_cpp(struct nfp_cpp *cpp, u32 *xpb_addr)
{

View file

@ -195,7 +195,8 @@ void nfp_cpp_mutex_free(struct nfp_cpp_mutex *mutex)
*/
int nfp_cpp_mutex_lock(struct nfp_cpp_mutex *mutex)
{
unsigned long warn_at = jiffies + 15 * HZ;
unsigned long warn_at = jiffies + NFP_MUTEX_WAIT_FIRST_WARN * HZ;
unsigned long err_at = jiffies + NFP_MUTEX_WAIT_ERROR * HZ;
unsigned int timeout_ms = 1;
int err;
@ -214,12 +215,16 @@ int nfp_cpp_mutex_lock(struct nfp_cpp_mutex *mutex)
return -ERESTARTSYS;
if (time_is_before_eq_jiffies(warn_at)) {
warn_at = jiffies + 60 * HZ;
warn_at = jiffies + NFP_MUTEX_WAIT_NEXT_WARN * HZ;
nfp_warn(mutex->cpp,
"Warning: waiting for NFP mutex [depth:%hd target:%d addr:%llx key:%08x]\n",
mutex->depth,
mutex->target, mutex->address, mutex->key);
}
if (time_is_before_eq_jiffies(err_at)) {
nfp_err(mutex->cpp, "Error: mutex wait timed out\n");
return -EBUSY;
}
}
return err;

View file

@ -93,6 +93,7 @@ enum nfp_nsp_cmd {
SPCODE_FW_LOAD = 6, /* Load fw from buffer, len in option */
SPCODE_ETH_RESCAN = 7, /* Rescan ETHs, write ETH_TABLE to buf */
SPCODE_ETH_CONTROL = 8, /* Update media config from buffer */
SPCODE_NSP_SENSORS = 12, /* Read NSP sensor(s) */
SPCODE_NSP_IDENTIFY = 13, /* Read NSP version */
};
@ -419,6 +420,14 @@ static int nfp_nsp_command_buf(struct nfp_nsp *nsp, u16 code, u32 option,
if (err < 0)
return err;
}
/* Zero out remaining part of the buffer */
if (out_buf && out_size && out_size > in_size) {
memset(out_buf, 0, out_size - in_size);
err = nfp_cpp_write(cpp, cpp_id, cpp_buf + in_size,
out_buf, out_size - in_size);
if (err < 0)
return err;
}
ret = nfp_nsp_command(nsp, code, option, cpp_id, cpp_buf);
if (ret < 0)
@ -498,3 +507,10 @@ int nfp_nsp_read_identify(struct nfp_nsp *state, void *buf, unsigned int size)
return nfp_nsp_command_buf(state, SPCODE_NSP_IDENTIFY, size, NULL, 0,
buf, size);
}
int nfp_nsp_read_sensors(struct nfp_nsp *state, unsigned int sensor_mask,
void *buf, unsigned int size)
{
return nfp_nsp_command_buf(state, SPCODE_NSP_SENSORS, sensor_mask,
NULL, 0, buf, size);
}

View file

@ -160,6 +160,7 @@ int __nfp_eth_set_split(struct nfp_nsp *nsp, unsigned int lanes);
* @primary: version of primarary bootloader
* @secondary: version id of secondary bootloader
* @nsp: version id of NSP
* @sensor_mask: mask of present sensors available on NIC
*/
struct nfp_nsp_identify {
char version[40];
@ -170,8 +171,19 @@ struct nfp_nsp_identify {
u16 primary;
u16 secondary;
u16 nsp;
u64 sensor_mask;
};
struct nfp_nsp_identify *__nfp_nsp_identify(struct nfp_nsp *nsp);
enum nfp_nsp_sensor_id {
NFP_SENSOR_CHIP_TEMPERATURE,
NFP_SENSOR_ASSEMBLY_POWER,
NFP_SENSOR_ASSEMBLY_12V_POWER,
NFP_SENSOR_ASSEMBLY_3V3_POWER,
};
int nfp_hwmon_read_sensor(struct nfp_cpp *cpp, enum nfp_nsp_sensor_id id,
long *val);
#endif

View file

@ -46,7 +46,8 @@ struct nsp_identify {
__le16 primary;
__le16 secondary;
__le16 nsp;
__le16 reserved;
u8 reserved[6];
__le64 sensor_mask;
};
struct nfp_nsp_identify *__nfp_nsp_identify(struct nfp_nsp *nsp)
@ -82,8 +83,52 @@ struct nfp_nsp_identify *__nfp_nsp_identify(struct nfp_nsp *nsp)
nspi->primary = le16_to_cpu(ni->primary);
nspi->secondary = le16_to_cpu(ni->secondary);
nspi->nsp = le16_to_cpu(ni->nsp);
nspi->sensor_mask = le64_to_cpu(ni->sensor_mask);
exit_free:
kfree(ni);
return nspi;
}
struct nfp_sensors {
__le32 chip_temp;
__le32 assembly_power;
__le32 assembly_12v_power;
__le32 assembly_3v3_power;
};
int nfp_hwmon_read_sensor(struct nfp_cpp *cpp, enum nfp_nsp_sensor_id id,
long *val)
{
struct nfp_sensors s;
struct nfp_nsp *nsp;
int ret;
nsp = nfp_nsp_open(cpp);
if (IS_ERR(nsp))
return PTR_ERR(nsp);
ret = nfp_nsp_read_sensors(nsp, BIT(id), &s, sizeof(s));
nfp_nsp_close(nsp);
if (ret < 0)
return ret;
switch (id) {
case NFP_SENSOR_CHIP_TEMPERATURE:
*val = le32_to_cpu(s.chip_temp);
break;
case NFP_SENSOR_ASSEMBLY_POWER:
*val = le32_to_cpu(s.assembly_power);
break;
case NFP_SENSOR_ASSEMBLY_12V_POWER:
*val = le32_to_cpu(s.assembly_12v_power);
break;
case NFP_SENSOR_ASSEMBLY_3V3_POWER:
*val = le32_to_cpu(s.assembly_3v3_power);
break;
default:
return -EINVAL;
}
return 0;
}

View file

@ -181,7 +181,8 @@ err_unlock_dev:
struct nfp_resource *
nfp_resource_acquire(struct nfp_cpp *cpp, const char *name)
{
unsigned long warn_at = jiffies + 15 * HZ;
unsigned long warn_at = jiffies + NFP_MUTEX_WAIT_FIRST_WARN * HZ;
unsigned long err_at = jiffies + NFP_MUTEX_WAIT_ERROR * HZ;
struct nfp_cpp_mutex *dev_mutex;
struct nfp_resource *res;
int err;
@ -214,10 +215,15 @@ nfp_resource_acquire(struct nfp_cpp *cpp, const char *name)
}
if (time_is_before_eq_jiffies(warn_at)) {
warn_at = jiffies + 60 * HZ;
warn_at = jiffies + NFP_MUTEX_WAIT_NEXT_WARN * HZ;
nfp_warn(cpp, "Warning: waiting for NFP resource %s\n",
name);
}
if (time_is_before_eq_jiffies(err_at)) {
nfp_err(cpp, "Error: resource %s timed out\n", name);
err = -EBUSY;
goto err_free;
}
}
nfp_cpp_mutex_free(dev_mutex);