Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright(c) 2017 - 2019 Pensando Systems, Inc
obj-$(CONFIG_IONIC) := ionic.o
ionic-y := ionic_main.o ionic_bus_pci.o ionic_devlink.o ionic_dev.o \
ionic_debugfs.o ionic_lif.o ionic_rx_filter.o ionic_ethtool.o \
ionic_txrx.o ionic_stats.o ionic_fw.o ionic_aux.o
ionic-$(CONFIG_PTP_1588_CLOCK) += ionic_phc.o
@@ -0,0 +1,93 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
#ifndef _IONIC_H_
#define _IONIC_H_
struct ionic_lif;
#include "ionic_if.h"
#include "ionic_dev.h"
#include "ionic_devlink.h"
#define IONIC_DRV_NAME "ionic"
#define IONIC_DRV_DESCRIPTION "Pensando Ethernet NIC Driver"
#define PCI_VENDOR_ID_PENSANDO 0x1dd8
#define PCI_DEVICE_ID_PENSANDO_IONIC_ETH_PF 0x1002
#define PCI_DEVICE_ID_PENSANDO_IONIC_ETH_VF 0x1003
#define DEVCMD_TIMEOUT 5
#define IONIC_ADMINQ_TIME_SLICE msecs_to_jiffies(100)
#define IONIC_PHC_UPDATE_NS 10000000000 /* 10s in nanoseconds */
#define NORMAL_PPB 1000000000 /* one billion parts per billion */
#define SCALED_PPM (1000000ull << 16) /* 2^16 million parts per 2^16 million */
struct ionic_vf {
u16 index;
u8 macaddr[6];
__le32 maxrate;
__le16 vlanid;
u8 spoofchk;
u8 trusted;
u8 linkstate;
dma_addr_t stats_pa;
struct ionic_lif_stats stats;
};
struct ionic {
struct pci_dev *pdev;
struct device *dev;
struct devlink_port dl_port;
struct ionic_dev idev;
struct mutex dev_cmd_lock; /* lock for dev_cmd operations */
struct dentry *dentry;
struct ionic_dev_bar bars[IONIC_BARS_MAX];
unsigned int num_bars;
struct ionic_identity ident;
struct workqueue_struct *wq;
struct ionic_lif *lif;
unsigned int nnqs_per_lif;
unsigned int neqs_per_lif;
unsigned int ntxqs_per_lif;
unsigned int nrxqs_per_lif;
unsigned int nintrs;
DECLARE_BITMAP(intrs, IONIC_INTR_CTRL_REGS_MAX);
cpumask_var_t *affinity_masks;
struct delayed_work doorbell_check_dwork;
struct notifier_block nb;
struct rw_semaphore vf_op_lock; /* lock for VF operations */
struct ionic_vf *vfs;
int num_vfs;
struct timer_list watchdog_timer;
int watchdog_period;
};
int ionic_adminq_post(struct ionic_lif *lif, struct ionic_admin_ctx *ctx);
int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx,
const int err, const bool do_msg);
int ionic_adminq_post_wait_nomsg(struct ionic_lif *lif, struct ionic_admin_ctx *ctx);
void ionic_adminq_netdev_err_print(struct ionic_lif *lif, u8 opcode,
u8 status, int err);
bool ionic_notifyq_service(struct ionic_cq *cq);
bool ionic_adminq_service(struct ionic_cq *cq);
int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_wait);
int ionic_dev_cmd_wait_nomsg(struct ionic *ionic, unsigned long max_wait);
void ionic_dev_cmd_dev_err_print(struct ionic *ionic, u8 opcode, u8 status,
int err);
int ionic_setup(struct ionic *ionic);
int ionic_identify(struct ionic *ionic);
int ionic_init(struct ionic *ionic);
int ionic_reset(struct ionic *ionic);
int ionic_port_identify(struct ionic *ionic);
int ionic_port_init(struct ionic *ionic);
int ionic_port_reset(struct ionic *ionic);
bool ionic_doorbell_wa(struct ionic *ionic);
#endif /* _IONIC_H_ */
@@ -0,0 +1,131 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
#ifndef _IONIC_API_H_
#define _IONIC_API_H_
#include <linux/auxiliary_bus.h>
#include "ionic_if.h"
#include "ionic_regs.h"
/**
* struct ionic_aux_dev - Auxiliary device information
* @lif: Logical interface
* @idx: Index identifier
* @adev: Auxiliary device
*/
struct ionic_aux_dev {
struct ionic_lif *lif;
int idx;
struct auxiliary_device adev;
};
/**
* struct ionic_admin_ctx - Admin command context
* @work: Work completion wait queue element
* @cmd: Admin command (64B) to be copied to the queue
* @comp: Admin completion (16B) copied from the queue
*/
struct ionic_admin_ctx {
struct completion work;
union ionic_adminq_cmd cmd;
union ionic_adminq_comp comp;
};
#define IONIC_INTR_INDEX_NOT_ASSIGNED -1
#define IONIC_INTR_NAME_MAX_SZ 32
/**
* struct ionic_intr_info - Interrupt information
* @name: Name identifier
* @rearm_count: Interrupt rearm count
* @index: Interrupt index position
* @vector: Interrupt number
* @dim_coal_hw: Interrupt coalesce value in hardware units
* @affinity_mask: CPU affinity mask
* @aff_notify: context for notification of IRQ affinity changes
*/
struct ionic_intr_info {
char name[IONIC_INTR_NAME_MAX_SZ];
u64 rearm_count;
unsigned int index;
unsigned int vector;
u32 dim_coal_hw;
cpumask_var_t *affinity_mask;
struct irq_affinity_notify aff_notify;
};
/**
* ionic_adminq_post_wait - Post an admin command and wait for response
* @lif: Logical interface
* @ctx: API admin command context
*
* Post the command to an admin queue in the ethernet driver. If this command
* succeeds, then the command has been posted, but that does not indicate a
* completion. If this command returns success, then the completion callback
* will eventually be called.
*
* Return: zero or negative error status
*/
int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx);
/**
* ionic_error_to_errno - Transform ionic_if errors to os errno
* @code: Ionic error number
*
* Return: Negative OS error number or zero
*/
int ionic_error_to_errno(enum ionic_status_code code);
/**
* ionic_request_rdma_reset - request reset or disable the device or lif
* @lif: Logical interface
*
* The reset is triggered asynchronously. It will wait until reset request
* completes or times out.
*/
void ionic_request_rdma_reset(struct ionic_lif *lif);
/**
* ionic_intr_alloc - Reserve a device interrupt
* @lif: Logical interface
* @intr: Reserved ionic interrupt structure
*
* Reserve an interrupt index and get irq number for that index.
*
* Return: zero or negative error status
*/
int ionic_intr_alloc(struct ionic_lif *lif, struct ionic_intr_info *intr);
/**
* ionic_intr_free - Release a device interrupt index
* @lif: Logical interface
* @intr: Interrupt index
*
* Mark the interrupt index unused so that it can be reserved again.
*/
void ionic_intr_free(struct ionic_lif *lif, int intr);
/**
* ionic_get_cmb - Reserve cmb pages
* @lif: Logical interface
* @pgid: First page index
* @pgaddr: First page bus addr (contiguous)
* @order: Log base two number of pages (PAGE_SIZE)
* @stride_log2: Size of stride to determine CMB pool
* @expdb: Will be set to true if this CMB region has expdb enabled
*
* Return: zero or negative error status
*/
int ionic_get_cmb(struct ionic_lif *lif, u32 *pgid, phys_addr_t *pgaddr,
int order, u8 stride_log2, bool *expdb);
/**
* ionic_put_cmb - Release cmb pages
* @lif: Logical interface
* @pgid: First page index
* @order: Log base two number of pages (PAGE_SIZE)
*/
void ionic_put_cmb(struct ionic_lif *lif, u32 pgid, int order);
#endif /* _IONIC_API_H_ */
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
#include <linux/kernel.h>
#include "ionic.h"
#include "ionic_lif.h"
#include "ionic_aux.h"
static DEFINE_IDA(aux_ida);
static void ionic_auxbus_release(struct device *dev)
{
struct ionic_aux_dev *ionic_adev;
ionic_adev = container_of(dev, struct ionic_aux_dev, adev.dev);
ida_free(&aux_ida, ionic_adev->adev.id);
kfree(ionic_adev);
}
int ionic_auxbus_register(struct ionic_lif *lif)
{
struct ionic_aux_dev *ionic_adev;
struct auxiliary_device *aux_dev;
int err, id;
if (!(le64_to_cpu(lif->ionic->ident.lif.capabilities) & IONIC_LIF_CAP_RDMA))
return 0;
ionic_adev = kzalloc_obj(*ionic_adev);
if (!ionic_adev)
return -ENOMEM;
aux_dev = &ionic_adev->adev;
id = ida_alloc(&aux_ida, GFP_KERNEL);
if (id < 0) {
dev_err(lif->ionic->dev, "Failed to allocate aux id: %d\n", id);
kfree(ionic_adev);
return id;
}
aux_dev->id = id;
aux_dev->name = "rdma";
aux_dev->dev.parent = &lif->ionic->pdev->dev;
aux_dev->dev.release = ionic_auxbus_release;
ionic_adev->lif = lif;
err = auxiliary_device_init(aux_dev);
if (err) {
dev_err(lif->ionic->dev, "Failed to initialize %s aux device: %d\n",
aux_dev->name, err);
ida_free(&aux_ida, id);
kfree(ionic_adev);
return err;
}
err = auxiliary_device_add(aux_dev);
if (err) {
dev_err(lif->ionic->dev, "Failed to add %s aux device: %d\n",
aux_dev->name, err);
auxiliary_device_uninit(aux_dev);
return err;
}
lif->ionic_adev = ionic_adev;
return 0;
}
void ionic_auxbus_unregister(struct ionic_lif *lif)
{
mutex_lock(&lif->adev_lock);
if (!lif->ionic_adev)
goto out;
auxiliary_device_delete(&lif->ionic_adev->adev);
auxiliary_device_uninit(&lif->ionic_adev->adev);
lif->ionic_adev = NULL;
out:
mutex_unlock(&lif->adev_lock);
}
void ionic_request_rdma_reset(struct ionic_lif *lif)
{
struct ionic *ionic = lif->ionic;
int err;
union ionic_dev_cmd cmd = {
.cmd.opcode = IONIC_CMD_RDMA_RESET_LIF,
.cmd.lif_index = cpu_to_le16(lif->index),
};
mutex_lock(&ionic->dev_cmd_lock);
ionic_dev_cmd_go(&ionic->idev, &cmd);
err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
mutex_unlock(&ionic->dev_cmd_lock);
if (err)
pr_warn("%s request_reset: error %d\n", __func__, err);
}
EXPORT_SYMBOL_NS(ionic_request_rdma_reset, "NET_IONIC");
@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
#ifndef _IONIC_AUX_H_
#define _IONIC_AUX_H_
int ionic_auxbus_register(struct ionic_lif *lif);
void ionic_auxbus_unregister(struct ionic_lif *lif);
#endif /* _IONIC_AUX_H_ */
@@ -0,0 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
#ifndef _IONIC_BUS_H_
#define _IONIC_BUS_H_
int ionic_bus_get_irq(struct ionic *ionic, unsigned int num);
const char *ionic_bus_info(struct ionic *ionic);
int ionic_bus_alloc_irq_vectors(struct ionic *ionic, unsigned int nintrs);
void ionic_bus_free_irq_vectors(struct ionic *ionic);
int ionic_bus_register_driver(void);
void ionic_bus_unregister_driver(void);
void __iomem *ionic_bus_map_dbpage(struct ionic *ionic, int page_num);
void ionic_bus_unmap_dbpage(struct ionic *ionic, void __iomem *page);
#endif /* _IONIC_BUS_H_ */
@@ -0,0 +1,540 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/pci.h>
#include "ionic.h"
#include "ionic_bus.h"
#include "ionic_lif.h"
#include "ionic_aux.h"
#include "ionic_debugfs.h"
/* Supported devices */
static const struct pci_device_id ionic_id_table[] = {
{ PCI_VDEVICE(PENSANDO, PCI_DEVICE_ID_PENSANDO_IONIC_ETH_PF) },
{ PCI_VDEVICE(PENSANDO, PCI_DEVICE_ID_PENSANDO_IONIC_ETH_VF) },
{ 0, } /* end of table */
};
MODULE_DEVICE_TABLE(pci, ionic_id_table);
int ionic_bus_get_irq(struct ionic *ionic, unsigned int num)
{
return pci_irq_vector(ionic->pdev, num);
}
const char *ionic_bus_info(struct ionic *ionic)
{
return pci_name(ionic->pdev);
}
int ionic_bus_alloc_irq_vectors(struct ionic *ionic, unsigned int nintrs)
{
return pci_alloc_irq_vectors(ionic->pdev, nintrs, nintrs,
PCI_IRQ_MSIX);
}
void ionic_bus_free_irq_vectors(struct ionic *ionic)
{
if (!ionic->nintrs)
return;
pci_free_irq_vectors(ionic->pdev);
}
static int ionic_map_bars(struct ionic *ionic)
{
struct pci_dev *pdev = ionic->pdev;
struct device *dev = ionic->dev;
struct ionic_dev_bar *bars;
unsigned int i, j;
bars = ionic->bars;
ionic->num_bars = 0;
for (i = 0, j = 0; i < IONIC_BARS_MAX; i++) {
if (!(pci_resource_flags(pdev, i) & IORESOURCE_MEM))
continue;
bars[j].len = pci_resource_len(pdev, i);
/* only map the whole bar 0 */
if (j > 0) {
bars[j].vaddr = NULL;
} else {
bars[j].vaddr = pci_iomap(pdev, i, bars[j].len);
if (!bars[j].vaddr) {
dev_err(dev,
"Cannot memory-map BAR %d, aborting\n",
i);
return -ENODEV;
}
}
bars[j].bus_addr = pci_resource_start(pdev, i);
bars[j].res_index = i;
ionic->num_bars++;
j++;
}
return 0;
}
static void ionic_unmap_bars(struct ionic *ionic)
{
struct ionic_dev_bar *bars = ionic->bars;
unsigned int i;
for (i = 0; i < IONIC_BARS_MAX; i++) {
if (bars[i].vaddr) {
iounmap(bars[i].vaddr);
bars[i].bus_addr = 0;
bars[i].vaddr = NULL;
bars[i].len = 0;
}
}
ionic->num_bars = 0;
}
void __iomem *ionic_bus_map_dbpage(struct ionic *ionic, int page_num)
{
return pci_iomap_range(ionic->pdev,
ionic->bars[IONIC_PCI_BAR_DBELL].res_index,
(u64)page_num << PAGE_SHIFT, PAGE_SIZE);
}
void ionic_bus_unmap_dbpage(struct ionic *ionic, void __iomem *page)
{
iounmap(page);
}
static void ionic_vf_dealloc_locked(struct ionic *ionic)
{
struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_STATSADDR };
struct ionic_vf *v;
int i;
if (!ionic->vfs)
return;
for (i = ionic->num_vfs - 1; i >= 0; i--) {
v = &ionic->vfs[i];
if (v->stats_pa) {
vfc.stats_pa = 0;
ionic_set_vf_config(ionic, i, &vfc);
dma_unmap_single(ionic->dev, v->stats_pa,
sizeof(v->stats), DMA_FROM_DEVICE);
v->stats_pa = 0;
}
}
kfree(ionic->vfs);
ionic->vfs = NULL;
ionic->num_vfs = 0;
}
static void ionic_vf_dealloc(struct ionic *ionic)
{
down_write(&ionic->vf_op_lock);
ionic_vf_dealloc_locked(ionic);
up_write(&ionic->vf_op_lock);
}
static int ionic_vf_alloc(struct ionic *ionic, int num_vfs)
{
struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_STATSADDR };
struct ionic_vf *v;
int err = 0;
int i;
down_write(&ionic->vf_op_lock);
ionic->vfs = kzalloc_objs(struct ionic_vf, num_vfs);
if (!ionic->vfs) {
err = -ENOMEM;
goto out;
}
for (i = 0; i < num_vfs; i++) {
v = &ionic->vfs[i];
v->stats_pa = dma_map_single(ionic->dev, &v->stats,
sizeof(v->stats), DMA_FROM_DEVICE);
if (dma_mapping_error(ionic->dev, v->stats_pa)) {
v->stats_pa = 0;
err = -ENODEV;
goto out;
}
ionic->num_vfs++;
/* ignore failures from older FW, we just won't get stats */
vfc.stats_pa = cpu_to_le64(v->stats_pa);
ionic_set_vf_config(ionic, i, &vfc);
}
out:
if (err)
ionic_vf_dealloc_locked(ionic);
up_write(&ionic->vf_op_lock);
return err;
}
static int ionic_sriov_configure(struct pci_dev *pdev, int num_vfs)
{
struct ionic *ionic = pci_get_drvdata(pdev);
struct device *dev = ionic->dev;
int ret = 0;
if (ionic->lif &&
test_bit(IONIC_LIF_F_FW_RESET, ionic->lif->state))
return -EBUSY;
if (num_vfs > 0) {
ret = pci_enable_sriov(pdev, num_vfs);
if (ret) {
dev_err(dev, "Cannot enable SRIOV: %d\n", ret);
goto out;
}
ret = ionic_vf_alloc(ionic, num_vfs);
if (ret) {
dev_err(dev, "Cannot alloc VFs: %d\n", ret);
pci_disable_sriov(pdev);
goto out;
}
ret = num_vfs;
} else {
pci_disable_sriov(pdev);
ionic_vf_dealloc(ionic);
}
out:
return ret;
}
static void ionic_clear_pci(struct ionic *ionic)
{
if (ionic->num_bars) {
ionic->idev.dev_info_regs = NULL;
ionic->idev.dev_cmd_regs = NULL;
ionic->idev.intr_status = NULL;
ionic->idev.intr_ctrl = NULL;
ionic_unmap_bars(ionic);
pci_release_regions(ionic->pdev);
}
if (pci_is_enabled(ionic->pdev))
pci_disable_device(ionic->pdev);
}
static int ionic_setup_one(struct ionic *ionic)
{
struct pci_dev *pdev = ionic->pdev;
struct device *dev = ionic->dev;
int err;
ionic_debugfs_add_dev(ionic);
/* Setup PCI device */
err = pci_enable_device_mem(pdev);
if (err) {
dev_err(dev, "Cannot enable PCI device: %d, aborting\n", err);
goto err_out_debugfs_del_dev;
}
err = pci_request_regions(pdev, IONIC_DRV_NAME);
if (err) {
dev_err(dev, "Cannot request PCI regions: %d, aborting\n", err);
goto err_out_clear_pci;
}
pcie_print_link_status(pdev);
err = ionic_map_bars(ionic);
if (err)
goto err_out_clear_pci;
/* Configure the device */
err = ionic_setup(ionic);
if (err) {
dev_err(dev, "Cannot setup device: %d, aborting\n", err);
goto err_out_clear_pci;
}
pci_set_master(pdev);
err = ionic_identify(ionic);
if (err) {
dev_err(dev, "Cannot identify device: %d, aborting\n", err);
goto err_out_teardown;
}
ionic_debugfs_add_ident(ionic);
ionic_map_cmb(ionic);
err = ionic_init(ionic);
if (err) {
dev_err(dev, "Cannot init device: %d, aborting\n", err);
goto err_out_teardown;
}
/* Configure the port */
err = ionic_port_identify(ionic);
if (err) {
dev_err(dev, "Cannot identify port: %d, aborting\n", err);
goto err_out_teardown;
}
err = ionic_port_init(ionic);
if (err) {
dev_err(dev, "Cannot init port: %d, aborting\n", err);
goto err_out_teardown;
}
return 0;
err_out_teardown:
ionic_dev_teardown(ionic);
err_out_clear_pci:
ionic_clear_pci(ionic);
err_out_debugfs_del_dev:
ionic_debugfs_del_dev(ionic);
return err;
}
static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct device *dev = &pdev->dev;
struct ionic *ionic;
int num_vfs;
int err;
ionic = ionic_devlink_alloc(dev);
if (!ionic)
return -ENOMEM;
ionic->pdev = pdev;
ionic->dev = dev;
pci_set_drvdata(pdev, ionic);
mutex_init(&ionic->dev_cmd_lock);
/* Query system for DMA addressing limitation for the device. */
err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(IONIC_ADDR_LEN));
if (err) {
dev_err(dev, "Unable to obtain 64-bit DMA for consistent allocations, aborting. err=%d\n",
err);
goto err_out;
}
#ifdef CONFIG_PPC64
/* Ensure MSI/MSI-X interrupts lie within addressable physical memory */
pdev->msi_addr_mask = DMA_BIT_MASK(32);
#endif
err = ionic_setup_one(ionic);
if (err)
goto err_out;
/* Allocate and init the LIF */
err = ionic_lif_size(ionic);
if (err) {
dev_err(dev, "Cannot size LIF: %d, aborting\n", err);
goto err_out_pci;
}
err = ionic_lif_alloc(ionic);
if (err) {
dev_err(dev, "Cannot allocate LIF: %d, aborting\n", err);
goto err_out_free_irqs;
}
err = ionic_lif_init(ionic->lif);
if (err) {
dev_err(dev, "Cannot init LIF: %d, aborting\n", err);
goto err_out_free_lifs;
}
init_rwsem(&ionic->vf_op_lock);
num_vfs = pci_num_vf(pdev);
if (num_vfs) {
dev_info(dev, "%d VFs found already enabled\n", num_vfs);
err = ionic_vf_alloc(ionic, num_vfs);
if (err)
dev_err(dev, "Cannot enable existing VFs: %d\n", err);
}
err = ionic_devlink_register(ionic);
if (err) {
dev_err(dev, "Cannot register devlink: %d\n", err);
goto err_out_deinit_lifs;
}
err = ionic_lif_register(ionic->lif);
if (err) {
dev_err(dev, "Cannot register LIF: %d, aborting\n", err);
goto err_out_deregister_devlink;
}
ionic_auxbus_register(ionic->lif);
mod_timer(&ionic->watchdog_timer,
round_jiffies(jiffies + ionic->watchdog_period));
ionic_queue_doorbell_check(ionic, IONIC_NAPI_DEADLINE);
return 0;
err_out_deregister_devlink:
ionic_devlink_unregister(ionic);
err_out_deinit_lifs:
ionic_vf_dealloc(ionic);
ionic_lif_deinit(ionic->lif);
err_out_free_lifs:
ionic_lif_free(ionic->lif);
ionic->lif = NULL;
err_out_free_irqs:
ionic_bus_free_irq_vectors(ionic);
err_out_pci:
ionic_dev_teardown(ionic);
ionic_clear_pci(ionic);
ionic_debugfs_del_dev(ionic);
err_out:
mutex_destroy(&ionic->dev_cmd_lock);
ionic_devlink_free(ionic);
return err;
}
static void ionic_remove(struct pci_dev *pdev)
{
struct ionic *ionic = pci_get_drvdata(pdev);
timer_shutdown_sync(&ionic->watchdog_timer);
if (ionic->lif) {
cancel_work_sync(&ionic->lif->deferred.work);
/* prevent adminq cmds if already known as down */
if (test_and_clear_bit(IONIC_LIF_F_FW_RESET, ionic->lif->state))
set_bit(IONIC_LIF_F_FW_STOPPING, ionic->lif->state);
if (ionic->lif->doorbell_wa)
cancel_delayed_work_sync(&ionic->doorbell_check_dwork);
ionic_auxbus_unregister(ionic->lif);
ionic_lif_unregister(ionic->lif);
ionic_devlink_unregister(ionic);
ionic_lif_deinit(ionic->lif);
ionic_lif_free(ionic->lif);
ionic->lif = NULL;
ionic_bus_free_irq_vectors(ionic);
}
ionic_port_reset(ionic);
ionic_reset(ionic);
ionic_dev_teardown(ionic);
ionic_clear_pci(ionic);
ionic_debugfs_del_dev(ionic);
mutex_destroy(&ionic->dev_cmd_lock);
ionic_devlink_free(ionic);
}
static void ionic_reset_prepare(struct pci_dev *pdev)
{
struct ionic *ionic = pci_get_drvdata(pdev);
struct ionic_lif *lif = ionic->lif;
dev_dbg(ionic->dev, "%s: device stopping\n", __func__);
set_bit(IONIC_LIF_F_FW_RESET, lif->state);
timer_delete_sync(&ionic->watchdog_timer);
cancel_work_sync(&lif->deferred.work);
ionic_auxbus_unregister(ionic->lif);
mutex_lock(&lif->queue_lock);
ionic_stop_queues_reconfig(lif);
ionic_txrx_free(lif);
ionic_lif_deinit(lif);
ionic_qcqs_free(lif);
ionic_debugfs_del_lif(lif);
mutex_unlock(&lif->queue_lock);
ionic_dev_teardown(ionic);
ionic_clear_pci(ionic);
ionic_debugfs_del_dev(ionic);
}
static void ionic_reset_done(struct pci_dev *pdev)
{
struct ionic *ionic = pci_get_drvdata(pdev);
struct ionic_lif *lif = ionic->lif;
int err;
err = ionic_setup_one(ionic);
if (err)
goto err_out;
ionic_debugfs_add_sizes(ionic);
ionic_debugfs_add_lif(ionic->lif);
err = ionic_restart_lif(lif);
if (err)
goto err_out;
mod_timer(&ionic->watchdog_timer, jiffies + 1);
err_out:
dev_dbg(ionic->dev, "%s: device recovery %s\n",
__func__, err ? "failed" : "done");
}
static pci_ers_result_t ionic_pci_error_detected(struct pci_dev *pdev,
pci_channel_state_t error)
{
if (error == pci_channel_io_frozen) {
ionic_reset_prepare(pdev);
return PCI_ERS_RESULT_NEED_RESET;
}
return PCI_ERS_RESULT_NONE;
}
static void ionic_pci_error_resume(struct pci_dev *pdev)
{
struct ionic *ionic = pci_get_drvdata(pdev);
struct ionic_lif *lif = ionic->lif;
if (lif && test_bit(IONIC_LIF_F_FW_RESET, lif->state))
pci_reset_function_locked(pdev);
}
static const struct pci_error_handlers ionic_err_handler = {
/* FLR handling */
.reset_prepare = ionic_reset_prepare,
.reset_done = ionic_reset_done,
/* PCI bus error detected on this device */
.error_detected = ionic_pci_error_detected,
.resume = ionic_pci_error_resume,
};
static struct pci_driver ionic_driver = {
.name = IONIC_DRV_NAME,
.id_table = ionic_id_table,
.probe = ionic_probe,
.remove = ionic_remove,
.sriov_configure = ionic_sriov_configure,
.err_handler = &ionic_err_handler
};
int ionic_bus_register_driver(void)
{
return pci_register_driver(&ionic_driver);
}
void ionic_bus_unregister_driver(void)
{
pci_unregister_driver(&ionic_driver);
}
@@ -0,0 +1,303 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
#include <linux/pci.h>
#include <linux/netdevice.h>
#include "ionic.h"
#include "ionic_bus.h"
#include "ionic_lif.h"
#include "ionic_debugfs.h"
#ifdef CONFIG_DEBUG_FS
static struct dentry *ionic_dir;
void ionic_debugfs_create(void)
{
ionic_dir = debugfs_create_dir(IONIC_DRV_NAME, NULL);
}
void ionic_debugfs_destroy(void)
{
debugfs_remove_recursive(ionic_dir);
}
void ionic_debugfs_add_dev(struct ionic *ionic)
{
ionic->dentry = debugfs_create_dir(ionic_bus_info(ionic), ionic_dir);
}
void ionic_debugfs_del_dev(struct ionic *ionic)
{
debugfs_remove_recursive(ionic->dentry);
ionic->dentry = NULL;
}
static int identity_show(struct seq_file *seq, void *v)
{
struct ionic *ionic = seq->private;
struct ionic_identity *ident;
ident = &ionic->ident;
seq_printf(seq, "nlifs: %d\n", ident->dev.nlifs);
seq_printf(seq, "nintrs: %d\n", ident->dev.nintrs);
seq_printf(seq, "ndbpgs_per_lif: %d\n", ident->dev.ndbpgs_per_lif);
seq_printf(seq, "intr_coal_mult: %d\n", ident->dev.intr_coal_mult);
seq_printf(seq, "intr_coal_div: %d\n", ident->dev.intr_coal_div);
seq_printf(seq, "max_ucast_filters: %d\n", ident->lif.eth.max_ucast_filters);
seq_printf(seq, "max_mcast_filters: %d\n", ident->lif.eth.max_mcast_filters);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(identity);
void ionic_debugfs_add_ident(struct ionic *ionic)
{
debugfs_create_file("identity", 0400, ionic->dentry,
ionic, &identity_fops);
}
void ionic_debugfs_add_sizes(struct ionic *ionic)
{
debugfs_create_u32("nlifs", 0400, ionic->dentry,
(u32 *)&ionic->ident.dev.nlifs);
debugfs_create_u32("nintrs", 0400, ionic->dentry, &ionic->nintrs);
debugfs_create_u32("ntxqs_per_lif", 0400, ionic->dentry,
(u32 *)&ionic->ident.lif.eth.config.queue_count[IONIC_QTYPE_TXQ]);
debugfs_create_u32("nrxqs_per_lif", 0400, ionic->dentry,
(u32 *)&ionic->ident.lif.eth.config.queue_count[IONIC_QTYPE_RXQ]);
}
static int q_tail_show(struct seq_file *seq, void *v)
{
struct ionic_queue *q = seq->private;
seq_printf(seq, "%d\n", q->tail_idx);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(q_tail);
static int q_head_show(struct seq_file *seq, void *v)
{
struct ionic_queue *q = seq->private;
seq_printf(seq, "%d\n", q->head_idx);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(q_head);
static int cq_tail_show(struct seq_file *seq, void *v)
{
struct ionic_cq *cq = seq->private;
seq_printf(seq, "%d\n", cq->tail_idx);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(cq_tail);
static const struct debugfs_reg32 intr_ctrl_regs[] = {
{ .name = "coal_init", .offset = 0, },
{ .name = "mask", .offset = 4, },
{ .name = "credits", .offset = 8, },
{ .name = "mask_on_assert", .offset = 12, },
{ .name = "coal_timer", .offset = 16, },
};
void ionic_debugfs_add_qcq(struct ionic_lif *lif, struct ionic_qcq *qcq)
{
struct dentry *qcq_dentry, *q_dentry, *cq_dentry;
struct ionic_dev *idev = &lif->ionic->idev;
struct dentry *intr_dentry, *stats_dentry;
struct debugfs_regset32 *intr_ctrl_regset;
struct ionic_intr_info *intr = &qcq->intr;
struct debugfs_blob_wrapper *desc_blob;
struct device *dev = lif->ionic->dev;
struct ionic_queue *q = &qcq->q;
struct ionic_cq *cq = &qcq->cq;
qcq_dentry = debugfs_create_dir(q->name, lif->dentry);
if (IS_ERR(qcq_dentry))
return;
qcq->dentry = qcq_dentry;
debugfs_create_x64("q_base_pa", 0400, qcq_dentry, &qcq->q_base_pa);
debugfs_create_x32("q_size", 0400, qcq_dentry, &qcq->q_size);
debugfs_create_x64("cq_base_pa", 0400, qcq_dentry, &qcq->cq_base_pa);
debugfs_create_x32("cq_size", 0400, qcq_dentry, &qcq->cq_size);
debugfs_create_x64("sg_base_pa", 0400, qcq_dentry, &qcq->sg_base_pa);
debugfs_create_x32("sg_size", 0400, qcq_dentry, &qcq->sg_size);
q_dentry = debugfs_create_dir("q", qcq->dentry);
debugfs_create_u32("index", 0400, q_dentry, &q->index);
debugfs_create_u32("num_descs", 0400, q_dentry, &q->num_descs);
debugfs_create_u32("desc_size", 0400, q_dentry, &q->desc_size);
debugfs_create_u32("pid", 0400, q_dentry, &q->pid);
debugfs_create_u32("qid", 0400, q_dentry, &q->hw_index);
debugfs_create_u32("qtype", 0400, q_dentry, &q->hw_type);
debugfs_create_u64("drop", 0400, q_dentry, &q->drop);
debugfs_create_file("tail", 0400, q_dentry, q, &q_tail_fops);
debugfs_create_file("head", 0400, q_dentry, q, &q_head_fops);
desc_blob = devm_kzalloc(dev, sizeof(*desc_blob), GFP_KERNEL);
if (!desc_blob)
return;
desc_blob->data = q->base;
desc_blob->size = (unsigned long)q->num_descs * q->desc_size;
debugfs_create_blob("desc_blob", 0400, q_dentry, desc_blob);
if (qcq->flags & IONIC_QCQ_F_SG) {
desc_blob = devm_kzalloc(dev, sizeof(*desc_blob), GFP_KERNEL);
if (!desc_blob)
return;
desc_blob->data = q->sg_base;
desc_blob->size = (unsigned long)q->num_descs * q->sg_desc_size;
debugfs_create_blob("sg_desc_blob", 0400, q_dentry,
desc_blob);
}
cq_dentry = debugfs_create_dir("cq", qcq->dentry);
debugfs_create_x64("base_pa", 0400, cq_dentry, &cq->base_pa);
debugfs_create_u32("num_descs", 0400, cq_dentry, &cq->num_descs);
debugfs_create_u32("desc_size", 0400, cq_dentry, &cq->desc_size);
debugfs_create_bool("done_color", 0400, cq_dentry, &cq->done_color);
debugfs_create_file("tail", 0400, cq_dentry, cq, &cq_tail_fops);
desc_blob = devm_kzalloc(dev, sizeof(*desc_blob), GFP_KERNEL);
if (!desc_blob)
return;
desc_blob->data = cq->base;
desc_blob->size = (unsigned long)cq->num_descs * cq->desc_size;
debugfs_create_blob("desc_blob", 0400, cq_dentry, desc_blob);
if (qcq->flags & IONIC_QCQ_F_INTR) {
intr_dentry = debugfs_create_dir("intr", qcq->dentry);
debugfs_create_u32("index", 0400, intr_dentry,
&intr->index);
debugfs_create_u32("vector", 0400, intr_dentry,
&intr->vector);
debugfs_create_u32("dim_coal_hw", 0400, intr_dentry,
&intr->dim_coal_hw);
intr_ctrl_regset = devm_kzalloc(dev, sizeof(*intr_ctrl_regset),
GFP_KERNEL);
if (!intr_ctrl_regset)
return;
intr_ctrl_regset->regs = intr_ctrl_regs;
intr_ctrl_regset->nregs = ARRAY_SIZE(intr_ctrl_regs);
intr_ctrl_regset->base = &idev->intr_ctrl[intr->index];
debugfs_create_regset32("intr_ctrl", 0400, intr_dentry,
intr_ctrl_regset);
}
if (qcq->flags & IONIC_QCQ_F_NOTIFYQ) {
stats_dentry = debugfs_create_dir("notifyblock", qcq->dentry);
debugfs_create_u64("eid", 0400, stats_dentry,
(u64 *)&lif->info->status.eid);
debugfs_create_u16("link_status", 0400, stats_dentry,
(u16 *)&lif->info->status.link_status);
debugfs_create_u32("link_speed", 0400, stats_dentry,
(u32 *)&lif->info->status.link_speed);
debugfs_create_u16("link_down_count", 0400, stats_dentry,
(u16 *)&lif->info->status.link_down_count);
}
}
static int netdev_show(struct seq_file *seq, void *v)
{
struct net_device *netdev = seq->private;
seq_printf(seq, "%s\n", netdev_name(netdev));
return 0;
}
DEFINE_SHOW_ATTRIBUTE(netdev);
static int lif_filters_show(struct seq_file *seq, void *v)
{
struct ionic_lif *lif = seq->private;
struct ionic_rx_filter *f;
struct hlist_head *head;
struct hlist_node *tmp;
unsigned int i;
seq_puts(seq, "id flow state type filter\n");
spin_lock_bh(&lif->rx_filters.lock);
for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) {
head = &lif->rx_filters.by_id[i];
hlist_for_each_entry_safe(f, tmp, head, by_id) {
switch (le16_to_cpu(f->cmd.match)) {
case IONIC_RX_FILTER_MATCH_VLAN:
seq_printf(seq, "0x%04x 0x%08x 0x%02x vlan 0x%04x\n",
f->filter_id, f->flow_id, f->state,
le16_to_cpu(f->cmd.vlan.vlan));
break;
case IONIC_RX_FILTER_MATCH_MAC:
seq_printf(seq, "0x%04x 0x%08x 0x%02x mac %pM\n",
f->filter_id, f->flow_id, f->state,
f->cmd.mac.addr);
break;
case IONIC_RX_FILTER_MATCH_MAC_VLAN:
seq_printf(seq, "0x%04x 0x%08x 0x%02x macvl 0x%04x %pM\n",
f->filter_id, f->flow_id, f->state,
le16_to_cpu(f->cmd.vlan.vlan),
f->cmd.mac.addr);
break;
case IONIC_RX_FILTER_STEER_PKTCLASS:
seq_printf(seq, "0x%04x 0x%08x 0x%02x rxstr 0x%llx\n",
f->filter_id, f->flow_id, f->state,
le64_to_cpu(f->cmd.pkt_class));
break;
}
}
}
spin_unlock_bh(&lif->rx_filters.lock);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(lif_filters);
void ionic_debugfs_add_lif(struct ionic_lif *lif)
{
struct dentry *lif_dentry;
lif_dentry = debugfs_create_dir(lif->name, lif->ionic->dentry);
if (IS_ERR_OR_NULL(lif_dentry))
return;
lif->dentry = lif_dentry;
debugfs_create_file("netdev", 0400, lif->dentry,
lif->netdev, &netdev_fops);
debugfs_create_file("filters", 0400, lif->dentry,
lif, &lif_filters_fops);
}
void ionic_debugfs_del_lif(struct ionic_lif *lif)
{
if (!lif->dentry)
return;
debugfs_remove_recursive(lif->dentry);
lif->dentry = NULL;
}
void ionic_debugfs_del_qcq(struct ionic_qcq *qcq)
{
debugfs_remove_recursive(qcq->dentry);
qcq->dentry = NULL;
}
#endif
@@ -0,0 +1,34 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
#ifndef _IONIC_DEBUGFS_H_
#define _IONIC_DEBUGFS_H_
#include <linux/debugfs.h>
#ifdef CONFIG_DEBUG_FS
void ionic_debugfs_create(void);
void ionic_debugfs_destroy(void);
void ionic_debugfs_add_dev(struct ionic *ionic);
void ionic_debugfs_del_dev(struct ionic *ionic);
void ionic_debugfs_add_ident(struct ionic *ionic);
void ionic_debugfs_add_sizes(struct ionic *ionic);
void ionic_debugfs_add_lif(struct ionic_lif *lif);
void ionic_debugfs_add_qcq(struct ionic_lif *lif, struct ionic_qcq *qcq);
void ionic_debugfs_del_lif(struct ionic_lif *lif);
void ionic_debugfs_del_qcq(struct ionic_qcq *qcq);
#else
static inline void ionic_debugfs_create(void) { }
static inline void ionic_debugfs_destroy(void) { }
static inline void ionic_debugfs_add_dev(struct ionic *ionic) { }
static inline void ionic_debugfs_del_dev(struct ionic *ionic) { }
static inline void ionic_debugfs_add_ident(struct ionic *ionic) { }
static inline void ionic_debugfs_add_sizes(struct ionic *ionic) { }
static inline void ionic_debugfs_add_lif(struct ionic_lif *lif) { }
static inline void ionic_debugfs_add_qcq(struct ionic_lif *lif, struct ionic_qcq *qcq) { }
static inline void ionic_debugfs_del_lif(struct ionic_lif *lif) { }
static inline void ionic_debugfs_del_qcq(struct ionic_qcq *qcq) { }
#endif
#endif /* _IONIC_DEBUGFS_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,403 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
#ifndef _IONIC_DEV_H_
#define _IONIC_DEV_H_
#include <linux/atomic.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include <linux/skbuff.h>
#include <linux/bpf_trace.h>
#include "ionic_if.h"
#include "ionic_regs.h"
#include "ionic_api.h"
#define IONIC_MAX_TX_DESC 8192
#define IONIC_MAX_RX_DESC 16384
#define IONIC_MIN_TXRX_DESC 64
#define IONIC_DEF_TXRX_DESC 1024
#define IONIC_RX_FILL_THRESHOLD 16
#define IONIC_RX_FILL_DIV 8
#define IONIC_TSO_DESCS_NEEDED 44 /* 64K TSO @1500B */
#define IONIC_LIFS_MAX 1024
#define IONIC_WATCHDOG_SECS 5
#define IONIC_ITR_COAL_USEC_DEFAULT 64
#define IONIC_DEV_CMD_REG_VERSION 1
#define IONIC_DEV_INFO_REG_COUNT 32
#define IONIC_DEV_CMD_REG_COUNT 32
#define IONIC_NAPI_DEADLINE (HZ) /* 1 sec */
#define IONIC_ADMIN_DOORBELL_DEADLINE (HZ / 2) /* 500ms */
#define IONIC_TX_DOORBELL_DEADLINE (HZ / 100) /* 10ms */
#define IONIC_RX_MIN_DOORBELL_DEADLINE (HZ / 100) /* 10ms */
#define IONIC_RX_MAX_DOORBELL_DEADLINE (HZ * 4) /* 4s */
#define IONIC_EXPDB_64B_WQE_LG2 6
#define IONIC_EXPDB_128B_WQE_LG2 7
#define IONIC_EXPDB_256B_WQE_LG2 8
#define IONIC_EXPDB_512B_WQE_LG2 9
struct ionic_dev_bar {
void __iomem *vaddr;
phys_addr_t bus_addr;
unsigned long len;
int res_index;
};
#ifndef __CHECKER__
/* Registers */
static_assert(sizeof(struct ionic_intr) == 32);
static_assert(sizeof(struct ionic_doorbell) == 8);
static_assert(sizeof(struct ionic_intr_status) == 8);
static_assert(sizeof(union ionic_dev_regs) == 4096);
static_assert(sizeof(union ionic_dev_info_regs) == 2048);
static_assert(sizeof(union ionic_dev_cmd_regs) == 2048);
static_assert(sizeof(struct ionic_lif_stats) == 1024);
static_assert(sizeof(struct ionic_admin_cmd) == 64);
static_assert(sizeof(struct ionic_admin_comp) == 16);
static_assert(sizeof(struct ionic_nop_cmd) == 64);
static_assert(sizeof(struct ionic_nop_comp) == 16);
/* Device commands */
static_assert(sizeof(struct ionic_dev_identify_cmd) == 64);
static_assert(sizeof(struct ionic_dev_identify_comp) == 16);
static_assert(sizeof(struct ionic_dev_init_cmd) == 64);
static_assert(sizeof(struct ionic_dev_init_comp) == 16);
static_assert(sizeof(struct ionic_dev_reset_cmd) == 64);
static_assert(sizeof(struct ionic_dev_reset_comp) == 16);
static_assert(sizeof(struct ionic_dev_getattr_cmd) == 64);
static_assert(sizeof(struct ionic_dev_getattr_comp) == 16);
static_assert(sizeof(struct ionic_dev_setattr_cmd) == 64);
static_assert(sizeof(struct ionic_dev_setattr_comp) == 16);
static_assert(sizeof(struct ionic_lif_setphc_cmd) == 64);
/* Port commands */
static_assert(sizeof(struct ionic_port_identify_cmd) == 64);
static_assert(sizeof(struct ionic_port_identify_comp) == 16);
static_assert(sizeof(struct ionic_port_init_cmd) == 64);
static_assert(sizeof(struct ionic_port_init_comp) == 16);
static_assert(sizeof(struct ionic_port_reset_cmd) == 64);
static_assert(sizeof(struct ionic_port_reset_comp) == 16);
static_assert(sizeof(struct ionic_port_getattr_cmd) == 64);
static_assert(sizeof(struct ionic_port_getattr_comp) == 16);
static_assert(sizeof(struct ionic_port_setattr_cmd) == 64);
static_assert(sizeof(struct ionic_port_setattr_comp) == 16);
/* LIF commands */
static_assert(sizeof(struct ionic_lif_init_cmd) == 64);
static_assert(sizeof(struct ionic_lif_init_comp) == 16);
static_assert(sizeof(struct ionic_lif_reset_cmd) == 64);
static_assert(sizeof(ionic_lif_reset_comp) == 16);
static_assert(sizeof(struct ionic_lif_getattr_cmd) == 64);
static_assert(sizeof(struct ionic_lif_getattr_comp) == 16);
static_assert(sizeof(struct ionic_lif_setattr_cmd) == 64);
static_assert(sizeof(struct ionic_lif_setattr_comp) == 16);
static_assert(sizeof(struct ionic_q_init_cmd) == 64);
static_assert(sizeof(struct ionic_q_init_comp) == 16);
static_assert(sizeof(struct ionic_q_control_cmd) == 64);
static_assert(sizeof(ionic_q_control_comp) == 16);
static_assert(sizeof(struct ionic_q_identify_cmd) == 64);
static_assert(sizeof(struct ionic_q_identify_comp) == 16);
static_assert(sizeof(struct ionic_rx_mode_set_cmd) == 64);
static_assert(sizeof(ionic_rx_mode_set_comp) == 16);
static_assert(sizeof(struct ionic_rx_filter_add_cmd) == 64);
static_assert(sizeof(struct ionic_rx_filter_add_comp) == 16);
static_assert(sizeof(struct ionic_rx_filter_del_cmd) == 64);
static_assert(sizeof(ionic_rx_filter_del_comp) == 16);
/* RDMA commands */
static_assert(sizeof(struct ionic_rdma_reset_cmd) == 64);
static_assert(sizeof(struct ionic_rdma_queue_cmd) == 64);
/* Events */
static_assert(sizeof(struct ionic_notifyq_cmd) == 4);
static_assert(sizeof(union ionic_notifyq_comp) == 64);
static_assert(sizeof(struct ionic_notifyq_event) == 64);
static_assert(sizeof(struct ionic_link_change_event) == 64);
static_assert(sizeof(struct ionic_reset_event) == 64);
static_assert(sizeof(struct ionic_heartbeat_event) == 64);
static_assert(sizeof(struct ionic_log_event) == 64);
/* I/O */
static_assert(sizeof(struct ionic_txq_desc) == 16);
static_assert(sizeof(struct ionic_txq_sg_desc) == 128);
static_assert(sizeof(struct ionic_txq_sg_desc_v1) == 256);
static_assert(sizeof(struct ionic_txq_comp) == 16);
static_assert(sizeof(struct ionic_rxq_desc) == 16);
static_assert(sizeof(struct ionic_rxq_sg_desc) == 128);
static_assert(sizeof(struct ionic_rxq_comp) == 16);
static_assert(sizeof(struct ionic_rxq_comp) == sizeof(struct ionic_txq_comp));
/* SR/IOV */
static_assert(sizeof(struct ionic_vf_setattr_cmd) == 64);
static_assert(sizeof(struct ionic_vf_setattr_comp) == 16);
static_assert(sizeof(struct ionic_vf_getattr_cmd) == 64);
static_assert(sizeof(struct ionic_vf_getattr_comp) == 16);
static_assert(sizeof(struct ionic_vf_ctrl_cmd) == 64);
static_assert(sizeof(struct ionic_vf_ctrl_comp) == 16);
#endif /* __CHECKER__ */
struct ionic_devinfo {
u8 asic_type;
u8 asic_rev;
char fw_version[IONIC_DEVINFO_FWVERS_BUFLEN + 1];
char serial_num[IONIC_DEVINFO_SERIAL_BUFLEN + 1];
};
struct ionic_dev {
union ionic_dev_info_regs __iomem *dev_info_regs;
union ionic_dev_cmd_regs __iomem *dev_cmd_regs;
struct ionic_hwstamp_regs __iomem *hwstamp_regs;
atomic_long_t last_check_time;
unsigned long last_hb_time;
u32 last_fw_hb;
bool fw_hb_ready;
bool fw_status_ready;
u8 fw_generation;
u8 opcode;
u64 __iomem *db_pages;
dma_addr_t phy_db_pages;
struct ionic_intr __iomem *intr_ctrl;
u64 __iomem *intr_status;
struct mutex cmb_inuse_lock; /* for cmb_inuse */
unsigned long *cmb_inuse;
dma_addr_t phy_cmb_pages;
u32 cmb_npages;
dma_addr_t phy_cmb_expdb64_pages;
dma_addr_t phy_cmb_expdb128_pages;
dma_addr_t phy_cmb_expdb256_pages;
dma_addr_t phy_cmb_expdb512_pages;
u32 port_info_sz;
struct ionic_port_info *port_info;
dma_addr_t port_info_pa;
struct ionic_port_extra_stats port_extra_stats_cache;
bool link_down_count_init;
u16 link_down_count_last;
u32 link_down_count_total;
struct ionic_devinfo dev_info;
};
struct ionic_queue;
struct ionic_qcq;
#define IONIC_MAX_BUF_LEN ((u16)-1)
#define IONIC_PAGE_SIZE MIN(PAGE_SIZE, IONIC_MAX_BUF_LEN)
#define IONIC_XDP_MAX_LINEAR_MTU (IONIC_PAGE_SIZE - \
(VLAN_ETH_HLEN + \
XDP_PACKET_HEADROOM + \
SKB_DATA_ALIGN(sizeof(struct skb_shared_info))))
struct ionic_buf_info {
struct page *page;
dma_addr_t dma_addr;
u32 page_offset;
u32 len;
};
#define IONIC_TX_MAX_FRAGS (1 + IONIC_TX_MAX_SG_ELEMS_V1)
#define IONIC_RX_MAX_FRAGS (1 + IONIC_RX_MAX_SG_ELEMS)
struct ionic_tx_desc_info {
unsigned int bytes;
unsigned int nbufs;
struct sk_buff *skb;
struct xdp_frame *xdpf;
enum xdp_action act;
struct ionic_buf_info bufs[MAX_SKB_FRAGS + 1];
};
struct ionic_rx_desc_info {
unsigned int nbufs;
struct ionic_buf_info bufs[IONIC_RX_MAX_FRAGS];
};
struct ionic_admin_desc_info {
void *ctx;
};
#define IONIC_QUEUE_NAME_MAX_SZ 16
struct ionic_queue {
struct device *dev;
struct ionic_lif *lif;
union {
void *info;
struct ionic_tx_desc_info *tx_info;
struct ionic_rx_desc_info *rx_info;
struct ionic_admin_desc_info *admin_info;
};
u64 dbval;
unsigned long dbell_deadline;
unsigned long dbell_jiffies;
u16 head_idx;
u16 tail_idx;
unsigned int index;
unsigned int num_descs;
unsigned int max_sg_elems;
u64 features;
unsigned int hw_type;
bool xdp_flush;
union {
void *base;
struct ionic_txq_desc *txq;
struct ionic_rxq_desc *rxq;
struct ionic_admin_cmd *adminq;
};
union {
void *sg_base;
struct ionic_txq_sg_desc *txq_sgl;
struct ionic_txq_sg_desc_v1 *txq_sgl_v1;
struct ionic_rxq_sg_desc *rxq_sgl;
};
struct xdp_rxq_info *xdp_rxq_info;
struct bpf_prog *xdp_prog;
struct page_pool *page_pool;
struct ionic_queue *partner;
union {
void __iomem *cmb_base;
struct ionic_txq_desc __iomem *cmb_txq;
struct ionic_rxq_desc __iomem *cmb_rxq;
};
unsigned int type;
unsigned int hw_index;
dma_addr_t base_pa;
dma_addr_t cmb_base_pa;
dma_addr_t sg_base_pa;
u64 drop;
unsigned int desc_size;
unsigned int sg_desc_size;
unsigned int pid;
char name[IONIC_QUEUE_NAME_MAX_SZ];
} ____cacheline_aligned_in_smp;
struct ionic_cq {
struct ionic_lif *lif;
struct ionic_queue *bound_q;
struct ionic_intr_info *bound_intr;
u16 tail_idx;
bool done_color;
unsigned int num_descs;
unsigned int desc_size;
void *base;
dma_addr_t base_pa;
struct ionic_dev *idev;
} ____cacheline_aligned_in_smp;
struct ionic;
static inline void ionic_intr_init(struct ionic_dev *idev,
struct ionic_intr_info *intr,
unsigned long index)
{
ionic_intr_clean(idev->intr_ctrl, index);
intr->index = index;
}
static inline unsigned int ionic_q_space_avail(struct ionic_queue *q)
{
unsigned int avail = q->tail_idx;
if (q->head_idx >= avail)
avail += q->num_descs - q->head_idx - 1;
else
avail -= q->head_idx + 1;
return avail;
}
static inline bool ionic_q_has_space(struct ionic_queue *q, unsigned int want)
{
return ionic_q_space_avail(q) >= want;
}
void ionic_init_devinfo(struct ionic *ionic);
int ionic_dev_setup(struct ionic *ionic);
void ionic_dev_teardown(struct ionic *ionic);
void ionic_dev_cmd_go(struct ionic_dev *idev, union ionic_dev_cmd *cmd);
u8 ionic_dev_cmd_status(struct ionic_dev *idev);
bool ionic_dev_cmd_done(struct ionic_dev *idev);
void ionic_dev_cmd_comp(struct ionic_dev *idev, union ionic_dev_cmd_comp *comp);
void ionic_dev_cmd_identify(struct ionic_dev *idev, u8 ver);
void ionic_dev_cmd_init(struct ionic_dev *idev);
void ionic_dev_cmd_reset(struct ionic_dev *idev);
void ionic_dev_cmd_port_identify(struct ionic_dev *idev);
void ionic_dev_cmd_port_init(struct ionic_dev *idev);
void ionic_dev_cmd_port_reset(struct ionic_dev *idev);
void ionic_dev_cmd_port_state(struct ionic_dev *idev, u8 state);
void ionic_dev_cmd_port_speed(struct ionic_dev *idev, u32 speed);
void ionic_dev_cmd_port_autoneg(struct ionic_dev *idev, u8 an_enable);
void ionic_dev_cmd_port_fec(struct ionic_dev *idev, u8 fec_type);
void ionic_dev_cmd_port_pause(struct ionic_dev *idev, u8 pause_type);
int ionic_set_vf_config(struct ionic *ionic, int vf,
struct ionic_vf_setattr_cmd *vfc);
void ionic_dev_cmd_queue_identify(struct ionic_dev *idev,
u16 lif_type, u8 qtype, u8 qver);
void ionic_vf_start(struct ionic *ionic);
void ionic_dev_cmd_lif_identify(struct ionic_dev *idev, u8 type, u8 ver);
void ionic_dev_cmd_lif_init(struct ionic_dev *idev, u16 lif_index,
dma_addr_t addr);
void ionic_dev_cmd_lif_reset(struct ionic_dev *idev, u16 lif_index);
void ionic_dev_cmd_adminq_init(struct ionic_dev *idev, struct ionic_qcq *qcq,
u16 lif_index, u16 intr_index);
int ionic_db_page_num(struct ionic_lif *lif, int pid);
void ionic_dev_cmd_discover_cmb(struct ionic_dev *idev);
void ionic_map_cmb(struct ionic *ionic);
int ionic_cq_init(struct ionic_lif *lif, struct ionic_cq *cq,
struct ionic_intr_info *intr,
unsigned int num_descs, size_t desc_size);
void ionic_cq_map(struct ionic_cq *cq, void *base, dma_addr_t base_pa);
void ionic_cq_bind(struct ionic_cq *cq, struct ionic_queue *q);
typedef bool (*ionic_cq_cb)(struct ionic_cq *cq);
typedef void (*ionic_cq_done_cb)(void *done_arg);
unsigned int ionic_cq_service(struct ionic_cq *cq, unsigned int work_to_do,
ionic_cq_cb cb, ionic_cq_done_cb done_cb,
void *done_arg);
unsigned int ionic_tx_cq_service(struct ionic_cq *cq,
unsigned int work_to_do,
bool in_napi);
int ionic_q_init(struct ionic_lif *lif, struct ionic_dev *idev,
struct ionic_queue *q, unsigned int index, const char *name,
unsigned int num_descs, size_t desc_size,
size_t sg_desc_size, unsigned int pid);
void ionic_q_post(struct ionic_queue *q, bool ring_doorbell);
bool ionic_q_is_posted(struct ionic_queue *q, unsigned int pos);
int ionic_heartbeat_check(struct ionic *ionic);
bool ionic_is_fw_running(struct ionic_dev *idev);
void ionic_doorbell_napi_work(struct work_struct *work);
void ionic_queue_doorbell_check(struct ionic *ionic, int delay);
bool ionic_adminq_poke_doorbell(struct ionic_queue *q);
bool ionic_txq_poke_doorbell(struct ionic_queue *q);
bool ionic_rxq_poke_doorbell(struct ionic_queue *q);
void ionic_reset_link_down_count(struct ionic_dev *idev);
#endif /* _IONIC_DEV_H_ */
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
#include <linux/module.h>
#include <linux/netdevice.h>
#include "ionic.h"
#include "ionic_bus.h"
#include "ionic_lif.h"
#include "ionic_devlink.h"
static int ionic_dl_flash_update(struct devlink *dl,
struct devlink_flash_update_params *params,
struct netlink_ext_ack *extack)
{
struct ionic *ionic = devlink_priv(dl);
return ionic_firmware_update(ionic->lif, params->fw, extack);
}
static int ionic_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
struct netlink_ext_ack *extack)
{
struct ionic *ionic = devlink_priv(dl);
struct ionic_dev *idev = &ionic->idev;
char buf[16];
int err = 0;
err = devlink_info_version_running_put(req,
DEVLINK_INFO_VERSION_GENERIC_FW,
idev->dev_info.fw_version);
if (err)
return err;
snprintf(buf, sizeof(buf), "0x%x", idev->dev_info.asic_type);
err = devlink_info_version_fixed_put(req,
DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
buf);
if (err)
return err;
snprintf(buf, sizeof(buf), "0x%x", idev->dev_info.asic_rev);
err = devlink_info_version_fixed_put(req,
DEVLINK_INFO_VERSION_GENERIC_ASIC_REV,
buf);
if (err)
return err;
err = devlink_info_serial_number_put(req, idev->dev_info.serial_num);
return err;
}
static const struct devlink_ops ionic_dl_ops = {
.info_get = ionic_dl_info_get,
.flash_update = ionic_dl_flash_update,
};
struct ionic *ionic_devlink_alloc(struct device *dev)
{
struct devlink *dl;
dl = devlink_alloc(&ionic_dl_ops, sizeof(struct ionic), dev);
if (!dl)
return NULL;
return devlink_priv(dl);
}
void ionic_devlink_free(struct ionic *ionic)
{
struct devlink *dl = priv_to_devlink(ionic);
devlink_free(dl);
}
int ionic_devlink_register(struct ionic *ionic)
{
struct devlink *dl = priv_to_devlink(ionic);
struct devlink_port_attrs attrs = {};
int err;
attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
devlink_port_attrs_set(&ionic->dl_port, &attrs);
err = devlink_port_register(dl, &ionic->dl_port, 0);
if (err) {
dev_err(ionic->dev, "devlink_port_register failed: %d\n", err);
return err;
}
SET_NETDEV_DEVLINK_PORT(ionic->lif->netdev, &ionic->dl_port);
devlink_register(dl);
return 0;
}
void ionic_devlink_unregister(struct ionic *ionic)
{
struct devlink *dl = priv_to_devlink(ionic);
devlink_unregister(dl);
devlink_port_unregister(&ionic->dl_port);
}
@@ -0,0 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
#ifndef _IONIC_DEVLINK_H_
#define _IONIC_DEVLINK_H_
#include <net/devlink.h>
int ionic_firmware_update(struct ionic_lif *lif, const struct firmware *fw,
struct netlink_ext_ack *extack);
struct ionic *ionic_devlink_alloc(struct device *dev);
void ionic_devlink_free(struct ionic *ionic);
int ionic_devlink_register(struct ionic *ionic);
void ionic_devlink_unregister(struct ionic *ionic);
#endif /* _IONIC_DEVLINK_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
#ifndef _IONIC_ETHTOOL_H_
#define _IONIC_ETHTOOL_H_
void ionic_ethtool_set_ops(struct net_device *netdev);
#endif /* _IONIC_ETHTOOL_H_ */
@@ -0,0 +1,201 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2020 Pensando Systems, Inc */
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/firmware.h>
#include "ionic.h"
#include "ionic_dev.h"
#include "ionic_lif.h"
#include "ionic_devlink.h"
/* The worst case wait for the install activity is about 25 minutes when
* installing a new CPLD, which is very seldom. Normal is about 30-35
* seconds. Since the driver can't tell if a CPLD update will happen we
* set the timeout for the ugly case.
*/
#define IONIC_FW_INSTALL_TIMEOUT (25 * 60)
#define IONIC_FW_SELECT_TIMEOUT 30
/* Number of periodic log updates during fw file download */
#define IONIC_FW_INTERVAL_FRACTION 32
static void ionic_dev_cmd_firmware_download(struct ionic_dev *idev, u64 addr,
u32 offset, u32 length)
{
union ionic_dev_cmd cmd = {
.fw_download.opcode = IONIC_CMD_FW_DOWNLOAD,
.fw_download.offset = cpu_to_le32(offset),
.fw_download.addr = cpu_to_le64(addr),
.fw_download.length = cpu_to_le32(length),
};
ionic_dev_cmd_go(idev, &cmd);
}
static void ionic_dev_cmd_firmware_install(struct ionic_dev *idev)
{
union ionic_dev_cmd cmd = {
.fw_control.opcode = IONIC_CMD_FW_CONTROL,
.fw_control.oper = IONIC_FW_INSTALL_ASYNC
};
ionic_dev_cmd_go(idev, &cmd);
}
static void ionic_dev_cmd_firmware_activate(struct ionic_dev *idev, u8 slot)
{
union ionic_dev_cmd cmd = {
.fw_control.opcode = IONIC_CMD_FW_CONTROL,
.fw_control.oper = IONIC_FW_ACTIVATE_ASYNC,
.fw_control.slot = slot
};
ionic_dev_cmd_go(idev, &cmd);
}
static int ionic_fw_status_long_wait(struct ionic *ionic,
const char *label,
unsigned long timeout,
u8 fw_cmd,
struct netlink_ext_ack *extack)
{
union ionic_dev_cmd cmd = {
.fw_control.opcode = IONIC_CMD_FW_CONTROL,
.fw_control.oper = fw_cmd,
};
unsigned long start_time;
unsigned long end_time;
int err;
start_time = jiffies;
end_time = start_time + (timeout * HZ);
do {
mutex_lock(&ionic->dev_cmd_lock);
ionic_dev_cmd_go(&ionic->idev, &cmd);
err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
mutex_unlock(&ionic->dev_cmd_lock);
msleep(20);
} while (time_before(jiffies, end_time) && (err == -EAGAIN || err == -ETIMEDOUT));
if (err == -EAGAIN || err == -ETIMEDOUT) {
NL_SET_ERR_MSG_MOD(extack, "Firmware wait timed out");
dev_err(ionic->dev, "DEV_CMD firmware wait %s timed out\n", label);
} else if (err) {
NL_SET_ERR_MSG_MOD(extack, "Firmware wait failed");
}
return err;
}
int ionic_firmware_update(struct ionic_lif *lif, const struct firmware *fw,
struct netlink_ext_ack *extack)
{
struct ionic_dev *idev = &lif->ionic->idev;
struct net_device *netdev = lif->netdev;
struct ionic *ionic = lif->ionic;
union ionic_dev_cmd_comp comp;
u32 buf_sz, copy_sz, offset;
struct devlink *dl;
int next_interval;
int err = 0;
u8 fw_slot;
netdev_info(netdev, "Installing firmware\n");
dl = priv_to_devlink(ionic);
devlink_flash_update_status_notify(dl, "Preparing to flash", NULL, 0, 0);
if (!idev->dev_cmd_regs) {
err = -ENXIO;
goto err_out;
}
buf_sz = sizeof(idev->dev_cmd_regs->data);
netdev_dbg(netdev,
"downloading firmware - size %d part_sz %d nparts %lu\n",
(int)fw->size, buf_sz, DIV_ROUND_UP(fw->size, buf_sz));
offset = 0;
next_interval = 0;
while (offset < fw->size) {
if (offset >= next_interval) {
devlink_flash_update_status_notify(dl, "Downloading", NULL,
offset, fw->size);
next_interval = offset + (fw->size / IONIC_FW_INTERVAL_FRACTION);
}
copy_sz = min_t(unsigned int, buf_sz, fw->size - offset);
mutex_lock(&ionic->dev_cmd_lock);
memcpy_toio(&idev->dev_cmd_regs->data, fw->data + offset, copy_sz);
ionic_dev_cmd_firmware_download(idev,
offsetof(union ionic_dev_cmd_regs, data),
offset, copy_sz);
err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
mutex_unlock(&ionic->dev_cmd_lock);
if (err) {
netdev_err(netdev,
"download failed offset 0x%x addr 0x%lx len 0x%x\n",
offset, offsetof(union ionic_dev_cmd_regs, data),
copy_sz);
NL_SET_ERR_MSG_MOD(extack, "Segment download failed");
goto err_out;
}
offset += copy_sz;
}
devlink_flash_update_status_notify(dl, "Downloading", NULL,
fw->size, fw->size);
devlink_flash_update_timeout_notify(dl, "Installing", NULL,
IONIC_FW_INSTALL_TIMEOUT);
mutex_lock(&ionic->dev_cmd_lock);
ionic_dev_cmd_firmware_install(idev);
err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
ionic_dev_cmd_comp(idev, (union ionic_dev_cmd_comp *)&comp);
fw_slot = comp.fw_control.slot;
mutex_unlock(&ionic->dev_cmd_lock);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Failed to start firmware install");
goto err_out;
}
err = ionic_fw_status_long_wait(ionic, "Installing",
IONIC_FW_INSTALL_TIMEOUT,
IONIC_FW_INSTALL_STATUS,
extack);
if (err)
goto err_out;
devlink_flash_update_timeout_notify(dl, "Selecting", NULL,
IONIC_FW_SELECT_TIMEOUT);
mutex_lock(&ionic->dev_cmd_lock);
ionic_dev_cmd_firmware_activate(idev, fw_slot);
err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
mutex_unlock(&ionic->dev_cmd_lock);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Failed to start firmware select");
goto err_out;
}
err = ionic_fw_status_long_wait(ionic, "Selecting",
IONIC_FW_SELECT_TIMEOUT,
IONIC_FW_ACTIVATE_STATUS,
extack);
if (err)
goto err_out;
netdev_info(netdev, "Firmware update completed\n");
err_out:
if (err)
devlink_flash_update_status_notify(dl, "Flash failed", NULL, 0, 0);
else
devlink_flash_update_status_notify(dl, "Flash done", NULL, 0, 0);
return err;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,413 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
#ifndef _IONIC_LIF_H_
#define _IONIC_LIF_H_
#include <linux/ptp_clock_kernel.h>
#include <linux/timecounter.h>
#include <linux/net_tstamp.h>
#include <linux/dim.h>
#include <linux/pci.h>
#include "ionic_rx_filter.h"
#include "ionic_api.h"
#define IONIC_ADMINQ_LENGTH 16 /* must be a power of two */
#define IONIC_NOTIFYQ_LENGTH 64 /* must be a power of two */
#define ADD_ADDR true
#define DEL_ADDR false
#define CAN_SLEEP true
#define CAN_NOT_SLEEP false
#define IONIC_RX_COPYBREAK_DEFAULT 256
#define IONIC_TX_BUDGET_DEFAULT 256
struct ionic_tx_stats {
u64 pkts;
u64 bytes;
u64 csum_none;
u64 csum;
u64 tso;
u64 tso_bytes;
u64 frags;
u64 vlan_inserted;
u64 clean;
u64 linearize;
u64 crc32_csum;
u64 dma_map_err;
u64 hwstamp_valid;
u64 hwstamp_invalid;
u64 xdp_frames;
};
struct ionic_rx_stats {
u64 pkts;
u64 bytes;
u64 csum_none;
u64 csum_complete;
u64 dropped;
u64 vlan_stripped;
u64 csum_error;
u64 dma_map_err;
u64 alloc_err;
u64 hwstamp_valid;
u64 hwstamp_invalid;
u64 xdp_drop;
u64 xdp_aborted;
u64 xdp_pass;
u64 xdp_tx;
u64 xdp_redirect;
};
#define IONIC_QCQ_F_INITED BIT(0)
#define IONIC_QCQ_F_SG BIT(1)
#define IONIC_QCQ_F_INTR BIT(2)
#define IONIC_QCQ_F_TX_STATS BIT(3)
#define IONIC_QCQ_F_RX_STATS BIT(4)
#define IONIC_QCQ_F_NOTIFYQ BIT(5)
#define IONIC_QCQ_F_CMB_RINGS BIT(6)
struct ionic_qcq {
void *q_base;
dma_addr_t q_base_pa;
u32 q_size;
u32 cq_size;
void *cq_base;
dma_addr_t cq_base_pa;
void *sg_base;
dma_addr_t sg_base_pa;
u32 sg_size;
unsigned int flags;
void __iomem *cmb_q_base;
phys_addr_t cmb_q_base_pa;
u32 cmb_q_size;
u32 cmb_pgid;
u32 cmb_order;
struct dim dim;
struct ionic_queue q;
struct ionic_cq cq;
struct napi_struct napi;
struct ionic_intr_info intr;
struct work_struct doorbell_napi_work;
struct dentry *dentry;
};
#define q_to_qcq(q) container_of(q, struct ionic_qcq, q)
#define q_to_tx_stats(q) (&(q)->lif->txqstats[(q)->index])
#define q_to_rx_stats(q) (&(q)->lif->rxqstats[(q)->index])
#define napi_to_qcq(napi) container_of(napi, struct ionic_qcq, napi)
#define napi_to_cq(napi) (&napi_to_qcq(napi)->cq)
enum ionic_deferred_work_type {
IONIC_DW_TYPE_RX_MODE,
IONIC_DW_TYPE_LINK_STATUS,
IONIC_DW_TYPE_LIF_RESET,
};
struct ionic_deferred_work {
struct list_head list;
enum ionic_deferred_work_type type;
union {
u8 addr[ETH_ALEN];
u8 fw_status;
};
};
struct ionic_deferred {
spinlock_t lock; /* lock for deferred work list */
struct list_head list;
struct work_struct work;
};
struct ionic_lif_sw_stats {
u64 tx_packets;
u64 tx_bytes;
u64 rx_packets;
u64 rx_bytes;
u64 tx_tso;
u64 tx_tso_bytes;
u64 tx_csum_none;
u64 tx_csum;
u64 rx_csum_none;
u64 rx_csum_complete;
u64 rx_csum_error;
u64 tx_hwstamp_valid;
u64 tx_hwstamp_invalid;
u64 rx_hwstamp_valid;
u64 rx_hwstamp_invalid;
u64 hw_tx_dropped;
u64 hw_rx_dropped;
u64 hw_rx_over_errors;
u64 hw_rx_missed_errors;
u64 hw_tx_aborted_errors;
u64 xdp_drop;
u64 xdp_aborted;
u64 xdp_pass;
u64 xdp_tx;
u64 xdp_redirect;
u64 xdp_frames;
};
enum ionic_lif_state_flags {
IONIC_LIF_F_INITED,
IONIC_LIF_F_UP,
IONIC_LIF_F_LINK_CHECK_REQUESTED,
IONIC_LIF_F_FILTER_SYNC_NEEDED,
IONIC_LIF_F_FW_RESET,
IONIC_LIF_F_FW_STOPPING,
IONIC_LIF_F_SPLIT_INTR,
IONIC_LIF_F_BROKEN,
IONIC_LIF_F_TX_DIM_INTR,
IONIC_LIF_F_RX_DIM_INTR,
IONIC_LIF_F_CMB_TX_RINGS,
IONIC_LIF_F_CMB_RX_RINGS,
/* leave this as last */
IONIC_LIF_F_STATE_SIZE
};
struct ionic_qtype_info {
u8 version;
u8 supported;
u64 features;
u16 desc_sz;
u16 comp_sz;
u16 sg_desc_sz;
u16 max_sg_elems;
u16 sg_desc_stride;
};
struct ionic_phc;
#define IONIC_LIF_NAME_MAX_SZ 32
struct ionic_lif {
struct net_device *netdev;
DECLARE_BITMAP(state, IONIC_LIF_F_STATE_SIZE);
struct ionic *ionic;
unsigned int index;
unsigned int hw_index;
struct mutex queue_lock; /* lock for queue structures */
struct mutex config_lock; /* lock for config actions */
spinlock_t adminq_lock; /* lock for AdminQ operations */
struct ionic_qcq *adminqcq;
struct ionic_qcq *notifyqcq;
struct ionic_qcq **txqcqs;
struct ionic_qcq *hwstamp_txq;
struct ionic_tx_stats *txqstats;
struct ionic_qcq **rxqcqs;
struct ionic_qcq *hwstamp_rxq;
struct ionic_rx_stats *rxqstats;
struct ionic_deferred deferred;
struct work_struct tx_timeout_work;
u64 last_eid;
unsigned int kern_pid;
u64 __iomem *kern_dbpage;
unsigned int neqs;
unsigned int nxqs;
unsigned int ntxq_descs;
unsigned int nrxq_descs;
u64 rxq_features;
u64 hw_features;
u16 rx_copybreak;
u16 rx_mode;
bool registered;
bool doorbell_wa;
u16 lif_type;
unsigned int nmcast;
unsigned int nucast;
unsigned int nvlans;
unsigned int max_vlans;
char name[IONIC_LIF_NAME_MAX_SZ];
union ionic_lif_identity *identity;
struct ionic_lif_info *info;
dma_addr_t info_pa;
u32 info_sz;
struct ionic_qtype_info qtype_info[IONIC_QTYPE_MAX];
struct ionic_aux_dev *ionic_adev;
struct mutex adev_lock; /* lock for aux_dev actions */
u8 rss_hash_key[IONIC_RSS_HASH_KEY_SIZE];
u8 *rss_ind_tbl;
dma_addr_t rss_ind_tbl_pa;
u32 rss_ind_tbl_sz;
u16 rss_types;
struct ionic_rx_filters rx_filters;
u32 rx_coalesce_usecs; /* what the user asked for */
u32 rx_coalesce_hw; /* what the hw is using */
u32 tx_coalesce_usecs; /* what the user asked for */
u32 tx_coalesce_hw; /* what the hw is using */
unsigned int dbid_count;
struct ionic_phc *phc;
struct dentry *dentry;
struct bpf_prog *xdp_prog;
};
struct ionic_phc {
spinlock_t lock; /* lock for cc and tc */
struct cyclecounter cc;
struct timecounter tc;
struct mutex config_lock; /* lock for ts_config */
struct kernel_hwtstamp_config ts_config;
u64 ts_config_rx_filt;
u32 ts_config_tx_mode;
u32 init_cc_mult;
long aux_work_delay;
struct ptp_clock_info ptp_info;
struct ptp_clock *ptp;
struct ionic_lif *lif;
};
struct ionic_queue_params {
unsigned int nxqs;
unsigned int ntxq_descs;
unsigned int nrxq_descs;
u64 rxq_features;
struct bpf_prog *xdp_prog;
bool intr_split;
bool cmb_tx;
bool cmb_rx;
};
static inline void ionic_init_queue_params(struct ionic_lif *lif,
struct ionic_queue_params *qparam)
{
qparam->nxqs = lif->nxqs;
qparam->ntxq_descs = lif->ntxq_descs;
qparam->nrxq_descs = lif->nrxq_descs;
qparam->rxq_features = lif->rxq_features;
qparam->xdp_prog = lif->xdp_prog;
qparam->intr_split = test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state);
qparam->cmb_tx = test_bit(IONIC_LIF_F_CMB_TX_RINGS, lif->state);
qparam->cmb_rx = test_bit(IONIC_LIF_F_CMB_RX_RINGS, lif->state);
}
static inline void ionic_set_queue_params(struct ionic_lif *lif,
struct ionic_queue_params *qparam)
{
lif->nxqs = qparam->nxqs;
lif->ntxq_descs = qparam->ntxq_descs;
lif->nrxq_descs = qparam->nrxq_descs;
lif->rxq_features = qparam->rxq_features;
if (qparam->intr_split)
set_bit(IONIC_LIF_F_SPLIT_INTR, lif->state);
else
clear_bit(IONIC_LIF_F_SPLIT_INTR, lif->state);
if (qparam->cmb_tx)
set_bit(IONIC_LIF_F_CMB_TX_RINGS, lif->state);
else
clear_bit(IONIC_LIF_F_CMB_TX_RINGS, lif->state);
if (qparam->cmb_rx)
set_bit(IONIC_LIF_F_CMB_RX_RINGS, lif->state);
else
clear_bit(IONIC_LIF_F_CMB_RX_RINGS, lif->state);
}
static inline u32 ionic_coal_usec_to_hw(struct ionic *ionic, u32 usecs)
{
u32 mult = le32_to_cpu(ionic->ident.dev.intr_coal_mult);
u32 div = le32_to_cpu(ionic->ident.dev.intr_coal_div);
/* Div-by-zero should never be an issue, but check anyway */
if (!div || !mult)
return 0;
/* Round up in case usecs is close to the next hw unit */
usecs += (div / mult) >> 1;
/* Convert from usecs to device units */
return (usecs * mult) / div;
}
static inline bool ionic_txq_hwstamp_enabled(struct ionic_queue *q)
{
return q->features & IONIC_TXQ_F_HWSTAMP;
}
void ionic_link_status_check_request(struct ionic_lif *lif, bool can_sleep);
void ionic_get_stats64(struct net_device *netdev,
struct rtnl_link_stats64 *ns);
void ionic_lif_deferred_enqueue(struct ionic_lif *lif,
struct ionic_deferred_work *work);
int ionic_lif_alloc(struct ionic *ionic);
int ionic_lif_init(struct ionic_lif *lif);
void ionic_lif_free(struct ionic_lif *lif);
void ionic_lif_deinit(struct ionic_lif *lif);
int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr);
int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr);
void ionic_stop_queues_reconfig(struct ionic_lif *lif);
void ionic_txrx_free(struct ionic_lif *lif);
void ionic_qcqs_free(struct ionic_lif *lif);
int ionic_restart_lif(struct ionic_lif *lif);
int ionic_lif_register(struct ionic_lif *lif);
void ionic_lif_unregister(struct ionic_lif *lif);
int ionic_lif_identify(struct ionic *ionic, u8 lif_type,
union ionic_lif_identity *lif_ident);
int ionic_lif_size(struct ionic *ionic);
#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
void ionic_lif_hwstamp_replay(struct ionic_lif *lif);
void ionic_lif_hwstamp_recreate_queues(struct ionic_lif *lif);
int ionic_hwstamp_set(struct net_device *netdev,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack);
int ionic_hwstamp_get(struct net_device *netdev,
struct kernel_hwtstamp_config *config);
ktime_t ionic_lif_phc_ktime(struct ionic_lif *lif, u64 counter);
void ionic_lif_register_phc(struct ionic_lif *lif);
void ionic_lif_unregister_phc(struct ionic_lif *lif);
void ionic_lif_alloc_phc(struct ionic_lif *lif);
void ionic_lif_free_phc(struct ionic_lif *lif);
#else
static inline void ionic_lif_hwstamp_replay(struct ionic_lif *lif) {}
static inline void ionic_lif_hwstamp_recreate_queues(struct ionic_lif *lif) {}
static inline int ionic_hwstamp_set(struct net_device *netdev,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack)
{
return -EOPNOTSUPP;
}
static inline int ionic_hwstamp_get(struct net_device *netdev,
struct kernel_hwtstamp_config *config)
{
return -EOPNOTSUPP;
}
static inline ktime_t ionic_lif_phc_ktime(struct ionic_lif *lif, u64 counter)
{
return ns_to_ktime(0);
}
static inline void ionic_lif_register_phc(struct ionic_lif *lif) {}
static inline void ionic_lif_unregister_phc(struct ionic_lif *lif) {}
static inline void ionic_lif_alloc_phc(struct ionic_lif *lif) {}
static inline void ionic_lif_free_phc(struct ionic_lif *lif) {}
#endif
int ionic_lif_create_hwstamp_txq(struct ionic_lif *lif);
int ionic_lif_create_hwstamp_rxq(struct ionic_lif *lif);
int ionic_lif_config_hwstamp_rxq_all(struct ionic_lif *lif, bool rx_all);
int ionic_lif_set_hwstamp_txmode(struct ionic_lif *lif, u16 txstamp_mode);
int ionic_lif_set_hwstamp_rxfilt(struct ionic_lif *lif, u64 pkt_class);
int ionic_lif_rss_config(struct ionic_lif *lif, u16 types,
const u8 *key, const u32 *indir);
void ionic_lif_rx_mode(struct ionic_lif *lif);
int ionic_reconfigure_queues(struct ionic_lif *lif,
struct ionic_queue_params *qparam);
#endif /* _IONIC_LIF_H_ */
@@ -0,0 +1,810 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
#include <linux/printk.h>
#include <linux/dynamic_debug.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/utsname.h>
#include <generated/utsrelease.h>
#include <linux/ctype.h>
#include "ionic.h"
#include "ionic_bus.h"
#include "ionic_lif.h"
#include "ionic_debugfs.h"
MODULE_DESCRIPTION(IONIC_DRV_DESCRIPTION);
MODULE_AUTHOR("Shannon Nelson <shannon.nelson@amd.com>");
MODULE_LICENSE("GPL");
static const char *ionic_error_to_str(enum ionic_status_code code)
{
switch (code) {
case IONIC_RC_SUCCESS:
return "IONIC_RC_SUCCESS";
case IONIC_RC_EVERSION:
return "IONIC_RC_EVERSION";
case IONIC_RC_EOPCODE:
return "IONIC_RC_EOPCODE";
case IONIC_RC_EIO:
return "IONIC_RC_EIO";
case IONIC_RC_EPERM:
return "IONIC_RC_EPERM";
case IONIC_RC_EQID:
return "IONIC_RC_EQID";
case IONIC_RC_EQTYPE:
return "IONIC_RC_EQTYPE";
case IONIC_RC_ENOENT:
return "IONIC_RC_ENOENT";
case IONIC_RC_EINTR:
return "IONIC_RC_EINTR";
case IONIC_RC_EAGAIN:
return "IONIC_RC_EAGAIN";
case IONIC_RC_ENOMEM:
return "IONIC_RC_ENOMEM";
case IONIC_RC_EFAULT:
return "IONIC_RC_EFAULT";
case IONIC_RC_EBUSY:
return "IONIC_RC_EBUSY";
case IONIC_RC_EEXIST:
return "IONIC_RC_EEXIST";
case IONIC_RC_EINVAL:
return "IONIC_RC_EINVAL";
case IONIC_RC_ENOSPC:
return "IONIC_RC_ENOSPC";
case IONIC_RC_ERANGE:
return "IONIC_RC_ERANGE";
case IONIC_RC_BAD_ADDR:
return "IONIC_RC_BAD_ADDR";
case IONIC_RC_DEV_CMD:
return "IONIC_RC_DEV_CMD";
case IONIC_RC_ENOSUPP:
return "IONIC_RC_ENOSUPP";
case IONIC_RC_ERROR:
return "IONIC_RC_ERROR";
case IONIC_RC_ERDMA:
return "IONIC_RC_ERDMA";
case IONIC_RC_EBAD_FW:
return "IONIC_RC_EBAD_FW";
default:
return "IONIC_RC_UNKNOWN";
}
}
int ionic_error_to_errno(enum ionic_status_code code)
{
switch (code) {
case IONIC_RC_SUCCESS:
return 0;
case IONIC_RC_EVERSION:
case IONIC_RC_EQTYPE:
case IONIC_RC_EQID:
case IONIC_RC_EINVAL:
return -EINVAL;
case IONIC_RC_ENOSUPP:
return -EOPNOTSUPP;
case IONIC_RC_EPERM:
return -EPERM;
case IONIC_RC_ENOENT:
return -ENOENT;
case IONIC_RC_EAGAIN:
return -EAGAIN;
case IONIC_RC_ENOMEM:
return -ENOMEM;
case IONIC_RC_EFAULT:
return -EFAULT;
case IONIC_RC_EBUSY:
return -EBUSY;
case IONIC_RC_EEXIST:
return -EEXIST;
case IONIC_RC_ENOSPC:
return -ENOSPC;
case IONIC_RC_ERANGE:
return -ERANGE;
case IONIC_RC_BAD_ADDR:
return -EFAULT;
case IONIC_RC_EOPCODE:
case IONIC_RC_EINTR:
case IONIC_RC_DEV_CMD:
case IONIC_RC_ERROR:
case IONIC_RC_ERDMA:
case IONIC_RC_EIO:
default:
return -EIO;
}
}
EXPORT_SYMBOL_NS(ionic_error_to_errno, "NET_IONIC");
static const char *ionic_opcode_to_str(enum ionic_cmd_opcode opcode)
{
switch (opcode) {
case IONIC_CMD_NOP:
return "IONIC_CMD_NOP";
case IONIC_CMD_INIT:
return "IONIC_CMD_INIT";
case IONIC_CMD_RESET:
return "IONIC_CMD_RESET";
case IONIC_CMD_IDENTIFY:
return "IONIC_CMD_IDENTIFY";
case IONIC_CMD_GETATTR:
return "IONIC_CMD_GETATTR";
case IONIC_CMD_SETATTR:
return "IONIC_CMD_SETATTR";
case IONIC_CMD_PORT_IDENTIFY:
return "IONIC_CMD_PORT_IDENTIFY";
case IONIC_CMD_PORT_INIT:
return "IONIC_CMD_PORT_INIT";
case IONIC_CMD_PORT_RESET:
return "IONIC_CMD_PORT_RESET";
case IONIC_CMD_PORT_GETATTR:
return "IONIC_CMD_PORT_GETATTR";
case IONIC_CMD_PORT_SETATTR:
return "IONIC_CMD_PORT_SETATTR";
case IONIC_CMD_LIF_INIT:
return "IONIC_CMD_LIF_INIT";
case IONIC_CMD_LIF_RESET:
return "IONIC_CMD_LIF_RESET";
case IONIC_CMD_LIF_IDENTIFY:
return "IONIC_CMD_LIF_IDENTIFY";
case IONIC_CMD_LIF_SETATTR:
return "IONIC_CMD_LIF_SETATTR";
case IONIC_CMD_LIF_GETATTR:
return "IONIC_CMD_LIF_GETATTR";
case IONIC_CMD_LIF_SETPHC:
return "IONIC_CMD_LIF_SETPHC";
case IONIC_CMD_RX_MODE_SET:
return "IONIC_CMD_RX_MODE_SET";
case IONIC_CMD_RX_FILTER_ADD:
return "IONIC_CMD_RX_FILTER_ADD";
case IONIC_CMD_RX_FILTER_DEL:
return "IONIC_CMD_RX_FILTER_DEL";
case IONIC_CMD_Q_IDENTIFY:
return "IONIC_CMD_Q_IDENTIFY";
case IONIC_CMD_Q_INIT:
return "IONIC_CMD_Q_INIT";
case IONIC_CMD_Q_CONTROL:
return "IONIC_CMD_Q_CONTROL";
case IONIC_CMD_RDMA_RESET_LIF:
return "IONIC_CMD_RDMA_RESET_LIF";
case IONIC_CMD_RDMA_CREATE_EQ:
return "IONIC_CMD_RDMA_CREATE_EQ";
case IONIC_CMD_RDMA_CREATE_CQ:
return "IONIC_CMD_RDMA_CREATE_CQ";
case IONIC_CMD_RDMA_CREATE_ADMINQ:
return "IONIC_CMD_RDMA_CREATE_ADMINQ";
case IONIC_CMD_FW_DOWNLOAD:
return "IONIC_CMD_FW_DOWNLOAD";
case IONIC_CMD_FW_CONTROL:
return "IONIC_CMD_FW_CONTROL";
case IONIC_CMD_FW_DOWNLOAD_V1:
return "IONIC_CMD_FW_DOWNLOAD_V1";
case IONIC_CMD_FW_CONTROL_V1:
return "IONIC_CMD_FW_CONTROL_V1";
case IONIC_CMD_VF_GETATTR:
return "IONIC_CMD_VF_GETATTR";
case IONIC_CMD_VF_SETATTR:
return "IONIC_CMD_VF_SETATTR";
default:
return "DEVCMD_UNKNOWN";
}
}
static void ionic_adminq_flush(struct ionic_lif *lif)
{
struct ionic_admin_desc_info *desc_info;
struct ionic_admin_cmd *desc;
unsigned long irqflags;
struct ionic_queue *q;
spin_lock_irqsave(&lif->adminq_lock, irqflags);
if (!lif->adminqcq) {
spin_unlock_irqrestore(&lif->adminq_lock, irqflags);
return;
}
q = &lif->adminqcq->q;
while (q->tail_idx != q->head_idx) {
desc = &q->adminq[q->tail_idx];
desc_info = &q->admin_info[q->tail_idx];
memset(desc, 0, sizeof(union ionic_adminq_cmd));
desc_info->ctx = NULL;
q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1);
}
spin_unlock_irqrestore(&lif->adminq_lock, irqflags);
}
void ionic_adminq_netdev_err_print(struct ionic_lif *lif, u8 opcode,
u8 status, int err)
{
const char *stat_str;
stat_str = (err == -ETIMEDOUT) ? "TIMEOUT" :
ionic_error_to_str(status);
netdev_err(lif->netdev, "%s (%d) failed: %s (%d)\n",
ionic_opcode_to_str(opcode), opcode, stat_str, err);
}
static int ionic_adminq_check_err(struct ionic_lif *lif,
struct ionic_admin_ctx *ctx,
const bool timeout,
const bool do_msg)
{
int err = 0;
if (ctx->comp.comp.status || timeout) {
err = timeout ? -ETIMEDOUT :
ionic_error_to_errno(ctx->comp.comp.status);
if (do_msg)
ionic_adminq_netdev_err_print(lif, ctx->cmd.cmd.opcode,
ctx->comp.comp.status, err);
if (timeout)
ionic_adminq_flush(lif);
}
return err;
}
bool ionic_notifyq_service(struct ionic_cq *cq)
{
struct ionic_deferred_work *work;
union ionic_notifyq_comp *comp;
struct net_device *netdev;
struct ionic_queue *q;
struct ionic_lif *lif;
u64 eid;
comp = &((union ionic_notifyq_comp *)cq->base)[cq->tail_idx];
q = cq->bound_q;
lif = q->admin_info[0].ctx;
netdev = lif->netdev;
eid = le64_to_cpu(comp->event.eid);
/* Have we run out of new completions to process? */
if ((s64)(eid - lif->last_eid) <= 0)
return false;
lif->last_eid = eid;
dev_dbg(lif->ionic->dev, "notifyq event:\n");
dynamic_hex_dump("event ", DUMP_PREFIX_OFFSET, 16, 1,
comp, sizeof(*comp), true);
switch (le16_to_cpu(comp->event.ecode)) {
case IONIC_EVENT_LINK_CHANGE:
ionic_link_status_check_request(lif, CAN_NOT_SLEEP);
break;
case IONIC_EVENT_RESET:
if (lif->ionic->idev.fw_status_ready &&
!test_bit(IONIC_LIF_F_FW_RESET, lif->state) &&
!test_and_set_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) {
work = kzalloc_obj(*work, GFP_ATOMIC);
if (!work) {
netdev_err(lif->netdev, "Reset event dropped\n");
clear_bit(IONIC_LIF_F_FW_STOPPING, lif->state);
} else {
work->type = IONIC_DW_TYPE_LIF_RESET;
ionic_lif_deferred_enqueue(lif, work);
}
}
break;
default:
netdev_warn(netdev, "Notifyq event ecode=%d eid=%lld\n",
comp->event.ecode, eid);
break;
}
return true;
}
bool ionic_adminq_service(struct ionic_cq *cq)
{
struct ionic_admin_desc_info *desc_info;
struct ionic_queue *q = cq->bound_q;
struct ionic_admin_comp *comp;
u16 index;
comp = &((struct ionic_admin_comp *)cq->base)[cq->tail_idx];
if (!color_match(comp->color, cq->done_color))
return false;
/* check for empty queue */
if (q->tail_idx == q->head_idx)
return false;
do {
desc_info = &q->admin_info[q->tail_idx];
index = q->tail_idx;
q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1);
if (likely(desc_info->ctx)) {
struct ionic_admin_ctx *ctx = desc_info->ctx;
memcpy(&ctx->comp, comp, sizeof(*comp));
dev_dbg(q->dev, "comp admin queue command:\n");
dynamic_hex_dump("comp ", DUMP_PREFIX_OFFSET, 16, 1,
&ctx->comp, sizeof(ctx->comp), true);
complete_all(&ctx->work);
desc_info->ctx = NULL;
}
} while (index != le16_to_cpu(comp->comp_index));
return true;
}
bool ionic_adminq_poke_doorbell(struct ionic_queue *q)
{
struct ionic_lif *lif = q->lif;
unsigned long now, then, dif;
unsigned long irqflags;
spin_lock_irqsave(&lif->adminq_lock, irqflags);
if (q->tail_idx == q->head_idx) {
spin_unlock_irqrestore(&lif->adminq_lock, irqflags);
return false;
}
now = READ_ONCE(jiffies);
then = q->dbell_jiffies;
dif = now - then;
if (dif > q->dbell_deadline) {
ionic_dbell_ring(q->lif->kern_dbpage, q->hw_type,
q->dbval | q->head_idx);
q->dbell_jiffies = now;
}
spin_unlock_irqrestore(&lif->adminq_lock, irqflags);
return true;
}
int ionic_adminq_post(struct ionic_lif *lif, struct ionic_admin_ctx *ctx)
{
struct ionic_admin_desc_info *desc_info;
struct ionic_admin_cmd *desc;
unsigned long irqflags;
struct ionic_queue *q;
int err = 0;
spin_lock_irqsave(&lif->adminq_lock, irqflags);
if (!lif->adminqcq) {
spin_unlock_irqrestore(&lif->adminq_lock, irqflags);
return -EIO;
}
q = &lif->adminqcq->q;
if (!ionic_q_has_space(q, 1)) {
err = -ENOSPC;
goto err_out;
}
err = ionic_heartbeat_check(lif->ionic);
if (err)
goto err_out;
desc_info = &q->admin_info[q->head_idx];
desc_info->ctx = ctx;
desc = &q->adminq[q->head_idx];
memcpy(desc, &ctx->cmd, sizeof(ctx->cmd));
dev_dbg(&lif->netdev->dev, "post admin queue command:\n");
dynamic_hex_dump("cmd ", DUMP_PREFIX_OFFSET, 16, 1,
&ctx->cmd, sizeof(ctx->cmd), true);
ionic_q_post(q, true);
err_out:
spin_unlock_irqrestore(&lif->adminq_lock, irqflags);
return err;
}
int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx,
const int err, const bool do_msg)
{
struct net_device *netdev = lif->netdev;
unsigned long time_limit;
unsigned long time_start;
unsigned long time_done;
unsigned long remaining;
const char *name;
name = ionic_opcode_to_str(ctx->cmd.cmd.opcode);
if (err) {
if (do_msg && !test_bit(IONIC_LIF_F_FW_RESET, lif->state))
netdev_err(netdev, "Posting of %s (%d) failed: %d\n",
name, ctx->cmd.cmd.opcode, err);
ctx->comp.comp.status = IONIC_RC_ERROR;
return err;
}
time_start = jiffies;
time_limit = time_start + HZ * (ulong)DEVCMD_TIMEOUT;
do {
remaining = wait_for_completion_timeout(&ctx->work,
IONIC_ADMINQ_TIME_SLICE);
/* check for done */
if (remaining)
break;
/* force a check of FW status and break out if FW reset */
ionic_heartbeat_check(lif->ionic);
if ((test_bit(IONIC_LIF_F_FW_RESET, lif->state) &&
!lif->ionic->idev.fw_status_ready) ||
test_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) {
if (do_msg)
netdev_warn(netdev, "%s (%d) interrupted, FW in reset\n",
name, ctx->cmd.cmd.opcode);
ctx->comp.comp.status = IONIC_RC_ERROR;
return -ENXIO;
}
} while (time_before(jiffies, time_limit));
time_done = jiffies;
dev_dbg(lif->ionic->dev, "%s: elapsed %d msecs\n",
__func__, jiffies_to_msecs(time_done - time_start));
return ionic_adminq_check_err(lif, ctx,
time_after_eq(time_done, time_limit),
do_msg);
}
static int __ionic_adminq_post_wait(struct ionic_lif *lif,
struct ionic_admin_ctx *ctx,
const bool do_msg)
{
int err;
if (!ionic_is_fw_running(&lif->ionic->idev))
return 0;
err = ionic_adminq_post(lif, ctx);
return ionic_adminq_wait(lif, ctx, err, do_msg);
}
int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx)
{
return __ionic_adminq_post_wait(lif, ctx, true);
}
EXPORT_SYMBOL_NS(ionic_adminq_post_wait, "NET_IONIC");
int ionic_adminq_post_wait_nomsg(struct ionic_lif *lif, struct ionic_admin_ctx *ctx)
{
return __ionic_adminq_post_wait(lif, ctx, false);
}
static void ionic_dev_cmd_clean(struct ionic *ionic)
{
struct ionic_dev *idev = &ionic->idev;
if (!idev->dev_cmd_regs)
return;
iowrite32(0, &idev->dev_cmd_regs->doorbell);
memset_io(&idev->dev_cmd_regs->cmd, 0, sizeof(idev->dev_cmd_regs->cmd));
}
void ionic_dev_cmd_dev_err_print(struct ionic *ionic, u8 opcode, u8 status,
int err)
{
const char *stat_str;
stat_str = (err == -ETIMEDOUT) ? "TIMEOUT" :
ionic_error_to_str(status);
dev_err(ionic->dev, "DEV_CMD %s (%d) error, %s (%d) failed\n",
ionic_opcode_to_str(opcode), opcode, stat_str, err);
}
static int __ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds,
const bool do_msg)
{
struct ionic_dev *idev = &ionic->idev;
unsigned long start_time;
unsigned long max_wait;
unsigned long duration;
bool fw_up;
int opcode;
bool done;
int err;
/* Wait for dev cmd to complete, retrying if we get EAGAIN,
* but don't wait any longer than max_seconds.
*/
max_wait = jiffies + (max_seconds * HZ);
try_again:
done = false;
opcode = idev->opcode;
start_time = jiffies;
for (fw_up = ionic_is_fw_running(idev);
!done && fw_up && time_before(jiffies, max_wait);
fw_up = ionic_is_fw_running(idev)) {
done = ionic_dev_cmd_done(idev);
if (done)
break;
usleep_range(100, 200);
}
duration = jiffies - start_time;
dev_dbg(ionic->dev, "DEVCMD %s (%d) done=%d took %ld secs (%ld jiffies)\n",
ionic_opcode_to_str(opcode), opcode,
done, duration / HZ, duration);
if (!done && !fw_up) {
ionic_dev_cmd_clean(ionic);
dev_warn(ionic->dev, "DEVCMD %s (%d) interrupted - FW is down\n",
ionic_opcode_to_str(opcode), opcode);
return -ENXIO;
}
if (!done && !time_before(jiffies, max_wait)) {
ionic_dev_cmd_clean(ionic);
dev_warn(ionic->dev, "DEVCMD %s (%d) timeout after %ld secs\n",
ionic_opcode_to_str(opcode), opcode, max_seconds);
return -ETIMEDOUT;
}
err = ionic_dev_cmd_status(&ionic->idev);
if (err) {
if (err == IONIC_RC_EAGAIN &&
time_before(jiffies, (max_wait - HZ))) {
dev_dbg(ionic->dev, "DEV_CMD %s (%d), %s (%d) retrying...\n",
ionic_opcode_to_str(opcode), opcode,
ionic_error_to_str(err), err);
iowrite32(0, &idev->dev_cmd_regs->done);
msleep(1000);
iowrite32(1, &idev->dev_cmd_regs->doorbell);
goto try_again;
}
if (!(opcode == IONIC_CMD_FW_CONTROL && err == IONIC_RC_EAGAIN))
if (do_msg)
ionic_dev_cmd_dev_err_print(ionic, opcode, err,
ionic_error_to_errno(err));
return ionic_error_to_errno(err);
}
ionic_dev_cmd_clean(ionic);
return 0;
}
int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds)
{
return __ionic_dev_cmd_wait(ionic, max_seconds, true);
}
int ionic_dev_cmd_wait_nomsg(struct ionic *ionic, unsigned long max_seconds)
{
return __ionic_dev_cmd_wait(ionic, max_seconds, false);
}
int ionic_setup(struct ionic *ionic)
{
int err;
err = ionic_dev_setup(ionic);
if (err)
return err;
ionic_reset(ionic);
return 0;
}
int ionic_identify(struct ionic *ionic)
{
struct ionic_identity *ident = &ionic->ident;
struct ionic_dev *idev = &ionic->idev;
size_t sz;
int err;
memset(ident, 0, sizeof(*ident));
ident->drv.os_type = cpu_to_le32(IONIC_OS_TYPE_LINUX);
strscpy(ident->drv.driver_ver_str, UTS_RELEASE,
sizeof(ident->drv.driver_ver_str));
mutex_lock(&ionic->dev_cmd_lock);
sz = min(sizeof(ident->drv), sizeof(idev->dev_cmd_regs->data));
memcpy_toio(&idev->dev_cmd_regs->data, &ident->drv, sz);
ionic_dev_cmd_identify(idev, IONIC_DEV_IDENTITY_VERSION_2);
err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
if (!err) {
sz = min(sizeof(ident->dev), sizeof(idev->dev_cmd_regs->data));
memcpy_fromio(&ident->dev, &idev->dev_cmd_regs->data, sz);
}
mutex_unlock(&ionic->dev_cmd_lock);
if (err) {
dev_err(ionic->dev, "Cannot identify ionic: %d\n", err);
goto err_out;
}
if (isprint(idev->dev_info.fw_version[0]) &&
isascii(idev->dev_info.fw_version[0]))
dev_info(ionic->dev, "FW: %.*s\n",
(int)(sizeof(idev->dev_info.fw_version) - 1),
idev->dev_info.fw_version);
else
dev_info(ionic->dev, "FW: (invalid string) 0x%02x 0x%02x 0x%02x 0x%02x ...\n",
(u8)idev->dev_info.fw_version[0],
(u8)idev->dev_info.fw_version[1],
(u8)idev->dev_info.fw_version[2],
(u8)idev->dev_info.fw_version[3]);
err = ionic_lif_identify(ionic, IONIC_LIF_TYPE_CLASSIC,
&ionic->ident.lif);
if (err) {
dev_err(ionic->dev, "Cannot identify LIFs: %d\n", err);
goto err_out;
}
return 0;
err_out:
return err;
}
int ionic_init(struct ionic *ionic)
{
struct ionic_dev *idev = &ionic->idev;
int err;
mutex_lock(&ionic->dev_cmd_lock);
ionic_dev_cmd_init(idev);
err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
mutex_unlock(&ionic->dev_cmd_lock);
return err;
}
int ionic_reset(struct ionic *ionic)
{
struct ionic_dev *idev = &ionic->idev;
int err;
if (!ionic_is_fw_running(idev))
return 0;
mutex_lock(&ionic->dev_cmd_lock);
ionic_dev_cmd_reset(idev);
err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
mutex_unlock(&ionic->dev_cmd_lock);
return err;
}
int ionic_port_identify(struct ionic *ionic)
{
struct ionic_identity *ident = &ionic->ident;
struct ionic_dev *idev = &ionic->idev;
size_t sz;
int err;
mutex_lock(&ionic->dev_cmd_lock);
ionic_dev_cmd_port_identify(idev);
err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
if (!err) {
sz = min(sizeof(ident->port), sizeof(idev->dev_cmd_regs->data));
memcpy_fromio(&ident->port, &idev->dev_cmd_regs->data, sz);
}
mutex_unlock(&ionic->dev_cmd_lock);
return err;
}
int ionic_port_init(struct ionic *ionic)
{
struct ionic_identity *ident = &ionic->ident;
struct ionic_dev *idev = &ionic->idev;
size_t sz;
int err;
if (!idev->port_info) {
idev->port_info_sz = ALIGN(sizeof(*idev->port_info), PAGE_SIZE);
idev->port_info = dma_alloc_coherent(ionic->dev,
idev->port_info_sz,
&idev->port_info_pa,
GFP_KERNEL);
if (!idev->port_info)
return -ENOMEM;
}
/* If the driver knows about more "extra stats" than the firmware,
* make sure these stats are marked as invalid.
*/
memset(&idev->port_info->extra_stats, 0xff,
sizeof(idev->port_info->extra_stats));
WRITE_ONCE(idev->link_down_count_init, false);
sz = min(sizeof(ident->port.config), sizeof(idev->dev_cmd_regs->data));
mutex_lock(&ionic->dev_cmd_lock);
memcpy_toio(&idev->dev_cmd_regs->data, &ident->port.config, sz);
ionic_dev_cmd_port_init(idev);
err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
ionic_dev_cmd_port_state(&ionic->idev, IONIC_PORT_ADMIN_STATE_UP);
ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
mutex_unlock(&ionic->dev_cmd_lock);
if (err) {
dev_err(ionic->dev, "Failed to init port\n");
dma_free_coherent(ionic->dev, idev->port_info_sz,
idev->port_info, idev->port_info_pa);
idev->port_info = NULL;
idev->port_info_pa = 0;
}
return err;
}
int ionic_port_reset(struct ionic *ionic)
{
struct ionic_dev *idev = &ionic->idev;
int err = 0;
if (!idev->port_info)
return 0;
if (ionic_is_fw_running(idev)) {
mutex_lock(&ionic->dev_cmd_lock);
ionic_dev_cmd_port_reset(idev);
err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
mutex_unlock(&ionic->dev_cmd_lock);
}
dma_free_coherent(ionic->dev, idev->port_info_sz,
idev->port_info, idev->port_info_pa);
idev->port_info = NULL;
idev->port_info_pa = 0;
return err;
}
static int __init ionic_init_module(void)
{
int ret;
ionic_debugfs_create();
ret = ionic_bus_register_driver();
if (ret)
ionic_debugfs_destroy();
return ret;
}
static void __exit ionic_cleanup_module(void)
{
ionic_bus_unregister_driver();
ionic_debugfs_destroy();
pr_info("%s removed\n", IONIC_DRV_NAME);
}
module_init(ionic_init_module);
module_exit(ionic_cleanup_module);
@@ -0,0 +1,661 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2017 - 2021 Pensando Systems, Inc */
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include "ionic.h"
#include "ionic_bus.h"
#include "ionic_lif.h"
#include "ionic_ethtool.h"
static int ionic_hwstamp_tx_mode(int config_tx_type)
{
switch (config_tx_type) {
case HWTSTAMP_TX_OFF:
return IONIC_TXSTAMP_OFF;
case HWTSTAMP_TX_ON:
return IONIC_TXSTAMP_ON;
case HWTSTAMP_TX_ONESTEP_SYNC:
return IONIC_TXSTAMP_ONESTEP_SYNC;
case HWTSTAMP_TX_ONESTEP_P2P:
return IONIC_TXSTAMP_ONESTEP_P2P;
default:
return -ERANGE;
}
}
static u64 ionic_hwstamp_rx_filt(int config_rx_filter)
{
switch (config_rx_filter) {
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
return IONIC_PKT_CLS_PTP1_ALL;
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
return IONIC_PKT_CLS_PTP1_SYNC;
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
return IONIC_PKT_CLS_PTP1_SYNC | IONIC_PKT_CLS_PTP1_DREQ;
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
return IONIC_PKT_CLS_PTP2_L4_ALL;
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
return IONIC_PKT_CLS_PTP2_L4_SYNC;
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
return IONIC_PKT_CLS_PTP2_L4_SYNC | IONIC_PKT_CLS_PTP2_L4_DREQ;
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
return IONIC_PKT_CLS_PTP2_L2_ALL;
case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
return IONIC_PKT_CLS_PTP2_L2_SYNC;
case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
return IONIC_PKT_CLS_PTP2_L2_SYNC | IONIC_PKT_CLS_PTP2_L2_DREQ;
case HWTSTAMP_FILTER_PTP_V2_EVENT:
return IONIC_PKT_CLS_PTP2_ALL;
case HWTSTAMP_FILTER_PTP_V2_SYNC:
return IONIC_PKT_CLS_PTP2_SYNC;
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
return IONIC_PKT_CLS_PTP2_SYNC | IONIC_PKT_CLS_PTP2_DREQ;
case HWTSTAMP_FILTER_NTP_ALL:
return IONIC_PKT_CLS_NTP_ALL;
default:
return 0;
}
}
static int ionic_lif_hwstamp_set_ts_config(struct ionic_lif *lif,
struct kernel_hwtstamp_config *new_ts,
struct netlink_ext_ack *extack)
{
struct kernel_hwtstamp_config *config;
struct kernel_hwtstamp_config ts = {};
struct ionic *ionic = lif->ionic;
int tx_mode = 0;
u64 rx_filt = 0;
int err, err2;
bool rx_all;
__le64 mask;
if (!lif->phc || !lif->phc->ptp)
return -EOPNOTSUPP;
mutex_lock(&lif->phc->config_lock);
if (new_ts) {
config = new_ts;
} else {
/* If called with new_ts == NULL, replay the previous request
* primarily for recovery after a FW_RESET.
* We saved the previous configuration request info, so copy
* the previous request for reference, clear the current state
* to match the device's reset state, and run with it.
*/
config = &ts;
memcpy(config, &lif->phc->ts_config, sizeof(*config));
memset(&lif->phc->ts_config, 0, sizeof(lif->phc->ts_config));
lif->phc->ts_config_tx_mode = 0;
lif->phc->ts_config_rx_filt = 0;
}
tx_mode = ionic_hwstamp_tx_mode(config->tx_type);
if (tx_mode < 0) {
NL_SET_ERR_MSG_MOD(extack,
"TX time stamping mode isn't supported");
err = tx_mode;
goto err_queues;
}
mask = cpu_to_le64(BIT_ULL(tx_mode));
if ((ionic->ident.lif.eth.hwstamp_tx_modes & mask) != mask) {
NL_SET_ERR_MSG_MOD(extack,
"TX time stamping mode isn't supported");
err = -ERANGE;
goto err_queues;
}
rx_filt = ionic_hwstamp_rx_filt(config->rx_filter);
rx_all = config->rx_filter != HWTSTAMP_FILTER_NONE && !rx_filt;
mask = cpu_to_le64(rx_filt);
if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) != mask) {
rx_filt = 0;
rx_all = true;
config->rx_filter = HWTSTAMP_FILTER_ALL;
}
dev_dbg(ionic->dev, "%s: config_rx_filter %d rx_filt %#llx rx_all %d\n",
__func__, config->rx_filter, rx_filt, rx_all);
if (tx_mode) {
err = ionic_lif_create_hwstamp_txq(lif);
if (err) {
NL_SET_ERR_MSG_MOD(extack,
"Error creating TX timestamp queue");
goto err_queues;
}
}
if (rx_filt) {
err = ionic_lif_create_hwstamp_rxq(lif);
if (err) {
NL_SET_ERR_MSG_MOD(extack,
"Error creating RX timestamp queue");
goto err_queues;
}
}
if (tx_mode != lif->phc->ts_config_tx_mode) {
err = ionic_lif_set_hwstamp_txmode(lif, tx_mode);
if (err) {
NL_SET_ERR_MSG_MOD(extack,
"Error enabling TX timestamp mode");
goto err_txmode;
}
}
if (rx_filt != lif->phc->ts_config_rx_filt) {
err = ionic_lif_set_hwstamp_rxfilt(lif, rx_filt);
if (err) {
NL_SET_ERR_MSG_MOD(extack,
"Error enabling RX timestamp mode");
goto err_rxfilt;
}
}
if (rx_all != (lif->phc->ts_config.rx_filter == HWTSTAMP_FILTER_ALL)) {
err = ionic_lif_config_hwstamp_rxq_all(lif, rx_all);
if (err) {
NL_SET_ERR_MSG_MOD(extack,
"Error enabling RX timestamp mode");
goto err_rxall;
}
}
memcpy(&lif->phc->ts_config, config, sizeof(*config));
lif->phc->ts_config_rx_filt = rx_filt;
lif->phc->ts_config_tx_mode = tx_mode;
mutex_unlock(&lif->phc->config_lock);
return 0;
err_rxall:
if (rx_filt != lif->phc->ts_config_rx_filt) {
rx_filt = lif->phc->ts_config_rx_filt;
err2 = ionic_lif_set_hwstamp_rxfilt(lif, rx_filt);
if (err2)
dev_err(ionic->dev,
"Failed to revert rx timestamp filter: %d\n", err2);
}
err_rxfilt:
if (tx_mode != lif->phc->ts_config_tx_mode) {
tx_mode = lif->phc->ts_config_tx_mode;
err2 = ionic_lif_set_hwstamp_txmode(lif, tx_mode);
if (err2)
dev_err(ionic->dev,
"Failed to revert tx timestamp mode: %d\n", err2);
}
err_txmode:
/* special queues remain allocated, just unused */
err_queues:
mutex_unlock(&lif->phc->config_lock);
return err;
}
int ionic_hwstamp_set(struct net_device *netdev,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack)
{
struct ionic_lif *lif = netdev_priv(netdev);
int err;
if (!lif->phc || !lif->phc->ptp)
return -EOPNOTSUPP;
mutex_lock(&lif->queue_lock);
err = ionic_lif_hwstamp_set_ts_config(lif, config, extack);
mutex_unlock(&lif->queue_lock);
if (err) {
netdev_info(lif->netdev, "hwstamp set failed: %d\n", err);
return err;
}
return 0;
}
void ionic_lif_hwstamp_replay(struct ionic_lif *lif)
{
int err;
if (!lif->phc || !lif->phc->ptp)
return;
mutex_lock(&lif->queue_lock);
err = ionic_lif_hwstamp_set_ts_config(lif, NULL, NULL);
mutex_unlock(&lif->queue_lock);
if (err)
netdev_info(lif->netdev, "hwstamp replay failed: %d\n", err);
}
void ionic_lif_hwstamp_recreate_queues(struct ionic_lif *lif)
{
int err;
if (!lif->phc || !lif->phc->ptp)
return;
mutex_lock(&lif->phc->config_lock);
if (lif->phc->ts_config_tx_mode) {
err = ionic_lif_create_hwstamp_txq(lif);
if (err)
netdev_info(lif->netdev, "hwstamp recreate txq failed: %d\n", err);
}
if (lif->phc->ts_config_rx_filt) {
err = ionic_lif_create_hwstamp_rxq(lif);
if (err)
netdev_info(lif->netdev, "hwstamp recreate rxq failed: %d\n", err);
}
mutex_unlock(&lif->phc->config_lock);
}
int ionic_hwstamp_get(struct net_device *netdev,
struct kernel_hwtstamp_config *config)
{
struct ionic_lif *lif = netdev_priv(netdev);
if (!lif->phc || !lif->phc->ptp)
return -EOPNOTSUPP;
mutex_lock(&lif->phc->config_lock);
memcpy(config, &lif->phc->ts_config, sizeof(*config));
mutex_unlock(&lif->phc->config_lock);
return 0;
}
static u64 ionic_hwstamp_read(struct ionic *ionic,
struct ptp_system_timestamp *sts)
{
u32 tick_high_before, tick_high, tick_low;
/* read and discard low part to defeat hw staging of high part */
ioread32(&ionic->idev.hwstamp_regs->tick_low);
tick_high_before = ioread32(&ionic->idev.hwstamp_regs->tick_high);
ptp_read_system_prets(sts);
tick_low = ioread32(&ionic->idev.hwstamp_regs->tick_low);
ptp_read_system_postts(sts);
tick_high = ioread32(&ionic->idev.hwstamp_regs->tick_high);
/* If tick_high changed, re-read tick_low once more. Assume tick_high
* cannot change again so soon as in the span of re-reading tick_low.
*/
if (tick_high != tick_high_before) {
ptp_read_system_prets(sts);
tick_low = ioread32(&ionic->idev.hwstamp_regs->tick_low);
ptp_read_system_postts(sts);
}
return (u64)tick_low | ((u64)tick_high << 32);
}
static u64 ionic_cc_read(struct cyclecounter *cc)
{
struct ionic_phc *phc = container_of(cc, struct ionic_phc, cc);
struct ionic *ionic = phc->lif->ionic;
return ionic_hwstamp_read(ionic, NULL);
}
static int ionic_setphc_cmd(struct ionic_phc *phc, struct ionic_admin_ctx *ctx)
{
ctx->work = COMPLETION_INITIALIZER_ONSTACK(ctx->work);
ctx->cmd.lif_setphc.opcode = IONIC_CMD_LIF_SETPHC;
ctx->cmd.lif_setphc.lif_index = cpu_to_le16(phc->lif->index);
ctx->cmd.lif_setphc.tick = cpu_to_le64(phc->tc.cycle_last);
ctx->cmd.lif_setphc.nsec = cpu_to_le64(phc->tc.nsec);
ctx->cmd.lif_setphc.frac = cpu_to_le64(phc->tc.frac);
ctx->cmd.lif_setphc.mult = cpu_to_le32(phc->cc.mult);
ctx->cmd.lif_setphc.shift = cpu_to_le32(phc->cc.shift);
return ionic_adminq_post(phc->lif, ctx);
}
static int ionic_phc_adjfine(struct ptp_clock_info *info, long scaled_ppm)
{
struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info);
struct ionic_admin_ctx ctx = {};
unsigned long irqflags;
s64 adj;
int err;
/* Reject phc adjustments during device upgrade */
if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state))
return -EBUSY;
/* Adjustment value scaled by 2^16 million */
adj = (s64)scaled_ppm * phc->init_cc_mult;
/* Adjustment value to scale */
adj /= (s64)SCALED_PPM;
/* Final adjusted multiplier */
adj += phc->init_cc_mult;
spin_lock_irqsave(&phc->lock, irqflags);
/* update the point-in-time basis to now, before adjusting the rate */
timecounter_read(&phc->tc);
phc->cc.mult = adj;
/* Setphc commands are posted in-order, sequenced by phc->lock. We
* need to drop the lock before waiting for the command to complete.
*/
err = ionic_setphc_cmd(phc, &ctx);
spin_unlock_irqrestore(&phc->lock, irqflags);
return ionic_adminq_wait(phc->lif, &ctx, err, true);
}
static int ionic_phc_adjtime(struct ptp_clock_info *info, s64 delta)
{
struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info);
struct ionic_admin_ctx ctx = {};
unsigned long irqflags;
int err;
/* Reject phc adjustments during device upgrade */
if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state))
return -EBUSY;
spin_lock_irqsave(&phc->lock, irqflags);
timecounter_adjtime(&phc->tc, delta);
/* Setphc commands are posted in-order, sequenced by phc->lock. We
* need to drop the lock before waiting for the command to complete.
*/
err = ionic_setphc_cmd(phc, &ctx);
spin_unlock_irqrestore(&phc->lock, irqflags);
return ionic_adminq_wait(phc->lif, &ctx, err, true);
}
static int ionic_phc_settime64(struct ptp_clock_info *info,
const struct timespec64 *ts)
{
struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info);
struct ionic_admin_ctx ctx = {};
unsigned long irqflags;
int err;
u64 ns;
/* Reject phc adjustments during device upgrade */
if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state))
return -EBUSY;
ns = timespec64_to_ns(ts);
spin_lock_irqsave(&phc->lock, irqflags);
timecounter_init(&phc->tc, &phc->cc, ns);
/* Setphc commands are posted in-order, sequenced by phc->lock. We
* need to drop the lock before waiting for the command to complete.
*/
err = ionic_setphc_cmd(phc, &ctx);
spin_unlock_irqrestore(&phc->lock, irqflags);
return ionic_adminq_wait(phc->lif, &ctx, err, true);
}
static int ionic_phc_gettimex64(struct ptp_clock_info *info,
struct timespec64 *ts,
struct ptp_system_timestamp *sts)
{
struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info);
struct ionic *ionic = phc->lif->ionic;
unsigned long irqflags;
u64 tick, ns;
/* Do not attempt to read device time during upgrade */
if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state))
return -EBUSY;
spin_lock_irqsave(&phc->lock, irqflags);
tick = ionic_hwstamp_read(ionic, sts);
ns = timecounter_cyc2time(&phc->tc, tick);
spin_unlock_irqrestore(&phc->lock, irqflags);
*ts = ns_to_timespec64(ns);
return 0;
}
static long ionic_phc_aux_work(struct ptp_clock_info *info)
{
struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info);
struct ionic_admin_ctx ctx = {};
unsigned long irqflags;
int err;
/* Do not update phc during device upgrade, but keep polling to resume
* after upgrade. Since we don't update the point in time basis, there
* is no expectation that we are maintaining the phc time during the
* upgrade. After upgrade, it will need to be readjusted back to the
* correct time by the ptp daemon.
*/
if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state))
return phc->aux_work_delay;
spin_lock_irqsave(&phc->lock, irqflags);
/* update point-in-time basis to now */
timecounter_read(&phc->tc);
/* Setphc commands are posted in-order, sequenced by phc->lock. We
* need to drop the lock before waiting for the command to complete.
*/
err = ionic_setphc_cmd(phc, &ctx);
spin_unlock_irqrestore(&phc->lock, irqflags);
ionic_adminq_wait(phc->lif, &ctx, err, true);
return phc->aux_work_delay;
}
ktime_t ionic_lif_phc_ktime(struct ionic_lif *lif, u64 tick)
{
unsigned long irqflags;
u64 ns;
if (!lif->phc)
return 0;
spin_lock_irqsave(&lif->phc->lock, irqflags);
ns = timecounter_cyc2time(&lif->phc->tc, tick);
spin_unlock_irqrestore(&lif->phc->lock, irqflags);
return ns_to_ktime(ns);
}
static const struct ptp_clock_info ionic_ptp_info = {
.owner = THIS_MODULE,
.name = "ionic_ptp",
.adjfine = ionic_phc_adjfine,
.adjtime = ionic_phc_adjtime,
.gettimex64 = ionic_phc_gettimex64,
.settime64 = ionic_phc_settime64,
.do_aux_work = ionic_phc_aux_work,
};
void ionic_lif_register_phc(struct ionic_lif *lif)
{
if (!lif->phc || !(lif->hw_features & IONIC_ETH_HW_TIMESTAMP))
return;
lif->phc->ptp = ptp_clock_register(&lif->phc->ptp_info, lif->ionic->dev);
if (IS_ERR(lif->phc->ptp)) {
dev_warn(lif->ionic->dev, "Cannot register phc device: %ld\n",
PTR_ERR(lif->phc->ptp));
lif->phc->ptp = NULL;
}
if (lif->phc->ptp)
ptp_schedule_worker(lif->phc->ptp, lif->phc->aux_work_delay);
}
void ionic_lif_unregister_phc(struct ionic_lif *lif)
{
if (!lif->phc || !lif->phc->ptp)
return;
ptp_clock_unregister(lif->phc->ptp);
lif->phc->ptp = NULL;
}
void ionic_lif_alloc_phc(struct ionic_lif *lif)
{
struct ionic *ionic = lif->ionic;
struct ionic_phc *phc;
u64 delay, diff, mult;
u64 frac = 0;
u64 features;
u32 shift;
if (!ionic->idev.hwstamp_regs)
return;
features = le64_to_cpu(ionic->ident.lif.eth.config.features);
if (!(features & IONIC_ETH_HW_TIMESTAMP))
return;
phc = devm_kzalloc(ionic->dev, sizeof(*phc), GFP_KERNEL);
if (!phc)
return;
phc->lif = lif;
phc->cc.read = ionic_cc_read;
phc->cc.mask = le64_to_cpu(ionic->ident.dev.hwstamp_mask);
phc->cc.mult = le32_to_cpu(ionic->ident.dev.hwstamp_mult);
phc->cc.shift = le32_to_cpu(ionic->ident.dev.hwstamp_shift);
if (!phc->cc.mult) {
dev_err(lif->ionic->dev,
"Invalid device PHC mask multiplier %u, disabling HW timestamp support\n",
phc->cc.mult);
devm_kfree(lif->ionic->dev, phc);
lif->phc = NULL;
return;
}
dev_dbg(lif->ionic->dev, "Device PHC mask %#llx mult %u shift %u\n",
phc->cc.mask, phc->cc.mult, phc->cc.shift);
spin_lock_init(&phc->lock);
mutex_init(&phc->config_lock);
/* max ticks is limited by the multiplier, or by the update period. */
if (phc->cc.shift + 2 + ilog2(IONIC_PHC_UPDATE_NS) >= 64) {
/* max ticks that do not overflow when multiplied by max
* adjusted multiplier (twice the initial multiplier)
*/
diff = U64_MAX / phc->cc.mult / 2;
} else {
/* approx ticks at four times the update period */
diff = (u64)IONIC_PHC_UPDATE_NS << (phc->cc.shift + 2);
diff = DIV_ROUND_UP(diff, phc->cc.mult);
}
/* transform to bitmask */
diff |= diff >> 1;
diff |= diff >> 2;
diff |= diff >> 4;
diff |= diff >> 8;
diff |= diff >> 16;
diff |= diff >> 32;
/* constrain to the hardware bitmask */
diff &= phc->cc.mask;
/* the wrap period is now defined by diff
*
* we will update the time basis at about 1/4 the wrap period, so
* should not see a difference of more than +/- diff/4.
*
* this is sufficient not see a difference of more than +/- diff/2, as
* required by timecounter_cyc2time, to detect an old time stamp.
*
* adjust the initial multiplier, being careful to avoid overflow:
* - do not overflow 63 bits: init_cc_mult * SCALED_PPM
* - do not overflow 64 bits: max_mult * (diff / 2)
*
* we want to increase the initial multiplier as much as possible, to
* allow for more precise adjustment in ionic_phc_adjfine.
*
* only adjust the multiplier if we can double it or more.
*/
mult = U64_MAX / 2 / max(diff / 2, SCALED_PPM);
shift = mult / phc->cc.mult;
if (shift >= 2) {
/* initial multiplier will be 2^n of hardware cc.mult */
shift = fls(shift);
/* increase cc.mult and cc.shift by the same 2^n and n. */
phc->cc.mult <<= shift;
phc->cc.shift += shift;
}
dev_dbg(lif->ionic->dev, "Initial PHC mask %#llx mult %u shift %u\n",
phc->cc.mask, phc->cc.mult, phc->cc.shift);
/* frequency adjustments are relative to the initial multiplier */
phc->init_cc_mult = phc->cc.mult;
timecounter_init(&phc->tc, &phc->cc, ktime_get_real_ns());
/* Update cycle_last at 1/4 the wrap period, or IONIC_PHC_UPDATE_NS */
delay = min_t(u64, IONIC_PHC_UPDATE_NS,
cyclecounter_cyc2ns(&phc->cc, diff / 4, 0, &frac));
dev_dbg(lif->ionic->dev, "Work delay %llu ms\n", delay / NSEC_PER_MSEC);
phc->aux_work_delay = nsecs_to_jiffies(delay);
phc->ptp_info = ionic_ptp_info;
/* We have allowed to adjust the multiplier up to +/- 1 part per 1.
* Here expressed as NORMAL_PPB (1 billion parts per billion).
*/
phc->ptp_info.max_adj = NORMAL_PPB;
lif->phc = phc;
}
void ionic_lif_free_phc(struct ionic_lif *lif)
{
if (!lif->phc)
return;
mutex_destroy(&lif->phc->config_lock);
devm_kfree(lif->ionic->dev, lif->phc);
lif->phc = NULL;
}
@@ -0,0 +1,136 @@
/* SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB) OR BSD-2-Clause */
/* Copyright (c) 2018-2019 Pensando Systems, Inc. All rights reserved. */
#ifndef IONIC_REGS_H
#define IONIC_REGS_H
#include <linux/io.h>
/** struct ionic_intr - interrupt control register set.
* @coal_init: coalesce timer initial value.
* @mask: interrupt mask value.
* @credits: interrupt credit count and return.
* @mask_assert: interrupt mask value on assert.
* @coal: coalesce timer time remaining.
*/
struct ionic_intr {
u32 coal_init;
u32 mask;
u32 credits;
u32 mask_assert;
u32 coal;
u32 rsvd[3];
};
#define IONIC_INTR_CTRL_REGS_MAX 2048
#define IONIC_INTR_CTRL_COAL_MAX 0x3F
/** enum ionic_intr_mask_vals - valid values for mask and mask_assert.
* @IONIC_INTR_MASK_CLEAR: unmask interrupt.
* @IONIC_INTR_MASK_SET: mask interrupt.
*/
enum ionic_intr_mask_vals {
IONIC_INTR_MASK_CLEAR = 0,
IONIC_INTR_MASK_SET = 1,
};
/** enum ionic_intr_credits_bits - bitwise composition of credits values.
* @IONIC_INTR_CRED_COUNT: bit mask of credit count, no shift needed.
* @IONIC_INTR_CRED_COUNT_SIGNED: bit mask of credit count, including sign bit.
* @IONIC_INTR_CRED_UNMASK: unmask the interrupt.
* @IONIC_INTR_CRED_RESET_COALESCE: reset the coalesce timer.
* @IONIC_INTR_CRED_REARM: unmask the and reset the timer.
*/
enum ionic_intr_credits_bits {
IONIC_INTR_CRED_COUNT = 0x7fffu,
IONIC_INTR_CRED_COUNT_SIGNED = 0xffffu,
IONIC_INTR_CRED_UNMASK = 0x10000u,
IONIC_INTR_CRED_RESET_COALESCE = 0x20000u,
IONIC_INTR_CRED_REARM = (IONIC_INTR_CRED_UNMASK |
IONIC_INTR_CRED_RESET_COALESCE),
};
static inline void ionic_intr_coal_init(struct ionic_intr __iomem *intr_ctrl,
int intr_idx, u32 coal)
{
iowrite32(coal, &intr_ctrl[intr_idx].coal_init);
}
static inline void ionic_intr_mask(struct ionic_intr __iomem *intr_ctrl,
int intr_idx, u32 mask)
{
iowrite32(mask, &intr_ctrl[intr_idx].mask);
}
static inline void ionic_intr_credits(struct ionic_intr __iomem *intr_ctrl,
int intr_idx, u32 cred, u32 flags)
{
if (WARN_ON_ONCE(cred > IONIC_INTR_CRED_COUNT)) {
cred = ioread32(&intr_ctrl[intr_idx].credits);
cred &= IONIC_INTR_CRED_COUNT_SIGNED;
}
iowrite32(cred | flags, &intr_ctrl[intr_idx].credits);
}
static inline void ionic_intr_clean(struct ionic_intr __iomem *intr_ctrl,
int intr_idx)
{
u32 cred;
cred = ioread32(&intr_ctrl[intr_idx].credits);
cred &= IONIC_INTR_CRED_COUNT_SIGNED;
cred |= IONIC_INTR_CRED_RESET_COALESCE;
iowrite32(cred, &intr_ctrl[intr_idx].credits);
}
static inline void ionic_intr_mask_assert(struct ionic_intr __iomem *intr_ctrl,
int intr_idx, u32 mask)
{
iowrite32(mask, &intr_ctrl[intr_idx].mask_assert);
}
/** enum ionic_dbell_bits - bitwise composition of dbell values.
*
* @IONIC_DBELL_QID_MASK: unshifted mask of valid queue id bits.
* @IONIC_DBELL_QID_SHIFT: queue id shift amount in dbell value.
* @IONIC_DBELL_QID: macro to build QID component of dbell value.
*
* @IONIC_DBELL_RING_MASK: unshifted mask of valid ring bits.
* @IONIC_DBELL_RING_SHIFT: ring shift amount in dbell value.
* @IONIC_DBELL_RING: macro to build ring component of dbell value.
*
* @IONIC_DBELL_RING_0: ring zero dbell component value.
* @IONIC_DBELL_RING_1: ring one dbell component value.
* @IONIC_DBELL_RING_2: ring two dbell component value.
* @IONIC_DBELL_RING_3: ring three dbell component value.
*
* @IONIC_DBELL_INDEX_MASK: bit mask of valid index bits, no shift needed.
*/
enum ionic_dbell_bits {
IONIC_DBELL_QID_MASK = 0xffffff,
IONIC_DBELL_QID_SHIFT = 24,
#define IONIC_DBELL_QID(n) \
(((u64)(n) & IONIC_DBELL_QID_MASK) << IONIC_DBELL_QID_SHIFT)
IONIC_DBELL_RING_MASK = 0x7,
IONIC_DBELL_RING_SHIFT = 16,
#define IONIC_DBELL_RING(n) \
(((u64)(n) & IONIC_DBELL_RING_MASK) << IONIC_DBELL_RING_SHIFT)
IONIC_DBELL_RING_0 = 0,
IONIC_DBELL_RING_1 = IONIC_DBELL_RING(1),
IONIC_DBELL_RING_2 = IONIC_DBELL_RING(2),
IONIC_DBELL_RING_3 = IONIC_DBELL_RING(3),
IONIC_DBELL_INDEX_MASK = 0xffff,
};
static inline void ionic_dbell_ring(u64 __iomem *db_page, int qtype, u64 val)
{
writeq(val, &db_page[qtype]);
}
#endif /* IONIC_REGS_H */
@@ -0,0 +1,619 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
#include <linux/netdevice.h>
#include <linux/dynamic_debug.h>
#include <linux/etherdevice.h>
#include <linux/list.h>
#include "ionic.h"
#include "ionic_lif.h"
#include "ionic_rx_filter.h"
void ionic_rx_filter_free(struct ionic_lif *lif, struct ionic_rx_filter *f)
{
struct device *dev = lif->ionic->dev;
hlist_del(&f->by_id);
hlist_del(&f->by_hash);
devm_kfree(dev, f);
}
void ionic_rx_filter_replay(struct ionic_lif *lif)
{
struct ionic_rx_filter_add_cmd *ac;
struct hlist_head new_id_list;
struct ionic_admin_ctx ctx;
struct ionic_rx_filter *f;
struct hlist_head *head;
struct hlist_node *tmp;
unsigned int key;
unsigned int i;
int err;
INIT_HLIST_HEAD(&new_id_list);
ac = &ctx.cmd.rx_filter_add;
for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) {
head = &lif->rx_filters.by_id[i];
hlist_for_each_entry_safe(f, tmp, head, by_id) {
ctx.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work);
memcpy(ac, &f->cmd, sizeof(f->cmd));
dev_dbg(&lif->netdev->dev, "replay filter command:\n");
dynamic_hex_dump("cmd ", DUMP_PREFIX_OFFSET, 16, 1,
&ctx.cmd, sizeof(ctx.cmd), true);
err = ionic_adminq_post_wait(lif, &ctx);
if (err) {
switch (le16_to_cpu(ac->match)) {
case IONIC_RX_FILTER_MATCH_VLAN:
netdev_info(lif->netdev, "Replay failed - %d: vlan %d\n",
err,
le16_to_cpu(ac->vlan.vlan));
break;
case IONIC_RX_FILTER_MATCH_MAC:
netdev_info(lif->netdev, "Replay failed - %d: mac %pM\n",
err, ac->mac.addr);
break;
case IONIC_RX_FILTER_MATCH_MAC_VLAN:
netdev_info(lif->netdev, "Replay failed - %d: vlan %d mac %pM\n",
err,
le16_to_cpu(ac->vlan.vlan),
ac->mac.addr);
break;
}
spin_lock_bh(&lif->rx_filters.lock);
ionic_rx_filter_free(lif, f);
spin_unlock_bh(&lif->rx_filters.lock);
continue;
}
/* remove from old id list, save new id in tmp list */
spin_lock_bh(&lif->rx_filters.lock);
hlist_del(&f->by_id);
spin_unlock_bh(&lif->rx_filters.lock);
f->filter_id = le32_to_cpu(ctx.comp.rx_filter_add.filter_id);
hlist_add_head(&f->by_id, &new_id_list);
}
}
/* rebuild the by_id hash lists with the new filter ids */
spin_lock_bh(&lif->rx_filters.lock);
hlist_for_each_entry_safe(f, tmp, &new_id_list, by_id) {
key = f->filter_id & IONIC_RX_FILTER_HLISTS_MASK;
head = &lif->rx_filters.by_id[key];
hlist_add_head(&f->by_id, head);
}
spin_unlock_bh(&lif->rx_filters.lock);
}
int ionic_rx_filters_init(struct ionic_lif *lif)
{
unsigned int i;
spin_lock_init(&lif->rx_filters.lock);
spin_lock_bh(&lif->rx_filters.lock);
for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) {
INIT_HLIST_HEAD(&lif->rx_filters.by_hash[i]);
INIT_HLIST_HEAD(&lif->rx_filters.by_id[i]);
}
spin_unlock_bh(&lif->rx_filters.lock);
return 0;
}
void ionic_rx_filters_deinit(struct ionic_lif *lif)
{
struct ionic_rx_filter *f;
struct hlist_head *head;
struct hlist_node *tmp;
unsigned int i;
spin_lock_bh(&lif->rx_filters.lock);
for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) {
head = &lif->rx_filters.by_id[i];
hlist_for_each_entry_safe(f, tmp, head, by_id)
ionic_rx_filter_free(lif, f);
}
spin_unlock_bh(&lif->rx_filters.lock);
}
int ionic_rx_filter_save(struct ionic_lif *lif, u32 flow_id, u16 rxq_index,
u32 hash, struct ionic_admin_ctx *ctx,
enum ionic_filter_state state)
{
struct device *dev = lif->ionic->dev;
struct ionic_rx_filter_add_cmd *ac;
struct ionic_rx_filter *f = NULL;
struct hlist_head *head;
unsigned int key;
ac = &ctx->cmd.rx_filter_add;
switch (le16_to_cpu(ac->match)) {
case IONIC_RX_FILTER_MATCH_VLAN:
key = le16_to_cpu(ac->vlan.vlan);
f = ionic_rx_filter_by_vlan(lif, le16_to_cpu(ac->vlan.vlan));
break;
case IONIC_RX_FILTER_MATCH_MAC:
key = *(u32 *)ac->mac.addr;
f = ionic_rx_filter_by_addr(lif, ac->mac.addr);
break;
case IONIC_RX_FILTER_MATCH_MAC_VLAN:
key = le16_to_cpu(ac->mac_vlan.vlan);
break;
case IONIC_RX_FILTER_STEER_PKTCLASS:
key = 0;
break;
default:
return -EINVAL;
}
if (f) {
/* remove from current linking so we can refresh it */
hlist_del(&f->by_id);
hlist_del(&f->by_hash);
} else {
f = devm_kzalloc(dev, sizeof(*f), GFP_ATOMIC);
if (!f)
return -ENOMEM;
}
f->flow_id = flow_id;
f->filter_id = le32_to_cpu(ctx->comp.rx_filter_add.filter_id);
f->state = state;
f->rxq_index = rxq_index;
memcpy(&f->cmd, ac, sizeof(f->cmd));
netdev_dbg(lif->netdev, "rx_filter add filter_id %d\n", f->filter_id);
INIT_HLIST_NODE(&f->by_hash);
INIT_HLIST_NODE(&f->by_id);
key = hash_32(key, IONIC_RX_FILTER_HASH_BITS);
head = &lif->rx_filters.by_hash[key];
hlist_add_head(&f->by_hash, head);
key = f->filter_id & IONIC_RX_FILTER_HLISTS_MASK;
head = &lif->rx_filters.by_id[key];
hlist_add_head(&f->by_id, head);
return 0;
}
struct ionic_rx_filter *ionic_rx_filter_by_vlan(struct ionic_lif *lif, u16 vid)
{
struct ionic_rx_filter *f;
struct hlist_head *head;
unsigned int key;
key = hash_32(vid, IONIC_RX_FILTER_HASH_BITS);
head = &lif->rx_filters.by_hash[key];
hlist_for_each_entry(f, head, by_hash) {
if (le16_to_cpu(f->cmd.match) != IONIC_RX_FILTER_MATCH_VLAN)
continue;
if (le16_to_cpu(f->cmd.vlan.vlan) == vid)
return f;
}
return NULL;
}
struct ionic_rx_filter *ionic_rx_filter_by_addr(struct ionic_lif *lif,
const u8 *addr)
{
struct ionic_rx_filter *f;
struct hlist_head *head;
unsigned int key;
key = hash_32(*(u32 *)addr, IONIC_RX_FILTER_HASH_BITS);
head = &lif->rx_filters.by_hash[key];
hlist_for_each_entry(f, head, by_hash) {
if (le16_to_cpu(f->cmd.match) != IONIC_RX_FILTER_MATCH_MAC)
continue;
if (memcmp(addr, f->cmd.mac.addr, ETH_ALEN) == 0)
return f;
}
return NULL;
}
struct ionic_rx_filter *ionic_rx_filter_rxsteer(struct ionic_lif *lif)
{
struct ionic_rx_filter *f;
struct hlist_head *head;
unsigned int key;
key = hash_32(0, IONIC_RX_FILTER_HASH_BITS);
head = &lif->rx_filters.by_hash[key];
hlist_for_each_entry(f, head, by_hash) {
if (le16_to_cpu(f->cmd.match) != IONIC_RX_FILTER_STEER_PKTCLASS)
continue;
return f;
}
return NULL;
}
static struct ionic_rx_filter *ionic_rx_filter_find(struct ionic_lif *lif,
struct ionic_rx_filter_add_cmd *ac)
{
switch (le16_to_cpu(ac->match)) {
case IONIC_RX_FILTER_MATCH_VLAN:
return ionic_rx_filter_by_vlan(lif, le16_to_cpu(ac->vlan.vlan));
case IONIC_RX_FILTER_MATCH_MAC:
return ionic_rx_filter_by_addr(lif, ac->mac.addr);
default:
netdev_err(lif->netdev, "unsupported filter match %d",
le16_to_cpu(ac->match));
return NULL;
}
}
int ionic_lif_list_addr(struct ionic_lif *lif, const u8 *addr, bool mode)
{
struct ionic_rx_filter *f;
int err;
spin_lock_bh(&lif->rx_filters.lock);
f = ionic_rx_filter_by_addr(lif, addr);
if (mode == ADD_ADDR && !f) {
struct ionic_admin_ctx ctx = {
.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
.cmd.rx_filter_add = {
.opcode = IONIC_CMD_RX_FILTER_ADD,
.lif_index = cpu_to_le16(lif->index),
.match = cpu_to_le16(IONIC_RX_FILTER_MATCH_MAC),
},
};
memcpy(ctx.cmd.rx_filter_add.mac.addr, addr, ETH_ALEN);
err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx,
IONIC_FILTER_STATE_NEW);
if (err) {
spin_unlock_bh(&lif->rx_filters.lock);
return err;
}
} else if (mode == ADD_ADDR && f) {
if (f->state == IONIC_FILTER_STATE_OLD)
f->state = IONIC_FILTER_STATE_SYNCED;
} else if (mode == DEL_ADDR && f) {
if (f->state == IONIC_FILTER_STATE_NEW)
ionic_rx_filter_free(lif, f);
else if (f->state == IONIC_FILTER_STATE_SYNCED)
f->state = IONIC_FILTER_STATE_OLD;
} else if (mode == DEL_ADDR && !f) {
spin_unlock_bh(&lif->rx_filters.lock);
return -ENOENT;
}
spin_unlock_bh(&lif->rx_filters.lock);
set_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state);
return 0;
}
static int ionic_lif_filter_add(struct ionic_lif *lif,
struct ionic_rx_filter_add_cmd *ac)
{
struct ionic_admin_ctx ctx = {
.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
};
struct ionic_rx_filter *f;
int nfilters;
int err = 0;
ctx.cmd.rx_filter_add = *ac;
ctx.cmd.rx_filter_add.opcode = IONIC_CMD_RX_FILTER_ADD;
ctx.cmd.rx_filter_add.lif_index = cpu_to_le16(lif->index);
spin_lock_bh(&lif->rx_filters.lock);
f = ionic_rx_filter_find(lif, &ctx.cmd.rx_filter_add);
if (f) {
/* don't bother if we already have it and it is sync'd */
if (f->state == IONIC_FILTER_STATE_SYNCED) {
spin_unlock_bh(&lif->rx_filters.lock);
return 0;
}
/* mark preemptively as sync'd to block any parallel attempts */
f->state = IONIC_FILTER_STATE_SYNCED;
} else {
/* save as SYNCED to catch any DEL requests while processing */
err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx,
IONIC_FILTER_STATE_SYNCED);
}
spin_unlock_bh(&lif->rx_filters.lock);
if (err)
return err;
/* Don't bother with the write to FW if we know there's no room,
* we can try again on the next sync attempt.
* Since the FW doesn't have a way to tell us the vlan limit,
* we start max_vlans at 0 until we hit the ENOSPC error.
*/
switch (le16_to_cpu(ctx.cmd.rx_filter_add.match)) {
case IONIC_RX_FILTER_MATCH_VLAN:
netdev_dbg(lif->netdev, "%s: rx_filter add VLAN %d\n",
__func__, ctx.cmd.rx_filter_add.vlan.vlan);
if (lif->max_vlans && lif->nvlans >= lif->max_vlans)
err = -ENOSPC;
break;
case IONIC_RX_FILTER_MATCH_MAC:
netdev_dbg(lif->netdev, "%s: rx_filter add ADDR %pM\n",
__func__, ctx.cmd.rx_filter_add.mac.addr);
nfilters = le32_to_cpu(lif->identity->eth.max_ucast_filters);
if ((lif->nucast + lif->nmcast) >= nfilters)
err = -ENOSPC;
break;
}
if (err != -ENOSPC)
err = ionic_adminq_post_wait_nomsg(lif, &ctx);
spin_lock_bh(&lif->rx_filters.lock);
if (err && err != -EEXIST) {
/* set the state back to NEW so we can try again later */
f = ionic_rx_filter_find(lif, &ctx.cmd.rx_filter_add);
if (f && f->state == IONIC_FILTER_STATE_SYNCED) {
f->state = IONIC_FILTER_STATE_NEW;
/* If -ENOSPC we won't waste time trying to sync again
* until there is a delete that might make room
*/
if (err != -ENOSPC)
set_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state);
}
spin_unlock_bh(&lif->rx_filters.lock);
/* store the max_vlans limit that we found */
if (err == -ENOSPC &&
le16_to_cpu(ctx.cmd.rx_filter_add.match) == IONIC_RX_FILTER_MATCH_VLAN)
lif->max_vlans = lif->nvlans;
/* Prevent unnecessary error messages on recoverable
* errors as the filter will get retried on the next
* sync attempt.
*/
switch (err) {
case -ENOSPC:
case -ENXIO:
case -ETIMEDOUT:
case -EAGAIN:
case -EBUSY:
return 0;
default:
break;
}
ionic_adminq_netdev_err_print(lif, ctx.cmd.cmd.opcode,
ctx.comp.comp.status, err);
switch (le16_to_cpu(ctx.cmd.rx_filter_add.match)) {
case IONIC_RX_FILTER_MATCH_VLAN:
netdev_info(lif->netdev, "rx_filter add failed: VLAN %d\n",
ctx.cmd.rx_filter_add.vlan.vlan);
break;
case IONIC_RX_FILTER_MATCH_MAC:
netdev_info(lif->netdev, "rx_filter add failed: ADDR %pM\n",
ctx.cmd.rx_filter_add.mac.addr);
break;
}
return err;
}
switch (le16_to_cpu(ctx.cmd.rx_filter_add.match)) {
case IONIC_RX_FILTER_MATCH_VLAN:
lif->nvlans++;
break;
case IONIC_RX_FILTER_MATCH_MAC:
if (is_multicast_ether_addr(ctx.cmd.rx_filter_add.mac.addr))
lif->nmcast++;
else
lif->nucast++;
break;
}
f = ionic_rx_filter_find(lif, &ctx.cmd.rx_filter_add);
if (f && f->state == IONIC_FILTER_STATE_OLD) {
/* Someone requested a delete while we were adding
* so update the filter info with the results from the add
* and the data will be there for the delete on the next
* sync cycle.
*/
err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx,
IONIC_FILTER_STATE_OLD);
} else {
err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx,
IONIC_FILTER_STATE_SYNCED);
}
spin_unlock_bh(&lif->rx_filters.lock);
return err;
}
int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr)
{
struct ionic_rx_filter_add_cmd ac = {
.match = cpu_to_le16(IONIC_RX_FILTER_MATCH_MAC),
};
memcpy(&ac.mac.addr, addr, ETH_ALEN);
return ionic_lif_filter_add(lif, &ac);
}
int ionic_lif_vlan_add(struct ionic_lif *lif, const u16 vid)
{
struct ionic_rx_filter_add_cmd ac = {
.match = cpu_to_le16(IONIC_RX_FILTER_MATCH_VLAN),
.vlan.vlan = cpu_to_le16(vid),
};
return ionic_lif_filter_add(lif, &ac);
}
static int ionic_lif_filter_del(struct ionic_lif *lif,
struct ionic_rx_filter_add_cmd *ac)
{
struct ionic_admin_ctx ctx = {
.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
.cmd.rx_filter_del = {
.opcode = IONIC_CMD_RX_FILTER_DEL,
.lif_index = cpu_to_le16(lif->index),
},
};
struct ionic_rx_filter *f;
int state;
int err;
spin_lock_bh(&lif->rx_filters.lock);
f = ionic_rx_filter_find(lif, ac);
if (!f) {
spin_unlock_bh(&lif->rx_filters.lock);
return -ENOENT;
}
switch (le16_to_cpu(ac->match)) {
case IONIC_RX_FILTER_MATCH_VLAN:
netdev_dbg(lif->netdev, "%s: rx_filter del VLAN %d id %d\n",
__func__, ac->vlan.vlan, f->filter_id);
lif->nvlans--;
break;
case IONIC_RX_FILTER_MATCH_MAC:
netdev_dbg(lif->netdev, "%s: rx_filter del ADDR %pM id %d\n",
__func__, ac->mac.addr, f->filter_id);
if (is_multicast_ether_addr(ac->mac.addr) && lif->nmcast)
lif->nmcast--;
else if (!is_multicast_ether_addr(ac->mac.addr) && lif->nucast)
lif->nucast--;
break;
}
state = f->state;
ctx.cmd.rx_filter_del.filter_id = cpu_to_le32(f->filter_id);
ionic_rx_filter_free(lif, f);
spin_unlock_bh(&lif->rx_filters.lock);
if (state != IONIC_FILTER_STATE_NEW) {
err = ionic_adminq_post_wait_nomsg(lif, &ctx);
switch (err) {
/* ignore these errors */
case -EEXIST:
case -ENXIO:
case -ETIMEDOUT:
case -EAGAIN:
case -EBUSY:
case 0:
break;
default:
ionic_adminq_netdev_err_print(lif, ctx.cmd.cmd.opcode,
ctx.comp.comp.status, err);
return err;
}
}
return 0;
}
int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr)
{
struct ionic_rx_filter_add_cmd ac = {
.match = cpu_to_le16(IONIC_RX_FILTER_MATCH_MAC),
};
memcpy(&ac.mac.addr, addr, ETH_ALEN);
return ionic_lif_filter_del(lif, &ac);
}
int ionic_lif_vlan_del(struct ionic_lif *lif, const u16 vid)
{
struct ionic_rx_filter_add_cmd ac = {
.match = cpu_to_le16(IONIC_RX_FILTER_MATCH_VLAN),
.vlan.vlan = cpu_to_le16(vid),
};
return ionic_lif_filter_del(lif, &ac);
}
struct sync_item {
struct list_head list;
struct ionic_rx_filter f;
};
void ionic_rx_filter_sync(struct ionic_lif *lif)
{
struct device *dev = lif->ionic->dev;
struct list_head sync_add_list;
struct list_head sync_del_list;
struct sync_item *sync_item;
struct ionic_rx_filter *f;
struct hlist_head *head;
struct hlist_node *tmp;
struct sync_item *spos;
unsigned int i;
INIT_LIST_HEAD(&sync_add_list);
INIT_LIST_HEAD(&sync_del_list);
clear_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state);
/* Copy the filters to be added and deleted
* into a separate local list that needs no locking.
*/
spin_lock_bh(&lif->rx_filters.lock);
for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) {
head = &lif->rx_filters.by_id[i];
hlist_for_each_entry_safe(f, tmp, head, by_id) {
if (f->state == IONIC_FILTER_STATE_NEW ||
f->state == IONIC_FILTER_STATE_OLD) {
sync_item = devm_kzalloc(dev, sizeof(*sync_item),
GFP_ATOMIC);
if (!sync_item)
goto loop_out;
sync_item->f = *f;
if (f->state == IONIC_FILTER_STATE_NEW)
list_add(&sync_item->list, &sync_add_list);
else
list_add(&sync_item->list, &sync_del_list);
}
}
}
loop_out:
spin_unlock_bh(&lif->rx_filters.lock);
/* If the add or delete fails, it won't get marked as sync'd
* and will be tried again in the next sync action.
* Do the deletes first in case we're in an overflow state and
* they can clear room for some new filters
*/
list_for_each_entry_safe(sync_item, spos, &sync_del_list, list) {
ionic_lif_filter_del(lif, &sync_item->f.cmd);
list_del(&sync_item->list);
devm_kfree(dev, sync_item);
}
list_for_each_entry_safe(sync_item, spos, &sync_add_list, list) {
ionic_lif_filter_add(lif, &sync_item->f.cmd);
list_del(&sync_item->list);
devm_kfree(dev, sync_item);
}
}
@@ -0,0 +1,49 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
#ifndef _IONIC_RX_FILTER_H_
#define _IONIC_RX_FILTER_H_
#define IONIC_RXQ_INDEX_ANY (0xFFFF)
enum ionic_filter_state {
IONIC_FILTER_STATE_SYNCED,
IONIC_FILTER_STATE_NEW,
IONIC_FILTER_STATE_OLD,
};
struct ionic_rx_filter {
u32 flow_id;
u32 filter_id;
u16 rxq_index;
enum ionic_filter_state state;
struct ionic_rx_filter_add_cmd cmd;
struct hlist_node by_hash;
struct hlist_node by_id;
};
#define IONIC_RX_FILTER_HASH_BITS 10
#define IONIC_RX_FILTER_HLISTS BIT(IONIC_RX_FILTER_HASH_BITS)
#define IONIC_RX_FILTER_HLISTS_MASK (IONIC_RX_FILTER_HLISTS - 1)
struct ionic_rx_filters {
spinlock_t lock; /* filter list lock */
struct hlist_head by_hash[IONIC_RX_FILTER_HLISTS]; /* by skb hash */
struct hlist_head by_id[IONIC_RX_FILTER_HLISTS]; /* by filter_id */
};
void ionic_rx_filter_free(struct ionic_lif *lif, struct ionic_rx_filter *f);
void ionic_rx_filter_replay(struct ionic_lif *lif);
int ionic_rx_filters_init(struct ionic_lif *lif);
void ionic_rx_filters_deinit(struct ionic_lif *lif);
int ionic_rx_filter_save(struct ionic_lif *lif, u32 flow_id, u16 rxq_index,
u32 hash, struct ionic_admin_ctx *ctx,
enum ionic_filter_state state);
struct ionic_rx_filter *ionic_rx_filter_by_vlan(struct ionic_lif *lif, u16 vid);
struct ionic_rx_filter *ionic_rx_filter_by_addr(struct ionic_lif *lif, const u8 *addr);
struct ionic_rx_filter *ionic_rx_filter_rxsteer(struct ionic_lif *lif);
void ionic_rx_filter_sync(struct ionic_lif *lif);
int ionic_lif_list_addr(struct ionic_lif *lif, const u8 *addr, bool mode);
int ionic_lif_vlan_add(struct ionic_lif *lif, const u16 vid);
int ionic_lif_vlan_del(struct ionic_lif *lif, const u16 vid);
#endif /* _IONIC_RX_FILTER_H_ */
@@ -0,0 +1,431 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
#include <linux/ethtool.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/netdevice.h>
#include "ionic.h"
#include "ionic_lif.h"
#include "ionic_stats.h"
static const struct ionic_stat_desc ionic_lif_stats_desc[] = {
IONIC_LIF_STAT_DESC(tx_packets),
IONIC_LIF_STAT_DESC(tx_bytes),
IONIC_LIF_STAT_DESC(rx_packets),
IONIC_LIF_STAT_DESC(rx_bytes),
IONIC_LIF_STAT_DESC(tx_tso),
IONIC_LIF_STAT_DESC(tx_tso_bytes),
IONIC_LIF_STAT_DESC(tx_csum_none),
IONIC_LIF_STAT_DESC(tx_csum),
IONIC_LIF_STAT_DESC(rx_csum_none),
IONIC_LIF_STAT_DESC(rx_csum_complete),
IONIC_LIF_STAT_DESC(rx_csum_error),
IONIC_LIF_STAT_DESC(hw_tx_dropped),
IONIC_LIF_STAT_DESC(hw_rx_dropped),
IONIC_LIF_STAT_DESC(hw_rx_over_errors),
IONIC_LIF_STAT_DESC(hw_rx_missed_errors),
IONIC_LIF_STAT_DESC(hw_tx_aborted_errors),
IONIC_LIF_STAT_DESC(xdp_drop),
IONIC_LIF_STAT_DESC(xdp_aborted),
IONIC_LIF_STAT_DESC(xdp_pass),
IONIC_LIF_STAT_DESC(xdp_tx),
IONIC_LIF_STAT_DESC(xdp_redirect),
IONIC_LIF_STAT_DESC(xdp_frames),
};
static const struct ionic_stat_desc ionic_port_stats_desc[] = {
IONIC_PORT_STAT_DESC(frames_rx_ok),
IONIC_PORT_STAT_DESC(frames_rx_all),
IONIC_PORT_STAT_DESC(frames_rx_bad_fcs),
IONIC_PORT_STAT_DESC(frames_rx_bad_all),
IONIC_PORT_STAT_DESC(octets_rx_ok),
IONIC_PORT_STAT_DESC(octets_rx_all),
IONIC_PORT_STAT_DESC(frames_rx_unicast),
IONIC_PORT_STAT_DESC(frames_rx_multicast),
IONIC_PORT_STAT_DESC(frames_rx_broadcast),
IONIC_PORT_STAT_DESC(frames_rx_pause),
IONIC_PORT_STAT_DESC(frames_rx_bad_length),
IONIC_PORT_STAT_DESC(frames_rx_undersized),
IONIC_PORT_STAT_DESC(frames_rx_oversized),
IONIC_PORT_STAT_DESC(frames_rx_fragments),
IONIC_PORT_STAT_DESC(frames_rx_jabber),
IONIC_PORT_STAT_DESC(frames_rx_pripause),
IONIC_PORT_STAT_DESC(frames_rx_stomped_crc),
IONIC_PORT_STAT_DESC(frames_rx_too_long),
IONIC_PORT_STAT_DESC(frames_rx_vlan_good),
IONIC_PORT_STAT_DESC(frames_rx_dropped),
IONIC_PORT_STAT_DESC(frames_rx_less_than_64b),
IONIC_PORT_STAT_DESC(frames_rx_64b),
IONIC_PORT_STAT_DESC(frames_rx_65b_127b),
IONIC_PORT_STAT_DESC(frames_rx_128b_255b),
IONIC_PORT_STAT_DESC(frames_rx_256b_511b),
IONIC_PORT_STAT_DESC(frames_rx_512b_1023b),
IONIC_PORT_STAT_DESC(frames_rx_1024b_1518b),
IONIC_PORT_STAT_DESC(frames_rx_1519b_2047b),
IONIC_PORT_STAT_DESC(frames_rx_2048b_4095b),
IONIC_PORT_STAT_DESC(frames_rx_4096b_8191b),
IONIC_PORT_STAT_DESC(frames_rx_8192b_9215b),
IONIC_PORT_STAT_DESC(frames_rx_other),
IONIC_PORT_STAT_DESC(frames_tx_ok),
IONIC_PORT_STAT_DESC(frames_tx_all),
IONIC_PORT_STAT_DESC(frames_tx_bad),
IONIC_PORT_STAT_DESC(octets_tx_ok),
IONIC_PORT_STAT_DESC(octets_tx_total),
IONIC_PORT_STAT_DESC(frames_tx_unicast),
IONIC_PORT_STAT_DESC(frames_tx_multicast),
IONIC_PORT_STAT_DESC(frames_tx_broadcast),
IONIC_PORT_STAT_DESC(frames_tx_pause),
IONIC_PORT_STAT_DESC(frames_tx_pripause),
IONIC_PORT_STAT_DESC(frames_tx_vlan),
IONIC_PORT_STAT_DESC(frames_tx_less_than_64b),
IONIC_PORT_STAT_DESC(frames_tx_64b),
IONIC_PORT_STAT_DESC(frames_tx_65b_127b),
IONIC_PORT_STAT_DESC(frames_tx_128b_255b),
IONIC_PORT_STAT_DESC(frames_tx_256b_511b),
IONIC_PORT_STAT_DESC(frames_tx_512b_1023b),
IONIC_PORT_STAT_DESC(frames_tx_1024b_1518b),
IONIC_PORT_STAT_DESC(frames_tx_1519b_2047b),
IONIC_PORT_STAT_DESC(frames_tx_2048b_4095b),
IONIC_PORT_STAT_DESC(frames_tx_4096b_8191b),
IONIC_PORT_STAT_DESC(frames_tx_8192b_9215b),
IONIC_PORT_STAT_DESC(frames_tx_other),
IONIC_PORT_STAT_DESC(frames_tx_pri_0),
IONIC_PORT_STAT_DESC(frames_tx_pri_1),
IONIC_PORT_STAT_DESC(frames_tx_pri_2),
IONIC_PORT_STAT_DESC(frames_tx_pri_3),
IONIC_PORT_STAT_DESC(frames_tx_pri_4),
IONIC_PORT_STAT_DESC(frames_tx_pri_5),
IONIC_PORT_STAT_DESC(frames_tx_pri_6),
IONIC_PORT_STAT_DESC(frames_tx_pri_7),
IONIC_PORT_STAT_DESC(frames_rx_pri_0),
IONIC_PORT_STAT_DESC(frames_rx_pri_1),
IONIC_PORT_STAT_DESC(frames_rx_pri_2),
IONIC_PORT_STAT_DESC(frames_rx_pri_3),
IONIC_PORT_STAT_DESC(frames_rx_pri_4),
IONIC_PORT_STAT_DESC(frames_rx_pri_5),
IONIC_PORT_STAT_DESC(frames_rx_pri_6),
IONIC_PORT_STAT_DESC(frames_rx_pri_7),
IONIC_PORT_STAT_DESC(tx_pripause_0_1us_count),
IONIC_PORT_STAT_DESC(tx_pripause_1_1us_count),
IONIC_PORT_STAT_DESC(tx_pripause_2_1us_count),
IONIC_PORT_STAT_DESC(tx_pripause_3_1us_count),
IONIC_PORT_STAT_DESC(tx_pripause_4_1us_count),
IONIC_PORT_STAT_DESC(tx_pripause_5_1us_count),
IONIC_PORT_STAT_DESC(tx_pripause_6_1us_count),
IONIC_PORT_STAT_DESC(tx_pripause_7_1us_count),
IONIC_PORT_STAT_DESC(rx_pripause_0_1us_count),
IONIC_PORT_STAT_DESC(rx_pripause_1_1us_count),
IONIC_PORT_STAT_DESC(rx_pripause_2_1us_count),
IONIC_PORT_STAT_DESC(rx_pripause_3_1us_count),
IONIC_PORT_STAT_DESC(rx_pripause_4_1us_count),
IONIC_PORT_STAT_DESC(rx_pripause_5_1us_count),
IONIC_PORT_STAT_DESC(rx_pripause_6_1us_count),
IONIC_PORT_STAT_DESC(rx_pripause_7_1us_count),
IONIC_PORT_STAT_DESC(rx_pause_1us_count),
IONIC_PORT_STAT_DESC(frames_tx_truncated),
};
static const struct ionic_stat_desc ionic_tx_stats_desc[] = {
IONIC_TX_STAT_DESC(pkts),
IONIC_TX_STAT_DESC(bytes),
IONIC_TX_STAT_DESC(clean),
IONIC_TX_STAT_DESC(dma_map_err),
IONIC_TX_STAT_DESC(linearize),
IONIC_TX_STAT_DESC(frags),
IONIC_TX_STAT_DESC(tso),
IONIC_TX_STAT_DESC(tso_bytes),
IONIC_TX_STAT_DESC(hwstamp_valid),
IONIC_TX_STAT_DESC(hwstamp_invalid),
IONIC_TX_STAT_DESC(csum_none),
IONIC_TX_STAT_DESC(csum),
IONIC_TX_STAT_DESC(vlan_inserted),
IONIC_TX_STAT_DESC(xdp_frames),
};
static const struct ionic_stat_desc ionic_rx_stats_desc[] = {
IONIC_RX_STAT_DESC(pkts),
IONIC_RX_STAT_DESC(bytes),
IONIC_RX_STAT_DESC(dma_map_err),
IONIC_RX_STAT_DESC(alloc_err),
IONIC_RX_STAT_DESC(csum_none),
IONIC_RX_STAT_DESC(csum_complete),
IONIC_RX_STAT_DESC(csum_error),
IONIC_RX_STAT_DESC(hwstamp_valid),
IONIC_RX_STAT_DESC(hwstamp_invalid),
IONIC_RX_STAT_DESC(dropped),
IONIC_RX_STAT_DESC(vlan_stripped),
IONIC_RX_STAT_DESC(xdp_drop),
IONIC_RX_STAT_DESC(xdp_aborted),
IONIC_RX_STAT_DESC(xdp_pass),
IONIC_RX_STAT_DESC(xdp_tx),
IONIC_RX_STAT_DESC(xdp_redirect),
};
#define IONIC_NUM_LIF_STATS ARRAY_SIZE(ionic_lif_stats_desc)
#define IONIC_NUM_PORT_STATS ARRAY_SIZE(ionic_port_stats_desc)
#define IONIC_NUM_TX_STATS ARRAY_SIZE(ionic_tx_stats_desc)
#define IONIC_NUM_RX_STATS ARRAY_SIZE(ionic_rx_stats_desc)
#define IONIC_NUM_EXTRA_PORT_STATS 1
#define MAX_Q(lif) ((lif)->netdev->real_num_tx_queues)
static void ionic_add_lif_txq_stats(struct ionic_lif *lif, int q_num,
struct ionic_lif_sw_stats *stats)
{
struct ionic_tx_stats *txstats = &lif->txqstats[q_num];
stats->tx_packets += txstats->pkts;
stats->tx_bytes += txstats->bytes;
stats->tx_tso += txstats->tso;
stats->tx_tso_bytes += txstats->tso_bytes;
stats->tx_csum_none += txstats->csum_none;
stats->tx_csum += txstats->csum;
stats->tx_hwstamp_valid += txstats->hwstamp_valid;
stats->tx_hwstamp_invalid += txstats->hwstamp_invalid;
stats->xdp_frames += txstats->xdp_frames;
}
static void ionic_add_lif_rxq_stats(struct ionic_lif *lif, int q_num,
struct ionic_lif_sw_stats *stats)
{
struct ionic_rx_stats *rxstats = &lif->rxqstats[q_num];
stats->rx_packets += rxstats->pkts;
stats->rx_bytes += rxstats->bytes;
stats->rx_csum_none += rxstats->csum_none;
stats->rx_csum_complete += rxstats->csum_complete;
stats->rx_csum_error += rxstats->csum_error;
stats->rx_hwstamp_valid += rxstats->hwstamp_valid;
stats->rx_hwstamp_invalid += rxstats->hwstamp_invalid;
stats->xdp_drop += rxstats->xdp_drop;
stats->xdp_aborted += rxstats->xdp_aborted;
stats->xdp_pass += rxstats->xdp_pass;
stats->xdp_tx += rxstats->xdp_tx;
stats->xdp_redirect += rxstats->xdp_redirect;
}
static void ionic_get_lif_stats(struct ionic_lif *lif,
struct ionic_lif_sw_stats *stats)
{
struct rtnl_link_stats64 ns;
int q_num;
memset(stats, 0, sizeof(*stats));
for (q_num = 0; q_num < MAX_Q(lif); q_num++) {
ionic_add_lif_txq_stats(lif, q_num, stats);
ionic_add_lif_rxq_stats(lif, q_num, stats);
}
if (lif->hwstamp_txq)
ionic_add_lif_txq_stats(lif, lif->hwstamp_txq->q.index, stats);
if (lif->hwstamp_rxq)
ionic_add_lif_rxq_stats(lif, lif->hwstamp_rxq->q.index, stats);
ionic_get_stats64(lif->netdev, &ns);
stats->hw_tx_dropped = ns.tx_dropped;
stats->hw_rx_dropped = ns.rx_dropped;
stats->hw_rx_over_errors = ns.rx_over_errors;
stats->hw_rx_missed_errors = ns.rx_missed_errors;
stats->hw_tx_aborted_errors = ns.tx_aborted_errors;
}
static u32 ionic_extra_port_stats_get_count(struct ionic_lif *lif)
{
struct ionic_dev *idev = &lif->ionic->idev;
struct ionic_port_extra_stats *pes_cache;
u32 count = 0;
if (!(lif->ionic->ident.dev.capabilities &
cpu_to_le64(IONIC_DEV_CAP_EXTRA_STATS)))
return count;
pes_cache = &idev->port_extra_stats_cache;
/* Treat all of the extra port stats as invalid in subsequent calls if
* port_info isn't set; otherwise cache a valid snapshot for them.
*/
if (!idev->port_info) {
memset(pes_cache, 0xff, sizeof(*pes_cache));
return count;
}
*pes_cache = idev->port_info->extra_stats;
if (pes_cache->rx_bits_phy != IONIC_STAT_INVALID)
count++;
return count;
}
static u64 ionic_sw_stats_get_count(struct ionic_lif *lif)
{
u64 total = 0, tx_queues = MAX_Q(lif), rx_queues = MAX_Q(lif);
if (lif->hwstamp_txq)
tx_queues += 1;
if (lif->hwstamp_rxq)
rx_queues += 1;
total += IONIC_NUM_LIF_STATS;
total += IONIC_NUM_PORT_STATS + ionic_extra_port_stats_get_count(lif);
total += tx_queues * IONIC_NUM_TX_STATS;
total += rx_queues * IONIC_NUM_RX_STATS;
return total;
}
static void ionic_sw_stats_get_tx_strings(struct ionic_lif *lif, u8 **buf,
int q_num)
{
int i;
for (i = 0; i < IONIC_NUM_TX_STATS; i++)
ethtool_sprintf(buf, "tx_%d_%s", q_num,
ionic_tx_stats_desc[i].name);
}
static void ionic_sw_stats_get_rx_strings(struct ionic_lif *lif, u8 **buf,
int q_num)
{
int i;
for (i = 0; i < IONIC_NUM_RX_STATS; i++)
ethtool_sprintf(buf, "rx_%d_%s", q_num,
ionic_rx_stats_desc[i].name);
}
static void ionic_extra_port_stats_get_strings(struct ionic_lif *lif, u8 **buf)
{
struct ionic_port_extra_stats *pes_cache;
if (!(lif->ionic->ident.dev.capabilities &
cpu_to_le64(IONIC_DEV_CAP_EXTRA_STATS)))
return;
pes_cache = &lif->ionic->idev.port_extra_stats_cache;
if (pes_cache->rx_bits_phy != IONIC_STAT_INVALID)
ethtool_puts(buf, "rx_bits_phy");
}
static void ionic_sw_stats_get_strings(struct ionic_lif *lif, u8 **buf)
{
int i, q_num;
for (i = 0; i < IONIC_NUM_LIF_STATS; i++)
ethtool_puts(buf, ionic_lif_stats_desc[i].name);
for (i = 0; i < IONIC_NUM_PORT_STATS; i++)
ethtool_puts(buf, ionic_port_stats_desc[i].name);
ionic_extra_port_stats_get_strings(lif, buf);
for (q_num = 0; q_num < MAX_Q(lif); q_num++)
ionic_sw_stats_get_tx_strings(lif, buf, q_num);
if (lif->hwstamp_txq)
ionic_sw_stats_get_tx_strings(lif, buf, lif->hwstamp_txq->q.index);
for (q_num = 0; q_num < MAX_Q(lif); q_num++)
ionic_sw_stats_get_rx_strings(lif, buf, q_num);
if (lif->hwstamp_rxq)
ionic_sw_stats_get_rx_strings(lif, buf, lif->hwstamp_rxq->q.index);
}
static void ionic_sw_stats_get_txq_values(struct ionic_lif *lif, u64 **buf,
int q_num)
{
struct ionic_tx_stats *txstats;
int i;
txstats = &lif->txqstats[q_num];
for (i = 0; i < IONIC_NUM_TX_STATS; i++) {
**buf = IONIC_READ_STAT64(txstats, &ionic_tx_stats_desc[i]);
(*buf)++;
}
}
static void ionic_sw_stats_get_rxq_values(struct ionic_lif *lif, u64 **buf,
int q_num)
{
struct ionic_rx_stats *rxstats;
int i;
rxstats = &lif->rxqstats[q_num];
for (i = 0; i < IONIC_NUM_RX_STATS; i++) {
**buf = IONIC_READ_STAT64(rxstats, &ionic_rx_stats_desc[i]);
(*buf)++;
}
}
static void ionic_extra_port_stats_get_values(struct ionic_lif *lif, u64 **buf)
{
struct ionic_port_extra_stats *pes_cache;
if (!(lif->ionic->ident.dev.capabilities &
cpu_to_le64(IONIC_DEV_CAP_EXTRA_STATS)))
return;
/* The number of statistics added to @buf here must equal
* ionic_extra_port_stats_get_count().
*/
pes_cache = &lif->ionic->idev.port_extra_stats_cache;
if (pes_cache->rx_bits_phy != IONIC_STAT_INVALID) {
**buf = le64_to_cpu(pes_cache->rx_bits_phy);
(*buf)++;
}
}
static void ionic_sw_stats_get_values(struct ionic_lif *lif, u64 **buf)
{
struct ionic_port_stats *port_stats;
struct ionic_lif_sw_stats lif_stats;
int i, q_num;
ionic_get_lif_stats(lif, &lif_stats);
for (i = 0; i < IONIC_NUM_LIF_STATS; i++) {
**buf = IONIC_READ_STAT64(&lif_stats, &ionic_lif_stats_desc[i]);
(*buf)++;
}
port_stats = &lif->ionic->idev.port_info->stats;
for (i = 0; i < IONIC_NUM_PORT_STATS; i++) {
**buf = IONIC_READ_STAT_LE64(port_stats,
&ionic_port_stats_desc[i]);
(*buf)++;
}
ionic_extra_port_stats_get_values(lif, buf);
for (q_num = 0; q_num < MAX_Q(lif); q_num++)
ionic_sw_stats_get_txq_values(lif, buf, q_num);
if (lif->hwstamp_txq)
ionic_sw_stats_get_txq_values(lif, buf, lif->hwstamp_txq->q.index);
for (q_num = 0; q_num < MAX_Q(lif); q_num++)
ionic_sw_stats_get_rxq_values(lif, buf, q_num);
if (lif->hwstamp_rxq)
ionic_sw_stats_get_rxq_values(lif, buf, lif->hwstamp_rxq->q.index);
}
const struct ionic_stats_group_intf ionic_stats_groups[] = {
/* SW Stats group */
{
.get_strings = ionic_sw_stats_get_strings,
.get_values = ionic_sw_stats_get_values,
.get_count = ionic_sw_stats_get_count,
},
/* Add more stat groups here */
};
const int ionic_num_stats_grps = ARRAY_SIZE(ionic_stats_groups);
@@ -0,0 +1,61 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
#ifndef _IONIC_STATS_H_
#define _IONIC_STATS_H_
#define IONIC_STAT_INVALID (cpu_to_le64(~0ULL))
#define IONIC_STAT_TO_OFFSET(type, stat_name) (offsetof(type, stat_name))
#define IONIC_STAT_DESC(type, stat_name) { \
.name = #stat_name, \
.offset = IONIC_STAT_TO_OFFSET(type, stat_name) \
}
#define IONIC_PORT_STAT_DESC(stat_name) \
IONIC_STAT_DESC(struct ionic_port_stats, stat_name)
#define IONIC_LIF_STAT_DESC(stat_name) \
IONIC_STAT_DESC(struct ionic_lif_sw_stats, stat_name)
#define IONIC_TX_STAT_DESC(stat_name) \
IONIC_STAT_DESC(struct ionic_tx_stats, stat_name)
#define IONIC_RX_STAT_DESC(stat_name) \
IONIC_STAT_DESC(struct ionic_rx_stats, stat_name)
#define IONIC_TX_Q_STAT_DESC(stat_name) \
IONIC_STAT_DESC(struct ionic_queue, stat_name)
#define IONIC_CQ_STAT_DESC(stat_name) \
IONIC_STAT_DESC(struct ionic_cq, stat_name)
#define IONIC_INTR_STAT_DESC(stat_name) \
IONIC_STAT_DESC(struct ionic_intr_info, stat_name)
#define IONIC_NAPI_STAT_DESC(stat_name) \
IONIC_STAT_DESC(struct ionic_napi_stats, stat_name)
/* Interface structure for a particalar stats group */
struct ionic_stats_group_intf {
void (*get_strings)(struct ionic_lif *lif, u8 **buf);
void (*get_values)(struct ionic_lif *lif, u64 **buf);
u64 (*get_count)(struct ionic_lif *lif);
};
extern const struct ionic_stats_group_intf ionic_stats_groups[];
extern const int ionic_num_stats_grps;
#define IONIC_READ_STAT64(base_ptr, desc_ptr) \
(*((u64 *)(((u8 *)(base_ptr)) + (desc_ptr)->offset)))
#define IONIC_READ_STAT_LE64(base_ptr, desc_ptr) \
__le64_to_cpu(*((__le64 *)(((u8 *)(base_ptr)) + (desc_ptr)->offset)))
struct ionic_stat_desc {
char name[ETH_GSTRING_LEN];
u64 offset;
};
#endif /* _IONIC_STATS_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
#ifndef _IONIC_TXRX_H_
#define _IONIC_TXRX_H_
struct bpf_prog;
void ionic_tx_flush(struct ionic_cq *cq);
void ionic_rx_fill(struct ionic_queue *q, struct bpf_prog *xdp_prog);
void ionic_rx_empty(struct ionic_queue *q);
void ionic_tx_empty(struct ionic_queue *q);
int ionic_rx_napi(struct napi_struct *napi, int budget);
int ionic_tx_napi(struct napi_struct *napi, int budget);
int ionic_txrx_napi(struct napi_struct *napi, int budget);
netdev_tx_t ionic_start_xmit(struct sk_buff *skb, struct net_device *netdev);
bool ionic_rx_service(struct ionic_cq *cq);
int ionic_xdp_xmit(struct net_device *netdev, int n, struct xdp_frame **xdp, u32 flags);
#endif /* _IONIC_TXRX_H_ */