diff options
Diffstat (limited to 'drivers/net/ethernet/pensando/ionic')
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic.h | 7 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c | 36 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_dev.c | 164 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_dev.h | 6 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_ethtool.c | 6 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_if.h | 6 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_lif.c | 327 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_lif.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_main.c | 129 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c | 37 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_stats.c | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_txrx.c | 72 |
12 files changed, 544 insertions, 249 deletions
diff --git a/drivers/net/ethernet/pensando/ionic/ionic.h b/drivers/net/ethernet/pensando/ionic/ionic.h index 5e25411ff02f..602f4d45d529 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic.h +++ b/drivers/net/ethernet/pensando/ionic/ionic.h @@ -18,7 +18,7 @@ struct ionic_lif; #define PCI_DEVICE_ID_PENSANDO_IONIC_ETH_PF 0x1002 #define PCI_DEVICE_ID_PENSANDO_IONIC_ETH_VF 0x1003 -#define DEVCMD_TIMEOUT 10 +#define DEVCMD_TIMEOUT 5 #define IONIC_ADMINQ_TIME_SLICE msecs_to_jiffies(100) #define IONIC_PHC_UPDATE_NS 10000000000 /* 10s in nanoseconds */ @@ -78,6 +78,9 @@ void ionic_adminq_netdev_err_print(struct ionic_lif *lif, u8 opcode, u8 status, int err); 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_set_dma_mask(struct ionic *ionic); int ionic_setup(struct ionic *ionic); @@ -89,4 +92,6 @@ int ionic_port_identify(struct ionic *ionic); int ionic_port_init(struct ionic *ionic); int ionic_port_reset(struct ionic *ionic); +const char *ionic_vf_attr_to_str(enum ionic_vf_attr attr); + #endif /* _IONIC_H_ */ diff --git a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c index 7e296fa71b36..ce436e97324a 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c @@ -109,8 +109,8 @@ void ionic_bus_unmap_dbpage(struct ionic *ionic, void __iomem *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; - dma_addr_t dma = 0; int i; if (!ionic->vfs) @@ -120,9 +120,8 @@ static void ionic_vf_dealloc_locked(struct ionic *ionic) v = &ionic->vfs[i]; if (v->stats_pa) { - (void)ionic_set_vf_config(ionic, i, - IONIC_VF_ATTR_STATSADDR, - (u8 *)&dma); + vfc.stats_pa = 0; + (void)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; @@ -143,6 +142,7 @@ static void ionic_vf_dealloc(struct ionic *ionic) 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; @@ -166,9 +166,10 @@ static int ionic_vf_alloc(struct ionic *ionic, int num_vfs) } ionic->num_vfs++; + /* ignore failures from older FW, we just won't get stats */ - (void)ionic_set_vf_config(ionic, i, IONIC_VF_ATTR_STATSADDR, - (u8 *)&v->stats_pa); + vfc.stats_pa = cpu_to_le64(v->stats_pa); + (void)ionic_set_vf_config(ionic, i, &vfc); } out: @@ -255,7 +256,7 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err = ionic_map_bars(ionic); if (err) - goto err_out_pci_disable_device; + goto err_out_pci_release_regions; /* Configure the device */ err = ionic_setup(ionic); @@ -319,22 +320,25 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) dev_err(dev, "Cannot enable existing VFs: %d\n", err); } - err = ionic_lif_register(ionic->lif); + err = ionic_devlink_register(ionic); if (err) { - dev_err(dev, "Cannot register LIF: %d, aborting\n", err); + dev_err(dev, "Cannot register devlink: %d\n", err); goto err_out_deinit_lifs; } - err = ionic_devlink_register(ionic); + err = ionic_lif_register(ionic->lif); if (err) { - dev_err(dev, "Cannot register devlink: %d\n", err); - goto err_out_deregister_lifs; + dev_err(dev, "Cannot register LIF: %d, aborting\n", err); + goto err_out_deregister_devlink; } + mod_timer(&ionic->watchdog_timer, + round_jiffies(jiffies + ionic->watchdog_period)); + return 0; -err_out_deregister_lifs: - ionic_lif_unregister(ionic->lif); +err_out_deregister_devlink: + ionic_devlink_unregister(ionic); err_out_deinit_lifs: ionic_vf_dealloc(ionic); ionic_lif_deinit(ionic->lif); @@ -348,7 +352,6 @@ err_out_port_reset: err_out_reset: ionic_reset(ionic); err_out_teardown: - del_timer_sync(&ionic->watchdog_timer); pci_clear_master(pdev); /* Don't fail the probe for these errors, keep * the hw interface around for inspection @@ -357,6 +360,7 @@ err_out_teardown: err_out_unmap_bars: ionic_unmap_bars(ionic); +err_out_pci_release_regions: pci_release_regions(pdev); err_out_pci_disable_device: pci_disable_device(pdev); @@ -376,8 +380,8 @@ static void ionic_remove(struct pci_dev *pdev) del_timer_sync(&ionic->watchdog_timer); if (ionic->lif) { - ionic_devlink_unregister(ionic); ionic_lif_unregister(ionic->lif); + ionic_devlink_unregister(ionic); ionic_lif_deinit(ionic->lif); ionic_lif_free(ionic->lif); ionic->lif = NULL; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.c b/drivers/net/ethernet/pensando/ionic/ionic_dev.c index d57e80d44c9d..9d0514cfeb5c 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_dev.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.c @@ -33,7 +33,8 @@ static void ionic_watchdog_cb(struct timer_list *t) !test_bit(IONIC_LIF_F_FW_RESET, lif->state)) ionic_link_status_check_request(lif, CAN_NOT_SLEEP); - if (test_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state)) { + if (test_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state) && + !test_bit(IONIC_LIF_F_FW_RESET, lif->state)) { work = kzalloc(sizeof(*work), GFP_ATOMIC); if (!work) { netdev_err(lif->netdev, "rxmode change dropped\n"); @@ -46,6 +47,24 @@ static void ionic_watchdog_cb(struct timer_list *t) } } +static void ionic_watchdog_init(struct ionic *ionic) +{ + struct ionic_dev *idev = &ionic->idev; + + timer_setup(&ionic->watchdog_timer, ionic_watchdog_cb, 0); + ionic->watchdog_period = IONIC_WATCHDOG_SECS * HZ; + + /* set times to ensure the first check will proceed */ + atomic_long_set(&idev->last_check_time, jiffies - 2 * HZ); + idev->last_hb_time = jiffies - 2 * ionic->watchdog_period; + /* init as ready, so no transition if the first check succeeds */ + idev->last_fw_hb = 0; + idev->fw_hb_ready = true; + idev->fw_status_ready = true; + idev->fw_generation = IONIC_FW_STS_F_GENERATION & + ioread8(&idev->dev_info_regs->fw_status); +} + void ionic_init_devinfo(struct ionic *ionic) { struct ionic_dev *idev = &ionic->idev; @@ -109,21 +128,7 @@ int ionic_dev_setup(struct ionic *ionic) return -EFAULT; } - timer_setup(&ionic->watchdog_timer, ionic_watchdog_cb, 0); - ionic->watchdog_period = IONIC_WATCHDOG_SECS * HZ; - - /* set times to ensure the first check will proceed */ - atomic_long_set(&idev->last_check_time, jiffies - 2 * HZ); - idev->last_hb_time = jiffies - 2 * ionic->watchdog_period; - /* init as ready, so no transition if the first check succeeds */ - idev->last_fw_hb = 0; - idev->fw_hb_ready = true; - idev->fw_status_ready = true; - idev->fw_generation = IONIC_FW_STS_F_GENERATION & - ioread8(&idev->dev_info_regs->fw_status); - - mod_timer(&ionic->watchdog_timer, - round_jiffies(jiffies + ionic->watchdog_period)); + ionic_watchdog_init(ionic); idev->db_pages = bar->vaddr; idev->phy_db_pages = bar->bus_addr; @@ -132,10 +137,21 @@ int ionic_dev_setup(struct ionic *ionic) } /* Devcmd Interface */ +bool ionic_is_fw_running(struct ionic_dev *idev) +{ + u8 fw_status = ioread8(&idev->dev_info_regs->fw_status); + + /* firmware is useful only if the running bit is set and + * fw_status != 0xff (bad PCI read) + */ + return (fw_status != 0xff) && (fw_status & IONIC_FW_STS_F_RUNNING); +} + int ionic_heartbeat_check(struct ionic *ionic) { - struct ionic_dev *idev = &ionic->idev; unsigned long check_time, last_check_time; + struct ionic_dev *idev = &ionic->idev; + struct ionic_lif *lif = ionic->lif; bool fw_status_ready = true; bool fw_hb_ready; u8 fw_generation; @@ -155,13 +171,10 @@ do_check_time: goto do_check_time; } - /* firmware is useful only if the running bit is set and - * fw_status != 0xff (bad PCI read) - * If fw_status is not ready don't bother with the generation. - */ fw_status = ioread8(&idev->dev_info_regs->fw_status); - if (fw_status == 0xff || !(fw_status & IONIC_FW_STS_F_RUNNING)) { + /* If fw_status is not ready don't bother with the generation */ + if (!ionic_is_fw_running(idev)) { fw_status_ready = false; } else { fw_generation = fw_status & IONIC_FW_STS_F_GENERATION; @@ -176,26 +189,40 @@ do_check_time: * the down, the next watchdog will see the fw is up * and the generation value stable, so will trigger * the fw-up activity. + * + * If we had already moved to FW_RESET from a RESET event, + * it is possible that we never saw the fw_status go to 0, + * so we fake the current idev->fw_status_ready here to + * force the transition and get FW up again. */ - fw_status_ready = false; + if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) + idev->fw_status_ready = false; /* go to running */ + else + fw_status_ready = false; /* go to down */ } } + dev_dbg(ionic->dev, "fw_status 0x%02x ready %d idev->ready %d last_hb 0x%x state 0x%02lx\n", + fw_status, fw_status_ready, idev->fw_status_ready, + idev->last_fw_hb, lif->state[0]); + /* is this a transition? */ - if (fw_status_ready != idev->fw_status_ready) { - struct ionic_lif *lif = ionic->lif; + if (fw_status_ready != idev->fw_status_ready && + !test_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) { bool trigger = false; idev->fw_status_ready = fw_status_ready; - if (!fw_status_ready) { - dev_info(ionic->dev, "FW stopped %u\n", fw_status); - if (lif && !test_bit(IONIC_LIF_F_FW_RESET, lif->state)) - trigger = true; - } else { - dev_info(ionic->dev, "FW running %u\n", fw_status); - if (lif && test_bit(IONIC_LIF_F_FW_RESET, lif->state)) - trigger = true; + if (!fw_status_ready && + !test_bit(IONIC_LIF_F_FW_RESET, lif->state) && + !test_and_set_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) { + dev_info(ionic->dev, "FW stopped 0x%02x\n", fw_status); + trigger = true; + + } else if (fw_status_ready && + test_bit(IONIC_LIF_F_FW_RESET, lif->state)) { + dev_info(ionic->dev, "FW running 0x%02x\n", fw_status); + trigger = true; } if (trigger) { @@ -210,12 +237,14 @@ do_check_time: } } - if (!fw_status_ready) + if (!idev->fw_status_ready) return -ENXIO; - /* wait at least one watchdog period since the last heartbeat */ + /* Because of some variability in the actual FW heartbeat, we + * wait longer than the DEVCMD_TIMEOUT before checking again. + */ last_check_time = idev->last_hb_time; - if (time_before(check_time, last_check_time + ionic->watchdog_period)) + if (time_before(check_time, last_check_time + DEVCMD_TIMEOUT * 2 * HZ)) return 0; fw_hb = ioread32(&idev->dev_info_regs->fw_heartbeat); @@ -392,60 +421,63 @@ void ionic_dev_cmd_port_pause(struct ionic_dev *idev, u8 pause_type) } /* VF commands */ -int ionic_set_vf_config(struct ionic *ionic, int vf, u8 attr, u8 *data) +int ionic_set_vf_config(struct ionic *ionic, int vf, + struct ionic_vf_setattr_cmd *vfc) { union ionic_dev_cmd cmd = { .vf_setattr.opcode = IONIC_CMD_VF_SETATTR, - .vf_setattr.attr = attr, + .vf_setattr.attr = vfc->attr, .vf_setattr.vf_index = cpu_to_le16(vf), }; int err; + memcpy(cmd.vf_setattr.pad, vfc->pad, sizeof(vfc->pad)); + + 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); + + return err; +} + +int ionic_dev_cmd_vf_getattr(struct ionic *ionic, int vf, u8 attr, + struct ionic_vf_getattr_comp *comp) +{ + union ionic_dev_cmd cmd = { + .vf_getattr.opcode = IONIC_CMD_VF_GETATTR, + .vf_getattr.attr = attr, + .vf_getattr.vf_index = cpu_to_le16(vf), + }; + int err; + + if (vf >= ionic->num_vfs) + return -EINVAL; + switch (attr) { case IONIC_VF_ATTR_SPOOFCHK: - cmd.vf_setattr.spoofchk = *data; - dev_dbg(ionic->dev, "%s: vf %d spoof %d\n", - __func__, vf, *data); - break; case IONIC_VF_ATTR_TRUST: - cmd.vf_setattr.trust = *data; - dev_dbg(ionic->dev, "%s: vf %d trust %d\n", - __func__, vf, *data); - break; case IONIC_VF_ATTR_LINKSTATE: - cmd.vf_setattr.linkstate = *data; - dev_dbg(ionic->dev, "%s: vf %d linkstate %d\n", - __func__, vf, *data); - break; case IONIC_VF_ATTR_MAC: - ether_addr_copy(cmd.vf_setattr.macaddr, data); - dev_dbg(ionic->dev, "%s: vf %d macaddr %pM\n", - __func__, vf, data); - break; case IONIC_VF_ATTR_VLAN: - cmd.vf_setattr.vlanid = cpu_to_le16(*(u16 *)data); - dev_dbg(ionic->dev, "%s: vf %d vlan %d\n", - __func__, vf, *(u16 *)data); - break; case IONIC_VF_ATTR_RATE: - cmd.vf_setattr.maxrate = cpu_to_le32(*(u32 *)data); - dev_dbg(ionic->dev, "%s: vf %d maxrate %d\n", - __func__, vf, *(u32 *)data); break; case IONIC_VF_ATTR_STATSADDR: - cmd.vf_setattr.stats_pa = cpu_to_le64(*(u64 *)data); - dev_dbg(ionic->dev, "%s: vf %d stats_pa 0x%08llx\n", - __func__, vf, *(u64 *)data); - break; default: return -EINVAL; } mutex_lock(&ionic->dev_cmd_lock); ionic_dev_cmd_go(&ionic->idev, &cmd); - err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); + err = ionic_dev_cmd_wait_nomsg(ionic, DEVCMD_TIMEOUT); + memcpy_fromio(comp, &ionic->idev.dev_cmd_regs->comp.vf_getattr, + sizeof(*comp)); mutex_unlock(&ionic->dev_cmd_lock); + if (err && comp->status != IONIC_RC_ENOSUPP) + ionic_dev_cmd_dev_err_print(ionic, cmd.vf_getattr.opcode, + comp->status, err); + return err; } diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.h b/drivers/net/ethernet/pensando/ionic/ionic_dev.h index e5acf3bd62b2..563c302eb033 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_dev.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.h @@ -318,7 +318,10 @@ 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, u8 attr, u8 *data); +int ionic_set_vf_config(struct ionic *ionic, int vf, + struct ionic_vf_setattr_cmd *vfc); +int ionic_dev_cmd_vf_getattr(struct ionic *ionic, int vf, u8 attr, + struct ionic_vf_getattr_comp *comp); void ionic_dev_cmd_queue_identify(struct ionic_dev *idev, u16 lif_type, u8 qtype, u8 qver); void ionic_dev_cmd_lif_identify(struct ionic_dev *idev, u8 type, u8 ver); @@ -353,5 +356,6 @@ void ionic_q_rewind(struct ionic_queue *q, struct ionic_desc_info *start); void ionic_q_service(struct ionic_queue *q, struct ionic_cq_info *cq_info, unsigned int stop_index); int ionic_heartbeat_check(struct ionic *ionic); +bool ionic_is_fw_running(struct ionic_dev *idev); #endif /* _IONIC_DEV_H_ */ diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c index 386a5cf1e224..01c22701482d 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c @@ -74,10 +74,10 @@ static void ionic_get_drvinfo(struct net_device *netdev, struct ionic_lif *lif = netdev_priv(netdev); struct ionic *ionic = lif->ionic; - strlcpy(drvinfo->driver, IONIC_DRV_NAME, sizeof(drvinfo->driver)); - strlcpy(drvinfo->fw_version, ionic->idev.dev_info.fw_version, + strscpy(drvinfo->driver, IONIC_DRV_NAME, sizeof(drvinfo->driver)); + strscpy(drvinfo->fw_version, ionic->idev.dev_info.fw_version, sizeof(drvinfo->fw_version)); - strlcpy(drvinfo->bus_info, ionic_bus_info(ionic), + strscpy(drvinfo->bus_info, ionic_bus_info(ionic), sizeof(drvinfo->bus_info)); } diff --git a/drivers/net/ethernet/pensando/ionic/ionic_if.h b/drivers/net/ethernet/pensando/ionic/ionic_if.h index 278610ed7227..4a90f611c611 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_if.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_if.h @@ -759,7 +759,7 @@ enum ionic_txq_desc_opcode { * IONIC_TXQ_DESC_OPCODE_CSUM_HW: * Offload 16-bit checksum computation to hardware. * If @csum_l3 is set then the packet's L3 checksum is - * updated. Similarly, if @csum_l4 is set the the L4 + * updated. Similarly, if @csum_l4 is set the L4 * checksum is updated. If @encap is set then encap header * checksums are also updated. * @@ -1368,9 +1368,9 @@ union ionic_port_config { * @status: link status (enum ionic_port_oper_status) * @id: port id * @speed: link speed (in Mbps) - * @link_down_count: number of times link went from from up to down + * @link_down_count: number of times link went from up to down * @fec_type: fec type (enum ionic_port_fec_type) - * @xcvr: tranceiver status + * @xcvr: transceiver status */ struct ionic_port_status { __le32 id; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c index 2ff7be17e5af..19d4848df17d 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c @@ -12,6 +12,7 @@ #include <linux/pci.h> #include <linux/cpumask.h> #include <linux/crash_dump.h> +#include <linux/vmalloc.h> #include "ionic.h" #include "ionic_bus.h" @@ -393,11 +394,11 @@ static void ionic_qcq_free(struct ionic_lif *lif, struct ionic_qcq *qcq) ionic_qcq_intr_free(lif, qcq); if (qcq->cq.info) { - devm_kfree(dev, qcq->cq.info); + vfree(qcq->cq.info); qcq->cq.info = NULL; } if (qcq->q.info) { - devm_kfree(dev, qcq->q.info); + vfree(qcq->q.info); qcq->q.info = NULL; } } @@ -528,8 +529,7 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, new->q.dev = dev; new->flags = flags; - new->q.info = devm_kcalloc(dev, num_descs, sizeof(*new->q.info), - GFP_KERNEL); + new->q.info = vzalloc(num_descs * sizeof(*new->q.info)); if (!new->q.info) { netdev_err(lif->netdev, "Cannot allocate queue info\n"); err = -ENOMEM; @@ -550,8 +550,7 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, if (err) goto err_out; - new->cq.info = devm_kcalloc(dev, num_descs, sizeof(*new->cq.info), - GFP_KERNEL); + new->cq.info = vzalloc(num_descs * sizeof(*new->cq.info)); if (!new->cq.info) { netdev_err(lif->netdev, "Cannot allocate completion queue info\n"); err = -ENOMEM; @@ -640,14 +639,14 @@ err_out_free_cq: err_out_free_q: dma_free_coherent(dev, new->q_size, new->q_base, new->q_base_pa); err_out_free_cq_info: - devm_kfree(dev, new->cq.info); + vfree(new->cq.info); err_out_free_irq: if (flags & IONIC_QCQ_F_INTR) { devm_free_irq(dev, new->intr.vector, &new->napi); ionic_intr_free(lif->ionic, new->intr.index); } err_out_free_q_info: - devm_kfree(dev, new->q.info); + vfree(new->q.info); err_out_free_qcq: devm_kfree(dev, new); err_out: @@ -775,8 +774,7 @@ static int ionic_lif_txq_init(struct ionic_lif *lif, struct ionic_qcq *qcq) dev_dbg(dev, "txq->hw_index %d\n", q->hw_index); if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) - netif_napi_add(lif->netdev, &qcq->napi, ionic_tx_napi, - NAPI_POLL_WEIGHT); + netif_napi_add(lif->netdev, &qcq->napi, ionic_tx_napi); qcq->flags |= IONIC_QCQ_F_INITED; @@ -831,11 +829,9 @@ static int ionic_lif_rxq_init(struct ionic_lif *lif, struct ionic_qcq *qcq) dev_dbg(dev, "rxq->hw_index %d\n", q->hw_index); if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) - netif_napi_add(lif->netdev, &qcq->napi, ionic_rx_napi, - NAPI_POLL_WEIGHT); + netif_napi_add(lif->netdev, &qcq->napi, ionic_rx_napi); else - netif_napi_add(lif->netdev, &qcq->napi, ionic_txrx_napi, - NAPI_POLL_WEIGHT); + netif_napi_add(lif->netdev, &qcq->napi, ionic_txrx_napi); qcq->flags |= IONIC_QCQ_F_INITED; @@ -1112,12 +1108,17 @@ static bool ionic_notifyq_service(struct ionic_cq *cq, ionic_link_status_check_request(lif, CAN_NOT_SLEEP); break; case IONIC_EVENT_RESET: - work = kzalloc(sizeof(*work), GFP_ATOMIC); - if (!work) { - netdev_err(lif->netdev, "Reset event dropped\n"); - } else { - work->type = IONIC_DW_TYPE_LIF_RESET; - ionic_lif_deferred_enqueue(&lif->deferred, work); + 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(sizeof(*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->deferred, work); + } } break; default: @@ -1433,7 +1434,7 @@ static int ionic_set_nic_features(struct ionic_lif *lif, if ((old_hw_features ^ lif->hw_features) & IONIC_ETH_HW_RX_HASH) ionic_lif_rss_config(lif, lif->rss_types, NULL, NULL); - if ((vlan_flags & features) && + if ((vlan_flags & le64_to_cpu(ctx.cmd.lif_setattr.features)) && !(vlan_flags & le64_to_cpu(ctx.comp.lif_setattr.features))) dev_info_once(lif->ionic->dev, "NIC is not supporting vlan offload, likely in SmartNIC mode\n"); @@ -1560,8 +1561,67 @@ static int ionic_set_features(struct net_device *netdev, return err; } +static int ionic_set_attr_mac(struct ionic_lif *lif, u8 *mac) +{ + struct ionic_admin_ctx ctx = { + .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), + .cmd.lif_setattr = { + .opcode = IONIC_CMD_LIF_SETATTR, + .index = cpu_to_le16(lif->index), + .attr = IONIC_LIF_ATTR_MAC, + }, + }; + + ether_addr_copy(ctx.cmd.lif_setattr.mac, mac); + return ionic_adminq_post_wait(lif, &ctx); +} + +static int ionic_get_attr_mac(struct ionic_lif *lif, u8 *mac_addr) +{ + struct ionic_admin_ctx ctx = { + .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), + .cmd.lif_getattr = { + .opcode = IONIC_CMD_LIF_GETATTR, + .index = cpu_to_le16(lif->index), + .attr = IONIC_LIF_ATTR_MAC, + }, + }; + int err; + + err = ionic_adminq_post_wait(lif, &ctx); + if (err) + return err; + + ether_addr_copy(mac_addr, ctx.comp.lif_getattr.mac); + return 0; +} + +static int ionic_program_mac(struct ionic_lif *lif, u8 *mac) +{ + u8 get_mac[ETH_ALEN]; + int err; + + err = ionic_set_attr_mac(lif, mac); + if (err) + return err; + + err = ionic_get_attr_mac(lif, get_mac); + if (err) + return err; + + /* To deal with older firmware that silently ignores the set attr mac: + * doesn't actually change the mac and doesn't return an error, so we + * do the get attr to verify whether or not the set actually happened + */ + if (!ether_addr_equal(get_mac, mac)) + return 1; + + return 0; +} + static int ionic_set_mac_address(struct net_device *netdev, void *sa) { + struct ionic_lif *lif = netdev_priv(netdev); struct sockaddr *addr = sa; u8 *mac; int err; @@ -1570,6 +1630,14 @@ static int ionic_set_mac_address(struct net_device *netdev, void *sa) if (ether_addr_equal(netdev->dev_addr, mac)) return 0; + err = ionic_program_mac(lif, mac); + if (err < 0) + return err; + + if (err > 0) + netdev_dbg(netdev, "%s: SET and GET ATTR Mac are not equal-due to old FW running\n", + __func__); + err = eth_prepare_mac_addr_change(netdev, addr); if (err) return err; @@ -1782,7 +1850,7 @@ static void ionic_lif_quiesce(struct ionic_lif *lif) err = ionic_adminq_post_wait(lif, &ctx); if (err) - netdev_err(lif->netdev, "lif quiesce failed %d\n", err); + netdev_dbg(lif->netdev, "lif quiesce failed %d\n", err); } static void ionic_txrx_disable(struct ionic_lif *lif) @@ -2152,6 +2220,76 @@ static int ionic_eth_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd } } +static int ionic_update_cached_vf_config(struct ionic *ionic, int vf) +{ + struct ionic_vf_getattr_comp comp = { 0 }; + int err; + u8 attr; + + attr = IONIC_VF_ATTR_VLAN; + err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp); + if (err && comp.status != IONIC_RC_ENOSUPP) + goto err_out; + if (!err) + ionic->vfs[vf].vlanid = comp.vlanid; + + attr = IONIC_VF_ATTR_SPOOFCHK; + err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp); + if (err && comp.status != IONIC_RC_ENOSUPP) + goto err_out; + if (!err) + ionic->vfs[vf].spoofchk = comp.spoofchk; + + attr = IONIC_VF_ATTR_LINKSTATE; + err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp); + if (err && comp.status != IONIC_RC_ENOSUPP) + goto err_out; + if (!err) { + switch (comp.linkstate) { + case IONIC_VF_LINK_STATUS_UP: + ionic->vfs[vf].linkstate = IFLA_VF_LINK_STATE_ENABLE; + break; + case IONIC_VF_LINK_STATUS_DOWN: + ionic->vfs[vf].linkstate = IFLA_VF_LINK_STATE_DISABLE; + break; + case IONIC_VF_LINK_STATUS_AUTO: + ionic->vfs[vf].linkstate = IFLA_VF_LINK_STATE_AUTO; + break; + default: + dev_warn(ionic->dev, "Unexpected link state %u\n", comp.linkstate); + break; + } + } + + attr = IONIC_VF_ATTR_RATE; + err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp); + if (err && comp.status != IONIC_RC_ENOSUPP) + goto err_out; + if (!err) + ionic->vfs[vf].maxrate = comp.maxrate; + + attr = IONIC_VF_ATTR_TRUST; + err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp); + if (err && comp.status != IONIC_RC_ENOSUPP) + goto err_out; + if (!err) + ionic->vfs[vf].trusted = comp.trust; + + attr = IONIC_VF_ATTR_MAC; + err = ionic_dev_cmd_vf_getattr(ionic, vf, attr, &comp); + if (err && comp.status != IONIC_RC_ENOSUPP) + goto err_out; + if (!err) + ether_addr_copy(ionic->vfs[vf].macaddr, comp.macaddr); + +err_out: + if (err) + dev_err(ionic->dev, "Failed to get %s for VF %d\n", + ionic_vf_attr_to_str(attr), vf); + + return err; +} + static int ionic_get_vf_config(struct net_device *netdev, int vf, struct ifla_vf_info *ivf) { @@ -2167,14 +2305,18 @@ static int ionic_get_vf_config(struct net_device *netdev, if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) { ret = -EINVAL; } else { - ivf->vf = vf; - ivf->vlan = le16_to_cpu(ionic->vfs[vf].vlanid); - ivf->qos = 0; - ivf->spoofchk = ionic->vfs[vf].spoofchk; - ivf->linkstate = ionic->vfs[vf].linkstate; - ivf->max_tx_rate = le32_to_cpu(ionic->vfs[vf].maxrate); - ivf->trusted = ionic->vfs[vf].trusted; - ether_addr_copy(ivf->mac, ionic->vfs[vf].macaddr); + ivf->vf = vf; + ivf->qos = 0; + + ret = ionic_update_cached_vf_config(ionic, vf); + if (!ret) { + ivf->vlan = le16_to_cpu(ionic->vfs[vf].vlanid); + ivf->spoofchk = ionic->vfs[vf].spoofchk; + ivf->linkstate = ionic->vfs[vf].linkstate; + ivf->max_tx_rate = le32_to_cpu(ionic->vfs[vf].maxrate); + ivf->trusted = ionic->vfs[vf].trusted; + ether_addr_copy(ivf->mac, ionic->vfs[vf].macaddr); + } } up_read(&ionic->vf_op_lock); @@ -2220,6 +2362,7 @@ static int ionic_get_vf_stats(struct net_device *netdev, int vf, static int ionic_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) { + struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_MAC }; struct ionic_lif *lif = netdev_priv(netdev); struct ionic *ionic = lif->ionic; int ret; @@ -2235,7 +2378,11 @@ static int ionic_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) { ret = -EINVAL; } else { - ret = ionic_set_vf_config(ionic, vf, IONIC_VF_ATTR_MAC, mac); + ether_addr_copy(vfc.macaddr, mac); + dev_dbg(ionic->dev, "%s: vf %d macaddr %pM\n", + __func__, vf, vfc.macaddr); + + ret = ionic_set_vf_config(ionic, vf, &vfc); if (!ret) ether_addr_copy(ionic->vfs[vf].macaddr, mac); } @@ -2247,6 +2394,7 @@ static int ionic_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) static int ionic_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos, __be16 proto) { + struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_VLAN }; struct ionic_lif *lif = netdev_priv(netdev); struct ionic *ionic = lif->ionic; int ret; @@ -2269,8 +2417,11 @@ static int ionic_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) { ret = -EINVAL; } else { - ret = ionic_set_vf_config(ionic, vf, - IONIC_VF_ATTR_VLAN, (u8 *)&vlan); + vfc.vlanid = cpu_to_le16(vlan); + dev_dbg(ionic->dev, "%s: vf %d vlan %d\n", + __func__, vf, le16_to_cpu(vfc.vlanid)); + + ret = ionic_set_vf_config(ionic, vf, &vfc); if (!ret) ionic->vfs[vf].vlanid = cpu_to_le16(vlan); } @@ -2282,6 +2433,7 @@ static int ionic_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, static int ionic_set_vf_rate(struct net_device *netdev, int vf, int tx_min, int tx_max) { + struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_RATE }; struct ionic_lif *lif = netdev_priv(netdev); struct ionic *ionic = lif->ionic; int ret; @@ -2298,8 +2450,11 @@ static int ionic_set_vf_rate(struct net_device *netdev, int vf, if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) { ret = -EINVAL; } else { - ret = ionic_set_vf_config(ionic, vf, - IONIC_VF_ATTR_RATE, (u8 *)&tx_max); + vfc.maxrate = cpu_to_le32(tx_max); + dev_dbg(ionic->dev, "%s: vf %d maxrate %d\n", + __func__, vf, le32_to_cpu(vfc.maxrate)); + + ret = ionic_set_vf_config(ionic, vf, &vfc); if (!ret) lif->ionic->vfs[vf].maxrate = cpu_to_le32(tx_max); } @@ -2310,9 +2465,9 @@ static int ionic_set_vf_rate(struct net_device *netdev, int vf, static int ionic_set_vf_spoofchk(struct net_device *netdev, int vf, bool set) { + struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_SPOOFCHK }; struct ionic_lif *lif = netdev_priv(netdev); struct ionic *ionic = lif->ionic; - u8 data = set; /* convert to u8 for config */ int ret; if (!netif_device_present(netdev)) @@ -2323,10 +2478,13 @@ static int ionic_set_vf_spoofchk(struct net_device *netdev, int vf, bool set) if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) { ret = -EINVAL; } else { - ret = ionic_set_vf_config(ionic, vf, - IONIC_VF_ATTR_SPOOFCHK, &data); + vfc.spoofchk = set; + dev_dbg(ionic->dev, "%s: vf %d spoof %d\n", + __func__, vf, vfc.spoofchk); + + ret = ionic_set_vf_config(ionic, vf, &vfc); if (!ret) - ionic->vfs[vf].spoofchk = data; + ionic->vfs[vf].spoofchk = set; } up_write(&ionic->vf_op_lock); @@ -2335,9 +2493,9 @@ static int ionic_set_vf_spoofchk(struct net_device *netdev, int vf, bool set) static int ionic_set_vf_trust(struct net_device *netdev, int vf, bool set) { + struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_TRUST }; struct ionic_lif *lif = netdev_priv(netdev); struct ionic *ionic = lif->ionic; - u8 data = set; /* convert to u8 for config */ int ret; if (!netif_device_present(netdev)) @@ -2348,10 +2506,13 @@ static int ionic_set_vf_trust(struct net_device *netdev, int vf, bool set) if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) { ret = -EINVAL; } else { - ret = ionic_set_vf_config(ionic, vf, - IONIC_VF_ATTR_TRUST, &data); + vfc.trust = set; + dev_dbg(ionic->dev, "%s: vf %d trust %d\n", + __func__, vf, vfc.trust); + + ret = ionic_set_vf_config(ionic, vf, &vfc); if (!ret) - ionic->vfs[vf].trusted = data; + ionic->vfs[vf].trusted = set; } up_write(&ionic->vf_op_lock); @@ -2360,20 +2521,21 @@ static int ionic_set_vf_trust(struct net_device *netdev, int vf, bool set) static int ionic_set_vf_link_state(struct net_device *netdev, int vf, int set) { + struct ionic_vf_setattr_cmd vfc = { .attr = IONIC_VF_ATTR_LINKSTATE }; struct ionic_lif *lif = netdev_priv(netdev); struct ionic *ionic = lif->ionic; - u8 data; + u8 vfls; int ret; switch (set) { case IFLA_VF_LINK_STATE_ENABLE: - data = IONIC_VF_LINK_STATUS_UP; + vfls = IONIC_VF_LINK_STATUS_UP; break; case IFLA_VF_LINK_STATE_DISABLE: - data = IONIC_VF_LINK_STATUS_DOWN; + vfls = IONIC_VF_LINK_STATUS_DOWN; break; case IFLA_VF_LINK_STATE_AUTO: - data = IONIC_VF_LINK_STATUS_AUTO; + vfls = IONIC_VF_LINK_STATUS_AUTO; break; default: return -EINVAL; @@ -2387,8 +2549,11 @@ static int ionic_set_vf_link_state(struct net_device *netdev, int vf, int set) if (vf >= pci_num_vf(ionic->pdev) || !ionic->vfs) { ret = -EINVAL; } else { - ret = ionic_set_vf_config(ionic, vf, - IONIC_VF_ATTR_LINKSTATE, &data); + vfc.linkstate = vfls; + dev_dbg(ionic->dev, "%s: vf %d linkstate %d\n", + __func__, vf, vfc.linkstate); + + ret = ionic_set_vf_config(ionic, vf, &vfc); if (!ret) ionic->vfs[vf].linkstate = set; } @@ -2652,11 +2817,15 @@ err_out: * than the full array, but leave the qcq shells in place */ for (i = lif->nxqs; i < lif->ionic->ntxqs_per_lif; i++) { - lif->txqcqs[i]->flags &= ~IONIC_QCQ_F_INTR; - ionic_qcq_free(lif, lif->txqcqs[i]); + if (lif->txqcqs && lif->txqcqs[i]) { + lif->txqcqs[i]->flags &= ~IONIC_QCQ_F_INTR; + ionic_qcq_free(lif, lif->txqcqs[i]); + } - lif->rxqcqs[i]->flags &= ~IONIC_QCQ_F_INTR; - ionic_qcq_free(lif, lif->rxqcqs[i]); + if (lif->rxqcqs && lif->rxqcqs[i]) { + lif->rxqcqs[i]->flags &= ~IONIC_QCQ_F_INTR; + ionic_qcq_free(lif, lif->rxqcqs[i]); + } } if (err) @@ -2835,6 +3004,7 @@ static void ionic_lif_handle_fw_down(struct ionic_lif *lif) mutex_unlock(&lif->queue_lock); + clear_bit(IONIC_LIF_F_FW_STOPPING, lif->state); dev_info(ionic->dev, "FW Down: LIFs stopped\n"); } @@ -2861,6 +3031,9 @@ static void ionic_lif_handle_fw_up(struct ionic_lif *lif) mutex_lock(&lif->queue_lock); + if (test_and_clear_bit(IONIC_LIF_F_BROKEN, lif->state)) + dev_info(ionic->dev, "FW Up: clearing broken state\n"); + err = ionic_qcqs_alloc(lif); if (err) goto err_unlock; @@ -2934,8 +3107,6 @@ void ionic_lif_free(struct ionic_lif *lif) /* unmap doorbell page */ ionic_bus_unmap_dbpage(lif->ionic, lif->kern_dbpage); lif->kern_dbpage = NULL; - kfree(lif->dbid_inuse); - lif->dbid_inuse = NULL; mutex_destroy(&lif->config_lock); mutex_destroy(&lif->queue_lock); @@ -2995,8 +3166,7 @@ static int ionic_lif_adminq_init(struct ionic_lif *lif) dev_dbg(dev, "adminq->hw_type %d\n", q->hw_type); dev_dbg(dev, "adminq->hw_index %d\n", q->hw_index); - netif_napi_add(lif->netdev, &qcq->napi, ionic_adminq_napi, - NAPI_POLL_WEIGHT); + netif_napi_add(lif->netdev, &qcq->napi, ionic_adminq_napi); napi_enable(&qcq->napi); @@ -3069,6 +3239,7 @@ static int ionic_station_set(struct ionic_lif *lif) .attr = IONIC_LIF_ATTR_MAC, }, }; + u8 mac_address[ETH_ALEN]; struct sockaddr addr; int err; @@ -3077,8 +3248,23 @@ static int ionic_station_set(struct ionic_lif *lif) return err; netdev_dbg(lif->netdev, "found initial MAC addr %pM\n", ctx.comp.lif_getattr.mac); - if (is_zero_ether_addr(ctx.comp.lif_getattr.mac)) - return 0; + ether_addr_copy(mac_address, ctx.comp.lif_getattr.mac); + + if (is_zero_ether_addr(mac_address)) { + eth_hw_addr_random(netdev); + netdev_dbg(netdev, "Random Mac generated: %pM\n", netdev->dev_addr); + ether_addr_copy(mac_address, netdev->dev_addr); + + err = ionic_program_mac(lif, mac_address); + if (err < 0) + return err; + + if (err > 0) { + netdev_dbg(netdev, "%s:SET/GET ATTR Mac are not same-due to old FW running\n", + __func__); + return 0; + } + } if (!is_zero_ether_addr(netdev->dev_addr)) { /* If the netdev mac is non-zero and doesn't match the default @@ -3086,12 +3272,11 @@ static int ionic_station_set(struct ionic_lif *lif) * likely here again after a fw-upgrade reset. We need to be * sure the netdev mac is in our filter list. */ - if (!ether_addr_equal(ctx.comp.lif_getattr.mac, - netdev->dev_addr)) + if (!ether_addr_equal(mac_address, netdev->dev_addr)) ionic_lif_addr_add(lif, netdev->dev_addr); } else { /* Update the netdev mac with the device's mac */ - memcpy(addr.sa_data, ctx.comp.lif_getattr.mac, netdev->addr_len); + ether_addr_copy(addr.sa_data, mac_address); addr.sa_family = AF_INET; err = eth_prepare_mac_addr_change(netdev, &addr); if (err) { @@ -3135,22 +3320,12 @@ int ionic_lif_init(struct ionic_lif *lif) return -EINVAL; } - lif->dbid_inuse = bitmap_zalloc(lif->dbid_count, GFP_KERNEL); - if (!lif->dbid_inuse) { - dev_err(dev, "Failed alloc doorbell id bitmap, aborting\n"); - return -ENOMEM; - } - - /* first doorbell id reserved for kernel (dbid aka pid == zero) */ - set_bit(0, lif->dbid_inuse); lif->kern_pid = 0; - dbpage_num = ionic_db_page_num(lif, lif->kern_pid); lif->kern_dbpage = ionic_bus_map_dbpage(lif->ionic, dbpage_num); if (!lif->kern_dbpage) { dev_err(dev, "Cannot map dbpage, aborting\n"); - err = -ENOMEM; - goto err_out_free_dbid; + return -ENOMEM; } err = ionic_lif_adminq_init(lif); @@ -3186,15 +3361,13 @@ int ionic_lif_init(struct ionic_lif *lif) return 0; err_out_notifyq_deinit: + napi_disable(&lif->adminqcq->napi); ionic_lif_qcq_deinit(lif, lif->notifyqcq); err_out_adminq_deinit: ionic_lif_qcq_deinit(lif, lif->adminqcq); ionic_lif_reset(lif); ionic_bus_unmap_dbpage(lif->ionic, lif->kern_dbpage); lif->kern_dbpage = NULL; -err_out_free_dbid: - kfree(lif->dbid_inuse); - lif->dbid_inuse = NULL; return err; } @@ -3214,7 +3387,7 @@ static void ionic_lif_set_netdev_info(struct ionic_lif *lif) }, }; - strlcpy(ctx.cmd.lif_setattr.name, lif->netdev->name, + strscpy(ctx.cmd.lif_setattr.name, lif->netdev->name, sizeof(ctx.cmd.lif_setattr.name)); ionic_adminq_post_wait(lif, &ctx); diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h index 9f7ab2f17f93..a53984bf3544 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h @@ -135,6 +135,7 @@ enum ionic_lif_state_flags { 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, @@ -213,7 +214,6 @@ struct ionic_lif { 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 long *dbid_inuse; unsigned int dbid_count; struct ionic_phc *phc; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_main.c b/drivers/net/ethernet/pensando/ionic/ionic_main.c index 875f4ec42efe..56f93b030551 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_main.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_main.c @@ -188,6 +188,28 @@ static const char *ionic_opcode_to_str(enum ionic_cmd_opcode opcode) } } +const char *ionic_vf_attr_to_str(enum ionic_vf_attr attr) +{ + switch (attr) { + case IONIC_VF_ATTR_SPOOFCHK: + return "IONIC_VF_ATTR_SPOOFCHK"; + case IONIC_VF_ATTR_TRUST: + return "IONIC_VF_ATTR_TRUST"; + case IONIC_VF_ATTR_LINKSTATE: + return "IONIC_VF_ATTR_LINKSTATE"; + case IONIC_VF_ATTR_MAC: + return "IONIC_VF_ATTR_MAC"; + case IONIC_VF_ATTR_VLAN: + return "IONIC_VF_ATTR_VLAN"; + case IONIC_VF_ATTR_RATE: + return "IONIC_VF_ATTR_RATE"; + case IONIC_VF_ATTR_STATSADDR: + return "IONIC_VF_ATTR_STATSADDR"; + default: + return "IONIC_VF_ATTR_UNKNOWN"; + } +} + static void ionic_adminq_flush(struct ionic_lif *lif) { struct ionic_desc_info *desc_info; @@ -215,9 +237,13 @@ static void ionic_adminq_flush(struct ionic_lif *lif) 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, - ionic_error_to_str(status), err); + ionic_opcode_to_str(opcode), opcode, stat_str, err); } static int ionic_adminq_check_err(struct ionic_lif *lif, @@ -318,6 +344,7 @@ int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx, 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; } @@ -331,11 +358,15 @@ int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx, if (remaining) break; - /* interrupt the wait if FW stopped */ - if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) { + /* force a check of FW status and break out if FW reset */ + (void)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_err(netdev, "%s (%d) interrupted, FW in reset\n", - name, ctx->cmd.cmd.opcode); + netdev_warn(netdev, "%s (%d) interrupted, FW in reset\n", + name, ctx->cmd.cmd.opcode); + ctx->comp.comp.status = IONIC_RC_ERROR; return -ENXIO; } @@ -370,21 +401,34 @@ int ionic_adminq_post_wait_nomsg(struct ionic_lif *lif, struct ionic_admin_ctx * static void ionic_dev_cmd_clean(struct ionic *ionic) { - union __iomem ionic_dev_cmd_regs *regs = ionic->idev.dev_cmd_regs; + struct ionic_dev *idev = &ionic->idev; - iowrite32(0, ®s->doorbell); - memset_io(®s->cmd, 0, sizeof(regs->cmd)); + iowrite32(0, &idev->dev_cmd_regs->doorbell); + memset_io(&idev->dev_cmd_regs->cmd, 0, sizeof(idev->dev_cmd_regs->cmd)); } -int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds) +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; + int done = 0; + bool fw_up; int opcode; - int hb = 0; - int done; int err; /* Wait for dev cmd to complete, retrying if we get EAGAIN, @@ -394,31 +438,24 @@ int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds) try_again: opcode = readb(&idev->dev_cmd_regs->cmd.cmd.opcode); start_time = jiffies; - do { + 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); - - /* Don't check the heartbeat on FW_CONTROL commands as they are - * notorious for interrupting the firmware's heartbeat update. - */ - if (opcode != IONIC_CMD_FW_CONTROL) - hb = ionic_heartbeat_check(ionic); - } while (!done && !hb && time_before(jiffies, max_wait)); + } 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 && hb) { - /* It is possible (but unlikely) that FW was busy and missed a - * heartbeat check but is still alive and will process this - * request, so don't clean the dev_cmd in this case. - */ - dev_dbg(ionic->dev, "DEVCMD %s (%d) failed - FW halted\n", - ionic_opcode_to_str(opcode), opcode); + 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; } @@ -437,23 +474,35 @@ try_again: ionic_opcode_to_str(opcode), opcode, ionic_error_to_str(err), err); - msleep(1000); 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)) - dev_err(ionic->dev, "DEV_CMD %s (%d) error, %s (%d) failed\n", - ionic_opcode_to_str(opcode), opcode, - ionic_error_to_str(err), err); + 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; @@ -540,6 +589,9 @@ 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); @@ -612,15 +664,17 @@ int ionic_port_init(struct ionic *ionic) int ionic_port_reset(struct ionic *ionic) { struct ionic_dev *idev = &ionic->idev; - int err; + int err = 0; if (!idev->port_info) return 0; - 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); + 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); @@ -628,9 +682,6 @@ int ionic_port_reset(struct ionic *ionic) idev->port_info = NULL; idev->port_info_pa = 0; - if (err) - dev_err(ionic->dev, "Failed to reset port\n"); - return err; } diff --git a/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c b/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c index f6e785f949f9..b7363376dfc8 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c @@ -376,10 +376,24 @@ static int ionic_lif_filter_add(struct ionic_lif *lif, spin_unlock_bh(&lif->rx_filters.lock); - if (err == -ENOSPC) { - if (le16_to_cpu(ctx.cmd.rx_filter_add.match) == IONIC_RX_FILTER_MATCH_VLAN) - lif->max_vlans = lif->nvlans; + /* 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, @@ -494,9 +508,22 @@ static int ionic_lif_filter_del(struct ionic_lif *lif, spin_unlock_bh(&lif->rx_filters.lock); if (state != IONIC_FILTER_STATE_NEW) { - err = ionic_adminq_post_wait(lif, &ctx); - if (err && err != -EEXIST) + 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; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_stats.c b/drivers/net/ethernet/pensando/ionic/ionic_stats.c index fd6806b4a1b9..9859a4432985 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_stats.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_stats.c @@ -151,7 +151,6 @@ static const struct ionic_stat_desc ionic_rx_stats_desc[] = { IONIC_RX_STAT_DESC(vlan_stripped), }; - #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) diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c index 94384f5d2a22..c03986bf2628 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c @@ -10,7 +10,6 @@ #include "ionic_lif.h" #include "ionic_txrx.h" - static inline void ionic_txq_post(struct ionic_queue *q, bool ring_dbell, ionic_desc_cb cb_func, void *cb_arg) { @@ -669,27 +668,37 @@ dma_fail: return -EIO; } +static void ionic_tx_desc_unmap_bufs(struct ionic_queue *q, + struct ionic_desc_info *desc_info) +{ + struct ionic_buf_info *buf_info = desc_info->bufs; + struct device *dev = q->dev; + unsigned int i; + + if (!desc_info->nbufs) + return; + + dma_unmap_single(dev, (dma_addr_t)buf_info->dma_addr, + buf_info->len, DMA_TO_DEVICE); + buf_info++; + for (i = 1; i < desc_info->nbufs; i++, buf_info++) + dma_unmap_page(dev, (dma_addr_t)buf_info->dma_addr, + buf_info->len, DMA_TO_DEVICE); + + desc_info->nbufs = 0; +} + static void ionic_tx_clean(struct ionic_queue *q, struct ionic_desc_info *desc_info, struct ionic_cq_info *cq_info, void *cb_arg) { - struct ionic_buf_info *buf_info = desc_info->bufs; struct ionic_tx_stats *stats = q_to_tx_stats(q); struct ionic_qcq *qcq = q_to_qcq(q); struct sk_buff *skb = cb_arg; - struct device *dev = q->dev; - unsigned int i; u16 qi; - if (desc_info->nbufs) { - dma_unmap_single(dev, (dma_addr_t)buf_info->dma_addr, - buf_info->len, DMA_TO_DEVICE); - buf_info++; - for (i = 1; i < desc_info->nbufs; i++, buf_info++) - dma_unmap_page(dev, (dma_addr_t)buf_info->dma_addr, - buf_info->len, DMA_TO_DEVICE); - } + ionic_tx_desc_unmap_bufs(q, desc_info); if (!skb) return; @@ -931,14 +940,16 @@ static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb) err = ionic_tx_tcp_inner_pseudo_csum(skb); else err = ionic_tx_tcp_pseudo_csum(skb); - if (err) + if (err) { + /* clean up mapping from ionic_tx_map_skb */ + ionic_tx_desc_unmap_bufs(q, desc_info); return err; + } if (encap) - hdrlen = skb_inner_transport_header(skb) - skb->data + - inner_tcp_hdrlen(skb); + hdrlen = skb_inner_tcp_all_headers(skb); else - hdrlen = skb_transport_offset(skb) + tcp_hdrlen(skb); + hdrlen = skb_tcp_all_headers(skb); tso_rem = len; seg_rem = min(tso_rem, hdrlen + mss); @@ -1003,8 +1014,8 @@ static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb) return 0; } -static int ionic_tx_calc_csum(struct ionic_queue *q, struct sk_buff *skb, - struct ionic_desc_info *desc_info) +static void ionic_tx_calc_csum(struct ionic_queue *q, struct sk_buff *skb, + struct ionic_desc_info *desc_info) { struct ionic_txq_desc *desc = desc_info->txq_desc; struct ionic_buf_info *buf_info = desc_info->bufs; @@ -1038,12 +1049,10 @@ static int ionic_tx_calc_csum(struct ionic_queue *q, struct sk_buff *skb, stats->crc32_csum++; else stats->csum++; - - return 0; } -static int ionic_tx_calc_no_csum(struct ionic_queue *q, struct sk_buff *skb, - struct ionic_desc_info *desc_info) +static void ionic_tx_calc_no_csum(struct ionic_queue *q, struct sk_buff *skb, + struct ionic_desc_info *desc_info) { struct ionic_txq_desc *desc = desc_info->txq_desc; struct ionic_buf_info *buf_info = desc_info->bufs; @@ -1074,12 +1083,10 @@ static int ionic_tx_calc_no_csum(struct ionic_queue *q, struct sk_buff *skb, desc->csum_offset = 0; stats->csum_none++; - - return 0; } -static int ionic_tx_skb_frags(struct ionic_queue *q, struct sk_buff *skb, - struct ionic_desc_info *desc_info) +static void ionic_tx_skb_frags(struct ionic_queue *q, struct sk_buff *skb, + struct ionic_desc_info *desc_info) { struct ionic_txq_sg_desc *sg_desc = desc_info->txq_sg_desc; struct ionic_buf_info *buf_info = &desc_info->bufs[1]; @@ -1093,31 +1100,24 @@ static int ionic_tx_skb_frags(struct ionic_queue *q, struct sk_buff *skb, } stats->frags += skb_shinfo(skb)->nr_frags; - - return 0; } static int ionic_tx(struct ionic_queue *q, struct sk_buff *skb) { struct ionic_desc_info *desc_info = &q->info[q->head_idx]; struct ionic_tx_stats *stats = q_to_tx_stats(q); - int err; if (unlikely(ionic_tx_map_skb(q, skb, desc_info))) return -EIO; /* set up the initial descriptor */ if (skb->ip_summed == CHECKSUM_PARTIAL) - err = ionic_tx_calc_csum(q, skb, desc_info); + ionic_tx_calc_csum(q, skb, desc_info); else - err = ionic_tx_calc_no_csum(q, skb, desc_info); - if (err) - return err; + ionic_tx_calc_no_csum(q, skb, desc_info); /* add frags */ - err = ionic_tx_skb_frags(q, skb, desc_info); - if (err) - return err; + ionic_tx_skb_frags(q, skb, desc_info); skb_tx_timestamp(skb); stats->pkts++; |