diff options
Diffstat (limited to 'drivers/net/ethernet/pensando/ionic/ionic_lif.c')
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_lif.c | 440 |
1 files changed, 245 insertions, 195 deletions
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c index af3a5368529c..23c9e196a784 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c @@ -11,6 +11,7 @@ #include <linux/interrupt.h> #include <linux/pci.h> #include <linux/cpumask.h> +#include <linux/crash_dump.h> #include "ionic.h" #include "ionic_bus.h" @@ -29,9 +30,6 @@ static const u8 ionic_qtype_versions[IONIC_QTYPE_MAX] = { */ }; -static void ionic_lif_rx_mode(struct ionic_lif *lif, unsigned int rx_mode); -static int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr); -static int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr); static void ionic_link_status_check(struct ionic_lif *lif); static void ionic_lif_handle_fw_down(struct ionic_lif *lif); static void ionic_lif_handle_fw_up(struct ionic_lif *lif); @@ -53,7 +51,19 @@ static void ionic_dim_work(struct work_struct *work) cur_moder = net_dim_get_rx_moderation(dim->mode, dim->profile_ix); qcq = container_of(dim, struct ionic_qcq, dim); new_coal = ionic_coal_usec_to_hw(qcq->q.lif->ionic, cur_moder.usec); - qcq->intr.dim_coal_hw = new_coal ? new_coal : 1; + new_coal = new_coal ? new_coal : 1; + + if (qcq->intr.dim_coal_hw != new_coal) { + unsigned int qi = qcq->cq.bound_q->index; + struct ionic_lif *lif = qcq->q.lif; + + qcq->intr.dim_coal_hw = new_coal; + + ionic_intr_coal_init(lif->ionic->idev.intr_ctrl, + lif->rxqcqs[qi]->intr.index, + qcq->intr.dim_coal_hw); + } + dim->state = DIM_START_MEASURE; } @@ -77,22 +87,23 @@ static void ionic_lif_deferred_work(struct work_struct *work) switch (w->type) { case IONIC_DW_TYPE_RX_MODE: - ionic_lif_rx_mode(lif, w->rx_mode); - break; - case IONIC_DW_TYPE_RX_ADDR_ADD: - ionic_lif_addr_add(lif, w->addr); - break; - case IONIC_DW_TYPE_RX_ADDR_DEL: - ionic_lif_addr_del(lif, w->addr); + ionic_lif_rx_mode(lif); break; case IONIC_DW_TYPE_LINK_STATUS: ionic_link_status_check(lif); break; case IONIC_DW_TYPE_LIF_RESET: - if (w->fw_status) + if (w->fw_status) { ionic_lif_handle_fw_up(lif); - else + } else { ionic_lif_handle_fw_down(lif); + + /* Fire off another watchdog to see + * if the FW is already back rather than + * waiting another whole cycle + */ + mod_timer(&lif->ionic->watchdog_timer, jiffies + 1); + } break; default: break; @@ -838,10 +849,8 @@ int ionic_lif_create_hwstamp_txq(struct ionic_lif *lif) u64 features; int err; - mutex_lock(&lif->queue_lock); - if (lif->hwstamp_txq) - goto out; + return 0; features = IONIC_Q_F_2X_CQ_DESC | IONIC_TXQ_F_HWSTAMP; @@ -883,9 +892,6 @@ int ionic_lif_create_hwstamp_txq(struct ionic_lif *lif) } } -out: - mutex_unlock(&lif->queue_lock); - return 0; err_qcq_enable: @@ -896,7 +902,6 @@ err_qcq_init: ionic_qcq_free(lif, txq); devm_kfree(lif->ionic->dev, txq); err_qcq_alloc: - mutex_unlock(&lif->queue_lock); return err; } @@ -908,10 +913,8 @@ int ionic_lif_create_hwstamp_rxq(struct ionic_lif *lif) u64 features; int err; - mutex_lock(&lif->queue_lock); - if (lif->hwstamp_rxq) - goto out; + return 0; features = IONIC_Q_F_2X_CQ_DESC | IONIC_RXQ_F_HWSTAMP; @@ -949,9 +952,6 @@ int ionic_lif_create_hwstamp_rxq(struct ionic_lif *lif) } } -out: - mutex_unlock(&lif->queue_lock); - return 0; err_qcq_enable: @@ -962,7 +962,6 @@ err_qcq_init: ionic_qcq_free(lif, rxq); devm_kfree(lif->ionic->dev, rxq); err_qcq_alloc: - mutex_unlock(&lif->queue_lock); return err; } @@ -1065,7 +1064,11 @@ static int ionic_lif_add_hwstamp_rxfilt(struct ionic_lif *lif, u64 pkt_class) if (err && err != -EEXIST) return err; - return ionic_rx_filter_save(lif, 0, qid, 0, &ctx); + spin_lock_bh(&lif->rx_filters.lock); + err = ionic_rx_filter_save(lif, 0, qid, 0, &ctx, IONIC_FILTER_STATE_SYNCED); + spin_unlock_bh(&lif->rx_filters.lock); + + return err; } int ionic_lif_set_hwstamp_rxfilt(struct ionic_lif *lif, u64 pkt_class) @@ -1238,7 +1241,7 @@ void ionic_get_stats64(struct net_device *netdev, ns->tx_errors = ns->tx_aborted_errors; } -static int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr) +int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr) { struct ionic_admin_ctx ctx = { .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), @@ -1248,27 +1251,83 @@ static int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr) .match = cpu_to_le16(IONIC_RX_FILTER_MATCH_MAC), }, }; + int nfilters = le32_to_cpu(lif->identity->eth.max_ucast_filters); + bool mc = is_multicast_ether_addr(addr); struct ionic_rx_filter *f; - int err; + int err = 0; + + memcpy(ctx.cmd.rx_filter_add.mac.addr, addr, ETH_ALEN); - /* don't bother if we already have it */ spin_lock_bh(&lif->rx_filters.lock); f = ionic_rx_filter_by_addr(lif, addr); + 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 (f) - return 0; + if (err) + return err; netdev_dbg(lif->netdev, "rx_filter add ADDR %pM\n", addr); - memcpy(ctx.cmd.rx_filter_add.mac.addr, addr, ETH_ALEN); - err = ionic_adminq_post_wait(lif, &ctx); - if (err && err != -EEXIST) - 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. + */ + if ((lif->nucast + lif->nmcast) >= nfilters) + err = -ENOSPC; + else + err = ionic_adminq_post_wait(lif, &ctx); - return ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &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_by_addr(lif, addr); + if (f && f->state == IONIC_FILTER_STATE_SYNCED) + f->state = IONIC_FILTER_STATE_NEW; + + spin_unlock_bh(&lif->rx_filters.lock); + + if (err == -ENOSPC) + return 0; + else + return err; + } + + if (mc) + lif->nmcast++; + else + lif->nucast++; + + f = ionic_rx_filter_by_addr(lif, addr); + 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; } -static int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr) +int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr) { struct ionic_admin_ctx ctx = { .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), @@ -1278,6 +1337,7 @@ static int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr) }, }; struct ionic_rx_filter *f; + int state; int err; spin_lock_bh(&lif->rx_filters.lock); @@ -1290,63 +1350,21 @@ static int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr) netdev_dbg(lif->netdev, "rx_filter del ADDR %pM (id %d)\n", addr, f->filter_id); + 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); - - err = ionic_adminq_post_wait(lif, &ctx); - if (err && err != -EEXIST) - return err; - - return 0; -} -static int ionic_lif_addr(struct ionic_lif *lif, const u8 *addr, bool add, - bool can_sleep) -{ - struct ionic_deferred_work *work; - unsigned int nmfilters; - unsigned int nufilters; + if (is_multicast_ether_addr(addr) && lif->nmcast) + lif->nmcast--; + else if (!is_multicast_ether_addr(addr) && lif->nucast) + lif->nucast--; - if (add) { - /* Do we have space for this filter? We test the counters - * here before checking the need for deferral so that we - * can return an overflow error to the stack. - */ - nmfilters = le32_to_cpu(lif->identity->eth.max_mcast_filters); - nufilters = le32_to_cpu(lif->identity->eth.max_ucast_filters); - - if ((is_multicast_ether_addr(addr) && lif->nmcast < nmfilters)) - lif->nmcast++; - else if (!is_multicast_ether_addr(addr) && - lif->nucast < nufilters) - lif->nucast++; - else - return -ENOSPC; - } else { - if (is_multicast_ether_addr(addr) && lif->nmcast) - lif->nmcast--; - else if (!is_multicast_ether_addr(addr) && lif->nucast) - lif->nucast--; - } + spin_unlock_bh(&lif->rx_filters.lock); - if (!can_sleep) { - work = kzalloc(sizeof(*work), GFP_ATOMIC); - if (!work) - return -ENOMEM; - work->type = add ? IONIC_DW_TYPE_RX_ADDR_ADD : - IONIC_DW_TYPE_RX_ADDR_DEL; - memcpy(work->addr, addr, ETH_ALEN); - netdev_dbg(lif->netdev, "deferred: rx_filter %s %pM\n", - add ? "add" : "del", addr); - ionic_lif_deferred_enqueue(&lif->deferred, work); - } else { - netdev_dbg(lif->netdev, "rx_filter %s %pM\n", - add ? "add" : "del", addr); - if (add) - return ionic_lif_addr_add(lif, addr); - else - return ionic_lif_addr_del(lif, addr); + if (state != IONIC_FILTER_STATE_NEW) { + err = ionic_adminq_post_wait(lif, &ctx); + if (err && err != -EEXIST) + return err; } return 0; @@ -1354,130 +1372,117 @@ static int ionic_lif_addr(struct ionic_lif *lif, const u8 *addr, bool add, static int ionic_addr_add(struct net_device *netdev, const u8 *addr) { - return ionic_lif_addr(netdev_priv(netdev), addr, ADD_ADDR, CAN_SLEEP); -} - -static int ionic_ndo_addr_add(struct net_device *netdev, const u8 *addr) -{ - return ionic_lif_addr(netdev_priv(netdev), addr, ADD_ADDR, CAN_NOT_SLEEP); + return ionic_lif_list_addr(netdev_priv(netdev), addr, ADD_ADDR); } static int ionic_addr_del(struct net_device *netdev, const u8 *addr) { - return ionic_lif_addr(netdev_priv(netdev), addr, DEL_ADDR, CAN_SLEEP); + return ionic_lif_list_addr(netdev_priv(netdev), addr, DEL_ADDR); } -static int ionic_ndo_addr_del(struct net_device *netdev, const u8 *addr) +void ionic_lif_rx_mode(struct ionic_lif *lif) { - return ionic_lif_addr(netdev_priv(netdev), addr, DEL_ADDR, CAN_NOT_SLEEP); -} - -static void ionic_lif_rx_mode(struct ionic_lif *lif, unsigned int rx_mode) -{ - struct ionic_admin_ctx ctx = { - .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), - .cmd.rx_mode_set = { - .opcode = IONIC_CMD_RX_MODE_SET, - .lif_index = cpu_to_le16(lif->index), - .rx_mode = cpu_to_le16(rx_mode), - }, - }; + struct net_device *netdev = lif->netdev; + unsigned int nfilters; + unsigned int nd_flags; char buf[128]; - int err; + u16 rx_mode; int i; #define REMAIN(__x) (sizeof(buf) - (__x)) - i = scnprintf(buf, sizeof(buf), "rx_mode 0x%04x -> 0x%04x:", - lif->rx_mode, rx_mode); - if (rx_mode & IONIC_RX_MODE_F_UNICAST) - i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_UNICAST"); - if (rx_mode & IONIC_RX_MODE_F_MULTICAST) - i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_MULTICAST"); - if (rx_mode & IONIC_RX_MODE_F_BROADCAST) - i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_BROADCAST"); - if (rx_mode & IONIC_RX_MODE_F_PROMISC) - i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_PROMISC"); - if (rx_mode & IONIC_RX_MODE_F_ALLMULTI) - i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_ALLMULTI"); - netdev_dbg(lif->netdev, "lif%d %s\n", lif->index, buf); + mutex_lock(&lif->config_lock); - err = ionic_adminq_post_wait(lif, &ctx); - if (err) - netdev_warn(lif->netdev, "set rx_mode 0x%04x failed: %d\n", - rx_mode, err); - else - lif->rx_mode = rx_mode; -} - -static void ionic_set_rx_mode(struct net_device *netdev, bool can_sleep) -{ - struct ionic_lif *lif = netdev_priv(netdev); - struct ionic_deferred_work *work; - unsigned int nfilters; - unsigned int rx_mode; + /* grab the flags once for local use */ + nd_flags = netdev->flags; rx_mode = IONIC_RX_MODE_F_UNICAST; - rx_mode |= (netdev->flags & IFF_MULTICAST) ? IONIC_RX_MODE_F_MULTICAST : 0; - rx_mode |= (netdev->flags & IFF_BROADCAST) ? IONIC_RX_MODE_F_BROADCAST : 0; - rx_mode |= (netdev->flags & IFF_PROMISC) ? IONIC_RX_MODE_F_PROMISC : 0; - rx_mode |= (netdev->flags & IFF_ALLMULTI) ? IONIC_RX_MODE_F_ALLMULTI : 0; + rx_mode |= (nd_flags & IFF_MULTICAST) ? IONIC_RX_MODE_F_MULTICAST : 0; + rx_mode |= (nd_flags & IFF_BROADCAST) ? IONIC_RX_MODE_F_BROADCAST : 0; + rx_mode |= (nd_flags & IFF_PROMISC) ? IONIC_RX_MODE_F_PROMISC : 0; + rx_mode |= (nd_flags & IFF_ALLMULTI) ? IONIC_RX_MODE_F_ALLMULTI : 0; - /* sync unicast addresses - * next check to see if we're in an overflow state + /* sync the mac filters */ + ionic_rx_filter_sync(lif); + + /* check for overflow state * if so, we track that we overflowed and enable NIC PROMISC * else if the overflow is set and not needed * we remove our overflow flag and check the netdev flags * to see if we can disable NIC PROMISC */ - if (can_sleep) - __dev_uc_sync(netdev, ionic_addr_add, ionic_addr_del); - else - __dev_uc_sync(netdev, ionic_ndo_addr_add, ionic_ndo_addr_del); nfilters = le32_to_cpu(lif->identity->eth.max_ucast_filters); - if (netdev_uc_count(netdev) + 1 > nfilters) { + if ((lif->nucast + lif->nmcast) >= nfilters) { rx_mode |= IONIC_RX_MODE_F_PROMISC; + rx_mode |= IONIC_RX_MODE_F_ALLMULTI; lif->uc_overflow = true; + lif->mc_overflow = true; } else if (lif->uc_overflow) { lif->uc_overflow = false; - if (!(netdev->flags & IFF_PROMISC)) - rx_mode &= ~IONIC_RX_MODE_F_PROMISC; - } - - /* same for multicast */ - if (can_sleep) - __dev_mc_sync(netdev, ionic_addr_add, ionic_addr_del); - else - __dev_mc_sync(netdev, ionic_ndo_addr_add, ionic_ndo_addr_del); - nfilters = le32_to_cpu(lif->identity->eth.max_mcast_filters); - if (netdev_mc_count(netdev) > nfilters) { - rx_mode |= IONIC_RX_MODE_F_ALLMULTI; - lif->mc_overflow = true; - } else if (lif->mc_overflow) { lif->mc_overflow = false; - if (!(netdev->flags & IFF_ALLMULTI)) + if (!(nd_flags & IFF_PROMISC)) + rx_mode &= ~IONIC_RX_MODE_F_PROMISC; + if (!(nd_flags & IFF_ALLMULTI)) rx_mode &= ~IONIC_RX_MODE_F_ALLMULTI; } + i = scnprintf(buf, sizeof(buf), "rx_mode 0x%04x -> 0x%04x:", + lif->rx_mode, rx_mode); + if (rx_mode & IONIC_RX_MODE_F_UNICAST) + i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_UNICAST"); + if (rx_mode & IONIC_RX_MODE_F_MULTICAST) + i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_MULTICAST"); + if (rx_mode & IONIC_RX_MODE_F_BROADCAST) + i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_BROADCAST"); + if (rx_mode & IONIC_RX_MODE_F_PROMISC) + i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_PROMISC"); + if (rx_mode & IONIC_RX_MODE_F_ALLMULTI) + i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_ALLMULTI"); + if (rx_mode & IONIC_RX_MODE_F_RDMA_SNIFFER) + i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_RDMA_SNIFFER"); + netdev_dbg(netdev, "lif%d %s\n", lif->index, buf); + if (lif->rx_mode != rx_mode) { - if (!can_sleep) { - work = kzalloc(sizeof(*work), GFP_ATOMIC); - if (!work) { - netdev_err(lif->netdev, "rxmode change dropped\n"); - return; - } - work->type = IONIC_DW_TYPE_RX_MODE; - work->rx_mode = rx_mode; - netdev_dbg(lif->netdev, "deferred: rx_mode\n"); - ionic_lif_deferred_enqueue(&lif->deferred, work); - } else { - ionic_lif_rx_mode(lif, rx_mode); - } + struct ionic_admin_ctx ctx = { + .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), + .cmd.rx_mode_set = { + .opcode = IONIC_CMD_RX_MODE_SET, + .lif_index = cpu_to_le16(lif->index), + }, + }; + int err; + + ctx.cmd.rx_mode_set.rx_mode = cpu_to_le16(rx_mode); + err = ionic_adminq_post_wait(lif, &ctx); + if (err) + netdev_warn(netdev, "set rx_mode 0x%04x failed: %d\n", + rx_mode, err); + else + lif->rx_mode = rx_mode; } + + mutex_unlock(&lif->config_lock); } static void ionic_ndo_set_rx_mode(struct net_device *netdev) { - ionic_set_rx_mode(netdev, CAN_NOT_SLEEP); + struct ionic_lif *lif = netdev_priv(netdev); + struct ionic_deferred_work *work; + + /* Sync the kernel filter list with the driver filter list */ + __dev_uc_sync(netdev, ionic_addr_add, ionic_addr_del); + __dev_mc_sync(netdev, ionic_addr_add, ionic_addr_del); + + /* Shove off the rest of the rxmode work to the work task + * which will include syncing the filters to the firmware. + */ + work = kzalloc(sizeof(*work), GFP_ATOMIC); + if (!work) { + netdev_err(lif->netdev, "rxmode change dropped\n"); + return; + } + work->type = IONIC_DW_TYPE_RX_MODE; + netdev_dbg(lif->netdev, "deferred: rx_mode\n"); + ionic_lif_deferred_enqueue(&lif->deferred, work); } static __le64 ionic_netdev_features_to_nic(netdev_features_t features) @@ -1606,7 +1611,6 @@ static int ionic_init_nic_features(struct ionic_lif *lif) features = NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER | - NETIF_F_RXHASH | NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM | @@ -1614,6 +1618,9 @@ static int ionic_init_nic_features(struct ionic_lif *lif) NETIF_F_TSO6 | NETIF_F_TSO_ECN; + if (lif->nxqs > 1) + features |= NETIF_F_RXHASH; + err = ionic_set_nic_features(lif, features); if (err) return err; @@ -1696,13 +1703,13 @@ static int ionic_set_mac_address(struct net_device *netdev, void *sa) if (!is_zero_ether_addr(netdev->dev_addr)) { netdev_info(netdev, "deleting mac addr %pM\n", netdev->dev_addr); - ionic_addr_del(netdev, netdev->dev_addr); + ionic_lif_addr_del(netdev_priv(netdev), netdev->dev_addr); } eth_commit_mac_addr_change(netdev, addr); netdev_info(netdev, "updating mac addr %pM\n", mac); - return ionic_addr_add(netdev, mac); + return ionic_lif_addr_add(netdev_priv(netdev), mac); } static void ionic_stop_queues_reconfig(struct ionic_lif *lif) @@ -1808,7 +1815,12 @@ static int ionic_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, if (err) return err; - return ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx); + spin_lock_bh(&lif->rx_filters.lock); + 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; } static int ionic_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, @@ -2111,7 +2123,7 @@ static int ionic_txrx_init(struct ionic_lif *lif) if (lif->netdev->features & NETIF_F_RXHASH) ionic_lif_rss_init(lif); - ionic_set_rx_mode(lif->netdev, CAN_SLEEP); + ionic_lif_rx_mode(lif); return 0; @@ -2209,9 +2221,11 @@ static int ionic_open(struct net_device *netdev) if (test_and_clear_bit(IONIC_LIF_F_BROKEN, lif->state)) netdev_info(netdev, "clearing broken state\n"); + mutex_lock(&lif->queue_lock); + err = ionic_txrx_alloc(lif); if (err) - return err; + goto err_unlock; err = ionic_txrx_init(lif); if (err) @@ -2232,12 +2246,21 @@ static int ionic_open(struct net_device *netdev) goto err_txrx_deinit; } + /* If hardware timestamping is enabled, but the queues were freed by + * ionic_stop, those need to be reallocated and initialized, too. + */ + ionic_lif_hwstamp_recreate_queues(lif); + + mutex_unlock(&lif->queue_lock); + return 0; err_txrx_deinit: ionic_txrx_deinit(lif); err_txrx_free: ionic_txrx_free(lif); +err_unlock: + mutex_unlock(&lif->queue_lock); return err; } @@ -2257,14 +2280,16 @@ static int ionic_stop(struct net_device *netdev) if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) return 0; + mutex_lock(&lif->queue_lock); ionic_stop_queues(lif); ionic_txrx_deinit(lif); ionic_txrx_free(lif); + mutex_unlock(&lif->queue_lock); return 0; } -static int ionic_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +static int ionic_eth_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { struct ionic_lif *lif = netdev_priv(netdev); @@ -2526,7 +2551,7 @@ static int ionic_set_vf_link_state(struct net_device *netdev, int vf, int set) static const struct net_device_ops ionic_netdev_ops = { .ndo_open = ionic_open, .ndo_stop = ionic_stop, - .ndo_do_ioctl = ionic_do_ioctl, + .ndo_eth_ioctl = ionic_eth_ioctl, .ndo_start_xmit = ionic_start_xmit, .ndo_get_stats64 = ionic_get_stats64, .ndo_set_rx_mode = ionic_ndo_set_rx_mode, @@ -2587,22 +2612,26 @@ int ionic_reconfigure_queues(struct ionic_lif *lif, struct ionic_qcq **tx_qcqs = NULL; struct ionic_qcq **rx_qcqs = NULL; unsigned int flags, i; - int err = -ENOMEM; + int err = 0; /* allocate temporary qcq arrays to hold new queue structs */ if (qparam->nxqs != lif->nxqs || qparam->ntxq_descs != lif->ntxq_descs) { tx_qcqs = devm_kcalloc(lif->ionic->dev, lif->ionic->ntxqs_per_lif, sizeof(struct ionic_qcq *), GFP_KERNEL); - if (!tx_qcqs) + if (!tx_qcqs) { + err = -ENOMEM; goto err_out; + } } if (qparam->nxqs != lif->nxqs || qparam->nrxq_descs != lif->nrxq_descs || qparam->rxq_features != lif->rxq_features) { rx_qcqs = devm_kcalloc(lif->ionic->dev, lif->ionic->nrxqs_per_lif, sizeof(struct ionic_qcq *), GFP_KERNEL); - if (!rx_qcqs) + if (!rx_qcqs) { + err = -ENOMEM; goto err_out; + } } /* allocate new desc_info and rings, but leave the interrupt setup @@ -2781,6 +2810,9 @@ err_out: ionic_qcq_free(lif, lif->rxqcqs[i]); } + if (err) + netdev_info(lif->netdev, "%s: failed %d\n", __func__, err); + return err; } @@ -2834,8 +2866,14 @@ int ionic_lif_alloc(struct ionic *ionic) lif->ionic = ionic; lif->index = 0; - lif->ntxq_descs = IONIC_DEF_TXRX_DESC; - lif->nrxq_descs = IONIC_DEF_TXRX_DESC; + + if (is_kdump_kernel()) { + lif->ntxq_descs = IONIC_MIN_TXRX_DESC; + lif->nrxq_descs = IONIC_MIN_TXRX_DESC; + } else { + lif->ntxq_descs = IONIC_DEF_TXRX_DESC; + lif->nrxq_descs = IONIC_DEF_TXRX_DESC; + } /* Convert the default coalesce value to actual hw resolution */ lif->rx_coalesce_usecs = IONIC_ITR_COAL_USEC_DEFAULT; @@ -3058,6 +3096,7 @@ void ionic_lif_deinit(struct ionic_lif *lif) ionic_lif_qcq_deinit(lif, lif->notifyqcq); ionic_lif_qcq_deinit(lif, lif->adminqcq); + mutex_destroy(&lif->config_lock); mutex_destroy(&lif->queue_lock); ionic_lif_reset(lif); } @@ -3185,7 +3224,7 @@ static int ionic_station_set(struct ionic_lif *lif) */ if (!ether_addr_equal(ctx.comp.lif_getattr.mac, netdev->dev_addr)) - ionic_lif_addr(lif, netdev->dev_addr, ADD_ADDR, CAN_SLEEP); + 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); @@ -3202,7 +3241,7 @@ static int ionic_station_set(struct ionic_lif *lif) netdev_dbg(lif->netdev, "adding station MAC addr %pM\n", netdev->dev_addr); - ionic_lif_addr(lif, netdev->dev_addr, ADD_ADDR, CAN_SLEEP); + ionic_lif_addr_add(lif, netdev->dev_addr); return 0; } @@ -3225,6 +3264,7 @@ int ionic_lif_init(struct ionic_lif *lif) lif->hw_index = le16_to_cpu(comp.hw_index); mutex_init(&lif->queue_lock); + mutex_init(&lif->config_lock); /* now that we have the hw_index we can figure out our doorbell page */ lif->dbid_count = le32_to_cpu(lif->ionic->ident.dev.ndbpgs_per_lif); @@ -3519,6 +3559,7 @@ int ionic_lif_size(struct ionic *ionic) unsigned int min_intrs; int err; + /* retrieve basic values from FW */ lc = &ident->lif.eth.config; dev_nintrs = le32_to_cpu(ident->dev.nintrs); neqs_per_lif = le32_to_cpu(ident->lif.rdma.eq_qtype.qid_count); @@ -3526,6 +3567,15 @@ int ionic_lif_size(struct ionic *ionic) ntxqs_per_lif = le32_to_cpu(lc->queue_count[IONIC_QTYPE_TXQ]); nrxqs_per_lif = le32_to_cpu(lc->queue_count[IONIC_QTYPE_RXQ]); + /* limit values to play nice with kdump */ + if (is_kdump_kernel()) { + dev_nintrs = 2; + neqs_per_lif = 0; + nnqs_per_lif = 0; + ntxqs_per_lif = 1; + nrxqs_per_lif = 1; + } + /* reserve last queue id for hardware timestamping */ if (lc->features & cpu_to_le64(IONIC_ETH_HW_TIMESTAMP)) { if (ntxqs_per_lif <= 1 || nrxqs_per_lif <= 1) { |