aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/cavium/liquidio/lio_ethtool.c')
-rw-r--r--drivers/net/ethernet/cavium/liquidio/lio_ethtool.c352
1 files changed, 312 insertions, 40 deletions
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c
index ebd353bc78ff..a63ddf07f168 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c
@@ -31,6 +31,7 @@
#include "cn23xx_pf_device.h"
#include "cn23xx_vf_device.h"
+static int lio_reset_queues(struct net_device *netdev, uint32_t num_qs);
static int octnet_get_link_stats(struct net_device *netdev);
struct oct_intrmod_context {
@@ -105,6 +106,7 @@ static const char oct_stats_strings[][ETH_GSTRING_LEN] = {
"tx_total_sent",
"tx_total_fwd",
"tx_err_pko",
+ "tx_err_pki",
"tx_err_link",
"tx_err_drop",
@@ -299,6 +301,35 @@ lio_get_vf_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
strncpy(drvinfo->bus_info, pci_name(oct->pci_dev), 32);
}
+static int
+lio_send_queue_count_update(struct net_device *netdev, uint32_t num_queues)
+{
+ struct lio *lio = GET_LIO(netdev);
+ struct octeon_device *oct = lio->oct_dev;
+ struct octnic_ctrl_pkt nctrl;
+ int ret = 0;
+
+ memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt));
+
+ nctrl.ncmd.u64 = 0;
+ nctrl.ncmd.s.cmd = OCTNET_CMD_QUEUE_COUNT_CTL;
+ nctrl.ncmd.s.param1 = num_queues;
+ nctrl.ncmd.s.param2 = num_queues;
+ nctrl.iq_no = lio->linfo.txpciq[0].s.q_no;
+ nctrl.wait_time = 100;
+ nctrl.netpndev = (u64)netdev;
+ nctrl.cb_fn = liquidio_link_ctrl_cmd_completion;
+
+ ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl);
+ if (ret < 0) {
+ dev_err(&oct->pci_dev->dev, "Failed to send Queue reset command (ret: 0x%x)\n",
+ ret);
+ return -1;
+ }
+
+ return 0;
+}
+
static void
lio_ethtool_get_channels(struct net_device *dev,
struct ethtool_channels *channel)
@@ -306,6 +337,7 @@ lio_ethtool_get_channels(struct net_device *dev,
struct lio *lio = GET_LIO(dev);
struct octeon_device *oct = lio->oct_dev;
u32 max_rx = 0, max_tx = 0, tx_count = 0, rx_count = 0;
+ u32 combined_count = 0, max_combined = 0;
if (OCTEON_CN6XXX(oct)) {
struct octeon_config *conf6x = CHIP_CONF(oct, cn6xxx);
@@ -315,22 +347,137 @@ lio_ethtool_get_channels(struct net_device *dev,
rx_count = CFG_GET_NUM_RXQS_NIC_IF(conf6x, lio->ifidx);
tx_count = CFG_GET_NUM_TXQS_NIC_IF(conf6x, lio->ifidx);
} else if (OCTEON_CN23XX_PF(oct)) {
-
- max_rx = oct->sriov_info.num_pf_rings;
- max_tx = oct->sriov_info.num_pf_rings;
- rx_count = lio->linfo.num_rxpciq;
- tx_count = lio->linfo.num_txpciq;
+ max_combined = lio->linfo.num_txpciq;
+ combined_count = oct->num_iqs;
} else if (OCTEON_CN23XX_VF(oct)) {
- max_tx = oct->sriov_info.rings_per_vf;
- max_rx = oct->sriov_info.rings_per_vf;
- rx_count = lio->linfo.num_rxpciq;
- tx_count = lio->linfo.num_txpciq;
+ u64 reg_val = 0ULL;
+ u64 ctrl = CN23XX_VF_SLI_IQ_PKT_CONTROL64(0);
+
+ reg_val = octeon_read_csr64(oct, ctrl);
+ reg_val = reg_val >> CN23XX_PKT_INPUT_CTL_RPVF_POS;
+ max_combined = reg_val & CN23XX_PKT_INPUT_CTL_RPVF_MASK;
+ combined_count = oct->num_iqs;
}
channel->max_rx = max_rx;
channel->max_tx = max_tx;
+ channel->max_combined = max_combined;
channel->rx_count = rx_count;
channel->tx_count = tx_count;
+ channel->combined_count = combined_count;
+}
+
+static int
+lio_irq_reallocate_irqs(struct octeon_device *oct, uint32_t num_ioqs)
+{
+ struct msix_entry *msix_entries;
+ int num_msix_irqs = 0;
+ int i;
+
+ if (!oct->msix_on)
+ return 0;
+
+ /* Disable the input and output queues now. No more packets will
+ * arrive from Octeon.
+ */
+ oct->fn_list.disable_interrupt(oct, OCTEON_ALL_INTR);
+
+ if (oct->msix_on) {
+ if (OCTEON_CN23XX_PF(oct))
+ num_msix_irqs = oct->num_msix_irqs - 1;
+ else if (OCTEON_CN23XX_VF(oct))
+ num_msix_irqs = oct->num_msix_irqs;
+
+ msix_entries = (struct msix_entry *)oct->msix_entries;
+ for (i = 0; i < num_msix_irqs; i++) {
+ if (oct->ioq_vector[i].vector) {
+ /* clear the affinity_cpumask */
+ irq_set_affinity_hint(msix_entries[i].vector,
+ NULL);
+ free_irq(msix_entries[i].vector,
+ &oct->ioq_vector[i]);
+ oct->ioq_vector[i].vector = 0;
+ }
+ }
+
+ /* non-iov vector's argument is oct struct */
+ if (OCTEON_CN23XX_PF(oct))
+ free_irq(msix_entries[i].vector, oct);
+
+ pci_disable_msix(oct->pci_dev);
+ kfree(oct->msix_entries);
+ oct->msix_entries = NULL;
+ }
+
+ kfree(oct->irq_name_storage);
+ oct->irq_name_storage = NULL;
+ if (octeon_setup_interrupt(oct, num_ioqs)) {
+ dev_info(&oct->pci_dev->dev, "Setup interrupt failed\n");
+ return 1;
+ }
+
+ /* Enable Octeon device interrupts */
+ oct->fn_list.enable_interrupt(oct, OCTEON_ALL_INTR);
+
+ return 0;
+}
+
+static int
+lio_ethtool_set_channels(struct net_device *dev,
+ struct ethtool_channels *channel)
+{
+ u32 combined_count, max_combined;
+ struct lio *lio = GET_LIO(dev);
+ struct octeon_device *oct = lio->oct_dev;
+ int stopped = 0;
+
+ if (strcmp(oct->fw_info.liquidio_firmware_version, "1.6.1") < 0) {
+ dev_err(&oct->pci_dev->dev, "Minimum firmware version required is 1.6.1\n");
+ return -EINVAL;
+ }
+
+ if (!channel->combined_count || channel->other_count ||
+ channel->rx_count || channel->tx_count)
+ return -EINVAL;
+
+ combined_count = channel->combined_count;
+
+ if (OCTEON_CN23XX_PF(oct)) {
+ max_combined = channel->max_combined;
+ } else if (OCTEON_CN23XX_VF(oct)) {
+ u64 reg_val = 0ULL;
+ u64 ctrl = CN23XX_VF_SLI_IQ_PKT_CONTROL64(0);
+
+ reg_val = octeon_read_csr64(oct, ctrl);
+ reg_val = reg_val >> CN23XX_PKT_INPUT_CTL_RPVF_POS;
+ max_combined = reg_val & CN23XX_PKT_INPUT_CTL_RPVF_MASK;
+ } else {
+ return -EINVAL;
+ }
+
+ if (combined_count > max_combined || combined_count < 1)
+ return -EINVAL;
+
+ if (combined_count == oct->num_iqs)
+ return 0;
+
+ ifstate_set(lio, LIO_IFSTATE_RESETTING);
+
+ if (netif_running(dev)) {
+ dev->netdev_ops->ndo_stop(dev);
+ stopped = 1;
+ }
+
+ if (lio_reset_queues(dev, combined_count))
+ return -EINVAL;
+
+ lio_irq_reallocate_irqs(oct, combined_count);
+ if (stopped)
+ dev->netdev_ops->ndo_open(dev);
+
+ ifstate_reset(lio, LIO_IFSTATE_RESETTING);
+
+ return 0;
}
static int lio_get_eeprom_len(struct net_device *netdev)
@@ -577,23 +724,18 @@ static int lio_set_phys_id(struct net_device *netdev,
break;
case ETHTOOL_ID_ON:
- if (oct->chip_id == OCTEON_CN66XX) {
+ if (oct->chip_id == OCTEON_CN66XX)
octnet_gpio_access(netdev, VITESSE_PHY_GPIO_CFG,
VITESSE_PHY_GPIO_HIGH);
-
- } else if (oct->chip_id == OCTEON_CN68XX) {
- return -EINVAL;
- } else {
+ else
return -EINVAL;
- }
+
break;
case ETHTOOL_ID_OFF:
if (oct->chip_id == OCTEON_CN66XX)
octnet_gpio_access(netdev, VITESSE_PHY_GPIO_CFG,
VITESSE_PHY_GPIO_LOW);
- else if (oct->chip_id == OCTEON_CN68XX)
- return -EINVAL;
else
return -EINVAL;
@@ -641,6 +783,9 @@ lio_ethtool_get_ringparam(struct net_device *netdev,
u32 tx_max_pending = 0, rx_max_pending = 0, tx_pending = 0,
rx_pending = 0;
+ if (ifstate_check(lio, LIO_IFSTATE_RESETTING))
+ return;
+
if (OCTEON_CN6XXX(oct)) {
struct octeon_config *conf6x = CHIP_CONF(oct, cn6xxx);
@@ -648,33 +793,147 @@ lio_ethtool_get_ringparam(struct net_device *netdev,
rx_max_pending = CN6XXX_MAX_OQ_DESCRIPTORS;
rx_pending = CFG_GET_NUM_RX_DESCS_NIC_IF(conf6x, lio->ifidx);
tx_pending = CFG_GET_NUM_TX_DESCS_NIC_IF(conf6x, lio->ifidx);
- } else if (OCTEON_CN23XX_PF(oct)) {
- struct octeon_config *conf23 = CHIP_CONF(oct, cn23xx_pf);
-
+ } else if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct)) {
tx_max_pending = CN23XX_MAX_IQ_DESCRIPTORS;
rx_max_pending = CN23XX_MAX_OQ_DESCRIPTORS;
- rx_pending = CFG_GET_NUM_RX_DESCS_NIC_IF(conf23, lio->ifidx);
- tx_pending = CFG_GET_NUM_TX_DESCS_NIC_IF(conf23, lio->ifidx);
- }
-
- if (lio->mtu > OCTNET_DEFAULT_FRM_SIZE - OCTNET_FRM_HEADER_SIZE) {
- ering->rx_pending = 0;
- ering->rx_max_pending = 0;
- ering->rx_mini_pending = 0;
- ering->rx_jumbo_pending = rx_pending;
- ering->rx_mini_max_pending = 0;
- ering->rx_jumbo_max_pending = rx_max_pending;
- } else {
- ering->rx_pending = rx_pending;
- ering->rx_max_pending = rx_max_pending;
- ering->rx_mini_pending = 0;
- ering->rx_jumbo_pending = 0;
- ering->rx_mini_max_pending = 0;
- ering->rx_jumbo_max_pending = 0;
+ rx_pending = oct->droq[0]->max_count;
+ tx_pending = oct->instr_queue[0]->max_count;
}
ering->tx_pending = tx_pending;
ering->tx_max_pending = tx_max_pending;
+ ering->rx_pending = rx_pending;
+ ering->rx_max_pending = rx_max_pending;
+ ering->rx_mini_pending = 0;
+ ering->rx_jumbo_pending = 0;
+ ering->rx_mini_max_pending = 0;
+ ering->rx_jumbo_max_pending = 0;
+}
+
+static int lio_reset_queues(struct net_device *netdev, uint32_t num_qs)
+{
+ struct lio *lio = GET_LIO(netdev);
+ struct octeon_device *oct = lio->oct_dev;
+ struct napi_struct *napi, *n;
+ int i, update = 0;
+
+ if (wait_for_pending_requests(oct))
+ dev_err(&oct->pci_dev->dev, "There were pending requests\n");
+
+ if (lio_wait_for_instr_fetch(oct))
+ dev_err(&oct->pci_dev->dev, "IQ had pending instructions\n");
+
+ if (octeon_set_io_queues_off(oct)) {
+ dev_err(&oct->pci_dev->dev, "setting io queues off failed\n");
+ return -1;
+ }
+
+ /* Disable the input and output queues now. No more packets will
+ * arrive from Octeon.
+ */
+ oct->fn_list.disable_io_queues(oct);
+ /* Delete NAPI */
+ list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list)
+ netif_napi_del(napi);
+
+ if (num_qs != oct->num_iqs) {
+ netif_set_real_num_rx_queues(netdev, num_qs);
+ netif_set_real_num_tx_queues(netdev, num_qs);
+ update = 1;
+ }
+
+ for (i = 0; i < MAX_OCTEON_OUTPUT_QUEUES(oct); i++) {
+ if (!(oct->io_qmask.oq & BIT_ULL(i)))
+ continue;
+ octeon_delete_droq(oct, i);
+ }
+
+ for (i = 0; i < MAX_OCTEON_INSTR_QUEUES(oct); i++) {
+ if (!(oct->io_qmask.iq & BIT_ULL(i)))
+ continue;
+ octeon_delete_instr_queue(oct, i);
+ }
+
+ if (oct->fn_list.setup_device_regs(oct)) {
+ dev_err(&oct->pci_dev->dev, "Failed to configure device registers\n");
+ return -1;
+ }
+
+ if (liquidio_setup_io_queues(oct, 0, num_qs, num_qs)) {
+ dev_err(&oct->pci_dev->dev, "IO queues initialization failed\n");
+ return -1;
+ }
+
+ /* Enable the input and output queues for this Octeon device */
+ if (oct->fn_list.enable_io_queues(oct)) {
+ dev_err(&oct->pci_dev->dev, "Failed to enable input/output queues");
+ return -1;
+ }
+
+ if (update && lio_send_queue_count_update(netdev, num_qs))
+ return -1;
+
+ return 0;
+}
+
+static int lio_ethtool_set_ringparam(struct net_device *netdev,
+ struct ethtool_ringparam *ering)
+{
+ u32 rx_count, tx_count, rx_count_old, tx_count_old;
+ struct lio *lio = GET_LIO(netdev);
+ struct octeon_device *oct = lio->oct_dev;
+ int stopped = 0;
+
+ if (!OCTEON_CN23XX_PF(oct) && !OCTEON_CN23XX_VF(oct))
+ return -EINVAL;
+
+ if (ering->rx_mini_pending || ering->rx_jumbo_pending)
+ return -EINVAL;
+
+ rx_count = clamp_t(u32, ering->rx_pending, CN23XX_MIN_OQ_DESCRIPTORS,
+ CN23XX_MAX_OQ_DESCRIPTORS);
+ tx_count = clamp_t(u32, ering->tx_pending, CN23XX_MIN_IQ_DESCRIPTORS,
+ CN23XX_MAX_IQ_DESCRIPTORS);
+
+ rx_count_old = oct->droq[0]->max_count;
+ tx_count_old = oct->instr_queue[0]->max_count;
+
+ if (rx_count == rx_count_old && tx_count == tx_count_old)
+ return 0;
+
+ ifstate_set(lio, LIO_IFSTATE_RESETTING);
+
+ if (netif_running(netdev)) {
+ netdev->netdev_ops->ndo_stop(netdev);
+ stopped = 1;
+ }
+
+ /* Change RX/TX DESCS count */
+ if (tx_count != tx_count_old)
+ CFG_SET_NUM_TX_DESCS_NIC_IF(octeon_get_conf(oct), lio->ifidx,
+ tx_count);
+ if (rx_count != rx_count_old)
+ CFG_SET_NUM_RX_DESCS_NIC_IF(octeon_get_conf(oct), lio->ifidx,
+ rx_count);
+
+ if (lio_reset_queues(netdev, lio->linfo.num_txpciq))
+ goto err_lio_reset_queues;
+
+ if (stopped)
+ netdev->netdev_ops->ndo_open(netdev);
+
+ ifstate_reset(lio, LIO_IFSTATE_RESETTING);
+
+ return 0;
+
+err_lio_reset_queues:
+ if (tx_count != tx_count_old)
+ CFG_SET_NUM_TX_DESCS_NIC_IF(octeon_get_conf(oct), lio->ifidx,
+ tx_count_old);
+ if (rx_count != rx_count_old)
+ CFG_SET_NUM_RX_DESCS_NIC_IF(octeon_get_conf(oct), lio->ifidx,
+ rx_count_old);
+ return -EINVAL;
}
static u32 lio_get_msglevel(struct net_device *netdev)
@@ -795,6 +1054,9 @@ lio_get_ethtool_stats(struct net_device *netdev,
struct net_device_stats *netstats = &netdev->stats;
int i = 0, j;
+ if (ifstate_check(lio, LIO_IFSTATE_RESETTING))
+ return;
+
netdev->netdev_ops->ndo_get_stats(netdev);
octnet_get_link_stats(netdev);
@@ -826,6 +1088,8 @@ lio_get_ethtool_stats(struct net_device *netdev,
data[i++] = CVM_CAST64(oct_dev->link_stats.fromhost.fw_total_fwd);
/*per_core_stats[j].link_stats[i].fromhost.fw_err_pko */
data[i++] = CVM_CAST64(oct_dev->link_stats.fromhost.fw_err_pko);
+ /*per_core_stats[j].link_stats[i].fromhost.fw_err_pki */
+ data[i++] = CVM_CAST64(oct_dev->link_stats.fromhost.fw_err_pki);
/*per_core_stats[j].link_stats[i].fromhost.fw_err_link */
data[i++] = CVM_CAST64(oct_dev->link_stats.fromhost.fw_err_link);
/*per_core_stats[cvmx_get_core_num()].link_stats[idx].fromhost.
@@ -1057,6 +1321,9 @@ static void lio_vf_get_ethtool_stats(struct net_device *netdev,
struct octeon_device *oct_dev = lio->oct_dev;
int i = 0, j, vj;
+ if (ifstate_check(lio, LIO_IFSTATE_RESETTING))
+ return;
+
netdev->netdev_ops->ndo_get_stats(netdev);
/* sum of oct->droq[oq_no]->stats->rx_pkts_received */
data[i++] = CVM_CAST64(netstats->rx_packets);
@@ -1079,7 +1346,7 @@ static void lio_vf_get_ethtool_stats(struct net_device *netdev,
/* lio->link_changes */
data[i++] = CVM_CAST64(lio->link_changes);
- for (vj = 0; vj < lio->linfo.num_txpciq; vj++) {
+ for (vj = 0; vj < oct_dev->num_iqs; vj++) {
j = lio->linfo.txpciq[vj].s.q_no;
/* packets to network port */
@@ -1121,7 +1388,7 @@ static void lio_vf_get_ethtool_stats(struct net_device *netdev,
}
/* RX */
- for (vj = 0; vj < lio->linfo.num_rxpciq; vj++) {
+ for (vj = 0; vj < oct_dev->num_oqs; vj++) {
j = lio->linfo.rxpciq[vj].s.q_no;
/* packets send to TCP/IP network stack */
@@ -1568,6 +1835,7 @@ octnet_nic_stats_callback(struct octeon_device *oct_dev,
tstats->fw_total_sent = rsp_tstats->fw_total_sent;
tstats->fw_total_fwd = rsp_tstats->fw_total_fwd;
tstats->fw_err_pko = rsp_tstats->fw_err_pko;
+ tstats->fw_err_pki = rsp_tstats->fw_err_pki;
tstats->fw_err_link = rsp_tstats->fw_err_link;
tstats->fw_err_drop = rsp_tstats->fw_err_drop;
tstats->fw_tso = rsp_tstats->fw_tso;
@@ -2587,7 +2855,9 @@ static const struct ethtool_ops lio_ethtool_ops = {
.get_link = ethtool_op_get_link,
.get_drvinfo = lio_get_drvinfo,
.get_ringparam = lio_ethtool_get_ringparam,
+ .set_ringparam = lio_ethtool_set_ringparam,
.get_channels = lio_ethtool_get_channels,
+ .set_channels = lio_ethtool_set_channels,
.set_phys_id = lio_set_phys_id,
.get_eeprom_len = lio_get_eeprom_len,
.get_eeprom = lio_get_eeprom,
@@ -2612,7 +2882,9 @@ static const struct ethtool_ops lio_vf_ethtool_ops = {
.get_link = ethtool_op_get_link,
.get_drvinfo = lio_get_vf_drvinfo,
.get_ringparam = lio_ethtool_get_ringparam,
+ .set_ringparam = lio_ethtool_set_ringparam,
.get_channels = lio_ethtool_get_channels,
+ .set_channels = lio_ethtool_set_channels,
.get_strings = lio_vf_get_strings,
.get_ethtool_stats = lio_vf_get_ethtool_stats,
.get_regs_len = lio_get_regs_len,